]> diplodocus.org Git - nmh/blob - uip/show.c
Quiet lock warning.
[nmh] / uip / show.c
1
2 /*
3 * show.c -- show/list messages
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/mime.h>
14
15 static struct swit switches[] = {
16 #define CHECKMIMESW 0
17 { "checkmime", 0 },
18 #define NOCHECKMIMESW 1
19 { "nocheckmime", 0 },
20 #define HEADSW 2
21 { "header", 0 },
22 #define NHEADSW 3
23 { "noheader", 0 },
24 #define FORMSW 4
25 { "form formfile", 0 },
26 #define PROGSW 5
27 { "moreproc program", 0 },
28 #define NPROGSW 6
29 { "nomoreproc", 0 },
30 #define LENSW 7
31 { "length lines", 0 },
32 #define WIDTHSW 8
33 { "width columns", 0 },
34 #define SHOWSW 9
35 { "showproc program", 0 },
36 #define SHOWMIMESW 10
37 { "showmimeproc program", 0 },
38 #define NSHOWSW 11
39 { "noshowproc", 0 },
40 #define DRFTSW 12
41 { "draft", 0 },
42 #define FILESW 13
43 { "file file", -4 }, /* interface from showfile */
44 #define VERSIONSW 14
45 { "version", 0 },
46 #define HELPSW 15
47 { "help", 0 },
48 { NULL, 0 }
49 };
50
51 /*
52 * static prototypes
53 */
54 static int is_nontext(char *);
55
56 /* prototype from mhlsbr.c */
57 int mhl (int, char **);
58
59 #define SHOW 0
60 #define NEXT 1
61 #define PREV 2
62
63
64 int
65 main (int argc, char **argv)
66 {
67 int draftsw = 0, headersw = 1, msgp = 0;
68 int nshow = 0, checkmime = 1, mime;
69 int vecp = 1, procp = 1, isdf = 0, mode = SHOW, msgnum;
70 char *cp, *maildir, *file = NULL, *folder = NULL, *proc;
71 char buf[BUFSIZ], **argp, **arguments;
72 char *msgs[MAXARGS], *vec[MAXARGS];
73 struct msgs *mp;
74
75 #ifdef LOCALE
76 setlocale(LC_ALL, "");
77 #endif
78 invo_name = r1bindex (argv[0], '/');
79
80 /* read user profile/context */
81 context_read();
82
83 if (!strcasecmp (invo_name, "next")) {
84 mode = NEXT;
85 } else if (!strcasecmp (invo_name, "prev")) {
86 mode = PREV;
87 }
88 arguments = getarguments (invo_name, argc, argv, 1);
89 argp = arguments;
90
91 while ((cp = *argp++)) {
92 if (*cp == '-') {
93 switch (smatch (++cp, switches)) {
94 case AMBIGSW:
95 ambigsw (cp, switches);
96 done (1);
97 case UNKWNSW:
98 case NPROGSW:
99 vec[vecp++] = --cp;
100 continue;
101
102 case HELPSW:
103 snprintf (buf, sizeof(buf),
104 "%s [+folder] %s[switches] [switches for showproc]",
105 invo_name, mode == SHOW ? "[msgs] ": "");
106 print_help (buf, switches, 1);
107 done (1);
108 case VERSIONSW:
109 print_version(invo_name);
110 done (1);
111
112 case DRFTSW:
113 if (file)
114 adios (NULL, "only one file at a time!");
115 draftsw++;
116 if (mode == SHOW)
117 continue;
118 usage:
119 adios (NULL,
120 "usage: %s [+folder] [switches] [switches for showproc]",
121 invo_name);
122 case FILESW:
123 if (mode != SHOW)
124 goto usage;
125 if (draftsw || file)
126 adios (NULL, "only one file at a time!");
127 if (!(cp = *argp++) || *cp == '-')
128 adios (NULL, "missing argument to %s", argp[-2]);
129 file = path (cp, TFILE);
130 continue;
131
132 case HEADSW:
133 headersw++;
134 continue;
135 case NHEADSW:
136 headersw = 0;
137 continue;
138
139 case FORMSW:
140 vec[vecp++] = --cp;
141 if (!(cp = *argp++) || *cp == '-')
142 adios (NULL, "missing argument to %s", argp[-2]);
143 vec[vecp++] = getcpy (etcpath(cp));
144 continue;
145
146 case PROGSW:
147 case LENSW:
148 case WIDTHSW:
149 vec[vecp++] = --cp;
150 if (!(cp = *argp++) || *cp == '-')
151 adios (NULL, "missing argument to %s", argp[-2]);
152 vec[vecp++] = cp;
153 continue;
154
155 case SHOWSW:
156 if (!(showproc = *argp++) || *showproc == '-')
157 adios (NULL, "missing argument to %s", argp[-2]);
158 nshow = 0;
159 continue;
160 case NSHOWSW:
161 nshow++;
162 continue;
163
164 case SHOWMIMESW:
165 if (!(showmimeproc = *argp++) || *showmimeproc == '-')
166 adios (NULL, "missing argument to %s", argp[-2]);
167 nshow = 0;
168 continue;
169 case CHECKMIMESW:
170 checkmime++;
171 continue;
172 case NOCHECKMIMESW:
173 checkmime = 0;
174 continue;
175 }
176 }
177 if (*cp == '+' || *cp == '@') {
178 if (folder)
179 adios (NULL, "only one folder at a time!");
180 else
181 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
182 } else {
183 if (mode != SHOW)
184 goto usage;
185 else
186 msgs[msgp++] = cp;
187 }
188 }
189 procp = vecp;
190
191 if (!context_find ("path"))
192 free (path ("./", TFOLDER));
193
194 if (draftsw || file) {
195 if (msgp)
196 adios (NULL, "only one file at a time!");
197 vec[vecp++] = draftsw
198 ? getcpy (m_draft (folder, msgp ? msgs[0] : NULL, 1, &isdf))
199 : file;
200 goto go_to_it;
201 }
202
203 #ifdef WHATNOW
204 if (!msgp && !folder && mode == SHOW && (cp = getenv ("mhdraft")) && *cp) {
205 draftsw++;
206 vec[vecp++] = cp;
207 goto go_to_it;
208 }
209 #endif /* WHATNOW */
210
211 if (!msgp) {
212 switch (mode) {
213 case NEXT:
214 msgs[msgp++] = "next";
215 break;
216 case PREV:
217 msgs[msgp++] = "prev";
218 break;
219 default:
220 msgs[msgp++] = "cur";
221 break;
222 }
223 }
224
225 if (!folder)
226 folder = getfolder (1);
227 maildir = m_maildir (folder);
228
229 if (chdir (maildir) == NOTOK)
230 adios (maildir, "unable to change directory to");
231
232 /* read folder and create message structure */
233 if (!(mp = folder_read (folder)))
234 adios (NULL, "unable to read folder %s", folder);
235
236 /* check for empty folder */
237 if (mp->nummsg == 0)
238 adios (NULL, "no messages in %s", folder);
239
240 /* parse all the message ranges/sequences and set SELECTED */
241 for (msgnum = 0; msgnum < msgp; msgnum++)
242 if (!m_convert (mp, msgs[msgnum]))
243 done (1);
244
245 /*
246 * Set the SELECT_UNSEEN bit for all the SELECTED messages,
247 * since we will use that as a tag to know which messages
248 * to remove from the "unseen" sequence.
249 */
250 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
251 if (is_selected(mp, msgnum))
252 set_unseen (mp, msgnum);
253
254 seq_setprev (mp); /* set the Previous-Sequence */
255 seq_setunseen (mp, 1); /* unset the Unseen-Sequence */
256
257 if (mp->numsel > MAXARGS - 2)
258 adios (NULL, "more than %d messages for show exec", MAXARGS - 2);
259
260 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
261 if (is_selected(mp, msgnum))
262 vec[vecp++] = getcpy (m_name (msgnum));
263
264 seq_setcur (mp, mp->hghsel); /* update current message */
265 seq_save (mp); /* synchronize sequences */
266 context_replace (pfolder, folder); /* update current folder */
267 context_save (); /* save the context file */
268
269 if (headersw && vecp == 2)
270 printf ("(Message %s:%s)\n", folder, vec[1]);
271
272 go_to_it: ;
273 fflush (stdout);
274
275 vec[vecp] = NULL;
276
277 /*
278 * Decide which "proc" to use
279 */
280 mime = 0;
281 if (nshow) {
282 proc = catproc;
283 } else {
284 /* check if any messages are non-text MIME messages */
285 if (checkmime && !getenv ("NOMHNPROC")) {
286 if (!draftsw && !file) {
287 /* loop through selected messages and check for MIME */
288 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
289 if (is_selected (mp, msgnum) && is_nontext (m_name (msgnum))) {
290 mime = 1;
291 break;
292 }
293 } else {
294 /* check the file or draft for MIME */
295 if (is_nontext (vec[vecp - 1]))
296 mime = 1;
297 }
298 }
299
300 /* Set the "proc" */
301 if (mime)
302 proc = showmimeproc;
303 else
304 proc = showproc;
305 }
306
307 if (folder && !draftsw && !file)
308 m_putenv ("mhfolder", folder);
309
310 /*
311 * For backward compatibility, if the "proc" is mhn,
312 * then add "-show" option. Add "-file" if showing
313 * file or draft.
314 */
315 if (strcmp (r1bindex (proc, '/'), "mhn") == 0) {
316 if (draftsw || file) {
317 vec[vecp] = vec[vecp - 1];
318 vec[vecp - 1] = "-file";
319 vecp++;
320 }
321 vec[vecp++] = "-show";
322 vec[vecp] = NULL;
323 }
324
325 /* If the "proc" is "mhshow", add "-file" if showing file or draft.
326 */
327 if (strcmp (r1bindex (proc, '/'), "mhshow") == 0 && (draftsw || file) ) {
328 vec[vecp] = vec[vecp - 1];
329 vec[vecp - 1] = "-file";
330 vec[++vecp] = NULL;
331 }
332
333 /*
334 * If "proc" is mhl, then run it internally
335 * rather than exec'ing it.
336 */
337 if (strcmp (r1bindex (proc, '/'), "mhl") == 0) {
338 vec[0] = "mhl";
339 mhl (vecp, vec);
340 done (0);
341 }
342
343 /*
344 * If you are not using a nmh command as your "proc", then
345 * add the path to the message names. Currently, we are just
346 * checking for mhn here, since we've already taken care of mhl.
347 */
348 if (!strcmp (r1bindex (proc, '/'), "mhl")
349 && !draftsw
350 && !file
351 && chdir (maildir = concat (m_maildir (""), "/", NULL)) != NOTOK) {
352 mp->foldpath = concat (mp->foldpath, "/", NULL);
353 cp = ssequal (maildir, mp->foldpath)
354 ? mp->foldpath + strlen (maildir)
355 : mp->foldpath;
356 for (msgnum = procp; msgnum < vecp; msgnum++)
357 vec[msgnum] = concat (cp, vec[msgnum], NULL);
358 }
359
360 vec[0] = r1bindex (proc, '/');
361 execvp (proc, vec);
362 adios (proc, "unable to exec");
363 return 0; /* dead code to satisfy the compiler */
364 }
365
366 /*
367 * Cheat: we are loaded with adrparse, which wants a routine called
368 * OfficialName(). We call adrparse:getm() with the correct arguments
369 * to prevent OfficialName() from being called. Hence, the following
370 * is to keep the loader happy.
371 */
372
373 char *
374 OfficialName (char *name)
375 {
376 return name;
377 }
378
379
380 /*
381 * Check if a message or file contains any non-text parts
382 */
383 static int
384 is_nontext (char *msgnam)
385 {
386 int result, state;
387 char *bp, *cp, *dp;
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 (!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 = (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 = (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 (!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 = (strcasecmp (bp, "7bit")
520 && strcasecmp (bp, "8bit")
521 && 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 }