Add %V and %H specifiers for mg_snrpintf()

This commit is contained in:
Sergey Lyubka 2022-06-22 13:15:52 +01:00
parent c446fe0c6d
commit a68f3dc4eb
4 changed files with 85 additions and 11 deletions

View File

@ -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

View File

@ -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) =

View File

@ -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) =

View File

@ -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) {