Replace free() with mh_free0()
[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 = getcpy(etcpath("mhl.headers"));
80
81         /*
82         ** If form is "mhl.null", suppress display of header.
83         */
84         if (strcmp(formsw, "mhl.null")==0)
85                 formsw = NULL;
86
87         for (ctp = cts; *ctp; ctp++) {
88                 ct = *ctp;
89
90                 if (!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 = getcpy(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
564         ** if it is not a (nested) part of a multipart/alternative,
565         ** or if it is one of the known subtypes of multipart.
566         */
567         if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
568                 return show_multi_internal(ct, alternate);
569
570         return NOTOK;
571 }
572
573
574 /*
575 ** show message body of subtypes of multipart that
576 ** we understand directly (mixed, alternate, etc...)
577 */
578
579 static int
580 show_multi_internal(CT ct, int alternate)
581 {
582         int alternating, nowalternate, result;
583         struct multipart *m = (struct multipart *) ct->c_ctparams;
584         struct part *part;
585         CT p;
586
587         alternating = 0;
588         nowalternate = alternate;
589
590         if (ct->c_subtype == MULTI_ALTERNATE) {
591                 nowalternate = 1;
592                 alternating  = 1;
593         }
594
595         /*
596         ** Other possible multipart types are:
597         ** - multipart/parallel
598         ** - multipart/mixed
599         ** - multipart/digest
600         ** - unknown subtypes of multipart (treat as mixed per rfc2046)
601         */
602
603         /*
604         ** alternate   -> we are a part inside an multipart/alternative
605         ** alternating -> we are a multipart/alternative
606         */
607
608         result = alternate ? NOTOK : OK;
609
610         for (part = m->mp_parts; part; part = part->mp_next) {
611                 p = part->mp_part;
612
613                 if (part_ok(p, 1) && type_ok(p, 1)) {
614                         int inneresult;
615
616                         inneresult = show_switch(p, nowalternate);
617                         switch (inneresult) {
618                         case NOTOK:
619                                 if (alternate && !alternating) {
620                                         result = NOTOK;
621                                         goto out;
622                                 }
623                                 continue;
624
625                         case OK:
626                         case DONE:
627                                 if (alternating) {
628                                         result = DONE;
629                                         break;
630                                 }
631                                 if (alternate) {
632                                         alternate = nowalternate = 0;
633                                         if (result == NOTOK)
634                                                 result = inneresult;
635                                 }
636                                 continue;
637                         }
638                         break;
639                 }
640         }
641
642         if (alternating && !part) {
643                 if (!alternate)
644                         content_error(NULL, ct, "don't know how to display any of the contents");
645                 result = NOTOK;
646                 goto out;
647         }
648 out:
649         return result;
650 }
651
652
653 /*
654 ** Parse display string for multipart content
655 ** and use external program to display it.
656 */
657
658 static int
659 show_multi_aux(CT ct, int alternate, char *cp)
660 {
661         int len, buflen, quoted;
662         int xlist;
663         char *bp, *pp, *file, buffer[BUFSIZ];
664         struct multipart *m = (struct multipart *) ct->c_ctparams;
665         struct part *part;
666         CI ci = &ct->c_ctinfo;
667         CT p;
668
669         for (part = m->mp_parts; part; part = part->mp_next) {
670                 p = part->mp_part;
671
672                 if (!p->c_ceopenfnx) {
673                         if (!alternate)
674                                 content_error(NULL, p, "don't know how to decode content");
675                         return NOTOK;
676                 }
677
678                 if (p->c_storage == NULL) {
679                         file = NULL;
680                         if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
681                                 return NOTOK;
682
683                         /* I'm not sure if this is necessary? */
684                         p->c_storage = getcpy(file);
685
686                         if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
687                                 return (alternate ? DONE : OK);
688                         (*p->c_ceclosefnx) (p);
689                 }
690         }
691
692         xlist = 0;
693
694         /* get buffer ready to go */
695         bp = buffer;
696         buflen = sizeof(buffer) - 1;
697         bp[0] = bp[buflen] = '\0';
698         quoted = 0;
699
700         /* Now parse display string */
701         for ( ; *cp && buflen > 0; cp++) {
702                 if (*cp == '%') {
703                         pp = bp;
704                         switch (*++cp) {
705                         case 'a':
706                                 /* insert parameters from Content-Type field */
707                                 {
708                                 char **ap, **ep;
709                                 char *s = "";
710
711                                 for (ap = ci->ci_attrs, ep = ci->ci_values;
712                                                 *ap; ap++, ep++) {
713                                         snprintf(bp, buflen, "%s%s=\"%s\"",
714                                                         s, *ap, *ep);
715                                         len = strlen(bp);
716                                         bp += len;
717                                         buflen -= len;
718                                         s = " ";
719                                 }
720                                 }
721                                 break;
722
723                         case 'c':
724                                 /* insert charset */
725                                 strncpy(bp, ct->c_charset ? ct->c_charset :
726                                                 "US-ASCII", buflen);
727                                 break;
728
729                         case 'd':
730                                 /* insert content description */
731                                 if (ct->c_descr) {
732                                         char *s;
733
734                                         s = trimcpy(ct->c_descr);
735                                         strncpy(bp, s, buflen);
736                                         mh_free0(&s);
737                                 }
738                                 break;
739
740                         case 'F':
741                         case 'f':
742                                 /* insert filename(s) containing content */
743                         {
744                                 char *s = "";
745
746                                 for (part = m->mp_parts; part;
747                                                 part = part->mp_next) {
748                                         p = part->mp_part;
749
750                                         snprintf(bp, buflen, "%s'%s'",
751                                                         s, p->c_storage);
752                                         len = strlen(bp);
753                                         bp += len;
754                                         buflen -= len;
755                                         s = " ";
756                                 }
757                                 /*
758                                 ** set our starting pointer back to bp,
759                                 ** to avoid requoting the filenames we
760                                 ** just added
761                                 */
762                                 pp = bp;
763                         }
764                         break;
765
766                         case 'l':
767                                 /*
768                                 ** display listing prior to displaying content
769                                 */
770                                 xlist = !nolist;
771                                 break;
772
773                         case 's':
774                                 /* insert subtype of content */
775                                 strncpy(bp, ci->ci_subtype, buflen);
776                                 break;
777
778                         case '%':
779                                 /* insert character % */
780                                 goto raw;
781
782                         default:
783                                 *bp++ = *--cp;
784                                 *bp = '\0';
785                                 buflen--;
786                                 continue;
787                         }
788                         len = strlen(bp);
789                         bp += len;
790                         buflen -= len;
791
792                         /* Did we actually insert something? */
793                         if (bp != pp) {
794                                 /*
795                                 ** Insert single quote if not inside quotes
796                                 ** already
797                                 */
798                                 if (!quoted && buflen) {
799                                         len = strlen(pp);
800                                         memmove(pp + 1, pp, len);
801                                         *pp++ = '\'';
802                                         buflen--;
803                                         bp++;
804                                 }
805                                 /* Escape existing quotes */
806                                 while ((pp = strchr(pp, '\'')) && buflen > 3) {
807                                         len = strlen(pp++);
808                                         memmove(pp + 3, pp, len);
809                                         *pp++ = '\\';
810                                         *pp++ = '\'';
811                                         *pp++ = '\'';
812                                         buflen -= 3;
813                                         bp += 3;
814                                 }
815                                 /*
816                                 ** If pp is still set, that means we ran
817                                 ** out of space.
818                                 */
819                                 if (pp)
820                                         buflen = 0;
821                                 if (!quoted && buflen) {
822                                         *bp++ = '\'';
823                                         *bp = '\0';
824                                         buflen--;
825                                 }
826                         }
827                 } else {
828 raw:
829                         *bp++ = *cp;
830                         *bp = '\0';
831                         buflen--;
832
833                         if (*cp == '\'')
834                                 quoted = !quoted;
835                 }
836         }
837
838         return show_content_aux2(ct, alternate, NULL, buffer,
839                         NOTOK, xlist, 0);
840 }
841
842
843 /*
844 ** show content of type "message/rfc822"
845 */
846
847 static int
848 show_message_rfc822(CT ct, int alternate)
849 {
850         char *cp, buffer[BUFSIZ];
851         CI ci = &ct->c_ctinfo;
852
853         /* Check for mhshow-show-type/subtype */
854         snprintf(buffer, sizeof(buffer), "mhshow-show-%s/%s",
855                                 ci->ci_type, ci->ci_subtype);
856         if ((cp = context_find(buffer)) && *cp != '\0')
857                 return show_content_aux(ct, alternate, cp, NULL);
858
859         /* Check for mhshow-show-type */
860         snprintf(buffer, sizeof(buffer), "mhshow-show-%s", ci->ci_type);
861         if ((cp = context_find(buffer)) && *cp != '\0')
862                 return show_content_aux(ct, alternate, cp, NULL);
863
864         if ((cp = ct->c_showproc))
865                 return show_content_aux(ct, alternate, cp, NULL);
866
867         /* default method for message/rfc822 */
868         if (ct->c_subtype == MESSAGE_RFC822) {
869                 cp = (ct->c_showproc = getcpy("%lshow -file %F"));
870                 return show_content_aux(ct, alternate, cp, NULL);
871         }
872
873         /* complain if we are not a part of a multipart/alternative */
874         if (!alternate)
875                 return show_content_aux(ct, alternate, "%l", NULL);
876
877         return NOTOK;
878 }
879
880
881 /*
882 ** Show content of type "message/partial".
883 */
884
885 static int
886 show_partial(CT ct, int alternate)
887 {
888         show_content_aux(ct, alternate, "%l", NULL);
889         puts("in order to display this message, you must reassemble it");
890         return NOTOK;
891 }
892
893
894 /*
895 ** Show how to retrieve content of type "message/external".
896 */
897 static int
898 show_external(CT ct, int alternate)
899 {
900         char **ap, **ep;
901         char *msg;
902         FILE *fp;
903         char buf[BUFSIZ];
904
905         msg = add("You need to fetch the contents yourself:\n", NULL);
906         ap = ct->c_ctinfo.ci_attrs;
907         ep = ct->c_ctinfo.ci_values;
908         for (; *ap; ap++, ep++) {
909                 msg = add(concat("\t", *ap, ": ", *ep, NULL), msg);
910         }
911         if (!(fp = fopen(ct->c_file, "r"))) {
912                 adios(EX_IOERR, ct->c_file, "unable to open");
913         }
914         fseek(fp, ct->c_begin, SEEK_SET);
915         while (!feof(fp) && ftell(fp) < ct->c_end) {
916                 if (!fgets(buf, sizeof buf, fp)) {
917                         adios(EX_IOERR, ct->c_file, "unable to read");
918                 }
919                 *strchr(buf, '\n') = '\0';
920                 if (!*buf) {
921                         continue;  /* skip empty lines */
922                 }
923                 msg = add(concat("\t", buf, "\n", NULL), msg);
924         }
925         fclose(fp);
926         show_content_aux(ct, alternate, "%l", NULL);
927         fputs(msg, stdout);
928         return OK;
929 }