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