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