Added format support for zputlit function escape. It requires
[mmh] / sbr / fmt_scan.c
index ca4020b..3818b85 100644 (file)
@@ -2,11 +2,12 @@
 /*
  * 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/tws.h>
 #include <h/fmt_compile.h>
 
-#ifdef TIME_WITH_SYS_TIME
+#ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
-# include <time.h>
-#else
-# ifdef TM_IN_SYS_TIME
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
 #endif
+#include <time.h>
 #ifdef MULTIBYTE_SUPPORT
 #  include <wctype.h>
 #  include <wchar.h>
 #endif
 
-#define        NFMTS MAXARGS
-
-extern char *formataddr ();    /* hook for custom address formatting */
-
 #ifdef LBL
 struct msgs *fmt_current_folder; /* current folder (set by main program) */
 #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
@@ -128,10 +120,11 @@ cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
 static void
 cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
     int remaining;     /* remaining output width available */
-    int c, ljust, w;
+    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;
 #endif
     char *sp;          /* current position in source string */
@@ -159,8 +152,12 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
            if (iswcntrl(wide_char) || iswspace(wide_char)) {
                sp += char_len;
 #else
+            int c;
            end--;
-           if (iscntrl(*sp) || isspace(*sp)) {
+            /* isnctrl(), etc., take an int argument.  Cygwin's ctype.h
+               intentionally warns if they are passed a char. */
+            c = *sp;
+           if (iscntrl(c) || isspace(c)) {
                sp++;
 #endif
                if (!prevCtrl) {
@@ -211,37 +208,64 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
 }
 
 static void
-cpstripped (char **start, char *end, char *str)
+cpstripped (char **dest, char *end, char *str)
 {
-    int c;
-    char *s = str;
+    int prevCtrl = 1;  /* This is 1 so we strip out leading spaces */
+    int len;
+#ifdef MULTIBYTE_SUPPORT
+    int char_len;
+    wchar_t wide_char;
+#endif /* MULTIBYTE_SUPPORT */
 
-    if (!s)
+    if (!str)
        return;
 
-    /* skip any initial control characters or spaces */
-    while ((c = (unsigned char) *s) &&
-#ifdef LOCALE
-           (iscntrl(c) || isspace(c)))
-#else
-           (c <= 32))
-#endif
-       s++;
-
-    /* compact repeated control characters and spaces into a single space */
-    while((c = (unsigned char) *s++) && *start < end)
-       if (!iscntrl(c) && !isspace(c))
-           *(*start)++ = c;
-       else {
-           while ((c = (unsigned char) *s) &&
-#ifdef LOCALE
-                   (iscntrl(c) || isspace(c)))
-#else
-                   (c <= 32))
-#endif
-               s++;
-           *(*start)++ = ' ';
+    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);
+
+       if (char_len <= 0 || *dest + char_len > end)
+           break;
+
+       len -= char_len;
+
+       if (iswcntrl(wide_char) || iswspace(wide_char)) {
+           str += char_len;
+#else /* MULTIBYTE_SUPPORT */
+       int c = *str;
+       len--;
+       if (iscntrl(c) || isspace(c)) {
+           str++;
+#endif /* MULTIBYTE_SUPPORT */
+           if (! prevCtrl) {
+               *(*dest)++ = ' ';
+           }
+
+           prevCtrl = 1;
+           continue;
        }
+
+       prevCtrl = 0;
+
+#ifdef MULTIBYTE_SUPPORT
+       memcpy(*dest, str, char_len);
+       str += char_len;
+       *dest += char_len;
+#else /* MULTIBYE_SUPPORT */
+       *(*dest)++ = *str++
+#endif /* MULTIBYTE_SUPPORT */
+    }
 }
 
 static char *lmonth[] = { "January",  "February","March",   "April",
@@ -293,11 +317,11 @@ get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
 }
 
 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)
 {
     char *cp, *ep;
     unsigned char *sp;
-    char *savestr;
+    char *savestr = NULL;
     unsigned char *str = NULL;
     char buffer[BUFSIZ], buffer2[BUFSIZ];
     int i, c, ljust, n;
@@ -308,8 +332,11 @@ 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) {
@@ -317,6 +344,27 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
        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);
+               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;
@@ -356,6 +404,30 @@ 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_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)");
 
@@ -508,13 +580,13 @@ 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))
                            *xp-- = '\0';
-                   if (ljust && i > 0 && strlen(str) > i)
+                   if (ljust && i > 0 && (int) strlen(str) > i)
                        str += strlen(str) - i;
            }
            break;
@@ -795,6 +867,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
@@ -813,6 +890,10 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            sp = fmt->f_text;
            indent = strlen (sp);
            wid -= indent;
+           if (wid <= 0) {
+               adios(NULL, "putaddr -- num register (%d) must be greater "
+                           "than label width (%d)", value, indent);
+           }
            while( (c = *sp++) && cp < ep)
                *cp++ = c;
            while (len > wid) {
@@ -914,10 +995,31 @@ fmt_scan (struct format *format, char *scanl, 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[-1] != '\n')
-       *cp++ = '\n';
-    *cp   = 0;
+    if (cp > scanl  &&  cp[-1] != '\n') {
+       if (cp - scanl < (int) max - 1) {
+           *cp++ = '\n';
+       } else {
+           cp[-1] = '\n';
+       }
+    }
+    *cp = '\0';
     return ((struct format *)0);
 #else /* JLR */
     if (cp[-1] != '\n')