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