From 90986dddba4b24b0449238f86f836581cfb93938 Mon Sep 17 00:00:00 2001 From: David Levine Date: Sun, 4 Nov 2012 20:58:47 -0600 Subject: [PATCH] Added format support for zputlit function escape. It requires that the str contents have zero display width, such as for terminal escape sequences. --- Makefile.am | 4 +- docs/pending-release-notes | 2 + etc/scan.highlighted | 40 +++++++++++ h/fmt_compile.h | 159 ++++++++++++++++++++++---------------------- man/mh-format.man | 10 ++- man/scan.man | 8 ++- sbr/fmt_compile.c | 3 + sbr/fmt_scan.c | 35 ++++++++++ test/common.sh.in | 4 +- test/scan/test-scan | 24 ++++++- uip/fmtdump.c | 1 + 11 files changed, 202 insertions(+), 88 deletions(-) create mode 100644 etc/scan.highlighted diff --git a/Makefile.am b/Makefile.am index ec21483..f4407a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -161,8 +161,8 @@ dist_sysconf_DATA = etc/MailAliases etc/components etc/digestcomps \ 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/scan.YYYYMMDD etc/scan.default etc/scan.mailx \ - etc/scan.nomime etc/scan.size etc/scan.time \ + etc/scan.YYYYMMDD etc/scan.default etc/scan.highlighted \ + etc/scan.mailx etc/scan.nomime etc/scan.size etc/scan.time \ etc/scan.timely etc/scan.unseen ## diff --git a/docs/pending-release-notes b/docs/pending-release-notes index 021c692..fc7728b 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -27,6 +27,8 @@ NEW FEATURES - Added -noall/-all switches to sortm(1). sortm -noall requires a messages argument. - $PAGER overrides the compiled-in default pager command. +- Added etc/scan.highlighted format file, as an example of how to + highlight/colorize the output of scan(1). ---------------------------- diff --git a/etc/scan.highlighted b/etc/scan.highlighted new file mode 100644 index 0000000..134317b --- /dev/null +++ b/etc/scan.highlighted @@ -0,0 +1,40 @@ +%; scan.highlighted +%; +%; This file shows how to use ANSI escape sequences to highlight a +%; current message in yellow and any unseen messages in cyan. It +%; differs from the default scan format in the two additional lines at +%; the beginning and the literal sequence at the end. +%; +%; To enable use of this format, add "-form scan.highlighted" to your +%; scan invocation or to your profile (~/.mh_profile), e.g.: +%; scan: -form scan.highlighted +%; +%; You can optionally copy this file to your MH Path and modify as +%; desired. +%; +%; The mh-sequence(5) man page describes the unseen sequence. +%; +%; A more readable approach would add escape sequences to your profile, e.g.: +%; normal:  +%; bright-black:  +%; red:  +%; green:  +%; yellow:  +%; blue:  +%; magenta:  +%; cyan:  +%; bright-white:  +%; underlined-white:  +%; To use them in the format, e.g., at the beginning: +%; %<(cur)%(void(profile yellow))%(zputlit)%>\ +%; %<(unseen)%(void(profile cyan))%(zputlit)%>\ +%; and at the end: +%; %(void(profile normal))%(zputlit) +%; +%<(cur)%(void(lit ))%(zputlit)%>\ +%<(unseen)%(void(lit ))%(zputlit)%>\ +%4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>\ +%02(mon{date})/%02(mday{date})%<{date} %|*%>\ +%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>\ +%<(zero)%17(decode(friendly{from}))%> \ +%(decode{subject})%<{body}<<%{body}>>%>%(void(lit ))%(zputlit) diff --git a/h/fmt_compile.h b/h/fmt_compile.h index 3cbae02..559f993 100644 --- a/h/fmt_compile.h +++ b/h/fmt_compile.h @@ -15,96 +15,97 @@ #define FT_STRF 9 /* "str" as text, filled */ #define FT_STRFW 10 /* "str" as text, filled, width in "value" */ #define FT_STRLIT 11 /* "str" as text, no space compression */ -#define FT_PUTADDR 12 /* split and print address line */ +#define FT_STRLITZ 12 /* literal text with zero display width */ +#define FT_PUTADDR 13 /* split and print address line */ /* types that modify the "str" or "value" registers */ -#define FT_LS_COMP 13 /* set "str" to component text */ -#define FT_LS_LIT 14 /* set "str" to literal text */ -#define FT_LS_GETENV 15 /* set "str" to getenv(text) */ -#define FT_LS_CFIND 16 /* set "str" to context_find(text) */ -#define FT_LS_DECODECOMP 17 /* set "str" to decoded component text */ -#define FT_LS_DECODE 18 /* decode "str" as RFC-2047 header */ -#define FT_LS_TRIM 19 /* trim trailing white space from "str" */ -#define FT_LV_COMP 20 /* set "value" to comp (as dec. num) */ -#define FT_LV_COMPFLAG 21 /* set "value" to comp flag word */ -#define FT_LV_LIT 22 /* set "value" to literal num */ -#define FT_LV_DAT 23 /* set "value" to dat[n] */ -#define FT_LV_STRLEN 24 /* set "value" to length of "str" */ -#define FT_LV_PLUS_L 25 /* set "value" += literal */ -#define FT_LV_MINUS_L 26 /* set "value" -= literal */ -#define FT_LV_DIVIDE_L 27 /* set "value" to value / literal */ -#define FT_LV_MODULO_L 28 /* set "value" to value % literal */ -#define FT_LV_CHAR_LEFT 29 /* set "value" to char left in output */ +#define FT_LS_COMP 14 /* set "str" to component text */ +#define FT_LS_LIT 15 /* set "str" to literal text */ +#define FT_LS_GETENV 16 /* set "str" to getenv(text) */ +#define FT_LS_CFIND 17 /* set "str" to context_find(text) */ +#define FT_LS_DECODECOMP 18 /* set "str" to decoded component text */ +#define FT_LS_DECODE 19 /* decode "str" as RFC-2047 header */ +#define FT_LS_TRIM 20 /* trim trailing white space from "str" */ +#define FT_LV_COMP 21 /* set "value" to comp (as dec. num) */ +#define FT_LV_COMPFLAG 22 /* set "value" to comp flag word */ +#define FT_LV_LIT 23 /* set "value" to literal num */ +#define FT_LV_DAT 24 /* set "value" to dat[n] */ +#define FT_LV_STRLEN 25 /* set "value" to length of "str" */ +#define FT_LV_PLUS_L 26 /* set "value" += literal */ +#define FT_LV_MINUS_L 27 /* set "value" -= literal */ +#define FT_LV_DIVIDE_L 28 /* set "value" to value / literal */ +#define FT_LV_MODULO_L 29 /* set "value" to value % literal */ +#define FT_LV_CHAR_LEFT 30 /* set "value" to char left in output */ -#define FT_LS_MONTH 30 /* set "str" to tws month */ -#define FT_LS_LMONTH 31 /* set "str" to long tws month */ -#define FT_LS_ZONE 32 /* set "str" to tws timezone */ -#define FT_LS_DAY 33 /* set "str" to tws weekday */ -#define FT_LS_WEEKDAY 34 /* set "str" to long tws weekday */ -#define FT_LS_822DATE 35 /* set "str" to 822 date str */ -#define FT_LS_PRETTY 36 /* set "str" to pretty (?) date str */ -#define FT_LV_SEC 37 /* set "value" to tws second */ -#define FT_LV_MIN 38 /* set "value" to tws minute */ -#define FT_LV_HOUR 39 /* set "value" to tws hour */ -#define FT_LV_MDAY 40 /* set "value" to tws day of month */ -#define FT_LV_MON 41 /* set "value" to tws month */ -#define FT_LV_YEAR 42 /* set "value" to tws year */ -#define FT_LV_YDAY 43 /* set "value" to tws day of year */ -#define FT_LV_WDAY 44 /* set "value" to tws weekday */ -#define FT_LV_ZONE 45 /* set "value" to tws timezone */ -#define FT_LV_CLOCK 46 /* set "value" to tws clock */ -#define FT_LV_RCLOCK 47 /* set "value" to now - tws clock */ -#define FT_LV_DAYF 48 /* set "value" to tws day flag */ -#define FT_LV_DST 49 /* set "value" to tws daylight savings flag */ -#define FT_LV_ZONEF 50 /* set "value" to tws timezone flag */ +#define FT_LS_MONTH 31 /* set "str" to tws month */ +#define FT_LS_LMONTH 32 /* set "str" to long tws month */ +#define FT_LS_ZONE 33 /* set "str" to tws timezone */ +#define FT_LS_DAY 34 /* set "str" to tws weekday */ +#define FT_LS_WEEKDAY 35 /* set "str" to long tws weekday */ +#define FT_LS_822DATE 36 /* set "str" to 822 date str */ +#define FT_LS_PRETTY 37 /* set "str" to pretty (?) date str */ +#define FT_LV_SEC 38 /* set "value" to tws second */ +#define FT_LV_MIN 39 /* set "value" to tws minute */ +#define FT_LV_HOUR 40 /* set "value" to tws hour */ +#define FT_LV_MDAY 41 /* set "value" to tws day of month */ +#define FT_LV_MON 42 /* set "value" to tws month */ +#define FT_LV_YEAR 43 /* set "value" to tws year */ +#define FT_LV_YDAY 44 /* set "value" to tws day of year */ +#define FT_LV_WDAY 45 /* set "value" to tws weekday */ +#define FT_LV_ZONE 46 /* set "value" to tws timezone */ +#define FT_LV_CLOCK 47 /* set "value" to tws clock */ +#define FT_LV_RCLOCK 48 /* set "value" to now - tws clock */ +#define FT_LV_DAYF 49 /* set "value" to tws day flag */ +#define FT_LV_DST 50 /* set "value" to tws daylight savings flag */ +#define FT_LV_ZONEF 51 /* set "value" to tws timezone flag */ -#define FT_LS_PERS 51 /* set "str" to person part of addr */ -#define FT_LS_MBOX 52 /* set "str" to mbox part of addr */ -#define FT_LS_HOST 53 /* set "str" to host part of addr */ -#define FT_LS_PATH 54 /* set "str" to route part of addr */ -#define FT_LS_GNAME 55 /* set "str" to group part of addr */ -#define FT_LS_NOTE 56 /* set "str" to comment part of addr */ -#define FT_LS_ADDR 57 /* set "str" to mbox@host */ -#define FT_LS_822ADDR 58 /* set "str" to 822 format addr */ -#define FT_LS_FRIENDLY 59 /* set "str" to "friendly" format addr */ -#define FT_LV_HOSTTYPE 60 /* set "value" to addr host type */ -#define FT_LV_INGRPF 61 /* set "value" to addr in-group flag */ -#define FT_LS_UNQUOTE 62 /* remove RFC 2822 quotes from "str" */ -#define FT_LV_NOHOSTF 63 /* set "value" to addr no-host flag */ +#define FT_LS_PERS 52 /* set "str" to person part of addr */ +#define FT_LS_MBOX 53 /* set "str" to mbox part of addr */ +#define FT_LS_HOST 54 /* set "str" to host part of addr */ +#define FT_LS_PATH 55 /* set "str" to route part of addr */ +#define FT_LS_GNAME 56 /* set "str" to group part of addr */ +#define FT_LS_NOTE 57 /* set "str" to comment part of addr */ +#define FT_LS_ADDR 58 /* set "str" to mbox@host */ +#define FT_LS_822ADDR 59 /* set "str" to 822 format addr */ +#define FT_LS_FRIENDLY 60 /* set "str" to "friendly" format addr */ +#define FT_LV_HOSTTYPE 61 /* set "value" to addr host type */ +#define FT_LV_INGRPF 62 /* set "value" to addr in-group flag */ +#define FT_LS_UNQUOTE 63 /* remove RFC 2822 quotes from "str" */ +#define FT_LV_NOHOSTF 64 /* set "value" to addr no-host flag */ /* Date Coercion */ -#define FT_LOCALDATE 64 /* Coerce date to local timezone */ -#define FT_GMTDATE 65 /* Coerce date to gmt */ +#define FT_LOCALDATE 65 /* Coerce date to local timezone */ +#define FT_GMTDATE 66 /* Coerce date to gmt */ /* pre-format processing */ -#define FT_PARSEDATE 66 /* parse comp into a date (tws) struct */ -#define FT_PARSEADDR 67 /* parse comp into a mailaddr struct */ -#define FT_FORMATADDR 68 /* let external routine format addr */ -#define FT_CONCATADDR 69 /* formataddr w/out duplicate removal */ -#define FT_MYMBOX 70 /* do "mymbox" test on comp */ +#define FT_PARSEDATE 67 /* parse comp into a date (tws) struct */ +#define FT_PARSEADDR 68 /* parse comp into a mailaddr struct */ +#define FT_FORMATADDR 69 /* let external routine format addr */ +#define FT_CONCATADDR 70 /* formataddr w/out duplicate removal */ +#define FT_MYMBOX 71 /* do "mymbox" test on comp */ /* misc. */ /* ADDTOSEQ only works if you include "options LBL" */ -#define FT_ADDTOSEQ 71 /* add current msg to a sequence */ +#define FT_ADDTOSEQ 72 /* add current msg to a sequence */ /* conditionals & control flow (must be last) */ -#define FT_SAVESTR 72 /* save current str reg */ -#define FT_DONE 73 /* stop formatting */ -#define FT_PAUSE 74 /* pause */ -#define FT_NOP 75 /* nop */ -#define FT_GOTO 76 /* (relative) goto */ -#define FT_IF_S_NULL 77 /* test if "str" null */ -#define FT_IF_S 78 /* test if "str" non-null */ -#define FT_IF_V_EQ 79 /* test if "value" = literal */ -#define FT_IF_V_NE 80 /* test if "value" != literal */ -#define FT_IF_V_GT 81 /* test if "value" > literal */ -#define FT_IF_MATCH 82 /* test if "str" contains literal */ -#define FT_IF_AMATCH 83 /* test if "str" starts with literal */ -#define FT_S_NULL 84 /* V = 1 if "str" null */ -#define FT_S_NONNULL 85 /* V = 1 if "str" non-null */ -#define FT_V_EQ 86 /* V = 1 if "value" = literal */ -#define FT_V_NE 87 /* V = 1 if "value" != literal */ -#define FT_V_GT 88 /* V = 1 if "value" > literal */ -#define FT_V_MATCH 89 /* V = 1 if "str" contains literal */ -#define FT_V_AMATCH 90 /* V = 1 if "str" starts with literal */ +#define FT_SAVESTR 73 /* save current str reg */ +#define FT_DONE 74 /* stop formatting */ +#define FT_PAUSE 75 /* pause */ +#define FT_NOP 76 /* nop */ +#define FT_GOTO 77 /* (relative) goto */ +#define FT_IF_S_NULL 78 /* test if "str" null */ +#define FT_IF_S 79 /* test if "str" non-null */ +#define FT_IF_V_EQ 80 /* test if "value" = literal */ +#define FT_IF_V_NE 81 /* test if "value" != literal */ +#define FT_IF_V_GT 82 /* test if "value" > literal */ +#define FT_IF_MATCH 83 /* test if "str" contains literal */ +#define FT_IF_AMATCH 84 /* test if "str" starts with literal */ +#define FT_S_NULL 85 /* V = 1 if "str" null */ +#define FT_S_NONNULL 86 /* V = 1 if "str" non-null */ +#define FT_V_EQ 87 /* V = 1 if "value" = literal */ +#define FT_V_NE 88 /* V = 1 if "value" != literal */ +#define FT_V_GT 89 /* V = 1 if "value" > literal */ +#define FT_V_MATCH 90 /* V = 1 if "str" contains literal */ +#define FT_V_AMATCH 91 /* V = 1 if "str" starts with literal */ #define IF_FUNCS FT_S_NULL /* start of "if" functions */ diff --git a/man/mh-format.man b/man/mh-format.man index 7aad129..6d84c97 100644 --- a/man/mh-format.man +++ b/man/mh-format.man @@ -1,4 +1,4 @@ -.TH MH-FORMAT %manext5% "March 24, 2012" "%nmhversion%" +.TH MH-FORMAT %manext5% "November 4, 2012" "%nmhversion%" .\" .\" %nmhwarning% .\" @@ -296,7 +296,8 @@ putnum expr print \fInum\fR putnumf expr print \fInum\fR in a fixed width .\" addtoseq literal add msg to sequence (LBL option) putlit expr print \fIstr\fR without space compression -nodate string integer Argument not a date string (0 or 1) +zputlit expr print \fIstr\fR without space compression; + \fIstr\fR must occupy no width on display formataddr expr append \fIarg\fR to \fIstr\fR as a (comma separated) address list concataddr expr append \fIarg\fR to \fIstr\fR as a @@ -424,8 +425,11 @@ the left up to the field width. The functions (\fIputnum\fR\^) and (\fIputstr\fR\^) are somewhat special: they print their result in the minimum number of characters required, and ignore any leading field width argument. The (\fIputlit\fR\^) -function outputs the exact contents of str register without any changes +function outputs the exact contents of the str register without any changes such as duplicate space removal or control character conversion. +The (\fIzputlit\fR\^) similarly outputs the exact contents of the str +register, but requires that those contents not occupy any output width. +It can therefore be used for outputting terminal escape sequences. .PP The available output width is kept in an internal register; any output past this width will be truncated. diff --git a/man/scan.man b/man/scan.man index 7bca7d2..95cf428 100644 --- a/man/scan.man +++ b/man/scan.man @@ -1,4 +1,4 @@ -.TH SCAN %manext1% "March 6, 2012" "%nmhversion%" +.TH SCAN %manext1% "November 4, 2012" "%nmhversion%" .\" .\" %nmhwarning% .\" @@ -189,6 +189,12 @@ modification of the message file itself. This feature is handy for scanning a draft folder, as message drafts usually aren't allowed to have dates in them. .PP +The +.B %etcdir% +directory contains several format files as examples of customized +.B scan +output. +.PP .B scan will update the .B nmh diff --git a/sbr/fmt_compile.c b/sbr/fmt_compile.c index ee495f1..1d03b21 100644 --- a/sbr/fmt_compile.c +++ b/sbr/fmt_compile.c @@ -35,6 +35,8 @@ * * - Add the code in fmt_scan.c to handle your new function. * + * - Add code to fmtdump.c to display your new function. + * * - Document the new function in the mh-format(5) man page. * */ @@ -137,6 +139,7 @@ static struct ftable functable[] = { { "putnumf", TF_EXPR, FT_NUMF, 0, 0 }, { "putaddr", TF_STR, FT_PUTADDR, 0, 0 }, { "putlit", TF_STR, FT_STRLIT, 0, 0 }, + { "zputlit", TF_STR, FT_STRLITZ, 0, 0 }, { "void", TF_NOP, 0, 0, 0 }, { "comp", TF_COMP, FT_LS_COMP, 0, TFL_PUTS }, diff --git a/sbr/fmt_scan.c b/sbr/fmt_scan.c index 51ba24c..3818b85 100644 --- a/sbr/fmt_scan.c +++ b/sbr/fmt_scan.c @@ -409,6 +409,25 @@ fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat) while ((c = *sp++) && cp < ep) *cp++ = c; break; + case FT_STRLITZ: { + size_t len = strlen (str); + + /* Don't want to emit part of an escape sequence. So if + there isn't enough room in the buffer for the entire + string, skip it completely. */ + if (cp - scanl + len + 1 < max) { + for (sp = str; *sp; *cp++ = *sp++) continue; + + /* This string doesn't count against the width. So + increase ep the same amount as cp, only if the + scan buffer will always be large enough. */ + if (ep - scanl + len + 1 < max) { + ep += len; + } + } + + break; + } case FT_STRFW: adios (NULL, "internal error (FT_STRFW)"); @@ -976,6 +995,22 @@ fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat) fmt++; } #ifndef JLR + /* Emit any trailing sequences of zero display length. */ + while (fmt->f_type != FT_DONE) { + if (fmt->f_type == FT_LS_LIT) { + str = fmt->f_text; + } else if (fmt->f_type == FT_STRLITZ) { + /* Don't want to emit part of an escape sequence. So if + there isn't enough room in the buffer for the entire + string, skip it completely. Need room for null + terminator, and maybe trailing newline (added below). */ + if (cp - scanl + strlen (str) + 1 < max) { + for (sp = str; *sp; *cp++ = *sp++) continue; + } + } + fmt++; + } + finished:; if (cp > scanl && cp[-1] != '\n') { if (cp - scanl < (int) max - 1) { diff --git a/test/common.sh.in b/test/common.sh.in index a81e156..a25409b 100644 --- a/test/common.sh.in +++ b/test/common.sh.in @@ -193,8 +193,8 @@ EOF for f in MailAliases components digestcomps distcomps forwcomps mhl.body \ mhl.digest mhl.format mhl.forward mhl.headers mhl.reply \ mhn.defaults rcvdistcomps replcomps replgroupcomps scan.MMDDYY \ - scan.YYYYMMDD scan.default scan.mailx scan.nomime scan.size \ - scan.time scan.timely scan.unseen + scan.YYYYMMDD scan.default scan.highlighted scan.mailx scan.nomime \ + scan.size scan.time scan.timely scan.unseen do cp "${MH_INST_DIR}${sysconfdir}/${f}" "${MH_TEST_DIR}/Mail" || exit 1 done diff --git a/test/scan/test-scan b/test/scan/test-scan index 1c6d949..fb69e29 100755 --- a/test/scan/test-scan +++ b/test/scan/test-scan @@ -30,8 +30,30 @@ cat > $expected <> EOF -scan -width 80 +inbox > $actual || exit 1 +scan +inbox -width 80 > $actual || exit 1 check "$expected" "$actual" +# check highlighting +cat > $expected <> + 2 09/29 Test2 Testing message 2<> + 3 09/29 Test3 Testing message 3<> + 4 09/29 Test4 Testing message 4<> + 5+ 09/29 Test5 Testing message 5<> + 6 09/29 Test6 Testing message 6<> + 7 09/29 Test7 Testing message 7<> + 8 09/29 Test8 Testing message 8<> + 9 09/29 Test9 Testing message 9<> + 10 09/29 Test10 Testing message 10<> +EOF + +printf 'Unseen-Sequence: unseen\n' >> $MH +mark -sequence cur 5 +mark -sequence unseen 10 +scan -form scan.highlighted -width 80 > $actual || exit 1 + +check "$expected" "$actual" + + exit $failed diff --git a/uip/fmtdump.c b/uip/fmtdump.c index 0b3283d..d69fb8c 100644 --- a/uip/fmtdump.c +++ b/uip/fmtdump.c @@ -355,6 +355,7 @@ f_typestr(int t) case FT_STRFW: return("STRFW"); case FT_PUTADDR: return("PUTADDR"); case FT_STRLIT: return("STRLIT"); + case FT_STRLITZ: return("STRLITZ"); case FT_LS_COMP: return("LS_COMP"); case FT_LS_LIT: return("LS_LIT"); case FT_LS_GETENV: return("LS_GETENV"); -- 1.7.10.4