No more mhshow-charset-* but instead automatic invocation of iconv(1).
[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 'c':
339                                 /* insert charset */
340                                 if (ct->c_charset) {
341                                         strncpy(bp, ct->c_charset, buflen);
342                                 }
343                                 break;
344
345                         case 'd':
346                                 /* insert content description */
347                                 if (ct->c_descr) {
348                                         char *s;
349
350                                         s = trimcpy(ct->c_descr);
351                                         strncpy(bp, s, buflen);
352                                         free(s);
353                                 }
354                                 break;
355
356                         case 'e':
357                                 /* exclusive execution */
358                                 xtty = 1;
359                                 break;
360
361                         case 'F':
362                                 /* %e, %f, and stdin is terminal not content */
363                                 xstdin = 1;
364                                 xtty = 1;
365                                 /* and fall... */
366
367                         case 'f':
368                                 /* insert filename containing content */
369                                 snprintf(bp, buflen, "'%s'", file);
370                                 /*
371                                 ** since we've quoted the file argument,
372                                 ** set things up to look past it, to avoid
373                                 ** problems with the quoting logic below.
374                                 ** (I know, I should figure out what's
375                                 ** broken with the quoting logic, but..)
376                                 */
377                                 len = strlen(bp);
378                                 buflen -= len;
379                                 bp += len;
380                                 pp = bp;
381                                 break;
382
383                         case 'p':
384                         case 'l':
385                                 /*
386                                 ** display listing prior to displaying content
387                                 */
388                                 xlist = !nolist;
389                                 break;
390
391                         case 's':
392                                 /* insert subtype of content */
393                                 strncpy(bp, ci->ci_subtype, buflen);
394                                 break;
395
396                         case '%':
397                                 /* insert character % */
398                                 goto raw;
399
400                         default:
401                                 *bp++ = *--cp;
402                                 *bp = '\0';
403                                 buflen--;
404                                 continue;
405                         }
406                         len = strlen(bp);
407                         bp += len;
408                         buflen -= len;
409
410                         /* Did we actually insert something? */
411                         if (bp != pp) {
412                                 /*
413                                 ** Insert single quote if not inside quotes
414                                 ** already
415                                 */
416                                 if (!quoted && buflen) {
417                                         len = strlen(pp);
418                                         memmove(pp + 1, pp, len);
419                                         *pp++ = '\'';
420                                         buflen--;
421                                         bp++;
422                                 }
423                                 /* Escape existing quotes */
424                                 while ((pp = strchr(pp, '\'')) && buflen > 3) {
425                                         len = strlen(pp++);
426                                         memmove(pp + 3, pp, len);
427                                         *pp++ = '\\';
428                                         *pp++ = '\'';
429                                         *pp++ = '\'';
430                                         buflen -= 3;
431                                         bp += 3;
432                                 }
433                                 /*
434                                 ** If pp is still set, that means we ran
435                                 ** out of space.
436                                 */
437                                 if (pp)
438                                         buflen = 0;
439                                 if (!quoted && buflen) {
440                                         *bp++ = '\'';
441                                         *bp = '\0';
442                                         buflen--;
443                                 }
444                         }
445                 } else {
446 raw:
447                         *bp++ = *cp;
448                         *bp = '\0';
449                         buflen--;
450
451                         if (*cp == '\'')
452                                 quoted = !quoted;
453                 }
454         }
455
456 got_command:
457         return show_content_aux2(ct, alternate, cracked, buffer,
458                         fd, xlist, xstdin, xtty);
459 }
460
461
462 /*
463 ** Routine to actually display the content
464 */
465
466 static int
467 show_content_aux2(CT ct, int alternate, char *cracked,
468         char *buffer, int fd, int xlist, int xstdin, int xtty)
469 {
470         pid_t child_id;
471         char *vec[4], exec[BUFSIZ + sizeof "exec "];
472
473         if (debugsw || cracked) {
474                 fflush(stdout);
475
476                 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
477                                  ct->c_file);
478                 if (ct->c_partno)
479                         fprintf(stderr, " part %s", ct->c_partno);
480                 if (cracked)
481                         fprintf(stderr, " using command (cd %s; %s)\n",
482                                         cracked, buffer);
483                 else
484                         fprintf(stderr, " using command %s\n", buffer);
485         }
486
487         if (xpid < 0 || (xtty && xpid)) {
488                 if (xpid < 0)
489                         xpid = -xpid;
490                 pidcheck(pidwait(xpid, NOTOK));
491                 xpid = 0;
492         }
493
494         if (xlist) {
495                 if (ct->c_type == CT_MULTIPART)
496                         list_content(ct, -1, 1, 0, 0);
497                 else
498                         list_switch(ct, -1, 1, 0, 0);
499         }
500
501         snprintf(exec, sizeof(exec), "exec %s", buffer);
502
503         vec[0] = "/bin/sh";
504         vec[1] = "-c";
505         vec[2] = exec;
506         vec[3] = NULL;
507
508         fflush(stdout);
509
510         switch (child_id = fork()) {
511         case NOTOK:
512                 advise("fork", "unable to");
513                 (*ct->c_ceclosefnx) (ct);
514                 return NOTOK;
515
516         case OK:
517                 if (cracked)
518                         chdir(cracked);
519                 if (!xstdin)
520                         dup2(fd, 0);
521                 close(fd);
522                 execvp("/bin/sh", vec);
523                 fprintf(stderr, "unable to exec ");
524                 perror("/bin/sh");
525                 _exit(-1);
526                 /* NOTREACHED */
527
528         default:
529                 pidcheck(pidXwait(child_id, NULL));
530
531                 if (fd != NOTOK)
532                         (*ct->c_ceclosefnx) (ct);
533                 return (alternate ? DONE : OK);
534         }
535 }
536
537
538 /*
539 ** show content of type "text"
540 */
541
542 static int
543 show_text(CT ct, int alternate)
544 {
545         char *cp, buffer[BUFSIZ];
546         CI ci = &ct->c_ctinfo;
547
548         /* Check for mhshow-show-type/subtype */
549         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
550                         invo_name, ci->ci_type, ci->ci_subtype);
551         if ((cp = context_find(buffer)) && *cp != '\0')
552                 return show_content_aux(ct, alternate, cp, NULL);
553
554         /* Check for mhshow-show-type */
555         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
556         if ((cp = context_find(buffer)) && *cp != '\0')
557                 return show_content_aux(ct, alternate, cp, NULL);
558
559         /*
560         ** Use default method if content is text/plain, or if
561         ** if it is not a text part of a multipart/alternative
562         */
563         if (!alternate || ct->c_subtype == TEXT_PLAIN) {
564                 if (!check_charset(ct->c_charset, strlen(ct->c_charset))) {
565                         snprintf(buffer, sizeof(buffer), "%%liconv -f '%s'",
566                                         ct->c_charset);
567                 } else {
568                         snprintf(buffer, sizeof(buffer), "%%lcat");
569                 }
570                 cp = (ct->c_showproc = getcpy(buffer));
571                 return show_content_aux(ct, alternate, cp, NULL);
572         }
573
574         return NOTOK;
575 }
576
577
578 /*
579 ** show message body of type "multipart"
580 */
581
582 static int
583 show_multi(CT ct, int alternate)
584 {
585         char *cp, buffer[BUFSIZ];
586         CI ci = &ct->c_ctinfo;
587
588         /* Check for mhshow-show-type/subtype */
589         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
590                         invo_name, ci->ci_type, ci->ci_subtype);
591         if ((cp = context_find(buffer)) && *cp != '\0')
592                 return show_multi_aux(ct, alternate, cp);
593
594         /* Check for mhshow-show-type */
595         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
596         if ((cp = context_find(buffer)) && *cp != '\0')
597                 return show_multi_aux(ct, alternate, cp);
598
599         if ((cp = ct->c_showproc))
600                 return show_multi_aux(ct, alternate, cp);
601
602         /*
603         ** Use default method to display this multipart content
604         ** if it is not a (nested) part of a multipart/alternative,
605         ** or if it is one of the known subtypes of multipart.
606         */
607         if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
608                 return show_multi_internal(ct, alternate);
609
610         return NOTOK;
611 }
612
613
614 /*
615 ** show message body of subtypes of multipart that
616 ** we understand directly (mixed, alternate, etc...)
617 */
618
619 static int
620 show_multi_internal(CT ct, int alternate)
621 {
622         int alternating, nowalternate, result;
623         struct multipart *m = (struct multipart *) ct->c_ctparams;
624         struct part *part;
625         CT p;
626
627         alternating = 0;
628         nowalternate = alternate;
629
630         if (ct->c_subtype == MULTI_ALTERNATE) {
631                 nowalternate = 1;
632                 alternating  = 1;
633         }
634
635         /*
636         ** Other possible multipart types are:
637         ** - multipart/parallel
638         ** - multipart/mixed
639         ** - multipart/digest
640         ** - unknown subtypes of multipart (treat as mixed per rfc2046)
641         */
642
643         /*
644         ** alternate   -> we are a part inside an multipart/alternative
645         ** alternating -> we are a multipart/alternative
646         */
647
648         result = alternate ? NOTOK : OK;
649
650         for (part = m->mp_parts; part; part = part->mp_next) {
651                 p = part->mp_part;
652
653                 if (part_ok(p, 1) && type_ok(p, 1)) {
654                         int inneresult;
655
656                         inneresult = show_switch(p, nowalternate);
657                         switch (inneresult) {
658                         case NOTOK:
659                                 if (alternate && !alternating) {
660                                         result = NOTOK;
661                                         goto out;
662                                 }
663                                 continue;
664
665                         case OK:
666                         case DONE:
667                                 if (alternating) {
668                                         result = DONE;
669                                         break;
670                                 }
671                                 if (alternate) {
672                                         alternate = nowalternate = 0;
673                                         if (result == NOTOK)
674                                                 result = inneresult;
675                                 }
676                                 continue;
677                         }
678                         break;
679                 }
680         }
681
682         if (alternating && !part) {
683                 if (!alternate)
684                         content_error(NULL, ct, "don't know how to display any of the contents");
685                 result = NOTOK;
686                 goto out;
687         }
688 out:
689         return result;
690 }
691
692
693 /*
694 ** Parse display string for multipart content
695 ** and use external program to display it.
696 */
697
698 static int
699 show_multi_aux(CT ct, int alternate, char *cp)
700 {
701         int len, buflen, quoted;
702         int xlist, xtty;
703         char *bp, *pp, *file, buffer[BUFSIZ];
704         struct multipart *m = (struct multipart *) ct->c_ctparams;
705         struct part *part;
706         CI ci = &ct->c_ctinfo;
707         CT p;
708
709         for (part = m->mp_parts; part; part = part->mp_next) {
710                 p = part->mp_part;
711
712                 if (!p->c_ceopenfnx) {
713                         if (!alternate)
714                                 content_error(NULL, p, "don't know how to decode content");
715                         return NOTOK;
716                 }
717
718                 if (p->c_storage == NULL) {
719                         file = NULL;
720                         if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
721                                 return NOTOK;
722
723                         /* I'm not sure if this is necessary? */
724                         p->c_storage = getcpy(file);
725
726                         if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
727                                 return (alternate ? DONE : OK);
728                         (*p->c_ceclosefnx) (p);
729                 }
730         }
731
732         xlist = 0;
733         xtty = 0;
734
735         /* get buffer ready to go */
736         bp = buffer;
737         buflen = sizeof(buffer) - 1;
738         bp[0] = bp[buflen] = '\0';
739         quoted = 0;
740
741         /* Now parse display string */
742         for ( ; *cp && buflen > 0; cp++) {
743                 if (*cp == '%') {
744                         pp = bp;
745                         switch (*++cp) {
746                         case 'a':
747                                 /* insert parameters from Content-Type field */
748                                 {
749                                 char **ap, **ep;
750                                 char *s = "";
751
752                                 for (ap = ci->ci_attrs, ep = ci->ci_values;
753                                                 *ap; ap++, ep++) {
754                                         snprintf(bp, buflen, "%s%s=\"%s\"",
755                                                         s, *ap, *ep);
756                                         len = strlen(bp);
757                                         bp += len;
758                                         buflen -= len;
759                                         s = " ";
760                                 }
761                                 }
762                                 break;
763
764                         case 'c':
765                                 /* insert charset */
766                                 if (ct->c_charset) {
767                                         strncpy(bp, ct->c_charset, buflen);
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         return show_content_aux2(ct, alternate, NULL, buffer,
891                         NOTOK, xlist, 0, xtty);
892 }
893
894
895 /*
896 ** show content of type "message/rfc822"
897 */
898
899 static int
900 show_message_rfc822(CT ct, int alternate)
901 {
902         char *cp, buffer[BUFSIZ];
903         CI ci = &ct->c_ctinfo;
904
905         /* Check for mhshow-show-type/subtype */
906         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
907                                 invo_name, ci->ci_type, ci->ci_subtype);
908         if ((cp = context_find(buffer)) && *cp != '\0')
909                 return show_content_aux(ct, alternate, cp, NULL);
910
911         /* Check for mhshow-show-type */
912         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
913         if ((cp = context_find(buffer)) && *cp != '\0')
914                 return show_content_aux(ct, alternate, cp, NULL);
915
916         if ((cp = ct->c_showproc))
917                 return show_content_aux(ct, alternate, cp, NULL);
918
919         /* default method for message/rfc822 */
920         if (ct->c_subtype == MESSAGE_RFC822) {
921                 cp = (ct->c_showproc = getcpy("%pshow -file '%F'"));
922                 return show_content_aux(ct, alternate, cp, NULL);
923         }
924
925         /* complain if we are not a part of a multipart/alternative */
926         if (!alternate)
927                 content_error(NULL, ct, "don't know how to display content");
928
929         return NOTOK;
930 }
931
932
933 /*
934 ** Show content of type "message/partial".
935 */
936
937 static int
938 show_partial(CT ct, int alternate)
939 {
940         content_error(NULL, ct,
941                 "in order to display this message, you must reassemble it");
942         return NOTOK;
943 }
944
945
946 /*
947 ** Show how to retrieve content of type "message/external".
948 */
949 static int
950 show_external(CT ct, int alternate)
951 {
952         char **ap, **ep;
953         char *msg;
954         FILE *fp;
955         char buf[BUFSIZ];
956
957         msg = add("You need to fetch the contents yourself:", NULL);
958         ap = ct->c_ctinfo.ci_attrs;
959         ep = ct->c_ctinfo.ci_values;
960         for (; *ap; ap++, ep++) {
961                 msg = add(concat("\n\t", *ap, ": ", *ep, NULL), msg);
962         }
963         if (!(fp = fopen(ct->c_file, "r"))) {
964                 adios(ct->c_file, "unable to open");
965         }
966         fseek(fp, ct->c_begin, SEEK_SET);
967         while (!feof(fp) && ftell(fp) < ct->c_end) {
968                 if (!fgets(buf, sizeof buf, fp)) {
969                         adios(ct->c_file, "unable to read");
970                 }
971                 *strchr(buf, '\n') = '\0';
972                 msg = add(concat("\n\t", buf, NULL), msg);
973         }
974         fclose(fp);
975         content_error(NULL, ct, msg);
976         return OK;
977 }