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