Remove RCS keywords, since they no longer work after git migration.
[mmh] / 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 static struct swit switches[] = {
16 #define ANDSW                   0
17     { "and", 0 },
18 #define ORSW                    1
19     { "or", 0 },
20 #define NOTSW                   2
21     { "not", 0 },
22 #define LBRSW                   3
23     { "lbrace", 0 },
24 #define RBRSW                   4
25     { "rbrace", 0 },
26 #define CCSW                    5
27     { "cc  pattern", 0 },
28 #define DATESW                  6
29     { "date  pattern", 0 },
30 #define FROMSW                  7
31     { "from  pattern", 0 },
32 #define SRCHSW                  8
33     { "search  pattern", 0 },
34 #define SUBJSW                  9
35     { "subject  pattern", 0 },
36 #define TOSW                   10
37     { "to  pattern", 0 },
38 #define OTHRSW                 11
39     { "-othercomponent  pattern", 0 },
40 #define AFTRSW                 12
41     { "after date", 0 },
42 #define BEFRSW                 13
43     { "before date", 0 },
44 #define DATFDSW                14
45     { "datefield field", 5 },
46 #define SEQSW                  15
47     { "sequence name", 0 },
48 #define PUBLSW                 16
49     { "public", 0 },
50 #define NPUBLSW                17
51     { "nopublic", 0 },
52 #define ZEROSW                 18
53     { "zero", 0 },
54 #define NZEROSW                19
55     { "nozero", 0 },
56 #define LISTSW                 20
57     { "list", 0 },
58 #define NLISTSW                21
59     { "nolist", 0 },
60 #define VERSIONSW              22
61     { "version", 0 },
62 #define HELPSW                 23
63     { "help", 0 },
64     { NULL, 0 }
65 };
66
67 static int listsw = -1;
68
69 static void putzero_done (int) NORETURN;
70
71 int
72 main (int argc, char **argv)
73 {
74     int publicsw = -1, zerosw = 1, seqp = 0, vecp = 0;
75     int lo, hi, msgnum;
76     char *maildir, *folder = NULL, buf[100];
77     char *cp, **argp, **arguments;
78     char *seqs[NUMATTRS + 1], *vec[MAXARGS];
79     struct msgs_array msgs = { 0, 0, NULL };
80     struct msgs *mp;
81     register FILE *fp;
82
83     done=putzero_done;
84
85 #ifdef LOCALE
86     setlocale(LC_ALL, "");
87 #endif
88     invo_name = r1bindex (argv[0], '/');
89
90     /* read user profile/context */
91     context_read();
92
93     arguments = getarguments (invo_name, argc, argv, 1);
94     argp = arguments;
95
96     while ((cp = *argp++)) {
97         if (*cp == '-') {
98             if (*++cp == '-') {
99                 vec[vecp++] = --cp;
100                 goto pattern;
101             }
102             switch (smatch (cp, switches)) {
103             case AMBIGSW: 
104                 ambigsw (cp, switches);
105                 listsw = 0;     /* HACK */
106                 done (1);
107             case UNKWNSW: 
108                 adios (NULL, "-%s unknown", cp);
109
110             case HELPSW: 
111                 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
112                           invo_name);
113                 print_help (buf, switches, 1);
114                 listsw = 0;     /* HACK */
115                 done (1);
116             case VERSIONSW:
117                 print_version(invo_name);
118                 listsw = 0;     /* HACK */
119                 done (1);
120
121             case CCSW: 
122             case DATESW: 
123             case FROMSW: 
124             case SUBJSW: 
125             case TOSW: 
126             case DATFDSW: 
127             case AFTRSW: 
128             case BEFRSW: 
129             case SRCHSW: 
130                 vec[vecp++] = --cp;
131             pattern:
132                 if (!(cp = *argp++))/* allow -xyz arguments */
133                     adios (NULL, "missing argument to %s", argp[-2]);
134                 vec[vecp++] = cp;
135                 continue;
136             case OTHRSW: 
137                 adios (NULL, "internal error!");
138
139             case ANDSW:
140             case ORSW:
141             case NOTSW:
142             case LBRSW:
143             case RBRSW:
144                 vec[vecp++] = --cp;
145                 continue;
146
147             case SEQSW: 
148                 if (!(cp = *argp++) || *cp == '-')
149                     adios (NULL, "missing argument to %s", 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", folder);
231
232     if (!pcompile (vec, NULL))
233         done (1);
234
235     lo = mp->lowsel;
236     hi = mp->hghsel;
237
238     /* If printing message numbers to standard out, 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 (pfolder, 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 }