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