]> diplodocus.org Git - nmh/blob - uip/mhshowsbr.c
Add test for long + encoded line; does not pass right now.
[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 PM pm;
340 char *s = "";
341
342 for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
343 snprintf (bp, buflen, "%s%s=\"%s\"", s, pm->pm_name,
344 pm->pm_value);
345 len = strlen (bp);
346 bp += len;
347 buflen -= len;
348 s = " ";
349 }
350 }
351 break;
352
353 case 'd':
354 /* insert content description */
355 if (ct->c_descr) {
356 char *s;
357
358 s = trimcpy (ct->c_descr);
359 strncpy (bp, s, buflen);
360 free (s);
361 }
362 break;
363
364 case 'e':
365 /* exclusive execution */
366 xtty = 1;
367 break;
368
369 case 'F':
370 /* %e, %f, and stdin is terminal not content */
371 xstdin = 1;
372 xtty = 1;
373 /* and fall... */
374
375 case 'f':
376 /* insert filename containing content */
377 snprintf (bp, buflen, "'%s'", file);
378 /* since we've quoted the file argument, set things up
379 * to look past it, to avoid problems with the quoting
380 * logic below. (I know, I should figure out what's
381 * broken with the quoting logic, but..)
382 */
383 len = strlen(bp);
384 buflen -= len;
385 bp += len;
386 pp = bp;
387 break;
388
389 case 'p':
390 /* %l, and pause prior to displaying content */
391 xpause = pausesw;
392 /* and fall... */
393
394 case 'l':
395 /* display listing prior to displaying content */
396 xlist = !nolist;
397 break;
398
399 case 's':
400 /* insert subtype of content */
401 strncpy (bp, ci->ci_subtype, buflen);
402 break;
403
404 case '%':
405 /* insert character % */
406 goto raw;
407
408 default:
409 *bp++ = *--cp;
410 *bp = '\0';
411 buflen--;
412 continue;
413 }
414 len = strlen (bp);
415 bp += len;
416 buflen -= len;
417
418 /* Did we actually insert something? */
419 if (bp != pp) {
420 /* Insert single quote if not inside quotes already */
421 if (!quoted && buflen) {
422 len = strlen (pp);
423 memmove (pp + 1, pp, len);
424 *pp++ = '\'';
425 buflen--;
426 bp++;
427 }
428 /* Escape existing quotes */
429 while ((pp = strchr (pp, '\'')) && buflen > 3) {
430 len = strlen (pp++);
431 memmove (pp + 3, pp, len);
432 *pp++ = '\\';
433 *pp++ = '\'';
434 *pp++ = '\'';
435 buflen -= 3;
436 bp += 3;
437 }
438 /* If pp is still set, that means we ran out of space. */
439 if (pp)
440 buflen = 0;
441 if (!quoted && buflen) {
442 *bp++ = '\'';
443 *bp = '\0';
444 buflen--;
445 }
446 }
447 } else {
448 raw:
449 *bp++ = *cp;
450 *bp = '\0';
451 buflen--;
452
453 if (*cp == '\'')
454 quoted = !quoted;
455 }
456 }
457
458 if (buflen <= 0 ||
459 (ct->c_termproc && (size_t) buflen <= strlen(ct->c_termproc))) {
460 /* content_error would provide a more useful error message
461 * here, except that if we got overrun, it probably would
462 * too.
463 */
464 fprintf(stderr, "Buffer overflow constructing show command!\n");
465 return NOTOK;
466 }
467
468 /* use charset string to modify display method */
469 if (ct->c_termproc) {
470 char term[BUFSIZ];
471
472 strncpy (term, buffer, sizeof(term));
473 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
474 }
475
476 got_command:
477 return show_content_aux2 (ct, serial, alternate, cracked, buffer,
478 fd, xlist, xpause, xstdin, xtty);
479 }
480
481
482 /*
483 * Routine to actually display the content
484 */
485
486 static int
487 show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer,
488 int fd, int xlist, int xpause, int xstdin, int xtty)
489 {
490 pid_t child_id;
491 int i;
492 char *vec[4], exec[BUFSIZ + sizeof "exec "];
493
494 if (debugsw || cracked) {
495 fflush (stdout);
496
497 fprintf (stderr, "%s msg %s", cracked ? "storing" : "show",
498 ct->c_file);
499 if (ct->c_partno)
500 fprintf (stderr, " part %s", ct->c_partno);
501 if (cracked)
502 fprintf (stderr, " using command (cd %s; %s)\n", cracked, buffer);
503 else
504 fprintf (stderr, " using command %s\n", buffer);
505 }
506
507 if (xpid < 0 || (xtty && xpid)) {
508 if (xpid < 0)
509 xpid = -xpid;
510 pidcheck(pidwait (xpid, NOTOK));
511 xpid = 0;
512 }
513
514 if (xlist) {
515 char prompt[BUFSIZ];
516
517 if (ct->c_type == CT_MULTIPART)
518 list_content (ct, -1, 1, 0, 0);
519 else
520 list_switch (ct, -1, 1, 0, 0);
521
522 if (xpause && isatty (fileno (stdout))) {
523 int intr;
524 SIGNAL_HANDLER istat;
525
526 if (SOprintf ("Press <return> to show content..."))
527 printf ("Press <return> to show content...");
528
529 istat = SIGNAL (SIGINT, intrser);
530 if ((intr = sigsetjmp (intrenv, 1)) == OK) {
531 fflush (stdout);
532 prompt[0] = 0;
533 read (fileno (stdout), prompt, sizeof(prompt));
534 }
535 SIGNAL (SIGINT, istat);
536 if (intr != OK || prompt[0] == 'n') {
537 (*ct->c_ceclosefnx) (ct);
538 return (alternate ? DONE : NOTOK);
539 }
540 if (prompt[0] == 'q') done(OK);
541 }
542 }
543
544 snprintf (exec, sizeof(exec), "exec %s", buffer);
545
546 vec[0] = "/bin/sh";
547 vec[1] = "-c";
548 vec[2] = exec;
549 vec[3] = NULL;
550
551 fflush (stdout);
552
553 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
554 sleep (5);
555 switch (child_id) {
556 case NOTOK:
557 advise ("fork", "unable to");
558 (*ct->c_ceclosefnx) (ct);
559 return NOTOK;
560
561 case OK:
562 if (cracked)
563 chdir (cracked);
564 if (!xstdin)
565 dup2 (fd, 0);
566 close (fd);
567 execvp ("/bin/sh", vec);
568 fprintf (stderr, "unable to exec ");
569 perror ("/bin/sh");
570 _exit (-1);
571 /* NOTREACHED */
572
573 default:
574 if (!serial) {
575 ct->c_pid = child_id;
576 if (xtty)
577 xpid = child_id;
578 } else {
579 pidcheck (pidXwait (child_id, NULL));
580 }
581
582 if (fd != NOTOK)
583 (*ct->c_ceclosefnx) (ct);
584 return (alternate ? DONE : OK);
585 }
586 }
587
588
589 /*
590 * show content of type "text"
591 */
592
593 static int
594 show_text (CT ct, int serial, int alternate)
595 {
596 char *cp, buffer[BUFSIZ];
597 CI ci = &ct->c_ctinfo;
598
599 /* Check for mhn-show-type/subtype */
600 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
601 invo_name, ci->ci_type, ci->ci_subtype);
602 if ((cp = context_find (buffer)) && *cp != '\0')
603 return show_content_aux (ct, serial, alternate, cp, NULL);
604
605 /* Check for mhn-show-type */
606 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
607 if ((cp = context_find (buffer)) && *cp != '\0')
608 return show_content_aux (ct, serial, alternate, cp, NULL);
609
610 /*
611 * Use default method if content is text/plain, or if
612 * if it is not a text part of a multipart/alternative
613 */
614 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
615 snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
616 moreproc && *moreproc ? moreproc : DEFAULT_PAGER);
617 cp = (ct->c_showproc = add (buffer, NULL));
618 return show_content_aux (ct, serial, alternate, cp, NULL);
619 }
620
621 return NOTOK;
622 }
623
624
625 /*
626 * show message body of type "multipart"
627 */
628
629 static int
630 show_multi (CT ct, int serial, int alternate)
631 {
632 char *cp, buffer[BUFSIZ];
633 CI ci = &ct->c_ctinfo;
634
635 /* Check for mhn-show-type/subtype */
636 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
637 invo_name, ci->ci_type, ci->ci_subtype);
638 if ((cp = context_find (buffer)) && *cp != '\0')
639 return show_multi_aux (ct, serial, alternate, cp);
640
641 /* Check for mhn-show-type */
642 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
643 if ((cp = context_find (buffer)) && *cp != '\0')
644 return show_multi_aux (ct, serial, alternate, cp);
645
646 if ((cp = ct->c_showproc))
647 return show_multi_aux (ct, serial, alternate, cp);
648
649 /*
650 * Use default method to display this multipart content. Even
651 * unknown types are displayable, since they're treated as mixed
652 * per RFC 2046.
653 */
654 return show_multi_internal (ct, serial, alternate);
655 }
656
657
658 /*
659 * show message body of subtypes of multipart that
660 * we understand directly (mixed, alternate, etc...)
661 */
662
663 static int
664 show_multi_internal (CT ct, int serial, int alternate)
665 {
666 int alternating, nowalternate, nowserial, result;
667 struct multipart *m = (struct multipart *) ct->c_ctparams;
668 struct part *part;
669 CT p;
670 sigset_t set, oset;
671
672 alternating = 0;
673 nowalternate = alternate;
674
675 if (ct->c_subtype == MULTI_PARALLEL) {
676 nowserial = serialsw;
677 } else if (ct->c_subtype == MULTI_ALTERNATE) {
678 nowalternate = 1;
679 alternating = 1;
680 nowserial = serial;
681 } else {
682 /*
683 * multipart/mixed
684 * mutlipart/digest
685 * unknown subtypes of multipart (treat as mixed per rfc2046)
686 */
687 nowserial = serial;
688 }
689
690 /* block a few signals */
691 if (!nowserial) {
692 sigemptyset (&set);
693 sigaddset (&set, SIGHUP);
694 sigaddset (&set, SIGINT);
695 sigaddset (&set, SIGQUIT);
696 sigaddset (&set, SIGTERM);
697 sigprocmask (SIG_BLOCK, &set, &oset);
698 }
699
700 /*
701 * alternate -> we are a part inside an multipart/alternative
702 * alternating -> we are a multipart/alternative
703 */
704
705 result = alternate ? NOTOK : OK;
706
707 for (part = m->mp_parts; part; part = part->mp_next) {
708 p = part->mp_part;
709
710 if (part_ok (p, 1) && type_ok (p, 1)) {
711 int inneresult;
712
713 inneresult = show_switch (p, nowserial, nowalternate);
714 switch (inneresult) {
715 case NOTOK:
716 if (alternate && !alternating) {
717 result = NOTOK;
718 goto out;
719 }
720 continue;
721
722 case OK:
723 case DONE:
724 if (alternating) {
725 result = DONE;
726 break;
727 }
728 if (alternate) {
729 alternate = nowalternate = 0;
730 if (result == NOTOK)
731 result = inneresult;
732 }
733 continue;
734 }
735 break;
736 }
737 }
738
739 if (alternating && !part) {
740 if (!alternate)
741 content_error (NULL, ct, "don't know how to display any of the contents");
742 result = NOTOK;
743 goto out;
744 }
745
746 if (serial && !nowserial) {
747 pid_t pid;
748 int kids;
749 int status;
750
751 kids = 0;
752 for (part = m->mp_parts; part; part = part->mp_next) {
753 p = part->mp_part;
754
755 if (p->c_pid > OK) {
756 if (kill (p->c_pid, 0) == NOTOK)
757 p->c_pid = 0;
758 else
759 kids++;
760 }
761 }
762
763 while (kids > 0 && (pid = wait (&status)) != NOTOK) {
764 pidcheck (status);
765
766 for (part = m->mp_parts; part; part = part->mp_next) {
767 p = part->mp_part;
768
769 if (xpid == pid)
770 xpid = 0;
771 if (p->c_pid == pid) {
772 p->c_pid = 0;
773 kids--;
774 break;
775 }
776 }
777 }
778 }
779
780 out:
781 if (!nowserial) {
782 /* reset the signal mask */
783 sigprocmask (SIG_SETMASK, &oset, &set);
784 }
785
786 return result;
787 }
788
789
790 /*
791 * Parse display string for multipart content
792 * and use external program to display it.
793 */
794
795 static int
796 show_multi_aux (CT ct, int serial, int alternate, char *cp)
797 {
798 int len, buflen, quoted;
799 int xlist, xpause, xtty;
800 char *bp, *pp, *file, buffer[BUFSIZ];
801 struct multipart *m = (struct multipart *) ct->c_ctparams;
802 struct part *part;
803 CI ci = &ct->c_ctinfo;
804 CT p;
805
806 for (part = m->mp_parts; part; part = part->mp_next) {
807 p = part->mp_part;
808
809 if (!p->c_ceopenfnx) {
810 if (!alternate)
811 content_error (NULL, p, "don't know how to decode content");
812 return NOTOK;
813 }
814
815 if (p->c_storage == NULL) {
816 file = NULL;
817 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
818 return NOTOK;
819
820 /* I'm not sure if this is necessary? */
821 p->c_storage = add (file, NULL);
822
823 if (p->c_showproc && !strcmp (p->c_showproc, "true"))
824 return (alternate ? DONE : OK);
825 (*p->c_ceclosefnx) (p);
826 }
827 }
828
829 xlist = 0;
830 xpause = 0;
831 xtty = 0;
832
833 /* get buffer ready to go */
834 bp = buffer;
835 buflen = sizeof(buffer) - 1;
836 bp[0] = bp[buflen] = '\0';
837 quoted = 0;
838
839 /* Now parse display string */
840 for ( ; *cp && buflen > 0; cp++) {
841 if (*cp == '%') {
842 pp = bp;
843 switch (*++cp) {
844 case 'a':
845 /* insert parameters from Content-Type field */
846 {
847 PM pm;
848 char *s = "";
849
850 for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
851 snprintf (bp, buflen, "%s%s=\"%s\"", s, pm->pm_name,
852 pm->pm_value);
853 len = strlen (bp);
854 bp += len;
855 buflen -= len;
856 s = " ";
857 }
858 }
859 break;
860
861 case 'd':
862 /* insert content description */
863 if (ct->c_descr) {
864 char *s;
865
866 s = trimcpy (ct->c_descr);
867 strncpy (bp, s, buflen);
868 free (s);
869 }
870 break;
871
872 case 'e':
873 /* exclusive execution */
874 xtty = 1;
875 break;
876
877 case 'F':
878 /* %e and %f */
879 xtty = 1;
880 /* and fall... */
881
882 case 'f':
883 /* insert filename(s) containing content */
884 {
885 char *s = "";
886
887 for (part = m->mp_parts; part; part = part->mp_next) {
888 p = part->mp_part;
889
890 snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
891 len = strlen (bp);
892 bp += len;
893 buflen -= len;
894 s = " ";
895 }
896 /* set our starting pointer back to bp, to avoid
897 * requoting the filenames we just added
898 */
899 pp = bp;
900 }
901 break;
902
903 case 'p':
904 /* %l, and pause prior to displaying content */
905 xpause = pausesw;
906 /* and fall... */
907
908 case 'l':
909 /* display listing prior to displaying content */
910 xlist = !nolist;
911 break;
912
913 case 's':
914 /* insert subtype of content */
915 strncpy (bp, ci->ci_subtype, buflen);
916 break;
917
918 case '%':
919 /* insert character % */
920 goto raw;
921
922 default:
923 *bp++ = *--cp;
924 *bp = '\0';
925 buflen--;
926 continue;
927 }
928 len = strlen (bp);
929 bp += len;
930 buflen -= len;
931
932 /* Did we actually insert something? */
933 if (bp != pp) {
934 /* Insert single quote if not inside quotes already */
935 if (!quoted && buflen) {
936 len = strlen (pp);
937 memmove (pp + 1, pp, len);
938 *pp++ = '\'';
939 buflen--;
940 bp++;
941 }
942 /* Escape existing quotes */
943 while ((pp = strchr (pp, '\'')) && buflen > 3) {
944 len = strlen (pp++);
945 memmove (pp + 3, pp, len);
946 *pp++ = '\\';
947 *pp++ = '\'';
948 *pp++ = '\'';
949 buflen -= 3;
950 bp += 3;
951 }
952 /* If pp is still set, that means we ran out of space. */
953 if (pp)
954 buflen = 0;
955 if (!quoted && buflen) {
956 *bp++ = '\'';
957 *bp = '\0';
958 buflen--;
959 }
960 }
961 } else {
962 raw:
963 *bp++ = *cp;
964 *bp = '\0';
965 buflen--;
966
967 if (*cp == '\'')
968 quoted = !quoted;
969 }
970 }
971
972 if (buflen <= 0 ||
973 (ct->c_termproc && buflen <= (ssize_t) strlen(ct->c_termproc))) {
974 /* content_error would provide a more useful error message
975 * here, except that if we got overrun, it probably would
976 * too.
977 */
978 fprintf(stderr, "Buffer overflow constructing show command!\n");
979 return NOTOK;
980 }
981
982 /* use charset string to modify display method */
983 if (ct->c_termproc) {
984 char term[BUFSIZ];
985
986 strncpy (term, buffer, sizeof(term));
987 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
988 }
989
990 return show_content_aux2 (ct, serial, alternate, NULL, buffer,
991 NOTOK, xlist, xpause, 0, xtty);
992 }
993
994
995 /*
996 * show content of type "message/rfc822"
997 */
998
999 static int
1000 show_message_rfc822 (CT ct, int serial, int alternate)
1001 {
1002 char *cp, buffer[BUFSIZ];
1003 CI ci = &ct->c_ctinfo;
1004
1005 /* Check for mhn-show-type/subtype */
1006 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
1007 invo_name, ci->ci_type, ci->ci_subtype);
1008 if ((cp = context_find (buffer)) && *cp != '\0')
1009 return show_content_aux (ct, serial, alternate, cp, NULL);
1010
1011 /* Check for mhn-show-type */
1012 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1013 if ((cp = context_find (buffer)) && *cp != '\0')
1014 return show_content_aux (ct, serial, alternate, cp, NULL);
1015
1016 if ((cp = ct->c_showproc))
1017 return show_content_aux (ct, serial, alternate, cp, NULL);
1018
1019 /* default method for message/rfc822 */
1020 if (ct->c_subtype == MESSAGE_RFC822) {
1021 cp = (ct->c_showproc = add ("%pshow -file '%F'", NULL));
1022 return show_content_aux (ct, serial, alternate, cp, NULL);
1023 }
1024
1025 /* complain if we are not a part of a multipart/alternative */
1026 if (!alternate)
1027 content_error (NULL, ct, "don't know how to display content");
1028
1029 return NOTOK;
1030 }
1031
1032
1033 /*
1034 * Show content of type "message/partial".
1035 */
1036
1037 static int
1038 show_partial (CT ct, int serial, int alternate)
1039 {
1040 NMH_UNUSED (serial);
1041 NMH_UNUSED (alternate);
1042
1043 content_error (NULL, ct,
1044 "in order to display this message, you must reassemble it");
1045 return NOTOK;
1046 }
1047
1048
1049 /*
1050 * Show content of type "message/external".
1051 *
1052 * THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1053 */
1054
1055 static int
1056 show_external (CT ct, int serial, int alternate)
1057 {
1058 struct exbody *e = (struct exbody *) ct->c_ctparams;
1059 CT p = e->eb_content;
1060
1061 if (!type_ok (p, 0))
1062 return OK;
1063
1064 return show_switch (p, serial, alternate);
1065 }
1066
1067
1068 static void
1069 intrser (int i)
1070 {
1071 NMH_UNUSED (i);
1072
1073 putchar ('\n');
1074 siglongjmp (intrenv, DONE);
1075 }