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 */