]> diplodocus.org Git - nmh/blobdiff - sbr/fmt_scan.c
Added scan and inc from mbox with multiple messages.
[nmh] / sbr / fmt_scan.c
index 3f004e5e709f713f0c704cb40ca05138320c4c4e..879f7716af6ee286b2a3919e5c1ed61f7ef01637 100644 (file)
 #  include <wchar.h>
 #endif
 
-#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 = { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
                              NULL, NULL };
@@ -118,7 +114,8 @@ cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
  * no more than n bytes are copied
  */
 static void
-cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
+cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n,
+         size_t max) {
     int remaining;     /* remaining output width available */
     int c, ljust;
     int end;           /* number of input bytes remaining in str */
@@ -129,7 +126,8 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
 #endif
     char *sp;          /* current position in source string */
     char *cp = *dest;  /* current position in destination string */
-    char *ep = cp + n; /* end of destination buffer */
+    char *ep = cp + n; /* end of destination buffer based on desired width */
+    char *epmax = cp + max; /* true end of destination buffer */
     int prevCtrl = 1;
 
     /* get alignment */
@@ -144,6 +142,13 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
        while (*sp && remaining > 0 && end > 0) {
 #ifdef MULTIBYTE_SUPPORT
            char_len = mbtowc(&wide_char, sp, end);
+
+           /* Account for multibyte characters taking only one character's
+              width of output. */
+           if (char_len > 1  &&  epmax - ep >= char_len - 1) {
+               ep += char_len - 1;
+           }
+
            if (char_len <= 0 || (cp + char_len > ep))
                break;
 
@@ -208,37 +213,70 @@ 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 *max, 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);
+
+       /* Account for multibyte characters taking only one character's
+          width of output. */
+       if (char_len > 1  &&  max - end >= char_len - 1) {
+           end += char_len - 1;
+       }
+
+       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",
@@ -290,7 +328,7 @@ 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;
@@ -305,8 +343,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) {
@@ -326,11 +367,11 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
 
            comp = fmt->f_comp;
 
-           if (! (comp->c_flags & CF_TRIMMED) && comp->c_text) {
-               i = strlen(comp->c_text);
+           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)
+                   strcmp(comp->c_name, "body") != 0 &&
+                   strcmp(comp->c_name, "text") != 0)
                    comp->c_text[i - 1] = '\0';
                comp->c_flags |= CF_TRIMMED;
            }
@@ -343,10 +384,11 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
        switch (fmt->f_type) {
 
        case FT_COMP:
-           cpstripped (&cp, ep, fmt->f_comp->c_text);
+           cpstripped (&cp, ep, scanl + max - 1, fmt->f_comp->c_text);
            break;
        case FT_COMPF:
-           cptrimmed (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, ep - cp);
+           cptrimmed (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill,
+                      ep - cp, scanl - cp + max - 1);
            break;
 
        case FT_LIT:
@@ -369,16 +411,36 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            break;
 
        case FT_STR:
-           cpstripped (&cp, ep, str);
+           cpstripped (&cp, ep, scanl + max - 1, str);
            break;
        case FT_STRF:
-           cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp);
+           cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp,
+                      scanl - cp + max - 1);
            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)");
 
@@ -877,7 +939,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                        *cp++ = ' ';
                }
            }
-           cpstripped (&cp, ep, lp);
+           cpstripped (&cp, ep, scanl + max - 1, lp);
            }
            break;
 
@@ -931,35 +993,34 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                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;
        }
        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;
+       } 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) {
+           *cp++ = '\n';
+       } else {
+           cp[-1] = '\n';
+       }
+    }
     *cp = '\0';
-    return (fmt->f_value ? ++fmt : (struct format *) 0);
-
-#endif /* JLR */
+    return ((struct format *)0);
 }