]> diplodocus.org Git - nmh/blob - uip/mhshowsbr.c
3d2d8eb0db647e5e1d04679c311ae79bec317255
[nmh] / uip / mhshowsbr.c
1
2 /*
3 * mhshowsbr.c -- routines to display the contents of MIME 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 <fcntl.h>
12 #include <h/signals.h>
13 #include <h/md5.h>
14 #include <errno.h>
15 #include <setjmp.h>
16 #include <signal.h>
17 #include <h/mts.h>
18 #include <h/tws.h>
19 #include <h/mime.h>
20 #include <h/mhparse.h>
21 #include <h/utils.h>
22
23 extern int debugsw;
24
25 int pausesw = 1;
26 int serialsw = 0;
27 int nolist = 0;
28
29 char *progsw = NULL;
30
31 /* flags for moreproc/header display */
32 int nomore = 0;
33 char *formsw = NULL;
34
35 pid_t xpid = 0;
36
37 static sigjmp_buf intrenv;
38
39
40 /* mhmisc.c */
41 int part_ok (CT, int);
42 int type_ok (CT, int);
43 void content_error (char *, CT, char *, ...);
44 void flush_errors (void);
45
46 /* mhlistsbr.c */
47 int list_switch (CT, int, int, int, int);
48 int list_content (CT, int, int, int, int);
49
50 /*
51 * prototypes
52 */
53 void show_all_messages (CT *);
54 int show_content_aux (CT, int, int, char *, char *);
55
56 /*
57 * static prototypes
58 */
59 static void show_single_message (CT, char *);
60 static void DisplayMsgHeader (CT, char *);
61 static int show_switch (CT, int, int);
62 static int show_content (CT, int, int);
63 static int show_content_aux2 (CT, int, int, char *, char *, int, int, int, int, int);
64 static int show_text (CT, int, int);
65 static int show_multi (CT, int, int);
66 static int show_multi_internal (CT, int, int);
67 static int show_multi_aux (CT, int, int, char *);
68 static int show_message_rfc822 (CT, int, int);
69 static int show_partial (CT, int, int);
70 static int show_external (CT, int, int);
71 static void intrser (int);
72
73
74 /*
75 * Top level entry point to show/display a group of messages
76 */
77
78 void
79 show_all_messages (CT *cts)
80 {
81 CT ct, *ctp;
82
83 /*
84 * If form is not specified, then get default form
85 * for showing headers of MIME messages.
86 */
87 if (!formsw)
88 formsw = getcpy (etcpath ("mhl.headers"));
89
90 /*
91 * If form is "mhl.null", suppress display of header.
92 */
93 if (!strcmp (formsw, "mhl.null"))
94 formsw = NULL;
95
96 for (ctp = cts; *ctp; ctp++) {
97 ct = *ctp;
98
99 /* if top-level type is ok, then display message */
100 if (type_ok (ct, 1))
101 show_single_message (ct, formsw);
102 }
103 }
104
105
106 /*
107 * Entry point to show/display a single message
108 */
109
110 static void
111 show_single_message (CT ct, char *form)
112 {
113 sigset_t set, oset;
114
115 int status;
116
117 /* Allow user executable bit so that temporary directories created by
118 * the viewer (e.g., lynx) are going to be accessible */
119 umask (ct->c_umask & ~(0100));
120
121 /*
122 * If you have a format file, then display
123 * the message headers.
124 */
125 if (form)
126 DisplayMsgHeader(ct, form);
127 else
128 xpid = 0;
129
130 /* Show the body of the message */
131 show_switch (ct, 1, 0);
132
133 if (ct->c_fp) {
134 fclose (ct->c_fp);
135 ct->c_fp = NULL;
136 }
137 if (ct->c_ceclosefnx)
138 (*ct->c_ceclosefnx) (ct);
139
140 /* block a few signals */
141 sigemptyset (&set);
142 sigaddset (&set, SIGHUP);
143 sigaddset (&set, SIGINT);
144 sigaddset (&set, SIGQUIT);
145 sigaddset (&set, SIGTERM);
146 sigprocmask (SIG_BLOCK, &set, &oset);
147
148 while (wait (&status) != NOTOK) {
149 pidcheck (status);
150 continue;
151 }
152
153 /* reset the signal mask */
154 sigprocmask (SIG_SETMASK, &oset, &set);
155
156 xpid = 0;
157 flush_errors ();
158 }
159
160
161 /*
162 * Use the mhlproc to show the header fields
163 */
164
165 static void
166 DisplayMsgHeader (CT ct, char *form)
167 {
168 pid_t child_id;
169 int i, vecp;
170 char **vec;
171 char *file;
172
173 vec = argsplit(mhlproc, &file, &vecp);
174 vec[vecp++] = getcpy("-form");
175 vec[vecp++] = getcpy(form);
176 vec[vecp++] = getcpy("-nobody");
177 vec[vecp++] = getcpy(ct->c_file);
178
179 /*
180 * If we've specified -(no)moreproc,
181 * then just pass that along.
182 */
183 if (nomore) {
184 vec[vecp++] = getcpy("-nomoreproc");
185 } else if (progsw) {
186 vec[vecp++] = getcpy("-moreproc");
187 vec[vecp++] = getcpy(progsw);
188 }
189 vec[vecp] = NULL;
190
191 fflush (stdout);
192
193 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
194 sleep (5);
195
196 switch (child_id) {
197 case NOTOK:
198 adios ("fork", "unable to");
199 /* NOTREACHED */
200
201 case OK:
202 execvp (file, vec);
203 fprintf (stderr, "unable to exec ");
204 perror (mhlproc);
205 _exit (-1);
206 /* NOTREACHED */
207
208 default:
209 xpid = -child_id;
210 break;
211 }
212
213 arglist_free(file, vec);
214 }
215
216
217 /*
218 * Switching routine. Call the correct routine
219 * based on content type.
220 */
221
222 static int
223 show_switch (CT ct, int serial, int alternate)
224 {
225 switch (ct->c_type) {
226 case CT_MULTIPART:
227 return show_multi (ct, serial, alternate);
228 break;
229
230 case CT_MESSAGE:
231 switch (ct->c_subtype) {
232 case MESSAGE_PARTIAL:
233 return show_partial (ct, serial, alternate);
234 break;
235
236 case MESSAGE_EXTERNAL:
237 return show_external (ct, serial, alternate);
238 break;
239
240 case MESSAGE_RFC822:
241 default:
242 return show_message_rfc822 (ct, serial, alternate);
243 break;
244 }
245 break;
246
247 case CT_TEXT:
248 return show_text (ct, serial, alternate);
249 break;
250
251 case CT_AUDIO:
252 case CT_IMAGE:
253 case CT_VIDEO:
254 case CT_APPLICATION:
255 return show_content (ct, serial, alternate);
256 break;
257
258 default:
259 adios (NULL, "unknown content type %d", ct->c_type);
260 break;
261 }
262
263 return 0; /* NOT REACHED */
264 }
265
266
267 /*
268 * Generic method for displaying content
269 */
270
271 static int
272 show_content (CT ct, int serial, int alternate)
273 {
274 char *cp, buffer[BUFSIZ];
275 CI ci = &ct->c_ctinfo;
276
277 /* Check for mhn-show-type/subtype */
278 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
279 invo_name, ci->ci_type, ci->ci_subtype);
280 if ((cp = context_find (buffer)) && *cp != '\0')
281 return show_content_aux (ct, serial, alternate, cp, NULL);
282
283 /* Check for mhn-show-type */
284 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
285 if ((cp = context_find (buffer)) && *cp != '\0')
286 return show_content_aux (ct, serial, alternate, cp, NULL);
287
288 if ((cp = ct->c_showproc))
289 return show_content_aux (ct, serial, alternate, cp, NULL);
290
291 /* complain if we are not a part of a multipart/alternative */
292 if (!alternate)
293 content_error (NULL, ct, "don't know how to display content");
294
295 return NOTOK;
296 }
297
298
299 /*
300 * Parse the display string for displaying generic content
301 */
302
303 int
304 show_content_aux (CT ct, int serial, int alternate, char *cp, char *cracked)
305 {
306 int fd, len, buflen, quoted;
307 int xstdin, xlist, xpause, xtty;
308 char *bp, *pp, *file, buffer[BUFSIZ];
309 CI ci = &ct->c_ctinfo;
310
311 if (!ct->c_ceopenfnx) {
312 if (!alternate)
313 content_error (NULL, ct, "don't know how to decode content");
314
315 return NOTOK;
316 }
317
318 file = NULL;
319 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
320 return NOTOK;
321 if (ct->c_showproc && !strcmp (ct->c_showproc, "true"))
322 return (alternate ? DONE : OK);
323
324 xlist = 0;
325 xpause = 0;
326 xstdin = 0;
327 xtty = 0;
328
329 if (cracked) {
330 strncpy (buffer, cp, sizeof(buffer));
331 goto got_command;
332 }
333
334 /* get buffer ready to go */
335 bp = buffer;
336 buflen = sizeof(buffer) - 1;
337 bp[0] = bp[buflen] = '\0';
338 quoted = 0;
339
340 /* Now parse display string */
341 for ( ; *cp && buflen > 0; cp++) {
342 if (*cp == '%') {
343 pp = bp;
344
345 switch (*++cp) {
346 case 'a':
347 /* insert parameters from Content-Type field */
348 {
349 char **ap, **ep;
350 char *s = "";
351
352 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
353 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
354 len = strlen (bp);
355 bp += len;
356 buflen -= len;
357 s = " ";
358 }
359 }
360 break;
361
362 case 'd':
363 /* insert content description */
364 if (ct->c_descr) {
365 char *s;
366
367 s = trimcpy (ct->c_descr);
368 strncpy (bp, s, buflen);
369 free (s);
370 }
371 break;
372
373 case 'e':
374 /* exclusive execution */
375 xtty = 1;
376 break;
377
378 case 'F':
379 /* %e, %f, and stdin is terminal not content */
380 xstdin = 1;
381 xtty = 1;
382 /* and fall... */
383
384 case 'f':
385 /* insert filename containing content */
386 snprintf (bp, buflen, "'%s'", file);
387 /* since we've quoted the file argument, set things up
388 * to look past it, to avoid problems with the quoting
389 * logic below. (I know, I should figure out what's
390 * broken with the quoting logic, but..)
391 */
392 len = strlen(bp);
393 buflen -= len;
394 bp += len;
395 pp = bp;
396 break;
397
398 case 'p':
399 /* %l, and pause prior to displaying content */
400 xpause = pausesw;
401 /* and fall... */
402
403 case 'l':
404 /* display listing prior to displaying content */
405 xlist = !nolist;
406 break;
407
408 case 's':
409 /* insert subtype of content */
410 strncpy (bp, ci->ci_subtype, buflen);
411 break;
412
413 case '%':
414 /* insert character % */
415 goto raw;
416
417 default:
418 *bp++ = *--cp;
419 *bp = '\0';
420 buflen--;
421 continue;
422 }
423 len = strlen (bp);
424 bp += len;
425 buflen -= len;
426
427 /* Did we actually insert something? */
428 if (bp != pp) {
429 /* Insert single quote if not inside quotes already */
430 if (!quoted && buflen) {
431 len = strlen (pp);
432 memmove (pp + 1, pp, len);
433 *pp++ = '\'';
434 buflen--;
435 bp++;
436 }
437 /* Escape existing quotes */
438 while ((pp = strchr (pp, '\'')) && buflen > 3) {
439 len = strlen (pp++);
440 memmove (pp + 3, pp, len);
441 *pp++ = '\\';
442 *pp++ = '\'';
443 *pp++ = '\'';
444 buflen -= 3;
445 bp += 3;
446 }
447 /* If pp is still set, that means we ran out of space. */
448 if (pp)
449 buflen = 0;
450 if (!quoted && buflen) {
451 *bp++ = '\'';
452 *bp = '\0';
453 buflen--;
454 }
455 }
456 } else {
457 raw:
458 *bp++ = *cp;
459 *bp = '\0';
460 buflen--;
461
462 if (*cp == '\'')
463 quoted = !quoted;
464 }
465 }
466
467 if (buflen <= 0 ||
468 (ct->c_termproc && (size_t) buflen <= strlen(ct->c_termproc))) {
469 /* content_error would provide a more useful error message
470 * here, except that if we got overrun, it probably would
471 * too.
472 */
473 fprintf(stderr, "Buffer overflow constructing show command!\n");
474 return NOTOK;
475 }
476
477 /* use charset string to modify display method */
478 if (ct->c_termproc) {
479 char term[BUFSIZ];
480
481 strncpy (term, buffer, sizeof(term));
482 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
483 }
484
485 got_command:
486 return show_content_aux2 (ct, serial, alternate, cracked, buffer,
487 fd, xlist, xpause, xstdin, xtty);
488 }
489
490
491 /*
492 * Routine to actually display the content
493 */
494
495 static int
496 show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer,
497 int fd, int xlist, int xpause, int xstdin, int xtty)
498 {
499 pid_t child_id;
500 int i;
501 char *vec[4], exec[BUFSIZ + sizeof "exec "];
502
503 if (debugsw || cracked) {
504 fflush (stdout);
505
506 fprintf (stderr, "%s msg %s", cracked ? "storing" : "show",
507 ct->c_file);
508 if (ct->c_partno)
509 fprintf (stderr, " part %s", ct->c_partno);
510 if (cracked)
511 fprintf (stderr, " using command (cd %s; %s)\n", cracked, buffer);
512 else
513 fprintf (stderr, " using command %s\n", buffer);
514 }
515
516 if (xpid < 0 || (xtty && xpid)) {
517 if (xpid < 0)
518 xpid = -xpid;
519 pidcheck(pidwait (xpid, NOTOK));
520 xpid = 0;
521 }
522
523 if (xlist) {
524 char prompt[BUFSIZ];
525
526 if (ct->c_type == CT_MULTIPART)
527 list_content (ct, -1, 1, 0, 0);
528 else
529 list_switch (ct, -1, 1, 0, 0);
530
531 if (xpause && isatty (fileno (stdout))) {
532 int intr;
533 SIGNAL_HANDLER istat;
534
535 if (SOprintf ("Press <return> to show content..."))
536 printf ("Press <return> to show content...");
537
538 istat = SIGNAL (SIGINT, intrser);
539 if ((intr = sigsetjmp (intrenv, 1)) == OK) {
540 fflush (stdout);
541 prompt[0] = 0;
542 read (fileno (stdout), prompt, sizeof(prompt));
543 }
544 SIGNAL (SIGINT, istat);
545 if (intr != OK || prompt[0] == 'n') {
546 (*ct->c_ceclosefnx) (ct);
547 return (alternate ? DONE : NOTOK);
548 }
549 if (prompt[0] == 'q') done(OK);
550 }
551 }
552
553 snprintf (exec, sizeof(exec), "exec %s", buffer);
554
555 vec[0] = "/bin/sh";
556 vec[1] = "-c";
557 vec[2] = exec;
558 vec[3] = NULL;
559
560 fflush (stdout);
561
562 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
563 sleep (5);
564 switch (child_id) {
565 case NOTOK:
566 advise ("fork", "unable to");
567 (*ct->c_ceclosefnx) (ct);
568 return NOTOK;
569
570 case OK:
571 if (cracked)
572 chdir (cracked);
573 if (!xstdin)
574 dup2 (fd, 0);
575 close (fd);
576 execvp ("/bin/sh", vec);
577 fprintf (stderr, "unable to exec ");
578 perror ("/bin/sh");
579 _exit (-1);
580 /* NOTREACHED */
581
582 default:
583 if (!serial) {
584 ct->c_pid = child_id;
585 if (xtty)
586 xpid = child_id;
587 } else {
588 pidcheck (pidXwait (child_id, NULL));
589 }
590
591 if (fd != NOTOK)
592 (*ct->c_ceclosefnx) (ct);
593 return (alternate ? DONE : OK);
594 }
595 }
596
597
598 /*
599 * show content of type "text"
600 */
601
602 static int
603 show_text (CT ct, int serial, int alternate)
604 {
605 char *cp, buffer[BUFSIZ];
606 CI ci = &ct->c_ctinfo;
607
608 /* Check for mhn-show-type/subtype */
609 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
610 invo_name, ci->ci_type, ci->ci_subtype);
611 if ((cp = context_find (buffer)) && *cp != '\0')
612 return show_content_aux (ct, serial, alternate, cp, NULL);
613
614 /* Check for mhn-show-type */
615 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
616 if ((cp = context_find (buffer)) && *cp != '\0')
617 return show_content_aux (ct, serial, alternate, cp, NULL);
618
619 /*
620 * Use default method if content is text/plain, or if
621 * if it is not a text part of a multipart/alternative
622 */
623 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
624 snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
625 moreproc && *moreproc ? moreproc : "more");
626 cp = (ct->c_showproc = add (buffer, NULL));
627 return show_content_aux (ct, serial, alternate, cp, NULL);
628 }
629
630 return NOTOK;
631 }
632
633
634 /*
635 * show message body of type "multipart"
636 */
637
638 static int
639 show_multi (CT ct, int serial, int alternate)
640 {
641 char *cp, buffer[BUFSIZ];
642 CI ci = &ct->c_ctinfo;
643
644 /* Check for mhn-show-type/subtype */
645 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
646 invo_name, ci->ci_type, ci->ci_subtype);
647 if ((cp = context_find (buffer)) && *cp != '\0')
648 return show_multi_aux (ct, serial, alternate, cp);
649
650 /* Check for mhn-show-type */
651 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
652 if ((cp = context_find (buffer)) && *cp != '\0')
653 return show_multi_aux (ct, serial, alternate, cp);
654
655 if ((cp = ct->c_showproc))
656 return show_multi_aux (ct, serial, alternate, cp);
657
658 /*
659 * Use default method to display this multipart content. Even
660 * unknown types are displayable, since they're treated as mixed
661 * per RFC 2046.
662 */
663 return show_multi_internal (ct, serial, alternate);
664 }
665
666
667 /*
668 * show message body of subtypes of multipart that
669 * we understand directly (mixed, alternate, etc...)
670 */
671
672 static int
673 show_multi_internal (CT ct, int serial, int alternate)
674 {
675 int alternating, nowalternate, nowserial, result;
676 struct multipart *m = (struct multipart *) ct->c_ctparams;
677 struct part *part;
678 CT p;
679 sigset_t set, oset;
680
681 alternating = 0;
682 nowalternate = alternate;
683
684 if (ct->c_subtype == MULTI_PARALLEL) {
685 nowserial = serialsw;
686 } else if (ct->c_subtype == MULTI_ALTERNATE) {
687 nowalternate = 1;
688 alternating = 1;
689 nowserial = serial;
690 } else {
691 /*
692 * multipart/mixed
693 * mutlipart/digest
694 * unknown subtypes of multipart (treat as mixed per rfc2046)
695 */
696 nowserial = serial;
697 }
698
699 /* block a few signals */
700 if (!nowserial) {
701 sigemptyset (&set);
702 sigaddset (&set, SIGHUP);
703 sigaddset (&set, SIGINT);
704 sigaddset (&set, SIGQUIT);
705 sigaddset (&set, SIGTERM);
706 sigprocmask (SIG_BLOCK, &set, &oset);
707 }
708
709 /*
710 * alternate -> we are a part inside an multipart/alternative
711 * alternating -> we are a multipart/alternative
712 */
713
714 result = alternate ? NOTOK : OK;
715
716 for (part = m->mp_parts; part; part = part->mp_next) {
717 p = part->mp_part;
718
719 if (part_ok (p, 1) && type_ok (p, 1)) {
720 int inneresult;
721
722 inneresult = show_switch (p, nowserial, nowalternate);
723 switch (inneresult) {
724 case NOTOK:
725 if (alternate && !alternating) {
726 result = NOTOK;
727 goto out;
728 }
729 continue;
730
731 case OK:
732 case DONE:
733 if (alternating) {
734 result = DONE;
735 break;
736 }
737 if (alternate) {
738 alternate = nowalternate = 0;
739 if (result == NOTOK)
740 result = inneresult;
741 }
742 continue;
743 }
744 break;
745 }
746 }
747
748 if (alternating && !part) {
749 if (!alternate)
750 content_error (NULL, ct, "don't know how to display any of the contents");
751 result = NOTOK;
752 goto out;
753 }
754
755 if (serial && !nowserial) {
756 pid_t pid;
757 int kids;
758 int status;
759
760 kids = 0;
761 for (part = m->mp_parts; part; part = part->mp_next) {
762 p = part->mp_part;
763
764 if (p->c_pid > OK) {
765 if (kill (p->c_pid, 0) == NOTOK)
766 p->c_pid = 0;
767 else
768 kids++;
769 }
770 }
771
772 while (kids > 0 && (pid = wait (&status)) != NOTOK) {
773 pidcheck (status);
774
775 for (part = m->mp_parts; part; part = part->mp_next) {
776 p = part->mp_part;
777
778 if (xpid == pid)
779 xpid = 0;
780 if (p->c_pid == pid) {
781 p->c_pid = 0;
782 kids--;
783 break;
784 }
785 }
786 }
787 }
788
789 out:
790 if (!nowserial) {
791 /* reset the signal mask */
792 sigprocmask (SIG_SETMASK, &oset, &set);
793 }
794
795 return result;
796 }
797
798
799 /*
800 * Parse display string for multipart content
801 * and use external program to display it.
802 */
803
804 static int
805 show_multi_aux (CT ct, int serial, int alternate, char *cp)
806 {
807 int len, buflen, quoted;
808 int xlist, xpause, xtty;
809 char *bp, *pp, *file, buffer[BUFSIZ];
810 struct multipart *m = (struct multipart *) ct->c_ctparams;
811 struct part *part;
812 CI ci = &ct->c_ctinfo;
813 CT p;
814
815 for (part = m->mp_parts; part; part = part->mp_next) {
816 p = part->mp_part;
817
818 if (!p->c_ceopenfnx) {
819 if (!alternate)
820 content_error (NULL, p, "don't know how to decode content");
821 return NOTOK;
822 }
823
824 if (p->c_storage == NULL) {
825 file = NULL;
826 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
827 return NOTOK;
828
829 /* I'm not sure if this is necessary? */
830 p->c_storage = add (file, NULL);
831
832 if (p->c_showproc && !strcmp (p->c_showproc, "true"))
833 return (alternate ? DONE : OK);
834 (*p->c_ceclosefnx) (p);
835 }
836 }
837
838 xlist = 0;
839 xpause = 0;
840 xtty = 0;
841
842 /* get buffer ready to go */
843 bp = buffer;
844 buflen = sizeof(buffer) - 1;
845 bp[0] = bp[buflen] = '\0';
846 quoted = 0;
847
848 /* Now parse display string */
849 for ( ; *cp && buflen > 0; cp++) {
850 if (*cp == '%') {
851 pp = bp;
852 switch (*++cp) {
853 case 'a':
854 /* insert parameters from Content-Type field */
855 {
856 char **ap, **ep;
857 char *s = "";
858
859 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
860 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
861 len = strlen (bp);
862 bp += len;
863 buflen -= len;
864 s = " ";
865 }
866 }
867 break;
868
869 case 'd':
870 /* insert content description */
871 if (ct->c_descr) {
872 char *s;
873
874 s = trimcpy (ct->c_descr);
875 strncpy (bp, s, buflen);
876 free (s);
877 }
878 break;
879
880 case 'e':
881 /* exclusive execution */
882 xtty = 1;
883 break;
884
885 case 'F':
886 /* %e and %f */
887 xtty = 1;
888 /* and fall... */
889
890 case 'f':
891 /* insert filename(s) containing content */
892 {
893 char *s = "";
894
895 for (part = m->mp_parts; part; part = part->mp_next) {
896 p = part->mp_part;
897
898 snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
899 len = strlen (bp);
900 bp += len;
901 buflen -= len;
902 s = " ";
903 }
904 /* set our starting pointer back to bp, to avoid
905 * requoting the filenames we just added
906 */
907 pp = bp;
908 }
909 break;
910
911 case 'p':
912 /* %l, and pause prior to displaying content */
913 xpause = pausesw;
914 /* and fall... */
915
916 case 'l':
917 /* display listing prior to displaying content */
918 xlist = !nolist;
919 break;
920
921 case 's':
922 /* insert subtype of content */
923 strncpy (bp, ci->ci_subtype, buflen);
924 break;
925
926 case '%':
927 /* insert character % */
928 goto raw;
929
930 default:
931 *bp++ = *--cp;
932 *bp = '\0';
933 buflen--;
934 continue;
935 }
936 len = strlen (bp);
937 bp += len;
938 buflen -= len;
939
940 /* Did we actually insert something? */
941 if (bp != pp) {
942 /* Insert single quote if not inside quotes already */
943 if (!quoted && buflen) {
944 len = strlen (pp);
945 memmove (pp + 1, pp, len);
946 *pp++ = '\'';
947 buflen--;
948 bp++;
949 }
950 /* Escape existing quotes */
951 while ((pp = strchr (pp, '\'')) && buflen > 3) {
952 len = strlen (pp++);
953 memmove (pp + 3, pp, len);
954 *pp++ = '\\';
955 *pp++ = '\'';
956 *pp++ = '\'';
957 buflen -= 3;
958 bp += 3;
959 }
960 /* If pp is still set, that means we ran out of space. */
961 if (pp)
962 buflen = 0;
963 if (!quoted && buflen) {
964 *bp++ = '\'';
965 *bp = '\0';
966 buflen--;
967 }
968 }
969 } else {
970 raw:
971 *bp++ = *cp;
972 *bp = '\0';
973 buflen--;
974
975 if (*cp == '\'')
976 quoted = !quoted;
977 }
978 }
979
980 if (buflen <= 0 ||
981 (ct->c_termproc && buflen <= (ssize_t) strlen(ct->c_termproc))) {
982 /* content_error would provide a more useful error message
983 * here, except that if we got overrun, it probably would
984 * too.
985 */
986 fprintf(stderr, "Buffer overflow constructing show command!\n");
987 return NOTOK;
988 }
989
990 /* use charset string to modify display method */
991 if (ct->c_termproc) {
992 char term[BUFSIZ];
993
994 strncpy (term, buffer, sizeof(term));
995 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
996 }
997
998 return show_content_aux2 (ct, serial, alternate, NULL, buffer,
999 NOTOK, xlist, xpause, 0, xtty);
1000 }
1001
1002
1003 /*
1004 * show content of type "message/rfc822"
1005 */
1006
1007 static int
1008 show_message_rfc822 (CT ct, int serial, int alternate)
1009 {
1010 char *cp, buffer[BUFSIZ];
1011 CI ci = &ct->c_ctinfo;
1012
1013 /* Check for mhn-show-type/subtype */
1014 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
1015 invo_name, ci->ci_type, ci->ci_subtype);
1016 if ((cp = context_find (buffer)) && *cp != '\0')
1017 return show_content_aux (ct, serial, alternate, cp, NULL);
1018
1019 /* Check for mhn-show-type */
1020 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1021 if ((cp = context_find (buffer)) && *cp != '\0')
1022 return show_content_aux (ct, serial, alternate, cp, NULL);
1023
1024 if ((cp = ct->c_showproc))
1025 return show_content_aux (ct, serial, alternate, cp, NULL);
1026
1027 /* default method for message/rfc822 */
1028 if (ct->c_subtype == MESSAGE_RFC822) {
1029 cp = (ct->c_showproc = add ("%pshow -file '%F'", NULL));
1030 return show_content_aux (ct, serial, alternate, cp, NULL);
1031 }
1032
1033 /* complain if we are not a part of a multipart/alternative */
1034 if (!alternate)
1035 content_error (NULL, ct, "don't know how to display content");
1036
1037 return NOTOK;
1038 }
1039
1040
1041 /*
1042 * Show content of type "message/partial".
1043 */
1044
1045 static int
1046 show_partial (CT ct, int serial, int alternate)
1047 {
1048 NMH_UNUSED (serial);
1049 NMH_UNUSED (alternate);
1050
1051 content_error (NULL, ct,
1052 "in order to display this message, you must reassemble it");
1053 return NOTOK;
1054 }
1055
1056
1057 /*
1058 * Show content of type "message/external".
1059 *
1060 * THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1061 */
1062
1063 static int
1064 show_external (CT ct, int serial, int alternate)
1065 {
1066 struct exbody *e = (struct exbody *) ct->c_ctparams;
1067 CT p = e->eb_content;
1068
1069 if (!type_ok (p, 0))
1070 return OK;
1071
1072 return show_switch (p, serial, alternate);
1073 }
1074
1075
1076 static void
1077 intrser (int i)
1078 {
1079 NMH_UNUSED (i);
1080
1081 putchar ('\n');
1082 siglongjmp (intrenv, DONE);
1083 }