From: David Levine Date: Thu, 10 Apr 2014 00:19:26 +0000 (-0500) Subject: Merge remote-tracking branch 'origin/fix-locking' X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/19bf8698eeb0ab3d7694232af115fa3f007d5c7b?hp=d6e398f9c1aa774ecc336877ac1c465b8f9b942b Merge remote-tracking branch 'origin/fix-locking' --- diff --git a/Makefile.am b/Makefile.am index f0670b31..91a915fd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -188,8 +188,9 @@ noinst_HEADERS = h/addrsbr.h h/aliasbr.h h/crawl_folders.h h/dropsbr.h \ dist_sysconf_DATA = etc/MailAliases etc/components etc/digestcomps \ etc/distcomps etc/forwcomps etc/mhl.body etc/mhl.digest \ etc/mhl.format etc/mhl.forward etc/mhl.headers \ - etc/mhl.reply etc/rcvdistcomps etc/rcvdistcomps.outbox \ - etc/replcomps etc/replgroupcomps etc/scan.MMDDYY \ + etc/mhl.reply etc/mhshow.marker etc/rcvdistcomps \ + etc/rcvdistcomps.outbox etc/replcomps etc/replgroupcomps \ + etc/scan.MMDDYY \ etc/scan.YYYYMMDD etc/scan.curses etc/scan.default \ etc/scan.highlighted \ etc/scan.mailx etc/scan.nomime etc/scan.size etc/scan.time \ @@ -575,7 +576,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/docs/pending-release-notes b/docs/pending-release-notes index 8c02d845..22385f4a 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -81,6 +81,9 @@ NEW FEATURES - The MIME parsing and generating routines now support RFC 2231 extended parameter information. - mh-mime(7) now provides an introduction to nmh's MIME handling. +- mhshow(1) will now by default display all text content under one pager, + and display markers for non-text and non-inline content. The content + markers are changeable via mh-format(5). ----------------- OBSOLETE FEATURES diff --git a/etc/mhshow.marker b/etc/mhshow.marker new file mode 100644 index 00000000..cc9ec85e --- /dev/null +++ b/etc/mhshow.marker @@ -0,0 +1,6 @@ +%; +%; This is provided as reference only; it shows the default content marker +%; used by mhshow when it decides to not display content. +%; +[ part %{part} - %{content-type} - %<{description}\ +%{description}%?{cdispo-filename}%{cdispo-filename}%|%{ctype-name}%> ] diff --git a/h/fmt_scan.h b/h/fmt_scan.h index 4d768b7b..d50dc441 100644 --- a/h/fmt_scan.h +++ b/h/fmt_scan.h @@ -267,6 +267,25 @@ int fmt_addcomptext(char *component, char *text); void fmt_appendcomp(int bucket, char *component, char *text); +/* + * Iterate over the complete hash table of component structures. + * + * Arguments are: + * + * comp - Pointer to the current component structure. The next + * component in the hash table after this component. To + * start (or restart) the iteration of the hash table + * this argument should be NULL. + * bucket - Pointer to hash bucket. Will be managed by this function, + * the caller should not modify this value. + * + * Returns the next component in the hash table. This value should be + * passed into the next call to fmt_nextcomp(). Returns NULL at the end + * of the hash table. + */ + +struct comp *fmt_nextcomp(struct comp *comp, unsigned int *bucket); + /* * The implementation of the %(formataddr) function. This is available for * programs to provide their own local implementation if they wish to do diff --git a/h/mhparse.h b/h/mhparse.h index 700c0cc3..d165a63b 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. @@ -472,4 +482,23 @@ char *get_param(PM first, const char *name, char replace, int fetchonly); */ char *get_param_value(PM pm, char replace); +/* + * Display MIME message(s) on standard out. + * + * Arguments are: + * + * cts - NULL terminated array of CT structures for messages + * to display + * concat - If true, concatenate all MIME parts. If false, show each + * MIME part under a separate pager. + * textonly - If true, only display "text" MIME parts + * inlineonly - If true, only display MIME parts that are marked with + * a disposition of "inline" (includes parts that lack a + * Content-Disposition header). + * markerform - The name of a file containg mh-format(5) code used to + * display markers about non-displayed MIME parts. + */ +void show_all_messages(CT *cts, int concat, int textonly, int inlineonly, + char *markerform); + extern int checksw; /* Add Content-MD5 field */ diff --git a/h/prototypes.h b/h/prototypes.h index 0edcf411..da5231ba 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -208,6 +208,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/man/mhshow.man b/man/mhshow.man index b6fe8838..fd696e08 100644 --- a/man/mhshow.man +++ b/man/mhshow.man @@ -1,4 +1,4 @@ -.TH MHSHOW %manext1% "March 16, 2014" "%nmhversion%" +.TH MHSHOW %manext1% "April 9, 2014" "%nmhversion%" .\" .\" %nmhwarning% .\" @@ -18,8 +18,13 @@ mhshow \- display MIME messages .RB [ \-type .IR content ] \&... +.RB [ \-concat " | " \-noconcat ] +.RB [ \-textonly " | " \-notextonly ] +.RB [ \-inlineonly " | " \-noinlineonly ] .RB [ \-form .IR formfile ] +.RB [ \-markform +.IR formfile ] .RB [ \-rcache .IR policy ] .RB [ \-wcache @@ -44,16 +49,34 @@ message headers as specified in RFC 2047. .PP By default .B mhshow -will display all parts of a multipart -message. By using the +will display only text parts of a message that are not marked as attachments. +This behavior can be changed by the +.B \-notextonly +and +.B \-noinlineonly +switches. +In addition, by using the .B \-part and .B \-type switches, you may -limit the scope of +further limit the scope of .B mhshow to particular subparts (of a -multipart content) and/or particular content types. +multipart content) and/or particular content types. The inclusion of any +.B \-part +or +.B \-type +switches will override the default settings of +.B \-textonly +and +.BR \-inlineonly. +.PP +By default +.B mhshow +will concatenate all content under one pager. If you which each part to +displayed separately, you can override the default behavior with +.B \-noconcat. .PP The option .B \-file @@ -571,6 +594,45 @@ e.g., which is created automatically during .B nmh installation. +.PP +SS Content\-Type Marker +If +.B mhshow +decides to not display a particular part due to the switches of +.B \-textonly +or +.B \-inlineonly +it will display a marker containing information about the part. This +marker is processed via +.IR mh\-format (5) +and can be changed by the use of the +.B \-markform +switch to specify a file containing the +.IR mh\-format (5) +instructions to use when displaying the content marker. In addition to +the normal set of +.IR mh\-format (5) +instructions, the following +.I component +escapes are supported: +.PP +.RS 5 +.nf +.ta \w'cdispo- 'u +\w'Returns 'u +.I "Escape Returns Description" +part string MIME part number +content\-type string MIME Content\-Type of part +description string Content\-Description header +disposition string Content disposition (attachment or inline) +ctype- string Value of from Content\-Type header +cdispo- string Value of from + Content\-Disposition header +.fi +.RE +All MIME parameters and the \*(lqContent-Description\*(rq header will have +RFC 2231 decoding applied and be converted +to the local character set. +.PP .SH FILES .B mhshow looks for all format files and mhn.defaults in multiple locations: @@ -588,6 +650,7 @@ is checked. ^$MHSHOW~^Additional profile entries ^%etcdir%/mhn.defaults~^System default MIME profile entries ^%etcdir%/mhl.headers~^The headers template +^%etcdir%/mhshow.marker~^Example content marker .fi .SH "PROFILE COMPONENTS" .fc ^ ~ @@ -619,6 +682,9 @@ is checked. .RB ` +folder "' defaults to the current folder" .RB ` msgs "' defaults to cur" .RB ` \-nocheck ' +.RB ` \-concat ' +.RB ` \-textonly ' +.RB ` \-inlineonly ' .RB ` \-form\ mhl.headers ' .RB ` \-rcache\ ask ' .RB ` \-wcache\ ask ' diff --git a/sbr/fmt_compile.c b/sbr/fmt_compile.c index 970e71e5..3f170f31 100644 --- a/sbr/fmt_compile.c +++ b/sbr/fmt_compile.c @@ -1087,6 +1087,25 @@ fmt_appendcomp(int bucket, char *component, char *text) } } +/* + * Iterate over our component hash table + */ + +struct comp * +fmt_nextcomp(struct comp *comp, unsigned int *bucket) +{ + if (comp == NULL) + *bucket = 0; + else + comp = comp->c_next; + + while (comp == NULL && *bucket < sizeof(wantcomp)/sizeof(wantcomp[0])) { + comp = wantcomp[(*bucket)++]; + } + + return comp; +} + /* * Free and reset our component hash table */ 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..efcab90c 100755 --- a/test/mhshow/test-charset +++ b/test/mhshow/test-charset @@ -18,6 +18,8 @@ setup_test LC_ALL=en_US.UTF-8; export LC_ALL +test "$ICONV_ENABLED" -eq 0 && text_size=10 || text_size=11 + expected=$MH_TEST_DIR/$$.expected actual=$MH_TEST_DIR/$$.actual @@ -46,7 +48,7 @@ Subject: test MIME-Version: 1.0 -part text/plain 10 +part text/plain $text_size 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/mhn.c b/uip/mhn.c index 3fd2d39c..7363688f 100644 --- a/uip/mhn.c +++ b/uip/mhn.c @@ -119,9 +119,6 @@ int part_ok (CT, int); int type_ok (CT, int); void flush_errors (void); -/* mhshowsbr.c */ -void show_all_messages (CT *); - /* mhstoresbr.c */ typedef struct mhstoreinfo *mhstoreinfo_t; mhstoreinfo_t mhstoreinfo_create(CT *, char *, const char *, int, int); @@ -563,7 +560,7 @@ do_cache: * Show the message content */ if (showsw) - show_all_messages (cts); + show_all_messages (cts, 0, 0, 0, NULL); /* Now free all the structures for the content */ for (ctp = cts; *ctp; ctp++) diff --git a/uip/mhshow.c b/uip/mhshow.c index e897f2b6..96b99b79 100644 --- a/uip/mhshow.c +++ b/uip/mhshow.c @@ -23,8 +23,15 @@ X("nocheck", 0, NCHECKSW) \ X("verbose", 0, VERBSW) \ X("noverbose", 0, NVERBSW) \ + X("concat", 0, CONCATSW) \ + X("noconcat", 0, NCONCATSW) \ + X("textonly", 0, TEXTONLYSW) \ + X("notextonly", 0, NTEXTONLYSW) \ + X("inlineonly", 0, INLINESW) \ + X("noinlineonly", 0, NINLINESW) \ X("file file", 0, FILESW) \ X("form formfile", 0, FORMSW) \ + X("markform formfile", 0, MARKFORMSW) \ X("part number", 0, PARTSW) \ X("type content", 0, TYPESW) \ X("rcache policy", 0, RCACHESW) \ @@ -83,9 +90,6 @@ int part_ok (CT, int); int type_ok (CT, int); void flush_errors (void); -/* mhshowsbr.c */ -void show_all_messages (CT *); - /* mhfree.c */ extern CT *cts; void freects_done (int) NORETURN; @@ -99,8 +103,8 @@ static void pipeser (int); int main (int argc, char **argv) { - int msgnum, *icachesw; - char *cp, *file = NULL, *folder = NULL; + int msgnum, *icachesw, concatsw = -1, textonly = -1, inlineonly = -1; + char *cp, *file = NULL, *folder = NULL, *markform = NULL; char *maildir, buf[100], **argp; char **arguments; struct msgs_array msgs = { 0, 0, NULL }; @@ -162,6 +166,25 @@ do_cache: checksw = 0; continue; + case CONCATSW: + concatsw = 1; + continue; + case NCONCATSW: + concatsw = 0; + continue; + case TEXTONLYSW: + textonly = 1; + continue; + case NTEXTONLYSW: + textonly = 0; + continue; + case INLINESW: + inlineonly = 1; + continue; + case NINLINESW: + inlineonly = 1; + continue; + case PARTSW: if (!(cp = *argp++) || *cp == '-') adios (NULL, "missing argument to %s", argp[-2]); @@ -194,6 +217,11 @@ do_cache: formsw = getcpy (etcpath (cp)); continue; + case MARKFORMSW: + if (!(markform = *argp++) || *markform == '-') + adios (NULL, "missing argument to %s", argp[-2]); + continue; + /* * Switches for moreproc/mhlproc */ @@ -235,6 +263,18 @@ do_cache: parts[npart] = NULL; types[ntype] = NULL; + /* + * If we had any specific parts or types specified, turn off text only + * content. + */ + + if (npart > 0 || ntype > 0) { + if (textonly == -1) + textonly = 0; + if (inlineonly == -1) + inlineonly = 0; + } + /* * Check if we've specified an additional profile */ @@ -366,10 +406,13 @@ do_cache: context_save (); /* save the context file */ } + if (concatsw) + m_popen(moreproc, 0); + /* * Show the message content */ - show_all_messages (cts); + show_all_messages (cts, concatsw, textonly, inlineonly, markform); /* Now free all the structures for the content */ for (ctp = cts; *ctp; ctp++) @@ -378,6 +421,9 @@ do_cache: free ((char *) cts); cts = NULL; + if (concatsw) + m_pclose(); + done (0); return 1; } diff --git a/uip/mhshowsbr.c b/uip/mhshowsbr.c index c53f22f3..3421f327 100644 --- a/uip/mhshowsbr.c +++ b/uip/mhshowsbr.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #ifdef HAVE_ICONV # include @@ -41,38 +42,61 @@ void flush_errors (void); /* * prototypes */ -void show_all_messages (CT *); int show_content_aux (CT, int, char *, char *); /* * static prototypes */ -static void show_single_message (CT, char *); -static void DisplayMsgHeader (CT, char *); -static int show_switch (CT, int); -static int show_content (CT, int); +static void show_single_message (CT, char *, int, int, int, struct format *); +static void DisplayMsgHeader (CT, char *, int); +static int show_switch (CT, int, int, int, int, struct format *); +static int show_content (CT, int, int, int, struct format *fmt); static int show_content_aux2 (CT, int, char *, char *, int, int, int); -static int show_text (CT, int); -static int show_multi (CT, int); -static int show_multi_internal (CT, int); +static int show_text (CT, int, int); +static int show_multi (CT, int, int, int, int, struct format *); +static int show_multi_internal (CT, int, int, int, int, struct format *fmt); static int show_multi_aux (CT, int, char *); static int show_message_rfc822 (CT, int); static int show_partial (CT, int); -static int show_external (CT, int); +static int show_external (CT, int, int, int, int, struct format *); static int parse_display_string (CT, char *, int *, int *, char *, char *, size_t, int multipart); static int convert_content_charset (CT, char **); +static struct format *compile_marker(char *); +static void output_marker (CT, struct format *); +static void free_markercomps (void); static int pidcheck(int); +/* + * Components (and list of parameters/components) we care about for the + * content marker display. + */ + +static struct comp *part_comp = NULL; +static struct comp *ctype_comp = NULL; +static struct comp *description_comp = NULL; +static struct comp *dispo_comp = NULL; + +struct param_comp_list { + char *param; + struct comp *comp; + struct param_comp_list *next; +}; + +static struct param_comp_list *ctype_pc_list = NULL; +static struct param_comp_list *dispo_pc_list = NULL; + /* * Top level entry point to show/display a group of messages */ void -show_all_messages (CT *cts) +show_all_messages (CT *cts, int concatsw, int textonly, int inlineonly, + char *markerform) { CT ct, *ctp; + struct format *fmt; /* * If form is not specified, then get default form @@ -81,6 +105,11 @@ show_all_messages (CT *cts) if (!formsw) formsw = getcpy (etcpath ("mhl.headers")); + /* + * Compile the content marker format line + */ + fmt = compile_marker(markerform); + /* * If form is "mhl.null", suppress display of header. */ @@ -92,8 +121,12 @@ show_all_messages (CT *cts) /* if top-level type is ok, then display message */ if (type_ok (ct, 1)) - show_single_message (ct, formsw); + show_single_message (ct, formsw, concatsw, textonly, inlineonly, + fmt); } + + free_markercomps(); + fmt_free(fmt, 1); } @@ -102,7 +135,8 @@ show_all_messages (CT *cts) */ static void -show_single_message (CT ct, char *form) +show_single_message (CT ct, char *form, int concatsw, int textonly, + int inlineonly, struct format *fmt) { sigset_t set, oset; @@ -117,10 +151,10 @@ show_single_message (CT ct, char *form) * the message headers. */ if (form) - DisplayMsgHeader(ct, form); + DisplayMsgHeader(ct, form, concatsw); /* Show the body of the message */ - show_switch (ct, 0); + show_switch (ct, 0, concatsw, textonly, inlineonly, fmt); if (ct->c_fp) { fclose (ct->c_fp); @@ -137,7 +171,7 @@ show_single_message (CT ct, char *form) sigaddset (&set, SIGTERM); sigprocmask (SIG_BLOCK, &set, &oset); - while (wait (&status) != NOTOK) { + while (!concatsw && wait (&status) != NOTOK) { pidcheck (status); continue; } @@ -154,7 +188,7 @@ show_single_message (CT ct, char *form) */ static void -DisplayMsgHeader (CT ct, char *form) +DisplayMsgHeader (CT ct, char *form, int concatsw) { pid_t child_id; int i, vecp; @@ -171,7 +205,7 @@ DisplayMsgHeader (CT ct, char *form) * If we've specified -(no)moreproc, * then just pass that along. */ - if (nomore) { + if (nomore || concatsw) { vec[vecp++] = getcpy("-nomoreproc"); } else if (progsw) { vec[vecp++] = getcpy("-moreproc"); @@ -211,11 +245,13 @@ DisplayMsgHeader (CT ct, char *form) */ static int -show_switch (CT ct, int alternate) +show_switch (CT ct, int alternate, int concatsw, int textonly, int inlineonly, + struct format *fmt) { switch (ct->c_type) { case CT_MULTIPART: - return show_multi (ct, alternate); + return show_multi (ct, alternate, concatsw, textonly, + inlineonly, fmt); case CT_MESSAGE: switch (ct->c_subtype) { @@ -223,7 +259,8 @@ show_switch (CT ct, int alternate) return show_partial (ct, alternate); case MESSAGE_EXTERNAL: - return show_external (ct, alternate); + return show_external (ct, alternate, concatsw, textonly, + inlineonly, fmt); case MESSAGE_RFC822: default: @@ -231,13 +268,13 @@ show_switch (CT ct, int alternate) } case CT_TEXT: - return show_text (ct, alternate); + return show_text (ct, alternate, concatsw); case CT_AUDIO: case CT_IMAGE: case CT_VIDEO: case CT_APPLICATION: - return show_content (ct, alternate); + return show_content (ct, alternate, textonly, inlineonly, fmt); default: adios (NULL, "unknown content type %d", ct->c_type); @@ -252,11 +289,22 @@ show_switch (CT ct, int alternate) */ static int -show_content (CT ct, int alternate) +show_content (CT ct, int alternate, int textonly, int inlineonly, + struct format *fmt) { char *cp, buffer[BUFSIZ]; CI ci = &ct->c_ctinfo; + /* + * If we're here, we are not a text type. So we don't need to check + * the content-type. + */ + + if (textonly || (inlineonly && is_inline(ct))) { + output_marker(ct, fmt); + return OK; + } + /* Check for invo_name-show-type/subtype */ snprintf (buffer, sizeof(buffer), "%s-show-%s/%s", invo_name, ci->ci_type, ci->ci_subtype); @@ -309,7 +357,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) { + (*ct->c_ceclosefnx) (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 : "", @@ -366,6 +418,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; @@ -408,7 +487,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 concatsw) { char *cp, buffer[BUFSIZ]; CI ci = &ct->c_ctinfo; @@ -429,8 +508,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 (concatsw) + 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); } @@ -444,7 +526,8 @@ show_text (CT ct, int alternate) */ static int -show_multi (CT ct, int alternate) +show_multi (CT ct, int alternate, int concatsw, int textonly, int inlineonly, + struct format *fmt) { char *cp, buffer[BUFSIZ]; CI ci = &ct->c_ctinfo; @@ -468,7 +551,8 @@ show_multi (CT ct, int alternate) * unknown types are displayable, since they're treated as mixed * per RFC 2046. */ - return show_multi_internal (ct, alternate); + return show_multi_internal (ct, alternate, concatsw, textonly, + inlineonly, fmt); } @@ -478,7 +562,8 @@ show_multi (CT ct, int alternate) */ static int -show_multi_internal (CT ct, int alternate) +show_multi_internal (CT ct, int alternate, int concatsw, int textonly, + int inlineonly, struct format *fmt) { int alternating, nowalternate, result; struct multipart *m = (struct multipart *) ct->c_ctparams; @@ -506,7 +591,8 @@ show_multi_internal (CT ct, int alternate) if (part_ok (p, 1) && type_ok (p, 1)) { int inneresult; - inneresult = show_switch (p, nowalternate); + inneresult = show_switch (p, nowalternate, concatsw, textonly, + inlineonly, fmt); switch (inneresult) { case NOTOK: if (alternate && !alternating) { @@ -652,7 +738,8 @@ show_partial (CT ct, int alternate) */ static int -show_external (CT ct, int alternate) +show_external (CT ct, int alternate, int concatsw, int textonly, int inlineonly, + struct format *fmt) { struct exbody *e = (struct exbody *) ct->c_ctparams; CT p = e->eb_content; @@ -660,7 +747,7 @@ show_external (CT ct, int alternate) if (!type_ok (p, 0)) return OK; - return show_switch (p, alternate); + return show_switch (p, alternate, concatsw, textonly, inlineonly, fmt); } @@ -1112,6 +1199,146 @@ convert_content_charset (CT ct, char **file) { return OK; } +/* + * Compile our format string and save any parameters we care about. + */ + +#define DEFAULT_MARKER "[ part %{part} - %{content-type} - %<{description}" \ + "%{description}%?{cdispo-filename}%{cdispo-filename}" \ + "%|%{ctype-name}%> ]" + +static struct format * +compile_marker(char *markerform) +{ + struct format *fmt; + char *fmtstring; + struct comp *comp = NULL; + unsigned int bucket; + struct param_comp_list *pc_entry; + + fmtstring = new_fs(markerform, NULL, DEFAULT_MARKER); + + (void) fmt_compile(fmtstring, &fmt, 1); + free(fmtstring); + + /* + * Things we care about: + * + * part - Part name (e.g., 1.1) + * content-type - Content-Type + * description - Content-Description + * disposition - Content-Disposition (inline, attachment) + * ctype- - Content-Type parameter + * cdispo- - Content-Disposition parameter + */ + + while ((comp = fmt_nextcomp(comp, &bucket)) != NULL) { + if (strcasecmp(comp->c_name, "part") == 0) { + part_comp = comp; + } else if (strcasecmp(comp->c_name, "content-type") == 0) { + ctype_comp = comp; + } else if (strcasecmp(comp->c_name, "description") == 0) { + description_comp = comp; + } else if (strcasecmp(comp->c_name, "disposition") == 0) { + dispo_comp = comp; + } else if (strncasecmp(comp->c_name, "ctype-", 6) == 0 && + strlen(comp->c_name) > 6) { + pc_entry = mh_xmalloc(sizeof(*pc_entry)); + pc_entry->param = getcpy(comp->c_name + 6); + pc_entry->comp = comp; + pc_entry->next = ctype_pc_list; + ctype_pc_list = pc_entry; + } else if (strncasecmp(comp->c_name, "cdispo-", 7) == 0 && + strlen(comp->c_name) > 7) { + pc_entry = mh_xmalloc(sizeof(*pc_entry)); + pc_entry->param = getcpy(comp->c_name + 7); + pc_entry->comp = comp; + pc_entry->next = dispo_pc_list; + dispo_pc_list = pc_entry; + } + } + + return fmt; +} + +/* + * Output on stdout an appropriate marker for this content, using mh-format + */ + +static void +output_marker(CT ct, struct format *fmt) +{ + char outbuf[BUFSIZ]; + struct param_comp_list *pcentry; + int dat[5]; + + /* + * Grab any items we care about. + */ + + if (ctype_comp && ct->c_ctinfo.ci_type) { + ctype_comp->c_text = concat(ct->c_ctinfo.ci_type, "/", + ct->c_ctinfo.ci_subtype, NULL); + } + + if (part_comp && ct->c_partno) { + part_comp->c_text = getcpy(ct->c_partno); + } + + if (description_comp && ct->c_descr) { + description_comp->c_text = getcpy(ct->c_descr); + } + + if (dispo_comp && ct->c_dispo_type) { + dispo_comp->c_text = getcpy(ct->c_dispo_type); + } + + for (pcentry = ctype_pc_list; pcentry != NULL; pcentry = pcentry->next) { + pcentry->comp->c_text = get_param(ct->c_ctinfo.ci_first_pm, + pcentry->param, '?', 0); + } + + for (pcentry = dispo_pc_list; pcentry != NULL; pcentry = pcentry->next) { + pcentry->comp->c_text = get_param(ct->c_dispo_first, + pcentry->param, '?', 0); + } + + fmt_scan(fmt, outbuf, sizeof(outbuf), sizeof(outbuf), dat, NULL); + + fputs(outbuf, stdout); + + fmt_freecomptext(); +} + +/* + * Reset (and free) any of the saved marker text + */ + +static void +free_markercomps(void) +{ + struct param_comp_list *pc_entry, *pc2; + + part_comp = NULL; + ctype_comp = NULL; + description_comp = NULL; + dispo_comp = NULL; + + for (pc_entry = ctype_pc_list; pc_entry != NULL; ) { + free(pc_entry->param); + pc2 = pc_entry->next; + free(pc_entry); + pc_entry = pc2; + } + + for (pc_entry = dispo_pc_list; pc_entry != NULL; ) { + free(pc_entry->param); + pc2 = pc_entry->next; + free(pc_entry); + pc_entry = pc2; + } +} + /* * Exit if the display process returned with a nonzero exit code, or terminated * with a SIGQUIT signal.