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