#include <h/utils.h>
#include <h/m_setjmp.h>
#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
/*
* MAJOR BUG:
#define FACEDFLT 0x008000 /* default for face */
#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 LBITS "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017FACEFMT\020FACEDFLT\021SPLIT\022NONEWLINE\023NOWRAP\024FMTFILTER"
+#define GFLAGS (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT | NOWRAP)
struct mcomp {
char *c_name; /* component name */
{ "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 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 void putstr (char *, long);
+static void putch (char, long);
static void intrser (int);
static void pipeser (int);
static void quitser (int);
static void mhladios (char *, char *, ...);
static void mhldone (int);
static void m_popen (char *);
+static void filterbody (struct mcomp *, char *, int, int, FILE *);
int mhl (int, char **);
int mhlsbr (int, char **, FILE *(*)());
continue;
}
if (dobody && !mh_strcasecmp (c1->c_name, "body")) {
- holder.c_text = mh_xmalloc (sizeof(buf));
- 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 (c1->c_flags & FMTFILTER && state == BODY &&
+ formatproc != NULL) {
+ filterbody(c1, buf, sizeof(buf), state, fp);
+ } else {
+ holder.c_text = mh_xmalloc (sizeof(buf));
+ 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);
+ }
+ 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 ((c1->c_flags & FACEDFLT) && c2->c_face == NULL) {
char *h, *o;
if ((h = mp->m_host) == NULL)
- h = LocalName ();
+ h = LocalName (0);
if ((o = OfficialName (h)))
h = o;
c2->c_face = concat ("address ", h, " ", mp->m_mbox,
onelp = NULL;
if (c1->c_flags & CLEARTEXT) {
- putstr (c1->c_text);
- putstr ("\n");
+ putstr (c1->c_text, c1->c_flags);
+ putstr ("\n", c1->c_flags);
return;
}
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);
+ putstr (c1->c_text ? c1->c_text : c1->c_name, c1->c_flags);
if (flag != BODYCOMP) {
- putstr (": ");
+ putstr (": ", c1->c_flags);
if (!(c1->c_flags & SPLIT))
c1->c_flags |= HDROUTPUT;
if ((count = c1->c_cwidth -
strlen (c1->c_text ? c1->c_text : c1->c_name) - 2) > 0)
while (count--)
- putstr (" ");
+ putstr (" ", c1->c_flags);
}
else
c1->c_flags |= HDROUTPUT; /* for BODYCOMP */
for (cp = c2->c_name; *cp; cp++)
if (islower (*cp))
*cp = toupper (*cp);
- putstr (c2->c_name);
- putstr (": ");
+ 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++)
count += c1->c_offset;
if ((cp = oneline (c2->c_text, c1->c_flags)))
- putstr(cp);
+ putstr(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);
+ putstr (c1->c_text ? c1->c_text : c1->c_name, c1->c_flags);
if (*cp)
- putstr (cp);
+ putstr (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 */
static void
-putstr (char *string)
+putstr (char *string, long flags)
{
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++);
+ putch (*string++, flags);
}
static void
-putch (char ch)
+putch (char ch, long flags)
{
char buf[BUFSIZ];
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 (ovtxt ? ovtxt : "", flags);
+ putch (ch, flags);
return;
}
if ((mp = getm (cp, NULL, 0, AD_NAME, NULL))) {
char *h, *o;
if ((h = mp->m_host) == NULL)
- h = LocalName ();
+ h = LocalName (0);
if ((o = OfficialName (h)))
h = o;
c1->c_face = concat ("address ", h, " ", mp->m_mbox, NULL);
pidwait (m_pid, OK);
m_pid = NOTOK;
}
+
+
+/*
+ * Filter the body of a message through a specified format program
+ */
+
+void
+filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
+{
+ 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");
+ }
+
+ if (pipe(fdoutput) < 0) {
+ adios(NULL, "Unable to create output pipe");
+ }
+
+ /*
+ * 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_getfld() until we're no longer in the BODY state
+ */
+
+ while (state == BODY) {
+ write(fdinput[1], buf, strlen(buf));
+ state = m_getfld(state, name, buf, bufsz, fp);
+ }
+
+ /*
+ * We should be done; time to exit.
+ */
+
+ close(fdinput[1]);
+ exit(0);
+ break;
+ case -1:
+ adios(NULL, "Unable to fork for filter writer process");
+ break;
+ }
+
+ /*
+ * Fork and exec() our filter program, after redirecting standard in
+ * and standard out appropriately.
+ */
+
+ switch (filterpid = fork()) {
+ case 0:
+ 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.
+ */
+
+ close(fdinput[0]);
+ close(fdinput[1]);
+ close(fdoutput[0]);
+ close(fdoutput[1]);
+
+ execlp(formatproc, formatproc, (char *) NULL);
+
+ adios(formatproc, "Unable to execute filter");
+
+ break;
+
+ case -1:
+ adios(NULL, "Unable to fork format program");
+ }
+
+ /*
+ * Close everything except our reader (fdoutput[0]);
+ */
+
+ close(fdinput[0]);
+ close(fdinput[1]);
+ close(fdoutput[1]);
+
+ /*
+ * As we read in this data, send it to putcomp
+ */
+
+ holder.c_text = buf;
+
+ while ((cc = read(fdoutput[0], buf, bufsz - 1)) > 0) {
+ buf[cc] = '\0';
+ putcomp(c1, &holder, BODYCOMP);
+ }
+
+ if (cc < 0) {
+ adios(NULL, "reading from formatproc");
+ }
+
+ /*
+ * 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 (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");
+ }
+ }
+
+ 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);
+ }
+ }
+
+ close(fdoutput[0]);
+}