diff options
author | David Lamparter <equinox@diac24.net> | 2021-03-22 10:12:42 +0100 |
---|---|---|
committer | David Lamparter <equinox@diac24.net> | 2021-03-30 22:34:56 +0200 |
commit | 7798203f5cf93b35aed96925f3ffd6fa00a44790 (patch) | |
tree | 11649e2d2d210eef677ac6a07065b805278cd636 | |
parent | lib: add `%*pHX` + `%*pHS` hexdump in printfrr (diff) | |
download | frr-7798203f5cf93b35aed96925f3ffd6fa00a44790.tar.xz frr-7798203f5cf93b35aed96925f3ffd6fa00a44790.zip |
lib: add `%pSQ` and `%pSE` string escape formats
These are for string quoting (`%pSQ`) and string escaping (`%pSE`); the
sets / escape methods are currently rather "basic" and might be extended
in the future.
Signed-off-by: David Lamparter <equinox@diac24.net>
-rw-r--r-- | lib/printfrr.h | 3 | ||||
-rw-r--r-- | lib/strformat.c | 197 | ||||
-rw-r--r-- | tests/lib/test_printfrr.c | 37 |
3 files changed, 232 insertions, 5 deletions
diff --git a/lib/printfrr.h b/lib/printfrr.h index 7083e8b58..4338ac3a2 100644 --- a/lib/printfrr.h +++ b/lib/printfrr.h @@ -283,6 +283,9 @@ struct va_format { #pragma FRR printfrr_ext "%pHS" (signed char *) #pragma FRR printfrr_ext "%pHS" (unsigned char *) #pragma FRR printfrr_ext "%pHS" (void *) + +#pragma FRR printfrr_ext "%pSE" (char *) +#pragma FRR printfrr_ext "%pSQ" (char *) #endif /* when using non-ISO-C compatible extension specifiers... */ diff --git a/lib/strformat.c b/lib/strformat.c index 8e49a666f..431e573a0 100644 --- a/lib/strformat.c +++ b/lib/strformat.c @@ -18,7 +18,10 @@ #include "config.h" #endif +#include "compiler.h" + #include <string.h> +#include <ctype.h> #include "printfrr.h" @@ -73,3 +76,197 @@ static ssize_t printfrr_hexdstr(struct fbuf *buf, struct printfrr_eargs *ea, return ret; } + +enum escape_flags { + ESC_N_R_T = (1 << 0), /* use \n \r \t instead of \x0a ...*/ + ESC_SPACE = (1 << 1), /* \ */ + ESC_BACKSLASH = (1 << 2), /* \\ */ + ESC_DBLQUOTE = (1 << 3), /* \" */ + ESC_SGLQUOTE = (1 << 4), /* \' */ + ESC_BACKTICK = (1 << 5), /* \` */ + ESC_DOLLAR = (1 << 6), /* \$ */ + ESC_CLBRACKET = (1 << 7), /* \] for RFC5424 syslog */ + ESC_OTHER = (1 << 8), /* remaining non-alpha */ + + ESC_ALL = ESC_N_R_T | ESC_SPACE | ESC_BACKSLASH | ESC_DBLQUOTE + | ESC_SGLQUOTE | ESC_DOLLAR | ESC_OTHER, + ESC_QUOTSTRING = ESC_N_R_T | ESC_BACKSLASH | ESC_DBLQUOTE, + /* if needed: ESC_SHELL = ... */ +}; + +static ssize_t bquote(struct fbuf *buf, const uint8_t *pos, size_t len, + unsigned int flags) +{ + ssize_t ret = 0; + const uint8_t *end = pos + len; + + for (; pos < end; pos++) { + /* here's to hoping this might be a bit faster... */ + if (__builtin_expect(!!isalnum(*pos), 1)) { + ret += bputch(buf, *pos); + continue; + } + + switch (*pos) { + case '%': + case '+': + case ',': + case '-': + case '.': + case '/': + case ':': + case '@': + case '_': + ret += bputch(buf, *pos); + continue; + + case '\r': + if (!(flags & ESC_N_R_T)) + break; + ret += bputch(buf, '\\'); + ret += bputch(buf, 'r'); + continue; + case '\n': + if (!(flags & ESC_N_R_T)) + break; + ret += bputch(buf, '\\'); + ret += bputch(buf, 'n'); + continue; + case '\t': + if (!(flags & ESC_N_R_T)) + break; + ret += bputch(buf, '\\'); + ret += bputch(buf, 't'); + continue; + + case ' ': + if (flags & ESC_SPACE) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '\\': + if (flags & ESC_BACKSLASH) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '"': + if (flags & ESC_DBLQUOTE) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '\'': + if (flags & ESC_SGLQUOTE) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '`': + if (flags & ESC_BACKTICK) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '$': + if (flags & ESC_DOLLAR) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case ']': + if (flags & ESC_CLBRACKET) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + /* remaining: !#&'()*;<=>?[^{|}~ */ + + default: + if (*pos >= 0x20 && *pos < 0x7f) { + if (flags & ESC_OTHER) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + } + } + ret += bputch(buf, '\\'); + ret += bputch(buf, 'x'); + ret += bputhex(buf, *pos); + } + + return ret; +} + +printfrr_ext_autoreg_p("SE", printfrr_escape) +static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + ssize_t len = printfrr_ext_len(ea); + const uint8_t *ptr = vptr; + bool null_is_empty = false; + + if (ea->fmt[0] == 'n') { + null_is_empty = true; + ea->fmt++; + } + + if (!ptr) { + if (null_is_empty) + return 0; + return bputs(buf, "(null)"); + } + + if (len < 0) + len = strlen((const char *)ptr); + + return bquote(buf, ptr, len, ESC_ALL); +} + +printfrr_ext_autoreg_p("SQ", printfrr_quote) +static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + ssize_t len = printfrr_ext_len(ea); + const uint8_t *ptr = vptr; + ssize_t ret = 0; + bool null_is_empty = false; + bool do_quotes = false; + unsigned int flags = ESC_QUOTSTRING; + + while (ea->fmt[0]) { + switch (ea->fmt[0]) { + case 'n': + null_is_empty = true; + ea->fmt++; + continue; + case 'q': + do_quotes = true; + ea->fmt++; + continue; + case 's': + flags |= ESC_CLBRACKET; + flags &= ~ESC_N_R_T; + ea->fmt++; + continue; + } + break; + } + + if (!ptr) { + if (null_is_empty) + return bputs(buf, do_quotes ? "\"\"" : ""); + return bputs(buf, "(null)"); + } + + if (len < 0) + len = strlen((const char *)ptr); + + if (do_quotes) + ret += bputch(buf, '"'); + ret += bquote(buf, ptr, len, flags); + if (do_quotes) + ret += bputch(buf, '"'); + return ret; +} diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 1ef10b19d..21b3a916b 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -218,13 +218,40 @@ int main(int argc, char **argv) uint8_t randhex[] = { 0x12, 0x34, 0x00, 0xca, 0xfe, 0x00, 0xaa, 0x55 }; - printchk("12 34 00 ca fe 00 aa 55", "%.8pHX", randhex); - printchk("12 34 00 ca fe 00 aa 55", "%.*pHX", + FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.8pHX", randhex)); + FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.*pHX", + (int)sizeof(randhex), randhex)); + FMT_NSTD(printchk("12 34 00 ca", "%.4pHX", randhex)); + + printchk("12 34 00 ca fe 00 aa 55", "%8pHX", randhex); + printchk("12 34 00 ca fe 00 aa 55", "%*pHX", (int)sizeof(randhex), randhex); - printchk("12 34 00 ca", "%.4pHX", randhex); + printchk("12 34 00 ca", "%4pHX", randhex); + + printchk("", "%pHX", randhex); + + printchk("12:34:00:ca:fe:00:aa:55", "%8pHXc", randhex); + printchk("123400cafe00aa55", "%8pHXn", randhex); + + printchk("/test/pa\\ th/\\~spe\\ncial\\x01/file.name", "%pSE", + "/test/pa th/~spe\ncial\x01/file.name"); + printchk("/test/pa\\ th/\\~spe\\n", "%17pSE", + "/test/pa th/~spe\ncial\x01/file.name"); + + char nulltest[] = { 'n', 'u', 0, 'l', 'l' }; + + printchk("nu\\x00ll", "%5pSE", nulltest); + printchk("nu\\x00ll", "%*pSE", 5, nulltest); - printchk("12:34:00:ca:fe:00:aa:55", "%.8pHXc", randhex); - printchk("123400cafe00aa55", "%.8pHXn", randhex); + printchk("bl\\\"ah\\x01te[st\\nab]c", "%pSQ", + "bl\"ah\x01te[st\nab]c"); + printchk("\"bl\\\"ah\\x01te[st\\nab]c\"", "%pSQq", + "bl\"ah\x01te[st\nab]c"); + printchk("\"bl\\\"ah\\x01te[st\\x0aab\\]c\"", "%pSQqs", + "bl\"ah\x01te[st\nab]c"); + printchk("\"\"", "%pSQqn", ""); + printchk("\"\"", "%pSQqn", (char *)NULL); + printchk("(null)", "%pSQq", (char *)NULL); return !!errors; } |