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