Created app_msgarg() (append message arg) and a simple resizable array
[mmh] / uip / pick.c
1
2 /*
3  * pick.c -- search for messages by content
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
10  */
11
12 #include <h/mh.h>
13 #include <h/tws.h>
14 #include <h/picksbr.h>
15 #include <h/utils.h>
16
17 static struct swit switches[] = {
18 #define ANDSW                   0
19     { "and", 0 },
20 #define ORSW                    1
21     { "or", 0 },
22 #define NOTSW                   2
23     { "not", 0 },
24 #define LBRSW                   3
25     { "lbrace", 0 },
26 #define RBRSW                   4
27     { "rbrace", 0 },
28 #define CCSW                    5
29     { "cc  pattern", 0 },
30 #define DATESW                  6
31     { "date  pattern", 0 },
32 #define FROMSW                  7
33     { "from  pattern", 0 },
34 #define SRCHSW                  8
35     { "search  pattern", 0 },
36 #define SUBJSW                  9
37     { "subject  pattern", 0 },
38 #define TOSW                   10
39     { "to  pattern", 0 },
40 #define OTHRSW                 11
41     { "-othercomponent  pattern", 0 },
42 #define AFTRSW                 12
43     { "after date", 0 },
44 #define BEFRSW                 13
45     { "before date", 0 },
46 #define DATFDSW                14
47     { "datefield field", 5 },
48 #define SEQSW                  15
49     { "sequence name", 0 },
50 #define PUBLSW                 16
51     { "public", 0 },
52 #define NPUBLSW                17
53     { "nopublic", 0 },
54 #define ZEROSW                 18
55     { "zero", 0 },
56 #define NZEROSW                19
57     { "nozero", 0 },
58 #define LISTSW                 20
59     { "list", 0 },
60 #define NLISTSW                21
61     { "nolist", 0 },
62 #define VERSIONSW              22
63     { "version", 0 },
64 #define HELPSW                 23
65     { "help", 0 },
66     { NULL, 0 }
67 };
68
69 static int listsw = -1;
70
71
72 int
73 main (int argc, char **argv)
74 {
75     int publicsw = -1, zerosw = 1, seqp = 0, vecp = 0;
76     int lo, hi, msgnum;
77     char *maildir, *folder = NULL, buf[100];
78     char *cp, **argp, **arguments;
79     char *seqs[NUMATTRS + 1], *vec[MAXARGS];
80     struct msgs_array msgs = { 0, 0, NULL };
81     struct msgs *mp;
82     register FILE *fp;
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                 seqs[seqp++] = cp;
154                 continue;
155             case PUBLSW: 
156                 publicsw = 1;
157                 continue;
158             case NPUBLSW: 
159                 publicsw = 0;
160                 continue;
161             case ZEROSW: 
162                 zerosw++;
163                 continue;
164             case NZEROSW: 
165                 zerosw = 0;
166                 continue;
167
168             case LISTSW: 
169                 listsw = 1;
170                 continue;
171             case NLISTSW: 
172                 listsw = 0;
173                 continue;
174             }
175         }
176         if (*cp == '+' || *cp == '@') {
177             if (folder)
178                 adios (NULL, "only one folder at a time!");
179             else
180                 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
181         } else
182                 app_msgarg(&msgs, cp);
183     }
184     vec[vecp] = NULL;
185
186     if (!context_find ("path"))
187         free (path ("./", TFOLDER));
188
189     /*
190      * If we didn't specify which messages to search,
191      * then search the whole folder.
192      */
193     if (!msgs.size)
194         app_msgarg(&msgs, "all");
195
196     if (!folder)
197         folder = getfolder (1);
198     maildir = m_maildir (folder);
199
200     if (chdir (maildir) == NOTOK)
201         adios (maildir, "unable to change directory to");
202
203     /* read folder and create message structure */
204     if (!(mp = folder_read (folder)))
205         adios (NULL, "unable to read folder %s", folder);
206
207     /* check for empty folder */
208     if (mp->nummsg == 0)
209         adios (NULL, "no messages in %s", folder);
210
211     /* parse all the message ranges/sequences and set SELECTED */
212     for (msgnum = 0; msgnum < msgs.size; msgnum++)
213         if (!m_convert (mp, msgs.msgs[msgnum]))
214             done (1);
215     seq_setprev (mp);   /* set the previous-sequence */
216
217     /*
218      * If we aren't saving the results to a sequence,
219      * we default to list the results.
220      */
221     if (listsw == -1)
222         listsw = !seqp;
223
224     if (publicsw == 1 && is_readonly(mp))
225         adios (NULL, "folder %s is read-only, so -public not allowed", folder);
226
227     if (!pcompile (vec, NULL))
228         done (1);
229
230     lo = mp->lowsel;
231     hi = mp->hghsel;
232
233     /*
234      * Scan through all the SELECTED messages and check for a
235      * match.  If the message does not match, then unselect it.
236      */
237     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
238         if (is_selected (mp, msgnum)) {
239             if ((fp = fopen (cp = m_name (msgnum), "r")) == NULL)
240                 admonish (cp, "unable to read message");
241             if (fp && pmatches (fp, msgnum, 0L, 0L)) {
242                 if (msgnum < lo)
243                     lo = msgnum;
244                 if (msgnum > hi)
245                     hi = msgnum;
246             } else {
247                 /* if it doesn't match, then unselect it */
248                 unset_selected (mp, msgnum);
249                 mp->numsel--;
250             }
251             if (fp)
252                 fclose (fp);
253         }
254     }
255
256     mp->lowsel = lo;
257     mp->hghsel = hi;
258
259     if (mp->numsel <= 0)
260         adios (NULL, "no messages match specification");
261
262     seqs[seqp] = NULL;
263
264     /*
265      * Add the matching messages to sequences
266      */
267     for (seqp = 0; seqs[seqp]; seqp++)
268         if (!seq_addsel (mp, seqs[seqp], publicsw, zerosw))
269             done (1);
270
271     /*
272      * Print the name of all the matches
273      */
274     if (listsw) {
275         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
276             if (is_selected (mp, msgnum))
277                 printf ("%s\n", m_name (msgnum));
278     } else {
279         printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
280     }
281
282     context_replace (pfolder, folder);  /* update current folder         */
283     seq_save (mp);                      /* synchronize message sequences */
284     context_save ();                    /* save the context file         */
285     folder_free (mp);                   /* free folder/message structure */
286     return done (0);
287 }
288
289
290 int
291 done (int status)
292 {
293     if (listsw && status && !isatty (fileno (stdout)))
294         printf ("0\n");
295     exit (status);
296     return 1;  /* dead code to satisfy the compiler */
297 }