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