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