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