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