diff --git a/common/format-output.c b/common/format-output.c index c6ab3bf4..c7e021b5 100644 --- a/common/format-output.c +++ b/common/format-output.c @@ -40,6 +40,48 @@ static void print_uuid(const u8 *uuid) } } +static void print_escaped(const char *str) +{ + while (*str) { + switch (*str) { + case '\b': /* 0x08 */ + putchar('\\'); + putchar('b'); + break; + case '\t': /* 0x09 */ + putchar('\\'); + putchar('t'); + break; + case '\n': /* 0x0a */ + putchar('\\'); + putchar('n'); + break; + case '\f': /* 0x0c */ + putchar('\\'); + putchar('f'); + break; + case '\r': /* 0x0d */ + putchar('\\'); + putchar('r'); + break; + /* Other control characters from 0 .. 31 */ + case '\v': /* 0x0b */ + case 0x00 ... 0x07: + case 0x0e ... 0x1f: + printf("\\u%04x", *str); + break; + /* '/' (solidus) not escaped */ + case '"': + case '\\': + putchar('\\'); + /* fallthrough */ + default: + putchar(*str); + } + str++; + } +} + static void fmt_indent1(int indent) { while (indent--) @@ -281,6 +323,10 @@ void fmt_print(struct format_ctx *fctx, const char* key, ...) if (row->fmt[0] == '%') { vprintf(row->fmt, args); + } else if (strcmp(row->fmt, "str") == 0) { + const char *str = va_arg(args, const char *); + + print_escaped(str); } else if (strcmp(row->fmt, "uuid") == 0) { const u8 *uuid = va_arg(args, const u8*); diff --git a/tests/json-formatter-test.c b/tests/json-formatter-test.c index c9289864..cfe47709 100644 --- a/tests/json-formatter-test.c +++ b/tests/json-formatter-test.c @@ -66,12 +66,38 @@ void test1() fmt_end(&fctx); } +/* Escaped strings */ +void test2_escape() +{ + static const struct rowspec rows1[] = { + { .key = "devid", .fmt = "%llu", .out_text = "devid", .out_json = "devid" }, + { .key = "path1", .fmt = "str", .out_text = "path1", .out_json = "path1" }, + { .key = "path2", .fmt = "str", .out_text = "path2", .out_json = "path2" }, + ROWSPEC_END + }; + struct format_ctx fctx; + char control_chars[] = { [0] = '.', [0x20] = 0 }; + int i; + + for (i = 1; i < 0x20; i++) + control_chars[i] = i; + + fmt_start(&fctx, rows1, 32, 0); + fmt_print_start_group(&fctx, "device-info", JSON_TYPE_MAP); + fmt_print(&fctx, "devid", 1); + fmt_print(&fctx, "path1", "/fun\ny/p\th/\b/\\/\f\"quo\rte\""); + fmt_print(&fctx, "path2", control_chars); + fmt_print_end_group(&fctx, NULL); + fmt_end(&fctx); +} + int main(int argc, char **argv) { int testno; static void (*tests[])() = { test_simple_empty, test1, + test2_escape, }; btrfs_config_init();