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