Relayouted all switch statements: case aligns with switch.
[mmh] / uip / mhlsbr.c
1 /*
2 ** mhlsbr.c -- main routines for nmh message lister
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 <h/signals.h>
11 #include <h/addrsbr.h>
12 #include <h/fmt_scan.h>
13 #include <h/tws.h>
14 #include <h/utils.h>
15 #include <setjmp.h>
16 #include <signal.h>
17
18 /*
19 ** MAJOR BUG:
20 ** for a component containing addresses, ADDRFMT, if COMPRESS is also
21 ** set, then addresses get split wrong (not at the spaces between commas).
22 ** To fix this correctly, putstr() should know about "atomic" strings that
23 ** must NOT be broken across lines.  That's too difficult for right now
24 ** (it turns out that there are a number of degernate cases), so in
25 ** oneline(), instead of
26 **
27 **       (*onelp == '\n' && !onelp[1])
28 **
29 ** being a terminating condition,
30 **
31 **       (*onelp == '\n' && (!onelp[1] || (flags & ADDRFMT)))
32 **
33 ** is used instead.  This cuts the line prematurely, and gives us a much
34 ** better chance of getting things right.
35 */
36
37 #define ONECOMP  0
38 #define TWOCOMP  1
39 #define BODYCOMP 2
40
41 #define QUOTE  '\\'
42
43 static struct swit mhlswitches[] = {
44 #define BELLSW  0
45         { "bell", 0 },
46 #define NBELLSW  1
47         { "nobell", 0 },
48 #define CLRSW  2
49         { "clear", 0 },
50 #define NCLRSW  3
51         { "noclear", 0 },
52 #define FOLDSW  4
53         { "folder +folder", 0 },
54 #define FORMSW  5
55         { "form formfile", 0 },
56 #define PROGSW  6
57         { "moreproc program", 0 },
58 #define NPROGSW  7
59         { "nomoreproc", 0 },
60 #define LENSW  8
61         { "length lines", 0 },
62 #define WIDTHSW  9
63         { "width columns", 0 },
64 #define SLEEPSW  10
65         { "sleep seconds",  0 },
66 #define BITSTUFFSW  11
67         { "dashstuffing", -12 },  /* interface from forw */
68 #define NBITSTUFFSW   12
69         { "nodashstuffing", -14 },  /* interface from forw */
70 #define VERSIONSW  13
71         { "version", 0 },
72 #define HELPSW  14
73         { "help", 0 },
74 #define FORW1SW  15
75         { "forward", -7 },  /* interface from forw */
76 #define FORW2SW  16
77         { "forwall", -7 },  /* interface from forw */
78 #define DGSTSW  17
79         { "digest list", -6 },
80 #define VOLUMSW  18
81         { "volume number", -6 },
82 #define ISSUESW  19
83         { "issue number", -5 },
84 #define NBODYSW  20
85         { "nobody", -6 },
86         { NULL, 0 }
87 };
88
89 #define NOCOMPONENT 0x000001  /* don't show component name   */
90 #define UPPERCASE   0x000002  /* display in all upper case   */
91 #define CENTER      0x000004  /* center line                 */
92 #define CLEARTEXT   0x000008  /* cleartext                   */
93 #define EXTRA       0x000010  /* an "extra" component        */
94 #define HDROUTPUT   0x000020  /* already output              */
95 #define CLEARSCR    0x000040  /* clear screen                */
96 #define LEFTADJUST  0x000080  /* left justify multiple lines */
97 #define COMPRESS    0x000100  /* compress text               */
98 #define ADDRFMT     0x000200  /* contains addresses          */
99 #define BELL        0x000400  /* sound bell at EOP           */
100 #define DATEFMT     0x000800  /* contains dates              */
101 #define FORMAT      0x001000  /* parse address/date/RFC-2047 field */
102 #define INIT        0x002000  /* initialize component        */
103 #define SPLIT       0x004000  /* split headers (don't concatenate) */
104 #define NONEWLINE   0x008000  /* don't write trailing newline */
105 #define LBITS       "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017SPLIT\020NONEWLINE"
106 #define GFLAGS      (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT)
107
108 struct mcomp {
109         char *c_name;  /* component name */
110         char *c_text;  /* component text */
111         char *c_ovtxt; /* text overflow indicator */
112         char *c_nfs;   /* iff FORMAT */
113         struct format *c_fmt;  /*  .. */
114         int c_offset;  /* left margin indentation */
115         int c_ovoff;   /* overflow indentation */
116         int c_width;   /* width of field */
117         int c_cwidth;  /* width of component */
118         int c_length;  /* length in lines */
119         long c_flags;
120         struct mcomp *c_next;
121 };
122
123 static struct mcomp *msghd = NULL;
124 static struct mcomp *msgtl = NULL;
125 static struct mcomp *fmthd = NULL;
126 static struct mcomp *fmttl = NULL;
127
128 static struct mcomp global = {
129         NULL, NULL, "", NULL, NULL, 0, -1, 80, -1, 40, BELL, 0
130 };
131
132 static struct mcomp holder = {
133         NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, 0
134 };
135
136 struct pair {
137         char *p_name;
138         long p_flags;
139 };
140
141 static struct pair pairs[] = {
142         { "Date", DATEFMT },
143         { "From", ADDRFMT },
144         { "Sender", ADDRFMT },
145         { "Reply-To", ADDRFMT },
146         { "To", ADDRFMT },
147         { "Cc", ADDRFMT },
148         { "Bcc", ADDRFMT },
149         { "Resent-Date", DATEFMT },
150         { "Resent-From", ADDRFMT },
151         { "Resent-Sender", ADDRFMT },
152         { "Resent-Reply-To", ADDRFMT },
153         { "Resent-To", ADDRFMT },
154         { "Resent-Cc", ADDRFMT },
155         { "Resent-Bcc", ADDRFMT },
156         { NULL, 0 }
157 };
158
159 struct triple {
160         char *t_name;
161         long t_on;
162         long t_off;
163 };
164
165 static struct triple triples[] = {
166         { "nocomponent",  NOCOMPONENT, 0 },
167         { "uppercase", UPPERCASE, 0 },
168         { "nouppercase", 0, UPPERCASE },
169         { "center", CENTER, 0 },
170         { "nocenter", 0, CENTER },
171         { "clearscreen", CLEARSCR, 0 },
172         { "noclearscreen", 0, CLEARSCR },
173         { "noclear", 0, CLEARSCR },
174         { "leftadjust", LEFTADJUST, 0 },
175         { "noleftadjust", 0, LEFTADJUST },
176         { "compress", COMPRESS, 0 },
177         { "nocompress", 0, COMPRESS },
178         { "split", SPLIT, 0 },
179         { "nosplit", 0, SPLIT },
180         { "addrfield", ADDRFMT, DATEFMT },
181         { "bell", BELL, 0 },
182         { "nobell", 0, BELL },
183         { "datefield", DATEFMT, ADDRFMT },
184         { "newline", 0, NONEWLINE },
185         { "nonewline", NONEWLINE, 0 },
186         { NULL, 0, 0 }
187 };
188
189
190 static int bellflg   = 0;
191 static int clearflg  = 0;
192 static int dashstuff = 0;
193 static int dobody    = 1;
194 static int forwflg   = 0;
195 static int forwall   = 0;
196
197 static int sleepsw = NOTOK;
198
199 static char *digest = NULL;
200 static int volume = 0;
201 static int issue = 0;
202
203 static int exitstat = 0;
204 static int mhldebug = 0;
205
206 #define PITTY  (-1)
207 #define NOTTY  0
208 #define ISTTY  1
209 static int ontty = NOTTY;
210
211 static int row;
212 static int column;
213
214 static int lm;
215 static int llim;
216 static int ovoff;
217 static int term;
218 static int wid;
219
220 static char *ovtxt;
221
222 static unsigned char *onelp;
223
224 static char *parptr;
225
226 static int num_ignores = 0;
227 static char *ignores[MAXARGS];
228
229 static  jmp_buf env;
230 static  jmp_buf mhlenv;
231
232 static char delim3[] = /* from forw.c */
233         "\n----------------------------------------------------------------------\n\n";
234 static char delim4[] = "\n------------------------------\n\n";
235
236 static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
237
238
239 /*
240 ** Redefine a couple of functions.
241 ** These are undefined later in the code.
242 */
243 #define adios mhladios
244 #define done  mhldone
245
246 /*
247 ** prototypes
248 */
249 static void mhl_format(char *, int, int);
250 static int evalvar(struct mcomp *);
251 static int ptoi(char *, int *);
252 static int ptos(char *, char **);
253 static char *parse(void);
254 static void process(char *, char *, int, int);
255 static void mhlfile(FILE *, char *, int, int);
256 static int mcomp_flags(char *);
257 static char *mcomp_add(long, char *, char *);
258 static void mcomp_format(struct mcomp *, struct mcomp *);
259 static struct mcomp *add_queue(struct mcomp **, struct mcomp **,
260                 char *, char *, int);
261 static void free_queue(struct mcomp **, struct mcomp **);
262 static void putcomp(struct mcomp *, struct mcomp *, int);
263 static char *oneline(char *, long);
264 static void putstr(char *);
265 static void putch(char);
266 static RETSIGTYPE intrser(int);
267 static RETSIGTYPE pipeser(int);
268 static RETSIGTYPE quitser(int);
269 static void mhladios(char *, char *, ...);
270 static void mhldone(int);
271 static void m_popen(char *);
272
273 int mhl(int, char **);
274 int mhlsbr(int, char **, FILE *(*)());
275 void m_pclose(void);
276
277 void clear_screen(void);  /* from termsbr.c */
278 int SOprintf(char *, ...);  /* from termsbr.c */
279 int sc_width(void);  /* from termsbr.c */
280 int sc_length(void);  /* from termsbr.c */
281 int sc_hardcopy(void);  /* from termsbr.c */
282
283
284 int
285 mhl(int argc, char **argv)
286 {
287         int length = 0, nomore = 0;
288         int i, width = 0, vecp = 0;
289         char *cp, *folder = NULL, *form = NULL;
290         char buf[BUFSIZ], *files[MAXARGS];
291         char **argp, **arguments;
292
293         invo_name = mhbasename(argv[0]);
294
295         /* read user profile/context */
296         context_read();
297
298         arguments = getarguments(invo_name, argc, argv, 1);
299         argp = arguments;
300
301         if ((cp = getenv("MHLDEBUG")) && *cp)
302                 mhldebug++;
303
304         while ((cp = *argp++)) {
305                 if (*cp == '-') {
306                         switch (smatch(++cp, mhlswitches)) {
307                         case AMBIGSW:
308                                 ambigsw(cp, mhlswitches);
309                                 done(1);
310                         case UNKWNSW:
311                                 adios(NULL, "-%s unknown\n", cp);
312
313                         case HELPSW:
314                                 snprintf(buf, sizeof(buf), "%s [switches] [files ...]", invo_name);
315                                 print_help(buf, mhlswitches, 1);
316                                 done(1);
317                         case VERSIONSW:
318                                 print_version(invo_name);
319                                 done(1);
320
321                         case BELLSW:
322                                 bellflg = 1;
323                                 continue;
324                         case NBELLSW:
325                                 bellflg = -1;
326                                 continue;
327
328                         case CLRSW:
329                                 clearflg = 1;
330                                 continue;
331                         case NCLRSW:
332                                 clearflg = -1;
333                                 continue;
334
335                         case FOLDSW:
336                                 if (!(folder = *argp++) || *folder == '-')
337                                         adios(NULL, "missing argument to %s",
338                                                         argp[-2]);
339                                 continue;
340                         case FORMSW:
341                                 if (!(form = *argp++) || *form == '-')
342                                         adios(NULL, "missing argument to %s",
343                                                         argp[-2]);
344                                 continue;
345
346                         case SLEEPSW:
347                                 if (!(cp = *argp++) || *cp == '-')
348                                         adios(NULL, "missing argument to %s",
349                                                         argp[-2]);
350                                 sleepsw = atoi(cp);  /* ZERO ok! */
351                                 continue;
352
353                         case PROGSW:
354                                 if (!(moreproc = *argp++) || *moreproc == '-')
355                                         adios(NULL, "missing argument to %s",
356                                                         argp[-2]);
357                                 continue;
358                         case NPROGSW:
359                                 nomore++;
360                                 continue;
361
362                         case LENSW:
363                                 if (!(cp = *argp++) || *cp == '-')
364                                         adios(NULL, "missing argument to %s",
365                                                         argp[-2]);
366                                 if ((length = atoi(cp)) < 1)
367                                         adios(NULL, "bad argument %s %s",
368                                                         argp[-2], cp);
369                                 continue;
370                         case WIDTHSW:
371                                 if (!(cp = *argp++) || *cp == '-')
372                                         adios(NULL, "missing argument to %s",
373                                                         argp[-2]);
374                                 if ((width = atoi(cp)) < 1)
375                                         adios(NULL, "bad argument %s %s",
376                                                         argp[-2], cp);
377                                 continue;
378
379                         case DGSTSW:
380                                 if (!(digest = *argp++) ||
381                                                 *digest == '-')
382                                         adios(NULL, "missing argument to %s",
383                                                         argp[-2]);
384                                 continue;
385                         case ISSUESW:
386                                 if (!(cp = *argp++) || *cp == '-')
387                                         adios(NULL, "missing argument to %s",
388                                                         argp[-2]);
389                                 if ((issue = atoi(cp)) < 1)
390                                         adios(NULL, "bad argument %s %s",
391                                                         argp[-2], cp);
392                                 continue;
393                         case VOLUMSW:
394                                 if (!(cp = *argp++) || *cp == '-')
395                                         adios(NULL, "missing argument to %s",
396                                                         argp[-2]);
397                                 if ((volume = atoi(cp)) < 1)
398                                         adios(NULL, "bad argument %s %s",
399                                                         argp[-2], cp);
400                                 continue;
401
402                         case FORW2SW:
403                                 forwall++;  /* fall */
404                         case FORW1SW:
405                                 forwflg++;
406                                 clearflg = -1;/* XXX */
407                                 continue;
408
409                         case BITSTUFFSW:
410                                 dashstuff = 1;  /* trinary logic */
411                                 continue;
412                         case NBITSTUFFSW:
413                                 dashstuff = -1;  /* trinary logic */
414                                 continue;
415
416                         case NBODYSW:
417                                 dobody = 0;
418                                 continue;
419                         }
420                 }
421                 files[vecp++] = cp;
422         }
423
424         if (!folder)
425                 folder = getenv("mhfolder");
426
427         if (isatty(fileno(stdout))) {
428                 if (!nomore && !sc_hardcopy() && moreproc && *moreproc != '\0') {
429                         if (mhl_action) {
430                                 SIGNAL(SIGINT, SIG_IGN);
431                                 SIGNAL2(SIGQUIT, quitser);
432                         }
433                         SIGNAL2(SIGPIPE, pipeser);
434                         m_popen(moreproc);
435                         ontty = PITTY;
436                 } else {
437                         SIGNAL(SIGINT, SIG_IGN);
438                         SIGNAL2(SIGQUIT, quitser);
439                         ontty = ISTTY;
440                 }
441         } else {
442                 ontty = NOTTY;
443         }
444
445         mhl_format(form ? form : mhlformat, length, width);
446
447         if (vecp == 0) {
448                 process(folder, NULL, 1, vecp = 1);
449         } else {
450                 for (i = 0; i < vecp; i++)
451                         process(folder, files[i], i + 1, vecp);
452         }
453
454         if (forwall) {
455                 if (digest) {
456                         printf("%s", delim4);
457                         if (volume == 0) {
458                                 snprintf(buf, sizeof(buf),
459                                         "End of %s Digest\n", digest);
460                         } else {
461                                 snprintf(buf, sizeof(buf), "End of %s Digest [Volume %d Issue %d]\n", digest, volume, issue);
462                         }
463                         i = strlen(buf);
464                         for (cp = buf + i; i > 1; i--)
465                                 *cp++ = '*';
466                         *cp++ = '\n';
467                         *cp = 0;
468                         printf("%s", buf);
469                 } else
470                         printf("\n------- End of Forwarded Message%s\n\n",
471                                 vecp > 1 ? "s" : "");
472         }
473
474         fflush(stdout);
475         if (ferror(stdout)) {
476                 adios("output", "error writing");
477         }
478
479         if (clearflg > 0 && ontty == NOTTY)
480                 clear_screen();
481
482         if (ontty == PITTY)
483                 m_pclose();
484
485         return exitstat;
486 }
487
488
489 static void
490 mhl_format(char *file, int length, int width)
491 {
492         int i;
493         char *bp, *cp, **ip;
494         char *ap, buffer[BUFSIZ], name[NAMESZ];
495         struct mcomp *c1;
496         struct stat st;
497         FILE *fp;
498         static dev_t dev = 0;
499         static ino_t ino = 0;
500         static time_t mtime = 0;
501
502         if (fmthd != NULL) {
503                 if (stat(etcpath(file), &st) != NOTOK
504                         && mtime == st.st_mtime
505                         && dev == st.st_dev
506                         && ino == st.st_ino)
507                         goto out;
508                 else
509                         free_queue(&fmthd, &fmttl);
510         }
511
512         if ((fp = fopen(etcpath(file), "r")) == NULL)
513                 adios(file, "unable to open format file");
514
515         if (fstat(fileno(fp), &st) != NOTOK) {
516                 mtime = st.st_mtime;
517                 dev = st.st_dev;
518                 ino = st.st_ino;
519         }
520
521         global.c_ovtxt = global.c_nfs = NULL;
522         global.c_fmt = NULL;
523         global.c_offset = 0;
524         global.c_ovoff = -1;
525         if ((i = sc_width()) > 5)
526                 global.c_width = i;
527         global.c_cwidth = -1;
528         if ((i = sc_length()) > 5)
529                 global.c_length = i - 1;
530         global.c_flags = BELL;  /* BELL is default */
531         *(ip = ignores) = NULL;
532
533         while (vfgets(fp, &ap) == OK) {
534                 bp = ap;
535                 if (*bp == ';')
536                         continue;
537
538                 if ((cp = strchr(bp, '\n')))
539                         *cp = 0;
540
541                 if (*bp == ':') {
542                         c1 = add_queue(&fmthd, &fmttl, NULL, bp + 1,
543                                         CLEARTEXT);
544                         continue;
545                 }
546
547                 parptr = bp;
548                 strncpy(name, parse(), sizeof(name));
549                 switch(*parptr) {
550                 case '\0':
551                 case ',':
552                 case '=':
553                         /*
554                         ** Split this list of fields to ignore, and copy
555                         ** it to the end of the current "ignores" list.
556                         */
557                         if (!mh_strcasecmp(name, "ignores")) {
558                                 char **tmparray, **p;
559                                 int n = 0;
560
561                                 /* split the fields */
562                                 tmparray = brkstring(getcpy(++parptr), ",",
563                                                 NULL);
564
565                                 /* count number of fields split */
566                                 p = tmparray;
567                                 while (*p++)
568                                         n++;
569
570                                 /*
571                                 ** copy pointers to split fields
572                                 ** to ignores array
573                                 */
574                                 ip = copyip(tmparray, ip,
575                                                 MAXARGS - num_ignores);
576                                 num_ignores += n;
577                                 continue;
578                         }
579                         parptr = bp;
580                         while (*parptr) {
581                                 if (evalvar(&global))
582                                         adios(NULL, "format file syntax error: %s", bp);
583                                 if (*parptr)
584                                         parptr++;
585                         }
586                         continue;
587
588                 case ':':
589                         c1 = add_queue(&fmthd, &fmttl, name, NULL, INIT);
590                         while (*parptr == ':' || *parptr == ',') {
591                                 parptr++;
592                                 if (evalvar(c1))
593                                         adios(NULL, "format file syntax error: %s", bp);
594                         }
595                         if (!c1->c_nfs && global.c_nfs) {
596                                 if ((c1->c_flags & DATEFMT) &&
597                                                 (global.c_flags & DATEFMT)) {
598                                         c1->c_nfs = getcpy(global.c_nfs);
599                                 } else if ((c1->c_flags & ADDRFMT) &&
600                                                 (global.c_flags & ADDRFMT)) {
601                                         c1->c_nfs = getcpy(global.c_nfs);
602                                 }
603                         }
604                         continue;
605
606                 default:
607                         adios(NULL, "format file syntax error: %s", bp);
608                 }
609         }
610         fclose(fp);
611
612         if (mhldebug) {
613                 for (c1 = fmthd; c1; c1 = c1->c_next) {
614                         fprintf(stderr, "c1: name=\"%s\" text=\"%s\" ovtxt=\"%s\"\n", c1->c_name, c1->c_text, c1->c_ovtxt);
615                         fprintf(stderr, "\tnfs=0x%x fmt=0x%x\n", (unsigned int)(unsigned long) c1->c_nfs, (unsigned int)(unsigned long) c1->c_fmt);
616                         fprintf(stderr, "\toffset=%d ovoff=%d width=%d cwidth=%d length=%d\n", c1->c_offset, c1->c_ovoff, c1->c_width, c1->c_cwidth, c1->c_length);
617                         fprintf (stderr, "\tflags=%s\n", snprintb(buffer, sizeof(buffer), (unsigned) c1->c_flags, LBITS));
618                 }
619         }
620
621 out:
622         if (clearflg == 1) {
623                 global.c_flags |= CLEARSCR;
624         } else {
625                 if (clearflg == -1)
626                         global.c_flags &= ~CLEARSCR;
627         }
628
629         switch (bellflg) {  /* command line may override format file */
630         case 1:
631                 global.c_flags |= BELL;
632                 break;
633         case -1:
634                 global.c_flags &= ~BELL;
635                 break;
636         }
637
638         if (length)
639                 global.c_length = length;
640         if (width)
641                 global.c_width = width;
642         if (global.c_length < 5)
643                 global.c_length = 10000;
644         if (global.c_width < 5)
645                 global.c_width = 10000;
646 }
647
648
649 static int
650 evalvar(struct mcomp *c1)
651 {
652         char *cp, name[NAMESZ];
653         struct triple *ap;
654
655         if (!*parptr)
656                 return 0;
657         strncpy(name, parse(), sizeof(name));
658
659         if (!mh_strcasecmp(name, "component")) {
660                 if (ptos(name, &c1->c_text))
661                         return 1;
662                 c1->c_flags &= ~NOCOMPONENT;
663                 return 0;
664         }
665
666         if (!mh_strcasecmp(name, "overflowtext"))
667                 return ptos(name, &c1->c_ovtxt);
668
669         if (!mh_strcasecmp(name, "formatfield")) {
670                 char *nfs;
671
672                 if (ptos(name, &cp))
673                         return 1;
674                 nfs = new_fs(NULL, NULL, cp);
675                 c1->c_nfs = getcpy(nfs);
676                 c1->c_flags |= FORMAT;
677                 return 0;
678         }
679
680         if (!mh_strcasecmp(name, "decode")) {
681                 char *nfs;
682
683                 nfs = new_fs(NULL, NULL, "%(decode{text})");
684                 c1->c_nfs = getcpy(nfs);
685                 c1->c_flags |= FORMAT;
686                 return 0;
687         }
688
689         if (!mh_strcasecmp(name, "offset"))
690                 return ptoi(name, &c1->c_offset);
691         if (!mh_strcasecmp(name, "overflowoffset"))
692                 return ptoi(name, &c1->c_ovoff);
693         if (!mh_strcasecmp(name, "width"))
694                 return ptoi(name, &c1->c_width);
695         if (!mh_strcasecmp(name, "compwidth"))
696                 return ptoi(name, &c1->c_cwidth);
697         if (!mh_strcasecmp(name, "length"))
698                 return ptoi(name, &c1->c_length);
699         if (!mh_strcasecmp(name, "nodashstuffing"))
700                 return (dashstuff = -1);
701
702         for (ap = triples; ap->t_name; ap++)
703                 if (!mh_strcasecmp(ap->t_name, name)) {
704                         c1->c_flags |= ap->t_on;
705                         c1->c_flags &= ~ap->t_off;
706                         return 0;
707                 }
708
709         return 1;
710 }
711
712
713 static int
714 ptoi(char *name, int *i)
715 {
716         char *cp;
717
718         if (*parptr++ != '=' || !*(cp = parse())) {
719                 advise(NULL, "missing argument to variable %s", name);
720                 return 1;
721         }
722
723         *i = atoi(cp);
724         return 0;
725 }
726
727
728 static int
729 ptos(char *name, char **s)
730 {
731         char c, *cp;
732
733         if (*parptr++ != '=') {
734                 advise(NULL, "missing argument to variable %s", name);
735                 return 1;
736         }
737
738         if (*parptr != '"') {
739                 for (cp = parptr; *parptr && *parptr != ':' && *parptr != ',';
740                                 parptr++)
741                         continue;
742         } else {
743                 for (cp = ++parptr; *parptr && *parptr != '"'; parptr++)
744                         if (*parptr == QUOTE)
745                                 if (!*++parptr)
746                                         parptr--;
747         }
748         c = *parptr;
749         *parptr = 0;
750         *s = getcpy(cp);
751         if ((*parptr = c) == '"')
752                 parptr++;
753         return 0;
754 }
755
756
757 static char *
758 parse(void)
759 {
760         int c;
761         char *cp;
762         static char result[NAMESZ];
763
764         for (cp = result; *parptr && (cp - result < NAMESZ); parptr++) {
765                 c = *parptr;
766                 if (isalnum(c)
767                                 || c == '.'
768                                 || c == '-'
769                                 || c == '_'
770                                 || c == '['
771                                 || c == ']')
772                         *cp++ = c;
773                 else
774                         break;
775         }
776         *cp = '\0';
777
778         return result;
779 }
780
781
782 static void
783 process(char *folder, char *fname, int ofilen, int ofilec)
784 {
785         char *cp = NULL;
786         FILE *fp = NULL;
787         struct mcomp *c1;
788
789         switch (setjmp(env)) {
790         case OK:
791                 if (fname) {
792                         fp = mhl_action ? (*mhl_action) (fname) :
793                                         fopen(fname, "r");
794                         if (fp == NULL) {
795                                 advise(fname, "unable to open");
796                                 exitstat++;
797                                 return;
798                         }
799                 } else {
800                         fname = "(stdin)";
801                         fp = stdin;
802                 }
803                 cp = folder ? concat(folder, ":", fname, NULL) : getcpy(fname);
804                 if (ontty != PITTY)
805                         SIGNAL(SIGINT, intrser);
806                 mhlfile(fp, cp, ofilen, ofilec);  /* FALL THROUGH! */
807
808         default:
809                 if (ontty != PITTY)
810                         SIGNAL(SIGINT, SIG_IGN);
811                 if (mhl_action == NULL && fp != stdin)
812                         fclose(fp);
813                 free(cp);
814                 if (holder.c_text) {
815                         free(holder.c_text);
816                         holder.c_text = NULL;
817                 }
818                 free_queue(&msghd, &msgtl);
819                 for (c1 = fmthd; c1; c1 = c1->c_next)
820                         c1->c_flags &= ~HDROUTPUT;
821                 break;
822         }
823 }
824
825
826 static void
827 mhlfile(FILE *fp, char *mname, int ofilen, int ofilec)
828 {
829         int state;
830         struct mcomp *c1, *c2, *c3;
831         char **ip, name[NAMESZ], buf[BUFSIZ];
832
833         if (forwall) {
834                 if (digest)
835                         printf("%s", ofilen == 1 ? delim3 : delim4);
836                 else {
837                         printf("\n-------");
838                         if (ofilen == 1)
839                                 printf(" Forwarded Message%s",
840                                                 ofilec > 1 ? "s" : "");
841                         else
842                                 printf(" Message %d", ofilen);
843                         printf("\n\n");
844                 }
845         } else {
846                 switch (ontty) {
847                 case PITTY:
848                         if (ofilec > 1) {
849                                 if (ofilen > 1) {
850                                         if ((global.c_flags & CLEARSCR))
851                                                 clear_screen();
852                                         else
853                                                 printf("\n\n\n");
854                                 }
855                                 printf(">>> %s\n\n", mname);
856                         }
857                         break;
858
859                 case ISTTY:
860                         strncpy(buf, "\n", sizeof(buf));
861                         if (ofilec > 1) {
862                                 if (SOprintf("Press <return> to list \"%s\"...", mname)) {
863                                         if (ofilen > 1)
864                                                 printf("\n\n\n");
865                                         printf("Press <return> to list \"%s\"...", mname);
866                                 }
867                                 fflush(stdout);
868                                 buf[0] = 0;
869                                 read(fileno(stdout), buf, sizeof(buf));
870                         }
871                         if (strchr(buf, '\n')) {
872                                 if ((global.c_flags & CLEARSCR))
873                                         clear_screen();
874                         } else
875                                 printf("\n");
876                         break;
877
878                 default:
879                         if (ofilec > 1) {
880                                 if (ofilen > 1) {
881                                         printf("\n\n\n");
882                                         if (clearflg > 0)
883                                                 clear_screen();
884                                 }
885                                 printf(">>> %s\n\n", mname);
886                         }
887                         break;
888                 }
889         }
890
891         for (state = FLD;;) {
892                 switch (state = m_getfld(state, name, buf, sizeof(buf), fp)) {
893                 case FLD:
894                 case FLDPLUS:
895                         for (ip = ignores; *ip; ip++)
896                                 if (!mh_strcasecmp(name, *ip)) {
897                                         while (state == FLDPLUS)
898                                                 state = m_getfld(state, name, buf, sizeof(buf), fp);
899                                         break;
900                                 }
901                         if (*ip)
902                                 continue;
903
904                         for (c2 = fmthd; c2; c2 = c2->c_next)
905                                 if (!mh_strcasecmp(c2->c_name, name))
906                                         break;
907                         c1 = NULL;
908                         if (!((c3 = c2 ? c2 : &global)->c_flags & SPLIT))
909                                 for (c1 = msghd; c1; c1 = c1->c_next)
910                                         if (!mh_strcasecmp(c1->c_name,
911                                                         c3->c_name)) {
912                                                 c1->c_text = mcomp_add(c1->c_flags, buf, c1->c_text);
913                                                 break;
914                                         }
915                         if (c1 == NULL)
916                                 c1 = add_queue(&msghd, &msgtl, name, buf, 0);
917                         while (state == FLDPLUS) {
918                                 state = m_getfld(state, name, buf,
919                                                 sizeof(buf), fp);
920                                 c1->c_text = add(buf, c1->c_text);
921                         }
922                         if (c2 == NULL)
923                                 c1->c_flags |= EXTRA;
924                         continue;
925
926                 case BODY:
927                 case FILEEOF:
928                         row = column = 0;
929                         for (c1 = fmthd; c1; c1 = c1->c_next) {
930                                 if (c1->c_flags & CLEARTEXT) {
931                                         putcomp(c1, c1, ONECOMP);
932                                         continue;
933                                 }
934                                 if (!mh_strcasecmp(c1->c_name, "messagename")) {
935                                         holder.c_text = concat("(Message ",
936                                                         mname, ")\n", NULL);
937                                         putcomp(c1, &holder, ONECOMP);
938                                         free(holder.c_text);
939                                         holder.c_text = NULL;
940                                         continue;
941                                 }
942                                 if (!mh_strcasecmp(c1->c_name, "extras")) {
943                                         for (c2 = msghd; c2; c2 = c2->c_next)
944                                                 if (c2->c_flags & EXTRA)
945                                                         putcomp(c1, c2, TWOCOMP);
946                                         continue;
947                                 }
948                                 if (dobody && !mh_strcasecmp(c1->c_name, "body")) {
949                                         holder.c_text = mh_xmalloc(sizeof(buf));
950                                         strncpy(holder.c_text, buf, sizeof(buf));
951                                         while (state == BODY) {
952                                                 putcomp(c1, &holder, BODYCOMP);
953                                                 state = m_getfld(state, name, holder.c_text, sizeof(buf), fp);
954                                         }
955                                         free(holder.c_text);
956                                         holder.c_text = NULL;
957                                         continue;
958                                 }
959                                 for (c2 = msghd; c2; c2 = c2->c_next)
960                                         if (!mh_strcasecmp(c2->c_name,
961                                                         c1->c_name)) {
962                                                 putcomp(c1, c2, ONECOMP);
963                                                 if (!(c1->c_flags & SPLIT))
964                                                         break;
965                                         }
966                         }
967                         return;
968
969                 case LENERR:
970                 case FMTERR:
971                         advise(NULL, "format error in message %s", mname);
972                         exitstat++;
973                         return;
974
975                 default:
976                         adios(NULL, "getfld() returned %d", state);
977                 }
978         }
979 }
980
981
982 static int
983 mcomp_flags(char *name)
984 {
985         struct pair *ap;
986
987         for (ap = pairs; ap->p_name; ap++)
988                 if (!mh_strcasecmp(ap->p_name, name))
989                         return (ap->p_flags);
990
991         return 0;
992 }
993
994
995 static char *
996 mcomp_add(long flags, char *s1, char *s2)
997 {
998         char *dp;
999
1000         if (!(flags & ADDRFMT))
1001                 return add(s1, s2);
1002
1003         if (s2 && *(dp = s2 + strlen(s2) - 1) == '\n')
1004                 *dp = 0;
1005
1006         return add(s1, add(",\n", s2));
1007 }
1008
1009
1010 struct pqpair {
1011         char *pq_text;
1012         char *pq_error;
1013         struct pqpair *pq_next;
1014 };
1015
1016
1017 static void
1018 mcomp_format(struct mcomp *c1, struct mcomp *c2)
1019 {
1020         int dat[5];
1021         char *ap, *cp;
1022         char buffer[BUFSIZ], error[BUFSIZ];
1023         struct comp *cptr;
1024         struct pqpair *p, *q;
1025         struct pqpair pq;
1026         struct mailname *mp;
1027
1028         ap = c2->c_text;
1029         c2->c_text = NULL;
1030         dat[0] = 0;
1031         dat[1] = 0;
1032         dat[2] = 0;
1033         dat[3] = sizeof(buffer) - 1;
1034         dat[4] = 0;
1035         fmt_compile(c1->c_nfs, &c1->c_fmt);
1036
1037         if (!(c1->c_flags & ADDRFMT)) {
1038                 FINDCOMP(cptr, "text");
1039                 if (cptr)
1040                         cptr->c_text = ap;
1041                 if ((cp = strrchr(ap, '\n')))  /* drop ending newline */
1042                         if (!cp[1])
1043                                 *cp = 0;
1044
1045                 fmt_scan(c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
1046                 /* Don't need to append a newline, dctime() already did */
1047                 c2->c_text = getcpy(buffer);
1048
1049                 free(ap);
1050                 return;
1051         }
1052
1053         (q = &pq)->pq_next = NULL;
1054         while ((cp = getname(ap))) {
1055                 if ((p = (struct pqpair *)
1056                                 calloc((size_t) 1, sizeof(*p))) == NULL)
1057                         adios(NULL, "unable to allocate pqpair memory");
1058
1059                 if ((mp = getm(cp, NULL, 0, AD_NAME, error)) == NULL) {
1060                         p->pq_text = getcpy(cp);
1061                         p->pq_error = getcpy(error);
1062                 } else {
1063                         p->pq_text = getcpy(mp->m_text);
1064                         mnfree(mp);
1065                 }
1066                 q = (q->pq_next = p);
1067         }
1068
1069         for (p = pq.pq_next; p; p = q) {
1070                 FINDCOMP(cptr, "text");
1071                 if (cptr)
1072                         cptr->c_text = p->pq_text;
1073                 FINDCOMP(cptr, "error");
1074                 if (cptr)
1075                         cptr->c_text = p->pq_error;
1076
1077                 fmt_scan(c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
1078                 if (*buffer) {
1079                         if (c2->c_text)
1080                                 c2->c_text = add(",\n", c2->c_text);
1081                         if (*(cp = buffer + strlen(buffer) - 1) == '\n')
1082                                 *cp = 0;
1083                         c2->c_text = add(buffer, c2->c_text);
1084                 }
1085
1086                 free(p->pq_text);
1087                 if (p->pq_error)
1088                         free(p->pq_error);
1089                 q = p->pq_next;
1090                 free((char *) p);
1091         }
1092
1093         c2->c_text = add("\n", c2->c_text);
1094         free (ap);
1095 }
1096
1097
1098 static struct mcomp *
1099 add_queue(struct mcomp **head, struct mcomp **tail, char *name,
1100                 char *text, int flags)
1101 {
1102         struct mcomp *c1;
1103
1104         if ((c1 = (struct mcomp *) calloc((size_t) 1, sizeof(*c1))) == NULL)
1105                 adios(NULL, "unable to allocate comp memory");
1106
1107         c1->c_flags = flags & ~INIT;
1108         if ((c1->c_name = name ? getcpy(name) : NULL))
1109                 c1->c_flags |= mcomp_flags(c1->c_name);
1110         c1->c_text = text ? getcpy(text) : NULL;
1111         if (flags & INIT) {
1112                 if (global.c_ovtxt)
1113                         c1->c_ovtxt = getcpy(global.c_ovtxt);
1114                 c1->c_offset = global.c_offset;
1115                 c1->c_ovoff = global. c_ovoff;
1116                 c1->c_width = c1->c_length = 0;
1117                 c1->c_cwidth = global.c_cwidth;
1118                 c1->c_flags |= global.c_flags & GFLAGS;
1119         }
1120         if (*head == NULL)
1121                 *head = c1;
1122         if (*tail != NULL)
1123                 (*tail)->c_next = c1;
1124         *tail = c1;
1125
1126         return c1;
1127 }
1128
1129
1130 static void
1131 free_queue(struct mcomp **head, struct mcomp **tail)
1132 {
1133         struct mcomp *c1, *c2;
1134
1135         for (c1 = *head; c1; c1 = c2) {
1136                 c2 = c1->c_next;
1137                 if (c1->c_name)
1138                         free(c1->c_name);
1139                 if (c1->c_text)
1140                         free(c1->c_text);
1141                 if (c1->c_ovtxt)
1142                         free(c1->c_ovtxt);
1143                 if (c1->c_nfs)
1144                         free(c1->c_nfs);
1145                 if (c1->c_fmt)
1146                         free((char *) c1->c_fmt);
1147                 free((char *) c1);
1148         }
1149
1150         *head = *tail = NULL;
1151 }
1152
1153
1154 static void
1155 putcomp(struct mcomp *c1, struct mcomp *c2, int flag)
1156 {
1157         int count, cchdr;
1158         unsigned char *cp;
1159
1160         cchdr = 0;
1161         lm = 0;
1162         llim = c1->c_length ? c1->c_length : -1;
1163         wid = c1->c_width ? c1->c_width : global.c_width;
1164         ovoff = (c1->c_ovoff >= 0 ? c1->c_ovoff : global.c_ovoff)
1165                         + c1->c_offset;
1166         if ((ovtxt = c1->c_ovtxt ? c1->c_ovtxt : global.c_ovtxt) == NULL)
1167                 ovtxt = "";
1168         if (wid < ovoff + strlen(ovtxt) + 5)
1169                 adios(NULL, "component: %s width(%d) too small for overflow(%d)", c1->c_name, wid, ovoff + strlen(ovtxt) + 5);
1170         onelp = NULL;
1171
1172         if (c1->c_flags & CLEARTEXT) {
1173                 putstr(c1->c_text);
1174                 putstr("\n");
1175                 return;
1176         }
1177
1178         if (c1->c_nfs && (c1->c_flags & (ADDRFMT | DATEFMT | FORMAT)))
1179                 mcomp_format(c1, c2);
1180
1181         if (c1->c_flags & CENTER) {
1182                 count = (c1->c_width ? c1->c_width : global.c_width)
1183                                 - c1->c_offset - strlen(c2->c_text);
1184                 if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT))
1185                         count -= strlen(c1->c_text ? c1->c_text : c1->c_name)
1186                                         + 2;
1187                 lm = c1->c_offset + (count / 2);
1188         } else {
1189                 if (c1->c_offset)
1190                         lm = c1->c_offset;
1191         }
1192
1193         if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT)) {
1194                 if (c1->c_flags & UPPERCASE)  /* uppercase component also */
1195                         for (cp = (c1->c_text ? c1->c_text : c1->c_name); *cp; cp++)
1196                                 if (islower(*cp))
1197                                         *cp = toupper(*cp);
1198                 putstr(c1->c_text ? c1->c_text : c1->c_name);
1199                 if (flag != BODYCOMP) {
1200                         putstr(": ");
1201                         if (!(c1->c_flags & SPLIT))
1202                                 c1->c_flags |= HDROUTPUT;
1203
1204                 cchdr++;
1205                 if ((count = c1->c_cwidth -
1206                         strlen(c1->c_text ? c1->c_text : c1->c_name) - 2) > 0)
1207                         while (count--)
1208                                 putstr(" ");
1209                 } else
1210                         c1->c_flags |= HDROUTPUT;  /* for BODYCOMP */
1211         }
1212
1213         if (flag == TWOCOMP && !(c2->c_flags & HDROUTPUT)
1214                 && !(c2->c_flags & NOCOMPONENT)) {
1215                 if (c1->c_flags & UPPERCASE)
1216                         for (cp = c2->c_name; *cp; cp++)
1217                                 if (islower(*cp))
1218                                         *cp = toupper(*cp);
1219                 putstr(c2->c_name);
1220                 putstr(": ");
1221                 if (!(c1->c_flags & SPLIT))
1222                         c2->c_flags |= HDROUTPUT;
1223
1224                 cchdr++;
1225                 if ((count = c1->c_cwidth - strlen(c2->c_name) - 2) > 0)
1226                         while (count--)
1227                                 putstr(" ");
1228         }
1229         if (c1->c_flags & UPPERCASE)
1230                 for (cp = c2->c_text; *cp; cp++)
1231                         if (islower(*cp))
1232                                 *cp = toupper(*cp);
1233
1234         count = 0;
1235         if (cchdr) {
1236                 if (flag == TWOCOMP)
1237                         count = (c1->c_cwidth >= 0) ?
1238                                         c1->c_cwidth : strlen(c2->c_name) + 2;
1239                 else
1240                         count = (c1->c_cwidth >= 0) ?
1241                                         c1->c_cwidth : strlen(c1->c_text ?
1242                                         c1->c_text : c1->c_name) + 2;
1243         }
1244         count += c1->c_offset;
1245
1246         if ((cp = oneline(c2->c_text, c1->c_flags)))
1247            putstr(cp);
1248         if (term == '\n')
1249                 putstr("\n");
1250         while ((cp = oneline(c2->c_text, c1->c_flags))) {
1251                 lm = count;
1252                 if (flag == BODYCOMP && !(c1->c_flags & NOCOMPONENT))
1253                         putstr(c1->c_text ? c1->c_text : c1->c_name);
1254                 if (*cp)
1255                         putstr(cp);
1256                 if (term == '\n')
1257                         putstr("\n");
1258         }
1259         if (flag == BODYCOMP && term == '\n')
1260                 c1->c_flags &= ~HDROUTPUT;  /* Buffer ended on a newline */
1261 }
1262
1263
1264 static char *
1265 oneline(char *stuff, long flags)
1266 {
1267         int spc;
1268         char *cp, *ret;
1269
1270         if (onelp == NULL)
1271                 onelp = stuff;
1272         if (*onelp == 0)
1273                 return (onelp = NULL);
1274
1275         ret = onelp;
1276         term = 0;
1277         if (flags & COMPRESS) {
1278                 for (spc = 1, cp = ret; *onelp; onelp++)
1279                         if (isspace(*onelp)) {
1280                                 if (*onelp == '\n' &&
1281                                                 (!onelp[1] ||
1282                                                 (flags & ADDRFMT))) {
1283                                         term = '\n';
1284                                         *onelp++ = 0;
1285                                         break;
1286                                 } else if (!spc) {
1287                                         *cp++ = ' ';
1288                                         spc++;
1289                                 }
1290                         } else {
1291                                 *cp++ = *onelp;
1292                                 spc = 0;
1293                         }
1294
1295                 *cp = 0;
1296         } else {
1297                 while (*onelp && *onelp != '\n')
1298                         onelp++;
1299                 if (*onelp == '\n') {
1300                         term = '\n';
1301                         *onelp++ = 0;
1302                 }
1303                 if (flags & LEFTADJUST)
1304                         while (*ret == ' ' || *ret == '\t')
1305                                 ret++;
1306         }
1307         if (*onelp == 0 && term == '\n' && (flags & NONEWLINE))
1308                 term = 0;
1309
1310         return ret;
1311 }
1312
1313
1314 static void
1315 putstr(char *string)
1316 {
1317         if (!column && lm > 0) {
1318                 while (lm > 0)
1319                         if (lm >= 8) {
1320                                 putch('\t');
1321                                 lm -= 8;
1322                         } else {
1323                                 putch(' ');
1324                                 lm--;
1325                         }
1326         }
1327         lm = 0;
1328         while (*string)
1329                 putch(*string++);
1330 }
1331
1332
1333 static void
1334 putch(char ch)
1335 {
1336         char buf[BUFSIZ];
1337
1338         if (llim == 0)
1339                 return;
1340
1341         switch (ch) {
1342         case '\n':
1343                 if (llim > 0)
1344                         llim--;
1345                 column = 0;
1346                 row++;
1347                 if (ontty != ISTTY || row != global.c_length)
1348                         break;
1349                 if (global.c_flags & BELL)
1350                         putchar('\007');
1351                 fflush(stdout);
1352                 buf[0] = 0;
1353                 read(fileno(stdout), buf, sizeof(buf));
1354                 if (strchr(buf, '\n')) {
1355                         if (global.c_flags & CLEARSCR)
1356                                 clear_screen();
1357                         row = 0;
1358                 } else {
1359                         putchar('\n');
1360                         row = global.c_length / 3;
1361                 }
1362                 return;
1363
1364         case '\t':
1365                 column |= 07;
1366                 column++;
1367                 break;
1368
1369         case '\b':
1370                 column--;
1371                 break;
1372
1373         case '\r':
1374                 column = 0;
1375                 break;
1376
1377         default:
1378                 /*
1379                 ** If we are forwarding this message, and the first
1380                 ** column contains a dash, then add a dash and a space.
1381                 */
1382                 if (column == 0 && forwflg && (dashstuff >= 0) && ch == '-') {
1383                         putchar('-');
1384                         putchar(' ');
1385                 }
1386                 if (ch >= ' ')
1387                         column++;
1388                 break;
1389         }
1390
1391         if (column >= wid) {
1392                 putch('\n');
1393                 if (ovoff > 0)
1394                         lm = ovoff;
1395                 putstr(ovtxt ? ovtxt : "");
1396                 putch(ch);
1397                 return;
1398         }
1399
1400         putchar(ch);
1401 }
1402
1403
1404 static RETSIGTYPE
1405 intrser(int i)
1406 {
1407 #ifndef RELIABLE_SIGNALS
1408         SIGNAL(SIGINT, intrser);
1409 #endif
1410
1411         discard(stdout);
1412         putchar('\n');
1413         longjmp(env, DONE);
1414 }
1415
1416
1417 static RETSIGTYPE
1418 pipeser(int i)
1419 {
1420 #ifndef RELIABLE_SIGNALS
1421         SIGNAL(SIGPIPE, pipeser);
1422 #endif
1423
1424         done(NOTOK);
1425 }
1426
1427
1428 static RETSIGTYPE
1429 quitser(int i)
1430 {
1431 #ifndef RELIABLE_SIGNALS
1432         SIGNAL(SIGQUIT, quitser);
1433 #endif
1434
1435         putchar('\n');
1436         fflush(stdout);
1437         done(NOTOK);
1438 }
1439
1440
1441 int
1442 mhlsbr(int argc, char **argv, FILE *(*action)())
1443 {
1444         SIGNAL_HANDLER istat = NULL, pstat = NULL, qstat = NULL;
1445         char *cp = NULL;
1446         struct mcomp *c1;
1447
1448         switch (setjmp(mhlenv)) {
1449         case OK:
1450                 cp = invo_name;
1451                 sleepsw = 0;  /* XXX */
1452                 bellflg = clearflg = forwflg = forwall = exitstat = 0;
1453                 digest = NULL;
1454                 ontty = NOTTY;
1455                 mhl_action = action;
1456
1457                 /*
1458                 ** If signal is at default action, then start ignoring
1459                 ** it, else let it set to its current action.
1460                 */
1461                 if ((istat = SIGNAL(SIGINT, SIG_IGN)) != SIG_DFL)
1462                         SIGNAL(SIGINT, istat);
1463                 if ((qstat = SIGNAL(SIGQUIT, SIG_IGN)) != SIG_DFL)
1464                         SIGNAL(SIGQUIT, qstat);
1465                 pstat = SIGNAL(SIGPIPE, pipeser);
1466                 mhl(argc, argv);  /* FALL THROUGH! */
1467
1468         default:
1469                 SIGNAL(SIGINT, istat);
1470                 SIGNAL(SIGQUIT, qstat);
1471                 SIGNAL(SIGPIPE, SIG_IGN);  /*
1472                                 ** should probably change to block
1473                                 ** instead
1474                                 */
1475                 if (ontty == PITTY)
1476                         m_pclose();
1477                 SIGNAL(SIGPIPE, pstat);
1478                 invo_name = cp;
1479                 if (holder.c_text) {
1480                         free(holder.c_text);
1481                         holder.c_text = NULL;
1482                 }
1483                 free_queue(&msghd, &msgtl);
1484                 for (c1 = fmthd; c1; c1 = c1->c_next)
1485                         c1->c_flags &= ~HDROUTPUT;
1486                 return exitstat;
1487         }
1488 }
1489
1490 #undef adios
1491 #undef done
1492
1493 static void
1494 mhladios(char *what, char *fmt, ...)
1495 {
1496         va_list ap;
1497
1498         va_start(ap, fmt);
1499         advertise(what, NULL, fmt, ap);
1500         va_end(ap);
1501         mhldone(1);
1502 }
1503
1504
1505 static void
1506 mhldone(int status)
1507 {
1508         exitstat = status;
1509         if (mhl_action)
1510                 longjmp(mhlenv, DONE);
1511         else
1512                 done(exitstat);
1513 }
1514
1515
1516 static int m_pid = NOTOK;
1517 static int sd = NOTOK;
1518
1519 static void
1520 m_popen(char *name)
1521 {
1522         int pd[2];
1523
1524         if (mhl_action && (sd = dup(fileno(stdout))) == NOTOK)
1525                 adios("standard output", "unable to dup()");
1526
1527         if (pipe(pd) == NOTOK)
1528                 adios("pipe", "unable to");
1529
1530         switch (m_pid = vfork()) {
1531         case NOTOK:
1532                 adios("fork", "unable to");
1533
1534         case OK:
1535                 SIGNAL(SIGINT, SIG_DFL);
1536                 SIGNAL(SIGQUIT, SIG_DFL);
1537
1538                 close(pd[1]);
1539                 if (pd[0] != fileno(stdin)) {
1540                         dup2(pd[0], fileno(stdin));
1541                         close(pd[0]);
1542                 }
1543                 execlp(name, mhbasename(name), NULL);
1544                 fprintf(stderr, "unable to exec ");
1545                 perror(name);
1546                 _exit(-1);
1547
1548         default:
1549                 close(pd[0]);
1550                 if (pd[1] != fileno(stdout)) {
1551                         dup2(pd[1], fileno(stdout));
1552                         close(pd[1]);
1553                 }
1554         }
1555 }
1556
1557
1558 void
1559 m_pclose(void)
1560 {
1561         if (m_pid == NOTOK)
1562                 return;
1563
1564         if (sd != NOTOK) {
1565                 fflush(stdout);
1566                 if (dup2(sd, fileno(stdout)) == NOTOK)
1567                         adios("standard output", "unable to dup2()");
1568
1569                 clearerr(stdout);
1570                 close(sd);
1571                 sd = NOTOK;
1572         }
1573         else
1574                 fclose(stdout);
1575
1576         pidwait(m_pid, OK);
1577         m_pid = NOTOK;
1578 }