]> diplodocus.org Git - nmh/blobdiff - sbr/fmt_scan.c
Always check that mktemp()/mktemp2() succeeds before trying to
[nmh] / sbr / fmt_scan.c
index 630cfc5c2f4b4db49a75a1abab89043dcf349d7c..9b84861cb123b25f5e4cbc71e2e3494caa33f6c1 100644 (file)
@@ -2,25 +2,31 @@
 /*
  * fmt_scan.c -- format string interpretation
  *
- * $Id$
+ * This code is Copyright (c) 2002, by the authors of nmh.  See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
+ *
+ * This is the engine that processes the format instructions created by
+ * fmt_compile (found in fmt_compile.c).
  */
 
 #include <h/mh.h>
 #include <h/addrsbr.h>
 #include <h/fmt_scan.h>
-#include <zotnet/tws/tws.h>
+#include <h/tws.h>
 #include <h/fmt_compile.h>
 
-#define        NFMTS MAXARGS
-
-extern char *formataddr ();    /* hook for custom address formatting */
-
-#ifdef LBL
-struct msgs *fmt_current_folder; /* current folder (set by main program) */
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#include <time.h>
+#ifdef MULTIBYTE_SUPPORT
+#  include <wctype.h>
+#  include <wchar.h>
 #endif
 
-extern int fmt_norm;           /* defined in sbr/fmt_def.c = AD_NAME */
-struct mailname fmt_mnull;
+struct mailname fmt_mnull = { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
+                             NULL, NULL };
 
 /*
  * static prototypes
@@ -43,13 +49,24 @@ match (char *str, char *sub)
 
 #ifdef LOCALE
     while ((c1 = *sub)) {
-       c1 = (isalpha(c1) && isupper(c1)) ? tolower(c1) : c1;
-       while ((c2 = *str++) && c1 != ((isalpha(c2) && isupper(c2)) ? tolower(c2) : c2))
+       c1 = (isascii((unsigned char) c1) && isalpha((unsigned char) c1) &&
+             isupper((unsigned char) c1)) ? tolower((unsigned char) c1) : c1;
+       while ((c2 = *str++) && c1 != ((isascii((unsigned char) c2) &&
+                                       isalpha((unsigned char) c2) &&
+                                       isupper((unsigned char) c2)) ?
+                                           tolower((unsigned char) c2) : c2))
            ;
        if (! c2)
            return 0;
        s1 = sub + 1; s2 = str;
-       while ((c1 = *s1++) && ((isalpha(c1) && isupper(c1)) ? tolower(c1) : c1) == ((isalpha(c2 =*s2++) && isupper(c2)) ? tolower(c2) : c2))
+       while ((c1 = *s1++) && ((isascii((unsigned char) c1) &&
+                                isalpha((unsigned char) c1) &&
+                                isupper((unsigned char) c1)) ?
+                                       tolower(c1) : c1) ==
+                       ((isascii((unsigned char) (c2 =*s2++)) &&
+                         isalpha((unsigned char) c2) &&
+                         isupper((unsigned char) c2)) ?
+                               tolower((unsigned char) c2) : c2))
            ;
        if (! c1)
            return 1;
@@ -71,151 +88,246 @@ match (char *str, char *sub)
 }
 
 /*
- * macros to format data
+ * copy a number to the destination subject to a maximum width
  */
+static void
+cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
+    int i, c;
+    char *sp;
+    char *cp = *dest;
+    char *ep = cp + n;
+
+    if (cp + wid < ep) {
+       if ((i = (num)) < 0)
+           i = -(num);
+       if ((c = (wid)) < 0)
+           c = -c;
+       sp = cp + c;
+       do {
+           *--sp = (i % 10) + '0';
+           i /= 10;
+       } while (i > 0 && sp > cp);
+       if (i > 0)
+           *sp = '?';
+       else if ((num) < 0 && sp > cp)
+           *--sp = '-';
+       while (sp > cp)
+           *--sp = fill;
+       cp += c;
+    }
+    *dest = cp;
+}
+
+/*
+ * copy string from str to dest padding with the fill character to a size
+ * of wid characters. if wid is negative, the string is right aligned
+ * no more than n bytes are copied
+ */
+static void
+cptrimmed(char **dest, char **ep, char *str, unsigned int wid, char fill,
+         char *epmax) {
+    int remaining;     /* remaining output width available */
+    int c, ljust;
+    int end;           /* number of input bytes remaining in str */
+#ifdef MULTIBYTE_SUPPORT
+    int char_len;      /* bytes in current character */
+    int w;
+    wchar_t wide_char;
+    char *altstr = NULL;
+#endif
+    char *sp;          /* current position in source string */
+    char *cp = *dest;  /* current position in destination string */
+    int prevCtrl = 1;
+
+    /* get alignment */
+    ljust = 0;
+    if ((remaining = (wid)) < 0) {
+       remaining = -remaining;
+       ljust++;
+    }
+    if ((sp = (str))) {
+#ifdef MULTIBYTE_SUPPORT
+       mbtowc(NULL, NULL, 0); /* reset shift state */
+#endif
+       end = strlen(str);
+       while (*sp && remaining > 0 && end > 0) {
+#ifdef MULTIBYTE_SUPPORT
+           char_len = mbtowc(&wide_char, sp, end);
+
+           /*
+            * See the relevant comments in cpstripped() to explain what's
+            * going on here; we want to handle the case where we get
+            * characters that mbtowc() cannot handle
+            */
 
-#define PUTDF(cp, num, wid, fill)\
-       if (cp + wid < ep) {\
-           if ((i = (num)) < 0)\
-               i = -(num);\
-           if ((c = (wid)) < 0)\
-               c = -c;\
-           sp = cp + c;\
-           do {\
-               *--sp = (i % 10) + '0';\
-               i /= 10;\
-           } while (i > 0 && sp > cp);\
-           if (i > 0)\
-               *sp = '?';\
-           else if ((num) < 0 && sp > cp)\
-               *--sp = '-';\
-           while (sp > cp)\
-               *--sp = fill;\
-           cp += c;\
+           if (char_len < 0) {
+               altstr = "?";
+               char_len = mbtowc(&wide_char, altstr, 1);
            }
 
-#define PUTD(cp, num)\
-       if (cp < ep) {\
-           if ((i = (num)) == 0)\
-               *cp++ = '0';\
-           else {\
-               if ((i = (num)) < 0) {\
-                   *cp++ = '-';\
-                   i = -(num);\
-                   }\
-               c = 10;\
-               while (c <= i) \
-                   c *= 10;\
-               while (cp < ep && c > 1) {\
-                   c /= 10;\
-                   *cp++ = (i / c) + '0';\
-                   i %= c;\
-                   }\
-               }\
+           if (char_len <= 0)
+               break;
+
+           w = wcwidth(wide_char);
+
+           /*
+            * Multibyte characters can have a variable number of column
+            * widths, so use the column width to bump the end pointer when
+            * appropriate.
+            */
+           if (w >= 0  &&  char_len > 1 && epmax - *ep >= char_len - w) {
+               *ep += char_len - w;
            }
 
-#ifdef LOCALE
-#define PUTSF(cp, str, wid, fill) {\
-               ljust = 0;\
-               if ((i = (wid)) < 0) {\
-                       i = -i;\
-                       ljust++;\
-               }\
-               if ((sp = (str))) {\
-                       if (ljust) {\
-                               c = strlen(sp);\
-                               if (c > i)\
-                                       sp += c - i;\
-                               else {\
-                                       while( --i >= c && cp < ep)\
-                                               *cp++ = fill;\
-                                       i++;\
-                               }\
-                       } else {\
-                           while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                               sp++;\
-                       }\
-                       while ((c = (unsigned char) *sp++) && --i >= 0 && cp < ep)\
-                               if (isgraph(c)) \
-                                   *cp++ = c;\
-                               else {\
-                                       while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                                               sp++;\
-                                           *cp++ = ' ';\
-                               }\
-               }\
-               if (!ljust)\
-               while( --i >= 0 && cp < ep)\
-                   *cp++ = fill;\
+           if (w >= 0  &&  cp + w > *ep)
+               break;
+
+           end -= char_len;
+
+           if (iswcntrl(wide_char) || iswspace(wide_char)) {
+               sp += char_len;
+#else
+            int c;
+           end--;
+            /* isnctrl(), etc., take an int argument.  Cygwin's ctype.h
+               intentionally warns if they are passed a char. */
+            c = (unsigned char) *sp;
+           if (iscntrl(c) || isspace(c)) {
+               sp++;
+#endif
+               if (!prevCtrl) {
+                   *cp++ = ' ';
+                   remaining--;
+               }
+
+               prevCtrl = 1;
+               continue;
+           }
+           prevCtrl = 0;
+
+#ifdef MULTIBYTE_SUPPORT
+           if (w >= 0 && remaining >= w) {
+               strncpy(cp, altstr ? altstr : sp, char_len);
+               cp += char_len;
+               remaining -= w;
+               altstr = NULL;
+           }
+           sp += char_len;
+#else
+           *cp++ = *sp++;
+           remaining--;
+#endif
+       }
+    }
+
+    if (ljust) {
+        char *endfield;
+       if (cp + remaining > *ep)
+           remaining = *ep - cp;
+       endfield = cp + remaining;
+       if (remaining > 0) {
+           /* copy string to the right */
+           while (--cp >= *dest)
+               *(cp + remaining) = *cp;
+           /* add padding at the beginning */
+           cp += remaining;
+           for (c=remaining; c>0; c--)
+               *cp-- = fill;
        }
+       *dest = endfield;
+    } else {
+       /* pad remaining space */
+       while (remaining-- > 0 && cp < *ep)
+               *cp++ = fill;
+       *dest = cp;
+    }
+}
 
-#define PUTS(cp, str) {\
-               if ((sp = (str))) {\
-                   while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                       sp++;\
-                   while((c = (unsigned char) *sp++) && cp < ep)\
-                       if (isgraph(c)) \
-                           *cp++ = c;\
-                       else {\
-                           while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                               sp++;\
-                           *cp++ = ' ';\
-                       }\
-               }\
+static void
+cpstripped (char **dest, char **end, char *max, char *str)
+{
+    int prevCtrl = 1;  /* This is 1 so we strip out leading spaces */
+    int len;
+#ifdef MULTIBYTE_SUPPORT
+    int char_len, w;
+    wchar_t wide_char;
+    char *altstr = NULL;
+#endif /* MULTIBYTE_SUPPORT */
+
+    if (!str)
+       return;
+
+    len = strlen(str);
+
+#ifdef MULTIBYTE_SUPPORT
+    mbtowc(NULL, NULL, 0);  /* Reset shift state */
+#endif /* MULTIBYTE_SUPPORT */
+
+    /*
+     * Process each character at a time; if we have multibyte support
+     * then deal with that here.
+     */
+
+    while (*str != '\0' && len > 0 && *dest < *end) {
+#ifdef MULTIBYTE_SUPPORT
+       char_len = mbtowc(&wide_char, str, len);
+       w = wcwidth(wide_char);
+
+       /*
+        * Account for multibyte characters, and increment the end pointer
+        * by the number of "extra" bytes in this character.  That's the
+        * character length (char_len) minus the column width (w).
+        */
+       if (w >= 0  &&  char_len > 1  &&  max - *end >= char_len - w) {
+           *end += char_len - w;
        }
 
-#else /* LOCALE */
-#define PUTSF(cp, str, wid, fill) {\
-               ljust = 0;\
-               if ((i = (wid)) < 0) {\
-                       i = -i;\
-                       ljust++;\
-               }\
-               if (sp = (str)) {\
-                       if (ljust) {\
-                               c = strlen(sp);\
-                               if (c > i)\
-                                       sp += c - i;\
-                               else {\
-                                       while( --i >= c && cp < ep)\
-                                               *cp++ = fill;\
-                                       i++;\
-                               }\
-                       } else {\
-                   while ((c = *sp) && c <= 32)\
-                       sp++;\
-                       }\
-                       while ((c = *sp++) && --i >= 0 && cp < ep)\
-                               if (c > 32) \
-                           *cp++ = c;\
-                       else {\
-                                       while ((c = *sp) && c <= 32)\
-                               sp++;\
-                           *cp++ = ' ';\
-                       }\
-               }\
-               if (!ljust)\
-               while( --i >= 0 && cp < ep)\
-                   *cp++ = fill;\
-               }
+       /*
+        * If mbrtowc() failed, then we have a character that isn't valid
+        * in the current encoding.  Replace it with a '?'.  We do that by
+        * setting the alstr variable to the value of the replacement string;
+        * altstr is used below when the bytes are copied into the output
+        * buffer.
+        */
+
+       if (char_len < 0) {
+           altstr = "?";
+           char_len = mbtowc(&wide_char, altstr, 1);
+       }
 
-#define PUTS(cp, str) {\
-               if (sp = (str)) {\
-                   while ((c = *sp) && c <= 32)\
-                       sp++;\
-                   while( (c = *sp++) && cp < ep)\
-                       if ( c > 32 ) \
-                           *cp++ = c;\
-                       else {\
-                           while ( (c = *sp) && c <= 32 )\
-                               sp++;\
-                           *cp++ = ' ';\
-                       }\
-               }\
-               }
+       if (char_len <= 0 || *dest + char_len > *end)
+           break;
 
-#endif /* LOCALE */
+       len -= char_len;
 
+       if (iswcntrl(wide_char) || iswspace(wide_char)) {
+           str += char_len;
+#else /* MULTIBYTE_SUPPORT */
+       int c = (unsigned char) *str;
+       len--;
+       if (iscntrl(c) || isspace(c)) {
+           str++;
+#endif /* MULTIBYTE_SUPPORT */
+           if (! prevCtrl) {
+               *(*dest)++ = ' ';
+           }
+
+           prevCtrl = 1;
+           continue;
+       }
+
+       prevCtrl = 0;
+
+#ifdef MULTIBYTE_SUPPORT
+       memcpy(*dest, altstr ? altstr : str, char_len);
+       str += char_len;
+       *dest += char_len;
+       altstr = NULL;
+#else /* MULTIBYE_SUPPORT */
+       *(*dest)++ = *str++
+#endif /* MULTIBYTE_SUPPORT */
+    }
+}
 
 static char *lmonth[] = { "January",  "February","March",   "April",
                          "May",      "June",    "July",    "August",
@@ -234,7 +346,7 @@ get_x400_friendly (char *mbox, char *buffer, int buffer_len)
        return NULL;
 
     if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
-       for (mbox = buffer; mbox = strchr(mbox, '.'); )
+       for (mbox = buffer; (mbox = strchr(mbox, '.')); )
            *mbox++ = ' ';
 
        return buffer;
@@ -261,17 +373,18 @@ get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
            || !(cp = strchr(mbox += idx + strlen (key), '/')))
        return 0;
 
-    snprintf (buffer, buffer_len, "%*.*s", cp - mbox, cp - mbox, mbox);
+    snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
     return 1;
 }
 
 struct format *
-fmt_scan (struct format *format, char *scanl, int width, int *dat)
+fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat,
+         struct fmt_callbacks *callbacks)
 {
     char *cp, *ep, *sp;
-    char *savestr, *str = NULL;
+    char *savestr = NULL, *str = NULL;
     char buffer[BUFSIZ], buffer2[BUFSIZ];
-    int i, c, ljust;
+    int i, c, ljust, n;
     int value = 0;
     time_t t;
     struct format *fmt;
@@ -279,18 +392,52 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
     struct tws *tws;
     struct mailname *mn;
 
+    /* ep keeps track of displayed characters.  They're limited by width.
+       The total number of characters, cp - scanl + 1 (for trailing NULL),
+       includes invisible control characters and is limited by max. */
     cp = scanl;
-    ep = scanl + width - 1;
+    ep = scanl + (width <= (int) max ? width : (int) max) - 1;
+
+    for (fmt = format; fmt->f_type != FT_DONE; fmt++)
+       switch (fmt->f_type) {
+       case FT_PARSEADDR:
+       case FT_PARSEDATE:
+           fmt->f_comp->c_flags &= ~CF_PARSED;
+           break;
+       case FT_COMP:
+       case FT_COMPF:
+       case FT_LS_COMP:
+       case FT_LS_DECODECOMP:
+           /*
+            * Trim these components of any newlines.
+            *
+            * But don't trim the "body" and "text" components.
+            */
+
+           comp = fmt->f_comp;
+
+           if (! (comp->c_flags & CF_TRIMMED) && comp->c_text &&
+               (i = strlen(comp->c_text)) > 0) {
+               if (comp->c_text[i - 1] == '\n' &&
+                   strcmp(comp->c_name, "body") != 0 &&
+                   strcmp(comp->c_name, "text") != 0)
+                   comp->c_text[i - 1] = '\0';
+               comp->c_flags |= CF_TRIMMED;
+           }
+           break;
+       }
+
     fmt = format;
 
     while (cp < ep) {
        switch (fmt->f_type) {
 
        case FT_COMP:
-           PUTS (cp, fmt->f_comp->c_text);
+           cpstripped (&cp, &ep, scanl + max - 1, fmt->f_comp->c_text);
            break;
        case FT_COMPF:
-           PUTSF (cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill);
+           cptrimmed (&cp, &ep, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill,
+                      scanl + max - 1);
            break;
 
        case FT_LIT:
@@ -313,19 +460,52 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            break;
 
        case FT_STR:
-           PUTS (cp, str);
+           cpstripped (&cp, &ep, scanl + max - 1, str);
            break;
        case FT_STRF:
-           PUTSF (cp, str, fmt->f_width, fmt->f_fill);
+           cptrimmed (&cp, &ep, str, fmt->f_width, fmt->f_fill, 
+                      scanl + max - 1);
+           break;
+       case FT_STRLIT:
+           if (str) {
+               sp = str;
+               while ((c = *sp++) && cp < ep)
+                   *cp++ = c;
+           }
+           break;
+       case FT_STRLITZ:
+           if (str) {
+               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)");
 
        case FT_NUM:
-           PUTD (cp, value);
+           n = snprintf(cp, ep - cp + 1, "%d", value);
+           if (n >= 0) {
+               if (n >= ep - cp) {
+                   cp = ep;
+               } else
+                   cp += n;
+           }
            break;
        case FT_NUMF:
-           PUTDF (cp, value, fmt->f_width, fmt->f_fill);
+           cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
            break;
 
        case FT_CHAR:
@@ -333,10 +513,16 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            break;
 
        case FT_DONE:
+           if (callbacks && callbacks->trace_func)
+               callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                     str, scanl);
            goto finished;
 
        case FT_IF_S:
            if (!(value = (str && *str))) {
+               if (callbacks && callbacks->trace_func)
+                   callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                         str, scanl);
                fmt += fmt->f_skip;
                continue;
            }
@@ -344,6 +530,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
        case FT_IF_S_NULL:
            if (!(value = (str == NULL || *str == 0))) {
+               if (callbacks && callbacks->trace_func)
+                   callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                         str, scanl);
                fmt += fmt->f_skip;
                continue;
            }
@@ -351,6 +540,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
        case FT_IF_V_EQ:
            if (value != fmt->f_value) {
+               if (callbacks && callbacks->trace_func)
+                   callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                         str, scanl);
                fmt += fmt->f_skip;
                continue;
            }
@@ -358,6 +550,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
        case FT_IF_V_NE:
            if (value == fmt->f_value) {
+               if (callbacks && callbacks->trace_func)
+                   callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                         str, scanl);
                fmt += fmt->f_skip;
                continue;
            }
@@ -365,6 +560,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
        case FT_IF_V_GT:
            if (value <= fmt->f_value) {
+               if (callbacks && callbacks->trace_func)
+                   callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                         str, scanl);
                fmt += fmt->f_skip;
                continue;
            }
@@ -372,6 +570,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
        case FT_IF_MATCH:
            if (!(value = (str && match (str, fmt->f_text)))) {
+               if (callbacks && callbacks->trace_func)
+                   callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                         str, scanl);
                fmt += fmt->f_skip;
                continue;
            }
@@ -386,6 +587,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
        case FT_IF_AMATCH:
            if (!(value = (str && uprf (str, fmt->f_text)))) {
+               if (callbacks && callbacks->trace_func)
+                   callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                         str, scanl);
                fmt += fmt->f_skip;
                continue;
            }
@@ -416,6 +620,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            break;
 
        case FT_GOTO:
+           if (callbacks && callbacks->trace_func)
+               callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                     str, scanl);
            fmt += fmt->f_skip;
            continue;
 
@@ -438,14 +645,14 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            break;
 
        case FT_LS_DECODECOMP:
-           if (decode_rfc2047(fmt->f_comp->c_text, buffer2))
+           if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
                str = buffer2;
            else
                str = fmt->f_comp->c_text;
            break;
 
        case FT_LS_DECODE:
-           if (str && decode_rfc2047(str, buffer2))
+           if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
                str = buffer2;
            break;
 
@@ -454,8 +661,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                    char *xp;
 
                    strncpy(buffer, str, sizeof(buffer));
+                   buffer[sizeof(buffer)-1] = '\0';
                    str = buffer;
-                   while (isspace(*str))
+                   while (isspace((unsigned char) *str))
                            str++;
                    ljust = 0;
                    if ((i = fmt->f_width) < 0) {
@@ -463,19 +671,19 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                            ljust++;
                    }
 
-                   if (!ljust && i > 0 && strlen(str) > i)
+                   if (!ljust && i > 0 && (int) strlen(str) > i)
                            str[i] = '\0';
                    xp = str;
                    xp += strlen(str) - 1;
-                   while (xp > str && isspace(*xp))
+                   while (xp > str && isspace((unsigned char) *xp))
                            *xp-- = '\0';
-                   if (ljust && i > 0 && strlen(str) > i)
+                   if (ljust && i > 0 && (int) strlen(str) > i)
                        str += strlen(str) - i;
            }
            break;
 
        case FT_LV_COMPFLAG:
-           value = fmt->f_comp->c_flags;
+           value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
            break;
        case FT_LV_COMP:
            value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
@@ -487,7 +695,10 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            value = dat[fmt->f_value];
            break;
        case FT_LV_STRLEN:
-           value = strlen(str);
+           if (str != NULL)
+                   value = strlen(str);
+           else
+                   value = 0;
            break;
        case FT_LV_CHAR_LEFT:
            value = width - (cp - scanl);
@@ -636,9 +847,10 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            }
            if (fmt->f_type == FT_LS_ADDR)
                goto unfriendly;
-           if ((str = mn->m_pers) == NULL)
+           if ((str = mn->m_pers) == NULL) {
                if ((str = mn->m_note)) {
                    strncpy (buffer, str, sizeof(buffer));
+                   buffer[sizeof(buffer)-1] = '\0';
                    str = buffer;
                    if (*str == '(')
                        str++;
@@ -674,6 +886,19 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                        break;
                  }
                }
+           }
+           break;
+
+
+               /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
+       case FT_LS_UNQUOTE:
+           if (str) {          
+               strncpy(buffer, str, sizeof(buffer));
+               /* strncpy doesn't NUL-terminate if it fills the buffer */
+               buffer[sizeof(buffer)-1] = '\0';
+               unquote_string(buffer, buffer2);
+               str = buffer2;
+            }
            break;
 
        case FT_LOCALDATE:
@@ -694,18 +919,32 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
        case FT_PARSEDATE:
            comp = fmt->f_comp;
+           if (comp->c_flags & CF_PARSED)
+               break;
            if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
                *comp->c_tws = *tws;
-               comp->c_flags = 0;
-           } else if (comp->c_flags >= 0) {
+               comp->c_flags &= ~CF_TRUE;
+           } else if ((comp->c_flags & CF_DATEFAB) == 0) {
                memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws);
-               comp->c_flags = 1;
+               comp->c_flags = CF_TRUE;
            }
+           comp->c_flags |= CF_PARSED;
            break;
 
        case FT_FORMATADDR:
            /* hook for custom address list formatting (see replsbr.c) */
-           str = formataddr (savestr, str);
+           if (callbacks && callbacks->formataddr)
+               str = callbacks->formataddr (savestr, str);
+           else
+               str = formataddr (savestr, str);
+           break;
+
+       case FT_CONCATADDR:
+           /* The same as formataddr, but doesn't do duplicate suppression */
+           if (callbacks && callbacks->concataddr)
+               str = callbacks->concataddr (savestr, str);
+           else
+               str = concataddr (savestr, str);
            break;
 
        case FT_PUTADDR:
@@ -725,21 +964,26 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            sp = fmt->f_text;
            indent = strlen (sp);
            wid -= indent;
-           while( (c = *sp++) && cp < ep)
-               *cp++ = c;
+           if (wid <= 0) {
+               adios(NULL, "putaddr -- num register (%d) must be greater "
+                           "than label width (%d)", value, indent);
+           }
+           while( (c = (unsigned char) *sp++) && cp < ep)
+               *cp++ = (char) c;
            while (len > wid) {
                /* try to break at a comma; failing that, break at a
                 * space.
                 */
                lastb = 0; sp = lp + wid;
-               while (sp > lp && (c = *--sp) != ',') {
+               while (sp > lp && (c = (unsigned char) *--sp) != ',') {
                    if (! lastb && isspace(c))
                        lastb = sp - 1;
                }
                if (sp == lp) {
                    if (! (sp = lastb)) {
                        sp = lp + wid - 1;
-                       while (*sp && *sp != ',' && !isspace(*sp))
+                       while (*sp && *sp != ',' &&
+                                               !isspace((unsigned char) *sp))
                            sp++;
                        if (*sp != ',')
                            sp--;
@@ -748,10 +992,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                len -= sp - lp + 1;
                while (cp < ep && lp <= sp)
                    *cp++ = *lp++;
-               *cp++ = '\n';
-               for (i=indent; cp < ep && i > 0; i--)
-                   *cp++ = ' ';
-               while (isspace(*lp))
+               while (isspace((unsigned char) *lp))
                    lp++, len--;
                if (*lp) {
                    if (cp < ep)
@@ -760,26 +1001,29 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                        *cp++ = ' ';
                }
            }
-           PUTS (cp, lp);
+           cpstripped (&cp, &ep, scanl + max - 1, lp);
            }
            break;
 
        case FT_PARSEADDR:
            comp = fmt->f_comp;
+           if (comp->c_flags & CF_PARSED)
+               break;
            if (comp->c_mn != &fmt_mnull)
                mnfree (comp->c_mn);
            if ((sp = comp->c_text) && (sp = getname(sp)) &&
-               (mn = getm (sp, NULL, 0, fmt_norm, NULL))) {
+               (mn = getm (sp, NULL, 0, NULL, 0))) {
                comp->c_mn = mn;
                while (getname(""))
                    ;
+               comp->c_flags |= CF_PARSED;
            } else {
                while (getname(""))             /* XXX */
                    ;
                comp->c_mn = &fmt_mnull;
            }
            break;
-           
+
        case FT_MYMBOX:
            /*
             * if there's no component, we say true.  Otherwise we
@@ -790,49 +1034,69 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            if (comp->c_mn != &fmt_mnull)
                mnfree (comp->c_mn);
            if ((sp = comp->c_text) && (sp = getname(sp)) &&
-               (mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
+               (mn = getm (sp, NULL, 0, NULL, 0))) {
                comp->c_mn = mn;
-               comp->c_flags = ismymbox(mn);
+               if (ismymbox(mn))
+                   comp->c_flags |= CF_TRUE;
+               else
+                   comp->c_flags &= ~CF_TRUE;
                while ((sp = getname(sp)))
-                   if (comp->c_flags == 0 &&
-                       (mn = getm (sp, NULL, 0, AD_NAME, NULL)))
-                       comp->c_flags |= ismymbox(mn);
+                   if ((comp->c_flags & CF_TRUE) == 0 &&
+                       (mn = getm (sp, NULL, 0, NULL, 0)))
+                       if (ismymbox(mn))
+                           comp->c_flags |= CF_TRUE;
            } else {
                while (getname(""))             /* XXX */
                    ;
-               comp->c_flags = (comp->c_text == 0);
+               if (comp->c_text == 0)
+                   comp->c_flags |= CF_TRUE;
+               else
+                   comp->c_flags &= ~CF_TRUE;
                comp->c_mn = &fmt_mnull;
            }
            break;
-
-       case FT_ADDTOSEQ:
-#ifdef LBL
-           /* If we're working on a folder (as opposed to a file), add the
-            * current msg to sequence given in literal field.  Don't
-            * disturb string or value registers.
-            */
-           if (fmt_current_folder)
-                   seq_addmsg(fmt_current_folder, fmt->f_text, dat[0], -1);
-#endif
-           break;
        }
+
+       /*
+        * Call our tracing callback function, if one was supplied
+        */
+
+       if (callbacks && callbacks->trace_func)
+           callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                 str, scanl);
        fmt++;
     }
-#ifndef JLR
-    finished:;
-    if (cp[-1] != '\n')
-       *cp++ = '\n';
-    *cp   = 0;
-    return ((struct format *)0);
-#else /* JLR */
-    if (cp[-1] != '\n')
-       *cp++ = '\n';
-    while (fmt->f_type != FT_DONE)
+
+    /* 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;
+           if (callbacks && callbacks->trace_func)
+               callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                     str, scanl);
+       } 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 (str && (cp - scanl + strlen (str) + 1 < max)) {
+               for (sp = str; *sp; *cp++ = *sp++) continue;
+           }
+           if (callbacks && callbacks->trace_func)
+               callbacks->trace_func(callbacks->trace_context, fmt, value,
+                                     str, scanl);
+       }
        fmt++;
+    }
 
-    finished:;    
+    finished:;
+    if (cp > scanl  &&  cp[-1] != '\n') {
+       if (cp - scanl < (int) max - 1) {
+           *cp++ = '\n';
+       } else {
+           cp[-1] = '\n';
+       }
+    }
     *cp = '\0';
-    return (fmt->f_value ? ++fmt : (struct format *) 0);
-
-#endif /* JLR */
+    return ((struct format *)0);
 }