diff options
Diffstat (limited to 'src/utils/lib/jsonwrt.c')
-rw-r--r-- | src/utils/lib/jsonwrt.c | 148 |
1 files changed, 118 insertions, 30 deletions
diff --git a/src/utils/lib/jsonwrt.c b/src/utils/lib/jsonwrt.c index 00e8b9d..f2003e8 100644 --- a/src/utils/lib/jsonwrt.c +++ b/src/utils/lib/jsonwrt.c @@ -8,16 +8,105 @@ */ #include <stdio.h> #include <inttypes.h> +#include <ctype.h> +#include <cctype.h> #include "c.h" -#include "carefulputc.h" #include "jsonwrt.h" +/* + * Requirements enumerated via testing (V8, Firefox, IE11): + * + * var charsToEscape = []; + * for (var i = 0; i < 65535; i += 1) { + * try { + * JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}'); + * } catch (e) { + * charsToEscape.push(i); + * } + * } + */ +static void fputs_quoted_case_json(const char *data, FILE *out, int dir) +{ + const char *p; + + fputc('"', out); + for (p = data; p && *p; p++) { + + const unsigned int c = (unsigned int) *p; + + /* From http://www.json.org + * + * The double-quote and backslashes would break out a string or + * init an escape sequence if not escaped. + * + * Note that single-quotes and forward slashes, while they're + * in the JSON spec, don't break double-quoted strings. + */ + if (c == '"' || c == '\\') { + fputc('\\', out); + fputc(c, out); + continue; + } + + /* All non-control characters OK; do the case swap as required. */ + if (c >= 0x20) { + /* + * Don't use locale sensitive ctype.h functions for regular + * ASCII chars, because for example with Turkish locale + * (aka LANG=tr_TR.UTF-8) toupper('I') returns 'I'. + */ + if (c <= 127) + fputc(dir == 1 ? c_toupper(c) : + dir == -1 ? c_tolower(c) : *p, out); + else + fputc(dir == 1 ? toupper(c) : + dir == -1 ? tolower(c) : *p, out); + continue; + } + + /* In addition, all chars under ' ' break Node's/V8/Chrome's, and + * Firefox's JSON.parse function + */ + switch (c) { + /* Handle short-hand cases to reduce output size. C + * has most of the same stuff here, so if there's an + * "Escape for C" function somewhere in the STL, we + * should probably be using it. + */ + case '\b': + fputs("\\b", out); + break; + case '\t': + fputs("\\t", out); + break; + case '\n': + fputs("\\n", out); + break; + case '\f': + fputs("\\f", out); + break; + case '\r': + fputs("\\r", out); + break; + default: + /* Other assorted control characters */ + fprintf(out, "\\u00%02x", c); + break; + } + } + fputc('"', out); +} + +#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0) +#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1) +#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1) void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent) { fmt->out = out; fmt->indent = indent; + fmt->after_close = 0; } void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) @@ -30,12 +119,16 @@ void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) { - if (fmt->postponed_break && !name) - ; - else { + if (name) { + if (fmt->after_close) + fputs(",\n", fmt->out); ul_jsonwrt_indent(fmt); - if (name) - fputs_quoted_json_lower(name, fmt->out); + fputs_quoted_json_lower(name, fmt->out); + } else { + if (fmt->after_close) + fputs(",", fmt->out); + else + ul_jsonwrt_indent(fmt); } switch (type) { @@ -44,21 +137,22 @@ void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) fmt->indent++; break; case UL_JSON_ARRAY: - fputs(name ? ": [\n" : "{\n", fmt->out); + fputs(name ? ": [\n" : "[\n", fmt->out); fmt->indent++; break; case UL_JSON_VALUE: fputs(name ? ": " : " ", fmt->out); break; } - fmt->postponed_break = 0; + fmt->after_close = 0; } -void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type, int islast) +void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) { - if (fmt->indent == 0) { - fputs("}\n", fmt->out); + if (fmt->indent == 1) { + fputs("\n}\n", fmt->out); fmt->indent--; + fmt->after_close = 1; return; } assert(fmt->indent > 0); @@ -66,63 +160,57 @@ void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type, int islast) switch (type) { case UL_JSON_OBJECT: fmt->indent--; + fputc('\n', fmt->out); ul_jsonwrt_indent(fmt); - fputs(islast ? "}" : "},", fmt->out); + fputs("}", fmt->out); break; case UL_JSON_ARRAY: fmt->indent--; + fputc('\n', fmt->out); ul_jsonwrt_indent(fmt); - fputs(islast ? "]" : "],", fmt->out); + fputs("]", fmt->out); break; case UL_JSON_VALUE: - if (!islast) - fputc(',', fmt->out); break; } - if (!islast && (type == UL_JSON_OBJECT || type == UL_JSON_ARRAY)) - fmt->postponed_break = 1; - else { - fputc('\n', fmt->out); - fmt->postponed_break = 0; - } + fmt->after_close = 1; } void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt, - const char *name, const char *data, int islast) + const char *name, const char *data) { ul_jsonwrt_value_open(fmt, name); if (data && *data) fputs(data, fmt->out); else fputs("null", fmt->out); - ul_jsonwrt_value_close(fmt, islast); + ul_jsonwrt_value_close(fmt); } void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, - const char *name, const char *data, int islast) + const char *name, const char *data) { ul_jsonwrt_value_open(fmt, name); if (data && *data) fputs_quoted_json(data, fmt->out); else fputs("null", fmt->out); - ul_jsonwrt_value_close(fmt, islast); + ul_jsonwrt_value_close(fmt); } void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, - const char *name, uint64_t data, int islast) + const char *name, uint64_t data) { ul_jsonwrt_value_open(fmt, name); fprintf(fmt->out, "%"PRIu64, data); - ul_jsonwrt_value_close(fmt, islast); + ul_jsonwrt_value_close(fmt); } void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, - const char *name, int data, int islast) + const char *name, int data) { ul_jsonwrt_value_open(fmt, name); fputs(data ? "true" : "false", fmt->out); - ul_jsonwrt_value_close(fmt, islast); + ul_jsonwrt_value_close(fmt); } - |