X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/832f687b6c2afdb9dd09e0c5bbc249e1bf22f433..7ebe4fe7bb8bf17b4e1058f687efbe9535d11df7:/sbr/fmt_scan.c diff --git a/sbr/fmt_scan.c b/sbr/fmt_scan.c index 51ba24cf..0d9e1e90 100644 --- a/sbr/fmt_scan.c +++ b/sbr/fmt_scan.c @@ -118,7 +118,8 @@ cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) { * no more than n bytes are copied */ static void -cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) { +cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n, + size_t max) { int remaining; /* remaining output width available */ int c, ljust; int end; /* number of input bytes remaining in str */ @@ -129,7 +130,8 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) { #endif char *sp; /* current position in source string */ char *cp = *dest; /* current position in destination string */ - char *ep = cp + n; /* end of destination buffer */ + char *ep = cp + n; /* end of destination buffer based on desired width */ + char *epmax = cp + max; /* true end of destination buffer */ int prevCtrl = 1; /* get alignment */ @@ -144,6 +146,13 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) { while (*sp && remaining > 0 && end > 0) { #ifdef MULTIBYTE_SUPPORT char_len = mbtowc(&wide_char, sp, end); + + /* Account for multibyte characters taking only one character's + width of output. */ + if (char_len > 1 && epmax - ep >= char_len - 1) { + ep += char_len - 1; + } + if (char_len <= 0 || (cp + char_len > ep)) break; @@ -208,7 +217,7 @@ cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) { } static void -cpstripped (char **dest, char *end, char *str) +cpstripped (char **dest, char *end, char *max, char *str) { int prevCtrl = 1; /* This is 1 so we strip out leading spaces */ int len; @@ -235,6 +244,12 @@ cpstripped (char **dest, char *end, char *str) #ifdef MULTIBYTE_SUPPORT char_len = mbtowc(&wide_char, str, len); + /* Account for multibyte characters taking only one character's + width of output. */ + if (char_len > 1 && max - end >= char_len - 1) { + end += char_len - 1; + } + if (char_len <= 0 || *dest + char_len > end) break; @@ -373,10 +388,11 @@ fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat) switch (fmt->f_type) { case FT_COMP: - cpstripped (&cp, ep, fmt->f_comp->c_text); + cpstripped (&cp, ep, scanl + max - 1, 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 (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, + ep - cp, scanl - cp + max - 1); break; case FT_LIT: @@ -399,16 +415,36 @@ fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat) break; case FT_STR: - cpstripped (&cp, ep, str); + cpstripped (&cp, ep, scanl + max - 1, str); break; case FT_STRF: - cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp); + cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp, + scanl - cp + max - 1); 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)"); @@ -907,7 +943,7 @@ fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat) *cp++ = ' '; } } - cpstripped (&cp, ep, lp); + cpstripped (&cp, ep, scanl + max - 1, lp); } break; @@ -975,7 +1011,23 @@ fmt_scan (struct format *format, char *scanl, size_t max, 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 > scanl && cp[-1] != '\n') { if (cp - scanl < (int) max - 1) { @@ -986,15 +1038,4 @@ fmt_scan (struct format *format, char *scanl, size_t max, int width, int *dat) } *cp = '\0'; return ((struct format *)0); -#else /* JLR */ - if (cp[-1] != '\n') - *cp++ = '\n'; - while (fmt->f_type != FT_DONE) - fmt++; - - finished:; - *cp = '\0'; - return (fmt->f_value ? ++fmt : (struct format *) 0); - -#endif /* JLR */ }