From: Ken Hornstein Date: Tue, 31 Jan 2012 17:58:04 +0000 (-0500) Subject: Add support for calling an external format program inside of mhl. X-Git-Url: http://git.marmaro.de/?a=commitdiff_plain;h=3e151103c954aad7871a197b438b8d79d557e07e;p=mmh Add support for calling an external format program inside of mhl. --- diff --git a/config/config.c b/config/config.c index 82d20b1..72896c2 100644 --- a/config/config.c +++ b/config/config.c @@ -194,6 +194,13 @@ char *faceproc = NULL; char *fileproc = nmhbindir (/refile); +/* + * This program is used to optionally format the bodies of messages by + * "mhl". + */ + +char *formatproc = NULL; + /* * This program is called to incorporate messages into a folder. */ diff --git a/h/mh.h b/h/mh.h index b0a841a..1f563b9 100644 --- a/h/mh.h +++ b/h/mh.h @@ -315,6 +315,7 @@ extern char *draft; extern char *faceproc; extern char *fileproc; extern char *foldprot; +extern char *formatproc; extern char *forwcomps; extern char *inbox; extern char *incproc; diff --git a/man/mhl.man b/man/mhl.man index cc62b7c..eff230f 100644 --- a/man/mhl.man +++ b/man/mhl.man @@ -250,6 +250,10 @@ decode flag decode text as RFC-2047 encoded header field addrfield flag field contains addresses datefield flag field contains dates +format flag Run component through formatproc filter + (body only) +noformat flag Do not run component through + formatproc filter (default) .fi .RE .PP @@ -315,7 +319,24 @@ can be given a default format string for either address or date fields (but not both). To do this, on a global line specify: either the flag addrfield or datefield, along with the appropriate formatfield variable string. - +.PP +The \*(lqformat\*(rq flag specifies that this component will be run through +the filter program specified by the +.IR formatproc +profile entry. This filter program is expected to read data on standard +input and output data on standard output. Currently the \*(lqformat\*(rq +flag is only supported for the \(*lqbody\*(rq component. The component +name will be prefixed to the output +.IR after +the filter has been run. The expected use of this is to filter a message +body to create more pleasing text to use in a reply message. +A suggested filter to use for +.BR repl(1) +is as follows: +.PP +.RS 5 +body:component=">",overflowtext=">",overflowoffset=0,format,nowrap +.RE .SH FILES .fc ^ ~ .nf @@ -331,6 +352,8 @@ appropriate formatfield variable string. .ta 2.4i .ta \w'ExtraBigProfileName 'u ^moreproc:~^Program to use as interactive front\-end +^formatproc:~^Program to use as a filter for components that +^^have the \*(lqformat\*(rq flag set. .fi .SH "SEE ALSO" diff --git a/man/repl.man b/man/repl.man index acc1c76..f67cee7 100644 --- a/man/repl.man +++ b/man/repl.man @@ -303,6 +303,22 @@ This message filter file cites the Message-ID and author of the message being replied\-to, and then outputs each line of the body prefaced with the \*(lq>\*(rq character. .PP +You can also use an external format program to format the message body. +The format program is specified by the +.IR formatproc +profile entry, and is enabled by the \*(lqformat\(*rq flag. A message +filter using an external format program would look like this: +.PP +.RS 5 +.nf +body:component=\*(lq>\*(rq,\|nowrap,\|format +.fi +.RE +.PP +See the +.BR mhl(1) +documentation for more information. +.PP To use the MIME rules for encapsulation, specify the .B \-mime switch. diff --git a/sbr/readconfig.c b/sbr/readconfig.c index 2a68019..7f30975 100644 --- a/sbr/readconfig.c +++ b/sbr/readconfig.c @@ -22,6 +22,7 @@ static struct procstr procs[] = { { "buildmimeproc", &buildmimeproc }, { "faceproc", &faceproc }, { "fileproc", &fileproc }, + { "formatproc", &formatproc }, { "incproc", &incproc }, { "installproc", &installproc }, { "lproc", &lproc }, diff --git a/uip/mhlsbr.c b/uip/mhlsbr.c index 5f355ab..1446c2a 100644 --- a/uip/mhlsbr.c +++ b/uip/mhlsbr.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * MAJOR BUG: @@ -110,7 +111,8 @@ static struct swit mhlswitches[] = { #define SPLIT 0x010000 /* split headers (don't concatenate) */ #define NONEWLINE 0x020000 /* don't write trailing newline */ #define NOWRAP 0x040000 /* Don't wrap lines ever */ -#define LBITS "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017FACEFMT\020FACEDFLT\021SPLIT\022NONEWLINE\023NOWRAP" +#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 { @@ -195,6 +197,8 @@ static struct triple triples[] = { { "nonewline", NONEWLINE, 0 }, { "wrap", 0, NOWRAP }, { "nowrap", NOWRAP, 0 }, + { "format", FMTFILTER, 0 }, + { "noformat", 0, FMTFILTER }, { NULL, 0, 0 } }; @@ -282,6 +286,7 @@ static int doface (struct mcomp *); 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 *(*)()); @@ -957,15 +962,20 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec) 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) @@ -1813,3 +1823,166 @@ m_pclose (void) 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)) > 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]); +} diff --git a/uip/mhparam.c b/uip/mhparam.c index 8384544..1b35518 100644 --- a/uip/mhparam.c +++ b/uip/mhparam.c @@ -46,6 +46,7 @@ static struct proc procs [] = { { "faceproc", &faceproc }, { "fileproc", &fileproc }, { "foldprot", &foldprot }, + { "formatproc", &formatproc }, { "incproc", &incproc }, { "installproc", &installproc }, { "lproc", &lproc },