Create two new mh-format functions: %(putlit) and %(concataddr).
authorKen Hornstein <kenh@pobox.com>
Fri, 13 Jan 2012 02:07:33 +0000 (21:07 -0500)
committerKen Hornstein <kenh@pobox.com>
Fri, 13 Jan 2012 02:07:33 +0000 (21:07 -0500)
%(putlit) outputs a string without any space compression or control character
conversion

%(concataddr) is just like %(formataddr), except that inside of repl there
will not be any duplicate address supression.

h/fmt_compile.h
man/mh-format.man
sbr/fmt_addr.c
sbr/fmt_compile.c
sbr/fmt_scan.c
uip/fmtdump.c
uip/replsbr.c

index 22be450..3cbae02 100644 (file)
 #define FT_STR         8       /* "str" as text                           */
 #define FT_STRF                9       /* "str" as text, filled                   */
 #define FT_STRFW       10      /* "str" as text, filled, width in "value" */
-#define FT_PUTADDR     11      /* split and print address line            */
+#define FT_STRLIT      11      /* "str" as text, no space compression     */
+#define FT_PUTADDR     12      /* split and print address line            */
 
 /* types that modify the "str" or "value" registers                     */
-#define FT_LS_COMP     12      /* set "str" to component text          */
-#define FT_LS_LIT      13      /* set "str" to literal text            */
-#define FT_LS_GETENV   14      /* set "str" to getenv(text)            */
-#define FT_LS_CFIND    15      /* set "str" to context_find(text)      */
-#define FT_LS_DECODECOMP 16    /* set "str" to decoded component text  */
-#define FT_LS_DECODE   17      /* decode "str" as RFC-2047 header      */
-#define FT_LS_TRIM     18      /* trim trailing white space from "str" */
-#define FT_LV_COMP     19      /* set "value" to comp (as dec. num)    */
-#define FT_LV_COMPFLAG 20      /* set "value" to comp flag word        */
-#define FT_LV_LIT      21      /* set "value" to literal num           */
-#define FT_LV_DAT      22      /* set "value" to dat[n]                */
-#define FT_LV_STRLEN   23      /* set "value" to length of "str"       */
-#define FT_LV_PLUS_L   24      /* set "value" += literal               */
-#define FT_LV_MINUS_L  25      /* set "value" -= literal               */
-#define FT_LV_DIVIDE_L 26      /* set "value" to value / literal       */
-#define FT_LV_MODULO_L 27      /* set "value" to value % literal       */
-#define FT_LV_CHAR_LEFT 28     /* set "value" to char left in output   */
+#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_MONTH    29      /* set "str" to tws month                   */
-#define FT_LS_LMONTH   30      /* set "str" to long tws month              */
-#define FT_LS_ZONE     31      /* set "str" to tws timezone                */
-#define FT_LS_DAY      32      /* set "str" to tws weekday                 */
-#define FT_LS_WEEKDAY  33      /* set "str" to long tws weekday            */
-#define FT_LS_822DATE  34      /* set "str" to 822 date str                */
-#define FT_LS_PRETTY   35      /* set "str" to pretty (?) date str         */
-#define FT_LV_SEC      36      /* set "value" to tws second                */
-#define FT_LV_MIN      37      /* set "value" to tws minute                */
-#define FT_LV_HOUR     38      /* set "value" to tws hour                  */
-#define FT_LV_MDAY     39      /* set "value" to tws day of month          */
-#define FT_LV_MON      40      /* set "value" to tws month                 */
-#define FT_LV_YEAR     41      /* set "value" to tws year                  */
-#define FT_LV_YDAY     42      /* set "value" to tws day of year           */
-#define FT_LV_WDAY     43      /* set "value" to tws weekday               */
-#define FT_LV_ZONE     44      /* set "value" to tws timezone              */
-#define FT_LV_CLOCK    45      /* set "value" to tws clock                 */
-#define FT_LV_RCLOCK   46      /* set "value" to now - tws clock           */
-#define FT_LV_DAYF     47      /* set "value" to tws day flag              */
-#define FT_LV_DST      48      /* set "value" to tws daylight savings flag */
-#define FT_LV_ZONEF    49      /* set "value" to tws timezone flag         */
+#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_PERS     50      /* set "str" to person part of addr    */
-#define FT_LS_MBOX     51      /* set "str" to mbox part of addr      */
-#define FT_LS_HOST     52      /* set "str" to host part of addr      */
-#define FT_LS_PATH     53      /* set "str" to route part of addr     */
-#define FT_LS_GNAME    54      /* set "str" to group part of addr     */
-#define FT_LS_NOTE     55      /* set "str" to comment part of addr   */
-#define FT_LS_ADDR     56      /* set "str" to mbox@host              */
-#define FT_LS_822ADDR  57      /* set "str" to 822 format addr        */
-#define FT_LS_FRIENDLY 58      /* set "str" to "friendly" format addr */
-#define FT_LV_HOSTTYPE 59      /* set "value" to addr host type       */
-#define FT_LV_INGRPF   60      /* set "value" to addr in-group flag   */
-#define FT_LS_UNQUOTE  61      /* remove RFC 2822 quotes from "str"   */
-#define FT_LV_NOHOSTF  62      /* set "value" to addr no-host 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 */
 
 /* Date Coercion */
-#define FT_LOCALDATE   63      /* Coerce date to local timezone */
-#define FT_GMTDATE     64      /* Coerce date to gmt            */
+#define FT_LOCALDATE   64      /* Coerce date to local timezone */
+#define FT_GMTDATE     65      /* Coerce date to gmt            */
 
 /* pre-format processing */
-#define FT_PARSEDATE   65      /* parse comp into a date (tws) struct */
-#define FT_PARSEADDR   66      /* parse comp into a mailaddr struct   */
-#define FT_FORMATADDR  67      /* let external routine format addr    */
-#define FT_MYMBOX      68      /* do "mymbox" test on comp            */
+#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            */
 
 /* misc. */            /* ADDTOSEQ only works if you include "options LBL" */
-#define FT_ADDTOSEQ    69      /* add current msg to a sequence       */
+#define FT_ADDTOSEQ    71      /* add current msg to a sequence       */
 
 /* conditionals & control flow (must be last) */
-#define FT_SAVESTR     70      /* save current str reg               */
-#define FT_DONE                71      /* stop formatting                    */
-#define FT_PAUSE       72      /* pause                              */
-#define FT_NOP         73      /* nop                                */
-#define FT_GOTO                74      /* (relative) goto                    */
-#define FT_IF_S_NULL   75      /* test if "str" null                 */
-#define FT_IF_S                76      /* test if "str" non-null             */
-#define FT_IF_V_EQ     77      /* test if "value" = literal          */
-#define FT_IF_V_NE     78      /* test if "value" != literal         */
-#define FT_IF_V_GT     79      /* test if "value" > literal          */
-#define FT_IF_MATCH    80      /* test if "str" contains literal     */
-#define FT_IF_AMATCH   81      /* test if "str" starts with literal  */
-#define FT_S_NULL      82      /* V = 1 if "str" null                */
-#define FT_S_NONNULL   83      /* V = 1 if "str" non-null            */
-#define FT_V_EQ                84      /* V = 1 if "value" = literal         */
-#define FT_V_NE                85      /* V = 1 if "value" != literal        */
-#define FT_V_GT                86      /* V = 1 if "value" > literal         */
-#define FT_V_MATCH     87      /* V = 1 if "str" contains literal    */
-#define FT_V_AMATCH    88      /* V = 1 if "str" starts with literal */
+#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 IF_FUNCS FT_S_NULL     /* start of "if" functions */
index 8925b8c..c50e02f 100644 (file)
@@ -1,5 +1,5 @@
 .\"
-.\" THIS FILE HAS BEEN AUTOMATICALLY GENERATED.  DO NOT EDIT.
+.\" %nmhwarning%
 .\"
 .TH MH-FORMAT %manext5% "%nmhdate%" MH.6.8 [%nmhversion%]
 .SH NAME
@@ -288,9 +288,14 @@ putstrf    expr            print \fIstr\fR in a fixed width
 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)
 formataddr     expr            append \fIarg\fR to \fIstr\fR as a
                        (comma separated) address list
+concataddr     expr            append \fIarg\fR to \fIstr\fR as a
+                       (comma separated) address list,
+                       including duplicates,
+                       see Special Handling
 putaddr        literal         print \fIstr\fR address list with
                        \fIarg\fR as optional label;
                        get line width from \fInum\fR
@@ -387,10 +392,24 @@ right-justification of the string within the field, with padding on
 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.
+required, and ignore any leading field width argument.  The (\fIputlit\fR\^)
+function outputs the exact contents of str register without any changes
+such as duplicate space removal or control character conversion.
 .PP
 The available output width is kept in an internal register; any output
 past this width will be truncated.
+.SS Special Handling
+A few functions have different behavior depending on what command they are
+being invoked from.
+.PP
+In
+.BR repl
+the (\fIformataddr\fR\^) function stores all email addresses encountered into
+an internal cache and will use this cache to suppress duplicate addresses.
+If you need to create an address list that includes previously-seen
+addresses you may use the (\fIconcataddr\fR\^) function, which is identical
+to (\fIformataddr\fR\^) in all other respects.  Note that (\fIconcataddr\fR\^)
+will NOT add addresses to the duplicate-suppression cache.
 .SS Examples
 With all this in mind,
 here's the default format string for
index be9b483..3186dea 100644 (file)
@@ -112,3 +112,8 @@ formataddr (char *orig, char *str)
     last_dst = dst;
     return (buf);
 }
+
+char *concataddr (char *orig, char *str)
+{
+    return formataddr(orig, str);
+}
index a86661e..8368728 100644 (file)
@@ -75,6 +75,7 @@ static struct ftable functable[] = {
      { "putnum",     TF_EXPR,  FT_NUM,         0,              0 },
      { "putnumf",    TF_EXPR,  FT_NUMF,        0,              0 },
      { "putaddr",    TF_STR,   FT_PUTADDR,     0,              0 },
+     { "putlit",     TF_STR,   FT_STRLIT,      0,              0 },
      { "void",       TF_NOP,   0,              0,              0 },
 
      { "comp",       TF_COMP,  FT_LS_COMP,     0,              TFL_PUTS },
@@ -139,6 +140,7 @@ static struct ftable functable[] = {
      { "ingrp",      TF_COMP,  FT_LV_INGRPF,   FT_PARSEADDR,   TFL_PUTN },
      { "nohost",     TF_COMP,  FT_LV_NOHOSTF,  FT_PARSEADDR,   TFL_PUTN },
      { "formataddr", TF_EXPR_SV,FT_FORMATADDR, FT_FORMATADDR,  0 },
+     { "concataddr", TF_EXPR_SV,FT_CONCATADDR, FT_FORMATADDR,  0 },
      { "friendly",   TF_COMP,  FT_LS_FRIENDLY, FT_PARSEADDR,   TFL_PUTS },
 
      { "mymbox",     TF_COMP,  FT_LV_COMPFLAG, FT_MYMBOX,      TFL_PUTN },
index 2bf1821..5240781 100644 (file)
@@ -23,6 +23,7 @@
 #endif
 
 extern char *formataddr ();    /* hook for custom address formatting */
+extern char *concataddr ();    /* address formatting but allowing duplicates */
 
 #ifdef LBL
 struct msgs *fmt_current_folder; /* current folder (set by main program) */
@@ -347,6 +348,11 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
        case FT_STRF:
            cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp);
            break;
+       case FT_STRLIT:
+           sp = str;
+           while ((c = *sp++) && cp < ep)
+               *cp++ = c;
+           break;
        case FT_STRFW:
            adios (NULL, "internal error (FT_STRFW)");
 
@@ -786,6 +792,11 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            str = formataddr (savestr, str);
            break;
 
+       case FT_CONCATADDR:
+           /* The same as formataddr, but doesn't do duplicate suppression */
+           str = concataddr (savestr, str);
+           break;
+
        case FT_PUTADDR:
            /* output the str register as an address component,
             * splitting it into multiple lines if necessary.  The
index 2abf05e..6566317 100644 (file)
@@ -354,6 +354,7 @@ f_typestr(int t)
        case FT_STRF: return("STRF");
        case FT_STRFW: return("STRFW");
        case FT_PUTADDR: return("PUTADDR");
+       case FT_STRLIT: return("STRLIT");
        case FT_LS_COMP: return("LS_COMP");
        case FT_LS_LIT: return("LS_LIT");
        case FT_LS_GETENV: return("LS_GETENV");
@@ -408,6 +409,7 @@ f_typestr(int t)
        case FT_PARSEDATE: return("PARSEDATE");
        case FT_PARSEADDR: return("PARSEADDR");
        case FT_FORMATADDR: return("FORMATADDR");
+       case FT_CONCATADDR: return("CONCATADDR");
        case FT_MYMBOX: return("MYMBOX");
 #ifdef FT_ADDTOSEQ
        case FT_ADDTOSEQ: return("ADDTOSEQ");
index 3dda470..9ca0bb9 100644 (file)
@@ -25,6 +25,7 @@ static char *badaddrs = NULL;
 static char *dfhost = NULL;
 
 static struct mailname mq = { NULL };
+static int nodupcheck = 0;             /* If set, no check for duplicates */
 
 /*
  * Buffer size for content part of header fields.
@@ -380,12 +381,38 @@ formataddr (char *orig, char *str)
 }
 
 
+/*
+ * fmt_scan will call this routine if the user includes the function
+ * "(concataddr {component})" in a format string.  This behaves exactly
+ * like formataddr, except that it does NOT suppress duplicate addresses
+ * between calls.
+ *
+ * As an implementation detail: I thought about splitting out formataddr()
+ * into the generic part and duplicate-suppressing part, but the call to
+ * insert() was buried deep within a couple of loops and I didn't see a
+ * way to do it easily.  So instead we simply set a special flag to stop
+ * the duplicate check and call formataddr().
+ */
+char *
+concataddr(char *orig, char *str)
+{
+    char *cp;
+
+    nodupcheck = 1;
+    cp = formataddr(orig, str);
+    nodupcheck = 0;
+    return cp;
+}
+
 static int
 insert (struct mailname *np)
 {
     char buffer[BUFSIZ];
     register struct mailname *mp;
 
+    if (nodupcheck)
+       return 1;
+
     if (np->m_mbox == NULL)
        return 0;