From a68f3dc4eb9f8316e96424c663e89a3dc1eda692 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Wed, 22 Jun 2022 13:15:52 +0100 Subject: [PATCH] Add %V and %H specifiers for mg_snrpintf() --- docs/README.md | 18 ++++++++++-------- mongoose.c | 27 +++++++++++++++++++++++++++ src/str.c | 27 +++++++++++++++++++++++++++ test/unit_test.c | 24 +++++++++++++++++++++--- 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1e5c90b3..62dce223 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2550,7 +2550,7 @@ standard function does, but in a predictable way that does not depend on the C library or the build environment. The return value can be larger than the buffer length `len`, in which case the overflow bytes are not printed. Mongoose library is often used to exchange data in JSON format, therefore a -non-standard `%Q` specifier for formatting JSON strings is also supported. +non-standard `%Q`, `%V`, `%H` specifiers for formatting JSON strings is also supported. Parameters: - `buf` - Pointer to pointer to output buffer @@ -2561,13 +2561,15 @@ Supported format specifiers: - `hhd`, `hd`, `d`, `ld`, `lld` - for `char`, `short`, `int`, `long`, `int64_t` - `hhu`, `hu`, `u`, `lu`, `llu` - same but for unsigned variants - `hhx`, `hx`, `x`, `lx`, `llx` - same, unsigned and hex output -- `s` - for `char *` -- `Q` - for `char *`, outputs double-quoted JSON-escaped string (extension) -- `M` - for `size_t (*)(char *, size_t, va_list *)`, calls another print function (extension) -- `g`, `f` - for `double` -- `c` - for `char` -- `%` - the `%` character itself -- `p` - for any pointer, prints `0x.....` hex value +- `s` - expect `char *` +- `Q` - expect `char *`, outputs double-quoted JSON-escaped string (extension) +- `H` - expect `int`, `void *`, outputs double-quoted hex string (extension) +- `V` - expect `int`, `void *`, outputs double-quoted base64 string (extension) +- `M` - expect `size_t (*)(char *, size_t, va_list *)`, calls another print function (extension) +- `g`, `f` - expect `double` +- `c` - expect `char` +- `%` - expect `%` character itself +- `p` - expect any pointer, prints `0x.....` hex value - `%X.Y` - optional width and precision modifiers - `%.*` - optional precision modifier specified as `int` argument diff --git a/mongoose.c b/mongoose.c index c77da32b..371d4a2c 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4518,6 +4518,33 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) { int p = va_arg(*ap, int); if (n < len) buf[n] = (char) p; n++; + } else if (c == 'H') { + // Print hex-encoded double-quoted string + size_t bl = (size_t) va_arg(*ap, int); + uint8_t *p = va_arg(*ap, uint8_t *), dquote = '"'; + const char *hex = "0123456789abcdef"; + n += mg_copys(buf, len, n, (char *) &dquote, 1); + for (j = 0; j < bl; j++) { + n += mg_copys(buf, len, n, (char *) &hex[(p[j] >> 4) & 15], 1); + n += mg_copys(buf, len, n, (char *) &hex[p[j] & 15], 1); + } + n += mg_copys(buf, len, n, (char *) &dquote, 1); + } else if (c == 'V') { + // Print base64-encoded double-quoted string + size_t bl = (size_t) va_arg(*ap, int); + uint8_t *p = va_arg(*ap, uint8_t *), dquote = '"'; + const char *t = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + n += mg_copys(buf, len, n, (char *) &dquote, 1); + for (j = 0; j < bl; j += 3) { + uint8_t c1 = p[j], c2 = j + 1 < bl ? p[j + 1] : 0, + c3 = j + 2 < bl ? p[j + 2] : 0; + char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='}; + if (j + 1 < bl) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)]; + if (j + 2 < bl) tmp[3] = t[c3 & 63]; + n += mg_copys(buf, len, n, tmp, sizeof(tmp)); + } + n += mg_copys(buf, len, n, (char *) &dquote, 1); } else if (c == 's' || c == 'Q') { char *p = va_arg(*ap, char *); size_t (*fn)(char *, size_t, size_t, char *, size_t) = diff --git a/src/str.c b/src/str.c index 73a705a3..132b5686 100644 --- a/src/str.c +++ b/src/str.c @@ -397,6 +397,33 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) { int p = va_arg(*ap, int); if (n < len) buf[n] = (char) p; n++; + } else if (c == 'H') { + // Print hex-encoded double-quoted string + size_t bl = (size_t) va_arg(*ap, int); + uint8_t *p = va_arg(*ap, uint8_t *), dquote = '"'; + const char *hex = "0123456789abcdef"; + n += mg_copys(buf, len, n, (char *) &dquote, 1); + for (j = 0; j < bl; j++) { + n += mg_copys(buf, len, n, (char *) &hex[(p[j] >> 4) & 15], 1); + n += mg_copys(buf, len, n, (char *) &hex[p[j] & 15], 1); + } + n += mg_copys(buf, len, n, (char *) &dquote, 1); + } else if (c == 'V') { + // Print base64-encoded double-quoted string + size_t bl = (size_t) va_arg(*ap, int); + uint8_t *p = va_arg(*ap, uint8_t *), dquote = '"'; + const char *t = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + n += mg_copys(buf, len, n, (char *) &dquote, 1); + for (j = 0; j < bl; j += 3) { + uint8_t c1 = p[j], c2 = j + 1 < bl ? p[j + 1] : 0, + c3 = j + 2 < bl ? p[j + 2] : 0; + char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='}; + if (j + 1 < bl) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)]; + if (j + 2 < bl) tmp[3] = t[c3 & 63]; + n += mg_copys(buf, len, n, tmp, sizeof(tmp)); + } + n += mg_copys(buf, len, n, (char *) &dquote, 1); } else if (c == 's' || c == 'Q') { char *p = va_arg(*ap, char *); size_t (*fn)(char *, size_t, size_t, char *, size_t) = diff --git a/test/unit_test.c b/test/unit_test.c index a477c056..4b8c15d1 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1389,9 +1389,12 @@ static size_t pf2(char *buf, size_t len, va_list *ap) { } static void test_str(void) { - struct mg_str s = mg_strdup(mg_str("a")); - ASSERT(mg_strcmp(s, mg_str("a")) == 0); - free((void *) s.ptr); + { + struct mg_str s = mg_strdup(mg_str("a")); + ASSERT(mg_strcmp(s, mg_str("a")) == 0); + free((void *) s.ptr); + } + ASSERT(mg_strcmp(mg_str(""), mg_str(NULL)) == 0); ASSERT(mg_strcmp(mg_str("a"), mg_str("b")) < 0); ASSERT(mg_strcmp(mg_str("b"), mg_str("a")) > 0); @@ -1552,6 +1555,21 @@ static void test_str(void) { TESTDOUBLE("%g", -HUGE_VAL, "-inf"); #endif } + + { + const char *expected = "[\"MA==\",\"MAo=\",\"MAr+\",\"MAr+Zw==\"]"; + char tmp[100], s[] = "0\n\xfeg"; + ASSERT(mg_snprintf(tmp, sizeof(tmp), "[%V,%V,%V,%V]", 1, s, 2, s, 3, s, 4, + s) == 33); + ASSERT(strcmp(tmp, expected) == 0); + } + + { + const char *expected = "\"002001200220616263\""; + char tmp[100], s[] = "\x00 \x01 \x02 abc"; + ASSERT(mg_snprintf(tmp, sizeof(tmp), "%H", 9, s) == 20); + ASSERT(strcmp(tmp, expected) == 0); + } } static void fn1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {