X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d945e180cbe2b26392fb21f7ae4dd2ccc603a81b..HEAD:/src/src/string.c diff --git a/src/src/string.c b/src/src/string.c index af187c199..1169f0e2c 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -649,7 +649,7 @@ uschar * t, * yield; /* First find the end of the string */ if (*s != '\"') - while (*s && !isspace(*s)) s++; + Uskip_nonwhite(&s); else { s++; @@ -1092,6 +1092,49 @@ return list; +/* Listmaker that takes a format string and args for the element. +A flag arg is required to handle embedded sep chars in the (expanded) element; +if false then no check is done */ + +gstring * +string_append_listele_fmt(gstring * list, uschar sep, BOOL check, + const char * fmt, ...) +{ +va_list ap; +unsigned start; +gstring * g; + +if (list && list->ptr) + { + list = string_catn(list, &sep, 1); + start = list->ptr; + } +else + start = 0; + +va_start(ap, fmt); +list = string_vformat_trc(list, US __FUNCTION__, __LINE__, + STRING_SPRINTF_BUFFER_SIZE, SVFMT_REBUFFER|SVFMT_EXTEND, fmt, ap); +va_end(ap); + +(void) string_from_gstring(list); + +/* if the appended element turns out to have an embedded sep char, rewind +and do the lazy-coded separate string method */ + +if (!check || !Ustrchr(&list->s[start], sep)) + return list; + +va_start(ap, fmt); +g = string_vformat_trc(NULL, US __FUNCTION__, __LINE__, + STRING_SPRINTF_BUFFER_SIZE, SVFMT_REBUFFER|SVFMT_EXTEND, fmt, ap); +va_end(ap); + +list->ptr = start; +return string_append_listele_n(list, sep, g->s, g->ptr); +} + + /* A slightly-bogus listmaker utility; the separator is a string so can be multiple chars - there is no checking for the element content containing any of the separator. */ @@ -1234,13 +1277,6 @@ return g; } -gstring * -string_cat(gstring * g, const uschar * s) -{ -return string_catn(g, s, Ustrlen(s)); -} - - /************************************************* * Append strings to another string * @@ -1333,7 +1369,7 @@ Arguments: ap variable-args pointer Flags: - SVFMT_EXTEND buffer can be created or exteded as needed + SVFMT_EXTEND buffer can be created or extended as needed SVFMT_REBUFFER buffer can be recopied to tainted mem as needed SVFMT_TAINT_NOCHK do not check inputs for taint @@ -1348,7 +1384,8 @@ The return value can be NULL to signify overflow. Field width: decimal digits, or * Precision: dot, followed by decimal digits or * Length modifiers: h L l ll z -Conversion specifiers: n d o u x X p f e E g G % c s S T W V Y D M +Conversion specifiers: n d o u x X p f e E g G % c s S T W V Y D M H Z b +Alternate-form: #: s/Y/b are silent about a null string Returns the possibly-new (if copy for growth or taint-handling was needed) string, not nul-terminated. @@ -1602,7 +1639,15 @@ while (*fp) goto INSERT_GSTRING; } #ifndef COMPILE_UTILITY - case 'V': /* Maybe convert ascii-art to UTF-8 chars */ + case 'b': /* blob pointer, carrying a string */ + { + blob * b = va_arg(ap, blob *); + if (b) { s = CS b->data; slen = b->len; } + else { s = null; slen = Ustrlen(s); } + goto INSERT_GSTRING; + } + + case 'V': /* string; maybe convert ascii-art to UTF-8 chars */ { gstring * zg = NULL; s = va_arg(ap, char *); @@ -1630,19 +1675,20 @@ while (*fp) goto INSERT_GSTRING; } - case 'W': /* Maybe mark up spaces & newlines */ + case 'W': /* Maybe mark up ctrls, spaces & newlines */ s = va_arg(ap, char *); - if (Ustrpbrk(s, " \n") && !IS_DEBUG(D_noutf8)) + if (s && !IS_DEBUG(D_noutf8)) { gstring * zg = NULL; int p = precision; - for ( ; *s; s++) - { - /* Take a given precision as applying to the input; expand - it for the transformed result */ - if (p >= 0 && --p < 0) break; - switch (*s) + /* If a precision was given, we can handle embedded NULs. Take it as + applying to the input and expand it for the transformed result */ + + for ( ; precision >= 0 || *s; s++) + if (p >= 0 && --p < 0) + break; + else switch (*s) { case ' ': zg = string_catn(zg, CUS UTF8_LIGHT_SHADE, 3); @@ -1653,12 +1699,19 @@ while (*fp) if (precision >= 0) precision += 3; break; default: - zg = string_catn(zg, CUS s, 1); + if (*s <= ' ') + { /* base of UTF8 symbols for ASCII control chars */ + uschar ctrl_symbol[3] = {[0]=0xe2, [1]=0x90, [2]=0x80}; + ctrl_symbol[2] |= *s; + zg = string_catn(zg, ctrl_symbol, 3); + if (precision >= 0) precision += 2; + } + else + zg = string_catn(zg, CUS s, 1); break; } - } if (zg) { s = CS zg->s; slen = gstring_length(zg); } - else { s = null; slen = Ustrlen(s); } + else { s = ""; slen = 0; } } else { @@ -1667,6 +1720,56 @@ while (*fp) } goto INSERT_GSTRING; + case 'Z': /* pdkim-style "quoteprint" */ + { + gstring * zg = NULL; + int p = precision; /* If given, we can handle embedded NULs */ + + s = va_arg(ap, char *); + for ( ; precision >= 0 || *s; s++) + if (p >= 0 && --p < 0) + break; + else switch (*s) + { + case ' ' : zg = string_catn(zg, US"{SP}", 4); break; + case '\t': zg = string_catn(zg, US"{TB}", 4); break; + case '\r': zg = string_catn(zg, US"{CR}", 4); break; + case '\n': zg = string_catn(zg, US"{LF}", 4); break; + case '{' : zg = string_catn(zg, US"{BO}", 4); break; + case '}' : zg = string_catn(zg, US"{BC}", 4); break; + default: + { + uschar u = *s; + if ( (u < 32) || (u > 127) ) + zg = string_fmt_append(zg, "{%02x}", u); + else + zg = string_catn(zg, US s, 1); + break; + } + } + if (zg) { s = CS zg->s; precision = slen = gstring_length(zg); } + else { s = ""; slen = 0; } + } + goto INSERT_GSTRING; + + case 'H': /* pdkim-style "hexprint" */ + { + s = va_arg(ap, char *); + if (precision < 0) break; /* precision must be given */ + if (s) + { + gstring * zg = NULL; + for (int p = precision; p > 0; p--) + zg = string_fmt_append(zg, "%02x", * US s++); + + if (zg) { s = CS zg->s; precision = slen = gstring_length(zg); } + else { s = ""; slen = 0; } + } + else + { s = ""; precision = slen = 6; } + } + goto INSERT_GSTRING; + #endif case 's': case 'S': /* Forces *lower* case */ @@ -1705,7 +1808,7 @@ while (*fp) } /* If a width is not specified and the precision is specified, set - the width to the precision, or the string length if shorted. */ + the width to the precision, or the string length if shorter. */ else if (precision >= 0) width = precision < slen ? precision : slen;