Initial revision
[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", 4 },
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             m_popen (moreproc);
438             ontty = PITTY;
439         } else {
440             SIGNAL (SIGINT, SIG_IGN);
441             SIGNAL2 (SIGQUIT, quitser);
442             ontty = ISTTY;
443         }
444     } else {
445         ontty = NOTTY;
446     }
447
448     mhl_format (form ? form : mhlformat, length, width);
449
450     if (vecp == 0) {
451         process (folder, NULL, 1, vecp = 1);
452     } else {
453         for (i = 0; i < vecp; i++)
454             process (folder, files[i], i + 1, vecp);
455     }
456
457     if (forwall) {
458         if (digest) {
459             printf ("%s", delim4);
460             if (volume == 0) {
461                 snprintf (buf, sizeof(buf), "End of %s Digest\n", digest);
462             } else {
463                 snprintf (buf, sizeof(buf), "End of %s Digest [Volume %d Issue %d]\n",
464                         digest, volume, issue);
465             }
466             i = strlen (buf);
467             for (cp = buf + i; i > 1; i--)
468                 *cp++ = '*';
469             *cp++ = '\n';
470             *cp = 0;
471             printf ("%s", buf);
472         }
473         else
474             printf ("\n------- End of Forwarded Message%s\n\n",
475                     vecp > 1 ? "s" : "");
476     }
477
478     if (clearflg > 0 && ontty == NOTTY)
479         clear_screen ();
480
481     if (ontty == PITTY)
482         m_pclose ();
483
484     return exitstat;
485 }
486
487
488 static void
489 mhl_format (char *file, int length, int width)
490 {
491     int i;
492     char *bp, *cp, **ip;
493     char *ap, buffer[BUFSIZ], name[NAMESZ];
494     struct mcomp *c1;
495     struct stat st;
496     FILE *fp;
497     static dev_t dev = 0;
498     static ino_t ino = 0;
499     static time_t mtime = 0;
500
501     if (fmthd != NULL)
502         if (stat (etcpath (file), &st) != NOTOK
503                 && mtime == st.st_mtime
504                 && dev == st.st_dev
505                 && ino == st.st_ino)
506             goto out;
507         else
508             free_queue (&fmthd, &fmttl);
509
510     if ((fp = fopen (etcpath (file), "r")) == NULL)
511         adios (file, "unable to open format file");
512
513     if (fstat (fileno (fp), &st) != NOTOK) {
514         mtime = st.st_mtime;
515         dev = st.st_dev;
516         ino = st.st_ino;
517     }
518
519     global.c_ovtxt = global.c_nfs = NULL;
520     global.c_fmt = NULL;
521     global.c_offset = 0;
522     global.c_ovoff = -1;
523     if ((i = sc_width ()) > 5)
524         global.c_width = i;
525     global.c_cwidth = -1;
526     if ((i = sc_length ()) > 5)
527         global.c_length = i - 1;
528     global.c_flags = BELL;              /* BELL is default */
529     *(ip = ignores) = NULL;
530
531     while (vfgets (fp, &ap) == OK) {
532         bp = ap;
533         if (*bp == ';')
534             continue;
535
536         if ((cp = strchr(bp, '\n')))
537             *cp = 0;
538
539         if (*bp == ':') {
540             c1 = add_queue (&fmthd, &fmttl, NULL, bp + 1, CLEARTEXT);
541             continue;
542         }
543
544         parptr = bp;
545         strncpy (name, parse(), sizeof(name));
546         switch (*parptr) {
547             case '\0': 
548             case ',': 
549             case '=': 
550                 /*
551                  * Split this list of fields to ignore, and copy
552                  * it to the end of the current "ignores" list.
553                  */
554                 if (!strcasecmp (name, "ignores")) {
555                     char **tmparray, **p;
556                     int n = 0;
557
558                     /* split the fields */
559                     tmparray = brkstring (getcpy (++parptr), ",", NULL);
560
561                     /* count number of fields split */
562                     p = tmparray;
563                     while (*p++)
564                         n++;
565
566                     /* copy pointers to split fields to ignores array */
567                     ip = copyip (tmparray, ip, MAXARGS - num_ignores);
568                     num_ignores += n;
569                     continue;
570                 }
571                 parptr = bp;
572                 while (*parptr) {
573                     if (evalvar (&global))
574                         adios (NULL, "format file syntax error: %s", bp);
575                     if (*parptr)
576                         parptr++;
577                 }
578                 continue;
579
580             case ':': 
581                 c1 = add_queue (&fmthd, &fmttl, name, NULL, INIT);
582                 while (*parptr == ':' || *parptr == ',') {
583                     parptr++;
584                     if (evalvar (c1))
585                         adios (NULL, "format file syntax error: %s", bp);
586                 }
587                 if (!c1->c_nfs && global.c_nfs)
588                     if (c1->c_flags & DATEFMT) {
589                         if (global.c_flags & DATEFMT)
590                             c1->c_nfs = getcpy (global.c_nfs);
591                     }
592                     else
593                         if (c1->c_flags & ADDRFMT) {
594                             if (global.c_flags & ADDRFMT)
595                                 c1->c_nfs = getcpy (global.c_nfs);
596                         }
597                 continue;
598
599             default: 
600                 adios (NULL, "format file syntax error: %s", bp);
601         }
602     }
603     fclose (fp);
604
605     if (mhldebug) {
606         for (c1 = fmthd; c1; c1 = c1->c_next) {
607             fprintf (stderr, "c1: name=\"%s\" text=\"%s\" ovtxt=\"%s\"\n",
608                     c1->c_name, c1->c_text, c1->c_ovtxt);
609             fprintf (stderr, "\tnfs=0x%x fmt=0x%x\n",
610                     (unsigned int) c1->c_nfs, (unsigned int) c1->c_fmt);
611             fprintf (stderr, "\toffset=%d ovoff=%d width=%d cwidth=%d length=%d\n",
612                     c1->c_offset, c1->c_ovoff, c1->c_width,
613                     c1->c_cwidth, c1->c_length);
614             fprintf (stderr, "\tflags=%s\n",
615                     snprintb (buffer, sizeof(buffer), (unsigned) c1->c_flags, LBITS));
616         }
617     }
618
619 out:
620     if (clearflg == 1) {
621         global.c_flags |= CLEARSCR;
622     } else {
623         if (clearflg == -1)
624             global.c_flags &= ~CLEARSCR;
625     }
626
627     switch (bellflg) {          /* command line may override format file */
628         case 1: 
629             global.c_flags |= BELL;
630             break;
631         case -1: 
632             global.c_flags &= ~BELL;
633             break;
634     }
635
636     if (length)
637         global.c_length = length;
638     if (width)
639         global.c_width = width;
640     if (global.c_length < 5)
641         global.c_length = 10000;
642     if (global.c_width < 5)
643         global.c_width = 10000;
644 }
645
646
647 static int
648 evalvar (struct mcomp *c1)
649 {
650     char *cp, name[NAMESZ];
651     struct triple *ap;
652
653     if (!*parptr)
654         return 0;
655     strncpy (name, parse(), sizeof(name));
656
657     if (!strcasecmp (name, "component")) {
658         if (ptos (name, &c1->c_text))
659             return 1;
660         c1->c_flags &= ~NOCOMPONENT;
661         return 0;
662     }
663
664     if (!strcasecmp (name, "overflowtext"))
665         return ptos (name, &c1->c_ovtxt);
666
667     if (!strcasecmp (name, "formatfield")) {
668         char *nfs;
669
670         if (ptos (name, &cp))
671             return 1;
672         nfs = new_fs (NULL, NULL, cp);
673         c1->c_nfs = getcpy (nfs);
674         c1->c_flags |= FORMAT;
675         return 0;
676     }
677
678     if (!strcasecmp (name, "decode")) {
679         char *nfs;
680
681         nfs = new_fs (NULL, NULL, "%(decode{text})");
682         c1->c_nfs = getcpy (nfs);
683         c1->c_flags |= FORMAT;
684         return 0;
685     }
686
687     if (!strcasecmp (name, "offset"))
688         return ptoi (name, &c1->c_offset);
689     if (!strcasecmp (name, "overflowoffset"))
690         return ptoi (name, &c1->c_ovoff);
691     if (!strcasecmp (name, "width"))
692         return ptoi (name, &c1->c_width);
693     if (!strcasecmp (name, "compwidth"))
694         return ptoi (name, &c1->c_cwidth);
695     if (!strcasecmp (name, "length"))
696         return ptoi (name, &c1->c_length);
697     if (!strcasecmp (name, "nodashstuffing"))
698         return (dashstuff = -1);
699
700     for (ap = triples; ap->t_name; ap++)
701         if (!strcasecmp (ap->t_name, name)) {
702             c1->c_flags |= ap->t_on;
703             c1->c_flags &= ~ap->t_off;
704             return 0;
705         }
706
707     return 1;
708 }
709
710
711 static int
712 ptoi (char *name, int *i)
713 {
714     char *cp;
715
716     if (*parptr++ != '=' || !*(cp = parse ())) {
717         advise (NULL, "missing argument to variable %s", name);
718         return 1;
719     }
720
721     *i = atoi (cp);
722     return 0;
723 }
724
725
726 static int
727 ptos (char *name, char **s)
728 {
729     char c, *cp;
730
731     if (*parptr++ != '=') {
732         advise (NULL, "missing argument to variable %s", name);
733         return 1;
734     }
735
736     if (*parptr != '"') {
737         for (cp = parptr;
738                 *parptr && *parptr != ':' && *parptr != ',';
739                 parptr++)
740             continue;
741     } else {
742         for (cp = ++parptr; *parptr && *parptr != '"'; parptr++)
743             if (*parptr == QUOTE)
744                 if (!*++parptr)
745                     parptr--;
746     }
747     c = *parptr;
748     *parptr = 0;
749     *s = getcpy (cp);
750     if ((*parptr = c) == '"')
751         parptr++;
752     return 0;
753 }
754
755
756 static char *
757 parse (void)
758 {
759     int c;
760     char *cp;
761     static char result[NAMESZ];
762
763     for (cp = result; *parptr && (cp - result < NAMESZ); parptr++) {
764         c = *parptr;
765         if (isalnum (c)
766                 || c == '.'
767                 || c == '-'
768                 || c == '_'
769                 || c =='['
770                 || c == ']')
771             *cp++ = c;
772         else
773             break;
774     }
775     *cp = '\0';
776
777     return result;
778 }
779
780
781 static void
782 process (char *folder, char *fname, int ofilen, int ofilec)
783 {
784     char *cp;
785     FILE *fp;
786     struct mcomp *c1;
787
788     switch (setjmp (env)) {
789         case OK: 
790             if (fname) {
791                 fp = mhl_action ? (*mhl_action) (fname) : fopen (fname, "r");
792                 if (fp == NULL) {
793                     advise (fname, "unable to open");
794                     exitstat++;
795                     return;
796                 }
797             } else {
798                 fname = "(stdin)";
799                 fp = stdin;
800             }
801             cp = folder ? concat (folder, ":", fname, NULL) : getcpy (fname);
802             if (ontty != PITTY)
803                 SIGNAL (SIGINT, intrser);
804             mhlfile (fp, cp, ofilen, ofilec);  /* FALL THROUGH! */
805
806         default: 
807             if (ontty != PITTY)
808                 SIGNAL (SIGINT, SIG_IGN);
809             if (mhl_action == NULL && fp != stdin)
810                 fclose (fp);
811             free (cp);
812             if (holder.c_text) {
813                 free (holder.c_text);
814                 holder.c_text = NULL;
815             }
816             free_queue (&msghd, &msgtl);
817             for (c1 = fmthd; c1; c1 = c1->c_next)
818                 c1->c_flags &= ~HDROUTPUT;
819             break;
820     }
821 }
822
823
824 static void
825 mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
826 {
827     int state;
828     struct mcomp *c1, *c2, *c3;
829     char **ip, name[NAMESZ], buf[BUFSIZ];
830
831     if (forwall) {
832         if (digest)
833             printf ("%s", ofilen == 1 ? delim3 : delim4);
834         else {
835             printf ("\n-------");
836             if (ofilen == 1)
837                 printf (" Forwarded Message%s", ofilec > 1 ? "s" : "");
838             else
839                 printf (" Message %d", ofilen);
840             printf ("\n\n");
841         }
842     } else {
843         switch (ontty) {
844             case PITTY: 
845                 if (ofilec > 1) {
846                     if (ofilen > 1) {
847                         if ((global.c_flags & CLEARSCR))
848                             clear_screen ();
849                         else
850                             printf ("\n\n\n");
851                     }
852                     printf (">>> %s\n\n", mname);
853                 }
854                 break;
855
856             case ISTTY: 
857                 strncpy (buf, "\n", sizeof(buf));
858                 if (ofilec > 1) {
859                     if (SOprintf ("Press <return> to list \"%s\"...", mname)) {
860                         if (ofilen > 1)
861                             printf ("\n\n\n");
862                         printf ("Press <return> to list \"%s\"...", mname);
863                     }
864                     fflush (stdout);
865                     buf[0] = 0;
866                     read (fileno (stdout), buf, sizeof(buf));
867                 }
868                 if (strchr(buf, '\n')) {
869                     if ((global.c_flags & CLEARSCR))
870                         clear_screen ();
871                 }
872                 else
873                     printf ("\n");
874                 break;
875
876             default: 
877                 if (ofilec > 1) {
878                     if (ofilen > 1) {
879                         printf ("\n\n\n");
880                         if (clearflg > 0)
881                             clear_screen ();
882                     }
883                     printf (">>> %s\n\n", mname);
884                 }
885                 break;
886         }
887     }
888
889     for (state = FLD;;) {
890         switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
891             case FLD: 
892             case FLDPLUS: 
893                 for (ip = ignores; *ip; ip++)
894                     if (!strcasecmp (name, *ip)) {
895                         while (state == FLDPLUS)
896                             state = m_getfld (state, name, buf, sizeof(buf), fp);
897                         break;
898                     }
899                 if (*ip)
900                     continue;
901
902                 for (c2 = fmthd; c2; c2 = c2->c_next)
903                     if (!strcasecmp (c2->c_name, name))
904                         break;
905                 c1 = NULL;
906                 if (!((c3 = c2 ? c2 : &global)->c_flags & SPLIT))
907                     for (c1 = msghd; c1; c1 = c1->c_next)
908                         if (!strcasecmp (c1->c_name, c3->c_name)) {
909                             c1->c_text =
910                                 mcomp_add (c1->c_flags, buf, c1->c_text);
911                             break;
912                         }
913                 if (c1 == NULL)
914                     c1 = add_queue (&msghd, &msgtl, name, buf, 0);
915                 while (state == FLDPLUS) {
916                     state = m_getfld (state, name, buf, sizeof(buf), fp);
917                     c1->c_text = add (buf, c1->c_text);
918                 }
919                 if (c2 == NULL)
920                     c1->c_flags |= EXTRA;
921                 continue;
922
923             case BODY: 
924             case FILEEOF: 
925                 row = column = 0;
926                 for (c1 = fmthd; c1; c1 = c1->c_next) {
927                     if (c1->c_flags & CLEARTEXT) {
928                         putcomp (c1, c1, ONECOMP);
929                         continue;
930                     }
931                     if (!strcasecmp (c1->c_name, "messagename")) {
932                         holder.c_text = concat ("(Message ", mname, ")\n",
933                                             NULL);
934                         putcomp (c1, &holder, ONECOMP);
935                         free (holder.c_text);
936                         holder.c_text = NULL;
937                         continue;
938                     }
939                     if (!strcasecmp (c1->c_name, "extras")) {
940                         for (c2 = msghd; c2; c2 = c2->c_next)
941                             if (c2->c_flags & EXTRA)
942                                 putcomp (c1, c2, TWOCOMP);
943                         continue;
944                     }
945                     if (dobody && !strcasecmp (c1->c_name, "body")) {
946                         if ((holder.c_text = malloc (sizeof(buf))) == NULL)
947                             adios (NULL, "unable to allocate buffer memory");
948                         strncpy (holder.c_text, buf, sizeof(buf));
949                         while (state == BODY) {
950                             putcomp (c1, &holder, BODYCOMP);
951                             state = m_getfld (state, name, holder.c_text,
952                                         sizeof(buf), fp);
953                         }
954                         free (holder.c_text);
955                         holder.c_text = NULL;
956                         continue;
957                     }
958                     for (c2 = msghd; c2; c2 = c2->c_next)
959                         if (!strcasecmp (c2->c_name, c1->c_name)) {
960                             putcomp (c1, c2, ONECOMP);
961                             if (!(c1->c_flags & SPLIT))
962                                 break;
963                         }
964                     if (faceproc && c2 == NULL && (c1->c_flags & FACEFMT))
965                         for (c2 = msghd; c2; c2 = c2->c_next)
966                             if (c2->c_flags & FACEDFLT) {
967                                 if (c2->c_face == NULL)
968                                     face_format (c2);
969                                 if ((holder.c_text = c2->c_face)) {
970                                     putcomp (c1, &holder, ONECOMP);
971                                     holder.c_text = NULL;
972                                 }
973                                 break;
974                             }
975                 }
976                 return;
977
978             case LENERR: 
979             case FMTERR: 
980                 advise (NULL, "format error in message %s", mname);
981                 exitstat++;
982                 return;
983
984             default: 
985                 adios (NULL, "getfld() returned %d", state);
986         }
987     }
988 }
989
990
991 static int
992 mcomp_flags (char *name)
993 {
994     struct pair *ap;
995
996     for (ap = pairs; ap->p_name; ap++)
997         if (!strcasecmp (ap->p_name, name))
998             return (ap->p_flags);
999
1000     return 0;
1001 }
1002
1003
1004 static char *
1005 mcomp_add (long flags, char *s1, char *s2)
1006 {
1007     char *dp;
1008
1009     if (!(flags & ADDRFMT))
1010         return add (s1, s2);
1011
1012     if (s2 && *(dp = s2 + strlen (s2) - 1) == '\n')
1013         *dp = 0;
1014
1015     return add (s1, add (",\n", s2));
1016 }
1017
1018
1019 struct pqpair {
1020     char *pq_text;
1021     char *pq_error;
1022     struct pqpair *pq_next;
1023 };
1024
1025
1026 static void
1027 mcomp_format (struct mcomp *c1, struct mcomp *c2)
1028 {
1029     int dat[5];
1030     char *ap, *cp;
1031     char buffer[BUFSIZ], error[BUFSIZ];
1032     struct comp *cptr;
1033     struct pqpair *p, *q;
1034     struct pqpair pq;
1035     struct mailname *mp;
1036
1037     ap = c2->c_text;
1038     c2->c_text = NULL;
1039     dat[0] = 0;
1040     dat[1] = 0;
1041     dat[2] = 0;
1042     dat[3] = sizeof(buffer) - 1;
1043     dat[4] = 0;
1044     fmt_compile (c1->c_nfs, &c1->c_fmt);
1045
1046     if (!(c1->c_flags & ADDRFMT)) {
1047         FINDCOMP (cptr, "text");
1048         if (cptr)
1049             cptr->c_text = ap;
1050         if ((cp = strrchr(ap, '\n')))   /* drop ending newline */
1051             if (!cp[1])
1052                 *cp = 0;
1053
1054         fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
1055         /* Don't need to append a newline, dctime() already did */
1056         c2->c_text = getcpy (buffer);
1057
1058         free (ap);
1059         return;
1060     }
1061
1062     (q = &pq)->pq_next = NULL;
1063     while ((cp = getname (ap))) {
1064         if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
1065             adios (NULL, "unable to allocate pqpair memory");
1066
1067         if ((mp = getm (cp, NULL, 0, AD_NAME, error)) == NULL) {
1068             p->pq_text = getcpy (cp);
1069             p->pq_error = getcpy (error);
1070         } else {
1071             if ((c1->c_flags & FACEDFLT) && c2->c_face == NULL) {
1072                 char   *h, *o;
1073                 if ((h = mp->m_host) == NULL)
1074                     h = LocalName ();
1075                 if ((o = OfficialName (h)))
1076                     h = o;
1077                 c2->c_face = concat ("address ", h, " ", mp->m_mbox,
1078                                     NULL);
1079             }
1080             p->pq_text = getcpy (mp->m_text);
1081             mnfree (mp);
1082         }
1083         q = (q->pq_next = p);
1084     }
1085
1086     for (p = pq.pq_next; p; p = q) {
1087         FINDCOMP (cptr, "text");
1088         if (cptr)
1089             cptr->c_text = p->pq_text;
1090         FINDCOMP (cptr, "error");
1091         if (cptr)
1092             cptr->c_text = p->pq_error;
1093
1094         fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
1095         if (*buffer) {
1096             if (c2->c_text)
1097                 c2->c_text = add (",\n", c2->c_text);
1098             if (*(cp = buffer + strlen (buffer) - 1) == '\n')
1099                 *cp = 0;
1100             c2->c_text = add (buffer, c2->c_text);
1101         }
1102
1103         free (p->pq_text);
1104         if (p->pq_error)
1105             free (p->pq_error);
1106         q = p->pq_next;
1107         free ((char *) p);
1108     }
1109
1110     c2->c_text = add ("\n", c2->c_text);
1111     free (ap);
1112 }
1113
1114
1115 static struct mcomp *
1116 add_queue (struct mcomp **head, struct mcomp **tail, char *name, char *text, int flags)
1117 {
1118     struct mcomp *c1;
1119
1120     if ((c1 = (struct mcomp *) calloc ((size_t) 1, sizeof(*c1))) == NULL)
1121         adios (NULL, "unable to allocate comp memory");
1122
1123     c1->c_flags = flags & ~INIT;
1124     if ((c1->c_name = name ? getcpy (name) : NULL))
1125         c1->c_flags |= mcomp_flags (c1->c_name);
1126     c1->c_text = text ? getcpy (text) : NULL;
1127     if (flags & INIT) {
1128         if (global.c_ovtxt)
1129             c1->c_ovtxt = getcpy (global.c_ovtxt);
1130         c1->c_offset = global.c_offset;
1131         c1->c_ovoff = global. c_ovoff;
1132         c1->c_width = c1->c_length = 0;
1133         c1->c_cwidth = global.c_cwidth;
1134         c1->c_flags |= global.c_flags & GFLAGS;
1135     }
1136     if (*head == NULL)
1137         *head = c1;
1138     if (*tail != NULL)
1139         (*tail)->c_next = c1;
1140     *tail = c1;
1141
1142     return c1;
1143 }
1144
1145
1146 static void
1147 free_queue (struct mcomp **head, struct mcomp **tail)
1148 {
1149     struct mcomp *c1, *c2;
1150
1151     for (c1 = *head; c1; c1 = c2) {
1152         c2 = c1->c_next;
1153         if (c1->c_name)
1154             free (c1->c_name);
1155         if (c1->c_text)
1156             free (c1->c_text);
1157         if (c1->c_ovtxt)
1158             free (c1->c_ovtxt);
1159         if (c1->c_nfs)
1160             free (c1->c_nfs);
1161         if (c1->c_fmt)
1162             free ((char *) c1->c_fmt);
1163         if (c1->c_face)
1164             free (c1->c_face);
1165         free ((char *) c1);
1166     }
1167
1168     *head = *tail = NULL;
1169 }
1170
1171
1172 static void
1173 putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
1174 {
1175     int count, cchdr;
1176     char *cp;
1177
1178     cchdr = 0;
1179     lm = 0;
1180     llim = c1->c_length ? c1->c_length : -1;
1181     wid = c1->c_width ? c1->c_width : global.c_width;
1182     ovoff = (c1->c_ovoff >= 0 ? c1->c_ovoff : global.c_ovoff)
1183         + c1->c_offset;
1184     if ((ovtxt = c1->c_ovtxt ? c1->c_ovtxt : global.c_ovtxt) == NULL)
1185         ovtxt = "";
1186     if (wid < ovoff + strlen (ovtxt) + 5)
1187         adios (NULL, "component: %s width(%d) too small for overflow(%d)",
1188                 c1->c_name, wid, ovoff + strlen (ovtxt) + 5);
1189     onelp = NULL;
1190
1191     if (c1->c_flags & CLEARTEXT) {
1192         putstr (c1->c_text);
1193         putstr ("\n");
1194         return;
1195     }
1196
1197     if (c1->c_flags & FACEFMT)
1198         switch (doface (c2)) {
1199             case NOTOK:         /* error */
1200             case OK:            /* async faceproc */
1201                 return;
1202
1203             default:            /* sync faceproc */
1204                 break;
1205         }
1206
1207     if (c1->c_nfs && (c1->c_flags & (ADDRFMT | DATEFMT | FORMAT)))
1208         mcomp_format (c1, c2);
1209
1210     if (c1->c_flags & CENTER) {
1211         count = (c1->c_width ? c1->c_width : global.c_width)
1212             - c1->c_offset - strlen (c2->c_text);
1213         if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT))
1214             count -= strlen (c1->c_text ? c1->c_text : c1->c_name) + 2;
1215         lm = c1->c_offset + (count / 2);
1216     } else {
1217         if (c1->c_offset)
1218             lm = c1->c_offset;
1219     }
1220
1221     if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT)) {
1222         if (c1->c_flags & UPPERCASE)            /* uppercase component also */
1223             for (cp = (c1->c_text ? c1->c_text : c1->c_name); *cp; cp++)
1224                 if (islower (*cp))
1225                     *cp = toupper (*cp);
1226         putstr (c1->c_text ? c1->c_text : c1->c_name);
1227         if (flag != BODYCOMP) {
1228             putstr (": ");
1229             if (!(c1->c_flags & SPLIT))
1230                 c1->c_flags |= HDROUTPUT;
1231
1232         cchdr++;
1233         if ((count = c1->c_cwidth -
1234                 strlen (c1->c_text ? c1->c_text : c1->c_name) - 2) > 0)
1235             while (count--)
1236                 putstr (" ");
1237         }
1238         else
1239             c1->c_flags |= HDROUTPUT;           /* for BODYCOMP */
1240     }
1241
1242     if (flag == TWOCOMP
1243             && !(c2->c_flags & HDROUTPUT)
1244             && !(c2->c_flags & NOCOMPONENT)) {
1245         if (c1->c_flags & UPPERCASE)
1246             for (cp = c2->c_name; *cp; cp++)
1247                 if (islower (*cp))
1248                     *cp = toupper (*cp);
1249         putstr (c2->c_name);
1250         putstr (": ");
1251         if (!(c1->c_flags & SPLIT))
1252             c2->c_flags |= HDROUTPUT;
1253
1254         cchdr++;
1255         if ((count = c1->c_cwidth - strlen (c2->c_name) - 2) > 0)
1256             while (count--)
1257                 putstr (" ");
1258     }
1259     if (c1->c_flags & UPPERCASE)
1260         for (cp = c2->c_text; *cp; cp++)
1261             if (islower (*cp))
1262                 *cp = toupper (*cp);
1263
1264     count = 0;
1265     if (cchdr)
1266         if (flag == TWOCOMP)
1267             count = (c1->c_cwidth >= 0) ? c1->c_cwidth
1268                         : strlen (c2->c_name) + 2;
1269         else
1270             count = (c1->c_cwidth >= 0) ? c1->c_cwidth
1271                         : strlen (c1->c_text ? c1->c_text : c1->c_name) + 2;
1272     count += c1->c_offset;
1273
1274     if ((cp = oneline (c2->c_text, c1->c_flags)))
1275        putstr(cp);
1276     if (term == '\n')
1277         putstr ("\n");
1278     while ((cp = oneline (c2->c_text, c1->c_flags))) {
1279         lm = count;
1280         if (flag == BODYCOMP
1281                 && !(c1->c_flags & NOCOMPONENT))
1282             putstr (c1->c_text ? c1->c_text : c1->c_name);
1283         if (*cp)
1284             putstr (cp);
1285         if (term == '\n')
1286             putstr ("\n");
1287     }
1288 }
1289
1290
1291 static char *
1292 oneline (char *stuff, long flags)
1293 {
1294     int spc;
1295     char *cp, *ret;
1296
1297     if (onelp == NULL)
1298         onelp = stuff;
1299     if (*onelp == 0)
1300         return (onelp = NULL);
1301
1302     ret = onelp;
1303     term = 0;
1304     if (flags & COMPRESS) {
1305         for (spc = 1, cp = ret; *onelp; onelp++)
1306             if (isspace (*onelp)) {
1307                 if (*onelp == '\n' && (!onelp[1] || (flags & ADDRFMT))) {
1308                     term = '\n';
1309                     *onelp++ = 0;
1310                     break;
1311                 }
1312                 else
1313                     if (!spc) {
1314                         *cp++ = ' ';
1315                         spc++;
1316                     }
1317             }
1318             else {
1319                 *cp++ = *onelp;
1320                 spc = 0;
1321             }
1322
1323         *cp = 0;
1324     }
1325     else {
1326         while (*onelp && *onelp != '\n')
1327             onelp++;
1328         if (*onelp == '\n') {
1329             term = '\n';
1330             *onelp++ = 0;
1331         }
1332         if (flags & LEFTADJUST)
1333             while (*ret == ' ' || *ret == '\t')
1334                 ret++;
1335     }
1336     if (*onelp == 0 && term == '\n' && (flags & NONEWLINE))
1337         term = 0;
1338
1339     return ret;
1340 }
1341
1342
1343 static void
1344 putstr (char *string)
1345 {
1346     if (!column && lm > 0)
1347         while (lm > 0)
1348             if (lm >= 8) {
1349                 putch ('\t');
1350                 lm -= 8;
1351             }
1352             else {
1353                 putch (' ');
1354                 lm--;
1355             }
1356     lm = 0;
1357     while (*string)
1358         putch (*string++);
1359 }
1360
1361
1362 static void
1363 putch (char ch)
1364 {
1365     char buf[BUFSIZ];
1366
1367     if (llim == 0)
1368         return;
1369
1370     switch (ch) {
1371         case '\n': 
1372             if (llim > 0)
1373                 llim--;
1374             column = 0;
1375             row++;
1376             if (ontty != ISTTY || row != global.c_length)
1377                 break;
1378             if (global.c_flags & BELL)
1379                 putchar ('\007');
1380             fflush (stdout);
1381             buf[0] = 0;
1382             read (fileno (stdout), buf, sizeof(buf));
1383             if (strchr(buf, '\n')) {
1384                 if (global.c_flags & CLEARSCR)
1385                     clear_screen ();
1386                 row = 0;
1387             } else {
1388                 putchar ('\n');
1389                 row = global.c_length / 3;
1390             }
1391             return;
1392
1393         case '\t': 
1394             column |= 07;
1395             column++;
1396             break;
1397
1398         case '\b': 
1399             column--;
1400             break;
1401
1402         case '\r': 
1403             column = 0;
1404             break;
1405
1406         default: 
1407             /*
1408              * If we are forwarding this message, and the first
1409              * column contains a dash, then add a dash and a space.
1410              */
1411             if (column == 0 && forwflg && (dashstuff >= 0) && ch == '-') {
1412                 putchar ('-');
1413                 putchar (' ');
1414             }
1415             if (ch >= ' ')
1416                 column++;
1417             break;
1418     }
1419
1420     if (column >= wid) {
1421         putch ('\n');
1422         if (ovoff > 0)
1423             lm = ovoff;
1424         putstr (ovtxt ? ovtxt : "");
1425         putch (ch);
1426         return;
1427     }
1428
1429     putchar (ch);
1430 }
1431
1432
1433 static RETSIGTYPE
1434 intrser (int i)
1435 {
1436 #ifndef RELIABLE_SIGNALS
1437     SIGNAL (SIGINT, intrser);
1438 #endif
1439
1440     discard (stdout);
1441     putchar ('\n');
1442     longjmp (env, DONE);
1443 }
1444
1445
1446 static RETSIGTYPE
1447 pipeser (int i)
1448 {
1449 #ifndef RELIABLE_SIGNALS
1450     SIGNAL (SIGPIPE, pipeser);
1451 #endif
1452
1453     done (NOTOK);
1454 }
1455
1456
1457 static RETSIGTYPE
1458 quitser (int i)
1459 {
1460 #ifndef RELIABLE_SIGNALS
1461     SIGNAL (SIGQUIT, quitser);
1462 #endif
1463
1464     putchar ('\n');
1465     fflush (stdout);
1466     done (NOTOK);
1467 }
1468
1469
1470 static void
1471 face_format (struct mcomp *c1)
1472 {
1473     char *cp;
1474     struct mailname *mp;
1475
1476     if ((cp = c1->c_text) == NULL)
1477         return;
1478
1479     if ((cp = getname (cp))) {
1480         if ((mp = getm (cp, NULL, 0, AD_NAME, NULL))) {
1481             char *h, *o;
1482             if ((h = mp->m_host) == NULL)
1483                 h = LocalName ();
1484             if ((o = OfficialName (h)))
1485                 h = o;
1486             c1->c_face = concat ("address ", h, " ", mp->m_mbox, NULL);
1487         }
1488
1489         while ((cp = getname (cp)))
1490             continue;
1491     }
1492 }
1493
1494
1495 /*
1496  * faceproc is two elements defining the image agent's location:
1497  *     Internet host
1498  *     UDP port
1499  */
1500
1501 #include <sys/socket.h>
1502 #include <netinet/in.h>
1503 #include <netdb.h>
1504
1505 #ifdef HAVE_ARPA_INET_H
1506 # include <arpa/inet.h>
1507 #endif
1508
1509 static int
1510 doface (struct mcomp *c1)
1511 {
1512     int result, sd;
1513     struct sockaddr_in in_socket;
1514     struct sockaddr_in *isock = &in_socket;
1515     static int inited = OK;
1516     static int addrlen;
1517     static struct in_addr addr;
1518     static unsigned short portno;
1519
1520     if (inited == OK) {
1521         char *cp;
1522         char **ap = brkstring (cp = getcpy (faceproc), " ", "\n");
1523         struct hostent *hp;
1524
1525         if (ap[0] == NULL || ap[1] == NULL) {
1526 bad_faceproc: ;
1527             free (cp);
1528             return (inited = NOTOK);
1529         }
1530
1531         if (!(hp = gethostbystring (ap[0])))
1532             goto bad_faceproc;
1533         memcpy((char *) &addr, hp->h_addr, addrlen = hp->h_length);
1534
1535         portno = htons ((unsigned short) atoi (ap[1]));
1536         free (cp);
1537
1538         inited = DONE;
1539     }
1540     if (inited == NOTOK)
1541         return NOTOK;
1542
1543     isock->sin_family = AF_INET;
1544     isock->sin_port = portno;
1545     memcpy((char *) &isock->sin_addr, (char *) &addr, addrlen);
1546
1547     if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == NOTOK)
1548         return NOTOK;
1549
1550     result = sendto (sd, c1->c_text, strlen (c1->c_text), 0,
1551                 (struct sockaddr *) isock, sizeof(*isock));
1552
1553     close (sd);
1554
1555     return (result != NOTOK ? OK : NOTOK);
1556 }
1557
1558 /*
1559  * COMMENTED OUT
1560  * This version doesn't use sockets
1561  */
1562 #if 0
1563
1564 static int
1565 doface (struct mcomp *c1)
1566 {
1567     int i, len, vecp;
1568     pid_t child_id;
1569     int result, pdi[2], pdo[2];
1570     char *bp, *cp;
1571     char buffer[BUFSIZ], *vec[10];
1572
1573     if (pipe (pdi) == NOTOK)
1574         return NOTOK;
1575     if (pipe (pdo) == NOTOK) {
1576         close (pdi[0]);
1577         close (pdi[1]);
1578         return NOTOK;
1579     }
1580
1581     for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
1582         sleep (5);
1583
1584     switch (child_id) {
1585         case NOTOK: 
1586             /* oops... fork error */
1587             return NOTOK;
1588
1589         case OK: 
1590             /* child process */
1591             SIGNAL (SIGINT, SIG_IGN);
1592             SIGNAL (SIGQUIT, SIG_IGN);
1593             if (pdi[0] != fileno (stdin)) {
1594                 dup2 (pdi[0], fileno (stdin));
1595                 close (pdi[0]);
1596             }
1597             close (pdi[1]);
1598             close (pdo[0]);
1599             if (pdo[1] != fileno (stdout)) {
1600                 dup2 (pdo[1], fileno (stdout));
1601                 close (pdo[1]);
1602             }
1603             vecp = 0;
1604             vec[vecp++] = r1bindex (faceproc, '/');
1605             vec[vecp++] = "-e";
1606             if (sleepsw != NOTOK) {
1607                 vec[vecp++] = "-s";
1608                 snprintf (buffer, sizeof(buffer), "%d", sleepsw);
1609                 vec[vecp++] = buffer;
1610             }
1611             vec[vecp] = NULL;
1612             execvp (faceproc, vec);
1613             fprintf (stderr, "unable to exec ");
1614             perror (faceproc);
1615             _exit (-1);         /* NOTREACHED */
1616
1617         default: 
1618             /* parent process */
1619             close (pdi[0]);
1620             i = strlen (c1->c_text);
1621             if (write (pdi[1], c1->c_text, i) != i)
1622                 adios ("pipe", "error writing to");
1623             free (c1->c_text), c1->c_text = NULL;
1624             close (pdi[1]);
1625
1626             close (pdo[1]);
1627             cp = NULL, len = 0;
1628             result = DONE;
1629             while ((i = read (pdo[0], buffer, strlen (buffer))) > 0) {
1630                 if (cp) {
1631                     int j;
1632                     char *dp;
1633                     if ((dp = realloc (cp, (unsigned) (j = len + i))) == NULL)
1634                         adios (NULL, "unable to allocate face storage");
1635                     memcpy(dp + len, buffer, i);
1636                     cp = dp, len = j;
1637                 }
1638                 else {
1639                     if ((cp = malloc ((unsigned) i)) == NULL)
1640                         adios (NULL, "unable to allocate face storage");
1641                     memcpy(cp, buffer, i);
1642                     len = i;
1643                 }
1644                 if (result == DONE)
1645                     for (bp = buffer + i - 1; bp >= buffer; bp--)
1646                         if (!isascii (*bp) || iscntrl (*bp)) {
1647                             result = OK;
1648                             break;
1649                         }
1650             }
1651             close (pdo[0]);
1652
1653 /* no waiting for child... */
1654
1655             if (result == OK) { /* binary */
1656                 if (write (1, cp, len) != len)
1657                     adios ("writing", "error");
1658                 free (cp);
1659             }
1660             else                /* empty */
1661                 if ((c1->c_text = cp) == NULL)
1662                     result = OK;
1663             break;
1664     }
1665
1666     return result;
1667 }
1668 #endif /* COMMENTED OUT */
1669
1670
1671 int
1672 mhlsbr (int argc, char **argv, FILE *(*action)())
1673 {
1674     SIGNAL_HANDLER istat, pstat, qstat;
1675     char *cp;
1676     struct mcomp *c1;
1677
1678     switch (setjmp (mhlenv)) {
1679         case OK: 
1680             cp = invo_name;
1681             sleepsw = 0;        /* XXX */
1682             bellflg = clearflg = forwflg = forwall = exitstat = 0;
1683             digest = NULL;
1684             ontty = NOTTY;
1685             mhl_action = action;
1686
1687             /*
1688              * If signal is at default action, then start ignoring
1689              * it, else let it set to its current action.
1690              */
1691             if ((istat = SIGNAL (SIGINT, SIG_IGN)) != SIG_DFL)
1692                 SIGNAL (SIGINT, istat);
1693             if ((qstat = SIGNAL (SIGQUIT, SIG_IGN)) != SIG_DFL)
1694                 SIGNAL (SIGQUIT, qstat);
1695             pstat = SIGNAL (SIGPIPE, pipeser);
1696             mhl (argc, argv);                  /* FALL THROUGH! */
1697
1698         default: 
1699             SIGNAL (SIGINT, istat);
1700             SIGNAL (SIGQUIT, qstat);
1701             SIGNAL (SIGPIPE, SIG_IGN);/* should probably change to block instead */
1702             if (ontty == PITTY)
1703                 m_pclose ();
1704             SIGNAL (SIGPIPE, pstat);
1705             invo_name = cp;
1706             if (holder.c_text) {
1707                 free (holder.c_text);
1708                 holder.c_text = NULL;
1709             }
1710             free_queue (&msghd, &msgtl);
1711             for (c1 = fmthd; c1; c1 = c1->c_next)
1712                 c1->c_flags &= ~HDROUTPUT;
1713             return exitstat;
1714     }
1715 }
1716
1717 #undef adios
1718 #undef done
1719
1720 static void
1721 mhladios (char *what, char *fmt, ...)
1722 {
1723     va_list ap;
1724
1725     va_start(ap, fmt);
1726     advertise (what, NULL, fmt, ap);
1727     va_end(ap);
1728     mhldone (1);
1729 }
1730
1731
1732 static void
1733 mhldone (int status)
1734 {
1735     exitstat = status;
1736     if (mhl_action)
1737         longjmp (mhlenv, DONE);
1738     else
1739         done (exitstat);
1740 }
1741
1742
1743 static  int m_pid = NOTOK;
1744 static  int sd = NOTOK;
1745
1746 static void
1747 m_popen (char *name)
1748 {
1749     int pd[2];
1750
1751     if (mhl_action && (sd = dup (fileno (stdout))) == NOTOK)
1752         adios ("standard output", "unable to dup()");
1753
1754     if (pipe (pd) == NOTOK)
1755         adios ("pipe", "unable to");
1756
1757     switch (m_pid = vfork ()) {
1758         case NOTOK: 
1759             adios ("fork", "unable to");
1760
1761         case OK: 
1762             SIGNAL (SIGINT, SIG_DFL);
1763             SIGNAL (SIGQUIT, SIG_DFL);
1764
1765             close (pd[1]);
1766             if (pd[0] != fileno (stdin)) {
1767                 dup2 (pd[0], fileno (stdin));
1768                 close (pd[0]);
1769             }
1770             execlp (name, r1bindex (name, '/'), NULL);
1771             fprintf (stderr, "unable to exec ");
1772             perror (name);
1773             _exit (-1);
1774
1775         default: 
1776             close (pd[0]);
1777             if (pd[1] != fileno (stdout)) {
1778                 dup2 (pd[1], fileno (stdout));
1779                 close (pd[1]);
1780             }
1781     }
1782 }
1783
1784
1785 void
1786 m_pclose (void)
1787 {
1788     if (m_pid == NOTOK)
1789         return;
1790
1791     if (sd != NOTOK) {
1792         fflush (stdout);
1793         if (dup2 (sd, fileno (stdout)) == NOTOK)
1794             adios ("standard output", "unable to dup2()");
1795
1796         clearerr (stdout);
1797         close (sd);
1798         sd = NOTOK;
1799     }
1800     else
1801         fclose (stdout);
1802
1803     pidwait (m_pid, OK);
1804     m_pid = NOTOK;
1805 }