From: David Levine Date: Thu, 5 Feb 2015 01:18:33 +0000 (-0600) Subject: Merge remote-tracking branch 'origin' into convertargs X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/1e0b1d40a6285c532b722f4584fa7ddde220deff?hp=397195e750b3ba046865bc2b0f72a6de6f305651 Merge remote-tracking branch 'origin' into convertargs --- diff --git a/config/config.c b/config/config.c index a5cdfd9b..5c0591ca 100644 --- a/config/config.c +++ b/config/config.c @@ -143,8 +143,9 @@ char *nmhaccessftp = "nmh-access-ftp"; /* profile entry for external url access command */ char *nmhaccessurl = "nmh-access-url"; -char *mhlibexecdir = NMHLIBEXECDIR; +char *mhbindir = NMHBINDIR; char *mhetcdir = NMHETCDIR; +char *mhlibexecdir = NMHLIBEXECDIR; /* * nmh not-so constants diff --git a/docs/pending-release-notes b/docs/pending-release-notes index efcdd072..334cfdd0 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -35,6 +35,12 @@ NEW FEATURES - Added mhical(1), to display, reply to, and cancel iCalendar (RFC 5545) event requests. - added multiply format function +- "mhparam bindir" prints the path to the directory containing the public + executables (${bindir}). +- new "-prefer" switch for mhshow (and mhlist and mhshow), to allow specifying + the preferred content types to show, if present in a multipart alternative. +- mh-format now has %(kilo) and %(kibi) functions, to allow printing + numbers as, for example, "10K", or "2.3Mi" ----------------- OBSOLETE FEATURES diff --git a/h/mhparse.h b/h/mhparse.h index c3b0b9f1..d1c94420 100644 --- a/h/mhparse.h +++ b/h/mhparse.h @@ -6,7 +6,7 @@ #define NPARTS 50 #define NTYPES 20 -#define NPARMS 10 +#define NPREFS 20 /* * Abstract type for header fields diff --git a/man/mhlist.man b/man/mhlist.man index a75e50d8..093b7a8c 100644 --- a/man/mhlist.man +++ b/man/mhlist.man @@ -18,6 +18,9 @@ mhlist \- list information about MIME messages .RB [ \-type .IR content ] \&... +.RB [ \-prefer +.IR content ] +\&... .RB [ \-headers " | " \-noheaders ] .RB [ \-realsize " | " \-norealsize ] .RB [ \-rcache @@ -152,9 +155,29 @@ switch must be used twice: once for message/external-body and once for the content externally referenced. .PP -The parts of a multipart/alternative part are listed in the reverse -order of their placement in the message. The listing therefore is -in decreasing order of preference, as defined in RFC 2046. +By default, the parts of a multipart/alternative part are listed in +the reverse order of their placement in the message. The listing +therefore is in decreasing order of preference, as defined in RFC +2046. The +.B \-prefer +switch can be used (one or more times, in order of descending +preference) to let MH know which content types from a +multipart/alternative MIME part are preferred by the user, in order to +override the default preference order. Thus, when viewed by +.BR mhlist , +the ordering of multipart/alternative parts will appear to change when +invoked with or without various +.B \-prefer +switches. +The +.B \-prefer +switch is functionally most important for +.IR mhshow , +but is also implemented in +.B mhlist +and +.B mhstore +to make common part number ordering possible across all three programs. .SS "Checking the Contents" The .B \-check diff --git a/man/mhparam.man b/man/mhparam.man index dfa5aacc..3df2a9ed 100644 --- a/man/mhparam.man +++ b/man/mhparam.man @@ -48,6 +48,7 @@ displayed and other arguments are ignored. can provide other information, such as the .B nmh version identifier, the locations of the nmh +.IR bindir , .I etcdir and .I libexecdir diff --git a/man/mhshow.man b/man/mhshow.man index 10104255..67516be5 100644 --- a/man/mhshow.man +++ b/man/mhshow.man @@ -18,6 +18,9 @@ mhshow \- display MIME messages .RB [ \-type .IR content ] \&... +.RB [ \-prefer +.IR content ] +\&... .RB [ \-concat " | " \-noconcat ] .RB [ \-textonly " | " \-notextonly ] .RB [ \-inlineonly " | " \-noinlineonly ] @@ -99,18 +102,44 @@ mail drop format to a folder of messages, see .IR inc (1)). .PP +The +.B \-part +switch can be used (one or more times) to restrict the +set of subparts that will be displayed. (Obviously with no +.B \-part +switches, all parts will be considered.) If a +.B \-part +switch specifies a specific subpart (i.e., a "leaf" in the tree of +MIME parts), then that part will always be displayed. If a +.B \-part +switch references a multipart/alternative part, then (in +the absence of a +.B \-type +switch) only the default subpart of that multipart will be displayed. +.PP A part specification consists of a series of numbers separated by dots. For example, in a multipart content containing three parts, these would be named as 1, 2, and 3, respectively. If part 2 was also a multipart content containing two parts, these would be named as 2.1 and 2.2, respectively. Note that the .B \-part -switch is effective for only +switch is effective only for messages containing a multipart content. If a message has some other kind of content, or if the part is itself another multipart content, the .B \-part switch will not prevent the content from being acted upon. .PP +The +.B \-type +switch can also be used to restrict (or, when used in conjunction with +.BR \-part , +to further restrict) the display of parts according to content type. +One or more +.B \-type +switches part will only select the first match +from a multipart/alternative, even if there is more than one +subpart that matches (one of) the given content type(s). +.PP A content specification consists of a content type and a subtype. The initial list of \*(lqstandard\*(rq content types and subtypes can be found in RFC 2046. @@ -137,12 +166,50 @@ A legal MIME message must contain a subtype specification. To specify a content, regardless of its subtype, just use the name of the content, e.g., \*(lqaudio\*(rq. To specify a specific subtype, separate the two with a slash, e.g., \*(lqaudio/basic\*(rq. -Note that regardless of the values given to the `\-type' switch, a +Note that regardless of the values given to the +.B \-type +switch, a multipart content (of any subtype listed above) is always acted upon. -Further note that if the `\-type' switch is used, and it is desirable to -act on a message/external-body content, then the `\-type' switch must +Further note that if the +.B \-type +switch is used, and it is desirable to +act on a message/external-body content, then the +.B \-type +switch must be used twice: once for message/external-body and once for the content externally referenced. +.PP +In the absence of +.BR \-prefer , +.B mhshow +will select the "best" displayable subpart from +multipart/alternative content. The +.B \-prefer +switch can be used (one or more times, in order of descending +preference) to let MH know which content types from a +multipart/alternative MIME part are preferred by the user, in order to +override the default selection for display. For example, mail is +often sent containing both plaintext and HTML-formatted versions of +the same content, and the HTML version is usually indicated to be the +"best" format for viewing. Using \*(lq-prefer text/plain\*(rq will +cause the plaintext version to be displayed if possible, but still +allow display of the HTML part if there is no plaintext subpart +available. Using \*(lq-prefer text/plain -prefer image/png\*(rq +would add a preference for PNG images, which might or might not +ever appear in the same multipart/alternative section with text/plain. +Implementation note: RFC 2046 requires that the subparts +of a multipart/alternative be ordered according to "faithfulness to +the original content", and MH by default selects the subpart ranked +most "faithful" by that ordering. The +.B \-prefer +switch reorders the alternative parts (only internally, never changing +the message file) to move the user's preferred part(s) to the "most +faithful" position. Thus, when viewed by +.BR mhlist , +the ordering of multipart/alternative parts will appear to change when +invoked with or without various +.B \-prefer +switches. .SS "Unseen Sequence" If the profile entry \*(lqUnseen\-Sequence\*(rq is present and non\-empty, then @@ -345,7 +412,7 @@ was built with .IR iconv (3), then all text/plain parts of the message(s) will be displayed using the character set of the current locale. See the -.BR mhparam (1) +.IR mhparam (1) man page for how determine whether your .B nmh installation includes diff --git a/man/mhstore.man b/man/mhstore.man index fbdd3702..2484d69e 100644 --- a/man/mhstore.man +++ b/man/mhstore.man @@ -20,6 +20,9 @@ mhstore \- store contents of MIME messages into files .RB [ \-type .IR content ] \&... +.RB [ \-prefer +.IR content ] +\&... .RB [ \-auto " | " \-noauto ] .RB [ \-clobber .IR always " | " auto " | " suffix " | " ask " | " never ] @@ -129,6 +132,26 @@ desirable to act on a message/external-body content, then the .B \-type switch must be used twice: once for message/external-body and once for the content externally referenced. +.PP +The +.B \-prefer +switch will alter the part ordering of multipart/alternative MIME sections +in order to override the sender-imposed default ordering. +The +.B \-prefer +switch is functionally most important for +.BR mhshow , +but is also implemented in +.B mhlist +and +.B mhstore +to make common part number ordering possible across all three programs. +See +.IR mhlist (1) +and +.IR mhshow (1) +for more information on +.BR \-prefer. .SS "Checking the Contents" The .B \-check diff --git a/test/mhlist/test-mhlist b/test/mhlist/test-mhlist index d14475c3..e5fc895c 100755 --- a/test/mhlist/test-mhlist +++ b/test/mhlist/test-mhlist @@ -220,6 +220,7 @@ cat > $expected < $msgfile < $actual 2>&1 check $expected $actual +## now check mhlist output + +msgfile=`mhpath new` +msgnum=`basename $msgfile` +cat > $msgfile < +Subject: mhlist test +Date: Thu, 29 Jan 2015 18:12:21 +0000 (GMT) +Content-Type: multipart/mixed; boundary="BoundaryMixed" + +--BoundaryMixed +Content-type: multipart/alternative; + boundary="BoundaryAlternative1" + +--BoundaryAlternative1 +Content-type: text/enriched; CHARSET=US-ASCII +Content-transfer-encoding: 7bit + +This is supposedly enriched. + +--BoundaryAlternative1 +Content-type: text/plain; charset=ISO-8859-1; format=flowed +Content-transfer-encoding: 8bit + +This is the body text/plain part. + +--BoundaryAlternative1 +Content-type: text/html; CHARSET=US-ASCII +Content-transfer-encoding: 7bit + +
+This is the text/html body part. +
+ +--BoundaryAlternative1-- + +--BoundaryMixed +Content-type: multipart/alternative; + boundary="BoundaryAlternative2" + +--BoundaryAlternative2 +Content-type: audio/wav +Content-transfer-encoding: 8bit + +pretend wav audio + +--BoundaryAlternative2 +Content-type: audio/mp3 +Content-transfer-encoding: 8bit + +pretend mp3 audio + +--BoundaryAlternative2 +Content-type: audio/basic +Content-transfer-encoding: 8bit + +pretend basic audio + +--BoundaryAlternative2-- + +--BoundaryMixed-- +EOF + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + exit $failed diff --git a/test/mhshow/test-subpart b/test/mhshow/test-subpart index 9739c48d..161dceab 100755 --- a/test/mhshow/test-subpart +++ b/test/mhshow/test-subpart @@ -59,4 +59,114 @@ EOF run_prog mhshow -part 1.1 -form mhl.null $msgnum > $actual 2>&1 check "$expected" "$actual" +cat > $msgfile < +Subject: mhshow -part/-type/prefer test +Date: Thu, 29 Jan 2015 18:12:21 +0000 (GMT) +Content-Type: multipart/mixed; boundary="BoundaryMixed" + +--BoundaryMixed +Content-type: multipart/alternative; + boundary="BoundaryAlternative1" + +--BoundaryAlternative1 +Content-type: text/plain; charset=ISO-8859-1; format=flowed +Content-transfer-encoding: 8bit + +This is the body text/plain part. +--BoundaryAlternative1 +Content-type: multipart/related; + boundary="BoundaryAlternative2"; type="text/html" + +--BoundaryAlternative2 +Content-type: text/html; CHARSET=US-ASCII +Content-transfer-encoding: quoted-printable + +
+This is the text/html body part. +
+ +--BoundaryAlternative2-- + +--BoundaryAlternative1-- + +--BoundaryMixed +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: inline + +_______________________________________________ +This is the final text/plain signature part. + +--BoundaryMixed-- +EOF + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + +# Write the expected output. +cat > $expected < $actual 2>&1 +check "$expected" "$actual" + exit $failed diff --git a/uip/mhcachesbr.c b/uip/mhcachesbr.c index 56ba9b71..201b78e1 100644 --- a/uip/mhcachesbr.c +++ b/uip/mhcachesbr.c @@ -38,7 +38,7 @@ char *cache_private; /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void content_error (char *, CT, char *, ...); void flush_errors (void); diff --git a/uip/mhlist.c b/uip/mhlist.c index 91b09841..34655236 100644 --- a/uip/mhlist.c +++ b/uip/mhlist.c @@ -32,6 +32,7 @@ X("file file", 0, FILESW) \ X("part number", 0, PARTSW) \ X("type content", 0, TYPESW) \ + X("prefer content", 0, PREFERSW) \ X("rcache policy", 0, RCACHESW) \ X("wcache policy", 0, WCACHESW) \ X("changecur", 0, CHGSW) \ @@ -62,6 +63,11 @@ extern char *parts[NPARTS + 1]; extern char *types[NTYPES + 1]; extern int userrs; +/* mhparse.c */ +extern char *preferred_types[]; +extern char *preferred_subtypes[]; +extern int npreferred; + /* * This is currently needed to keep mhparse happy. * This needs to be changed. @@ -74,7 +80,7 @@ int debugsw = 0; CT parse_mime (char *); /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void flush_errors (void); @@ -186,6 +192,18 @@ do_cache: types[ntype++] = cp; continue; + case PREFERSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + if (npreferred >= NPREFS) + adios (NULL, "too many preferred types (starting with %s), %d max", + cp, NPREFS); + preferred_types[npreferred] = cp; + cp = strchr(cp, '/'); + if (cp) *cp++ = '\0'; + preferred_subtypes[npreferred++] = cp; + continue; + case FILESW: if (!(cp = *argp++) || (*cp == '-' && cp[1])) adios (NULL, "missing argument to %s", argp[-2]); diff --git a/uip/mhlistsbr.c b/uip/mhlistsbr.c index 309357b1..0110d9e0 100644 --- a/uip/mhlistsbr.c +++ b/uip/mhlistsbr.c @@ -18,7 +18,7 @@ #include /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void flush_errors (void); @@ -343,7 +343,7 @@ list_multi (CT ct, int toplevel, int realsize, int verbose, int debug, for (part = m->mp_parts; part; part = part->mp_next) { CT p = part->mp_part; - if (part_ok (p, 1) && type_ok (p, 1)) + if (part_ok (p) && type_ok (p, 1)) list_switch (p, 0, realsize, verbose, debug, dispo); } diff --git a/uip/mhmisc.c b/uip/mhmisc.c index f4b89168..8b1f0d00 100644 --- a/uip/mhmisc.c +++ b/uip/mhmisc.c @@ -30,26 +30,54 @@ static char *errs = NULL; /* * prototypes */ -int part_ok (CT, int); +int part_ok (CT); +int part_exact(CT ct); int type_ok (CT, int); void content_error (char *, CT, char *, ...); void flush_errors (void); int -part_ok (CT ct, int sP) +part_ok (CT ct) { char **ap; int len; - if (npart == 0 || (ct->c_type == CT_MULTIPART && (sP || ct->c_subtype))) + /* a part is "ok", i.e., should be processed, if: + - there were no -part arguments + - this part is a multipart + */ + if (npart == 0 || ct->c_type == CT_MULTIPART) { return 1; + } + /* or if: + - this part is a an exact match for any -part option + - this part is a sub-part of any -part option + */ for (ap = parts; *ap; ap++) { len = strlen(*ap); if (!strncmp (*ap, ct->c_partno, len) && - (!ct->c_partno[len] || ct->c_partno[len] == '.' )) + (!ct->c_partno[len] || ct->c_partno[len] == '.' )) { + return 1; + } + } + + return 0; +} + +int +part_exact(CT ct) +{ + char **ap; + + if (!ct->c_partno) + return 0; + + for (ap = parts; *ap; ap++) { + if (!strcmp (*ap, ct->c_partno)) { return 1; + } } return 0; diff --git a/uip/mhn.c b/uip/mhn.c index 39d86933..be433d69 100644 --- a/uip/mhn.c +++ b/uip/mhn.c @@ -115,7 +115,7 @@ static int storesw = 0; CT parse_mime (char *); /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void flush_errors (void); diff --git a/uip/mhparam.c b/uip/mhparam.c index acb416c7..4fe8c8d3 100644 --- a/uip/mhparam.c +++ b/uip/mhparam.c @@ -29,6 +29,7 @@ DEFINE_SWITCH_ENUM(MHPARAM); DEFINE_SWITCH_ARRAY(MHPARAM, switches); #undef X +extern char *mhbindir; extern char *mhetcdir; extern char *mhlibexecdir; @@ -104,6 +105,7 @@ static struct proc procs [] = { { "version", &version_num }, { "whatnowproc", &whatnowproc }, { "whomproc", &whomproc }, + { "bindir", &mhbindir }, { "etcdir", &mhetcdir }, { "libdir", &mhlibexecdir }, { "libexecdir", &mhlibexecdir }, diff --git a/uip/mhparse.c b/uip/mhparse.c index a3a80f9a..f021b07f 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -43,6 +43,12 @@ int bogus_mp_content; int suppress_extraneous_trailing_semicolon_warning; int extraneous_trailing_semicolon; +/* list of preferred type/subtype pairs, for -prefer */ +char *preferred_types[NPREFS], + *preferred_subtypes[NPREFS]; +int npreferred; + + /* * Structures for TEXT messages */ @@ -104,7 +110,7 @@ static struct k2v EncodingType[] = { int find_cache (CT, int, int *, char *, char *, int); /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void content_error (char *, CT, char *, ...); @@ -121,6 +127,7 @@ static int InitGeneric (CT); static int InitText (CT); static int InitMultiPart (CT); static void reverse_parts (CT); +static void prefer_parts(CT ct); static int InitMessage (CT); static int InitApplication (CT); static int init_encoding (CT, OpenCEFunc); @@ -1223,8 +1230,10 @@ end_part: last_part: /* reverse the order of the parts for multipart/alternative */ - if (ct->c_subtype == MULTI_ALTERNATE) + if (ct->c_subtype == MULTI_ALTERNATE) { reverse_parts (ct); + prefer_parts (ct); + } /* * label all subparts with part number, and @@ -1270,9 +1279,15 @@ last_part: /* - * reverse the order of the parts of a multipart/alternative + * reverse the order of the parts of a multipart/alternative, + * presumably to put the "most favored" alternative first, for + * ease of choosing/displaying it later on. from a mail message on + * nmh-workers, from kenh: + * "Stock" MH 6.8.5 did not have a reverse_parts() function, but I + * see code in mhn that did the same thing... Acccording to the RCS + * logs, that code was around from the initial checkin of mhn.c by + * John Romine in 1992, which is as far back as we have." */ - static void reverse_parts (CT ct) { @@ -1289,6 +1304,60 @@ reverse_parts (CT ct) } } +static void +move_preferred_part (CT ct, char *type, char *subtype) +{ + struct multipart *m = (struct multipart *) ct->c_ctparams; + struct part *part, *prev, *head, *nhead, *ntail; + struct part h, n; + CI ci; + + /* move the matching part(s) to the head of the list: walk the + * list of parts, move matching parts to a new list (maintaining + * their order), and finally, concatenate the old list onto the + * new. + */ + + head = &h; + nhead = &n; + + head->mp_next = m->mp_parts; + nhead->mp_next = NULL; + ntail = nhead; + + prev = head; + part = head->mp_next; + while (part != NULL) { + ci = &part->mp_part->c_ctinfo; + if (!strcasecmp(ci->ci_type, type) && + (!subtype || !strcasecmp(ci->ci_subtype, subtype))) { + prev->mp_next = part->mp_next; + part->mp_next = NULL; + ntail->mp_next = part; + ntail = part; + part = prev->mp_next; + } else { + prev = part; + part = prev->mp_next; + } + } + ntail->mp_next = head->mp_next; + m->mp_parts = nhead->mp_next; + +} + +/* + * move parts that match the user's preferences (-prefer) to the head + * of the line. process preferences in reverse so first one given + * ends up first in line + */ +static void +prefer_parts(CT ct) +{ + int i; + for (i = npreferred-1; i >= 0; i--) + move_preferred_part(ct, preferred_types[i], preferred_subtypes[i]); +} diff --git a/uip/mhshow.c b/uip/mhshow.c index 0227d0c2..c8343763 100644 --- a/uip/mhshow.c +++ b/uip/mhshow.c @@ -34,6 +34,7 @@ X("markform formfile", 0, MARKFORMSW) \ X("part number", 0, PARTSW) \ X("type content", 0, TYPESW) \ + X("prefer content", 0, PREFERSW) \ X("rcache policy", 0, RCACHESW) \ X("wcache policy", 0, WCACHESW) \ X("version", 0, VERSIONSW) \ @@ -77,6 +78,11 @@ extern char *parts[NPARTS + 1]; extern char *types[NTYPES + 1]; extern int userrs; +/* mhparse.c */ +extern char *preferred_types[]; +extern char *preferred_subtypes[]; +extern int npreferred; + int debugsw = 0; int verbosw = 0; @@ -86,7 +92,7 @@ int verbosw = 0; CT parse_mime (char *); /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void flush_errors (void); @@ -203,6 +209,18 @@ do_cache: types[ntype++] = cp; continue; + case PREFERSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + if (npreferred >= NPREFS) + adios (NULL, "too many preferred types (starting with %s), %d max", + cp, NPREFS); + preferred_types[npreferred] = cp; + cp = strchr(cp, '/'); + if (cp) *cp++ = '\0'; + preferred_subtypes[npreferred++] = cp; + continue; + case FILESW: if (!(cp = *argp++) || (*cp == '-' && cp[1])) adios (NULL, "missing argument to %s", argp[-2]); diff --git a/uip/mhshowsbr.c b/uip/mhshowsbr.c index 440fa9e7..31d25eb1 100644 --- a/uip/mhshowsbr.c +++ b/uip/mhshowsbr.c @@ -22,6 +22,8 @@ #endif /* ! HAVE_ICONV */ extern int debugsw; +extern int npart; +extern int ntype; int nolist = 0; @@ -33,7 +35,8 @@ char *formsw = NULL; /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); +int part_exact (CT); int type_ok (CT, int); void content_error (char *, CT, char *, ...); void flush_errors (void); @@ -340,7 +343,7 @@ show_content_aux (CT ct, int alternate, char *cp, char *cracked, struct format * if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK) return NOTOK; if (ct->c_showproc && !strcmp (ct->c_showproc, "true")) - return (alternate ? DONE : OK); + return OK; if (! strcmp(invo_name, "mhshow") && ct->c_type == CT_TEXT && ct->c_subtype == TEXT_PLAIN) { @@ -490,7 +493,7 @@ show_content_aux2 (CT ct, int alternate, char *cracked, char *buffer, if (fd != NOTOK) (*ct->c_ceclosefnx) (ct); - return (alternate ? DONE : status); + return (alternate ? OK : status); } } } @@ -546,8 +549,9 @@ show_multi (CT ct, int alternate, int concatsw, int textonly, int inlineonly, if ((cp = context_find_by_type ("show", ci->ci_type, ci->ci_subtype))) return show_multi_aux (ct, alternate, cp, fmt); - if ((cp = ct->c_showproc)) + if ((cp = ct->c_showproc)) { return show_multi_aux (ct, alternate, cp, fmt); + } /* * Use default method to display this multipart content. Even @@ -572,6 +576,9 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly, struct multipart *m = (struct multipart *) ct->c_ctparams; struct part *part; int request_matched; + int display_success; + int mult_alt_done; + int ret; CT p; alternating = 0; @@ -583,41 +590,57 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly, } /* - * alternate -> we are a part inside an multipart/alternative + * alternate -> we are a part inside a multipart/alternative * alternating -> we are a multipart/alternative */ - result = alternate ? NOTOK : OK; + result = NOTOK; request_matched = 0; + display_success = 0; + mult_alt_done = 0; for (part = m->mp_parts; part; part = part->mp_next) { p = part->mp_part; - if (part_ok (p, 1) && type_ok (p, 1)) { - int inneresult; + /* while looking for the right displayable alternative, we + * use a looser search criterion than we do after finding it. + * specifically, while still looking, part_ok() will match + * "parent" parts (e.g. "-part 2" where 2 is a high-level + * multipart). after finding it, we use part_exact() to only + * choose a part that was requested explicitly. + */ + if ((part_exact(p) && type_ok(p, 1)) || + (!mult_alt_done && part_ok (p) && type_ok (p, 1))) { - request_matched = 1; + int inneresult; inneresult = show_switch (p, nowalternate, concatsw, textonly, inlineonly, fmt); switch (inneresult) { - case NOTOK: + case NOTOK: /* hard display error */ + request_matched = 1; if (alternate && !alternating) { result = NOTOK; goto out; } continue; - case OK: - case DONE: + case DONE: /* found no match on content type */ + continue; + + case OK: /* display successful */ + request_matched = 1; + display_success = 1; + result = OK; + + /* if we got success on a sub-part of + * multipart/alternative, we're done, unless + * there's a chance an explicit part should be + * matched later in the alternatives. */ if (alternating) { - result = DONE; - break; - } - if (alternate) { + mult_alt_done = 1; + } else if (alternate) { alternate = nowalternate = 0; - if (result == NOTOK) - result = inneresult; } continue; } @@ -625,17 +648,23 @@ show_multi_internal (CT ct, int alternate, int concatsw, int textonly, } } - if (alternating && !part && request_matched) { + /* we're supposed to be displaying at least something from a + * multipart/alternative. if we've had parts to consider, and + * we've had no success, then we should complain. we shouldn't + * complain if none of the parts matched any -part or -type option. + */ + if (alternating && request_matched && !display_success) { + /* if we're ourselves an alternate. don't complain yet. */ if (!alternate) content_error (NULL, ct, "don't know how to display any of the contents"); result = NOTOK; - goto out; } out: /* if no parts matched what was requested, there can't have been - * any display errors, so we report OK. */ - return request_matched ? result : OK; + * any display errors. we report DONE rather than OK. */ + ret = request_matched ? result : DONE; + return ret; } @@ -671,7 +700,7 @@ show_multi_aux (CT ct, int alternate, char *cp, struct format *fmt) p->c_storage = add (file, NULL); if (p->c_showproc && !strcmp (p->c_showproc, "true")) - return (alternate ? DONE : OK); + return OK; (*p->c_ceclosefnx) (p); } } diff --git a/uip/mhstore.c b/uip/mhstore.c index 17dcc01d..a3481034 100644 --- a/uip/mhstore.c +++ b/uip/mhstore.c @@ -29,6 +29,7 @@ X("outfile outfile", 0, OUTFILESW) \ X("part number", 0, PARTSW) \ X("type content", 0, TYPESW) \ + X("prefer content", 0, PREFERSW) \ X("rcache policy", 0, RCACHESW) \ X("wcache policy", 0, WCACHESW) \ X("version", 0, VERSIONSW) \ @@ -58,6 +59,11 @@ extern char *parts[NPARTS + 1]; extern char *types[NTYPES + 1]; extern int userrs; +/* mhparse.c */ +extern char *preferred_types[]; +extern char *preferred_subtypes[]; +extern int npreferred; + #define quitser pipeser /* mhparse.c */ @@ -65,7 +71,7 @@ int debugsw = 0; CT parse_mime (char *); /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void flush_errors (void); @@ -175,6 +181,18 @@ do_cache: types[ntype++] = cp; continue; + case PREFERSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + if (npreferred >= NPREFS) + adios (NULL, "too many preferred types (starting with %s), %d max", + cp, NPREFS); + preferred_types[npreferred] = cp; + cp = strchr(cp, '/'); + if (cp) *cp++ = '\0'; + preferred_subtypes[npreferred++] = cp; + continue; + case FILESW: if (!(cp = *argp++) || (*cp == '-' && cp[1])) adios (NULL, "missing argument to %s", argp[-2]); diff --git a/uip/mhstoresbr.c b/uip/mhstoresbr.c index ae835fbf..c53f7ad3 100644 --- a/uip/mhstoresbr.c +++ b/uip/mhstoresbr.c @@ -77,7 +77,7 @@ typedef int (*qsort_comp) (const void *, const void *); /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void flush_errors (void); @@ -287,7 +287,7 @@ store_multi (CT ct, mhstoreinfo_t info) for (part = m->mp_parts; part; part = part->mp_next) { CT p = part->mp_part; - if (part_ok (p, 1) && type_ok (p, 1)) { + if (part_ok (p) && type_ok (p, 1)) { if (ct->c_storage) { /* Support mhstore -outfile. The MIME parser doesn't load c_storage, so we know that p->c_storage is