e4d1370440d089a26b6f4b2810b551c749d85d02
[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 = mhbasename(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 = getcpy(expandfol(cp));
186                 } else
187                         app_msgarg(&msgs, cp);
188         }
189         vec[vecp] = NULL;
190
191         /*
192         ** If we didn't specify which messages to search,
193         ** then search the whole folder.
194         */
195         if (!msgs.size)
196                 app_msgarg(&msgs, seq_all);
197
198         if (!folder)
199                 folder = getcurfol();
200         maildir = toabsdir(folder);
201
202         if (chdir(maildir) == NOTOK)
203                 adios(maildir, "unable to change directory to");
204
205         /* read folder and create message structure */
206         if (!(mp = folder_read(folder)))
207                 adios(NULL, "unable to read folder %s", folder);
208
209         /* check for empty folder */
210         if (mp->nummsg == 0)
211                 adios(NULL, "no messages in %s", folder);
212
213         /* parse all the message ranges/sequences and set SELECTED */
214         for (msgnum = 0; msgnum < msgs.size; msgnum++)
215                 if (!m_convert(mp, msgs.msgs[msgnum]))
216                         done(1);
217         seq_setprev(mp);  /* set the previous-sequence */
218
219         /*
220         ** If we aren't saving the results to a sequence,
221         ** we default to list the results.
222         */
223         if (listsw == -1)
224                 listsw = !seqp;
225
226         if (publicsw == 1 && is_readonly(mp))
227                 adios(NULL, "folder %s is read-only, so -public not allowed",
228                                 folder);
229
230         if (!pcompile(vec, NULL))
231                 done(1);
232
233         lo = mp->lowsel;
234         hi = mp->hghsel;
235
236         /*
237         ** If printing message numbers to standard out,
238         ** force line buffering on.
239         */
240         if (listsw)
241                 setvbuf(stdout, NULL, _IOLBF, 0);
242
243         /*
244         ** Scan through all the SELECTED messages and check for a
245         ** match.  If the message does not match, then unselect it.
246         */
247         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
248                 if (is_selected(mp, msgnum)) {
249                         if ((fp = fopen(cp = m_name(msgnum), "r")) == NULL)
250                                 admonish(cp, "unable to read message");
251                         if (fp && pmatches(fp, msgnum, 0L, 0L)) {
252                                 if (msgnum < lo)
253                                         lo = msgnum;
254                                 if (msgnum > hi)
255                                         hi = msgnum;
256
257                                 if (listsw)
258                                         printf("%s\n", m_name(msgnum));
259                         } else {
260                                 /* if it doesn't match, then unselect it */
261                                 unset_selected(mp, msgnum);
262                                 mp->numsel--;
263                         }
264                         if (fp)
265                                 fclose(fp);
266                 }
267         }
268
269         mp->lowsel = lo;
270         mp->hghsel = hi;
271
272         if (mp->numsel <= 0)
273                 adios(NULL, "no messages match specification");
274
275         seqs[seqp] = NULL;
276
277         /*
278         ** Add the matching messages to sequences
279         */
280         for (seqp = 0; seqs[seqp]; seqp++)
281                 if (!seq_addsel(mp, seqs[seqp], publicsw, zerosw))
282                         done(1);
283
284         /*
285         ** Print total matched if not printing each matched message number.
286         */
287         if (!listsw) {
288                 printf("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
289         }
290
291         context_replace(curfolder, folder);  /* update current folder */
292         seq_save(mp);  /* synchronize message sequences */
293         context_save();  /* save the context file */
294         folder_free(mp);  /* free folder/message structure */
295         done(0);
296         return 1;
297 }
298
299
300 static void
301 putzero_done(int status)
302 {
303         if (listsw && status && !isatty(fileno(stdout)))
304                 printf("0\n");
305         exit(status);
306 }