]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: add `%pSQ` and `%pSE` string escape formats
authorDavid Lamparter <equinox@diac24.net>
Mon, 22 Mar 2021 09:12:42 +0000 (10:12 +0100)
committerDavid Lamparter <equinox@diac24.net>
Tue, 30 Mar 2021 20:34:56 +0000 (22:34 +0200)
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>
lib/printfrr.h
lib/strformat.c
tests/lib/test_printfrr.c

index 7083e8b58251fb487e34589f62ddcbee2fce9ec2..4338ac3a2fa365e741a5b338f79df25a307f3eb6 100644 (file)
@@ -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... */
index 8e49a666fa74a6c67a605cbbaf718d94fcb3f629..431e573a0c830e42bafe0ccd0ee189331e30f6b2 100644 (file)
 #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;
+}
index 1ef10b19d0a2e2d9c22dcc6248788e5043d6fbfb..21b3a916b862464b49b69f527aa22882b53fba92 100644 (file)
@@ -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;
 }