]> diplodocus.org Git - nmh/blob - uip/pick.c
Document argsplit changes in mh-profile man page.
[nmh] / uip / pick.c
1
2 /*
3 * pick.c -- search for messages by content
4 *
5 * This code is Copyright (c) 2002, 2008, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
8 */
9
10 #include <h/mh.h>
11 #include <h/tws.h>
12 #include <h/picksbr.h>
13 #include <h/utils.h>
14
15 #define PICK_SWITCHES \
16 X("and", 0, ANDSW) \
17 X("or", 0, ORSW) \
18 X("not", 0, NOTSW) \
19 X("lbrace", 0, LBRSW) \
20 X("rbrace", 0, RBRSW) \
21 X("cc pattern", 0, CCSW) \
22 X("date pattern", 0, DATESW) \
23 X("from pattern", 0, FROMSW) \
24 X("search pattern", 0, SRCHSW) \
25 X("subject pattern", 0, SUBJSW) \
26 X("to pattern", 0, TOSW) \
27 X("-othercomponent pattern", 0, OTHRSW) \
28 X("after date", 0, AFTRSW) \
29 X("before date", 0, BEFRSW) \
30 X("datefield field", 5, DATFDSW) \
31 X("sequence name", 0, SEQSW) \
32 X("nosequence", 0, NSEQSW) \
33 X("public", 0, PUBLSW) \
34 X("nopublic", 0, NPUBLSW) \
35 X("zero", 0, ZEROSW) \
36 X("nozero", 0, NZEROSW) \
37 X("list", 0, LISTSW) \
38 X("nolist", 0, NLISTSW) \
39 X("version", 0, VERSIONSW) \
40 X("help", 0, HELPSW) \
41
42 #define X(sw, minchars, id) id,
43 DEFINE_SWITCH_ENUM(PICK);
44 #undef X
45
46 #define X(sw, minchars, id) { sw, minchars, id },
47 DEFINE_SWITCH_ARRAY(PICK, switches);
48 #undef X
49
50 static int listsw = -1;
51
52 static void putzero_done (int) NORETURN;
53
54 int
55 main (int argc, char **argv)
56 {
57 int publicsw = -1, zerosw = 1, vecp = 0;
58 size_t seqp = 0;
59 int lo, hi, msgnum;
60 char *maildir, *folder = NULL, buf[100];
61 char *cp, **argp, **arguments;
62 char *seqs[NUMATTRS + 1], *vec[MAXARGS];
63 struct msgs_array msgs = { 0, 0, NULL };
64 struct msgs *mp;
65 register FILE *fp;
66
67 done=putzero_done;
68
69 #ifdef LOCALE
70 setlocale(LC_ALL, "");
71 #endif
72 invo_name = r1bindex (argv[0], '/');
73
74 /* read user profile/context */
75 context_read();
76
77 arguments = getarguments (invo_name, argc, argv, 1);
78 argp = arguments;
79
80 while ((cp = *argp++)) {
81 if (*cp == '-') {
82 if (*++cp == '-') {
83 vec[vecp++] = --cp;
84 goto pattern;
85 }
86 switch (smatch (cp, switches)) {
87 case AMBIGSW:
88 ambigsw (cp, switches);
89 listsw = 0; /* HACK */
90 done (1);
91 case UNKWNSW:
92 adios (NULL, "-%s unknown", cp);
93
94 case HELPSW:
95 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
96 invo_name);
97 print_help (buf, switches, 1);
98 listsw = 0; /* HACK */
99 done (0);
100 case VERSIONSW:
101 print_version(invo_name);
102 listsw = 0; /* HACK */
103 done (0);
104
105 case CCSW:
106 case DATESW:
107 case FROMSW:
108 case SUBJSW:
109 case TOSW:
110 case DATFDSW:
111 case AFTRSW:
112 case BEFRSW:
113 case SRCHSW:
114 vec[vecp++] = --cp;
115 pattern:
116 if (!(cp = *argp++))/* allow -xyz arguments */
117 adios (NULL, "missing argument to %s", argp[-2]);
118 vec[vecp++] = cp;
119 continue;
120 case OTHRSW:
121 adios (NULL, "internal error!");
122
123 case ANDSW:
124 case ORSW:
125 case NOTSW:
126 case LBRSW:
127 case RBRSW:
128 vec[vecp++] = --cp;
129 continue;
130
131 case SEQSW:
132 if (!(cp = *argp++) || *cp == '-')
133 adios (NULL, "missing argument to %s", argp[-2]);
134
135 /* check if too many sequences specified */
136 if (seqp >= NUMATTRS)
137 adios (NULL, "too many sequences (more than %d) specified", NUMATTRS);
138
139 if (!seq_nameok (cp))
140 done (1);
141
142 seqs[seqp++] = cp;
143 continue;
144 case NSEQSW:
145 seqp = 0;
146 continue;
147 case PUBLSW:
148 publicsw = 1;
149 continue;
150 case NPUBLSW:
151 publicsw = 0;
152 continue;
153 case ZEROSW:
154 zerosw++;
155 continue;
156 case NZEROSW:
157 zerosw = 0;
158 continue;
159
160 case LISTSW:
161 listsw = 1;
162 continue;
163 case NLISTSW:
164 listsw = 0;
165 continue;
166 }
167 }
168 if (*cp == '+' || *cp == '@') {
169 if (folder)
170 adios (NULL, "only one folder at a time!");
171 else
172 folder = pluspath (cp);
173 } else
174 app_msgarg(&msgs, cp);
175 }
176 vec[vecp] = NULL;
177
178 if (!context_find ("path"))
179 free (path ("./", TFOLDER));
180
181 /*
182 * If we didn't specify which messages to search,
183 * then search the whole folder.
184 */
185 if (!msgs.size)
186 app_msgarg(&msgs, "all");
187
188 if (!folder)
189 folder = getfolder (1);
190 maildir = m_maildir (folder);
191
192 if (chdir (maildir) == NOTOK)
193 adios (maildir, "unable to change directory to");
194
195 /* read folder and create message structure */
196 if (!(mp = folder_read (folder)))
197 adios (NULL, "unable to read folder %s", folder);
198
199 /* check for empty folder */
200 if (mp->nummsg == 0)
201 adios (NULL, "no messages in %s", folder);
202
203 /* parse all the message ranges/sequences and set SELECTED */
204 for (msgnum = 0; msgnum < msgs.size; msgnum++)
205 if (!m_convert (mp, msgs.msgs[msgnum]))
206 done (1);
207 seq_setprev (mp); /* set the previous-sequence */
208
209 /*
210 * If we aren't saving the results to a sequence,
211 * we default to list the results.
212 */
213 if (listsw == -1)
214 listsw = !seqp;
215
216 if (publicsw == 1 && is_readonly(mp))
217 adios (NULL, "folder %s is read-only, so -public not allowed", folder);
218
219 if (!pcompile (vec, NULL))
220 done (1);
221
222 lo = mp->lowsel;
223 hi = mp->hghsel;
224
225 /* If printing message numbers to standard out, force line buffering on.
226 */
227 if (listsw)
228 setvbuf (stdout, NULL, _IOLBF, 0);
229
230 /*
231 * Scan through all the SELECTED messages and check for a
232 * match. If the message does not match, then unselect it.
233 */
234 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
235 if (is_selected (mp, msgnum)) {
236 if ((fp = fopen (cp = m_name (msgnum), "r")) == NULL)
237 admonish (cp, "unable to read message");
238 if (fp && pmatches (fp, msgnum, 0L, 0L)) {
239 if (msgnum < lo)
240 lo = msgnum;
241 if (msgnum > hi)
242 hi = msgnum;
243
244 if (listsw)
245 printf ("%s\n", m_name (msgnum));
246 } else {
247 /* if it doesn't match, then unselect it */
248 unset_selected (mp, msgnum);
249 mp->numsel--;
250 }
251 if (fp)
252 fclose (fp);
253 }
254 }
255
256 mp->lowsel = lo;
257 mp->hghsel = hi;
258
259 if (mp->numsel <= 0)
260 adios (NULL, "no messages match specification");
261
262 seqs[seqp] = NULL;
263
264 /*
265 * Add the matching messages to sequences
266 */
267 for (seqp = 0; seqs[seqp]; seqp++)
268 if (!seq_addsel (mp, seqs[seqp], publicsw, zerosw))
269 done (1);
270
271 /*
272 * Print total matched if not printing each matched message number.
273 */
274 if (!listsw) {
275 printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
276 }
277
278 context_replace (pfolder, folder); /* update current folder */
279 seq_save (mp); /* synchronize message sequences */
280 context_save (); /* save the context file */
281 folder_free (mp); /* free folder/message structure */
282 done (0);
283 return 1;
284 }
285
286
287 static void
288 putzero_done (int status)
289 {
290 if (listsw && status && !isatty (fileno (stdout)))
291 printf ("0\n");
292 exit (status);
293 }