- If nmh was configured with iconv(3) support, mhshow will convert, if
necessary, the charset of text/plain content to match the user's
locale setting.
+- Added support for %{charset} display string escape to mhshow(1).
-----------------
OBSOLETE FEATURES
-.TH MHSHOW %manext1% "February 9, 2014" "%nmhversion%"
+.TH MHSHOW %manext1% "February 16, 2014" "%nmhversion%"
.\"
.\" %nmhwarning%
.\"
.PP
.RS 5
.nf
-.ta \w'%F 'u
-%a Insert parameters from Content-Type field
-%e exclusive execution
-%f Insert filename containing content
-%F %e, %f, and stdin is terminal not content
-%l display listing prior to displaying content
-%p %l, and ask for confirmation
-%s Insert content subtype
-%d Insert content description
-%% Insert the character %
+.ta \w'%F 'u
+%a Insert parameters from Content-Type field
+%{charset} Insert the charset value from the Content-Type field
+%e exclusive execution
+%f Insert filename containing content
+%F %e, %f, and stdin is terminal not content
+%l display listing prior to displaying content
+%p %l, and ask for confirmation
+%s Insert content subtype
+%d Insert content description
+%% Insert the character %
.fi
.RE
.PP
expected="$MH_TEST_DIR"/$$.expected
actual="$MH_TEST_DIR"/$$.actual
+# check charset conversion
msgfile=`mhpath new`
cat >"$msgfile" <<EOF
From: foo@example.edu
run_prog mhshow -nopause last >"$actual" 2>&1
check "$expected" "$actual"
+cat >>"$MH" <<'EOF'
+mhshow-show-text/plain: echo %{charset}
+EOF
+
+# check expansion of %{charset} by itself
+msgfile=`mhpath new`
+cat >"$msgfile" <<EOF
+From: foo@example.edu
+To: bar@example.edu
+Subject: test display with %{charset} expansion
+MIME-Version: 1.0
+Content-Type: text/plain; charset=windows-1252
+Content-Transfer-Encoding: quoted-printable
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+
+4 =F7 2 =3D 2
+EOF
+
+cat >"$expected" <<EOF
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+To: bar@example.edu
+From: foo@example.edu
+Subject: test display with %{charset} expansion
+
+MIME-Version: 1.0
+
+windows-1252
+EOF
+
+run_prog mhshow -nopause last >"$actual" 2>&1
+check "$expected" "$actual"
+
+# check expansion of empty %{charset} by itself
+msgfile=`mhpath new`
+cat >"$msgfile" <<EOF
+From: foo@example.edu
+To: bar@example.edu
+Subject: test display with empty %{charset} expansion
+MIME-Version: 1.0
+Content-Type: text/plain
+Content-Transfer-Encoding: quoted-printable
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+
+4 =F7 2 =3D 2
+EOF
+
+cat >"$expected" <<EOF
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+To: bar@example.edu
+From: foo@example.edu
+Subject: test display with empty %{charset} expansion
+
+MIME-Version: 1.0
+
+
+EOF
+
+run_prog mhshow -nopause last >"$actual" 2>&1
+check "$expected" "$actual"
+
+grep -v 'mhshow-show-text/plain:' "$MH" >"$MH.new"
+mv -f "$MH.new" "$MH"
+cat >>"$MH" <<'EOF'
+mhshow-show-text/plain: echo `ap -format '%(void(lit %{charset}))%<(nonnull)-I %(putstr)%>' 0`
+EOF
+
+# check expansion of embedded %{charset} with no text following
+cat >"$expected" <<EOF
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+To: bar@example.edu
+From: foo@example.edu
+Subject: test display with %{charset} expansion
+
+MIME-Version: 1.0
+
+-I windows-1252
+EOF
+
+run_prog mhshow -nopause prev >"$actual" 2>&1
+check "$expected" "$actual"
+
+# check expansion of empty embedded %{charset} with no text following
+cat >"$expected" <<EOF
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+To: bar@example.edu
+From: foo@example.edu
+Subject: test display with empty %{charset} expansion
+
+MIME-Version: 1.0
+
+
+EOF
+
+run_prog mhshow -nopause last >"$actual" 2>&1
+check "$expected" "$actual"
+
+sed -e 's%\(mhshow-show-text/plain:.*\)%\1 file%' "$MH" >"$MH.new"
+mv -f "$MH.new" "$MH"
+
+# check expansion of embedded %{charset} with text following
+cat >"$expected" <<EOF
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+To: bar@example.edu
+From: foo@example.edu
+Subject: test display with %{charset} expansion
+
+MIME-Version: 1.0
+
+-I windows-1252 file
+EOF
+
+run_prog mhshow -nopause prev >"$actual" 2>&1
+check "$expected" "$actual"
+
+# check expansion of empty embedded %{charset} with text following
+cat >"$expected" <<EOF
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+To: bar@example.edu
+From: foo@example.edu
+Subject: test display with empty %{charset} expansion
+
+MIME-Version: 1.0
+
+file
+EOF
+
+run_prog mhshow -nopause last >"$actual" 2>&1
+check "$expected" "$actual"
+
+sed -e 's/%{charset}/%{unknown}/' "$MH" >"$MH.new"
+mv -f "$MH.new" "$MH"
+
+# check expansion of unknown parameter
+msgfile=`mhpath new`
+cat >"$msgfile" <<EOF
+From: foo@example.edu
+To: bar@example.edu
+Subject: test display with unknown C-T parameter expansion
+MIME-Version: 1.0
+Content-Type: text/plain; charset=windows-1252
+Content-Transfer-Encoding: quoted-printable
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+
+4 =F7 2 =3D 2
+EOF
+
+cat >"$expected" <<EOF
+Date: Sun, 18 Dec 2005 00:52:39 +0100
+To: bar@example.edu
+From: foo@example.edu
+Subject: test display with unknown C-T parameter expansion
+
+MIME-Version: 1.0
+
+-I %{unknown} file
+EOF
+
+run_prog mhshow -nopause last >"$actual" 2>&1
+check "$expected" "$actual"
+
+
exit $failed
static int parse_display_string (CT, char *, int *, int *, int *, int *, char *,
char *, size_t, int multipart);
static int convert_content_charset (CT, char **);
+static int parameter_value (CI, const char *, const char *, const char **);
static void intrser (int);
/* and fall... */
case 'f':
- if (multipart) {
- /* insert filename(s) containing content */
- struct multipart *m = (struct multipart *) ct->c_ctparams;
- struct part *part;
- char *s = "";
- CT p;
-
- for (part = m->mp_parts; part; part = part->mp_next) {
- p = part->mp_part;
-
- snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
- len = strlen (bp);
- bp += len;
- buflen -= len;
- s = " ";
- }
- /* set our starting pointer back to bp, to avoid
- * requoting the filenames we just added
- */
- pp = bp;
- } else {
- /* insert filename containing content */
- snprintf (bp, buflen, "'%s'", file);
- /* since we've quoted the file argument, set things up
- * to look past it, to avoid problems with the quoting
- * logic below. (I know, I should figure out what's
- * broken with the quoting logic, but..)
- */
- len = strlen(bp);
- buflen -= len;
- bp += len;
- pp = bp;
- }
- break;
+ if (multipart) {
+ /* insert filename(s) containing content */
+ struct multipart *m = (struct multipart *) ct->c_ctparams;
+ struct part *part;
+ char *s = "";
+ CT p;
+
+ for (part = m->mp_parts; part; part = part->mp_next) {
+ p = part->mp_part;
+
+ snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
+ len = strlen (bp);
+ bp += len;
+ buflen -= len;
+ s = " ";
+ }
+ /* set our starting pointer back to bp, to avoid
+ * requoting the filenames we just added
+ */
+ pp = bp;
+ } else {
+ /* insert filename containing content */
+ snprintf (bp, buflen, "'%s'", file);
+ /* since we've quoted the file argument, set things up
+ * to look past it, to avoid problems with the quoting
+ * logic below. (I know, I should figure out what's
+ * broken with the quoting logic, but..)
+ */
+ len = strlen(bp);
+ buflen -= len;
+ bp += len;
+ pp = bp;
+ }
+ break;
case 'p':
/* %l, and pause prior to displaying content */
/* insert character % */
goto raw;
+ case '{' : {
+ static const char param[] = "charset";
+ const char *value;
+ const int found = parameter_value (ci, param, cp, &value);
+
+ if (found == OK) {
+ /* Because it'll get incremented in the next iteration,
+ just increment by 1 for the '{'. */
+ cp += strlen(param) + 1;
+
+ /* cp points to the param and it's set in the
+ Content-Type header. */
+ strncpy (bp, value, buflen);
+ break;
+ } else if (found == 1) {
+ /* cp points to the param and it's not set in the
+ Content-Type header, so skip it. */
+ cp += strlen(param) + 1;
+
+ if (*cp == '\0') {
+ break;
+ } else {
+ if (*(cp + 1) == '\0') {
+ break;
+ } else {
+ ++cp;
+ /* Increment cp again so that the last
+ character of the %{} token isn't output
+ after falling thru below. */
+ ++cp;
+ }
+ }
+ } else {
+ /* cp points to an unrecognized parameter. Output
+ it as-is, starting here with the "%{". */
+ *bp++ = '%';
+ *bp++ = '{';
+ *bp = '\0';
+ buflen -= 2;
+ break;
+ }
+
+ /* No parameter was found, so fall thru to default to
+ output the rest of the text as-is. */
+ }
+
default:
*bp++ = *--cp;
*bp = '\0';
}
+/*
+ * Return values:
+ * NOTOK if cp doesn't point to {param}
+ * OK if cp points to {param} and that attribute exists, and returns
+ * its value
+ * 1 if cp points to {param} and that attribute doesn't exist
+ */
+int
+parameter_value (CI ci, const char *param, const char *cp, const char **value) {
+ int found = NOTOK;
+ char *braced_param = concat ("{", param, "}", NULL);
+
+ if (! strncasecmp(cp, braced_param, strlen (braced_param))) {
+ char **ap, **ep;
+
+ found = 1;
+
+ for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ++ap, ++ep) {
+ if (!strcasecmp (*ap, param)) {
+ found = OK;
+ *value = *ep;
+ break;
+ }
+ }
+ }
+
+ free (braced_param);
+ return found;
+}
+
+
static void
intrser (int i)
{