X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/322f6f0518f2fca892733dea85d87c33fcb65e83..94187a80bd60baab4b9c4b949ad820d730578123:/sbr/fmt_scan.c diff --git a/sbr/fmt_scan.c b/sbr/fmt_scan.c index ca4020b0..791146a2 100644 --- a/sbr/fmt_scan.c +++ b/sbr/fmt_scan.c @@ -1,12 +1,11 @@ - -/* - * fmt_scan.c -- format string interpretation - * - * $Id$ +/* 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 * complete copyright information. + * + * This is the engine that processes the format instructions created by + * fmt_compile (found in fmt_compile.c). */ #include @@ -14,37 +13,25 @@ #include #include #include +#include +#include "unquote.h" -#ifdef TIME_WITH_SYS_TIME +#ifdef HAVE_SYS_TIME_H # include -# include -#else -# ifdef TM_IN_SYS_TIME -# include -# else -# include -# endif #endif +#include #ifdef MULTIBYTE_SUPPORT # include # include #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 */ -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); @@ -60,111 +47,149 @@ match (char *str, char *sub) int c1, c2; char *s1, *s2; -#ifdef LOCALE while ((c1 = *sub)) { - c1 = (isalpha(c1) && isupper(c1)) ? tolower(c1) : c1; - while ((c2 = *str++) && c1 != ((isalpha(c2) && isupper(c2)) ? tolower(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++) && ((isalpha(c1) && isupper(c1)) ? tolower(c1) : c1) == ((isalpha(c2 =*s2++) && isupper(c2)) ? tolower(c2) : c2)) + while ((c1 = *s1++) && + tolower((unsigned char)c1) == tolower((unsigned char)(c2 = *s2++))) ; if (! c1) return 1; } -#else - while ((c1 = *sub)) { - while ((c2 = *str++) && (c1 | 040) != (c2 | 040)) - ; - if (! c2) - return 0; - s1 = sub + 1; s2 = str; - while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040)) - ; - if (! c1) - return 1; - } -#endif return 1; } /* * copy a number to the destination subject to a maximum width */ -static void -cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) { - int i, c; - char *sp; - char *cp = *dest; - char *ep = cp + n; - - if (cp + wid < ep) { - if ((i = (num)) < 0) - i = -(num); - if ((c = (wid)) < 0) - c = -c; - sp = cp + c; +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 { - *--sp = (i % 10) + '0'; + charstring_push_back (rev, i % 10 + '0'); i /= 10; - } while (i > 0 && sp > cp); - if (i > 0) - *sp = '?'; - else if ((num) < 0 && sp > cp) - *--sp = '-'; - while (sp > cp) - *--sp = fill; - cp += c; + } 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, '-'); + } + + { + /* Output the string in reverse. */ + size_t b = charstring_bytes (rev); + const char *cp = b ? &charstring_buffer (rev)[b] : NULL; + + for (; b > 0; --b) { + charstring_push_back (dest, *--cp); + } + } + + charstring_free (rev); } - *dest = cp; } /* - * copy string from str to dest padding with the fill character to a size - * of wid characters. if wid is negative, the string is right aligned - * no more than n bytes are copied + * copy string from str to dest padding with the fill character to a + * size of wid characters. if wid is negative, the string is right + * aligned no more than max characters are copied */ -static void -cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) { +void +cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) { int remaining; /* remaining output width available */ - int c, ljust, w; - int end; /* number of input bytes remaining in str */ + int rjust; + struct charstring *trimmed; + size_t end; /* number of input bytes remaining in str */ #ifdef MULTIBYTE_SUPPORT int char_len; /* bytes in current character */ + int w; wchar_t wide_char; + char *altstr = NULL; #endif char *sp; /* current position in source string */ - char *cp = *dest; /* current position in destination string */ - char *ep = cp + n; /* end of destination buffer */ int prevCtrl = 1; /* get alignment */ - ljust = 0; - if ((remaining = (wid)) < 0) { + rjust = 0; + if ((remaining = wid) < 0) { remaining = -remaining; - ljust++; + rjust++; } - if ((sp = (str))) { - mbtowc(NULL, NULL, 0); /* reset shift state */ + if (remaining > (int) max) { remaining = max; } + + trimmed = rjust ? charstring_create(remaining) : dest; + + if ((sp = str)) { +#ifdef MULTIBYTE_SUPPORT + if (mbtowc(NULL, NULL, 0)) {} /* reset shift state */ +#endif end = strlen(str); while (*sp && remaining > 0 && end > 0) { #ifdef MULTIBYTE_SUPPORT char_len = mbtowc(&wide_char, sp, end); - if (char_len <= 0 || (cp + char_len > ep)) + + /* + * See the relevant comments in cpstripped() to explain what's + * going on here; we want to handle the case where we get + * characters that mbtowc() cannot handle + */ + + if (char_len < 0) { + altstr = "?"; + char_len = mbtowc(&wide_char, altstr, 1); + } + + if (char_len <= 0) { + break; + } + + w = wcwidth(wide_char); + + /* If w > remaining, w must be positive. */ + if (w > remaining) { break; + } end -= char_len; 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 = (unsigned char) *sp; + if (iscntrl(c) || isspace(c)) { sp++; #endif if (!prevCtrl) { - *cp++ = ' '; + charstring_push_back (trimmed, ' '); remaining--; } @@ -174,75 +199,147 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) { prevCtrl = 0; #ifdef MULTIBYTE_SUPPORT - w = wcwidth(wide_char); if (w >= 0 && remaining >= w) { - strncpy(cp, sp, char_len); - cp += char_len; + charstring_push_back_chars (trimmed, altstr ? altstr : sp, + char_len, w); remaining -= w; + altstr = NULL; } sp += char_len; #else - *cp++ = *sp++; + charstring_push_back (trimmed, *sp++); remaining--; #endif } } - if (ljust) { - if (cp + remaining > ep) - remaining = ep - cp; - ep = cp + remaining; - if (remaining > 0) { - /* copy string to the right */ - while (--cp >= *dest) - *(cp + remaining) = *cp; - /* add padding at the beginning */ - cp += remaining; - for (c=remaining; c>0; c--) - *cp-- = fill; - } - *dest = ep; - } else { - /* pad remaining space */ - while (remaining-- > 0 && cp < ep) - *cp++ = fill; - *dest = cp; + 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) +{ + static bool deja_vu; + static char *oddchar; + static size_t oddlen; + static char *spacechar; + static size_t spacelen; + char *end; + bool squash; + char *src; + int srclen; + wchar_t rune; + 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); + assert(oddlen > 0); + + assert(wcwidth(L' ') == 1); /* Need to pad in ones. */ + spacechar = mh_xmalloc(two); + spacelen = wcstombs(spacechar, L" ", two); + assert(spacelen > 0); + } + + 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 (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 ((size_t)w > max) { + /* No room for rune; pad. */ + while (max--) + charstring_push_back_chars(dest, spacechar, spacelen, 1); + return; + } + + charstring_push_back_chars(dest, src, srclen, w); + max -= w; + } +} +#endif + +#ifndef MULTIBYTE_SUPPORT static void -cpstripped (char **start, char *end, char *str) +cpstripped (charstring_t dest, size_t max, char *str) { + bool squash; int c; - char *s = str; - 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)++ = ' '; - } + 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", @@ -293,23 +390,28 @@ 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, charstring_t scanlp, int width, int *dat, + struct fmt_callbacks *callbacks) { - char *cp, *ep; - unsigned char *sp; - char *savestr; - unsigned char *str = NULL; - char buffer[BUFSIZ], buffer2[BUFSIZ]; - int i, c, ljust, n; - int value = 0; + char *sp; + char *savestr, *str; + char buffer[NMH_BUFSIZ], buffer2[NMH_BUFSIZ]; + int i, c, rjust; + int value; time_t t; + size_t max; struct format *fmt; struct comp *comp; struct tws *tws; struct mailname *mn; - cp = scanl; - ep = scanl + width - 1; + /* + * max is the same as width, but unsigned so comparisons + * with charstring_chars() won't raise compile warnings. + */ + max = width; + savestr = str = NULL; + value = 0; for (fmt = format; fmt->f_type != FT_DONE; fmt++) switch (fmt->f_type) { @@ -317,70 +419,167 @@ 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)) > 0) { + 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; - while (cp < ep) { + for ( ; charstring_chars (scanlp) < max; ) { switch (fmt->f_type) { case FT_COMP: - cpstripped (&cp, ep, fmt->f_comp->c_text); + cpstripped (scanlp, max - charstring_chars (scanlp), + 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 (scanlp, fmt->f_comp->c_text, fmt->f_width, + fmt->f_fill, max - charstring_chars (scanlp)); break; case FT_LIT: sp = fmt->f_text; - while( (c = *sp++) && cp < ep) - *cp++ = c; + while ((c = *sp++) && charstring_chars (scanlp) < max) { + charstring_push_back (scanlp, c); + } break; case FT_LITF: sp = fmt->f_text; - ljust = 0; + rjust = 0; i = fmt->f_width; if (i < 0) { i = -i; - ljust++; /* XXX should do something with this */ + rjust++; /* XXX should do something with this */ + } + while ((c = *sp++) && --i >= 0 && charstring_chars (scanlp) < max) { + charstring_push_back (scanlp, c); + } + while (--i >= 0 && charstring_chars (scanlp) < max) { + charstring_push_back (scanlp, fmt->f_fill); } - while( (c = *sp++) && --i >= 0 && cp < ep) - *cp++ = c; - while( --i >= 0 && cp < ep) - *cp++ = fmt->f_fill; break; case FT_STR: - cpstripped (&cp, ep, str); + cpstripped (scanlp, max - charstring_chars (scanlp), str); break; case FT_STRF: - cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp); + cptrimmed (scanlp, str, fmt->f_width, fmt->f_fill, + max - charstring_chars (scanlp)); + break; + case FT_STRLIT: + if (str) { + sp = str; + while ((c = *sp++) && charstring_chars (scanlp) < max) { + charstring_push_back (scanlp, c); + } + } + break; + case FT_STRLITZ: + if (str) charstring_push_back_chars (scanlp, str, strlen (str), 0); break; case FT_STRFW: adios (NULL, "internal error (FT_STRFW)"); - case FT_NUM: - n = snprintf(cp, ep - cp + 1, "%d", value); - if (n >= 0) { - if (n >= ep - cp) { - cp = ep; - } else - cp += n; + case FT_NUM: { + int num = value; + unsigned int wid; + + for (wid = num <= 0; num; ++wid, num /= 10) {} + cpnumber (scanlp, value, wid, ' ', + max - charstring_chars (scanlp)); + break; + } + case FT_LS_KILO: + case FT_LS_KIBI: + { + char *unitcp; + unsigned int whole, tenths; + unsigned int scale = 0; + unsigned int val = (unsigned int)value; + char *kibisuff = NULL; + + switch (fmt->f_type) { + case FT_LS_KILO: scale = 1000; kibisuff = ""; break; + case FT_LS_KIBI: scale = 1024; kibisuff = "i"; break; + } + + if (val < scale) { + snprintf(buffer, sizeof(buffer), "%u", val); + } else { + /* To prevent divide by 0, found by clang static + analyzer. */ + if (scale == 0) { scale = 1; } + + /* find correct scale for size (Kilo/Mega/Giga/Tera) */ + for (unitcp = "KMGT"; val > (scale * scale); val /= scale) { + if (!*++unitcp) + break; + } + + if (!*unitcp) { + strcpy(buffer, "huge"); + } else { + /* val is scale times too big. we want tenths */ + val *= 10; + + /* round up */ + val += (scale - 1); + val /= scale; + + whole = val / 10; + tenths = val - (whole * 10); + + if (tenths) { + snprintf(buffer, sizeof(buffer), "%u.%u%c%s", + whole, tenths, *unitcp, kibisuff); + } else { + snprintf(buffer, sizeof(buffer), "%u%c%s", + whole, *unitcp, kibisuff); + } + } + } + str = buffer; } break; case FT_NUMF: - cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp); + cpnumber (scanlp, value, fmt->f_width, fmt->f_fill, + max - charstring_chars (scanlp)); break; case FT_CHAR: - *cp++ = fmt->f_char; + charstring_push_back (scanlp, fmt->f_char); break; case FT_DONE: + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); goto finished; case FT_IF_S: if (!(value = (str && *str))) { + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; } @@ -388,6 +587,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_IF_S_NULL: if (!(value = (str == NULL || *str == 0))) { + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; } @@ -395,6 +597,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_IF_V_EQ: if (value != fmt->f_value) { + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; } @@ -402,6 +607,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_IF_V_NE: if (value == fmt->f_value) { + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; } @@ -409,6 +617,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_IF_V_GT: if (value <= fmt->f_value) { + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; } @@ -416,6 +627,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_IF_MATCH: if (!(value = (str && match (str, fmt->f_text)))) { + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; } @@ -430,6 +644,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_IF_AMATCH: if (!(value = (str && uprf (str, fmt->f_text)))) { + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; } @@ -460,6 +677,9 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) break; case FT_GOTO: + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); fmt += fmt->f_skip; continue; @@ -495,26 +715,27 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_LS_TRIM: if (str) { - unsigned char *xp; + char *xp; - strncpy(buffer, str, sizeof(buffer)); + if (str != buffer) + strncpy(buffer, str, sizeof(buffer)); buffer[sizeof(buffer)-1] = '\0'; str = buffer; - while (isspace(*str)) + while (isspace((unsigned char) *str)) str++; - ljust = 0; + rjust = 0; if ((i = fmt->f_width) < 0) { i = -i; - ljust++; + rjust++; } - if (!ljust && i > 0 && strlen(str) > i) + if (!rjust && i > 0 && (int) strlen(str) > i) str[i] = '\0'; xp = str; xp += strlen(str) - 1; - while (xp > str && isspace(*xp)) + while (xp > str && isspace((unsigned char) *xp)) *xp-- = '\0'; - if (ljust && i > 0 && strlen(str) > i) + if (rjust && i > 0 && (int) strlen(str) > i) str += strlen(str) - i; } break; @@ -538,7 +759,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) value = 0; break; case FT_LV_CHAR_LEFT: - value = width - (cp - scanl); + value = max - charstring_bytes (scanlp); break; case FT_LV_PLUS_L: value += fmt->f_value; @@ -546,15 +767,18 @@ fmt_scan (struct format *format, char *scanl, 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; + value /= fmt->f_value; else value = 0; break; case FT_LV_MODULO_L: if (fmt->f_value) - value = value % fmt->f_value; + value %= fmt->f_value; else value = 0; break; @@ -617,7 +841,7 @@ fmt_scan (struct format *format, char *scanl, 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))) @@ -630,14 +854,15 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) default: value = -1; break; } + 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); @@ -686,7 +911,8 @@ fmt_scan (struct format *format, char *scanl, 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 == '(') @@ -702,7 +928,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) } } else if (!(str = get_x400_friendly (mn->m_mbox, buffer, sizeof(buffer)))) { - unfriendly: ; + unfriendly: switch (mn->m_type) { case LOCALHOST: str = mn->m_mbox; @@ -730,32 +956,11 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */ case FT_LS_UNQUOTE: if (str) { - int m; - 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'; - str = buffer; - - /* we will parse from buffer to buffer2 */ - n = 0; /* n is the input position in str */ - m = 0; /* m is the ouput position in buffer2 */ - - while ( str[n] != '\0') { - switch ( str[n] ) { - case '\\': - n++; - if ( str[n] != '\0') - buffer2[m++] = str[n++]; - break; - case '"': - n++; - break; - default: - buffer2[m++] = str[n++]; - break; - } - } - buffer2[m] = '\0'; + unquote_string(buffer, buffer2); str = buffer2; } break; @@ -784,7 +989,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) *comp->c_tws = *tws; comp->c_flags &= ~CF_TRUE; } else if ((comp->c_flags & CF_DATEFAB) == 0) { - memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws); + ZERO(comp->c_tws); comp->c_flags = CF_TRUE; } comp->c_flags |= CF_PARSED; @@ -792,7 +997,18 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_FORMATADDR: /* hook for custom address list formatting (see replsbr.c) */ - str = formataddr (savestr, str); + if (callbacks && callbacks->formataddr) + str = callbacks->formataddr (savestr, str); + else + str = formataddr (savestr, str); + break; + + case FT_CONCATADDR: + /* The same as formataddr, but doesn't do duplicate suppression */ + if (callbacks && callbacks->concataddr) + str = callbacks->concataddr (savestr, str); + else + str = concataddr (savestr, str); break; case FT_PUTADDR: @@ -803,49 +1019,58 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) * (e.g., "To: ") */ { - unsigned char *lp; - char *lastb; + char *lp, *lastb; int indent, wid, len; lp = str; wid = value; - len = strlen (str); + len = str ? strlen (str) : 0; sp = fmt->f_text; indent = strlen (sp); wid -= indent; - while( (c = *sp++) && cp < ep) - *cp++ = c; + if (wid <= 0) { + adios(NULL, "putaddr -- num register (%d) must be greater " + "than label width (%d)", value, indent); + } + while ((c = *sp++) && charstring_chars (scanlp) < max) { + charstring_push_back (scanlp, c); + } while (len > wid) { /* try to break at a comma; failing that, break at a * space. */ lastb = 0; sp = lp + wid; - while (sp > lp && (c = *--sp) != ',') { + while (sp > lp && (c = (unsigned char) *--sp) != ',') { if (! lastb && isspace(c)) lastb = sp - 1; } if (sp == lp) { if (! (sp = lastb)) { sp = lp + wid - 1; - while (*sp && *sp != ',' && !isspace(*sp)) + while (*sp && *sp != ',' && + !isspace((unsigned char) *sp)) sp++; if (*sp != ',') sp--; } } len -= sp - lp + 1; - while (cp < ep && lp <= sp) - *cp++ = *lp++; - while (isspace(*lp)) + while (lp <= sp && charstring_chars (scanlp) < max) { + charstring_push_back (scanlp, *lp++); + } + while (isspace((unsigned char) *lp)) lp++, len--; if (*lp) { - if (cp < ep) - *cp++ = '\n'; - for (i=indent; cp < ep && i > 0; i--) - *cp++ = ' '; + if (charstring_chars (scanlp) < max) { + charstring_push_back (scanlp, '\n'); + } + for (i=indent; + charstring_chars (scanlp) < max && i > 0; + i--) + charstring_push_back (scanlp, ' '); } } - cpstripped (&cp, ep, lp); + cpstripped (scanlp, max - charstring_chars (scanlp), lp); } break; @@ -856,7 +1081,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) if (comp->c_mn != &fmt_mnull) mnfree (comp->c_mn); if ((sp = comp->c_text) && (sp = getname(sp)) && - (mn = getm (sp, NULL, 0, fmt_norm, NULL))) { + (mn = getm (sp, NULL, 0, NULL, 0))) { comp->c_mn = mn; while (getname("")) ; @@ -869,6 +1094,8 @@ fmt_scan (struct format *format, char *scanl, 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 @@ -878,56 +1105,106 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) if (comp->c_mn != &fmt_mnull) mnfree (comp->c_mn); if ((sp = comp->c_text) && (sp = getname(sp)) && - (mn = getm (sp, NULL, 0, AD_NAME, NULL))) { + (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, AD_NAME, NULL))) - if (ismymbox(mn)) + (mn = getm (sp, NULL, 0, NULL, 0))) + 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. */ + 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; } - 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 + 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; } + + /* + * Call our tracing callback function, if one was supplied + */ + + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); 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; + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); + } 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 (str) { + for (sp = str; *sp; ++sp) { + charstring_push_back (scanlp, *sp); + } + } + if (callbacks && callbacks->trace_func) + callbacks->trace_func(callbacks->trace_context, fmt, value, + str, charstring_buffer (scanlp)); + } fmt++; + } - finished:; - *cp = '\0'; - return (fmt->f_value ? ++fmt : (struct format *) 0); + finished: + if (charstring_bytes (scanlp) > 0) { + /* + * Append a newline if the last character wasn't. + */ +#ifdef MULTIBYTE_SUPPORT + /* + * It's a little tricky because the last byte might be part of + * a multibyte character, in which case we assume that wasn't + * a newline. + */ + size_t last_char_len = charstring_last_char_len (scanlp); +#else /* ! MULTIBYTE_SUPPORT */ + size_t last_char_len = 1; +#endif /* ! MULTIBYTE_SUPPORT */ + + if (last_char_len > 1 || + charstring_buffer (scanlp)[charstring_bytes (scanlp) - 1] != '\n') { + charstring_push_back (scanlp, '\n'); + } + } -#endif /* JLR */ + return NULL; }