Rearranged whitespace (and comments) in all the code!
[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]",
111                                                   invo_name);
112                                 print_help (buf, switches, 1);
113                                 listsw = 0;  /* HACK */
114                                 done (1);
115                         case VERSIONSW:
116                                 print_version(invo_name);
117                                 listsw = 0;  /* HACK */
118                                 done (1);
119
120                         case CCSW:
121                         case DATESW:
122                         case FROMSW:
123                         case SUBJSW:
124                         case TOSW:
125                         case DATFDSW:
126                         case AFTRSW:
127                         case BEFRSW:
128                         case SRCHSW:
129                                 vec[vecp++] = --cp;
130                         pattern:
131                                 if (!(cp = *argp++))/* allow -xyz arguments */
132                                         adios (NULL, "missing argument to %s", 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", argp[-2]);
149
150                                 /* check if too many sequences specified */
151                                 if (seqp >= NUMATTRS)
152                                         adios (NULL, "too many sequences (more than %d) specified", NUMATTRS);
153
154                                 if (!seq_nameok (cp))
155                                   done (1);
156
157                                 seqs[seqp++] = cp;
158                                 continue;
159                         case PUBLSW:
160                                 publicsw = 1;
161                                 continue;
162                         case NPUBLSW:
163                                 publicsw = 0;
164                                 continue;
165                         case ZEROSW:
166                                 zerosw++;
167                                 continue;
168                         case NZEROSW:
169                                 zerosw = 0;
170                                 continue;
171
172                         case LISTSW:
173                                 listsw = 1;
174                                 continue;
175                         case NLISTSW:
176                                 listsw = 0;
177                                 continue;
178                         }
179                 }
180                 if (*cp == '+' || *cp == '@') {
181                         if (folder)
182                                 adios (NULL, "only one folder at a time!");
183                         else
184                                 folder = pluspath (cp);
185                 } else
186                                 app_msgarg(&msgs, cp);
187         }
188         vec[vecp] = NULL;
189
190         if (!context_find ("path"))
191                 free (path ("./", TFOLDER));
192
193         /*
194          * If we didn't specify which messages to search,
195          * then search the whole folder.
196          */
197         if (!msgs.size)
198                 app_msgarg(&msgs, "all");
199
200         if (!folder)
201                 folder = getfolder (1);
202         maildir = m_maildir (folder);
203
204         if (chdir (maildir) == NOTOK)
205                 adios (maildir, "unable to change directory to");
206
207         /* read folder and create message structure */
208         if (!(mp = folder_read (folder)))
209                 adios (NULL, "unable to read folder %s", folder);
210
211         /* check for empty folder */
212         if (mp->nummsg == 0)
213                 adios (NULL, "no messages in %s", folder);
214
215         /* parse all the message ranges/sequences and set SELECTED */
216         for (msgnum = 0; msgnum < msgs.size; msgnum++)
217                 if (!m_convert (mp, msgs.msgs[msgnum]))
218                         done (1);
219         seq_setprev (mp);  /* set the previous-sequence */
220
221         /*
222          * If we aren't saving the results to a sequence,
223          * we default to list the results.
224          */
225         if (listsw == -1)
226                 listsw = !seqp;
227
228         if (publicsw == 1 && is_readonly(mp))
229                 adios (NULL, "folder %s is read-only, so -public not allowed", folder);
230
231         if (!pcompile (vec, NULL))
232                 done (1);
233
234         lo = mp->lowsel;
235         hi = mp->hghsel;
236
237         /* If printing message numbers to standard out, force line buffering on.
238          */
239         if (listsw)
240                 setvbuf (stdout, NULL, _IOLBF, 0);
241
242         /*
243          * Scan through all the SELECTED messages and check for a
244          * match.  If the message does not match, then unselect it.
245          */
246         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
247                 if (is_selected (mp, msgnum)) {
248                         if ((fp = fopen (cp = m_name (msgnum), "r")) == NULL)
249                                 admonish (cp, "unable to read message");
250                         if (fp && pmatches (fp, msgnum, 0L, 0L)) {
251                                 if (msgnum < lo)
252                                         lo = msgnum;
253                                 if (msgnum > hi)
254                                         hi = msgnum;
255
256                                 if (listsw)
257                                         printf ("%s\n", m_name (msgnum));
258                         } else {
259                                 /* if it doesn't match, then unselect it */
260                                 unset_selected (mp, msgnum);
261                                 mp->numsel--;
262                         }
263                         if (fp)
264                                 fclose (fp);
265                 }
266         }
267
268         mp->lowsel = lo;
269         mp->hghsel = hi;
270
271         if (mp->numsel <= 0)
272                 adios (NULL, "no messages match specification");
273
274         seqs[seqp] = NULL;
275
276         /*
277          * Add the matching messages to sequences
278          */
279         for (seqp = 0; seqs[seqp]; seqp++)
280                 if (!seq_addsel (mp, seqs[seqp], publicsw, zerosw))
281                         done (1);
282
283         /*
284          * Print total matched if not printing each matched message number.
285          */
286         if (!listsw) {
287                 printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
288         }
289
290         context_replace (pfolder, folder);  /* update current folder */
291         seq_save (mp);  /* synchronize message sequences */
292         context_save ();  /* save the context file */
293         folder_free (mp);  /* free folder/message structure */
294         done (0);
295         return 1;
296 }
297
298
299 static void
300 putzero_done (int status)
301 {
302         if (listsw && status && !isatty (fileno (stdout)))
303                 printf ("0\n");
304         exit (status);
305 }