X-Git-Url: http://git.marmaro.de/?a=blobdiff_plain;f=sbr%2Ffmt_scan.c;h=3818b85e30cbe5a4d6af139fb4e3b7d4cba0fa28;hb=90986dddba4b24b0449238f86f836581cfb93938;hp=5dc7f22c91269d2a71c6b273e057822f23dd7e33;hpb=6c42153ad9362cc676ea66563bf400d7511b3b68;p=mmh diff --git a/sbr/fmt_scan.c b/sbr/fmt_scan.c index 5dc7f22..3818b85 100644 --- a/sbr/fmt_scan.c +++ b/sbr/fmt_scan.c @@ -2,11 +2,12 @@ /* * fmt_scan.c -- format string interpretation * - * $Id$ - * * 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 @@ -15,27 +16,22 @@ #include #include -#ifdef TIME_WITH_SYS_TIME +#ifdef HAVE_SYS_TIME_H # include -# include -#else -# ifdef TM_IN_SYS_TIME -# include -# else -# include -# endif #endif - -#define NFMTS MAXARGS - -extern char *formataddr (); /* hook for custom address formatting */ +#include +#ifdef MULTIBYTE_SUPPORT +# include +# include +#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; +struct mailname fmt_mnull = { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, + NULL, NULL }; /* * static prototypes @@ -86,151 +82,191 @@ match (char *str, char *sub) } /* - * macros to format data + * 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; + do { + *--sp = (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; + } + *dest = cp; +} -#define PUTDF(cp, num, wid, fill)\ - if (cp + wid < ep) {\ - if ((i = (num)) < 0)\ - i = -(num);\ - if ((c = (wid)) < 0)\ - c = -c;\ - sp = cp + c;\ - do {\ - *--sp = (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;\ - } +/* + * 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 + */ +static void +cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) { + int remaining; /* remaining output width available */ + int c, ljust; + int end; /* number of input bytes remaining in str */ +#ifdef MULTIBYTE_SUPPORT + int char_len; /* bytes in current character */ + int w; + wchar_t wide_char; +#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) { + remaining = -remaining; + ljust++; + } + if ((sp = (str))) { + mbtowc(NULL, NULL, 0); /* reset shift state */ + 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)) + break; -#define PUTD(cp, num)\ - if (cp < ep) {\ - if ((i = (num)) == 0)\ - *cp++ = '0';\ - else {\ - if ((i = (num)) < 0) {\ - *cp++ = '-';\ - i = -(num);\ - }\ - c = 10;\ - while (c <= i) \ - c *= 10;\ - while (cp < ep && c > 1) {\ - c /= 10;\ - *cp++ = (i / c) + '0';\ - i %= c;\ - }\ - }\ - } + end -= char_len; -#ifdef LOCALE -#define PUTSF(cp, str, wid, fill) {\ - ljust = 0;\ - if ((i = (wid)) < 0) {\ - i = -i;\ - ljust++;\ - }\ - if ((sp = (str))) {\ - if (ljust) {\ - c = strlen(sp);\ - if (c > i)\ - sp += c - i;\ - else {\ - while( --i >= c && cp < ep)\ - *cp++ = fill;\ - i++;\ - }\ - } else {\ - while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\ - sp++;\ - }\ - while ((c = (unsigned char) *sp++) && --i >= 0 && cp < ep)\ - if (isgraph(c)) \ - *cp++ = c;\ - else {\ - while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\ - sp++;\ - *cp++ = ' ';\ - }\ - }\ - if (!ljust)\ - while( --i >= 0 && cp < ep)\ - *cp++ = fill;\ + if (iswcntrl(wide_char) || iswspace(wide_char)) { + sp += char_len; +#else + int c; + end--; + /* isnctrl(), etc., take an int argument. Cygwin's ctype.h + intentionally warns if they are passed a char. */ + c = *sp; + if (iscntrl(c) || isspace(c)) { + sp++; +#endif + if (!prevCtrl) { + *cp++ = ' '; + remaining--; + } + + prevCtrl = 1; + continue; + } + prevCtrl = 0; + +#ifdef MULTIBYTE_SUPPORT + w = wcwidth(wide_char); + if (w >= 0 && remaining >= w) { + strncpy(cp, sp, char_len); + cp += char_len; + remaining -= w; + } + sp += char_len; +#else + *cp++ = *sp++; + remaining--; +#endif } + } -#define PUTS(cp, str) {\ - if ((sp = (str))) {\ - while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\ - sp++;\ - while((c = (unsigned char) *sp++) && cp < ep)\ - if (isgraph(c)) \ - *cp++ = c;\ - else {\ - while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\ - sp++;\ - *cp++ = ' ';\ - }\ - }\ + 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; + } +} -#else /* LOCALE */ -#define PUTSF(cp, str, wid, fill) {\ - ljust = 0;\ - if ((i = (wid)) < 0) {\ - i = -i;\ - ljust++;\ - }\ - if (sp = (str)) {\ - if (ljust) {\ - c = strlen(sp);\ - if (c > i)\ - sp += c - i;\ - else {\ - while( --i >= c && cp < ep)\ - *cp++ = fill;\ - i++;\ - }\ - } else {\ - while ((c = *sp) && c <= 32)\ - sp++;\ - }\ - while ((c = *sp++) && --i >= 0 && cp < ep)\ - if (c > 32) \ - *cp++ = c;\ - else {\ - while ((c = *sp) && c <= 32)\ - sp++;\ - *cp++ = ' ';\ - }\ - }\ - if (!ljust)\ - while( --i >= 0 && cp < ep)\ - *cp++ = fill;\ - } +static void +cpstripped (char **dest, char *end, char *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 (!str) + return; + + 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); + + 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)++ = ' '; + } -#define PUTS(cp, str) {\ - if (sp = (str)) {\ - while ((c = *sp) && c <= 32)\ - sp++;\ - while( (c = *sp++) && cp < ep)\ - if ( c > 32 ) \ - *cp++ = c;\ - else {\ - while ( (c = *sp) && c <= 32 )\ - sp++;\ - *cp++ = ' ';\ - }\ - }\ - } + prevCtrl = 1; + continue; + } -#endif /* LOCALE */ + 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", "May", "June", "July", "August", @@ -276,17 +312,19 @@ get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len) || !(cp = strchr(mbox += idx + strlen (key), '/'))) return 0; - snprintf (buffer, buffer_len, "%*.*s", cp - mbox, cp - mbox, mbox); + snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox); return 1; } 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, *sp; - char *savestr, *str = NULL; + char *cp, *ep; + unsigned char *sp; + char *savestr = NULL; + unsigned char *str = NULL; char buffer[BUFSIZ], buffer2[BUFSIZ]; - int i, c, ljust; + int i, c, ljust, n; int value = 0; time_t t; struct format *fmt; @@ -294,18 +332,51 @@ 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) { + case FT_PARSEADDR: + 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); + 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) { switch (fmt->f_type) { case FT_COMP: - PUTS (cp, fmt->f_comp->c_text); + cpstripped (&cp, ep, fmt->f_comp->c_text); break; case FT_COMPF: - PUTSF (cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill); + cptrimmed (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, ep - cp); break; case FT_LIT: @@ -328,19 +399,49 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) break; case FT_STR: - PUTS (cp, str); + cpstripped (&cp, ep, str); break; case FT_STRF: - PUTSF (cp, str, fmt->f_width, fmt->f_fill); + cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp); + 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)"); case FT_NUM: - PUTD (cp, value); + n = snprintf(cp, ep - cp + 1, "%d", value); + if (n >= 0) { + if (n >= ep - cp) { + cp = ep; + } else + cp += n; + } break; case FT_NUMF: - PUTDF (cp, value, fmt->f_width, fmt->f_fill); + cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp); break; case FT_CHAR: @@ -453,22 +554,23 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) break; case FT_LS_DECODECOMP: - if (decode_rfc2047(fmt->f_comp->c_text, buffer2)) + if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2))) str = buffer2; else str = fmt->f_comp->c_text; break; case FT_LS_DECODE: - if (str && decode_rfc2047(str, buffer2)) + if (str && decode_rfc2047(str, buffer2, sizeof(buffer2))) str = buffer2; break; case FT_LS_TRIM: if (str) { - char *xp; + unsigned char *xp; strncpy(buffer, str, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; str = buffer; while (isspace(*str)) str++; @@ -478,19 +580,19 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) ljust++; } - if (!ljust && i > 0 && strlen(str) > i) + if (!ljust && i > 0 && (int) strlen(str) > i) str[i] = '\0'; xp = str; xp += strlen(str) - 1; while (xp > str && isspace(*xp)) *xp-- = '\0'; - if (ljust && i > 0 && strlen(str) > i) + if (ljust && i > 0 && (int) strlen(str) > i) str += strlen(str) - i; } break; case FT_LV_COMPFLAG: - value = fmt->f_comp->c_flags; + value = (fmt->f_comp->c_flags & CF_TRUE) != 0; break; case FT_LV_COMP: value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0; @@ -502,7 +604,10 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) value = dat[fmt->f_value]; break; case FT_LV_STRLEN: - value = strlen(str); + if (str != NULL) + value = strlen(str); + else + value = 0; break; case FT_LV_CHAR_LEFT: value = width - (cp - scanl); @@ -654,6 +759,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) if ((str = mn->m_pers) == NULL) { if ((str = mn->m_note)) { strncpy (buffer, str, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; str = buffer; if (*str == '(') str++; @@ -692,6 +798,40 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) } break; + + /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */ + case FT_LS_UNQUOTE: + if (str) { + int m; + 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'; + str = buffer2; + } + break; + case FT_LOCALDATE: comp = fmt->f_comp; if ((t = comp->c_tws->tw_clock) == 0) @@ -710,13 +850,16 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) case FT_PARSEDATE: comp = fmt->f_comp; + if (comp->c_flags & CF_PARSED) + break; if ((sp = comp->c_text) && (tws = dparsetime(sp))) { *comp->c_tws = *tws; - comp->c_flags = 0; - } else if (comp->c_flags >= 0) { + comp->c_flags &= ~CF_TRUE; + } else if ((comp->c_flags & CF_DATEFAB) == 0) { memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws); - comp->c_flags = 1; + comp->c_flags = CF_TRUE; } + comp->c_flags |= CF_PARSED; break; case FT_FORMATADDR: @@ -724,6 +867,11 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) str = formataddr (savestr, str); break; + case FT_CONCATADDR: + /* The same as formataddr, but doesn't do duplicate suppression */ + str = concataddr (savestr, str); + break; + case FT_PUTADDR: /* output the str register as an address component, * splitting it into multiple lines if necessary. The @@ -732,7 +880,8 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) * (e.g., "To: ") */ { - char *lp, *lastb; + unsigned char *lp; + char *lastb; int indent, wid, len; lp = str; @@ -741,6 +890,10 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) sp = fmt->f_text; indent = strlen (sp); wid -= indent; + if (wid <= 0) { + adios(NULL, "putaddr -- num register (%d) must be greater " + "than label width (%d)", value, indent); + } while( (c = *sp++) && cp < ep) *cp++ = c; while (len > wid) { @@ -773,12 +926,14 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) *cp++ = ' '; } } - PUTS (cp, lp); + cpstripped (&cp, ep, lp); } break; case FT_PARSEADDR: comp = fmt->f_comp; + if (comp->c_flags & CF_PARSED) + break; if (comp->c_mn != &fmt_mnull) mnfree (comp->c_mn); if ((sp = comp->c_text) && (sp = getname(sp)) && @@ -786,13 +941,14 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) comp->c_mn = mn; while (getname("")) ; + comp->c_flags |= CF_PARSED; } else { while (getname("")) /* XXX */ ; comp->c_mn = &fmt_mnull; } break; - + case FT_MYMBOX: /* * if there's no component, we say true. Otherwise we @@ -805,15 +961,22 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) if ((sp = comp->c_text) && (sp = getname(sp)) && (mn = getm (sp, NULL, 0, AD_NAME, NULL))) { comp->c_mn = mn; - comp->c_flags = ismymbox(mn); + if (ismymbox(mn)) + comp->c_flags |= CF_TRUE; + else + comp->c_flags &= ~CF_TRUE; while ((sp = getname(sp))) - if (comp->c_flags == 0 && + if ((comp->c_flags & CF_TRUE) == 0 && (mn = getm (sp, NULL, 0, AD_NAME, NULL))) - comp->c_flags |= ismymbox(mn); + if (ismymbox(mn)) + comp->c_flags |= CF_TRUE; } else { while (getname("")) /* XXX */ ; - comp->c_flags = (comp->c_text == 0); + if (comp->c_text == 0) + comp->c_flags |= CF_TRUE; + else + comp->c_flags &= ~CF_TRUE; comp->c_mn = &fmt_mnull; } break; @@ -832,10 +995,31 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) fmt++; } #ifndef JLR + /* 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[-1] != '\n') - *cp++ = '\n'; - *cp = 0; + if (cp > scanl && cp[-1] != '\n') { + if (cp - scanl < (int) max - 1) { + *cp++ = '\n'; + } else { + cp[-1] = '\n'; + } + } + *cp = '\0'; return ((struct format *)0); #else /* JLR */ if (cp[-1] != '\n') @@ -843,7 +1027,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat) while (fmt->f_type != FT_DONE) fmt++; - finished:; + finished:; *cp = '\0'; return (fmt->f_value ? ++fmt : (struct format *) 0);