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