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