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