]> diplodocus.org Git - nmh/blobdiff - sbr/fmt_scan.c
Corrected comment: whom(1) does not use context_foil().
[nmh] / sbr / fmt_scan.c
index 3c93a5a70b856f7564e86306fa518cdb0b40d7fe..f9aee54b0d2bd866f23bb4075424c58ee025722e 100644 (file)
@@ -8,12 +8,19 @@
  * 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
@@ -63,55 +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, int wid, char fill, size_t max) {
-    /* Maybe we should handle left padding at some point? */
-    if (wid == 0)
-       return;
-    if (wid < 0)
-        wid = -wid; /* OK because wid originally a short. */
-    if ((size_t)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. */
-           --wid;
-           if (fill == ' ') {
-               charstring_push_back (rev, '-');
-           }
-       }
-       while (wid-- > 0  &&  fill != 0) {
-           charstring_push_back (rev, fill);
-       }
-       if (num < 0  &&  fill == '0') {
-           charstring_push_back (rev, '-');
-       }
+cpnumber(charstring_t dest, int num, int width, char fill, size_t max)
+{
+    if (width == 0 || width == INT_MIN) {
+        return;
+    }
 
-       {
-           /* Output the string in reverse. */
-           size_t b = charstring_bytes (rev);
-           const char *cp = b  ?  &charstring_buffer (rev)[b]  :  NULL;
+    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 `?'. */
+    }
 
-           for (; b > 0; --b) {
-               charstring_push_back (dest, *--cp);
-           }
-       }
+    char *s = m_str(num);
+    size_t len = strlen(s);
+    if (len == w) {
+        charstring_append_cstring(dest, s);
+        return;
+    }
 
-       charstring_free (rev);
+    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;
+    }
+
+    /* Transform 1234567 -> 1234?67 and -1234567 -> 1234-?7. */
+    char *news = s + len - w;
+    if (neg) {
+        *news = '-';
     }
+    news[neg] = '?';
+    charstring_append_cstring(dest, news);
 }
 
 /*
@@ -120,7 +138,8 @@ cpnumber(charstring_t dest, int num, 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 */
     bool rjust;
     struct charstring *trimmed;
@@ -228,9 +247,9 @@ static void
 cpstripped (charstring_t dest, size_t max, char *str)
 {
     static bool deja_vu;
-    static char *oddchar;
+    static char oddchar[MB_LEN_MAX * 2];
     static size_t oddlen;
-    static char *spacechar;
+    static char spacechar[MB_LEN_MAX * 2];
     static size_t spacelen;
     char *end;
     bool squash;
@@ -240,19 +259,12 @@ cpstripped (charstring_t dest, size_t max, char *str)
     int w;
 
     if (!deja_vu) {
-        size_t two;
-
         deja_vu = true;
 
-        two = MB_CUR_MAX * 2; /* Varies at run-time. */
-
-        oddchar = mh_xmalloc(two);
-        oddlen = wcstombs(oddchar, L"?", two);
+        oddlen = wcstombs(oddchar, L"?", sizeof oddchar);
         assert(oddlen > 0);
-
         assert(wcwidth(L' ') == 1); /* Need to pad in ones. */
-        spacechar = mh_xmalloc(two);
-        spacelen = wcstombs(spacechar, L" ", two);
+        spacelen = wcstombs(spacechar, L" ", sizeof spacechar);
         assert(spacelen > 0);
     }
 
@@ -394,7 +406,10 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
          struct fmt_callbacks *callbacks)
 {
     char *sp;
-    char *savestr, *str;
+    /* 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;
@@ -738,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;
 
@@ -772,15 +788,18 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
            value *= fmt->f_value;
            break;
        case FT_LV_DIVIDE_L:
-           if (fmt->f_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 %= fmt->f_value;
            else
+                // FIXME: Tell the user, and probably stop.
                value = 0;
            break;
        case FT_SAVESTR:
@@ -927,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:
@@ -1128,7 +1148,11 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat,
                               comp->c_mn will be run through
                               FT_LS_ADDR, which will strip off any
                               pers name. */
-                           free (comp->c_text);
+                           /* 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;
                        }