]> diplodocus.org Git - nmh/blob - uip/show.c
Feed fileproc and mhlproc from rcvdist, send, and whatnow to post.
[nmh] / uip / show.c
1
2 /*
3 * show.c -- show/list messages
4 *
5 * This code is Copyright (c) 2002, 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/mime.h>
12 #include <h/utils.h>
13
14 static struct swit switches[] = {
15 #define CHECKMIMESW 0
16 { "checkmime", 0 },
17 #define NOCHECKMIMESW 1
18 { "nocheckmime", 0 },
19 #define HEADSW 2
20 { "header", 0 },
21 #define NHEADSW 3
22 { "noheader", 0 },
23 #define FORMSW 4
24 { "form formfile", 0 },
25 #define PROGSW 5
26 { "moreproc program", 0 },
27 #define NPROGSW 6
28 { "nomoreproc", 0 },
29 #define LENSW 7
30 { "length lines", 0 },
31 #define WIDTHSW 8
32 { "width columns", 0 },
33 #define SHOWSW 9
34 { "showproc program", 0 },
35 #define SHOWMIMESW 10
36 { "showmimeproc program", 0 },
37 #define NSHOWSW 11
38 { "noshowproc", 0 },
39 #define DRFTSW 12
40 { "draft", 0 },
41 #define FILESW 13
42 { "file file", -4 }, /* interface from showfile */
43 #define VERSIONSW 14
44 { "version", 0 },
45 #define HELPSW 15
46 { "help", 0 },
47 { NULL, 0 }
48 };
49
50 /*
51 * static prototypes
52 */
53 static int is_nontext(char *);
54
55 /* prototype from mhlsbr.c */
56 int mhl (int, char **);
57
58 #define SHOW 0
59 #define NEXT 1
60 #define PREV 2
61
62
63 int
64 main (int argc, char **argv)
65 {
66 int draftsw = 0, headersw = 1, msgp = 0;
67 int nshow = 0, checkmime = 1, mime;
68 int vecp = 1, procp = 1, isdf = 0, mode = SHOW, msgnum;
69 char *cp, *maildir, *file = NULL, *folder = NULL, *proc;
70 char buf[BUFSIZ], **argp, **arguments;
71 char *msgs[MAXARGS], *vec[MAXARGS];
72 struct msgs *mp = NULL;
73
74 #ifdef LOCALE
75 setlocale(LC_ALL, "");
76 #endif
77 invo_name = r1bindex (argv[0], '/');
78
79 /* read user profile/context */
80 context_read();
81
82 if (!mh_strcasecmp (invo_name, "next")) {
83 mode = NEXT;
84 } else if (!mh_strcasecmp (invo_name, "prev")) {
85 mode = PREV;
86 }
87 arguments = getarguments (invo_name, argc, argv, 1);
88 argp = arguments;
89
90 while ((cp = *argp++)) {
91 if (*cp == '-') {
92 switch (smatch (++cp, switches)) {
93 case AMBIGSW:
94 ambigsw (cp, switches);
95 done (1);
96 case UNKWNSW:
97 case NPROGSW:
98 vec[vecp++] = --cp;
99 continue;
100
101 case HELPSW:
102 snprintf (buf, sizeof(buf),
103 "%s [+folder] %s[switches] [switches for showproc]",
104 invo_name, mode == SHOW ? "[msgs] ": "");
105 print_help (buf, switches, 1);
106 done (1);
107 case VERSIONSW:
108 print_version(invo_name);
109 done (1);
110
111 case DRFTSW:
112 if (file)
113 adios (NULL, "only one file at a time!");
114 draftsw++;
115 if (mode == SHOW)
116 continue;
117 usage:
118 adios (NULL,
119 "usage: %s [+folder] [switches] [switches for showproc]",
120 invo_name);
121 case FILESW:
122 if (mode != SHOW)
123 goto usage;
124 if (draftsw || file)
125 adios (NULL, "only one file at a time!");
126 if (!(cp = *argp++) || *cp == '-')
127 adios (NULL, "missing argument to %s", argp[-2]);
128 file = path (cp, TFILE);
129 continue;
130
131 case HEADSW:
132 headersw++;
133 continue;
134 case NHEADSW:
135 headersw = 0;
136 continue;
137
138 case FORMSW:
139 vec[vecp++] = --cp;
140 if (!(cp = *argp++) || *cp == '-')
141 adios (NULL, "missing argument to %s", argp[-2]);
142 vec[vecp++] = getcpy (etcpath(cp));
143 continue;
144
145 case PROGSW:
146 case LENSW:
147 case WIDTHSW:
148 vec[vecp++] = --cp;
149 if (!(cp = *argp++) || *cp == '-')
150 adios (NULL, "missing argument to %s", argp[-2]);
151 vec[vecp++] = cp;
152 continue;
153
154 case SHOWSW:
155 if (!(showproc = *argp++) || *showproc == '-')
156 adios (NULL, "missing argument to %s", argp[-2]);
157 nshow = 0;
158 continue;
159 case NSHOWSW:
160 nshow++;
161 continue;
162
163 case SHOWMIMESW:
164 if (!(showmimeproc = *argp++) || *showmimeproc == '-')
165 adios (NULL, "missing argument to %s", argp[-2]);
166 nshow = 0;
167 continue;
168 case CHECKMIMESW:
169 checkmime++;
170 continue;
171 case NOCHECKMIMESW:
172 checkmime = 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 = pluspath (cp);
181 } else {
182 if (mode != SHOW)
183 goto usage;
184 else
185 msgs[msgp++] = cp;
186 }
187 }
188 procp = vecp;
189
190 if (!context_find ("path"))
191 free (path ("./", TFOLDER));
192
193 if (draftsw || file) {
194 if (msgp)
195 adios (NULL, "only one file at a time!");
196 vec[vecp++] = draftsw
197 ? getcpy (m_draft (folder, msgp ? msgs[0] : NULL, 1, &isdf))
198 : file;
199 goto go_to_it;
200 }
201
202 #ifdef WHATNOW
203 if (!msgp && !folder && mode == SHOW && (cp = getenv ("mhdraft")) && *cp) {
204 draftsw++;
205 vec[vecp++] = cp;
206 goto go_to_it;
207 }
208 #endif /* WHATNOW */
209
210 if (!msgp) {
211 switch (mode) {
212 case NEXT:
213 msgs[msgp++] = "next";
214 break;
215 case PREV:
216 msgs[msgp++] = "prev";
217 break;
218 default:
219 msgs[msgp++] = "cur";
220 break;
221 }
222 }
223
224 if (!folder)
225 folder = getfolder (1);
226 maildir = m_maildir (folder);
227
228 if (chdir (maildir) == NOTOK)
229 adios (maildir, "unable to change directory to");
230
231 /* read folder and create message structure */
232 if (!(mp = folder_read (folder)))
233 adios (NULL, "unable to read folder %s", folder);
234
235 /* check for empty folder */
236 if (mp->nummsg == 0)
237 adios (NULL, "no messages in %s", folder);
238
239 /* parse all the message ranges/sequences and set SELECTED */
240 for (msgnum = 0; msgnum < msgp; msgnum++)
241 if (!m_convert (mp, msgs[msgnum]))
242 done (1);
243
244 /*
245 * Set the SELECT_UNSEEN bit for all the SELECTED messages,
246 * since we will use that as a tag to know which messages
247 * to remove from the "unseen" sequence.
248 */
249 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
250 if (is_selected(mp, msgnum))
251 set_unseen (mp, msgnum);
252
253 seq_setprev (mp); /* set the Previous-Sequence */
254 seq_setunseen (mp, 1); /* unset the Unseen-Sequence */
255
256 if (mp->numsel > MAXARGS - 2)
257 adios (NULL, "more than %d messages for show exec", MAXARGS - 2);
258
259 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
260 if (is_selected(mp, msgnum))
261 vec[vecp++] = getcpy (m_name (msgnum));
262
263 seq_setcur (mp, mp->hghsel); /* update current message */
264 seq_save (mp); /* synchronize sequences */
265 context_replace (pfolder, folder); /* update current folder */
266 context_save (); /* save the context file */
267
268 if (headersw && vecp == 2)
269 printf ("(Message %s:%s)\n", folder, vec[1]);
270
271 go_to_it: ;
272 fflush (stdout);
273
274 vec[vecp] = NULL;
275
276 /*
277 * Decide which "proc" to use
278 */
279 mime = 0;
280 if (nshow) {
281 proc = catproc;
282 } else {
283 /* check if any messages are non-text MIME messages */
284 if (checkmime && !getenv ("NOMHNPROC")) {
285 if (!draftsw && !file) {
286 /* loop through selected messages and check for MIME */
287 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
288 if (is_selected (mp, msgnum) && is_nontext (m_name (msgnum))) {
289 mime = 1;
290 break;
291 }
292 } else {
293 /* check the file or draft for MIME */
294 if (is_nontext (vec[vecp - 1]))
295 mime = 1;
296 }
297 }
298
299 /* Set the "proc" */
300 if (mime)
301 proc = showmimeproc;
302 else
303 proc = showproc;
304 }
305
306 if (folder && !draftsw && !file)
307 m_putenv ("mhfolder", folder);
308
309 /*
310 * For backward compatibility, if the "proc" is mhn,
311 * then add "-show" option. Add "-file" if showing
312 * file or draft.
313 */
314 if (strcmp (r1bindex (proc, '/'), "mhn") == 0) {
315 if (draftsw || file) {
316 vec[vecp] = vec[vecp - 1];
317 vec[vecp - 1] = "-file";
318 vecp++;
319 }
320 vec[vecp++] = "-show";
321 vec[vecp] = NULL;
322 }
323
324 /* If the "proc" is "mhshow", add "-file" if showing file or draft.
325 */
326 if (strcmp (r1bindex (proc, '/'), "mhshow") == 0 && (draftsw || file) ) {
327 vec[vecp] = vec[vecp - 1];
328 vec[vecp - 1] = "-file";
329 vec[++vecp] = NULL;
330 }
331
332 /*
333 * If "proc" is mhl, then run it internally
334 * rather than exec'ing it.
335 */
336 if (strcmp (r1bindex (proc, '/'), "mhl") == 0) {
337 vec[0] = "mhl";
338 mhl (vecp, vec);
339 done (0);
340 }
341
342 /*
343 * If you are not using a nmh command as your "proc", then
344 * add the path to the message names. Currently, we are just
345 * checking for mhn here, since we've already taken care of mhl.
346 */
347 if (!strcmp (r1bindex (proc, '/'), "mhl")
348 && !draftsw
349 && !file
350 && chdir (maildir = concat (m_maildir (""), "/", NULL)) != NOTOK) {
351 mp->foldpath = concat (mp->foldpath, "/", NULL);
352 cp = ssequal (maildir, mp->foldpath)
353 ? mp->foldpath + strlen (maildir)
354 : mp->foldpath;
355 for (msgnum = procp; msgnum < vecp; msgnum++)
356 vec[msgnum] = concat (cp, vec[msgnum], NULL);
357 }
358
359 vec[0] = r1bindex (proc, '/');
360 execvp (proc, vec);
361 adios (proc, "unable to exec");
362 return 0; /* dead code to satisfy the compiler */
363 }
364
365 /*
366 * Cheat: we are loaded with adrparse, which wants a routine called
367 * OfficialName(). We call adrparse:getm() with the correct arguments
368 * to prevent OfficialName() from being called. Hence, the following
369 * is to keep the loader happy.
370 */
371
372 char *
373 OfficialName (char *name)
374 {
375 return name;
376 }
377
378
379 /*
380 * Check if a message or file contains any non-text parts
381 */
382 static int
383 is_nontext (char *msgnam)
384 {
385 int result, state;
386 unsigned char *bp, *dp;
387 char *cp;
388 char buf[BUFSIZ], name[NAMESZ];
389 FILE *fp;
390
391 if ((fp = fopen (msgnam, "r")) == NULL)
392 return 0;
393
394 for (state = FLD;;) {
395 switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
396 case FLD:
397 case FLDPLUS:
398 case FLDEOF:
399 /*
400 * Check Content-Type field
401 */
402 if (!mh_strcasecmp (name, TYPE_FIELD)) {
403 int passno;
404 char c;
405
406 cp = add (buf, NULL);
407 while (state == FLDPLUS) {
408 state = m_getfld (state, name, buf, sizeof(buf), fp);
409 cp = add (buf, cp);
410 }
411 bp = cp;
412 passno = 1;
413
414 again:
415 for (; isspace (*bp); bp++)
416 continue;
417 if (*bp == '(') {
418 int i;
419
420 for (bp++, i = 0;;) {
421 switch (*bp++) {
422 case '\0':
423 invalid:
424 result = 0;
425 goto out;
426 case '\\':
427 if (*bp++ == '\0')
428 goto invalid;
429 continue;
430 case '(':
431 i++;
432 /* and fall... */
433 default:
434 continue;
435 case ')':
436 if (--i < 0)
437 break;
438 continue;
439 }
440 break;
441 }
442 }
443 if (passno == 2) {
444 if (*bp != '/')
445 goto invalid;
446 bp++;
447 passno = 3;
448 goto again;
449 }
450 for (dp = bp; istoken (*dp); dp++)
451 continue;
452 c = *dp;
453 *dp = '\0';
454 if (!*bp)
455 goto invalid;
456 if (passno > 1) {
457 if ((result = (mh_strcasecmp (bp, "plain") != 0)))
458 goto out;
459 *dp = c;
460 for (dp++; isspace (*dp); dp++)
461 continue;
462 if (*dp) {
463 if ((result = !uprf (dp, "charset")))
464 goto out;
465 dp += sizeof("charset") - 1;
466 while (isspace (*dp))
467 dp++;
468 if (*dp++ != '=')
469 goto invalid;
470 while (isspace (*dp))
471 dp++;
472 if (*dp == '"') {
473 if ((bp = strchr(++dp, '"')))
474 *bp = '\0';
475 } else {
476 for (bp = dp; *bp; bp++)
477 if (!istoken (*bp)) {
478 *bp = '\0';
479 break;
480 }
481 }
482 } else {
483 /* Default character set */
484 dp = "US-ASCII";
485 }
486 /* Check the character set */
487 result = !check_charset (dp, strlen (dp));
488 } else {
489 if (!(result = (mh_strcasecmp (bp, "text") != 0))) {
490 *dp = c;
491 bp = dp;
492 passno = 2;
493 goto again;
494 }
495 }
496 out:
497 free (cp);
498 if (result) {
499 fclose (fp);
500 return result;
501 }
502 break;
503 }
504
505 /*
506 * Check Content-Transfer-Encoding field
507 */
508 if (!mh_strcasecmp (name, ENCODING_FIELD)) {
509 cp = add (buf, NULL);
510 while (state == FLDPLUS) {
511 state = m_getfld (state, name, buf, sizeof(buf), fp);
512 cp = add (buf, cp);
513 }
514 for (bp = cp; isspace (*bp); bp++)
515 continue;
516 for (dp = bp; istoken (*dp); dp++)
517 continue;
518 *dp = '\0';
519 result = (mh_strcasecmp (bp, "7bit")
520 && mh_strcasecmp (bp, "8bit")
521 && mh_strcasecmp (bp, "binary"));
522
523 free (cp);
524 if (result) {
525 fclose (fp);
526 return result;
527 }
528 break;
529 }
530
531 /*
532 * Just skip the rest of this header
533 * field and go to next one.
534 */
535 while (state == FLDPLUS)
536 state = m_getfld (state, name, buf, sizeof(buf), fp);
537 break;
538
539 /*
540 * We've passed the message header,
541 * so message is just text.
542 */
543 default:
544 fclose (fp);
545 return 0;
546 }
547 }
548 }