X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/0a934c0ff1b5623956da677751e485c52e1ab361..ec173fd2c:/sbr/fmt_scan.c diff --git a/sbr/fmt_scan.c b/sbr/fmt_scan.c index 83e48b8b..f9aee54b 100644 --- a/sbr/fmt_scan.c +++ b/sbr/fmt_scan.c @@ -8,12 +8,19 @@ * fmt_compile (found in fmt_compile.c). */ -#include -#include -#include -#include -#include -#include +#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 @@ -31,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); @@ -63,50 +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. */ - --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; + } + + 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); } /* @@ -115,9 +138,10 @@ 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 @@ -127,18 +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 */ @@ -188,10 +212,10 @@ cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) { remaining--; } - prevCtrl = 1; + prevCtrl = true; continue; } - prevCtrl = 0; + prevCtrl = false; #ifdef MULTIBYTE_SUPPORT if (w >= 0 && remaining >= w) { @@ -222,75 +246,80 @@ cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) { 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; - int char_len, w; - wchar_t wide_char; - char *altstr = NULL; - - if (!str) { - return; - } - - len = strlen(str); - - if (mbtowc(NULL, NULL, 0)) {} /* Reset shift state */ - - /* - * Process each character at a time; if we have multibyte support - * then deal with that here. - */ - - while (*str != '\0' && len > 0 && max > 0) { - char_len = mbtowc(&wide_char, str, len); + 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; - /* - * If mbrtowc() failed, then we have a character that isn't valid - * in the current encoding, or len wasn't enough for the whole - * multi-byte rune to be read. 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); - } + if (!deja_vu) { + deja_vu = true; - if (char_len <= 0) { - break; - } + 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); + } - len -= char_len; + 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 (iswcntrl(wide_char) || iswspace(wide_char)) { - str += char_len; - if (! prevCtrl) { - charstring_push_back (dest, ' '); - --max; - } + 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; + } - prevCtrl = 1; - continue; - } + if ((size_t)w > max) { + /* No room for rune; pad. */ + while (max--) + charstring_push_back_chars(dest, spacechar, spacelen, 1); + return; + } - prevCtrl = 0; - - w = wcwidth(wide_char); - assert(w >= 0); - if (max >= (size_t) w) { - charstring_push_back_chars (dest, altstr ? altstr : str, char_len, w); - max -= w; - str += char_len; - altstr = NULL; - } else { - /* Not enough width available for the last character. Output - space(s) to fill. */ - while (max-- > 0) { - charstring_push_back (dest, ' '); - } - break; - } + charstring_push_back_chars(dest, src, srclen, w); + max -= w; } } #endif @@ -299,38 +328,27 @@ cpstripped (charstring_t dest, size_t max, char *str) 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; + bool squash; + int c; - if (!str) { + if (!str) return; - } - - len = strlen(str); - - /* - * Process each character at a time; if we have multibyte support - * then deal with that here. - */ - - while (*str != '\0' && len > 0 && max > 0) { - int c = (unsigned char) *str; - len--; - if (iscntrl(c) || isspace(c)) { - str++; - if (! prevCtrl) { - charstring_push_back (dest, ' '); - --max; - } - prevCtrl = 1; - continue; - } - - prevCtrl = 0; - - charstring_push_back (dest, *str++); - --max; + 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 @@ -388,9 +406,13 @@ 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, rjust; + int i, c; + bool rjust; int value; time_t t; size_t max; @@ -458,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); @@ -491,7 +513,7 @@ 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; @@ -717,10 +739,10 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat, 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) @@ -731,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; @@ -765,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: @@ -835,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))) @@ -920,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: @@ -983,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; @@ -1023,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) { @@ -1121,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; } @@ -1200,5 +1231,5 @@ fmt_scan (struct format *format, charstring_t scanlp, int width, int *dat, } } - return (NULL); + return NULL; }