]> diplodocus.org Git - nmh/blobdiff - sbr/fmt_scan.c
Corrected comment: whom(1) does not use context_foil().
[nmh] / sbr / fmt_scan.c
index 08405da1e00158842740f67dda8f63814551315d..f9aee54b0d2bd866f23bb4075424c58ee025722e 100644 (file)
@@ -1,6 +1,4 @@
-
-/*
- * fmt_scan.c -- format string interpretation
+/* fmt_scan.c -- format string interpretation
  *
  * This code is Copyright (c) 2002, by the authors of nmh.  See the
  * COPYRIGHT file in the root directory of the nmh distribution for
  * fmt_compile (found in fmt_compile.c).
  */
 
-#include <h/mh.h>
-#include <h/addrsbr.h>
-#include <h/fmt_scan.h>
-#include <h/tws.h>
-#include <h/fmt_compile.h>
-#include <h/utils.h>
+#include "h/mh.h"
+#include "fmt_addr.h"
+#include "dtime.h"
+#include "strindex.h"
+#include "fmt_rfc2047.h"
+#include "uprf.h"
+#include "context_find.h"
+#include "error.h"
+#include "h/addrsbr.h"
+#include "h/fmt_scan.h"
+#include "h/tws.h"
+#include "h/fmt_compile.h"
+#include "h/utils.h"
+#include "unquote.h"
 
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
@@ -32,7 +38,7 @@ struct mailname fmt_mnull = { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
 /*
  * static prototypes
  */
-static int match (char *, char *);
+static int match (char *, char *) PURE;
 static char *get_x400_friendly (char *, char *, int);
 static int get_x400_comp (char *, char *, char *, int);
 
@@ -49,24 +55,14 @@ match (char *str, char *sub)
     char *s1, *s2;
 
     while ((c1 = *sub)) {
-       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))
+       c1 = tolower((unsigned char)c1);
+       while ((c2 = *str++) && c1 != tolower((unsigned char)c2))
            ;
        if (! c2)
            return 0;
        s1 = sub + 1; s2 = str;
-       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))
+       while ((c1 = *s1++) &&
+            tolower((unsigned char)c1) == tolower((unsigned char)(c2 = *s2++)))
            ;
        if (! c1)
            return 1;
@@ -74,45 +70,66 @@ match (char *str, char *sub)
     return 1;
 }
 
-/*
- * copy a number to the destination subject to a maximum width
- */
+/* cpnumber formats num as a signed decimal,
+ * appending it to dest if the result doesn't exceed max.
+ * The absolute value of width is the minimum width to produce.
+ * A smaller string is padded;
+ * on the left if width is positive, else the right.
+ * Left-padding uses fill.  It is either ' ' or '0'.
+ * Right-padding is always with space. */
 void
-cpnumber(charstring_t dest, int num, unsigned int wid, char fill, size_t max) {
-    if (wid < (num >= 0 ? max : max-1)) {
-       /* Build up the string representation of num in reverse. */
-       charstring_t rev = charstring_create (0);
-       int i = num >= 0 ? num : -num;
-
-       do {
-           charstring_push_back (rev, i % 10  +  '0');
-           i /= 10;
-       } while (--wid > 0  &&  i > 0);
-       if (i > 0) {
-           /* Overflowed the field (wid). */
-           charstring_push_back (rev, '?');
-       } else if (num < 0  &&  wid > 0) {
-           /* Shouldn't need the wid > 0 check, that's why the condition
-               at the top checks wid < max-1 when num < 0. */
-           charstring_push_back (rev, '-');
-           --wid;
-       }
-       while (wid-- > 0  &&  fill != 0) {
-           charstring_push_back (rev, fill);
-       }
+cpnumber(charstring_t dest, int num, int width, char fill, size_t max)
+{
+    if (width == 0 || width == INT_MIN) {
+        return;
+    }
+
+    bool padright = width < 0;
+    if (padright) {
+        width = -width; /* Can't overflow after above check. */
+    }
+    size_t w = width;
+    if (w > max) {
+        return; /* The padded result can't fit. */
+    }
+    if (num < 0 && w == 1) {
+        return; /* No room for `-' and a digit or `?'. */
+    }
 
-       {
-           /* Output the string in reverse. */
-           size_t b = charstring_bytes (rev);
-           const char *cp = b  ?  &charstring_buffer (rev)[b]  :  NULL;
+    char *s = m_str(num);
+    size_t len = strlen(s);
+    if (len == w) {
+        charstring_append_cstring(dest, s);
+        return;
+    }
 
-           for (; b > 0; --b) {
-               charstring_push_back (dest, *--cp);
-           }
-       }
+    bool neg = *s == '-';
+    if (len < w) {
+        if (padright) {
+            charstring_append_cstring(dest, s);
+            while (len++ < w) {
+                charstring_push_back(dest, ' ');
+            }
+            return;
+        }
+
+        if (neg && fill == '0') {
+            charstring_push_back(dest, *s++);
+        }
+        while (len++ < w) {
+            charstring_push_back(dest, fill);
+        }
+        charstring_append_cstring(dest, s);
+        return;
+    }
 
-       charstring_free (rev);
+    /* Transform 1234567 -> 1234?67 and -1234567 -> 1234-?7. */
+    char *news = s + len - w;
+    if (neg) {
+        *news = '-';
     }
+    news[neg] = '?';
+    charstring_append_cstring(dest, news);
 }
 
 /*
@@ -121,9 +138,11 @@ cpnumber(charstring_t dest, int num, unsigned int wid, char fill, size_t max) {
  * aligned no more than max characters are copied
  */
 void
-cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) {
+cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max)
+{
     int remaining;     /* remaining output width available */
-    int rjust;
+    bool rjust;
+    struct charstring *trimmed;
     size_t end;        /* number of input bytes remaining in str */
 #ifdef MULTIBYTE_SUPPORT
     int char_len;      /* bytes in current character */
@@ -132,16 +151,18 @@ cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) {
     char *altstr = NULL;
 #endif
     char *sp;          /* current position in source string */
-    int prevCtrl = 1;
 
     /* get alignment */
-    rjust = 0;
+    rjust = false;
     if ((remaining = wid) < 0) {
        remaining = -remaining;
-       rjust++;
+       rjust = true;
     }
     if (remaining > (int) max) { remaining = max; }
 
+    trimmed = rjust ? charstring_create(remaining) : dest;
+
+    bool prevCtrl = true;
     if ((sp = str)) {
 #ifdef MULTIBYTE_SUPPORT
        if (mbtowc(NULL, NULL, 0)) {} /* reset shift state */
@@ -187,133 +208,150 @@ cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) {
                sp++;
 #endif
                if (!prevCtrl) {
-                   charstring_push_back (dest, ' ');
+                    charstring_push_back (trimmed, ' ');
                    remaining--;
                }
 
-               prevCtrl = 1;
+               prevCtrl = true;
                continue;
            }
-           prevCtrl = 0;
+           prevCtrl = false;
 
 #ifdef MULTIBYTE_SUPPORT
            if (w >= 0 && remaining >= w) {
-               charstring_push_back_chars (dest, altstr ? altstr : sp,
+                charstring_push_back_chars (trimmed, altstr ? altstr : sp,
                                            char_len, w);
                remaining -= w;
                altstr = NULL;
            }
            sp += char_len;
 #else
-           charstring_push_back (dest, *sp++);
+            charstring_push_back (trimmed, *sp++);
            remaining--;
 #endif
        }
     }
 
-    if (rjust) {
-       if (remaining > 0) {
-           /* copy string to the right */
-           charstring_t copy = charstring_copy (dest);
-
-           /* add padding at the beginning */
-           charstring_clear (dest);
-           for (; remaining > 0; --remaining) {
-               charstring_push_back (dest, fill);
-           }
-
-           charstring_append (dest, copy);
+    while (remaining-- > 0) {
+        charstring_push_back(dest, fill);
+    }
 
-           charstring_free (copy);
-       }
-    } else {
-       /* pad remaining space */
-       while (remaining-- > 0) {
-           charstring_push_back (dest, fill);
-       }
+    if (rjust) {
+        charstring_append(dest, trimmed);
+        charstring_free(trimmed);
     }
 }
 
+#ifdef MULTIBYTE_SUPPORT
 static void
 cpstripped (charstring_t dest, size_t 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
-    if (mbtowc(NULL, NULL, 0)) {}  /* Reset shift state */
-#endif /* MULTIBYTE_SUPPORT */
+    static bool deja_vu;
+    static char oddchar[MB_LEN_MAX * 2];
+    static size_t oddlen;
+    static char spacechar[MB_LEN_MAX * 2];
+    static size_t spacelen;
+    char *end;
+    bool squash;
+    char *src;
+    int srclen;
+    wchar_t rune;
+    int w;
 
-    /*
-     * Process each character at a time; if we have multibyte support
-     * then deal with that here.
-     */
+    if (!deja_vu) {
+        deja_vu = true;
 
-    while (*str != '\0' && len > 0 && max > 0) {
-#ifdef MULTIBYTE_SUPPORT
-       char_len = mbtowc(&wide_char, str, len);
-       w = wcwidth(wide_char);
+        oddlen = wcstombs(oddchar, L"?", sizeof oddchar);
+        assert(oddlen > 0);
+        assert(wcwidth(L' ') == 1); /* Need to pad in ones. */
+        spacelen = wcstombs(spacechar, L" ", sizeof spacechar);
+        assert(spacelen > 0);
+    }
 
-       /*
-        * 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 (!str)
+        return; /* It's unclear why no padding in this case. */
+    end = str + strlen(str);
+
+    if (mbtowc(NULL, NULL, 0))
+        {} /* Reset shift state. */
+
+    squash = true; /* Trim `space' or `cntrl' from the start. */
+    while (max) {
+        if (!*str)
+            return; /* It's unclear why no padding in this case. */
+
+        srclen = mbtowc(&rune, str, end - str);
+        if (srclen == -1) {
+            /* Invalid rune, or not enough bytes to finish it. */
+            rune = L'?';
+            src = oddchar;
+            srclen = oddlen;
+            str++; /* Skip one byte. */
+        } else {
+            src = str;
+            str += srclen;
+        }
 
-       if (char_len < 0) {
-           altstr = "?";
-           char_len = mbtowc(&wide_char, altstr, 1);
-       }
+        if (iswspace(rune) || iswcntrl(rune)) {
+            if (squash)
+                continue; /* Amidst a run of these. */
+            rune = L' ';
+            src = spacechar;
+            srclen = spacelen;
+            squash = true;
+        } else
+            squash = false;
+
+        w = wcwidth(rune);
+        if (w == -1) {
+            rune = L'?';
+            w = wcwidth(rune);
+            assert(w != -1);
+            src = oddchar;
+            srclen = oddlen;
+        }
 
-       if (char_len <= 0) {
-           break;
-       }
+        if ((size_t)w > max) {
+            /* No room for rune;  pad. */
+            while (max--)
+                charstring_push_back_chars(dest, spacechar, spacelen, 1);
+            return;
+        }
 
-       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) {
-               charstring_push_back (dest, ' ');
-               --max;
-           }
+        charstring_push_back_chars(dest, src, srclen, w);
+        max -= w;
+    }
+}
+#endif
 
-           prevCtrl = 1;
-           continue;
-       }
+#ifndef MULTIBYTE_SUPPORT
+static void
+cpstripped (charstring_t dest, size_t max, char *str)
+{
+    bool squash;
+    int c;
 
-       prevCtrl = 0;
+    if (!str)
+       return;
 
-#ifdef MULTIBYTE_SUPPORT
-       charstring_push_back_chars (dest, altstr ? altstr : str, char_len, w);
-        max -= w;
-       str += char_len;
-       altstr = NULL;
-#else /* MULTIBYE_SUPPORT */
-       charstring_push_back (dest, *str++);
-        --max;
-#endif /* MULTIBYTE_SUPPORT */
+    squash = true; /* Strip leading cases. */
+    while (max--) {
+        c = (unsigned char)*str++;
+        if (!c)
+            return;
+
+       if (isspace(c) || iscntrl(c)) {
+            if (squash)
+                continue;
+            c = ' ';
+            squash = true;
+        } else
+            squash = false;
+
+       charstring_push_back(dest, (char)c);
     }
 }
+#endif
 
 static char *lmonth[] = { "January",  "February","March",   "April",
                          "May",      "June",    "July",    "August",
@@ -368,9 +406,13 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
          struct fmt_callbacks *callbacks)
 {
     char *sp;
-    char *savestr, *str;
-    char buffer[BUFSIZ], buffer2[BUFSIZ];
-    int i, c, rjust;
+    /* If str points to part of buffer[] or buffer2[] then it must only
+     * ever point at their first element as otherwise undefined
+     * behaviour from overlapping strncpy(3)s can result. */
+    char *str, *savestr;
+    char buffer[NMH_BUFSIZ], buffer2[NMH_BUFSIZ];
+    int i, c;
+    bool rjust;
     int value;
     time_t t;
     size_t max;
@@ -438,11 +480,11 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            break;
        case FT_LITF:
            sp = fmt->f_text;
-           rjust = 0;
+           rjust = false;
            i = fmt->f_width;
            if (i < 0) {
                i = -i;
-               rjust++;                /* XXX should do something with this */
+               rjust = true;           /* XXX should do something with this */
            }
            while ((c = *sp++) && --i >= 0 && charstring_chars (scanlp) < max) {
                charstring_push_back (scanlp, c);
@@ -471,13 +513,13 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            if (str) charstring_push_back_chars (scanlp, str, strlen (str), 0);
            break;
        case FT_STRFW:
-           adios (NULL, "internal error (FT_STRFW)");
+           die("internal error (FT_STRFW)");
 
        case FT_NUM: {
            int num = value;
            unsigned int wid;
 
-           for (wid = num <= 0  ?  1  :  0; num; ++wid, num /= 10) {}
+            for (wid = num <= 0; num; ++wid, num /= 10) {}
            cpnumber (scanlp, value, wid, ' ',
                      max - charstring_chars (scanlp));
            break;
@@ -691,15 +733,16 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            if (str) {
                    char *xp;
 
-                   strncpy(buffer, str, sizeof(buffer));
+                   if (str != buffer)
+                       strncpy(buffer, str, sizeof(buffer));
                    buffer[sizeof(buffer)-1] = '\0';
                    str = buffer;
                    while (isspace((unsigned char) *str))
                            str++;
-                   rjust = 0;
+                   rjust = false;
                    if ((i = fmt->f_width) < 0) {
                            i = -i;
-                           rjust++;
+                           rjust = true;
                    }
 
                    if (!rjust && i > 0 && (int) strlen(str) > i)
@@ -710,6 +753,7 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
                            *xp-- = '\0';
                    if (rjust && i > 0 && (int) strlen(str) > i)
                        str += strlen(str) - i;
+                    str = memmove(buffer, str, strlen(str) + 1);
            }
            break;
 
@@ -740,16 +784,22 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
        case FT_LV_MINUS_L:
            value = fmt->f_value - value;
            break;
+       case FT_LV_MULTIPLY_L:
+           value *= fmt->f_value;
+           break;
        case FT_LV_DIVIDE_L:
-           if (fmt->f_value)
-               value = value / fmt->f_value;
-           else
+            if (fmt->f_value == 0 || (fmt->f_value == -1 && value == INT_MIN)) {
+                // FIXME: Tell the user, and probably stop.
                value = 0;
+            } else {
+                value /= fmt->f_value;
+            }
            break;
        case FT_LV_MODULO_L:
            if (fmt->f_value)
-               value = value % fmt->f_value;
+               value %= fmt->f_value;
            else
+                // FIXME: Tell the user, and probably stop.
                value = 0;
            break;
        case FT_SAVESTR:
@@ -811,7 +861,7 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
        case FT_LV_RCLOCK:
            if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
                value = dmktime(fmt->f_comp->c_tws);
-           value = time((time_t *) 0) - value;
+           value = time(NULL) - value;
            break;
        case FT_LV_DAYF:
            if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
@@ -826,13 +876,13 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            }
            break;
        case FT_LV_ZONEF:
-           if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
+           if (fmt->f_comp->c_tws->tw_flags & TW_SZEXP)
                    value = 1;
            else
                    value = -1;
            break;
        case FT_LV_DST:
-           value = fmt->f_comp->c_tws->tw_flags & TW_DST;
+           value = fmt->f_comp->c_tws->tw_flags & TW_DST ? 1 : 0;
            break;
        case FT_LS_822DATE:
            str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
@@ -881,7 +931,8 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
                goto unfriendly;
            if ((str = mn->m_pers) == NULL) {
                if ((str = mn->m_note)) {
-                   strncpy (buffer, str, sizeof(buffer));
+                   if (str != buffer)
+                       strncpy (buffer, str, sizeof(buffer));
                    buffer[sizeof(buffer)-1] = '\0';
                    str = buffer;
                    if (*str == '(')
@@ -895,6 +946,7 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
                            else
                                break;
                    }
+                    str = memmove(buffer, str, strlen(str) + 1);
                } else if (!(str = get_x400_friendly (mn->m_mbox,
                                buffer, sizeof(buffer)))) {
        unfriendly:
@@ -925,7 +977,8 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
                /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
        case FT_LS_UNQUOTE:
            if (str) {          
-               strncpy(buffer, str, sizeof(buffer));
+               if (str != buffer)
+                   strncpy(buffer, str, sizeof(buffer));
                /* strncpy doesn't NUL-terminate if it fills the buffer */
                buffer[sizeof(buffer)-1] = '\0';
                unquote_string(buffer, buffer2);
@@ -957,7 +1010,7 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
                *comp->c_tws = *tws;
                comp->c_flags &= ~CF_TRUE;
            } else if ((comp->c_flags & CF_DATEFAB) == 0) {
-               memset (comp->c_tws, 0, sizeof *comp->c_tws);
+               ZERO(comp->c_tws);
                comp->c_flags = CF_TRUE;
            }
            comp->c_flags |= CF_PARSED;
@@ -997,7 +1050,7 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            indent = strlen (sp);
            wid -= indent;
            if (wid <= 0) {
-               adios(NULL, "putaddr -- num register (%d) must be greater "
+               die("putaddr -- num register (%d) must be greater "
                            "than label width (%d)", value, indent);
            }
            while ((c = *sp++) && charstring_chars (scanlp) < max) {
@@ -1062,6 +1115,8 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            break;
 
        case FT_MYMBOX:
+       case FT_GETMYMBOX:
+       case FT_GETMYADDR:
            /*
             * if there's no component, we say true.  Otherwise we
             * say "true" only if we can parse the address and it
@@ -1073,24 +1128,50 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            if ((sp = comp->c_text) && (sp = getname(sp)) &&
                (mn = getm (sp, NULL, 0, NULL, 0))) {
                comp->c_mn = mn;
-               if (ismymbox(mn))
+               if (ismymbox(mn)) {
                    comp->c_flags |= CF_TRUE;
-               else
+                   /* Set str for use with FT_GETMYMBOX.  With
+                      FT_GETMYADDR, comp->c_mn will be run through
+                      FT_LS_ADDR, which will strip off any pers
+                      name. */
+                   str = mn->m_text;
+               } else {
                    comp->c_flags &= ~CF_TRUE;
+               }
                while ((sp = getname(sp)))
                    if ((comp->c_flags & CF_TRUE) == 0 &&
                        (mn = getm (sp, NULL, 0, NULL, 0)))
-                       if (ismymbox(mn))
+                       if (ismymbox(mn)) {
                            comp->c_flags |= CF_TRUE;
+                           /* Set str and comp->c_text for use with
+                              FT_GETMYMBOX.  With FT_GETMYADDR,
+                              comp->c_mn will be run through
+                              FT_LS_ADDR, which will strip off any
+                              pers name. */
+                           /* NB: We remove the call to free() here
+                              because it interferes with the buffer
+                              management in scansbr.c.  Revisit this
+                              when we clean up memory handling */
+                           /* free (comp->c_text); */
+                           comp->c_text = str = strdup (mn->m_text);
+                           comp->c_mn = mn;
+                       }
+               comp->c_flags |= CF_PARSED;
            } else {
                while (getname(""))             /* XXX */
                    ;
                if (comp->c_text == 0)
                    comp->c_flags |= CF_TRUE;
-               else
+               else {
                    comp->c_flags &= ~CF_TRUE;
+               }
                comp->c_mn = &fmt_mnull;
            }
+           if ((comp->c_flags & CF_TRUE) == 0  &&
+               (fmt->f_type == FT_GETMYMBOX || fmt->f_type == FT_GETMYADDR)) {
+               /* Fool FT_LS_ADDR into not producing an address. */
+               comp->c_mn = &fmt_mnull; comp->c_text = NULL;
+           }
            break;
        }
 
@@ -1150,5 +1231,5 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
        }
     }
 
-    return ((struct format *)0);
+    return NULL;
 }