|  | #include "test-tool.h" | 
|  | #include "cache.h" | 
|  | #include "json-writer.h" | 
|  |  | 
|  | static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}"; | 
|  | static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}"; | 
|  | static const char *expect_obj3 = "{\"a\":0,\"b\":4294967295,\"c\":9223372036854775807}"; | 
|  | static const char *expect_obj4 = "{\"t\":true,\"f\":false,\"n\":null}"; | 
|  | static const char *expect_obj5 = "{\"abc\\tdef\":\"abc\\\\def\"}"; | 
|  | static const char *expect_obj6 = "{\"a\":3.14}"; | 
|  |  | 
|  | static const char *pretty_obj1 = ("{\n" | 
|  | "  \"a\": \"abc\",\n" | 
|  | "  \"b\": 42,\n" | 
|  | "  \"c\": true\n" | 
|  | "}"); | 
|  | static const char *pretty_obj2 = ("{\n" | 
|  | "  \"a\": -1,\n" | 
|  | "  \"b\": 2147483647,\n" | 
|  | "  \"c\": 0\n" | 
|  | "}"); | 
|  | static const char *pretty_obj3 = ("{\n" | 
|  | "  \"a\": 0,\n" | 
|  | "  \"b\": 4294967295,\n" | 
|  | "  \"c\": 9223372036854775807\n" | 
|  | "}"); | 
|  | static const char *pretty_obj4 = ("{\n" | 
|  | "  \"t\": true,\n" | 
|  | "  \"f\": false,\n" | 
|  | "  \"n\": null\n" | 
|  | "}"); | 
|  |  | 
|  | static struct json_writer obj1 = JSON_WRITER_INIT; | 
|  | static struct json_writer obj2 = JSON_WRITER_INIT; | 
|  | static struct json_writer obj3 = JSON_WRITER_INIT; | 
|  | static struct json_writer obj4 = JSON_WRITER_INIT; | 
|  | static struct json_writer obj5 = JSON_WRITER_INIT; | 
|  | static struct json_writer obj6 = JSON_WRITER_INIT; | 
|  |  | 
|  | static void make_obj1(int pretty) | 
|  | { | 
|  | jw_object_begin(&obj1, pretty); | 
|  | { | 
|  | jw_object_string(&obj1, "a", "abc"); | 
|  | jw_object_intmax(&obj1, "b", 42); | 
|  | jw_object_true(&obj1, "c"); | 
|  | } | 
|  | jw_end(&obj1); | 
|  | } | 
|  |  | 
|  | static void make_obj2(int pretty) | 
|  | { | 
|  | jw_object_begin(&obj2, pretty); | 
|  | { | 
|  | jw_object_intmax(&obj2, "a", -1); | 
|  | jw_object_intmax(&obj2, "b", 0x7fffffff); | 
|  | jw_object_intmax(&obj2, "c", 0); | 
|  | } | 
|  | jw_end(&obj2); | 
|  | } | 
|  |  | 
|  | static void make_obj3(int pretty) | 
|  | { | 
|  | jw_object_begin(&obj3, pretty); | 
|  | { | 
|  | jw_object_intmax(&obj3, "a", 0); | 
|  | jw_object_intmax(&obj3, "b", 0xffffffff); | 
|  | jw_object_intmax(&obj3, "c", 0x7fffffffffffffffULL); | 
|  | } | 
|  | jw_end(&obj3); | 
|  | } | 
|  |  | 
|  | static void make_obj4(int pretty) | 
|  | { | 
|  | jw_object_begin(&obj4, pretty); | 
|  | { | 
|  | jw_object_true(&obj4, "t"); | 
|  | jw_object_false(&obj4, "f"); | 
|  | jw_object_null(&obj4, "n"); | 
|  | } | 
|  | jw_end(&obj4); | 
|  | } | 
|  |  | 
|  | static void make_obj5(int pretty) | 
|  | { | 
|  | jw_object_begin(&obj5, pretty); | 
|  | { | 
|  | jw_object_string(&obj5, "abc" "\x09" "def", "abc" "\\" "def"); | 
|  | } | 
|  | jw_end(&obj5); | 
|  | } | 
|  |  | 
|  | static void make_obj6(int pretty) | 
|  | { | 
|  | jw_object_begin(&obj6, pretty); | 
|  | { | 
|  | jw_object_double(&obj6, "a", 2, 3.14159); | 
|  | } | 
|  | jw_end(&obj6); | 
|  | } | 
|  |  | 
|  | static const char *expect_arr1 = "[\"abc\",42,true]"; | 
|  | static const char *expect_arr2 = "[-1,2147483647,0]"; | 
|  | static const char *expect_arr3 = "[0,4294967295,9223372036854775807]"; | 
|  | static const char *expect_arr4 = "[true,false,null]"; | 
|  |  | 
|  | static const char *pretty_arr1 = ("[\n" | 
|  | "  \"abc\",\n" | 
|  | "  42,\n" | 
|  | "  true\n" | 
|  | "]"); | 
|  | static const char *pretty_arr2 = ("[\n" | 
|  | "  -1,\n" | 
|  | "  2147483647,\n" | 
|  | "  0\n" | 
|  | "]"); | 
|  | static const char *pretty_arr3 = ("[\n" | 
|  | "  0,\n" | 
|  | "  4294967295,\n" | 
|  | "  9223372036854775807\n" | 
|  | "]"); | 
|  | static const char *pretty_arr4 = ("[\n" | 
|  | "  true,\n" | 
|  | "  false,\n" | 
|  | "  null\n" | 
|  | "]"); | 
|  |  | 
|  | static struct json_writer arr1 = JSON_WRITER_INIT; | 
|  | static struct json_writer arr2 = JSON_WRITER_INIT; | 
|  | static struct json_writer arr3 = JSON_WRITER_INIT; | 
|  | static struct json_writer arr4 = JSON_WRITER_INIT; | 
|  |  | 
|  | static void make_arr1(int pretty) | 
|  | { | 
|  | jw_array_begin(&arr1, pretty); | 
|  | { | 
|  | jw_array_string(&arr1, "abc"); | 
|  | jw_array_intmax(&arr1, 42); | 
|  | jw_array_true(&arr1); | 
|  | } | 
|  | jw_end(&arr1); | 
|  | } | 
|  |  | 
|  | static void make_arr2(int pretty) | 
|  | { | 
|  | jw_array_begin(&arr2, pretty); | 
|  | { | 
|  | jw_array_intmax(&arr2, -1); | 
|  | jw_array_intmax(&arr2, 0x7fffffff); | 
|  | jw_array_intmax(&arr2, 0); | 
|  | } | 
|  | jw_end(&arr2); | 
|  | } | 
|  |  | 
|  | static void make_arr3(int pretty) | 
|  | { | 
|  | jw_array_begin(&arr3, pretty); | 
|  | { | 
|  | jw_array_intmax(&arr3, 0); | 
|  | jw_array_intmax(&arr3, 0xffffffff); | 
|  | jw_array_intmax(&arr3, 0x7fffffffffffffffULL); | 
|  | } | 
|  | jw_end(&arr3); | 
|  | } | 
|  |  | 
|  | static void make_arr4(int pretty) | 
|  | { | 
|  | jw_array_begin(&arr4, pretty); | 
|  | { | 
|  | jw_array_true(&arr4); | 
|  | jw_array_false(&arr4); | 
|  | jw_array_null(&arr4); | 
|  | } | 
|  | jw_end(&arr4); | 
|  | } | 
|  |  | 
|  | static char *expect_nest1 = | 
|  | "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}"; | 
|  |  | 
|  | static struct json_writer nest1 = JSON_WRITER_INIT; | 
|  |  | 
|  | static void make_nest1(int pretty) | 
|  | { | 
|  | jw_object_begin(&nest1, pretty); | 
|  | { | 
|  | jw_object_sub_jw(&nest1, "obj1", &obj1); | 
|  | jw_object_sub_jw(&nest1, "arr1", &arr1); | 
|  | } | 
|  | jw_end(&nest1); | 
|  | } | 
|  |  | 
|  | static char *expect_inline1 = | 
|  | "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}"; | 
|  |  | 
|  | static char *pretty_inline1 = | 
|  | ("{\n" | 
|  | "  \"obj1\": {\n" | 
|  | "    \"a\": \"abc\",\n" | 
|  | "    \"b\": 42,\n" | 
|  | "    \"c\": true\n" | 
|  | "  },\n" | 
|  | "  \"arr1\": [\n" | 
|  | "    \"abc\",\n" | 
|  | "    42,\n" | 
|  | "    true\n" | 
|  | "  ]\n" | 
|  | "}"); | 
|  |  | 
|  | static struct json_writer inline1 = JSON_WRITER_INIT; | 
|  |  | 
|  | static void make_inline1(int pretty) | 
|  | { | 
|  | jw_object_begin(&inline1, pretty); | 
|  | { | 
|  | jw_object_inline_begin_object(&inline1, "obj1"); | 
|  | { | 
|  | jw_object_string(&inline1, "a", "abc"); | 
|  | jw_object_intmax(&inline1, "b", 42); | 
|  | jw_object_true(&inline1, "c"); | 
|  | } | 
|  | jw_end(&inline1); | 
|  | jw_object_inline_begin_array(&inline1, "arr1"); | 
|  | { | 
|  | jw_array_string(&inline1, "abc"); | 
|  | jw_array_intmax(&inline1, 42); | 
|  | jw_array_true(&inline1); | 
|  | } | 
|  | jw_end(&inline1); | 
|  | } | 
|  | jw_end(&inline1); | 
|  | } | 
|  |  | 
|  | static char *expect_inline2 = | 
|  | "[[1,2],[3,4],{\"a\":\"abc\"}]"; | 
|  |  | 
|  | static char *pretty_inline2 = | 
|  | ("[\n" | 
|  | "  [\n" | 
|  | "    1,\n" | 
|  | "    2\n" | 
|  | "  ],\n" | 
|  | "  [\n" | 
|  | "    3,\n" | 
|  | "    4\n" | 
|  | "  ],\n" | 
|  | "  {\n" | 
|  | "    \"a\": \"abc\"\n" | 
|  | "  }\n" | 
|  | "]"); | 
|  |  | 
|  | static struct json_writer inline2 = JSON_WRITER_INIT; | 
|  |  | 
|  | static void make_inline2(int pretty) | 
|  | { | 
|  | jw_array_begin(&inline2, pretty); | 
|  | { | 
|  | jw_array_inline_begin_array(&inline2); | 
|  | { | 
|  | jw_array_intmax(&inline2, 1); | 
|  | jw_array_intmax(&inline2, 2); | 
|  | } | 
|  | jw_end(&inline2); | 
|  | jw_array_inline_begin_array(&inline2); | 
|  | { | 
|  | jw_array_intmax(&inline2, 3); | 
|  | jw_array_intmax(&inline2, 4); | 
|  | } | 
|  | jw_end(&inline2); | 
|  | jw_array_inline_begin_object(&inline2); | 
|  | { | 
|  | jw_object_string(&inline2, "a", "abc"); | 
|  | } | 
|  | jw_end(&inline2); | 
|  | } | 
|  | jw_end(&inline2); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * When super is compact, we expect subs to be compacted (even if originally | 
|  | * pretty). | 
|  | */ | 
|  | static const char *expect_mixed1 = | 
|  | ("{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true}," | 
|  | "\"arr1\":[\"abc\",42,true]}"); | 
|  |  | 
|  | /* | 
|  | * When super is pretty, a compact sub (obj1) is kept compact and a pretty | 
|  | * sub (arr1) is re-indented. | 
|  | */ | 
|  | static const char *pretty_mixed1 = | 
|  | ("{\n" | 
|  | "  \"obj1\": {\"a\":\"abc\",\"b\":42,\"c\":true},\n" | 
|  | "  \"arr1\": [\n" | 
|  | "    \"abc\",\n" | 
|  | "    42,\n" | 
|  | "    true\n" | 
|  | "  ]\n" | 
|  | "}"); | 
|  |  | 
|  | static struct json_writer mixed1 = JSON_WRITER_INIT; | 
|  |  | 
|  | static void make_mixed1(int pretty) | 
|  | { | 
|  | jw_init(&obj1); | 
|  | jw_init(&arr1); | 
|  |  | 
|  | make_obj1(0); /* obj1 is compact */ | 
|  | make_arr1(1); /* arr1 is pretty */ | 
|  |  | 
|  | jw_object_begin(&mixed1, pretty); | 
|  | { | 
|  | jw_object_sub_jw(&mixed1, "obj1", &obj1); | 
|  | jw_object_sub_jw(&mixed1, "arr1", &arr1); | 
|  | } | 
|  | jw_end(&mixed1); | 
|  | } | 
|  |  | 
|  | static void cmp(const char *test, const struct json_writer *jw, const char *exp) | 
|  | { | 
|  | if (!strcmp(jw->json.buf, exp)) | 
|  | return; | 
|  |  | 
|  | printf("error[%s]: observed '%s' expected '%s'\n", | 
|  | test, jw->json.buf, exp); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | #define t(v) do { make_##v(0); cmp(#v, &v, expect_##v); } while (0) | 
|  | #define p(v) do { make_##v(1); cmp(#v, &v, pretty_##v); } while (0) | 
|  |  | 
|  | /* | 
|  | * Run some basic regression tests with some known patterns. | 
|  | * These tests also demonstrate how to use the jw_ API. | 
|  | */ | 
|  | static int unit_tests(void) | 
|  | { | 
|  | /* comptact (canonical) forms */ | 
|  | t(obj1); | 
|  | t(obj2); | 
|  | t(obj3); | 
|  | t(obj4); | 
|  | t(obj5); | 
|  | t(obj6); | 
|  |  | 
|  | t(arr1); | 
|  | t(arr2); | 
|  | t(arr3); | 
|  | t(arr4); | 
|  |  | 
|  | t(nest1); | 
|  |  | 
|  | t(inline1); | 
|  | t(inline2); | 
|  |  | 
|  | jw_init(&obj1); | 
|  | jw_init(&obj2); | 
|  | jw_init(&obj3); | 
|  | jw_init(&obj4); | 
|  |  | 
|  | jw_init(&arr1); | 
|  | jw_init(&arr2); | 
|  | jw_init(&arr3); | 
|  | jw_init(&arr4); | 
|  |  | 
|  | jw_init(&inline1); | 
|  | jw_init(&inline2); | 
|  |  | 
|  | /* pretty forms */ | 
|  | p(obj1); | 
|  | p(obj2); | 
|  | p(obj3); | 
|  | p(obj4); | 
|  |  | 
|  | p(arr1); | 
|  | p(arr2); | 
|  | p(arr3); | 
|  | p(arr4); | 
|  |  | 
|  | p(inline1); | 
|  | p(inline2); | 
|  |  | 
|  | /* mixed forms */ | 
|  | t(mixed1); | 
|  | jw_init(&mixed1); | 
|  | p(mixed1); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void get_s(int line_nr, char **s_in) | 
|  | { | 
|  | *s_in = strtok(NULL, " "); | 
|  | if (!*s_in) | 
|  | die("line[%d]: expected: <s>", line_nr); | 
|  | } | 
|  |  | 
|  | static void get_i(int line_nr, intmax_t *s_in) | 
|  | { | 
|  | char *s; | 
|  | char *endptr; | 
|  |  | 
|  | get_s(line_nr, &s); | 
|  |  | 
|  | *s_in = strtol(s, &endptr, 10); | 
|  | if (*endptr || errno == ERANGE) | 
|  | die("line[%d]: invalid integer value", line_nr); | 
|  | } | 
|  |  | 
|  | static void get_d(int line_nr, double *s_in) | 
|  | { | 
|  | char *s; | 
|  | char *endptr; | 
|  |  | 
|  | get_s(line_nr, &s); | 
|  |  | 
|  | *s_in = strtod(s, &endptr); | 
|  | if (*endptr || errno == ERANGE) | 
|  | die("line[%d]: invalid float value", line_nr); | 
|  | } | 
|  |  | 
|  | static int pretty; | 
|  |  | 
|  | #define MAX_LINE_LENGTH (64 * 1024) | 
|  |  | 
|  | static char *get_trimmed_line(char *buf, int buf_size) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | if (!fgets(buf, buf_size, stdin)) | 
|  | return NULL; | 
|  |  | 
|  | len = strlen(buf); | 
|  | while (len > 0) { | 
|  | char c = buf[len - 1]; | 
|  | if (c == '\n' || c == '\r' || c == ' ' || c == '\t') | 
|  | buf[--len] = 0; | 
|  | else | 
|  | break; | 
|  | } | 
|  |  | 
|  | while (*buf == ' ' || *buf == '\t') | 
|  | buf++; | 
|  |  | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static int scripted(void) | 
|  | { | 
|  | struct json_writer jw = JSON_WRITER_INIT; | 
|  | char buf[MAX_LINE_LENGTH]; | 
|  | char *line; | 
|  | int line_nr = 0; | 
|  |  | 
|  | line = get_trimmed_line(buf, MAX_LINE_LENGTH); | 
|  | if (!line) | 
|  | return 0; | 
|  |  | 
|  | if (!strcmp(line, "object")) | 
|  | jw_object_begin(&jw, pretty); | 
|  | else if (!strcmp(line, "array")) | 
|  | jw_array_begin(&jw, pretty); | 
|  | else | 
|  | die("expected first line to be 'object' or 'array'"); | 
|  |  | 
|  | while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) { | 
|  | char *verb; | 
|  | char *key; | 
|  | char *s_value; | 
|  | intmax_t i_value; | 
|  | double d_value; | 
|  |  | 
|  | line_nr++; | 
|  |  | 
|  | verb = strtok(line, " "); | 
|  |  | 
|  | if (!strcmp(verb, "end")) { | 
|  | jw_end(&jw); | 
|  | } | 
|  | else if (!strcmp(verb, "object-string")) { | 
|  | get_s(line_nr, &key); | 
|  | get_s(line_nr, &s_value); | 
|  | jw_object_string(&jw, key, s_value); | 
|  | } | 
|  | else if (!strcmp(verb, "object-int")) { | 
|  | get_s(line_nr, &key); | 
|  | get_i(line_nr, &i_value); | 
|  | jw_object_intmax(&jw, key, i_value); | 
|  | } | 
|  | else if (!strcmp(verb, "object-double")) { | 
|  | get_s(line_nr, &key); | 
|  | get_i(line_nr, &i_value); | 
|  | get_d(line_nr, &d_value); | 
|  | jw_object_double(&jw, key, i_value, d_value); | 
|  | } | 
|  | else if (!strcmp(verb, "object-true")) { | 
|  | get_s(line_nr, &key); | 
|  | jw_object_true(&jw, key); | 
|  | } | 
|  | else if (!strcmp(verb, "object-false")) { | 
|  | get_s(line_nr, &key); | 
|  | jw_object_false(&jw, key); | 
|  | } | 
|  | else if (!strcmp(verb, "object-null")) { | 
|  | get_s(line_nr, &key); | 
|  | jw_object_null(&jw, key); | 
|  | } | 
|  | else if (!strcmp(verb, "object-object")) { | 
|  | get_s(line_nr, &key); | 
|  | jw_object_inline_begin_object(&jw, key); | 
|  | } | 
|  | else if (!strcmp(verb, "object-array")) { | 
|  | get_s(line_nr, &key); | 
|  | jw_object_inline_begin_array(&jw, key); | 
|  | } | 
|  | else if (!strcmp(verb, "array-string")) { | 
|  | get_s(line_nr, &s_value); | 
|  | jw_array_string(&jw, s_value); | 
|  | } | 
|  | else if (!strcmp(verb, "array-int")) { | 
|  | get_i(line_nr, &i_value); | 
|  | jw_array_intmax(&jw, i_value); | 
|  | } | 
|  | else if (!strcmp(verb, "array-double")) { | 
|  | get_i(line_nr, &i_value); | 
|  | get_d(line_nr, &d_value); | 
|  | jw_array_double(&jw, i_value, d_value); | 
|  | } | 
|  | else if (!strcmp(verb, "array-true")) | 
|  | jw_array_true(&jw); | 
|  | else if (!strcmp(verb, "array-false")) | 
|  | jw_array_false(&jw); | 
|  | else if (!strcmp(verb, "array-null")) | 
|  | jw_array_null(&jw); | 
|  | else if (!strcmp(verb, "array-object")) | 
|  | jw_array_inline_begin_object(&jw); | 
|  | else if (!strcmp(verb, "array-array")) | 
|  | jw_array_inline_begin_array(&jw); | 
|  | else | 
|  | die("unrecognized token: '%s'", verb); | 
|  | } | 
|  |  | 
|  | if (!jw_is_terminated(&jw)) | 
|  | die("json not terminated: '%s'", jw.json.buf); | 
|  |  | 
|  | printf("%s\n", jw.json.buf); | 
|  |  | 
|  | strbuf_release(&jw.json); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int cmd__json_writer(int argc, const char **argv) | 
|  | { | 
|  | argc--; /* skip over "json-writer" arg */ | 
|  | argv++; | 
|  |  | 
|  | if (argc > 0 && argv[0][0] == '-') { | 
|  | if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--unit")) | 
|  | return unit_tests(); | 
|  |  | 
|  | if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--pretty")) | 
|  | pretty = 1; | 
|  | } | 
|  |  | 
|  | return scripted(); | 
|  | } |