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