X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/017a82124bf2ea39ced5aa4c8f969c18b3c2fb90..e35fb433:/uip/mhlsbr.c?ds=inline diff --git a/uip/mhlsbr.c b/uip/mhlsbr.c index 882304cb..5ff3f4dc 100644 --- a/uip/mhlsbr.c +++ b/uip/mhlsbr.c @@ -1,17 +1,20 @@ - -/* - * mhlsbr.c -- main routines for nmh message lister +/* mhlsbr.c -- main routines for nmh message lister * - * $Id$ + * This code is Copyright (c) 2002, by the authors of nmh. See the + * COPYRIGHT file in the root directory of the nmh distribution for + * complete copyright information. */ #include #include #include #include -#include +#include +#include +#include "sbr/m_popen.h" #include -#include +#include +#include "sbr/terminal.h" /* * MAJOR BUG: @@ -19,7 +22,7 @@ * set, then addresses get split wrong (not at the spaces between commas). * To fix this correctly, putstr() should know about "atomic" strings that * must NOT be broken across lines. That's too difficult for right now - * (it turns out that there are a number of degernate cases), so in + * (it turns out that there are a number of degenerate cases), so in * oneline(), instead of * * (*onelp == '\n' && !onelp[1]) @@ -38,55 +41,38 @@ #define QUOTE '\\' -static struct swit mhlswitches[] = { -#define BELLSW 0 - { "bell", 0 }, -#define NBELLSW 1 - { "nobell", 0 }, -#define CLRSW 2 - { "clear", 0 }, -#define NCLRSW 3 - { "noclear", 0 }, -#define FACESW 4 - { "faceproc program", 0 }, -#define NFACESW 5 - { "nofaceproc", 0 }, -#define FOLDSW 6 - { "folder +folder", 0 }, -#define FORMSW 7 - { "form formfile", 0 }, -#define PROGSW 8 - { "moreproc program", 0 }, -#define NPROGSW 9 - { "nomoreproc", 0 }, -#define LENSW 10 - { "length lines", 0 }, -#define WIDTHSW 11 - { "width columns", 0 }, -#define SLEEPSW 12 - { "sleep seconds", 0 }, -#define BITSTUFFSW 13 - { "dashstuffing", -12 }, /* interface from forw */ -#define NBITSTUFFSW 14 - { "nodashstuffing", -14 }, /* interface from forw */ -#define VERSIONSW 15 - { "version", 0 }, -#define HELPSW 16 - { "help", 0 }, -#define FORW1SW 17 - { "forward", -7 }, /* interface from forw */ -#define FORW2SW 18 - { "forwall", -7 }, /* interface from forw */ -#define DGSTSW 19 - { "digest list", -6 }, -#define VOLUMSW 20 - { "volume number", -6 }, -#define ISSUESW 21 - { "issue number", -5 }, -#define NBODYSW 22 - { "nobody", -6 }, - { NULL, 0 } -}; +#define MHL_SWITCHES \ + X("bell", 0, BELLSW) \ + X("nobell", 0, NBELLSW) \ + X("clear", 0, CLRSW) \ + X("noclear", 0, NCLRSW) \ + X("folder +folder", 0, FOLDSW) \ + X("form formfile", 0, FORMSW) \ + X("moreproc program", 0, PROGSW) \ + X("nomoreproc", 0, NPROGSW) \ + X("length lines", 0, LENSW) \ + X("width columns", 0, WIDTHSW) \ + X("sleep seconds", 0, SLEEPSW) \ + X("dashstuffing", -12, BITSTUFFSW) /* interface from forw */ \ + X("nodashstuffing", -14, NBITSTUFFSW) /* interface from forw */ \ + X("version", 0, VERSIONSW) \ + X("help", 0, HELPSW) \ + X("forward", -7, FORW1SW) /* interface from forw */ \ + X("forwall", -7, FORW2SW) /* interface from forw */ \ + X("digest list", -6, DGSTSW) \ + X("volume number", -6, VOLUMSW) \ + X("issue number", -5, ISSUESW) \ + X("nobody", -6, NBODYSW) \ + X("fmtproc program", 0, FMTPROCSW) \ + X("nofmtproc", 0, NFMTPROCSW) \ + +#define X(sw, minchars, id) id, +DEFINE_SWITCH_ENUM(MHL); +#undef X + +#define X(sw, minchars, id) { sw, minchars, id }, +DEFINE_SWITCH_ARRAY(MHL, mhlswitches); +#undef X #define NOCOMPONENT 0x000001 /* don't show component name */ #define UPPERCASE 0x000002 /* display in all upper case */ @@ -102,12 +88,67 @@ static struct swit mhlswitches[] = { #define DATEFMT 0x000800 /* contains dates */ #define FORMAT 0x001000 /* parse address/date/RFC-2047 field */ #define INIT 0x002000 /* initialize component */ -#define FACEFMT 0x004000 /* contains face */ -#define FACEDFLT 0x008000 /* default for face */ +#define RTRIM 0x004000 /* trim trailing whitespace */ #define SPLIT 0x010000 /* split headers (don't concatenate) */ #define NONEWLINE 0x020000 /* don't write trailing newline */ -#define LBITS "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017FACEFMT\020FACEDFLT\021SPLIT\022NONEWLINE" -#define GFLAGS (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT) +#define NOWRAP 0x040000 /* Don't wrap lines ever */ +#define FMTFILTER 0x080000 /* Filter through format filter */ +#define INVISIBLE 0x100000 /* count byte in display columns? */ +#define FORCE7BIT 0x200000 /* don't display 8-bit bytes */ +#define LBITS "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017RTRIM\021SPLIT\022NONEWLINE\023NOWRAP\024FMTFILTER\025INVISIBLE\026FORCE7BIT" +#define GFLAGS (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT | NOWRAP) + +/* + * A format string to be used as a command-line argument to the body + * format filter. + */ + +struct arglist { + struct format *a_fmt; + char *a_nfs; + struct arglist *a_next; +}; + +/* + * Linked list of command line arguments for the body format filter. This + * USED to be in "struct mcomp", but the format API got cleaned up and even + * though it reduced the code we had to do, it make things more complicated + * for us. Specifically: + * + * - The interface to the hash table has been cleaned up, which means the + * rooting around in the hash table is no longer necessary (yay!). But + * this ALSO means that we have to make sure that we call our format + * compilation routines before we process the message, because the + * components need to be visible in the hash table so we can save them for + * later. So we moved them out of "mcomp" and now compile them right before + * header processing starts. + * - We also use format strings to handle other components in the mhl + * configuration (using "formatfield" and "decode"), but here life + * gets complicated: they aren't dealt with in the normal way. Instead + * of referring to a component like {from}, each component is processed + * using the special {text} component. But these format strings need to be + * compiled BEFORE we compile the format arguments; in the previous + * implementation they were compiled and scanned as the headers were + * read, and that would reset the hash table that we need to populate + * the components used by the body format filter. So we are compiling + * the formatfield component strings ahead of time and then scanning them + * later. + * + * Okay, fine ... this was broken before. But you know what? Fixing this + * the right way will make things easier down the road. + * + * One side-effect to this change: format strings are now compiled only once + * for components specified with "formatfield", but they are compiled for + * every message for format arguments. + */ + +static struct arglist *arglist_head; +static struct arglist *arglist_tail; +static int filter_nargs = 0; + +/* + * Flags/options for each component + */ struct mcomp { char *c_name; /* component name */ @@ -115,13 +156,14 @@ struct mcomp { char *c_ovtxt; /* text overflow indicator */ char *c_nfs; /* iff FORMAT */ struct format *c_fmt; /* .. */ - char *c_face; /* face designator */ + struct comp *c_c_text; /* Ref to {text} in FORMAT */ + struct comp *c_c_error; /* Ref to {error} */ int c_offset; /* left margin indentation */ int c_ovoff; /* overflow indentation */ int c_width; /* width of field */ int c_cwidth; /* width of component */ int c_length; /* length in lines */ - long c_flags; + unsigned long c_flags; struct mcomp *c_next; }; @@ -131,21 +173,21 @@ static struct mcomp *fmthd = NULL; static struct mcomp *fmttl = NULL; static struct mcomp global = { - NULL, NULL, "", NULL, NULL, 0, -1, 80, -1, 40, BELL, 0 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, -1, 80, -1, 40, BELL, NULL }; static struct mcomp holder = { - NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, 0 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, NULL }; struct pair { char *p_name; - long p_flags; + unsigned long p_flags; }; static struct pair pairs[] = { { "Date", DATEFMT }, - { "From", ADDRFMT|FACEDFLT }, + { "From", ADDRFMT }, { "Sender", ADDRFMT }, { "Reply-To", ADDRFMT }, { "To", ADDRFMT }, @@ -158,14 +200,13 @@ static struct pair pairs[] = { { "Resent-To", ADDRFMT }, { "Resent-cc", ADDRFMT }, { "Resent-Bcc", ADDRFMT }, - { "Face", FACEFMT }, { NULL, 0 } }; struct triple { char *t_name; - long t_on; - long t_off; + unsigned long t_on; + unsigned long t_off; }; static struct triple triples[] = { @@ -183,15 +224,37 @@ static struct triple triples[] = { { "nocompress", 0, COMPRESS }, { "split", SPLIT, 0 }, { "nosplit", 0, SPLIT }, + { "rtrim", RTRIM, 0 }, + { "nortrim", 0, RTRIM }, { "addrfield", ADDRFMT, DATEFMT }, { "bell", BELL, 0 }, { "nobell", 0, BELL }, { "datefield", DATEFMT, ADDRFMT }, { "newline", 0, NONEWLINE }, { "nonewline", NONEWLINE, 0 }, + { "wrap", 0, NOWRAP }, + { "nowrap", NOWRAP, 0 }, + { "format", FMTFILTER, 0 }, + { "noformat", 0, FMTFILTER }, { NULL, 0, 0 } }; +static char *addrcomps[] = { + "from", + "sender", + "reply-to", + "to", + "cc", + "bcc", + "resent-from", + "resent-sender", + "resent-reply-to", + "resent-to", + "resent-cc", + "resent-bcc", + NULL +}; + static int bellflg = 0; static int clearflg = 0; @@ -209,19 +272,21 @@ static int issue = 0; static int exitstat = 0; static int mhldebug = 0; +static int filesize = 0; + #define PITTY (-1) #define NOTTY 0 #define ISTTY 1 static int ontty = NOTTY; static int row; -static int column; +static unsigned int column; static int lm; static int llim; static int ovoff; static int term; -static int wid; +static unsigned int wid; static char *ovtxt; @@ -239,15 +304,7 @@ static char delim3[] = /* from forw.c */ "\n----------------------------------------------------------------------\n\n"; static char delim4[] = "\n------------------------------\n\n"; -static FILE *(*mhl_action) () = (FILE *(*) ()) 0; - - -/* - * Redefine a couple of functions. - * These are undefined later in the code. - */ -#define adios mhladios -#define done mhldone +static FILE *(*mhl_action)(char *); /* * prototypes @@ -259,75 +316,61 @@ static int ptos (char *, char **); static char *parse (void); static void process (char *, char *, int, int); static void mhlfile (FILE *, char *, int, int); -static int mcomp_flags (char *); -static char *mcomp_add (long, char *, char *); +static int mcomp_flags (char *) PURE; +static char *mcomp_add (unsigned long, char *, char *); static void mcomp_format (struct mcomp *, struct mcomp *); static struct mcomp *add_queue (struct mcomp **, struct mcomp **, char *, char *, int); static void free_queue (struct mcomp **, struct mcomp **); static void putcomp (struct mcomp *, struct mcomp *, int); -static char *oneline (char *, long); -static void putstr (char *); -static void putch (char); -static RETSIGTYPE intrser (int); -static RETSIGTYPE pipeser (int); -static RETSIGTYPE quitser (int); -static void face_format (struct mcomp *); -static int doface (struct mcomp *); -static void mhladios (char *, char *, ...); -static void mhldone (int); -static void m_popen (char *); - -int mhl (int, char **); -int mhlsbr (int, char **, FILE *(*)()); -void m_pclose (void); - -void clear_screen (void); /* from termsbr.c */ -int SOprintf (char *, ...); /* from termsbr.c */ -int sc_width (void); /* from termsbr.c */ -int sc_length (void); /* from termsbr.c */ -int sc_hardcopy (void); /* from termsbr.c */ -struct hostent *gethostbystring (); +static char *oneline (char *, unsigned long); +static void putstr (char *, unsigned long); +static void putch (char, unsigned long); +static void intrser (int); +static void pipeser (int); +static void quitser (int); +static void mhladios (char *, char *, ...) CHECK_PRINTF(2, 3) NORETURN; +static void mhldone (int) NORETURN; +static void filterbody (struct mcomp *, char *, int, int, + m_getfld_state_t); +static void compile_formatfield(struct mcomp *); +static void compile_filterargs (void); int mhl (int argc, char **argv) { int length = 0, nomore = 0; - int i, width = 0, vecp = 0; + unsigned int i, vecp = 0; + int width = 0; char *cp, *folder = NULL, *form = NULL; char buf[BUFSIZ], *files[MAXARGS]; char **argp, **arguments; + /* Need this if called from main() of show(1). */ invo_name = r1bindex (argv[0], '/'); - /* read user profile/context */ - context_read(); - arguments = getarguments (invo_name, argc, argv, 1); argp = arguments; if ((cp = getenv ("MHLDEBUG")) && *cp) mhldebug++; - if ((cp = getenv ("FACEPROC"))) - faceproc = cp; - while ((cp = *argp++)) { if (*cp == '-') { switch (smatch (++cp, mhlswitches)) { case AMBIGSW: ambigsw (cp, mhlswitches); - done (1); + mhldone (1); case UNKWNSW: - adios (NULL, "-%s unknown\n", cp); + mhladios (NULL, "-%s unknown\n", cp); case HELPSW: snprintf (buf, sizeof(buf), "%s [switches] [files ...]", invo_name); print_help (buf, mhlswitches, 1); - done (1); + mhldone (0); case VERSIONSW: print_version(invo_name); - done (1); + mhldone (0); case BELLSW: bellflg = 1; @@ -345,76 +388,78 @@ mhl (int argc, char **argv) case FOLDSW: if (!(folder = *argp++) || *folder == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); continue; case FORMSW: if (!(form = *argp++) || *form == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); continue; - case FACESW: - if (!(faceproc = *argp++) || *faceproc == '-') - adios (NULL, "missing argument to %s", argp[-2]); - continue; - case NFACESW: - faceproc = NULL; - continue; case SLEEPSW: if (!(cp = *argp++) || *cp == '-') - adios (NULL, "missing argument to %s", argp[-2]); - sleepsw = atoi (cp);/* ZERO ok! */ + mhladios (NULL, "missing argument to %s", argp[-2]); + sleepsw = atoi (cp);/* ZERO ok! */ continue; case PROGSW: if (!(moreproc = *argp++) || *moreproc == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); continue; case NPROGSW: nomore++; continue; + case FMTPROCSW: + if (!(formatproc = *argp++) || *formatproc == '-') + mhladios (NULL, "missing argument to %s", argp[-2]); + continue; + case NFMTPROCSW: + formatproc = NULL; + continue; + case LENSW: if (!(cp = *argp++) || *cp == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); if ((length = atoi (cp)) < 1) - adios (NULL, "bad argument %s %s", argp[-2], cp); + mhladios (NULL, "bad argument %s %s", argp[-2], cp); continue; case WIDTHSW: if (!(cp = *argp++) || *cp == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); if ((width = atoi (cp)) < 1) - adios (NULL, "bad argument %s %s", argp[-2], cp); + mhladios (NULL, "bad argument %s %s", argp[-2], cp); continue; case DGSTSW: if (!(digest = *argp++) || *digest == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); continue; case ISSUESW: if (!(cp = *argp++) || *cp == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); if ((issue = atoi (cp)) < 1) - adios (NULL, "bad argument %s %s", argp[-2], cp); + mhladios (NULL, "bad argument %s %s", argp[-2], cp); continue; case VOLUMSW: if (!(cp = *argp++) || *cp == '-') - adios (NULL, "missing argument to %s", argp[-2]); + mhladios (NULL, "missing argument to %s", argp[-2]); if ((volume = atoi (cp)) < 1) - adios (NULL, "bad argument %s %s", argp[-2], cp); + mhladios (NULL, "bad argument %s %s", argp[-2], cp); continue; case FORW2SW: - forwall++; /* fall */ + forwall++; + /* FALLTHRU */ case FORW1SW: forwflg++; clearflg = -1;/* XXX */ continue; case BITSTUFFSW: - dashstuff = 1; /* trinary logic */ + dashstuff = 1; /* ternary logic */ continue; case NBITSTUFFSW: - dashstuff = -1; /* trinary logic */ + dashstuff = -1; /* ternary logic */ continue; case NBODYSW: @@ -429,13 +474,13 @@ mhl (int argc, char **argv) folder = getenv ("mhfolder"); if (isatty (fileno (stdout))) { - if (!nomore && !sc_hardcopy() && moreproc && *moreproc != '\0') { + if (!nomore && moreproc && *moreproc != '\0') { if (mhl_action) { SIGNAL (SIGINT, SIG_IGN); SIGNAL2 (SIGQUIT, quitser); } SIGNAL2 (SIGPIPE, pipeser); - m_popen (moreproc); + m_popen (moreproc, mhl_action != NULL); ontty = PITTY; } else { SIGNAL (SIGINT, SIG_IGN); @@ -457,7 +502,7 @@ mhl (int argc, char **argv) if (forwall) { if (digest) { - printf ("%s", delim4); + fputs(delim4, stdout); if (volume == 0) { snprintf (buf, sizeof(buf), "End of %s Digest\n", digest); } else { @@ -469,15 +514,20 @@ mhl (int argc, char **argv) *cp++ = '*'; *cp++ = '\n'; *cp = 0; - printf ("%s", buf); + fputs(buf, stdout); } else - printf ("\n------- End of Forwarded Message%s\n\n", - vecp > 1 ? "s" : ""); + printf ("\n------- End of Forwarded Message%s\n", + PLURALS(vecp)); } + fflush(stdout); + if(ferror(stdout)){ + mhladios("output", "error writing"); + } + if (clearflg > 0 && ontty == NOTTY) - clear_screen (); + nmh_clear_screen (); if (ontty == PITTY) m_pclose (); @@ -490,8 +540,8 @@ static void mhl_format (char *file, int length, int width) { int i; - char *bp, *cp, **ip; - char *ap, buffer[BUFSIZ], name[NAMESZ]; + char *bp, **ip; + char *ap, name[NAMESZ]; struct mcomp *c1; struct stat st; FILE *fp; @@ -505,12 +555,11 @@ mhl_format (char *file, int length, int width) && dev == st.st_dev && ino == st.st_ino) goto out; - else - free_queue (&fmthd, &fmttl); + free_queue (&fmthd, &fmttl); } if ((fp = fopen (etcpath (file), "r")) == NULL) - adios (file, "unable to open format file"); + mhladios (file, "unable to open format file"); if (fstat (fileno (fp), &st) != NOTOK) { mtime = st.st_mtime; @@ -529,17 +578,17 @@ mhl_format (char *file, int length, int width) global.c_length = i - 1; global.c_flags = BELL; /* BELL is default */ *(ip = ignores) = NULL; + filter_nargs = 0; while (vfgets (fp, &ap) == OK) { bp = ap; if (*bp == ';') continue; - if ((cp = strchr(bp, '\n'))) - *cp = 0; + trim_suffix_c(bp, '\n'); if (*bp == ':') { - c1 = add_queue (&fmthd, &fmttl, NULL, bp + 1, CLEARTEXT); + (void) add_queue (&fmthd, &fmttl, NULL, bp + 1, CLEARTEXT); continue; } @@ -558,7 +607,7 @@ mhl_format (char *file, int length, int width) int n = 0; /* split the fields */ - tmparray = brkstring (getcpy (++parptr), ",", NULL); + tmparray = brkstring (mh_xstrdup(++parptr), ",", NULL); /* count number of fields split */ p = tmparray; @@ -573,7 +622,7 @@ mhl_format (char *file, int length, int width) parptr = bp; while (*parptr) { if (evalvar (&global)) - adios (NULL, "format file syntax error: %s", bp); + mhladios (NULL, "format file syntax error: %s", bp); if (*parptr) parptr++; } @@ -584,33 +633,37 @@ mhl_format (char *file, int length, int width) while (*parptr == ':' || *parptr == ',') { parptr++; if (evalvar (c1)) - adios (NULL, "format file syntax error: %s", bp); + mhladios (NULL, "format file syntax error: %s", bp); } if (!c1->c_nfs && global.c_nfs) { if (c1->c_flags & DATEFMT) { - if (global.c_flags & DATEFMT) - c1->c_nfs = getcpy (global.c_nfs); - } - else - if (c1->c_flags & ADDRFMT) { - if (global.c_flags & ADDRFMT) - c1->c_nfs = getcpy (global.c_nfs); + if (global.c_flags & DATEFMT) { + c1->c_nfs = mh_xstrdup(global.c_nfs); + compile_formatfield(c1); } + } else if (c1->c_flags & ADDRFMT) { + if (global.c_flags & ADDRFMT) { + c1->c_nfs = mh_xstrdup(global.c_nfs); + compile_formatfield(c1); + } + } } continue; default: - adios (NULL, "format file syntax error: %s", bp); + mhladios (NULL, "format file syntax error: %s", bp); } } fclose (fp); if (mhldebug) { for (c1 = fmthd; c1; c1 = c1->c_next) { + char buffer[BUFSIZ]; + fprintf (stderr, "c1: name=\"%s\" text=\"%s\" ovtxt=\"%s\"\n", c1->c_name, c1->c_text, c1->c_ovtxt); - fprintf (stderr, "\tnfs=0x%x fmt=0x%x\n", - (unsigned int) c1->c_nfs, (unsigned int) c1->c_fmt); + fprintf(stderr, "\tnfs=%p fmt=%p\n", + (void *)c1->c_nfs, (void *)c1->c_fmt); fprintf (stderr, "\toffset=%d ovoff=%d width=%d cwidth=%d length=%d\n", c1->c_offset, c1->c_ovoff, c1->c_width, c1->c_cwidth, c1->c_length); @@ -668,21 +721,17 @@ evalvar (struct mcomp *c1) return ptos (name, &c1->c_ovtxt); if (!strcasecmp (name, "formatfield")) { - char *nfs; - if (ptos (name, &cp)) return 1; - nfs = new_fs (NULL, NULL, cp); - c1->c_nfs = getcpy (nfs); + c1->c_nfs = getcpy (new_fs (NULL, NULL, cp)); + compile_formatfield(c1); c1->c_flags |= FORMAT; return 0; } if (!strcasecmp (name, "decode")) { - char *nfs; - - nfs = new_fs (NULL, NULL, "%(decode{text})"); - c1->c_nfs = getcpy (nfs); + c1->c_nfs = getcpy (new_fs (NULL, NULL, "%(decode{text})")); + compile_formatfield(c1); c1->c_flags |= FORMAT; return 0; } @@ -707,6 +756,34 @@ evalvar (struct mcomp *c1) return 0; } + if (!strcasecmp (name, "formatarg")) { + struct arglist *args; + + if (ptos (name, &cp)) + return 1; + + if (! c1->c_name || strcasecmp (c1->c_name, "body")) { + inform("format filters are currently only supported on " + "the \"body\" component"); + return 1; + } + + NEW0(args); + + if (arglist_tail) + arglist_tail->a_next = args; + + arglist_tail = args; + + if (! arglist_head) + arglist_head = args; + + args->a_nfs = getcpy (new_fs (NULL, NULL, cp)); + filter_nargs++; + + return 0; + } + return 1; } @@ -717,7 +794,7 @@ ptoi (char *name, int *i) char *cp; if (*parptr++ != '=' || !*(cp = parse ())) { - advise (NULL, "missing argument to variable %s", name); + inform("missing argument to variable %s", name); return 1; } @@ -732,7 +809,7 @@ ptos (char *name, char **s) char c, *cp; if (*parptr++ != '=') { - advise (NULL, "missing argument to variable %s", name); + inform("missing argument to variable %s", name); return 1; } @@ -749,7 +826,7 @@ ptos (char *name, char **s) } c = *parptr; *parptr = 0; - *s = getcpy (cp); + *s = mh_xstrdup(cp); if ((*parptr = c) == '"') parptr++; return 0; @@ -765,15 +842,14 @@ parse (void) for (cp = result; *parptr && (cp - result < NAMESZ); parptr++) { c = *parptr; - if (isalnum (c) - || c == '.' - || c == '-' - || c == '_' - || c =='[' - || c == ']') - *cp++ = c; - else + if (!isalnum (c) + && c != '.' + && c != '-' + && c != '_' + && c !='[' + && c != ']') break; + *cp++ = c; } *cp = '\0'; @@ -781,12 +857,24 @@ parse (void) } +/* + * Process one file/message + */ + static void process (char *folder, char *fname, int ofilen, int ofilec) { - char *cp; - FILE *fp; + /* static to prevent "might be clobbered" warning from gcc 4.9.2: */ + static char *cp; + static FILE *fp; struct mcomp *c1; + struct stat st; + struct arglist *ap; + /* volatile to prevent "might be clobbered" warning from gcc: */ + char *volatile fname2 = fname ? fname : "(stdin)"; + + cp = NULL; + fp = NULL; switch (setjmp (env)) { case OK: @@ -798,49 +886,64 @@ process (char *folder, char *fname, int ofilen, int ofilec) return; } } else { - fname = "(stdin)"; fp = stdin; } - cp = folder ? concat (folder, ":", fname, NULL) : getcpy (fname); + if (fstat(fileno(fp), &st) == 0) { + filesize = st.st_size; + } else { + filesize = 0; + } + cp = folder ? concat (folder, ":", fname2, NULL) : mh_xstrdup(fname2); if (ontty != PITTY) SIGNAL (SIGINT, intrser); - mhlfile (fp, cp, ofilen, ofilec); /* FALL THROUGH! */ + mhlfile (fp, cp, ofilen, ofilec); + free (cp); - default: + for (ap = arglist_head; ap; ap = ap->a_next) { + fmt_free(ap->a_fmt, 0); + ap->a_fmt = NULL; + } + + if (arglist_head) + fmt_free(NULL, 1); + /* FALLTHRU */ + + default: if (ontty != PITTY) SIGNAL (SIGINT, SIG_IGN); - if (mhl_action == NULL && fp != stdin) + if (mhl_action == NULL && fp != stdin && fp != NULL) fclose (fp); - free (cp); - if (holder.c_text) { - free (holder.c_text); - holder.c_text = NULL; - } + mh_xfree(holder.c_text); + holder.c_text = NULL; free_queue (&msghd, &msgtl); for (c1 = fmthd; c1; c1 = c1->c_next) c1->c_flags &= ~HDROUTPUT; break; } + } static void mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) { - int state; + int state, bucket; struct mcomp *c1, *c2, *c3; - char **ip, name[NAMESZ], buf[BUFSIZ]; + char **ip, name[NAMESZ], buf[NMH_BUFSIZ]; + m_getfld_state_t gstate; + + compile_filterargs(); if (forwall) { if (digest) - printf ("%s", ofilen == 1 ? delim3 : delim4); + fputs(ofilen == 1 ? delim3 : delim4, stdout); else { printf ("\n-------"); if (ofilen == 1) - printf (" Forwarded Message%s", ofilec > 1 ? "s" : ""); + printf (" Forwarded Message%s", PLURALS(ofilec)); else printf (" Message %d", ofilen); - printf ("\n\n"); + puts("\n"); } } else { switch (ontty) { @@ -848,9 +951,9 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) if (ofilec > 1) { if (ofilen > 1) { if ((global.c_flags & CLEARSCR)) - clear_screen (); + nmh_clear_screen (); else - printf ("\n\n\n"); + puts("\n\n"); } printf (">>> %s\n\n", mname); } @@ -861,27 +964,29 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) if (ofilec > 1) { if (SOprintf ("Press to list \"%s\"...", mname)) { if (ofilen > 1) - printf ("\n\n\n"); + puts("\n\n"); printf ("Press to list \"%s\"...", mname); } fflush (stdout); buf[0] = 0; - read (fileno (stdout), buf, sizeof(buf)); + if (read (fileno (stdout), buf, sizeof(buf)) < 0) { + advise ("stdout", "read"); + } } if (strchr(buf, '\n')) { if ((global.c_flags & CLEARSCR)) - clear_screen (); + nmh_clear_screen (); } else - printf ("\n"); + putchar('\n'); break; default: if (ofilec > 1) { if (ofilen > 1) { - printf ("\n\n\n"); + puts("\n\n"); if (clearflg > 0) - clear_screen (); + nmh_clear_screen (); } printf (">>> %s\n\n", mname); } @@ -889,26 +994,33 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) } } - for (state = FLD;;) { - switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) { + gstate = m_getfld_state_init(fp); + for (;;) { + int bufsz = sizeof buf; + switch (state = m_getfld2(&gstate, name, buf, &bufsz)) { case FLD: case FLDPLUS: + bucket = fmt_addcomptext(name, buf); for (ip = ignores; *ip; ip++) if (!strcasecmp (name, *ip)) { - while (state == FLDPLUS) - state = m_getfld (state, name, buf, sizeof(buf), fp); + while (state == FLDPLUS) { + bufsz = sizeof buf; + state = m_getfld2(&gstate, name, buf, &bufsz); + fmt_appendcomp(bucket, name, buf); + } break; } if (*ip) continue; for (c2 = fmthd; c2; c2 = c2->c_next) - if (!strcasecmp (c2->c_name, name)) + if (!strcasecmp (FENDNULL(c2->c_name), name)) break; c1 = NULL; if (!((c3 = c2 ? c2 : &global)->c_flags & SPLIT)) for (c1 = msghd; c1; c1 = c1->c_next) - if (!strcasecmp (c1->c_name, c3->c_name)) { + if (!strcasecmp (FENDNULL(c1->c_name), + FENDNULL(c3->c_name))) { c1->c_text = mcomp_add (c1->c_flags, buf, c1->c_text); break; @@ -916,8 +1028,10 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) if (c1 == NULL) c1 = add_queue (&msghd, &msgtl, name, buf, 0); while (state == FLDPLUS) { - state = m_getfld (state, name, buf, sizeof(buf), fp); + bufsz = sizeof buf; + state = m_getfld2(&gstate, name, buf, &bufsz); c1->c_text = add (buf, c1->c_text); + fmt_appendcomp(bucket, name, buf); } if (c2 == NULL) c1->c_flags |= EXTRA; @@ -931,7 +1045,8 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) putcomp (c1, c1, ONECOMP); continue; } - if (!strcasecmp (c1->c_name, "messagename")) { + if (!c1->c_name || + !strcasecmp (c1->c_name, "messagename")) { holder.c_text = concat ("(Message ", mname, ")\n", NULL); putcomp (c1, &holder, ONECOMP); @@ -939,53 +1054,51 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) holder.c_text = NULL; continue; } - if (!strcasecmp (c1->c_name, "extras")) { + if (!c1->c_name || !strcasecmp (c1->c_name, "extras")) { for (c2 = msghd; c2; c2 = c2->c_next) if (c2->c_flags & EXTRA) putcomp (c1, c2, TWOCOMP); continue; } - if (dobody && !strcasecmp (c1->c_name, "body")) { - if ((holder.c_text = malloc (sizeof(buf))) == NULL) - adios (NULL, "unable to allocate buffer memory"); - strncpy (holder.c_text, buf, sizeof(buf)); - while (state == BODY) { - putcomp (c1, &holder, BODYCOMP); - state = m_getfld (state, name, holder.c_text, - sizeof(buf), fp); + if (dobody && (!c1->c_name || + !strcasecmp (c1->c_name, "body"))) { + if (c1->c_flags & FMTFILTER && state == BODY && + formatproc != NULL) { + filterbody(c1, buf, sizeof(buf), state, gstate); + } else { + holder.c_text = mh_xmalloc (sizeof(buf)); + strncpy (holder.c_text, buf, sizeof(buf)); + while (state == BODY) { + putcomp (c1, &holder, BODYCOMP); + bufsz = sizeof buf; + state = m_getfld2(&gstate, name, holder.c_text, + &bufsz); + } + free (holder.c_text); + holder.c_text = NULL; } - free (holder.c_text); - holder.c_text = NULL; continue; } for (c2 = msghd; c2; c2 = c2->c_next) - if (!strcasecmp (c2->c_name, c1->c_name)) { + if (!strcasecmp (FENDNULL(c2->c_name), + FENDNULL(c1->c_name))) { putcomp (c1, c2, ONECOMP); if (!(c1->c_flags & SPLIT)) break; } - if (faceproc && c2 == NULL && (c1->c_flags & FACEFMT)) - for (c2 = msghd; c2; c2 = c2->c_next) - if (c2->c_flags & FACEDFLT) { - if (c2->c_face == NULL) - face_format (c2); - if ((holder.c_text = c2->c_face)) { - putcomp (c1, &holder, ONECOMP); - holder.c_text = NULL; - } - break; - } } + m_getfld_state_destroy (&gstate); return; case LENERR: case FMTERR: - advise (NULL, "format error in message %s", mname); + inform("format error in message %s", mname); exitstat++; + m_getfld_state_destroy (&gstate); return; default: - adios (NULL, "getfld() returned %d", state); + mhladios (NULL, "getfld() returned %d", state); } } } @@ -1005,7 +1118,7 @@ mcomp_flags (char *name) static char * -mcomp_add (long flags, char *s1, char *s2) +mcomp_add (unsigned long flags, char *s1, char *s2) { char *dp; @@ -1031,8 +1144,7 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2) { int dat[5]; char *ap, *cp; - char buffer[BUFSIZ], error[BUFSIZ]; - struct comp *cptr; + char error[BUFSIZ]; struct pqpair *p, *q; struct pqpair pq; struct mailname *mp; @@ -1041,60 +1153,56 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2) c2->c_text = NULL; dat[0] = 0; dat[1] = 0; - dat[2] = 0; - dat[3] = sizeof(buffer) - 1; + dat[2] = filesize; + dat[3] = BUFSIZ - 1; dat[4] = 0; - fmt_compile (c1->c_nfs, &c1->c_fmt); if (!(c1->c_flags & ADDRFMT)) { - FINDCOMP (cptr, "text"); - if (cptr) - cptr->c_text = ap; + charstring_t scanl = charstring_create (BUFSIZ); + + if (c1->c_c_text) + c1->c_c_text->c_text = ap; if ((cp = strrchr(ap, '\n'))) /* drop ending newline */ if (!cp[1]) *cp = 0; - fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat); + fmt_scan (c1->c_fmt, scanl, BUFSIZ - 1, dat, NULL); /* Don't need to append a newline, dctime() already did */ - c2->c_text = getcpy (buffer); + c2->c_text = charstring_buffer_copy (scanl); + charstring_free (scanl); - free (ap); + /* ap is now owned by the component struct, so do NOT free it here */ return; } (q = &pq)->pq_next = NULL; while ((cp = getname (ap))) { - if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL) - adios (NULL, "unable to allocate pqpair memory"); - - if ((mp = getm (cp, NULL, 0, AD_NAME, error)) == NULL) { - p->pq_text = getcpy (cp); - p->pq_error = getcpy (error); - } else { - if ((c1->c_flags & FACEDFLT) && c2->c_face == NULL) { - char *h, *o; - if ((h = mp->m_host) == NULL) - h = LocalName (); - if ((o = OfficialName (h))) - h = o; - c2->c_face = concat ("address ", h, " ", mp->m_mbox, - NULL); - } - p->pq_text = getcpy (mp->m_text); - mnfree (mp); - } - q = (q->pq_next = p); + NEW0(p); + if ((mp = getm (cp, NULL, 0, error, sizeof(error))) == NULL) { + p->pq_text = mh_xstrdup(cp); + p->pq_error = mh_xstrdup(error); + } else { + p->pq_text = getcpy (mp->m_text); + mnfree (mp); + } + q = (q->pq_next = p); } for (p = pq.pq_next; p; p = q) { - FINDCOMP (cptr, "text"); - if (cptr) - cptr->c_text = p->pq_text; - FINDCOMP (cptr, "error"); - if (cptr) - cptr->c_text = p->pq_error; + charstring_t scanl = charstring_create (BUFSIZ); + char *buffer; - fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat); + if (c1->c_c_text) { + c1->c_c_text->c_text = p->pq_text; + p->pq_text = NULL; + } + if (c1->c_c_error) { + c1->c_c_error->c_text = p->pq_error; + p->pq_error = NULL; + } + + fmt_scan (c1->c_fmt, scanl, BUFSIZ - 1, dat, NULL); + buffer = charstring_buffer_copy (scanl); if (*buffer) { if (c2->c_text) c2->c_text = add (",\n", c2->c_text); @@ -1102,12 +1210,12 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2) *cp = 0; c2->c_text = add (buffer, c2->c_text); } + charstring_free (scanl); - free (p->pq_text); - if (p->pq_error) - free (p->pq_error); + mh_xfree(p->pq_text); + mh_xfree(p->pq_error); q = p->pq_next; - free ((char *) p); + free(p); } c2->c_text = add ("\n", c2->c_text); @@ -1120,21 +1228,19 @@ add_queue (struct mcomp **head, struct mcomp **tail, char *name, char *text, int { struct mcomp *c1; - if ((c1 = (struct mcomp *) calloc ((size_t) 1, sizeof(*c1))) == NULL) - adios (NULL, "unable to allocate comp memory"); - + NEW0(c1); c1->c_flags = flags & ~INIT; - if ((c1->c_name = name ? getcpy (name) : NULL)) - c1->c_flags |= mcomp_flags (c1->c_name); - c1->c_text = text ? getcpy (text) : NULL; + if ((c1->c_name = name ? mh_xstrdup(name) : NULL)) + c1->c_flags |= mcomp_flags (c1->c_name); + c1->c_text = text ? mh_xstrdup(text) : NULL; if (flags & INIT) { - if (global.c_ovtxt) - c1->c_ovtxt = getcpy (global.c_ovtxt); - c1->c_offset = global.c_offset; - c1->c_ovoff = global. c_ovoff; - c1->c_width = c1->c_length = 0; - c1->c_cwidth = global.c_cwidth; - c1->c_flags |= global.c_flags & GFLAGS; + if (global.c_ovtxt) + c1->c_ovtxt = mh_xstrdup(global.c_ovtxt); + c1->c_offset = global.c_offset; + c1->c_ovoff = global. c_ovoff; + c1->c_width = c1->c_length = 0; + c1->c_cwidth = global.c_cwidth; + c1->c_flags |= global.c_flags & GFLAGS; } if (*head == NULL) *head = c1; @@ -1153,19 +1259,13 @@ free_queue (struct mcomp **head, struct mcomp **tail) for (c1 = *head; c1; c1 = c2) { c2 = c1->c_next; - if (c1->c_name) - free (c1->c_name); - if (c1->c_text) - free (c1->c_text); - if (c1->c_ovtxt) - free (c1->c_ovtxt); - if (c1->c_nfs) - free (c1->c_nfs); + mh_xfree(c1->c_name); + mh_xfree(c1->c_text); + mh_xfree(c1->c_ovtxt); + mh_xfree(c1->c_nfs); if (c1->c_fmt) - free ((char *) c1->c_fmt); - if (c1->c_face) - free (c1->c_face); - free ((char *) c1); + fmt_free (c1->c_fmt, 0); + free(c1); } *head = *tail = NULL; @@ -1175,8 +1275,22 @@ free_queue (struct mcomp **head, struct mcomp **tail) static void putcomp (struct mcomp *c1, struct mcomp *c2, int flag) { + char *text; /* c1's text, or the name as a fallback. */ + char *trimmed_prefix; int count, cchdr; char *cp; + const int utf8 = strcasecmp(get_charset(), "UTF-8") == 0; + + if (! utf8 && flag != BODYCOMP) { + /* Don't print 8-bit bytes in header field values if not in a + UTF-8 locale, as required by RFC 6532. */ + c1->c_flags |= FORCE7BIT; + } + + text = c1->c_text ? c1->c_text : c1->c_name; + /* Create a copy with trailing whitespace trimmed, for use with + * blank lines. */ + trimmed_prefix = rtrim(mh_xstrdup(FENDNULL(text))); cchdr = 0; lm = 0; @@ -1187,26 +1301,17 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag) if ((ovtxt = c1->c_ovtxt ? c1->c_ovtxt : global.c_ovtxt) == NULL) ovtxt = ""; if (wid < ovoff + strlen (ovtxt) + 5) - adios (NULL, "component: %s width(%d) too small for overflow(%d)", + mhladios(NULL, "component: %s width(%d) too small for overflow(%zu)", c1->c_name, wid, ovoff + strlen (ovtxt) + 5); onelp = NULL; if (c1->c_flags & CLEARTEXT) { - putstr (c1->c_text); - putstr ("\n"); + putstr (c1->c_flags & RTRIM ? rtrim (c1->c_text) : c1->c_text, + c1->c_flags); + putstr ("\n", c1->c_flags); return; } - if (c1->c_flags & FACEFMT) - switch (doface (c2)) { - case NOTOK: /* error */ - case OK: /* async faceproc */ - return; - - default: /* sync faceproc */ - break; - } - if (c1->c_nfs && (c1->c_flags & (ADDRFMT | DATEFMT | FORMAT))) mcomp_format (c1, c2); @@ -1214,7 +1319,7 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag) count = (c1->c_width ? c1->c_width : global.c_width) - c1->c_offset - strlen (c2->c_text); if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT)) - count -= strlen (c1->c_text ? c1->c_text : c1->c_name) + 2; + count -= strlen(text) + 2; lm = c1->c_offset + (count / 2); } else { if (c1->c_offset) @@ -1223,20 +1328,17 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag) if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT)) { if (c1->c_flags & UPPERCASE) /* uppercase component also */ - for (cp = (c1->c_text ? c1->c_text : c1->c_name); *cp; cp++) - if (islower (*cp)) - *cp = toupper (*cp); - putstr (c1->c_text ? c1->c_text : c1->c_name); + to_upper(text); + putstr(text, c1->c_flags); if (flag != BODYCOMP) { - putstr (": "); + putstr (": ", c1->c_flags); if (!(c1->c_flags & SPLIT)) c1->c_flags |= HDROUTPUT; cchdr++; - if ((count = c1->c_cwidth - - strlen (c1->c_text ? c1->c_text : c1->c_name) - 2) > 0) + if ((count = c1->c_cwidth - strlen(text) - 2) > 0) while (count--) - putstr (" "); + putstr (" ", c1->c_flags); } else c1->c_flags |= HDROUTPUT; /* for BODYCOMP */ @@ -1246,54 +1348,64 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag) && !(c2->c_flags & HDROUTPUT) && !(c2->c_flags & NOCOMPONENT)) { if (c1->c_flags & UPPERCASE) - for (cp = c2->c_name; *cp; cp++) - if (islower (*cp)) - *cp = toupper (*cp); - putstr (c2->c_name); - putstr (": "); + to_upper(c2->c_name); + putstr (c2->c_name, c1->c_flags); + putstr (": ", c1->c_flags); if (!(c1->c_flags & SPLIT)) c2->c_flags |= HDROUTPUT; cchdr++; if ((count = c1->c_cwidth - strlen (c2->c_name) - 2) > 0) while (count--) - putstr (" "); + putstr (" ", c1->c_flags); } if (c1->c_flags & UPPERCASE) - for (cp = c2->c_text; *cp; cp++) - if (islower (*cp)) - *cp = toupper (*cp); + to_upper(c2->c_text); count = 0; if (cchdr) { if (flag == TWOCOMP) count = (c1->c_cwidth >= 0) ? c1->c_cwidth - : strlen (c2->c_name) + 2; + : (int) strlen (c2->c_name) + 2; else - count = (c1->c_cwidth >= 0) ? c1->c_cwidth - : strlen (c1->c_text ? c1->c_text : c1->c_name) + 2; + count = (c1->c_cwidth >= 0) ? (size_t) c1->c_cwidth + : strlen(text) + 2; } count += c1->c_offset; if ((cp = oneline (c2->c_text, c1->c_flags))) - putstr(cp); + /* Output line, trimming trailing whitespace if requested. */ + putstr (c1->c_flags & RTRIM ? rtrim (cp) : cp, c1->c_flags); if (term == '\n') - putstr ("\n"); + putstr ("\n", c1->c_flags); while ((cp = oneline (c2->c_text, c1->c_flags))) { lm = count; if (flag == BODYCOMP - && !(c1->c_flags & NOCOMPONENT)) - putstr (c1->c_text ? c1->c_text : c1->c_name); - if (*cp) - putstr (cp); + && !(c1->c_flags & NOCOMPONENT)) { + /* Output component, trimming trailing whitespace if there + is no text on the line. */ + if (*cp) { + putstr(text, c1->c_flags); + } else { + putstr (trimmed_prefix, c1->c_flags); + } + } + if (*cp) { + /* Output line, trimming trailing whitespace if requested. */ + putstr (c1->c_flags & RTRIM ? rtrim (cp) : cp, c1->c_flags); + } if (term == '\n') - putstr ("\n"); + putstr ("\n", c1->c_flags); } + if (flag == BODYCOMP && term == '\n') + c1->c_flags &= ~HDROUTPUT; /* Buffer ended on a newline */ + + free (trimmed_prefix); } static char * -oneline (char *stuff, long flags) +oneline (char *stuff, unsigned long flags) { int spc; char *cp, *ret; @@ -1307,17 +1419,16 @@ oneline (char *stuff, long flags) term = 0; if (flags & COMPRESS) { for (spc = 1, cp = ret; *onelp; onelp++) - if (isspace (*onelp)) { + if (isspace ((unsigned char) *onelp)) { if (*onelp == '\n' && (!onelp[1] || (flags & ADDRFMT))) { term = '\n'; *onelp++ = 0; break; } - else - if (!spc) { - *cp++ = ' '; - spc++; - } + if (!spc) { + *cp++ = ' '; + spc++; + } } else { *cp++ = *onelp; @@ -1345,27 +1456,56 @@ oneline (char *stuff, long flags) static void -putstr (char *string) +putstr (char *string, unsigned long flags) { + /* To not count, for the purpose of counting columns, all of + the bytes of a multibyte character. */ + int char_len; + if (!column && lm > 0) { while (lm > 0) if (lm >= 8) { - putch ('\t'); + putch ('\t', flags); lm -= 8; } else { - putch (' '); + putch (' ', flags); lm--; } } lm = 0; - while (*string) - putch (*string++); + +#ifdef MULTIBYTE_SUPPORT + if (mbtowc (NULL, NULL, 0)) {} /* reset shift state */ + char_len = 0; +#else + NMH_UNUSED (char_len); +#endif + + while (*string) { + flags &= ~INVISIBLE; +#ifdef MULTIBYTE_SUPPORT + /* mbtowc should never return 0, because *string is non-NULL. */ + if (char_len <= 0) { + /* Find number of bytes in next character. */ + if ((char_len = + mbtowc (NULL, string, (size_t) MB_CUR_MAX)) == -1) { + char_len = 1; + } + } else { + /* Multibyte character, after the first byte. */ + flags |= INVISIBLE; + } + + --char_len; +#endif + putch (*string++, flags); + } } static void -putch (char ch) +putch (char ch, unsigned long flags) { char buf[BUFSIZ]; @@ -1384,10 +1524,12 @@ putch (char ch) putchar ('\007'); fflush (stdout); buf[0] = 0; - read (fileno (stdout), buf, sizeof(buf)); + if (read (fileno (stdout), buf, sizeof(buf)) < 0) { + advise ("stdout", "read"); + } if (strchr(buf, '\n')) { if (global.c_flags & CLEARSCR) - clear_screen (); + nmh_clear_screen (); row = 0; } else { putchar ('\n'); @@ -1417,30 +1559,49 @@ putch (char ch) putchar ('-'); putchar (' '); } - if (ch >= ' ') - column++; + /* + * Increment the character count, unless + * 1) In UTF-8 locale, this is other than the last byte of + a multibyte character, or + * 2) In C locale, will print a non-printable character. + */ + if ((flags & FORCE7BIT) == 0) { + /* UTF-8 locale */ + if ((flags & INVISIBLE) == 0) { + /* If multibyte character, its first byte only. */ + ++column; + } + } else { + /* If not an ASCII character, the replace character will be + displayed. Count it. */ + if (! isascii((unsigned char) ch) || isprint((unsigned char) ch)) { + ++column; + } + } break; } - if (column >= wid) { - putch ('\n'); + if (column >= wid && (flags & NOWRAP) == 0) { + putch ('\n', flags); if (ovoff > 0) lm = ovoff; - putstr (ovtxt ? ovtxt : ""); - putch (ch); + putstr (FENDNULL(ovtxt), flags); + putch (ch, flags); return; } - putchar (ch); + if (flags & FORCE7BIT && ! isascii((unsigned char) ch)) { + putchar ('?'); + } else { + putchar (ch); + } } -static RETSIGTYPE +static void intrser (int i) { -#ifndef RELIABLE_SIGNALS - SIGNAL (SIGINT, intrser); -#endif + NMH_UNUSED (i); discard (stdout); putchar ('\n'); @@ -1448,363 +1609,316 @@ intrser (int i) } -static RETSIGTYPE +static void pipeser (int i) { -#ifndef RELIABLE_SIGNALS - SIGNAL (SIGPIPE, pipeser); -#endif + NMH_UNUSED (i); - done (NOTOK); + mhldone (NOTOK); } -static RETSIGTYPE +static void quitser (int i) { -#ifndef RELIABLE_SIGNALS - SIGNAL (SIGQUIT, quitser); -#endif + NMH_UNUSED (i); putchar ('\n'); fflush (stdout); - done (NOTOK); + mhldone (NOTOK); } static void -face_format (struct mcomp *c1) +mhladios (char *what, char *fmt, ...) { - char *cp; - struct mailname *mp; + va_list ap; - if ((cp = c1->c_text) == NULL) - return; + va_start(ap, fmt); + advertise (what, NULL, fmt, ap); + va_end(ap); + mhldone (1); +} - if ((cp = getname (cp))) { - if ((mp = getm (cp, NULL, 0, AD_NAME, NULL))) { - char *h, *o; - if ((h = mp->m_host) == NULL) - h = LocalName (); - if ((o = OfficialName (h))) - h = o; - c1->c_face = concat ("address ", h, " ", mp->m_mbox, NULL); - } - while ((cp = getname (cp))) - continue; - } +static void +mhldone (int status) +{ + exitstat = status; + if (mhl_action) + longjmp (mhlenv, DONE); + done (exitstat); } /* - * faceproc is two elements defining the image agent's location: - * Internet host - * UDP port + * Compile a format string used by the formatfield option and save it + * for later. + * + * We will want the {text} (and possibly {error}) components for later, + * so look for them and save them if we find them. */ -#include -#include -#include +static void +compile_formatfield(struct mcomp *c1) +{ + fmt_compile(c1->c_nfs, &c1->c_fmt, 1); + + /* + * As a note to myself and any other poor bastard who is looking through + * this code in the future .... + * + * When the format hash table is reset later on (as it almost certainly + * will be), there will still be references to these components in the + * compiled format instructions. Thus these component references will + * be free'd when the format instructions are free'd (by fmt_free()). + * + * So, in other words ... don't go free'ing them yourself! + */ + + c1->c_c_text = fmt_findcomp("text"); + c1->c_c_error = fmt_findcomp("error"); +} -#ifdef HAVE_ARPA_INET_H -# include -#endif +/* + * Compile all of the arguments for our format list. + * + * Iterate through the linked list of format strings and compile them. + * Note that we reset the format hash table before we start, but we do NOT + * reset it between calls to fmt_compile(). + * + */ -static int -doface (struct mcomp *c1) +static void +compile_filterargs (void) { - int result, sd; - struct sockaddr_in in_socket; - struct sockaddr_in *isock = &in_socket; - static int inited = OK; - static int addrlen; - static struct in_addr addr; - static unsigned short portno; - - if (inited == OK) { - char *cp; - char **ap = brkstring (cp = getcpy (faceproc), " ", "\n"); - struct hostent *hp; - - if (ap[0] == NULL || ap[1] == NULL) { -bad_faceproc: ; - free (cp); - return (inited = NOTOK); - } - - if (!(hp = gethostbystring (ap[0]))) - goto bad_faceproc; - memcpy((char *) &addr, hp->h_addr, addrlen = hp->h_length); + struct arglist *arg = arglist_head; + struct comp *cptr; + char **ap; - portno = htons ((unsigned short) atoi (ap[1])); - free (cp); + fmt_free(NULL, 1); - inited = DONE; + while (arg) { + fmt_compile(arg->a_nfs, &arg->a_fmt, 0); + arg = arg->a_next; } - if (inited == NOTOK) - return NOTOK; - - isock->sin_family = AF_INET; - isock->sin_port = portno; - memcpy((char *) &isock->sin_addr, (char *) &addr, addrlen); - if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == NOTOK) - return NOTOK; + /* + * Search through and mark any components that are address components + */ - result = sendto (sd, c1->c_text, strlen (c1->c_text), 0, - (struct sockaddr *) isock, sizeof(*isock)); - - close (sd); - - return (result != NOTOK ? OK : NOTOK); + for (ap = addrcomps; *ap; ap++) { + cptr = fmt_findcomp (*ap); + if (cptr) + cptr->c_type |= CT_ADDR; + } } /* - * COMMENTED OUT - * This version doesn't use sockets + * Filter the body of a message through a specified format program */ -#if 0 -static int -doface (struct mcomp *c1) +static void +filterbody (struct mcomp *c1, char *buf, int bufsz, int state, + m_getfld_state_t gstate) { - int i, len, vecp; - pid_t child_id; - int result, pdi[2], pdo[2]; - char *bp, *cp; - char buffer[BUFSIZ], *vec[10]; - - if (pipe (pdi) == NOTOK) - return NOTOK; - if (pipe (pdo) == NOTOK) { - close (pdi[0]); - close (pdi[1]); - return NOTOK; + struct mcomp holder; + char name[NAMESZ]; + int fdinput[2], fdoutput[2], waitstat; + ssize_t cc; + pid_t writerpid, filterpid; + + /* + * Create pipes so we can communicate with our filter process. + */ + + if (pipe(fdinput) < 0) { + adios(NULL, "Unable to create input pipe"); } - for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++) - sleep (5); - - switch (child_id) { - case NOTOK: - /* oops... fork error */ - return NOTOK; - - case OK: - /* child process */ - SIGNAL (SIGINT, SIG_IGN); - SIGNAL (SIGQUIT, SIG_IGN); - if (pdi[0] != fileno (stdin)) { - dup2 (pdi[0], fileno (stdin)); - close (pdi[0]); - } - close (pdi[1]); - close (pdo[0]); - if (pdo[1] != fileno (stdout)) { - dup2 (pdo[1], fileno (stdout)); - close (pdo[1]); - } - vecp = 0; - vec[vecp++] = r1bindex (faceproc, '/'); - vec[vecp++] = "-e"; - if (sleepsw != NOTOK) { - vec[vecp++] = "-s"; - snprintf (buffer, sizeof(buffer), "%d", sleepsw); - vec[vecp++] = buffer; - } - vec[vecp] = NULL; - execvp (faceproc, vec); - fprintf (stderr, "unable to exec "); - perror (faceproc); - _exit (-1); /* NOTREACHED */ + if (pipe(fdoutput) < 0) { + adios(NULL, "Unable to create output pipe"); + } - default: - /* parent process */ - close (pdi[0]); - i = strlen (c1->c_text); - if (write (pdi[1], c1->c_text, i) != i) - adios ("pipe", "error writing to"); - free (c1->c_text), c1->c_text = NULL; - close (pdi[1]); - - close (pdo[1]); - cp = NULL, len = 0; - result = DONE; - while ((i = read (pdo[0], buffer, strlen (buffer))) > 0) { - if (cp) { - int j; - char *dp; - if ((dp = realloc (cp, (unsigned) (j = len + i))) == NULL) - adios (NULL, "unable to allocate face storage"); - memcpy(dp + len, buffer, i); - cp = dp, len = j; - } - else { - if ((cp = malloc ((unsigned) i)) == NULL) - adios (NULL, "unable to allocate face storage"); - memcpy(cp, buffer, i); - len = i; - } - if (result == DONE) - for (bp = buffer + i - 1; bp >= buffer; bp--) - if (!isascii (*bp) || iscntrl (*bp)) { - result = OK; - break; - } + /* + * Here's what we're doing to do. + * + * - Fork ourselves and start writing data to the write side of the + * input pipe (fdinput[1]). + * + * - Fork and exec our filter program. We set the standard input of + * our filter program to be the read side of our input pipe (fdinput[0]). + * Standard output is set to the write side of our output pipe + * (fdoutput[1]). + * + * - We read from the read side of the output pipe (fdoutput[0]). + * + * We're forking because that's the simplest way to prevent any deadlocks. + * (without doing something like switching to non-blocking I/O and using + * select or poll, and I'm not interested in doing that). + */ + + switch (writerpid = fork()) { + case 0: + /* + * Our child process - just write to the filter input (fdinput[1]). + * Close all other descriptors that we don't need. + */ + + close(fdinput[0]); + close(fdoutput[0]); + close(fdoutput[1]); + + /* + * Call m_getfld2() until we're no longer in the BODY state + */ + + while (state == BODY) { + int bufsz2 = bufsz; + if (write(fdinput[1], buf, strlen(buf)) < 0) { + advise ("pipe output", "write"); } - close (pdo[0]); - -/* no waiting for child... */ + state = m_getfld2(&gstate, name, buf, &bufsz2); + } - if (result == OK) { /* binary */ - if (write (1, cp, len) != len) - adios ("writing", "error"); - free (cp); - } - else /* empty */ - if ((c1->c_text = cp) == NULL) - result = OK; - break; + /* + * We should be done; time to exit. + */ + + close(fdinput[1]); + /* + * Make sure we call _exit(), otherwise we may flush out the stdio + * buffers that we have duplicated from the parent. + */ + _exit(0); + case -1: + adios(NULL, "Unable to fork for filter writer process"); + break; } - return result; -} -#endif /* COMMENTED OUT */ - - -int -mhlsbr (int argc, char **argv, FILE *(*action)()) -{ - SIGNAL_HANDLER istat, pstat, qstat; - char *cp; - struct mcomp *c1; - - switch (setjmp (mhlenv)) { - case OK: - cp = invo_name; - sleepsw = 0; /* XXX */ - bellflg = clearflg = forwflg = forwall = exitstat = 0; - digest = NULL; - ontty = NOTTY; - mhl_action = action; - + /* + * Fork and exec() our filter program, after redirecting standard in + * and standard out appropriately. + */ + + switch (filterpid = fork()) { + char **args, *program; + struct arglist *a; + int i, dat[5], s, argp; + + case 0: + /* + * Configure an argument array for us + */ + + args = argsplit(formatproc, &program, &argp); + args[argp + filter_nargs] = NULL; + dat[0] = 0; + dat[1] = 0; + dat[2] = 0; + dat[3] = BUFSIZ; + dat[4] = 0; + + /* + * Pull out each argument and scan them. + */ + + for (a = arglist_head, i = argp; a != NULL; a = a->a_next, i++) { + charstring_t scanl = charstring_create (BUFSIZ); + + fmt_scan(a->a_fmt, scanl, BUFSIZ, dat, NULL); + args[i] = charstring_buffer_copy (scanl); + charstring_free (scanl); /* - * If signal is at default action, then start ignoring - * it, else let it set to its current action. + * fmt_scan likes to put a trailing newline at the end of the + * format string. If we have one, get rid of it. */ - if ((istat = SIGNAL (SIGINT, SIG_IGN)) != SIG_DFL) - SIGNAL (SIGINT, istat); - if ((qstat = SIGNAL (SIGQUIT, SIG_IGN)) != SIG_DFL) - SIGNAL (SIGQUIT, qstat); - pstat = SIGNAL (SIGPIPE, pipeser); - mhl (argc, argv); /* FALL THROUGH! */ - - default: - SIGNAL (SIGINT, istat); - SIGNAL (SIGQUIT, qstat); - SIGNAL (SIGPIPE, SIG_IGN);/* should probably change to block instead */ - if (ontty == PITTY) - m_pclose (); - SIGNAL (SIGPIPE, pstat); - invo_name = cp; - if (holder.c_text) { - free (holder.c_text); - holder.c_text = NULL; - } - free_queue (&msghd, &msgtl); - for (c1 = fmthd; c1; c1 = c1->c_next) - c1->c_flags &= ~HDROUTPUT; - return exitstat; - } -} + s = strlen(args[i]); + if (args[i][s - 1] == '\n') + args[i][s - 1] = '\0'; -#undef adios -#undef done - -static void -mhladios (char *what, char *fmt, ...) -{ - va_list ap; + if (mhldebug) + fprintf(stderr, "filterarg: fmt=\"%s\", output=\"%s\"\n", + a->a_nfs, args[i]); + } - va_start(ap, fmt); - advertise (what, NULL, fmt, ap); - va_end(ap); - mhldone (1); -} + if (dup2(fdinput[0], STDIN_FILENO) < 0) { + adios("formatproc", "Unable to dup2() standard input"); + } + if (dup2(fdoutput[1], STDOUT_FILENO) < 0) { + adios("formatproc", "Unable to dup2() standard output"); + } + /* + * Close everything (especially the old input and output + * descriptors, since they've been dup'd to stdin and stdout), + * and exec the formatproc. + */ -static void -mhldone (int status) -{ - exitstat = status; - if (mhl_action) - longjmp (mhlenv, DONE); - else - done (exitstat); -} + close(fdinput[0]); + close(fdinput[1]); + close(fdoutput[0]); + close(fdoutput[1]); + execvp(formatproc, args); -static int m_pid = NOTOK; -static int sd = NOTOK; + adios(formatproc, "Unable to execute filter"); -static void -m_popen (char *name) -{ - int pd[2]; + break; - if (mhl_action && (sd = dup (fileno (stdout))) == NOTOK) - adios ("standard output", "unable to dup()"); + case -1: + adios(NULL, "Unable to fork format program"); + } - if (pipe (pd) == NOTOK) - adios ("pipe", "unable to"); + /* + * Close everything except our reader (fdoutput[0]); + */ - switch (m_pid = vfork ()) { - case NOTOK: - adios ("fork", "unable to"); + close(fdinput[0]); + close(fdinput[1]); + close(fdoutput[1]); - case OK: - SIGNAL (SIGINT, SIG_DFL); - SIGNAL (SIGQUIT, SIG_DFL); + /* + * As we read in this data, send it to putcomp + */ - close (pd[1]); - if (pd[0] != fileno (stdin)) { - dup2 (pd[0], fileno (stdin)); - close (pd[0]); - } - execlp (name, r1bindex (name, '/'), NULL); - fprintf (stderr, "unable to exec "); - perror (name); - _exit (-1); + holder.c_text = buf; - default: - close (pd[0]); - if (pd[1] != fileno (stdout)) { - dup2 (pd[1], fileno (stdout)); - close (pd[1]); - } + while ((cc = read(fdoutput[0], buf, bufsz - 1)) > 0) { + buf[cc] = '\0'; + putcomp(c1, &holder, BODYCOMP); } -} + if (cc < 0) { + adios(NULL, "reading from formatproc"); + } -void -m_pclose (void) -{ - if (m_pid == NOTOK) - return; + /* + * See if we got any errors along the way. I'm a little leery of calling + * waitpid() without WNOHANG, but it seems to be the most correct solution. + */ - if (sd != NOTOK) { - fflush (stdout); - if (dup2 (sd, fileno (stdout)) == NOTOK) - adios ("standard output", "unable to dup2()"); + if (waitpid(filterpid, &waitstat, 0) < 0) { + if (errno != ECHILD) { + adios("filterproc", "Unable to determine status"); + } + } else { + if (! (WIFEXITED(waitstat) && WEXITSTATUS(waitstat) == 0)) { + pidstatus(waitstat, stderr, "filterproc"); + } + } - clearerr (stdout); - close (sd); - sd = NOTOK; + if (waitpid(writerpid, &waitstat, 0) < 0) { + if (errno != ECHILD) { + adios("writer process", "Unable to determine status"); + done(1); + } + } else { + if (! (WIFEXITED(waitstat) && WEXITSTATUS(waitstat) == 0)) { + pidstatus(waitstat, stderr, "writer process"); + done(1); + } } - else - fclose (stdout); - pidwait (m_pid, OK); - m_pid = NOTOK; + close(fdoutput[0]); }