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