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