From: Ken Hornstein Date: Thu, 3 Apr 2014 22:12:56 +0000 (-0400) Subject: Moving closer to fixing mhshow completely. Includes fix from David Levine X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/eaf54b62d94b8a2ab4a82ed30ab3c08214f51571?ds=inline Moving closer to fixing mhshow completely. Includes fix from David Levine for a slight bug in charset conversion. --- diff --git a/Makefile.am b/Makefile.am index f0670b31..71ab0e08 100644 --- a/Makefile.am +++ b/Makefile.am @@ -575,7 +575,7 @@ sbr_libmh_a_SOURCES = sbr/addrsbr.c sbr/ambigsw.c sbr/atooi.c sbr/arglist.c \ sbr/fmt_rfc2047.c sbr/fmt_scan.c sbr/lock_file.c \ sbr/m_atoi.c sbr/m_backup.c sbr/m_convert.c \ sbr/m_draft.c sbr/m_getfld.c sbr/m_gmprot.c \ - sbr/m_maildir.c sbr/m_name.c sbr/m_rand.c \ + sbr/m_maildir.c sbr/m_name.c sbr/m_popen.c sbr/m_rand.c \ sbr/makedir.c sbr/message_id.c sbr/mime_type.c sbr/mts.c \ sbr/norm_charmap.c sbr/path.c \ sbr/peekc.c sbr/pidwait.c sbr/pidstatus.c \ diff --git a/h/mhparse.h b/h/mhparse.h index cfa1e53f..c2ba18a2 100644 --- a/h/mhparse.h +++ b/h/mhparse.h @@ -343,11 +343,21 @@ const struct str2init *get_ce_method (const char *); char *content_charset (CT); int convert_charset (CT, char *, int *); +/* + * Given a content structure, return true if the content has a disposition + * of "inline". + * + * Arguments are: + * + * ct - Content structure to examine + */ +int is_inline(CT ct); + /* * Given a list of messages, display information about them on standard * output. * - * Argumens are: + * Arguments are: * * cts - An array of CT elements of messages that need to be * displayed. Array is terminated by a NULL. diff --git a/h/prototypes.h b/h/prototypes.h index f8adbac5..7a4f8a38 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -207,6 +207,23 @@ char *m_mktemp(const char *, int *, FILE **); char *m_mktemp2(const char *, const char *, int *, FILE **); char *m_mktemps(const char *pfx, const char *suffix, int *, FILE **); char *get_temp_dir(); + +/* + * Create a subprocess and redirect our standard output to it. + * + * Arguments are: + * + * name - Name of process to create + * savestdout - If true, will save the current stdout file descriptor and + * m_pclose() will close it at the appropriate time. + */ +void m_popen(char *name, int savestdout); + +/* + * Wait for the last process opened by m_popen(). + */ +void m_pclose(void); + void m_unknown(m_getfld_state_t *, FILE *); int makedir (char *); char *message_id (time_t, int); diff --git a/sbr/m_popen.c b/sbr/m_popen.c new file mode 100644 index 00000000..8a3bae64 --- /dev/null +++ b/sbr/m_popen.c @@ -0,0 +1,83 @@ + +/* + * m_popen.c -- Interface for a popen() call that redirects the current + * process standard output to the popen()d process. + * + * This code is Copyright (c) 2014, by the authors of nmh. See the + * COPYRIGHT file in the root directory of the nmh distribution for + * complete copyright information. + */ + +#include +#include + +static int m_pid = NOTOK; /* Process we're waiting for */ +static int sd = NOTOK; /* Original standard output */ + +/* + * Fork a process and redirect our standard output to that process + */ + +void +m_popen (char *name, int savestdout) +{ + int pd[2]; + char *file; + char **arglist; + + if (savestdout && (sd = dup (fileno (stdout))) == NOTOK) + adios ("standard output", "unable to dup()"); + + if (pipe (pd) == NOTOK) + adios ("pipe", "unable to"); + + switch (m_pid = fork()) { + case NOTOK: + adios ("fork", "unable to"); + + case OK: + SIGNAL (SIGINT, SIG_DFL); + SIGNAL (SIGQUIT, SIG_DFL); + + close (pd[1]); + if (pd[0] != fileno (stdin)) { + dup2 (pd[0], fileno (stdin)); + close (pd[0]); + } + arglist = argsplit(name, &file, NULL); + execvp (file, arglist); + fprintf (stderr, "unable to exec "); + perror (name); + _exit (-1); + + default: + close (pd[0]); + if (pd[1] != fileno (stdout)) { + dup2 (pd[1], fileno (stdout)); + close (pd[1]); + } + } +} + + +void +m_pclose (void) +{ + if (m_pid == NOTOK) + return; + + if (sd != NOTOK) { + fflush (stdout); + if (dup2 (sd, fileno (stdout)) == NOTOK) + adios ("standard output", "unable to dup2()"); + + clearerr (stdout); + close (sd); + sd = NOTOK; + } + else + fclose (stdout); + + pidwait (m_pid, OK); + m_pid = NOTOK; +} diff --git a/test/mhshow/test-charset b/test/mhshow/test-charset index 974442c6..0f3a53ca 100755 --- a/test/mhshow/test-charset +++ b/test/mhshow/test-charset @@ -46,7 +46,7 @@ Subject: test MIME-Version: 1.0 -part text/plain 10 +part text/plain 11 This is a test EOF diff --git a/test/mhshow/test-textcharset b/test/mhshow/test-textcharset index 9fad5d8d..5fc07e6e 100755 --- a/test/mhshow/test-textcharset +++ b/test/mhshow/test-textcharset @@ -47,7 +47,7 @@ Subject: test display with charset conversion MIME-Version: 1.0 -part text/plain 10 +part text/plain 11 4 ÷ 2 = 2 EOF diff --git a/uip/mhlsbr.c b/uip/mhlsbr.c index 3ebfa69e..e9d4de8e 100644 --- a/uip/mhlsbr.c +++ b/uip/mhlsbr.c @@ -332,7 +332,6 @@ 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 *, m_getfld_state_t); static void compile_formatfield(struct mcomp *); @@ -482,7 +481,7 @@ mhl (int argc, char **argv) SIGNAL2 (SIGQUIT, quitser); } SIGNAL2 (SIGPIPE, pipeser); - m_popen (moreproc); + m_popen (moreproc, mhl_action != NULL); ontty = PITTY; } else { SIGNAL (SIGINT, SIG_IGN); @@ -1644,74 +1643,6 @@ mhldone (int status) } -static int m_pid = NOTOK; -static int sd = NOTOK; - -static void -m_popen (char *name) -{ - int pd[2]; - char *file; - char **arglist; - - if (mhl_action && (sd = dup (fileno (stdout))) == NOTOK) - adios ("standard output", "unable to dup()"); - - if (pipe (pd) == NOTOK) - adios ("pipe", "unable to"); - - switch (m_pid = fork()) { - case NOTOK: - adios ("fork", "unable to"); - - case OK: - SIGNAL (SIGINT, SIG_DFL); - SIGNAL (SIGQUIT, SIG_DFL); - - close (pd[1]); - if (pd[0] != fileno (stdin)) { - dup2 (pd[0], fileno (stdin)); - close (pd[0]); - } - arglist = argsplit(name, &file, NULL); - execvp (file, arglist); - fprintf (stderr, "unable to exec "); - perror (name); - _exit (-1); - - default: - close (pd[0]); - if (pd[1] != fileno (stdout)) { - dup2 (pd[1], fileno (stdout)); - close (pd[1]); - } - } -} - - -void -m_pclose (void) -{ - if (m_pid == NOTOK) - return; - - if (sd != NOTOK) { - fflush (stdout); - if (dup2 (sd, fileno (stdout)) == NOTOK) - adios ("standard output", "unable to dup2()"); - - clearerr (stdout); - close (sd); - sd = NOTOK; - } - else - fclose (stdout); - - pidwait (m_pid, OK); - m_pid = NOTOK; -} - - /* * Compile a format string used by the formatfield option and save it * for later. diff --git a/uip/mhmisc.c b/uip/mhmisc.c index 7d320239..f4b89168 100644 --- a/uip/mhmisc.c +++ b/uip/mhmisc.c @@ -75,6 +75,29 @@ type_ok (CT ct, int sP) } +/* + * Returns true if this content is marked as "inline". + * + * Technically we should check parent content to see if they have + * disposition to use as a default, but we don't right now. Maybe + * later .... + */ + +int +is_inline(CT ct) +{ + /* + * If there isn't any disposition at all, it's "inline". Obviously + * if it's "inline", then it's inline. RFC 2183 says if it's an unknown + * disposition, treat it as 'attachment'. + */ + + if (! ct->c_dispo_type || strcasecmp(ct->c_dispo_type, "inline") == 0) + return 1; + else + return 0; +} + int make_intermediates (char *file) { diff --git a/uip/mhshow.c b/uip/mhshow.c index edf3f1e3..ab589551 100644 --- a/uip/mhshow.c +++ b/uip/mhshow.c @@ -382,6 +382,9 @@ do_cache: context_save (); /* save the context file */ } + if (concat) + m_popen(moreproc, 0); + /* * Show the message content */ @@ -394,6 +397,9 @@ do_cache: free ((char *) cts); cts = NULL; + if (concat) + m_pclose(); + done (0); return 1; } diff --git a/uip/mhshowsbr.c b/uip/mhshowsbr.c index c418b21d..9ca34192 100644 --- a/uip/mhshowsbr.c +++ b/uip/mhshowsbr.c @@ -51,7 +51,7 @@ static void DisplayMsgHeader (CT, char *, int); static int show_switch (CT, int, int, int, int); static int show_content (CT, int); static int show_content_aux2 (CT, int, char *, char *, int, int, int); -static int show_text (CT, int); +static int show_text (CT, int, int); static int show_multi (CT, int, int, int, int); static int show_multi_internal (CT, int, int, int, int); static int show_multi_aux (CT, int, char *); @@ -137,7 +137,7 @@ show_single_message (CT ct, char *form, int concat, int textonly, sigaddset (&set, SIGTERM); sigprocmask (SIG_BLOCK, &set, &oset); - while (wait (&status) != NOTOK) { + while (!concat && wait (&status) != NOTOK) { pidcheck (status); continue; } @@ -232,7 +232,7 @@ show_switch (CT ct, int alternate, int concat, int textonly, int inlineonly) } case CT_TEXT: - return show_text (ct, alternate); + return show_text (ct, alternate, concat); case CT_AUDIO: case CT_IMAGE: @@ -310,7 +310,11 @@ show_content_aux (CT ct, int alternate, char *cp, char *cracked) unfortunately the type checks are necessary without some code rearrangement. And to make this really ugly, only do it in mhshow, not mhfixmsg, mhn, or mhstore. */ - if (convert_content_charset (ct, &file) != OK) { + if (convert_content_charset (ct, &file) == OK) { + close_encoding (ct); + if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK) + return NOTOK; + } else { admonish (NULL, "unable to convert character set%s to %s", ct->c_partno ? "of part " : "", ct->c_partno ? ct->c_partno : "", @@ -367,6 +371,33 @@ show_content_aux2 (CT ct, int alternate, char *cracked, char *buffer, list_switch (ct, -1, 1, 0, 0, 0); } + /* + * If the command is a zero-length string, just write the output on + * stdout. + */ + + if (buffer[0] == '\0') { + char readbuf[BUFSIZ]; + ssize_t cc; + + if (fd == NOTOK) { + advise(NULL, "Cannot use NULL command to display content-type " + "%s/%s", ct->c_ctinfo.ci_type, ct->c_ctinfo.ci_subtype); + return NOTOK; + } + + while ((cc = read(fd, readbuf, sizeof(readbuf))) > 0) { + fwrite(readbuf, sizeof(char), cc, stdout); + } + + if (cc < 0) { + advise("read", "while reading text content"); + return NOTOK; + } + + return OK; + } + vec = argsplit(buffer, &file, &vecp); vec[vecp++] = NULL; @@ -409,7 +440,7 @@ show_content_aux2 (CT ct, int alternate, char *cracked, char *buffer, */ static int -show_text (CT ct, int alternate) +show_text (CT ct, int alternate, int concat) { char *cp, buffer[BUFSIZ]; CI ci = &ct->c_ctinfo; @@ -430,8 +461,11 @@ show_text (CT ct, int alternate) * if it is not a text part of a multipart/alternative */ if (!alternate || ct->c_subtype == TEXT_PLAIN) { - snprintf (buffer, sizeof(buffer), "%%l%s %%F", progsw ? progsw : - moreproc && *moreproc ? moreproc : DEFAULT_PAGER); + if (concat) + snprintf(buffer, sizeof(buffer), "%%l"); + else + snprintf (buffer, sizeof(buffer), "%%l%s %%F", progsw ? progsw : + moreproc && *moreproc ? moreproc : DEFAULT_PAGER); cp = (ct->c_showproc = add (buffer, NULL)); return show_content_aux (ct, alternate, cp, NULL); } @@ -505,7 +539,8 @@ show_multi_internal (CT ct, int alternate, int concat, int textonly, for (part = m->mp_parts; part; part = part->mp_next) { p = part->mp_part; - if (part_ok (p, 1) && type_ok (p, 1)) { + if (part_ok (p, 1) && type_ok (p, 1) && + (!inlineonly || is_inline(ct))) { int inneresult; inneresult = show_switch (p, nowalternate, concat, textonly, @@ -660,7 +695,7 @@ show_external (CT ct, int alternate, int concat, int textonly, int inlineonly) struct exbody *e = (struct exbody *) ct->c_ctparams; CT p = e->eb_content; - if (!type_ok (p, 0)) + if (!type_ok (p, 0) || (inlineonly && !is_inline(p))) return OK; return show_switch (p, alternate, concat, textonly, inlineonly);