/* * Copyright (c) 2004-2013 Sergey Lyubka * Copyright (c) 2018 Cesanta Software Limited * All rights reserved * * Licensed under the Apache License, Version 2.0 (the ""License""); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an ""AS IS"" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ #include "frozen.h" #include #include #include #include #include #if !defined(WEAK) #if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) #define WEAK __attribute__((weak)) #else #define WEAK #endif #endif #ifdef _WIN32 #undef snprintf #undef vsnprintf #define snprintf cs_win_snprintf #define vsnprintf cs_win_vsnprintf int cs_win_snprintf(char *str, size_t size, const char *format, ...); int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap); #if _MSC_VER >= 1700 || (defined(__GNUC__)) #include #else typedef _int64 int64_t; typedef unsigned _int64 uint64_t; #endif #define PRId64 "I64d" #define PRIu64 "I64u" #else /* _WIN32 */ /* wants this for C++ */ #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #endif /* _WIN32 */ #ifndef INT64_FMT #define INT64_FMT PRId64 #endif #ifndef UINT64_FMT #define UINT64_FMT PRIu64 #endif #ifndef va_copy #define va_copy(x, y) x = y #endif #ifndef JSON_ENABLE_ARRAY #define JSON_ENABLE_ARRAY 1 #endif struct frozen { const char *end; const char *cur; const char *cur_name; size_t cur_name_len; /* For callback API */ char path[JSON_MAX_PATH_LEN]; size_t path_len; void *callback_data; json_walk_callback_t callback; }; struct fstate { const char *ptr; size_t path_len; }; #define SET_STATE(fr, ptr, str, len) \ struct fstate fstate = {(ptr), (fr)->path_len}; \ json_append_to_path((fr), (str), (len)); #define CALL_BACK(fr, tok, value, len) \ do { \ if ((fr)->callback && \ ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')) { \ struct json_token t = {(value), (int) (len), (tok)}; \ \ /* Call the callback with the given value and current name */ \ (fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \ (fr)->path, &t); \ \ /* Reset the name */ \ (fr)->cur_name = NULL; \ (fr)->cur_name_len = 0; \ } \ } while (0) static int json_append_to_path(struct frozen *f, const char *str, int size) { int n = f->path_len; int left = sizeof(f->path) - n - 1; if (size > left) size = left; memcpy(f->path + n, str, size); f->path[n + size] = '\0'; f->path_len += size; return n; } static void json_truncate_path(struct frozen *f, size_t len) { f->path_len = len; f->path[len] = '\0'; } static int json_parse_object(struct frozen *f); static int json_parse_value(struct frozen *f); #define EXPECT(cond, err_code) \ do { \ if (!(cond)) return (err_code); \ } while (0) #define TRY(expr) \ do { \ int _n = expr; \ if (_n < 0) return _n; \ } while (0) #define END_OF_STRING (-1) static int json_left(const struct frozen *f) { return f->end - f->cur; } static int json_isspace(int ch) { return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; } static void json_skip_whitespaces(struct frozen *f) { while (f->cur < f->end && json_isspace(*f->cur)) f->cur++; } static int json_cur(struct frozen *f) { json_skip_whitespaces(f); return f->cur >= f->end ? END_OF_STRING : *(unsigned char *) f->cur; } static int json_test_and_skip(struct frozen *f, int expected) { int ch = json_cur(f); if (ch == expected) { f->cur++; return 0; } return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; } static int json_isalpha(int ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); } static int json_isdigit(int ch) { return ch >= '0' && ch <= '9'; } static int json_isxdigit(int ch) { return json_isdigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); } static int json_get_escape_len(const char *s, int len) { switch (*s) { case 'u': return len < 6 ? JSON_STRING_INCOMPLETE : json_isxdigit(s[1]) && json_isxdigit(s[2]) && json_isxdigit(s[3]) && json_isxdigit(s[4]) ? 5 : JSON_STRING_INVALID; case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': return len < 2 ? JSON_STRING_INCOMPLETE : 1; default: return JSON_STRING_INVALID; } } /* identifier = letter { letter | digit | '_' } */ static int json_parse_identifier(struct frozen *f) { EXPECT(json_isalpha(json_cur(f)), JSON_STRING_INVALID); { SET_STATE(f, f->cur, "", 0); while (f->cur < f->end && (*f->cur == '_' || json_isalpha(*f->cur) || json_isdigit(*f->cur))) { f->cur++; } json_truncate_path(f, fstate.path_len); CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); } return 0; } static int json_get_utf8_char_len(unsigned char ch) { if ((ch & 0x80) == 0) return 1; switch (ch & 0xf0) { case 0xf0: return 4; case 0xe0: return 3; default: return 2; } } /* string = '"' { quoted_printable_chars } '"' */ static int json_parse_string(struct frozen *f) { int n, ch = 0, len = 0; TRY(json_test_and_skip(f, '"')); { SET_STATE(f, f->cur, "", 0); for (; f->cur < f->end; f->cur += len) { ch = *(unsigned char *) f->cur; len = json_get_utf8_char_len((unsigned char) ch); EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ EXPECT(len <= json_left(f), JSON_STRING_INCOMPLETE); if (ch == '\\') { EXPECT((n = json_get_escape_len(f->cur + 1, json_left(f))) > 0, n); len += n; } else if (ch == '"') { json_truncate_path(f, fstate.path_len); CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); f->cur++; break; }; } } return ch == '"' ? 0 : JSON_STRING_INCOMPLETE; } /* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ static int json_parse_number(struct frozen *f) { int ch = json_cur(f); SET_STATE(f, f->cur, "", 0); if (ch == '-') f->cur++; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); if (f->cur + 1 < f->end && f->cur[0] == '0' && f->cur[1] == 'x') { f->cur += 2; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); EXPECT(json_isxdigit(f->cur[0]), JSON_STRING_INVALID); while (f->cur < f->end && json_isxdigit(f->cur[0])) f->cur++; } else { EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; if (f->cur < f->end && f->cur[0] == '.') { f->cur++; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; } if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { f->cur++; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; } } json_truncate_path(f, fstate.path_len); CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr); return 0; } #if JSON_ENABLE_ARRAY /* array = '[' [ value { ',' value } ] ']' */ static int json_parse_array(struct frozen *f) { int i = 0, current_path_len; char buf[20]; CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); TRY(json_test_and_skip(f, '[')); { { SET_STATE(f, f->cur - 1, "", 0); while (json_cur(f) != ']') { snprintf(buf, sizeof(buf), "[%d]", i); i++; current_path_len = json_append_to_path(f, buf, strlen(buf)); f->cur_name = f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/; f->cur_name_len = strlen(buf) - 2 /*braces*/; TRY(json_parse_value(f)); json_truncate_path(f, current_path_len); if (json_cur(f) == ',') f->cur++; } TRY(json_test_and_skip(f, ']')); json_truncate_path(f, fstate.path_len); CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr); } } return 0; } #endif /* JSON_ENABLE_ARRAY */ static int json_expect(struct frozen *f, const char *s, int len, enum json_token_type tok_type) { int i, n = json_left(f); SET_STATE(f, f->cur, "", 0); for (i = 0; i < len; i++) { if (i >= n) return JSON_STRING_INCOMPLETE; if (f->cur[i] != s[i]) return JSON_STRING_INVALID; } f->cur += len; json_truncate_path(f, fstate.path_len); CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr); return 0; } /* value = 'null' | 'true' | 'false' | number | string | array | object */ static int json_parse_value(struct frozen *f) { int ch = json_cur(f); switch (ch) { case '"': TRY(json_parse_string(f)); break; case '{': TRY(json_parse_object(f)); break; #if JSON_ENABLE_ARRAY case '[': TRY(json_parse_array(f)); break; #endif case 'n': TRY(json_expect(f, "null", 4, JSON_TYPE_NULL)); break; case 't': TRY(json_expect(f, "true", 4, JSON_TYPE_TRUE)); break; case 'f': TRY(json_expect(f, "false", 5, JSON_TYPE_FALSE)); break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': TRY(json_parse_number(f)); break; default: return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; } return 0; } /* key = identifier | string */ static int json_parse_key(struct frozen *f) { int ch = json_cur(f); if (json_isalpha(ch)) { TRY(json_parse_identifier(f)); } else if (ch == '"') { TRY(json_parse_string(f)); } else { return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; } return 0; } /* pair = key ':' value */ static int json_parse_pair(struct frozen *f) { int current_path_len; const char *tok; json_skip_whitespaces(f); tok = f->cur; TRY(json_parse_key(f)); { f->cur_name = *tok == '"' ? tok + 1 : tok; f->cur_name_len = *tok == '"' ? f->cur - tok - 2 : f->cur - tok; current_path_len = json_append_to_path(f, f->cur_name, f->cur_name_len); } TRY(json_test_and_skip(f, ':')); TRY(json_parse_value(f)); json_truncate_path(f, current_path_len); return 0; } /* object = '{' pair { ',' pair } '}' */ static int json_parse_object(struct frozen *f) { CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); TRY(json_test_and_skip(f, '{')); { SET_STATE(f, f->cur - 1, ".", 1); while (json_cur(f) != '}') { TRY(json_parse_pair(f)); if (json_cur(f) == ',') f->cur++; } TRY(json_test_and_skip(f, '}')); json_truncate_path(f, fstate.path_len); CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); } return 0; } static int json_doit(struct frozen *f) { if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID; if (f->end == f->cur) return JSON_STRING_INCOMPLETE; return json_parse_value(f); } int json_escape(struct json_out *out, const char *p, size_t len) WEAK; int json_escape(struct json_out *out, const char *p, size_t len) { size_t i, cl, n = 0; const char *hex_digits = "0123456789abcdef"; const char *specials = "btnvfr"; for (i = 0; i < len; i++) { unsigned char ch = ((unsigned char *) p)[i]; if (ch == '"' || ch == '\\') { n += out->printer(out, "\\", 1); n += out->printer(out, p + i, 1); } else if (ch >= '\b' && ch <= '\r') { n += out->printer(out, "\\", 1); n += out->printer(out, &specials[ch - '\b'], 1); } else if (isprint(ch)) { n += out->printer(out, p + i, 1); } else if ((cl = json_get_utf8_char_len(ch)) == 1) { n += out->printer(out, "\\u00", 4); n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1); n += out->printer(out, &hex_digits[ch % 0xf], 1); } else { n += out->printer(out, p + i, cl); i += cl - 1; } } return n; } int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK; int json_printer_buf(struct json_out *out, const char *buf, size_t len) { size_t avail = out->u.buf.size - out->u.buf.len; size_t n = len < avail ? len : avail; memcpy(out->u.buf.buf + out->u.buf.len, buf, n); out->u.buf.len += n; if (out->u.buf.size > 0) { size_t idx = out->u.buf.len; if (idx >= out->u.buf.size) idx = out->u.buf.size - 1; out->u.buf.buf[idx] = '\0'; } return len; } int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK; int json_printer_file(struct json_out *out, const char *buf, size_t len) { return fwrite(buf, 1, len, out->u.fp); } #if JSON_ENABLE_BASE64 static int b64idx(int c) { if (c < 26) { return c + 'A'; } else if (c < 52) { return c - 26 + 'a'; } else if (c < 62) { return c - 52 + '0'; } else { return c == 62 ? '+' : '/'; } } static int b64rev(int c) { if (c >= 'A' && c <= 'Z') { return c - 'A'; } else if (c >= 'a' && c <= 'z') { return c + 26 - 'a'; } else if (c >= '0' && c <= '9') { return c + 52 - '0'; } else if (c == '+') { return 62; } else if (c == '/') { return 63; } else { return 64; } } static int b64enc(struct json_out *out, const unsigned char *p, int n) { char buf[4]; int i, len = 0; for (i = 0; i < n; i += 3) { int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0; buf[0] = b64idx(a >> 2); buf[1] = b64idx((a & 3) << 4 | (b >> 4)); buf[2] = b64idx((b & 15) << 2 | (c >> 6)); buf[3] = b64idx(c & 63); if (i + 1 >= n) buf[2] = '='; if (i + 2 >= n) buf[3] = '='; len += out->printer(out, buf, sizeof(buf)); } return len; } static int b64dec(const char *src, int n, char *dst) { const char *end = src + n; int len = 0; while (src + 3 < end) { int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), d = b64rev(src[3]); dst[len++] = (a << 2) | (b >> 4); if (src[2] != '=') { dst[len++] = (b << 4) | (c >> 2); if (src[3] != '=') { dst[len++] = (c << 6) | d; } } src += 4; } return len; } #endif /* JSON_ENABLE_BASE64 */ static unsigned char hexdec(const char *s) { #define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W') int a = tolower(*(const unsigned char *) s); int b = tolower(*(const unsigned char *) (s + 1)); return (HEXTOI(a) << 4) | HEXTOI(b); } int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK; int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { int len = 0; const char *quote = "\"", *null = "null"; va_list ap; va_copy(ap, xap); while (*fmt != '\0') { if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) { len += out->printer(out, fmt, 1); fmt++; } else if (fmt[0] == '%') { char buf[21]; size_t skip = 2; if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) { int64_t val = va_arg(ap, int64_t); const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT; snprintf(buf, sizeof(buf), fmt2, val); len += out->printer(out, buf, strlen(buf)); skip += 2; } else if (fmt[1] == 'z' && fmt[2] == 'u') { size_t val = va_arg(ap, size_t); snprintf(buf, sizeof(buf), "%lu", (unsigned long) val); len += out->printer(out, buf, strlen(buf)); skip += 1; } else if (fmt[1] == 'M') { json_printf_callback_t f = va_arg(ap, json_printf_callback_t); len += f(out, &ap); } else if (fmt[1] == 'B') { int val = va_arg(ap, int); const char *str = val ? "true" : "false"; len += out->printer(out, str, strlen(str)); } else if (fmt[1] == 'H') { #if JSON_ENABLE_HEX const char *hex = "0123456789abcdef"; int i, n = va_arg(ap, int); const unsigned char *p = va_arg(ap, const unsigned char *); len += out->printer(out, quote, 1); for (i = 0; i < n; i++) { len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1); len += out->printer(out, &hex[p[i] & 0xf], 1); } len += out->printer(out, quote, 1); #endif /* JSON_ENABLE_HEX */ } else if (fmt[1] == 'V') { #if JSON_ENABLE_BASE64 const unsigned char *p = va_arg(ap, const unsigned char *); int n = va_arg(ap, int); len += out->printer(out, quote, 1); len += b64enc(out, p, n); len += out->printer(out, quote, 1); #endif /* JSON_ENABLE_BASE64 */ } else if (fmt[1] == 'Q' || (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) { size_t l = 0; const char *p; if (fmt[1] == '.') { l = (size_t) va_arg(ap, int); skip += 2; } p = va_arg(ap, char *); if (p == NULL) { len += out->printer(out, null, 4); } else { if (fmt[1] == 'Q') { l = strlen(p); } len += out->printer(out, quote, 1); len += json_escape(out, p, l); len += out->printer(out, quote, 1); } } else { /* * we delegate printing to the system printf. * The goal here is to delegate all modifiers parsing to the system * printf, as you can see below we still have to parse the format * types. * * Currently, %s with strings longer than 20 chars will require * double-buffering (an auxiliary buffer will be allocated from heap). * TODO(dfrank): reimplement %s and %.*s in order to avoid that. */ const char *end_of_format_specifier = "sdfFeEgGlhuIcx.*-0123456789"; int n = strspn(fmt + 1, end_of_format_specifier); char *pbuf = buf; int need_len, size = sizeof(buf); char fmt2[20]; va_list ap_copy; strncpy(fmt2, fmt, n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1); fmt2[n + 1] = '\0'; va_copy(ap_copy, ap); need_len = vsnprintf(pbuf, size, fmt2, ap_copy); va_end(ap_copy); if (need_len < 0) { /* * Windows & eCos vsnprintf implementation return -1 on overflow * instead of needed size. */ pbuf = NULL; while (need_len < 0) { free(pbuf); size *= 2; if ((pbuf = (char *) malloc(size)) == NULL) break; va_copy(ap_copy, ap); need_len = vsnprintf(pbuf, size, fmt2, ap_copy); va_end(ap_copy); } } else if (need_len >= (int) sizeof(buf)) { /* * resulting string doesn't fit into a stack-allocated buffer `buf`, * so we need to allocate a new buffer from heap and use it */ if ((pbuf = (char *) malloc(need_len + 1)) != NULL) { va_copy(ap_copy, ap); vsnprintf(pbuf, need_len + 1, fmt2, ap_copy); va_end(ap_copy); } } if (pbuf == NULL) { buf[0] = '\0'; pbuf = buf; } /* * however we need to parse the type ourselves in order to advance * the va_list by the correct amount; there is no portable way to * inherit the advancement made by vprintf. * 32-bit (linux or windows) passes va_list by value. */ if ((n + 1 == (int) strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) || (n + 1 == (int) strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) { (void) va_arg(ap, int64_t); } else if (strcmp(fmt2, "%.*s") == 0) { (void) va_arg(ap, int); (void) va_arg(ap, char *); } else { switch (fmt2[n]) { case 'u': case 'd': (void) va_arg(ap, int); break; case 'g': case 'f': (void) va_arg(ap, double); break; case 'p': (void) va_arg(ap, void *); break; default: /* many types are promoted to int */ (void) va_arg(ap, int); } } len += out->printer(out, pbuf, strlen(pbuf)); skip = n + 1; /* If buffer was allocated from heap, free it */ if (pbuf != buf) { free(pbuf); pbuf = NULL; } } fmt += skip; } else if (*fmt == '_' || json_isalpha(*fmt)) { len += out->printer(out, quote, 1); while (*fmt == '_' || json_isalpha(*fmt) || json_isdigit(*fmt)) { len += out->printer(out, fmt, 1); fmt++; } len += out->printer(out, quote, 1); } else { len += out->printer(out, fmt, 1); fmt++; } } va_end(ap); return len; } int json_printf(struct json_out *out, const char *fmt, ...) WEAK; int json_printf(struct json_out *out, const char *fmt, ...) { int n; va_list ap; va_start(ap, fmt); n = json_vprintf(out, fmt, ap); va_end(ap); return n; } int json_printf_array(struct json_out *out, va_list *ap) WEAK; int json_printf_array(struct json_out *out, va_list *ap) { int len = 0; char *arr = va_arg(*ap, char *); size_t i, arr_size = va_arg(*ap, size_t); size_t elem_size = va_arg(*ap, size_t); const char *fmt = va_arg(*ap, char *); len += json_printf(out, "[", 1); for (i = 0; arr != NULL && i < arr_size / elem_size; i++) { union { int64_t i; double d; } val; memcpy(&val, arr + i * elem_size, elem_size > sizeof(val) ? sizeof(val) : elem_size); if (i > 0) len += json_printf(out, ", "); if (strpbrk(fmt, "efg") != NULL) { len += json_printf(out, fmt, val.d); } else { len += json_printf(out, fmt, val.i); } } len += json_printf(out, "]", 1); return len; } #ifdef _WIN32 int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) WEAK; int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) { int res = _vsnprintf(str, size, format, ap); va_end(ap); if (res >= size) { str[size - 1] = '\0'; } return res; } int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK; int cs_win_snprintf(char *str, size_t size, const char *format, ...) { int res; va_list ap; va_start(ap, format); res = vsnprintf(str, size, format, ap); va_end(ap); return res; } #endif /* _WIN32 */ int json_walk(const char *json_string, int json_string_length, json_walk_callback_t callback, void *callback_data) WEAK; int json_walk(const char *json_string, int json_string_length, json_walk_callback_t callback, void *callback_data) { struct frozen frozen; memset(&frozen, 0, sizeof(frozen)); frozen.end = json_string + json_string_length; frozen.cur = json_string; frozen.callback_data = callback_data; frozen.callback = callback; TRY(json_doit(&frozen)); return frozen.cur - json_string; } struct scan_array_info { int found; char path[JSON_MAX_PATH_LEN]; struct json_token *token; }; static void json_scanf_array_elem_cb(void *callback_data, const char *name, size_t name_len, const char *path, const struct json_token *token) { struct scan_array_info *info = (struct scan_array_info *) callback_data; (void) name; (void) name_len; if (strcmp(path, info->path) == 0) { *info->token = *token; info->found = 1; } } int json_scanf_array_elem(const char *s, int len, const char *path, int idx, struct json_token *token) WEAK; int json_scanf_array_elem(const char *s, int len, const char *path, int idx, struct json_token *token) { struct scan_array_info info; info.token = token; info.found = 0; memset(token, 0, sizeof(*token)); snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx); json_walk(s, len, json_scanf_array_elem_cb, &info); return info.found ? token->len : -1; } struct json_scanf_info { int num_conversions; char *path; const char *fmt; void *target; void *user_data; int type; }; int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK; int json_unescape(const char *src, int slen, char *dst, int dlen) { char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p; const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t"; while (src < send) { if (*src == '\\') { if (++src >= send) return JSON_STRING_INCOMPLETE; if (*src == 'u') { if (send - src < 5) return JSON_STRING_INCOMPLETE; /* Here we go: this is a \u.... escape. Process simple one-byte chars */ if (src[1] == '0' && src[2] == '0') { /* This is \u00xx character from the ASCII range */ if (dst < dend) *dst = hexdec(src + 3); src += 4; } else { /* Complex \uXXXX escapes drag utf8 lib... Do it at some stage */ return JSON_STRING_INVALID; } } else if ((p = (char *) strchr(esc1, *src)) != NULL) { if (dst < dend) *dst = esc2[p - esc1]; } else { return JSON_STRING_INVALID; } } else { if (dst < dend) *dst = *src; } dst++; src++; } return dst - orig_dst; } static void json_scanf_cb(void *callback_data, const char *name, size_t name_len, const char *path, const struct json_token *token) { struct json_scanf_info *info = (struct json_scanf_info *) callback_data; char buf[32]; /* Must be enough to hold numbers */ (void) name; (void) name_len; if (token->ptr == NULL) { /* * We're not interested here in the events for which we have no value; * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START */ return; } if (strcmp(path, info->path) != 0) { /* It's not the path we're looking for, so, just ignore this callback */ return; } switch (info->type) { case 'B': info->num_conversions++; switch (sizeof(bool)) { case sizeof(char): *(char *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); break; case sizeof(int): *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); break; default: /* should never be here */ abort(); } break; case 'M': { union { void *p; json_scanner_t f; } u = {info->target}; info->num_conversions++; u.f(token->ptr, token->len, info->user_data); break; } case 'Q': { char **dst = (char **) info->target; if (token->type == JSON_TYPE_NULL) { *dst = NULL; } else { int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0); if (unescaped_len >= 0 && (*dst = (char *) malloc(unescaped_len + 1)) != NULL) { info->num_conversions++; if (json_unescape(token->ptr, token->len, *dst, unescaped_len) == unescaped_len) { (*dst)[unescaped_len] = '\0'; } else { free(*dst); *dst = NULL; } } } break; } case 'H': { #if JSON_ENABLE_HEX char **dst = (char **) info->user_data; int i, len = token->len / 2; *(int *) info->target = len; if ((*dst = (char *) malloc(len + 1)) != NULL) { for (i = 0; i < len; i++) { (*dst)[i] = hexdec(token->ptr + 2 * i); } (*dst)[len] = '\0'; info->num_conversions++; } #endif /* JSON_ENABLE_HEX */ break; } case 'V': { #if JSON_ENABLE_BASE64 char **dst = (char **) info->target; int len = token->len * 4 / 3 + 2; if ((*dst = (char *) malloc(len + 1)) != NULL) { int n = b64dec(token->ptr, token->len, *dst); (*dst)[n] = '\0'; *(int *) info->user_data = n; info->num_conversions++; } #endif /* JSON_ENABLE_BASE64 */ break; } case 'T': info->num_conversions++; *(struct json_token *) info->target = *token; break; default: if (token->len >= (int) sizeof(buf)) break; /* Before converting, copy into tmp buffer in order to 0-terminate it */ memcpy(buf, token->ptr, token->len); buf[token->len] = '\0'; /* NB: Use of base 0 for %d, %ld, %u and %lu is intentional. */ if (info->fmt[1] == 'd' || (info->fmt[1] == 'l' && info->fmt[2] == 'd') || info->fmt[1] == 'i') { char *endptr = NULL; long r = strtol(buf, &endptr, 0 /* base */); if (*endptr == '\0') { if (info->fmt[1] == 'l') { *((long *) info->target) = r; } else { *((int *) info->target) = (int) r; } info->num_conversions++; } } else if (info->fmt[1] == 'u' || (info->fmt[1] == 'l' && info->fmt[2] == 'u')) { char *endptr = NULL; unsigned long r = strtoul(buf, &endptr, 0 /* base */); if (*endptr == '\0') { if (info->fmt[1] == 'l') { *((unsigned long *) info->target) = r; } else { *((unsigned int *) info->target) = (unsigned int) r; } info->num_conversions++; } } else { #if !JSON_MINIMAL info->num_conversions += sscanf(buf, info->fmt, info->target); #endif } break; } } int json_vscanf(const char *s, int len, const char *fmt, va_list ap) WEAK; int json_vscanf(const char *s, int len, const char *fmt, va_list ap) { char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20]; int i = 0; char *p = NULL; struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0}; while (fmt[i] != '\0') { if (fmt[i] == '{') { strcat(path, "."); i++; } else if (fmt[i] == '}') { if ((p = strrchr(path, '.')) != NULL) *p = '\0'; i++; } else if (fmt[i] == '%') { info.target = va_arg(ap, void *); info.type = fmt[i + 1]; switch (fmt[i + 1]) { case 'M': case 'V': case 'H': info.user_data = va_arg(ap, void *); /* FALLTHROUGH */ case 'B': case 'Q': case 'T': i += 2; break; default: { const char *delims = ", \t\r\n]}"; int conv_len = strcspn(fmt + i + 1, delims) + 1; memcpy(fmtbuf, fmt + i, conv_len); fmtbuf[conv_len] = '\0'; i += conv_len; if (fmt[i] != '}') i += strspn(fmt + i, delims); break; } } json_walk(s, len, json_scanf_cb, &info); } else if (json_isalpha(fmt[i]) || json_get_utf8_char_len(fmt[i]) > 1) { char *pe; const char *delims = ": \r\n\t"; int key_len = strcspn(&fmt[i], delims); if ((p = strrchr(path, '.')) != NULL) p[1] = '\0'; pe = path + strlen(path); memcpy(pe, fmt + i, key_len); pe[key_len] = '\0'; i += key_len + strspn(fmt + i + key_len, delims); } else { i++; } } return info.num_conversions; } int json_scanf(const char *str, int len, const char *fmt, ...) WEAK; int json_scanf(const char *str, int len, const char *fmt, ...) { int result; va_list ap; va_start(ap, fmt); result = json_vscanf(str, len, fmt, ap); va_end(ap); return result; } int json_vfprintf(const char *file_name, const char *fmt, va_list ap) WEAK; int json_vfprintf(const char *file_name, const char *fmt, va_list ap) { int res = -1; FILE *fp = fopen(file_name, "wb"); if (fp != NULL) { struct json_out out = JSON_OUT_FILE(fp); res = json_vprintf(&out, fmt, ap); fputc('\n', fp); fclose(fp); } return res; } int json_fprintf(const char *file_name, const char *fmt, ...) WEAK; int json_fprintf(const char *file_name, const char *fmt, ...) { int result; va_list ap; va_start(ap, fmt); result = json_vfprintf(file_name, fmt, ap); va_end(ap); return result; } char *json_fread(const char *path) WEAK; char *json_fread(const char *path) { FILE *fp; char *data = NULL; if ((fp = fopen(path, "rb")) == NULL) { } else if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); } else { long size = ftell(fp); if (size > 0 && (data = (char *) malloc(size + 1)) != NULL) { fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */ if (fread(data, 1, size, fp) != (size_t) size) { free(data); data = NULL; } else { data[size] = '\0'; } } fclose(fp); } return data; } struct json_setf_data { const char *json_path; const char *base; /* Pointer to the source JSON string */ int matched; /* Matched part of json_path */ int pos; /* Offset of the mutated value begin */ int end; /* Offset of the mutated value end */ int prev; /* Offset of the previous token end */ }; static int get_matched_prefix_len(const char *s1, const char *s2) { int i = 0; while (s1[i] && s2[i] && s1[i] == s2[i]) i++; return i; } static void json_vsetf_cb(void *userdata, const char *name, size_t name_len, const char *path, const struct json_token *t) { struct json_setf_data *data = (struct json_setf_data *) userdata; int off, len = get_matched_prefix_len(path, data->json_path); if (t->ptr == NULL) return; off = t->ptr - data->base; if (len > data->matched) data->matched = len; /* * If there is no exact path match, set the mutation position to tbe end * of the object or array */ if (len < data->matched && data->pos == 0 && (t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END)) { data->pos = data->end = data->prev; } /* Exact path match. Set mutation position to the value of this token */ if (strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START && t->type != JSON_TYPE_ARRAY_START) { data->pos = off; data->end = off + t->len; } /* * For deletion, we need to know where the previous value ends, because * we don't know where matched value key starts. * When the mutation position is not yet set, remember each value end. * When the mutation position is already set, but it is at the beginning * of the object/array, we catch the end of the object/array and see * whether the object/array start is closer then previously stored prev. */ if (data->pos == 0) { data->prev = off + t->len; /* pos is not yet set */ } else if ((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos && off + 1 > data->prev) { data->prev = off + 1; } (void) name; (void) name_len; } int json_vsetf(const char *s, int len, struct json_out *out, const char *json_path, const char *json_fmt, va_list ap) WEAK; int json_vsetf(const char *s, int len, struct json_out *out, const char *json_path, const char *json_fmt, va_list ap) { struct json_setf_data data; memset(&data, 0, sizeof(data)); data.json_path = json_path; data.base = s; data.end = len; json_walk(s, len, json_vsetf_cb, &data); if (json_fmt == NULL) { /* Deletion codepath */ json_printf(out, "%.*s", data.prev, s); /* Trim comma after the value that begins at object/array start */ if (s[data.prev - 1] == '{' || s[data.prev - 1] == '[') { int i = data.end; while (i < len && json_isspace(s[i])) i++; if (s[i] == ',') data.end = i + 1; /* Point after comma */ } json_printf(out, "%.*s", len - data.end, s + data.end); } else { /* Modification codepath */ int n, off = data.matched, depth = 0; /* Print the unchanged beginning */ json_printf(out, "%.*s", data.pos, s); /* Add missing keys */ while ((n = strcspn(&json_path[off], ".[")) > 0) { if (s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0) { json_printf(out, ","); } if (off > 0 && json_path[off - 1] != '.') break; json_printf(out, "%.*Q:", n, json_path + off); off += n; if (json_path[off] != '\0') { json_printf(out, "%c", json_path[off] == '.' ? '{' : '['); depth++; off++; } } /* Print the new value */ json_vprintf(out, json_fmt, ap); /* Close brackets/braces of the added missing keys */ for (; off > data.matched; off--) { int ch = json_path[off]; const char *p = ch == '.' ? "}" : ch == '[' ? "]" : ""; json_printf(out, "%s", p); } /* Print the rest of the unchanged string */ json_printf(out, "%.*s", len - data.end, s + data.end); } return data.end > data.pos ? 1 : 0; } int json_setf(const char *s, int len, struct json_out *out, const char *json_path, const char *json_fmt, ...) WEAK; int json_setf(const char *s, int len, struct json_out *out, const char *json_path, const char *json_fmt, ...) { int result; va_list ap; va_start(ap, json_fmt); result = json_vsetf(s, len, out, json_path, json_fmt, ap); va_end(ap); return result; } struct prettify_data { struct json_out *out; int level; int last_token; }; static void indent(struct json_out *out, int level) { while (level-- > 0) out->printer(out, " ", 2); } static void print_key(struct prettify_data *pd, const char *path, const char *name, int name_len) { if (pd->last_token != JSON_TYPE_INVALID && pd->last_token != JSON_TYPE_ARRAY_START && pd->last_token != JSON_TYPE_OBJECT_START) { pd->out->printer(pd->out, ",", 1); } if (path[0] != '\0') pd->out->printer(pd->out, "\n", 1); indent(pd->out, pd->level); if (path[0] != '\0' && path[strlen(path) - 1] != ']') { pd->out->printer(pd->out, "\"", 1); pd->out->printer(pd->out, name, (int) name_len); pd->out->printer(pd->out, "\"", 1); pd->out->printer(pd->out, ": ", 2); } } static void prettify_cb(void *userdata, const char *name, size_t name_len, const char *path, const struct json_token *t) { struct prettify_data *pd = (struct prettify_data *) userdata; switch (t->type) { case JSON_TYPE_OBJECT_START: case JSON_TYPE_ARRAY_START: print_key(pd, path, name, name_len); pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_START ? "[" : "{", 1); pd->level++; break; case JSON_TYPE_OBJECT_END: case JSON_TYPE_ARRAY_END: pd->level--; if (pd->last_token != JSON_TYPE_INVALID && pd->last_token != JSON_TYPE_ARRAY_START && pd->last_token != JSON_TYPE_OBJECT_START) { pd->out->printer(pd->out, "\n", 1); indent(pd->out, pd->level); } pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_END ? "]" : "}", 1); break; case JSON_TYPE_NUMBER: case JSON_TYPE_NULL: case JSON_TYPE_TRUE: case JSON_TYPE_FALSE: case JSON_TYPE_STRING: print_key(pd, path, name, name_len); if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); pd->out->printer(pd->out, t->ptr, t->len); if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); break; default: break; } pd->last_token = t->type; } int json_prettify(const char *s, int len, struct json_out *out) WEAK; int json_prettify(const char *s, int len, struct json_out *out) { struct prettify_data pd = {out, 0, JSON_TYPE_INVALID}; return json_walk(s, len, prettify_cb, &pd); } int json_prettify_file(const char *file_name) WEAK; int json_prettify_file(const char *file_name) { int res = -1; char *s = json_fread(file_name); FILE *fp; if (s != NULL && (fp = fopen(file_name, "wb")) != NULL) { struct json_out out = JSON_OUT_FILE(fp); res = json_prettify(s, strlen(s), &out); if (res < 0) { /* On error, restore the old content */ fclose(fp); fp = fopen(file_name, "wb"); fseek(fp, 0, SEEK_SET); fwrite(s, 1, strlen(s), fp); } else { fputc('\n', fp); } fclose(fp); } free(s); return res; } struct next_data { void *handle; // Passed handle. Changed if a next entry is found const char *path; // Path to the iterated object/array int path_len; // Path length - optimisation int found; // Non-0 if found the next entry struct json_token *key; // Object's key struct json_token *val; // Object's value int *idx; // Array index }; static void next_set_key(struct next_data *d, const char *name, int name_len, int is_array) { if (is_array) { /* Array. Set index and reset key */ if (d->key != NULL) { d->key->len = 0; d->key->ptr = NULL; } if (d->idx != NULL) *d->idx = atoi(name); } else { /* Object. Set key and make index -1 */ if (d->key != NULL) { d->key->ptr = name; d->key->len = name_len; } if (d->idx != NULL) *d->idx = -1; } } static void json_next_cb(void *userdata, const char *name, size_t name_len, const char *path, const struct json_token *t) { struct next_data *d = (struct next_data *) userdata; const char *p = path + d->path_len; if (d->found) return; if (d->path_len >= (int) strlen(path)) return; if (strncmp(d->path, path, d->path_len) != 0) return; if (strchr(p + 1, '.') != NULL) return; /* More nested objects - skip */ if (strchr(p + 1, '[') != NULL) return; /* Ditto for arrays */ // {OBJECT,ARRAY}_END types do not pass name, _START does. Save key. if (t->type == JSON_TYPE_OBJECT_START || t->type == JSON_TYPE_ARRAY_START) { next_set_key(d, name, name_len, p[0] == '['); } else if (d->handle == NULL || d->handle < (void *) t->ptr) { if (t->type != JSON_TYPE_OBJECT_END && t->type != JSON_TYPE_ARRAY_END) { next_set_key(d, name, name_len, p[0] == '['); } if (d->val != NULL) *d->val = *t; d->handle = (void *) t->ptr; d->found = 1; } } static void *json_next(const char *s, int len, void *handle, const char *path, struct json_token *key, struct json_token *val, int *i) { struct json_token tmpval, *v = val == NULL ? &tmpval : val; struct json_token tmpkey, *k = key == NULL ? &tmpkey : key; int tmpidx, *pidx = i == NULL ? &tmpidx : i; struct next_data data = {handle, path, (int) strlen(path), 0, k, v, pidx}; json_walk(s, len, json_next_cb, &data); return data.found ? data.handle : NULL; } void *json_next_key(const char *s, int len, void *handle, const char *path, struct json_token *key, struct json_token *val) WEAK; void *json_next_key(const char *s, int len, void *handle, const char *path, struct json_token *key, struct json_token *val) { return json_next(s, len, handle, path, key, val, NULL); } void *json_next_elem(const char *s, int len, void *handle, const char *path, int *idx, struct json_token *val) WEAK; void *json_next_elem(const char *s, int len, void *handle, const char *path, int *idx, struct json_token *val) { return json_next(s, len, handle, path, NULL, val, idx); } static int json_sprinter(struct json_out *out, const char *str, size_t len) { size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf); size_t new_len = len + old_len; char *p = (char *) realloc(out->u.buf.buf, new_len + 1); if (p != NULL) { memcpy(p + old_len, str, len); p[new_len] = '\0'; out->u.buf.buf = p; } return len; } char *json_vasprintf(const char *fmt, va_list ap) WEAK; char *json_vasprintf(const char *fmt, va_list ap) { struct json_out out; memset(&out, 0, sizeof(out)); out.printer = json_sprinter; json_vprintf(&out, fmt, ap); return out.u.buf.buf; } char *json_asprintf(const char *fmt, ...) WEAK; char *json_asprintf(const char *fmt, ...) { char *result = NULL; va_list ap; va_start(ap, fmt); result = json_vasprintf(fmt, ap); va_end(ap); return result; }