gs

painless gist creator
git clone git://edryd.org/gs
Log | Files | Refs | LICENSE

commit fd74bf29c24cd7e8ef50622d6ff9267dab699989
parent 7b4b3b7c53668a4bc6dbf4b04cb7f11071fc805d
Author: Ed van Bruggen <edvb@uw.edu>
Date:   Sat, 28 Mar 2020 12:59:04 -0700

Update frozen static library

Diffstat:
extern/frozen.c | 825++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
extern/frozen.h | 139++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 771 insertions(+), 193 deletions(-)

diff --git a/extern/frozen.c b/extern/frozen.c @@ -1,25 +1,25 @@ /* * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> - * Copyright (c) 2013 Cesanta Software Limited + * Copyright (c) 2018 Cesanta Software Limited * All rights reserved * - * This library is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see <http: *www.gnu.org/licenses/>. + * 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 * - * You are free to use this library under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Alternatively, you can license this library under a commercial - * license, as set out in <http://cesanta.com/products.html>. + * 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 <ctype.h> #include <stdarg.h> #include <stdio.h> @@ -35,6 +35,8 @@ #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, ...); @@ -47,33 +49,27 @@ typedef unsigned _int64 uint64_t; #endif #define PRId64 "I64d" #define PRIu64 "I64u" -#if !defined(SIZE_T_FMT) -#if _MSC_VER >= 1310 -#define SIZE_T_FMT "Iu" -#else -#define SIZE_T_FMT "u" -#endif -#endif #else /* _WIN32 */ /* <inttypes.h> wants this for C++ */ #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include <inttypes.h> -#if !defined(SIZE_T_FMT) -#define SIZE_T_FMT "zu" -#endif #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_MAX_PATH_LEN -#define JSON_MAX_PATH_LEN 60 +#ifndef JSON_ENABLE_ARRAY +#define JSON_ENABLE_ARRAY 1 #endif struct frozen { @@ -97,13 +93,13 @@ struct fstate { #define SET_STATE(fr, ptr, str, len) \ struct fstate fstate = {(ptr), (fr)->path_len}; \ - append_to_path((fr), (str), (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), (len), (tok)}; \ + 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, \ @@ -115,24 +111,23 @@ struct fstate { } \ } while (0) -static int append_to_path(struct frozen *f, const char *str, int size) { +static int json_append_to_path(struct frozen *f, const char *str, int size) { int n = f->path_len; - f->path_len += - snprintf(f->path + f->path_len, sizeof(f->path) - (f->path_len), "%.*s", size, str); - if (f->path_len > sizeof(f->path) - 1) { - f->path_len = sizeof(f->path) - 1; - } - + 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 truncate_path(struct frozen *f, size_t len) { +static void json_truncate_path(struct frozen *f, size_t len) { f->path_len = len; f->path[len] = '\0'; } -static int parse_object(struct frozen *f); -static int parse_value(struct frozen *f); +static int json_parse_object(struct frozen *f); +static int json_parse_value(struct frozen *f); #define EXPECT(cond, err_code) \ do { \ @@ -147,25 +142,25 @@ static int parse_value(struct frozen *f); #define END_OF_STRING (-1) -static int left(const struct frozen *f) { +static int json_left(const struct frozen *f) { return f->end - f->cur; } -static int is_space(int ch) { +static int json_isspace(int ch) { return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; } -static void skip_whitespaces(struct frozen *f) { - while (f->cur < f->end && is_space(*f->cur)) f->cur++; +static void json_skip_whitespaces(struct frozen *f) { + while (f->cur < f->end && json_isspace(*f->cur)) f->cur++; } -static int cur(struct frozen *f) { - skip_whitespaces(f); +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 test_and_skip(struct frozen *f, int expected) { - int ch = cur(f); +static int json_test_and_skip(struct frozen *f, int expected) { + int ch = json_cur(f); if (ch == expected) { f->cur++; return 0; @@ -173,24 +168,25 @@ static int test_and_skip(struct frozen *f, int expected) { return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; } -static int is_alpha(int ch) { +static int json_isalpha(int ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); } -static int is_digit(int ch) { +static int json_isdigit(int ch) { return ch >= '0' && ch <= '9'; } -static int is_hex_digit(int ch) { - return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); +static int json_isxdigit(int ch) { + return json_isdigit(ch) || (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F'); } -static int get_escape_len(const char *s, int len) { +static int json_get_escape_len(const char *s, int len) { switch (*s) { case 'u': return len < 6 ? JSON_STRING_INCOMPLETE - : is_hex_digit(s[1]) && is_hex_digit(s[2]) && - is_hex_digit(s[3]) && is_hex_digit(s[4]) + : json_isxdigit(s[1]) && json_isxdigit(s[2]) && + json_isxdigit(s[3]) && json_isxdigit(s[4]) ? 5 : JSON_STRING_INVALID; case '"': @@ -208,21 +204,21 @@ static int get_escape_len(const char *s, int len) { } /* identifier = letter { letter | digit | '_' } */ -static int parse_identifier(struct frozen *f) { - EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID); +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 == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) { + (*f->cur == '_' || json_isalpha(*f->cur) || json_isdigit(*f->cur))) { f->cur++; } - truncate_path(f, fstate.path_len); + json_truncate_path(f, fstate.path_len); CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); } return 0; } -static int get_utf8_char_len(unsigned char ch) { +static int json_get_utf8_char_len(unsigned char ch) { if ((ch & 0x80) == 0) return 1; switch (ch & 0xf0) { case 0xf0: @@ -235,21 +231,21 @@ static int get_utf8_char_len(unsigned char ch) { } /* string = '"' { quoted_printable_chars } '"' */ -static int parse_string(struct frozen *f) { +static int json_parse_string(struct frozen *f) { int n, ch = 0, len = 0; - TRY(test_and_skip(f, '"')); + 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 = get_utf8_char_len((unsigned char) ch); + len = json_get_utf8_char_len((unsigned char) ch); EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ - EXPECT(len < left(f), JSON_STRING_INCOMPLETE); + EXPECT(len <= json_left(f), JSON_STRING_INCOMPLETE); if (ch == '\\') { - EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n); + EXPECT((n = json_get_escape_len(f->cur + 1, json_left(f))) > 0, n); len += n; } else if (ch == '"') { - truncate_path(f, fstate.path_len); + json_truncate_path(f, fstate.path_len); CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); f->cur++; break; @@ -260,70 +256,79 @@ static int parse_string(struct frozen *f) { } /* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ -static int parse_number(struct frozen *f) { - int ch = cur(f); +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); - EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); - while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; - if (f->cur < f->end && f->cur[0] == '.') { - f->cur++; - EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); - EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); - while (f->cur < f->end && is_digit(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++; + 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(is_digit(f->cur[0]), JSON_STRING_INVALID); - while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; + 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++; + } } - truncate_path(f, fstate.path_len); + 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 parse_array(struct frozen *f) { +static int json_parse_array(struct frozen *f) { int i = 0, current_path_len; char buf[20]; - TRY(test_and_skip(f, '[')); + CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); + TRY(json_test_and_skip(f, '[')); { - CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); { SET_STATE(f, f->cur - 1, "", 0); - while (cur(f) != ']') { + while (json_cur(f) != ']') { snprintf(buf, sizeof(buf), "[%d]", i); i++; - current_path_len = append_to_path(f, buf, strlen(buf)); + 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(parse_value(f)); - truncate_path(f, current_path_len); - if (cur(f) == ',') f->cur++; + TRY(json_parse_value(f)); + json_truncate_path(f, current_path_len); + if (json_cur(f) == ',') f->cur++; } - TRY(test_and_skip(f, ']')); - truncate_path(f, fstate.path_len); + 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 expect(struct frozen *f, const char *s, int len, - enum json_token_type tok_type) { - int i, n = left(f); +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; - truncate_path(f, fstate.path_len); + json_truncate_path(f, fstate.path_len); CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr); @@ -331,27 +336,29 @@ static int expect(struct frozen *f, const char *s, int len, } /* value = 'null' | 'true' | 'false' | number | string | array | object */ -static int parse_value(struct frozen *f) { - int ch = cur(f); +static int json_parse_value(struct frozen *f) { + int ch = json_cur(f); switch (ch) { case '"': - TRY(parse_string(f)); + TRY(json_parse_string(f)); break; case '{': - TRY(parse_object(f)); + TRY(json_parse_object(f)); break; +#if JSON_ENABLE_ARRAY case '[': - TRY(parse_array(f)); + TRY(json_parse_array(f)); break; +#endif case 'n': - TRY(expect(f, "null", 4, JSON_TYPE_NULL)); + TRY(json_expect(f, "null", 4, JSON_TYPE_NULL)); break; case 't': - TRY(expect(f, "true", 4, JSON_TYPE_TRUE)); + TRY(json_expect(f, "true", 4, JSON_TYPE_TRUE)); break; case 'f': - TRY(expect(f, "false", 5, JSON_TYPE_FALSE)); + TRY(json_expect(f, "false", 5, JSON_TYPE_FALSE)); break; case '-': case '0': @@ -364,7 +371,7 @@ static int parse_value(struct frozen *f) { case '7': case '8': case '9': - TRY(parse_number(f)); + TRY(json_parse_number(f)); break; default: return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; @@ -374,15 +381,12 @@ static int parse_value(struct frozen *f) { } /* key = identifier | string */ -static int parse_key(struct frozen *f) { - int ch = cur(f); -#if 0 - printf("%s [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur); -#endif - if (is_alpha(ch)) { - TRY(parse_identifier(f)); +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(parse_string(f)); + TRY(json_parse_string(f)); } else { return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; } @@ -390,46 +394,44 @@ static int parse_key(struct frozen *f) { } /* pair = key ':' value */ -static int parse_pair(struct frozen *f) { +static int json_parse_pair(struct frozen *f) { int current_path_len; const char *tok; - skip_whitespaces(f); + json_skip_whitespaces(f); tok = f->cur; - TRY(parse_key(f)); + 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 = append_to_path(f, f->cur_name, f->cur_name_len); + current_path_len = json_append_to_path(f, f->cur_name, f->cur_name_len); } - TRY(test_and_skip(f, ':')); - TRY(parse_value(f)); - truncate_path(f, current_path_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 parse_object(struct frozen *f) { - TRY(test_and_skip(f, '{')); +static int json_parse_object(struct frozen *f) { + CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); + TRY(json_test_and_skip(f, '{')); { - CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); - { - SET_STATE(f, f->cur - 1, ".", 1); - while (cur(f) != '}') { - TRY(parse_pair(f)); - if (cur(f) == ',') f->cur++; - } - TRY(test_and_skip(f, '}')); - truncate_path(f, fstate.path_len); - CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); + 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 doit(struct frozen *f) { +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 parse_value(f); + return json_parse_value(f); } int json_escape(struct json_out *out, const char *p, size_t len) WEAK; @@ -448,7 +450,7 @@ int json_escape(struct json_out *out, const char *p, size_t len) { n += out->printer(out, &specials[ch - '\b'], 1); } else if (isprint(ch)) { n += out->printer(out, p + i, 1); - } else if ((cl = get_utf8_char_len(ch)) == 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); @@ -480,6 +482,7 @@ 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'; @@ -508,13 +511,6 @@ static int b64rev(int c) { } } -static uint8_t 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); -} - static int b64enc(struct json_out *out, const unsigned char *p, int n) { char buf[4]; int i, len = 0; @@ -548,6 +544,14 @@ static int b64dec(const char *src, int n, char *dst) { } 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) { @@ -572,7 +576,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { skip += 2; } else if (fmt[1] == 'z' && fmt[2] == 'u') { size_t val = va_arg(ap, size_t); - snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val); + snprintf(buf, sizeof(buf), "%lu", (unsigned long) val); len += out->printer(out, buf, strlen(buf)); skip += 1; } else if (fmt[1] == 'M') { @@ -583,6 +587,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { 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 *); @@ -592,12 +597,15 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { 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; @@ -631,30 +639,48 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { * TODO(dfrank): reimplement %s and %.*s in order to avoid that. */ - const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789"; - size_t n = strspn(fmt + 1, end_of_format_specifier); + const char *end_of_format_specifier = "sdfFeEgGlhuIcx.*-0123456789"; + int n = strspn(fmt + 1, end_of_format_specifier); char *pbuf = buf; - size_t need_len; + int need_len, size = sizeof(buf); char fmt2[20]; - va_list sub_ap; - strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : n + 1); + va_list ap_copy; + strncpy(fmt2, fmt, + n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1); fmt2[n + 1] = '\0'; - va_copy(sub_ap, ap); - need_len = - vsnprintf(buf, sizeof(buf), fmt2, sub_ap) + 1 /* null-term */; - /* - * TODO(lsm): Fix windows & eCos code path here. Their vsnprintf - * implementation returns -1 on overflow rather needed size. - */ - if (need_len > sizeof(buf)) { + 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 */ - pbuf = (char *) malloc(need_len); - va_copy(sub_ap, ap); - vsnprintf(pbuf, need_len, fmt2, sub_ap); + 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; } /* @@ -663,10 +689,11 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { * inherit the advancement made by vprintf. * 32-bit (linux or windows) passes va_list by value. */ - if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) || - (n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) { + 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); - skip += strlen(PRIu64) - 1; } else if (strcmp(fmt2, "%.*s") == 0) { (void) va_arg(ap, int); (void) va_arg(ap, char *); @@ -699,14 +726,15 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { } } fmt += skip; - } else if (*fmt == '_' || is_alpha(*fmt)) { + } else if (*fmt == '_' || json_isalpha(*fmt)) { len += out->printer(out, quote, 1); - while (*fmt == '_' || is_alpha(*fmt) || is_digit(*fmt)) { + 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++; } } @@ -741,7 +769,7 @@ int json_printf_array(struct json_out *out, va_list *ap) { memcpy(&val, arr + i * elem_size, elem_size > sizeof(val) ? sizeof(val) : elem_size); if (i > 0) len += json_printf(out, ", "); - if (strchr(fmt, 'f') != NULL) { + if (strpbrk(fmt, "efg") != NULL) { len += json_printf(out, fmt, val.d); } else { len += json_printf(out, fmt, val.i); @@ -752,7 +780,8 @@ int json_printf_array(struct json_out *out, va_list *ap) { } #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) 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); @@ -785,12 +814,13 @@ int json_walk(const char *json_string, int json_string_length, frozen.callback_data = callback_data; frozen.callback = callback; - TRY(doit(&frozen)); + 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; }; @@ -805,6 +835,7 @@ static void json_scanf_array_elem_cb(void *callback_data, const char *name, if (strcmp(path, info->path) == 0) { *info->token = *token; + info->found = 1; } } @@ -814,10 +845,11 @@ 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 token->len; + return info.found ? token->len : -1; } struct json_scanf_info { @@ -838,8 +870,16 @@ int json_unescape(const char *src, int slen, char *dst, int dlen) { if (*src == '\\') { if (++src >= send) return JSON_STRING_INCOMPLETE; if (*src == 'u') { - /* TODO(lsm): \uXXXX escapes drag utf8 lib... Do it at some stage */ - return JSON_STRING_INVALID; + 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 { @@ -859,15 +899,11 @@ 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 (strcmp(path, info->path) != 0) { - /* It's not the path we're looking for, so, just ignore this callback */ - return; - } - if (token->ptr == NULL) { /* * We're not interested here in the events for which we have no value; @@ -876,10 +912,25 @@ static void json_scanf_cb(void *callback_data, const char *name, 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++; - *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); + 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 { @@ -899,13 +950,19 @@ static void json_scanf_cb(void *callback_data, const char *name, if (unescaped_len >= 0 && (*dst = (char *) malloc(unescaped_len + 1)) != NULL) { info->num_conversions++; - json_unescape(token->ptr, token->len, *dst, unescaped_len); - (*dst)[unescaped_len] = '\0'; + 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; @@ -916,9 +973,11 @@ static void json_scanf_cb(void *callback_data, const char *name, (*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) { @@ -927,6 +986,7 @@ static void json_scanf_cb(void *callback_data, const char *name, *(int *) info->user_data = n; info->num_conversions++; } +#endif /* JSON_ENABLE_BASE64 */ break; } case 'T': @@ -934,7 +994,40 @@ static void json_scanf_cb(void *callback_data, const char *name, *(struct json_token *) info->target = *token; break; default: - info->num_conversions += sscanf(token->ptr, info->fmt, info->target); + 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; } } @@ -970,18 +1063,22 @@ int json_vscanf(const char *s, int len, const char *fmt, va_list ap) { default: { const char *delims = ", \t\r\n]}"; int conv_len = strcspn(fmt + i + 1, delims) + 1; - snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", conv_len, fmt + i); + memcpy(fmtbuf, fmt + i, conv_len); + fmtbuf[conv_len] = '\0'; i += conv_len; i += strspn(fmt + i, delims); break; } } json_walk(s, len, json_scanf_cb, &info); - } else if (is_alpha(fmt[i]) || get_utf8_char_len(fmt[i]) > 1) { + } 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'; - sprintf(path + strlen(path), "%.*s", key_len, &fmt[i]); + 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++; @@ -999,3 +1096,375 @@ int json_scanf(const char *str, int len, const char *fmt, ...) { 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; +} diff --git a/extern/frozen.h b/extern/frozen.h @@ -1,20 +1,19 @@ /* * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> - * Copyright (c) 2013 Cesanta Software Limited + * Copyright (c) 2018 Cesanta Software Limited * All rights reserved * - * This library is dual-licensed: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. For the terms of this - * license, see <http: *www.gnu.org/licenses/>. + * 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 * - * You are free to use this library under the terms of the GNU General - * Public License, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Alternatively, you can license this library under a commercial - * license, as set out in <http://cesanta.com/products.html>. + * 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. */ #ifndef CS_FROZEN_FROZEN_H_ @@ -28,6 +27,13 @@ extern "C" { #include <stddef.h> #include <stdio.h> +#if defined(_WIN32) && _MSC_VER < 1700 +typedef int bool; +enum { false = 0, true = 1 }; +#else +#include <stdbool.h> +#endif + /* JSON token type */ enum json_token_type { JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */ @@ -94,6 +100,7 @@ typedef void (*json_walk_callback_t)(void *callback_data, const char *name, /* * Parse `json_string`, invoking `callback` in a way similar to SAX parsers; * see `json_walk_callback_t`. + * Return number of processed bytes, or a negative error code. */ int json_walk(const char *json_string, int json_string_length, json_walk_callback_t callback, void *callback_data); @@ -127,7 +134,7 @@ extern int json_printer_file(struct json_out *, const char *, size_t); #define JSON_OUT_FILE(fp) \ { \ json_printer_file, { \ - { (void *) fp, 0, 0 } \ + { (char *) fp, 0, 0 } \ } \ } @@ -144,7 +151,7 @@ typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap); * - `%M` invokes a json_printf_callback_t function. That callback function * can consume more parameters. * - * Return number of bytes printed. If the return value is bigger then the + * Return number of bytes printed. If the return value is bigger than the * supplied buffer, that is an indicator of overflow. In the overflow case, * overflown bytes are not printed. */ @@ -152,6 +159,27 @@ int json_printf(struct json_out *, const char *fmt, ...); int json_vprintf(struct json_out *, const char *fmt, va_list ap); /* + * Same as json_printf, but prints to a file. + * File is created if does not exist. File is truncated if already exists. + */ +int json_fprintf(const char *file_name, const char *fmt, ...); +int json_vfprintf(const char *file_name, const char *fmt, va_list ap); + +/* + * Print JSON into an allocated 0-terminated string. + * Return allocated string, or NULL on error. + * Example: + * + * ```c + * char *str = json_asprintf("{a:%H}", 3, "abc"); + * printf("%s\n", str); // Prints "616263" + * free(str); + * ``` + */ +char *json_asprintf(const char *fmt, ...); +char *json_vasprintf(const char *fmt, va_list ap); + +/* * Helper %M callback that prints contiguous C arrays. * Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt * Return number of bytes printed. @@ -165,7 +193,8 @@ int json_printf_array(struct json_out *, va_list *ap); * 1. Object keys in the format string may be not quoted, e.g. "{key: %d}" * 2. Order of keys in an object is irrelevant. * 3. Several extra format specifiers are supported: - * - %B: consumes `int *`, expects boolean `true` or `false`. + * - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`), + * expects boolean `true` or `false`. * - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned * string is malloc-ed, caller must free() the string. * - %V: consumes `char **`, `int *`. Expects base64-encoded string. @@ -193,7 +222,7 @@ typedef void (*json_scanner_t)(const char *str, int len, void *user_data); /* * Helper function to scan array item with given path and index. * Fills `token` with the matched JSON token. - * Return 0 if no array element found, otherwise non-0. + * Return -1 if no array element found, otherwise non-negative token length. */ int json_scanf_array_elem(const char *s, int len, const char *path, int index, struct json_token *token); @@ -213,6 +242,86 @@ int json_unescape(const char *src, int slen, char *dst, int dlen); */ int json_escape(struct json_out *out, const char *str, size_t str_len); +/* + * Read the whole file in memory. + * Return malloc-ed file content, or NULL on error. The caller must free(). + */ +char *json_fread(const char *file_name); + +/* + * Update given JSON string `s,len` by changing the value at given `json_path`. + * The result is saved to `out`. If `json_fmt` == NULL, that deletes the key. + * If path is not present, missing keys are added. Array path without an + * index pushes a value to the end of an array. + * Return 1 if the string was changed, 0 otherwise. + * + * Example: s is a JSON string { "a": 1, "b": [ 2 ] } + * json_setf(s, len, out, ".a", "7"); // { "a": 7, "b": [ 2 ] } + * json_setf(s, len, out, ".b", "7"); // { "a": 1, "b": 7 } + * json_setf(s, len, out, ".b[]", "7"); // { "a": 1, "b": [ 2,7 ] } + * json_setf(s, len, out, ".b", NULL); // { "a": 1 } + */ +int json_setf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, ...); + +int json_vsetf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, va_list ap); + +/* + * Pretty-print JSON string `s,len` into `out`. + * Return number of processed bytes in `s`. + */ +int json_prettify(const char *s, int len, struct json_out *out); + +/* + * Prettify JSON file `file_name`. + * Return number of processed bytes, or negative number of error. + * On error, file content is not modified. + */ +int json_prettify_file(const char *file_name); + +/* + * Iterate over an object at given JSON `path`. + * On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL + * for `key`, or `val`, in which case they won't be populated. + * Return an opaque value suitable for the next iteration, or NULL when done. + * + * Example: + * + * ```c + * void *h = NULL; + * struct json_token key, val; + * while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) { + * printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr); + * } + * ``` + */ +void *json_next_key(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val); + +/* + * Iterate over an array at given JSON `path`. + * Similar to `json_next_key`, but fills array index `idx` instead of `key`. + */ +void *json_next_elem(const char *s, int len, void *handle, const char *path, + int *idx, struct json_token *val); + +#ifndef JSON_MAX_PATH_LEN +#define JSON_MAX_PATH_LEN 256 +#endif + +#ifndef JSON_MINIMAL +#define JSON_MINIMAL 0 +#endif + +#ifndef JSON_ENABLE_BASE64 +#define JSON_ENABLE_BASE64 !JSON_MINIMAL +#endif + +#ifndef JSON_ENABLE_HEX +#define JSON_ENABLE_HEX !JSON_MINIMAL +#endif + #ifdef __cplusplus } #endif /* __cplusplus */