commit 8be30f8913b1bc963a7a2703eb0fa940849b0912
parent c50effd2e8f2e838951b812a4719d97e512e70e5
Author: Ed van Bruggen <edvb54@gmail.com>
Date: Mon, 31 Jul 2017 23:42:45 -0700
Create extern/ for arg.h and frozen
Diffstat:
Makefile | | | 4 | ++-- |
arg.h | | | 63 | --------------------------------------------------------------- |
config.mk | | | 6 | +++--- |
extern/arg.h | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
extern/frozen.c | | | 1001 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
extern/frozen.h | | | 220 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
frozen.c | | | 1001 | ------------------------------------------------------------------------------- |
frozen.h | | | 220 | ------------------------------------------------------------------------------- |
gs.c | | | 4 | ++-- |
9 files changed, 1291 insertions(+), 1291 deletions(-)
diff --git a/Makefile b/Makefile
@@ -4,7 +4,7 @@
include config.mk
EXE = gs
-SRC = $(wildcard *.c)
+SRC = $(wildcard *.c) $(wildcard */*.c)
OBJ = $(SRC:.c=.o)
all: options $(EXE)
@@ -22,7 +22,7 @@ options:
@echo $(CC) $<
@$(CC) -c -o $@ $< $(CFLAGS)
-${OBJ}: config.h config.mk
+$(OBJ): config.h config.mk
config.h:
@echo creating $@ from config.def.h
diff --git a/arg.h b/arg.h
@@ -1,63 +0,0 @@
-/*
- * Copy me if you can.
- * by 20h
- */
-
-#ifndef ARG_H__
-#define ARG_H__
-
-extern char *argv0;
-
-/* use main(int argc, char *argv[]) */
-#define ARGBEGIN for (argv0 = basename(*argv), argv++, argc--;\
- argv[0] && argv[0][0] == '-'\
- && argv[0][1];\
- argc--, argv++) {\
- char argc_;\
- char **argv_;\
- int brk_;\
- if (argv[0][1] == '-' && argv[0][2] == '\0') {\
- argv++;\
- argc--;\
- break;\
- }\
- for (brk_ = 0, argv[0]++, argv_ = argv;\
- argv[0][0] && !brk_;\
- argv[0]++) {\
- if (argv_ != argv)\
- break;\
- argc_ = argv[0][0];\
- switch (argc_)
-
-/* Handles -NUM syntax */
-#define ARGNUM case '0':\
- case '1':\
- case '2':\
- case '3':\
- case '4':\
- case '5':\
- case '6':\
- case '7':\
- case '8':\
- case '9'
-
-#define ARGEND }\
- }
-
-#define ARGC() argc_
-
-#define ARGNUMF() (brk_ = 1, atoi(argv[0]))
-
-#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
- ((x), abort(), (char *)0) :\
- (brk_ = 1, (argv[0][1] != '\0')?\
- (&argv[0][1]) :\
- (argc--, argv++, argv[0])))
-
-#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
- (char *)0 :\
- (brk_ = 1, (argv[0][1] != '\0')?\
- (&argv[0][1]) :\
- (argc--, argv++, argv[0])))
-
-#endif
diff --git a/config.mk b/config.mk
@@ -3,7 +3,7 @@ VERSION = 0.0.0
# paths
PREFIX = /usr/local
-MANPREFIX = ${PREFIX}/share/man
+MANPREFIX = $(PREFIX)/share/man
# includes and libraries
INCS = -Iinclude
@@ -11,8 +11,8 @@ LIBS = -lcurl
# flags
CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=600
-CFLAGS = -g -std=c99 -pedantic -Wall ${INCS} ${CPPFLAGS}
-LDFLAGS = -g ${LIBS}
+CFLAGS = -g -std=c99 -pedantic -Wall $(INCS) $(CPPFLAGS)
+LDFLAGS = -g $(LIBS)
# compiler and linker
CC = gcc
diff --git a/extern/arg.h b/extern/arg.h
@@ -0,0 +1,63 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = basename(*argv), argv++, argc--;\
+ argv[0] && argv[0][0] == '-'\
+ && argv[0][1];\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (brk_ = 0, argv[0]++, argv_ = argv;\
+ argv[0][0] && !brk_;\
+ argv[0]++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][0];\
+ switch (argc_)
+
+/* Handles -NUM syntax */
+#define ARGNUM case '0':\
+ case '1':\
+ case '2':\
+ case '3':\
+ case '4':\
+ case '5':\
+ case '6':\
+ case '7':\
+ case '8':\
+ case '9'
+
+#define ARGEND }\
+ }
+
+#define ARGC() argc_
+
+#define ARGNUMF() (brk_ = 1, atoi(argv[0]))
+
+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ ((x), abort(), (char *)0) :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ (char *)0 :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#endif
diff --git a/extern/frozen.c b/extern/frozen.c
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
+ * Copyright (c) 2013 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/>.
+ *
+ * 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.
+ *
+ * Alternatively, you can license this library under a commercial
+ * license, as set out in <http://cesanta.com/products.html>.
+ */
+
+#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */
+
+#include "frozen.h"
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(WEAK)
+#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)
+#define WEAK __attribute__((weak))
+#else
+#define WEAK
+#endif
+#endif
+
+#ifdef _WIN32
+#define snprintf cs_win_snprintf
+#define vsnprintf cs_win_vsnprintf
+int cs_win_snprintf(char *str, size_t size, const char *format, ...);
+int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap);
+#if _MSC_VER >= 1700
+#include <stdint.h>
+#else
+typedef _int64 int64_t;
+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 */
+
+#define INT64_FMT PRId64
+#define UINT64_FMT PRIu64
+
+#ifndef va_copy
+#define va_copy(x, y) x = y
+#endif
+
+#ifndef JSON_MAX_PATH_LEN
+#define JSON_MAX_PATH_LEN 60
+#endif
+
+struct frozen {
+ const char *end;
+ const char *cur;
+
+ const char *cur_name;
+ size_t cur_name_len;
+
+ /* For callback API */
+ char path[JSON_MAX_PATH_LEN];
+ size_t path_len;
+ void *callback_data;
+ json_walk_callback_t callback;
+};
+
+struct fstate {
+ const char *ptr;
+ size_t path_len;
+};
+
+#define SET_STATE(fr, ptr, str, len) \
+ struct fstate fstate = {(ptr), (fr)->path_len}; \
+ 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)}; \
+ \
+ /* Call the callback with the given value and current name */ \
+ (fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \
+ (fr)->path, &t); \
+ \
+ /* Reset the name */ \
+ (fr)->cur_name = NULL; \
+ (fr)->cur_name_len = 0; \
+ } \
+ } while (0)
+
+static int 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;
+ }
+
+ return n;
+}
+
+static void 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);
+
+#define EXPECT(cond, err_code) \
+ do { \
+ if (!(cond)) return (err_code); \
+ } while (0)
+
+#define TRY(expr) \
+ do { \
+ int _n = expr; \
+ if (_n < 0) return _n; \
+ } while (0)
+
+#define END_OF_STRING (-1)
+
+static int left(const struct frozen *f) {
+ return f->end - f->cur;
+}
+
+static int is_space(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 int cur(struct frozen *f) {
+ 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);
+ if (ch == expected) {
+ f->cur++;
+ return 0;
+ }
+ return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
+}
+
+static int is_alpha(int ch) {
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
+}
+
+static int is_digit(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 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])
+ ? 5
+ : JSON_STRING_INVALID;
+ case '"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ return len < 2 ? JSON_STRING_INCOMPLETE : 1;
+ default:
+ return JSON_STRING_INVALID;
+ }
+}
+
+/* identifier = letter { letter | digit | '_' } */
+static int parse_identifier(struct frozen *f) {
+ EXPECT(is_alpha(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++;
+ }
+ 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) {
+ if ((ch & 0x80) == 0) return 1;
+ switch (ch & 0xf0) {
+ case 0xf0:
+ return 4;
+ case 0xe0:
+ return 3;
+ default:
+ return 2;
+ }
+}
+
+/* string = '"' { quoted_printable_chars } '"' */
+static int parse_string(struct frozen *f) {
+ int n, ch = 0, len = 0;
+ TRY(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);
+ EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */
+ EXPECT(len < left(f), JSON_STRING_INCOMPLETE);
+ if (ch == '\\') {
+ EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n);
+ len += n;
+ } else if (ch == '"') {
+ truncate_path(f, fstate.path_len);
+ CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr);
+ f->cur++;
+ break;
+ };
+ }
+ }
+ return ch == '"' ? 0 : JSON_STRING_INCOMPLETE;
+}
+
+/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */
+static int parse_number(struct frozen *f) {
+ int ch = 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++;
+ 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++;
+ }
+ truncate_path(f, fstate.path_len);
+ CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr);
+ return 0;
+}
+
+/* array = '[' [ value { ',' value } ] ']' */
+static int 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);
+ {
+ SET_STATE(f, f->cur - 1, "", 0);
+ while (cur(f) != ']') {
+ snprintf(buf, sizeof(buf), "[%d]", i);
+ i++;
+ current_path_len = 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(test_and_skip(f, ']'));
+ truncate_path(f, fstate.path_len);
+ CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr);
+ }
+ }
+ return 0;
+}
+
+static int expect(struct frozen *f, const char *s, int len,
+ enum json_token_type tok_type) {
+ int i, n = 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);
+
+ CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr);
+
+ return 0;
+}
+
+/* value = 'null' | 'true' | 'false' | number | string | array | object */
+static int parse_value(struct frozen *f) {
+ int ch = cur(f);
+
+ switch (ch) {
+ case '"':
+ TRY(parse_string(f));
+ break;
+ case '{':
+ TRY(parse_object(f));
+ break;
+ case '[':
+ TRY(parse_array(f));
+ break;
+ case 'n':
+ TRY(expect(f, "null", 4, JSON_TYPE_NULL));
+ break;
+ case 't':
+ TRY(expect(f, "true", 4, JSON_TYPE_TRUE));
+ break;
+ case 'f':
+ TRY(expect(f, "false", 5, JSON_TYPE_FALSE));
+ break;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ TRY(parse_number(f));
+ break;
+ default:
+ return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
+ }
+
+ return 0;
+}
+
+/* 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));
+ } else if (ch == '"') {
+ TRY(parse_string(f));
+ } else {
+ return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
+ }
+ return 0;
+}
+
+/* pair = key ':' value */
+static int parse_pair(struct frozen *f) {
+ int current_path_len;
+ const char *tok;
+ skip_whitespaces(f);
+ tok = f->cur;
+ TRY(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);
+ }
+ TRY(test_and_skip(f, ':'));
+ TRY(parse_value(f));
+ truncate_path(f, current_path_len);
+ return 0;
+}
+
+/* object = '{' pair { ',' pair } '}' */
+static int parse_object(struct frozen *f) {
+ TRY(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);
+ }
+ }
+ return 0;
+}
+
+static int 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);
+}
+
+int json_escape(struct json_out *out, const char *p, size_t len) WEAK;
+int json_escape(struct json_out *out, const char *p, size_t len) {
+ size_t i, cl, n = 0;
+ const char *hex_digits = "0123456789abcdef";
+ const char *specials = "btnvfr";
+
+ for (i = 0; i < len; i++) {
+ unsigned char ch = ((unsigned char *) p)[i];
+ if (ch == '"' || ch == '\\') {
+ n += out->printer(out, "\\", 1);
+ n += out->printer(out, p + i, 1);
+ } else if (ch >= '\b' && ch <= '\r') {
+ n += out->printer(out, "\\", 1);
+ n += out->printer(out, &specials[ch - '\b'], 1);
+ } else if (isprint(ch)) {
+ n += out->printer(out, p + i, 1);
+ } else if ((cl = get_utf8_char_len(ch)) == 1) {
+ n += out->printer(out, "\\u00", 4);
+ n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1);
+ n += out->printer(out, &hex_digits[ch % 0xf], 1);
+ } else {
+ n += out->printer(out, p + i, cl);
+ i += cl - 1;
+ }
+ }
+
+ return n;
+}
+
+int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK;
+int json_printer_buf(struct json_out *out, const char *buf, size_t len) {
+ size_t avail = out->u.buf.size - out->u.buf.len;
+ size_t n = len < avail ? len : avail;
+ memcpy(out->u.buf.buf + out->u.buf.len, buf, n);
+ out->u.buf.len += n;
+ if (out->u.buf.size > 0) {
+ size_t idx = out->u.buf.len;
+ if (idx >= out->u.buf.size) idx = out->u.buf.size - 1;
+ out->u.buf.buf[idx] = '\0';
+ }
+ return len;
+}
+
+int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK;
+int json_printer_file(struct json_out *out, const char *buf, size_t len) {
+ return fwrite(buf, 1, len, out->u.fp);
+}
+
+static int b64idx(int c) {
+ if (c < 26) {
+ return c + 'A';
+ } else if (c < 52) {
+ return c - 26 + 'a';
+ } else if (c < 62) {
+ return c - 52 + '0';
+ } else {
+ return c == 62 ? '+' : '/';
+ }
+}
+
+static int b64rev(int c) {
+ if (c >= 'A' && c <= 'Z') {
+ return c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ return c + 26 - 'a';
+ } else if (c >= '0' && c <= '9') {
+ return c + 52 - '0';
+ } else if (c == '+') {
+ return 62;
+ } else if (c == '/') {
+ return 63;
+ } else {
+ return 64;
+ }
+}
+
+static 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;
+ for (i = 0; i < n; i += 3) {
+ int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0;
+ buf[0] = b64idx(a >> 2);
+ buf[1] = b64idx((a & 3) << 4 | (b >> 4));
+ buf[2] = b64idx((b & 15) << 2 | (c >> 6));
+ buf[3] = b64idx(c & 63);
+ if (i + 1 >= n) buf[2] = '=';
+ if (i + 2 >= n) buf[3] = '=';
+ len += out->printer(out, buf, sizeof(buf));
+ }
+ return len;
+}
+
+static int b64dec(const char *src, int n, char *dst) {
+ const char *end = src + n;
+ int len = 0;
+ while (src + 3 < end) {
+ int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]),
+ d = b64rev(src[3]);
+ dst[len++] = (a << 2) | (b >> 4);
+ if (src[2] != '=') {
+ dst[len++] = (b << 4) | (c >> 2);
+ if (src[3] != '=') {
+ dst[len++] = (c << 6) | d;
+ }
+ }
+ src += 4;
+ }
+ return len;
+}
+
+int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK;
+int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
+ int len = 0;
+ const char *quote = "\"", *null = "null";
+ va_list ap;
+ va_copy(ap, xap);
+
+ while (*fmt != '\0') {
+ if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) {
+ len += out->printer(out, fmt, 1);
+ fmt++;
+ } else if (fmt[0] == '%') {
+ char buf[21];
+ size_t skip = 2;
+
+ if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) {
+ int64_t val = va_arg(ap, int64_t);
+ const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT;
+ snprintf(buf, sizeof(buf), fmt2, val);
+ len += out->printer(out, buf, strlen(buf));
+ skip += 2;
+ } else if (fmt[1] == 'z' && fmt[2] == 'u') {
+ size_t val = va_arg(ap, size_t);
+ snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val);
+ len += out->printer(out, buf, strlen(buf));
+ skip += 1;
+ } else if (fmt[1] == 'M') {
+ json_printf_callback_t f = va_arg(ap, json_printf_callback_t);
+ len += f(out, &ap);
+ } else if (fmt[1] == 'B') {
+ int val = va_arg(ap, int);
+ const char *str = val ? "true" : "false";
+ len += out->printer(out, str, strlen(str));
+ } else if (fmt[1] == 'H') {
+ const char *hex = "0123456789abcdef";
+ int i, n = va_arg(ap, int);
+ const unsigned char *p = va_arg(ap, const unsigned char *);
+ len += out->printer(out, quote, 1);
+ for (i = 0; i < n; i++) {
+ len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1);
+ len += out->printer(out, &hex[p[i] & 0xf], 1);
+ }
+ len += out->printer(out, quote, 1);
+ } else if (fmt[1] == 'V') {
+ 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);
+ } else if (fmt[1] == 'Q' ||
+ (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) {
+ size_t l = 0;
+ const char *p;
+
+ if (fmt[1] == '.') {
+ l = (size_t) va_arg(ap, int);
+ skip += 2;
+ }
+ p = va_arg(ap, char *);
+
+ if (p == NULL) {
+ len += out->printer(out, null, 4);
+ } else {
+ if (fmt[1] == 'Q') {
+ l = strlen(p);
+ }
+ len += out->printer(out, quote, 1);
+ len += json_escape(out, p, l);
+ len += out->printer(out, quote, 1);
+ }
+ } else {
+ /*
+ * we delegate printing to the system printf.
+ * The goal here is to delegate all modifiers parsing to the system
+ * printf, as you can see below we still have to parse the format
+ * types.
+ *
+ * Currently, %s with strings longer than 20 chars will require
+ * double-buffering (an auxiliary buffer will be allocated from heap).
+ * TODO(dfrank): reimplement %s and %.*s in order to avoid that.
+ */
+
+ const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789";
+ size_t n = strspn(fmt + 1, end_of_format_specifier);
+ char *pbuf = buf;
+ size_t need_len;
+ char fmt2[20];
+ va_list sub_ap;
+ strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : 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)) {
+ /*
+ * 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);
+ }
+
+ /*
+ * however we need to parse the type ourselves in order to advance
+ * the va_list by the correct amount; there is no portable way to
+ * inherit the advancement made by vprintf.
+ * 32-bit (linux or windows) passes va_list by value.
+ */
+ if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) ||
+ (n + 1 == 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 *);
+ } else {
+ switch (fmt2[n]) {
+ case 'u':
+ case 'd':
+ (void) va_arg(ap, int);
+ break;
+ case 'g':
+ case 'f':
+ (void) va_arg(ap, double);
+ break;
+ case 'p':
+ (void) va_arg(ap, void *);
+ break;
+ default:
+ /* many types are promoted to int */
+ (void) va_arg(ap, int);
+ }
+ }
+
+ len += out->printer(out, pbuf, strlen(pbuf));
+ skip = n + 1;
+
+ /* If buffer was allocated from heap, free it */
+ if (pbuf != buf) {
+ free(pbuf);
+ pbuf = NULL;
+ }
+ }
+ fmt += skip;
+ } else if (*fmt == '_' || is_alpha(*fmt)) {
+ len += out->printer(out, quote, 1);
+ while (*fmt == '_' || is_alpha(*fmt) || is_digit(*fmt)) {
+ len += out->printer(out, fmt, 1);
+ fmt++;
+ }
+ len += out->printer(out, quote, 1);
+ } else {
+ fmt++;
+ }
+ }
+ va_end(ap);
+
+ return len;
+}
+
+int json_printf(struct json_out *out, const char *fmt, ...) WEAK;
+int json_printf(struct json_out *out, const char *fmt, ...) {
+ int n;
+ va_list ap;
+ va_start(ap, fmt);
+ n = json_vprintf(out, fmt, ap);
+ va_end(ap);
+ return n;
+}
+
+int json_printf_array(struct json_out *out, va_list *ap) WEAK;
+int json_printf_array(struct json_out *out, va_list *ap) {
+ int len = 0;
+ char *arr = va_arg(*ap, char *);
+ size_t i, arr_size = va_arg(*ap, size_t);
+ size_t elem_size = va_arg(*ap, size_t);
+ const char *fmt = va_arg(*ap, char *);
+ len += json_printf(out, "[", 1);
+ for (i = 0; arr != NULL && i < arr_size / elem_size; i++) {
+ union {
+ int64_t i;
+ double d;
+ } val;
+ memcpy(&val, arr + i * elem_size,
+ elem_size > sizeof(val) ? sizeof(val) : elem_size);
+ if (i > 0) len += json_printf(out, ", ");
+ if (strchr(fmt, 'f') != NULL) {
+ len += json_printf(out, fmt, val.d);
+ } else {
+ len += json_printf(out, fmt, val.i);
+ }
+ }
+ len += json_printf(out, "]", 1);
+ return len;
+}
+
+#ifdef _WIN32
+int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) WEAK;
+int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ int res = _vsnprintf(str, size, format, ap);
+ va_end(ap);
+ if (res >= size) {
+ str[size - 1] = '\0';
+ }
+ return res;
+}
+
+int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK;
+int cs_win_snprintf(char *str, size_t size, const char *format, ...) {
+ int res;
+ va_list ap;
+ va_start(ap, format);
+ res = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return res;
+}
+#endif /* _WIN32 */
+
+int json_walk(const char *json_string, int json_string_length,
+ json_walk_callback_t callback, void *callback_data) WEAK;
+int json_walk(const char *json_string, int json_string_length,
+ json_walk_callback_t callback, void *callback_data) {
+ struct frozen frozen;
+
+ memset(&frozen, 0, sizeof(frozen));
+ frozen.end = json_string + json_string_length;
+ frozen.cur = json_string;
+ frozen.callback_data = callback_data;
+ frozen.callback = callback;
+
+ TRY(doit(&frozen));
+
+ return frozen.cur - json_string;
+}
+
+struct scan_array_info {
+ char path[JSON_MAX_PATH_LEN];
+ struct json_token *token;
+};
+
+static void json_scanf_array_elem_cb(void *callback_data, const char *name,
+ size_t name_len, const char *path,
+ const struct json_token *token) {
+ struct scan_array_info *info = (struct scan_array_info *) callback_data;
+
+ (void) name;
+ (void) name_len;
+
+ if (strcmp(path, info->path) == 0) {
+ *info->token = *token;
+ }
+}
+
+int json_scanf_array_elem(const char *s, int len, const char *path, int idx,
+ struct json_token *token) WEAK;
+int json_scanf_array_elem(const char *s, int len, const char *path, int idx,
+ struct json_token *token) {
+ struct scan_array_info info;
+ info.token = token;
+ 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;
+}
+
+struct json_scanf_info {
+ int num_conversions;
+ char *path;
+ const char *fmt;
+ void *target;
+ void *user_data;
+ int type;
+};
+
+int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK;
+int json_unescape(const char *src, int slen, char *dst, int dlen) {
+ char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p;
+ const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t";
+
+ while (src < send) {
+ if (*src == '\\') {
+ if (++src >= send) return JSON_STRING_INCOMPLETE;
+ if (*src == 'u') {
+ /* TODO(lsm): \uXXXX escapes drag utf8 lib... Do it at some stage */
+ return JSON_STRING_INVALID;
+ } else if ((p = (char *) strchr(esc1, *src)) != NULL) {
+ if (dst < dend) *dst = esc2[p - esc1];
+ } else {
+ return JSON_STRING_INVALID;
+ }
+ } else {
+ if (dst < dend) *dst = *src;
+ }
+ dst++;
+ src++;
+ }
+
+ return dst - orig_dst;
+}
+
+static void json_scanf_cb(void *callback_data, const char *name,
+ size_t name_len, const char *path,
+ const struct json_token *token) {
+ struct json_scanf_info *info = (struct json_scanf_info *) callback_data;
+
+ (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;
+ * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START
+ */
+ return;
+ }
+
+ switch (info->type) {
+ case 'B':
+ info->num_conversions++;
+ *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);
+ break;
+ case 'M': {
+ union {
+ void *p;
+ json_scanner_t f;
+ } u = {info->target};
+ info->num_conversions++;
+ u.f(token->ptr, token->len, info->user_data);
+ break;
+ }
+ case 'Q': {
+ char **dst = (char **) info->target;
+ if (token->type == JSON_TYPE_NULL) {
+ *dst = NULL;
+ } else {
+ int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0);
+ if (unescaped_len >= 0 &&
+ (*dst = (char *) malloc(unescaped_len + 1)) != NULL) {
+ info->num_conversions++;
+ json_unescape(token->ptr, token->len, *dst, unescaped_len);
+ (*dst)[unescaped_len] = '\0';
+ }
+ }
+ break;
+ }
+ case 'H': {
+ char **dst = (char **) info->user_data;
+ int i, len = token->len / 2;
+ *(int *) info->target = len;
+ if ((*dst = (char *) malloc(len + 1)) != NULL) {
+ for (i = 0; i < len; i++) {
+ (*dst)[i] = hexdec(token->ptr + 2 * i);
+ }
+ (*dst)[len] = '\0';
+ info->num_conversions++;
+ }
+ break;
+ }
+ case 'V': {
+ char **dst = (char **) info->target;
+ int len = token->len * 4 / 3 + 2;
+ if ((*dst = (char *) malloc(len + 1)) != NULL) {
+ int n = b64dec(token->ptr, token->len, *dst);
+ (*dst)[n] = '\0';
+ *(int *) info->user_data = n;
+ info->num_conversions++;
+ }
+ break;
+ }
+ case 'T':
+ info->num_conversions++;
+ *(struct json_token *) info->target = *token;
+ break;
+ default:
+ info->num_conversions += sscanf(token->ptr, info->fmt, info->target);
+ break;
+ }
+}
+
+int json_vscanf(const char *s, int len, const char *fmt, va_list ap) WEAK;
+int json_vscanf(const char *s, int len, const char *fmt, va_list ap) {
+ char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20];
+ int i = 0;
+ char *p = NULL;
+ struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0};
+
+ while (fmt[i] != '\0') {
+ if (fmt[i] == '{') {
+ strcat(path, ".");
+ i++;
+ } else if (fmt[i] == '}') {
+ if ((p = strrchr(path, '.')) != NULL) *p = '\0';
+ i++;
+ } else if (fmt[i] == '%') {
+ info.target = va_arg(ap, void *);
+ info.type = fmt[i + 1];
+ switch (fmt[i + 1]) {
+ case 'M':
+ case 'V':
+ case 'H':
+ info.user_data = va_arg(ap, void *);
+ /* FALLTHROUGH */
+ case 'B':
+ case 'Q':
+ case 'T':
+ i += 2;
+ break;
+ default: {
+ const char *delims = ", \t\r\n]}";
+ int conv_len = strcspn(fmt + i + 1, delims) + 1;
+ snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", conv_len, fmt + i);
+ 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) {
+ 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]);
+ i += key_len + strspn(fmt + i + key_len, delims);
+ } else {
+ i++;
+ }
+ }
+ return info.num_conversions;
+}
+
+int json_scanf(const char *str, int len, const char *fmt, ...) WEAK;
+int json_scanf(const char *str, int len, const char *fmt, ...) {
+ int result;
+ va_list ap;
+ va_start(ap, fmt);
+ result = json_vscanf(str, len, fmt, ap);
+ va_end(ap);
+ return result;
+}
diff --git a/extern/frozen.h b/extern/frozen.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
+ * Copyright (c) 2013 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/>.
+ *
+ * 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.
+ *
+ * Alternatively, you can license this library under a commercial
+ * license, as set out in <http://cesanta.com/products.html>.
+ */
+
+#ifndef CS_FROZEN_FROZEN_H_
+#define CS_FROZEN_FROZEN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+
+/* JSON token type */
+enum json_token_type {
+ JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */
+ JSON_TYPE_STRING,
+ JSON_TYPE_NUMBER,
+ JSON_TYPE_TRUE,
+ JSON_TYPE_FALSE,
+ JSON_TYPE_NULL,
+ JSON_TYPE_OBJECT_START,
+ JSON_TYPE_OBJECT_END,
+ JSON_TYPE_ARRAY_START,
+ JSON_TYPE_ARRAY_END,
+
+ JSON_TYPES_CNT
+};
+
+/*
+ * Structure containing token type and value. Used in `json_walk()` and
+ * `json_scanf()` with the format specifier `%T`.
+ */
+struct json_token {
+ const char *ptr; /* Points to the beginning of the value */
+ int len; /* Value length */
+ enum json_token_type type; /* Type of the token, possible values are above */
+};
+
+#define JSON_INVALID_TOKEN \
+ { 0, 0, JSON_TYPE_INVALID }
+
+/* Error codes */
+#define JSON_STRING_INVALID -1
+#define JSON_STRING_INCOMPLETE -2
+
+/*
+ * Callback-based SAX-like API.
+ *
+ * Property name and length is given only if it's available: i.e. if current
+ * event is an object's property. In other cases, `name` is `NULL`. For
+ * example, name is never given:
+ * - For the first value in the JSON string;
+ * - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END
+ *
+ * E.g. for the input `{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }`,
+ * the sequence of callback invocations will be as follows:
+ *
+ * - type: JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULL
+ * - type: JSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123"
+ * - type: JSON_TYPE_ARRAY_START, name: "bar", path: ".bar", value: NULL
+ * - type: JSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1"
+ * - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2"
+ * - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL
+ * - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true"
+ * - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\":
+ *true }"
+ * - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, {
+ *\"baz\": true } ]"
+ * - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123,
+ *\"bar\": [ 1, 2, { \"baz\": true } ] }"
+ */
+typedef void (*json_walk_callback_t)(void *callback_data, const char *name,
+ size_t name_len, const char *path,
+ const struct json_token *token);
+
+/*
+ * Parse `json_string`, invoking `callback` in a way similar to SAX parsers;
+ * see `json_walk_callback_t`.
+ */
+int json_walk(const char *json_string, int json_string_length,
+ json_walk_callback_t callback, void *callback_data);
+
+/*
+ * JSON generation API.
+ * struct json_out abstracts output, allowing alternative printing plugins.
+ */
+struct json_out {
+ int (*printer)(struct json_out *, const char *str, size_t len);
+ union {
+ struct {
+ char *buf;
+ size_t size;
+ size_t len;
+ } buf;
+ void *data;
+ FILE *fp;
+ } u;
+};
+
+extern int json_printer_buf(struct json_out *, const char *, size_t);
+extern int json_printer_file(struct json_out *, const char *, size_t);
+
+#define JSON_OUT_BUF(buf, len) \
+ { \
+ json_printer_buf, { \
+ { buf, len, 0 } \
+ } \
+ }
+#define JSON_OUT_FILE(fp) \
+ { \
+ json_printer_file, { \
+ { (void *) fp, 0, 0 } \
+ } \
+ }
+
+typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap);
+
+/*
+ * Generate formatted output into a given sting buffer.
+ * This is a superset of printf() function, with extra format specifiers:
+ * - `%B` print json boolean, `true` or `false`. Accepts an `int`.
+ * - `%Q` print quoted escaped string or `null`. Accepts a `const char *`.
+ * - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *`
+ * - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`.
+ * - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`.
+ * - `%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
+ * supplied buffer, that is an indicator of overflow. In the overflow case,
+ * overflown bytes are not printed.
+ */
+int json_printf(struct json_out *, const char *fmt, ...);
+int json_vprintf(struct json_out *, 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.
+ */
+int json_printf_array(struct json_out *, va_list *ap);
+
+/*
+ * Scan JSON string `str`, performing scanf-like conversions according to `fmt`.
+ * This is a `scanf()` - like function, with following differences:
+ *
+ * 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`.
+ * - %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.
+ * Result string is base64-decoded, malloced and NUL-terminated.
+ * The length of result string is stored in `int *` placeholder.
+ * Caller must free() the result.
+ * - %H: consumes `int *`, `char **`.
+ * Expects a hex-encoded string, e.g. "fa014f".
+ * Result string is hex-decoded, malloced and NUL-terminated.
+ * The length of the result string is stored in `int *` placeholder.
+ * Caller must free() the result.
+ * - %M: consumes custom scanning function pointer and
+ * `void *user_data` parameter - see json_scanner_t definition.
+ * - %T: consumes `struct json_token *`, fills it out with matched token.
+ *
+ * Return number of elements successfully scanned & converted.
+ * Negative number means scan error.
+ */
+int json_scanf(const char *str, int str_len, const char *fmt, ...);
+int json_vscanf(const char *str, int str_len, const char *fmt, va_list ap);
+
+/* json_scanf's %M handler */
+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.
+ */
+int json_scanf_array_elem(const char *s, int len, const char *path, int index,
+ struct json_token *token);
+
+/*
+ * Unescape JSON-encoded string src,slen into dst, dlen.
+ * src and dst may overlap.
+ * If destination buffer is too small (or zero-length), result string is not
+ * written but the length is counted nevertheless (similar to snprintf).
+ * Return the length of unescaped string in bytes.
+ */
+int json_unescape(const char *src, int slen, char *dst, int dlen);
+
+/*
+ * Escape a string `str`, `str_len` into the printer `out`.
+ * Return the number of bytes printed.
+ */
+int json_escape(struct json_out *out, const char *str, size_t str_len);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CS_FROZEN_FROZEN_H_ */
diff --git a/frozen.c b/frozen.c
@@ -1,1001 +0,0 @@
-/*
- * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
- * Copyright (c) 2013 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/>.
- *
- * 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.
- *
- * Alternatively, you can license this library under a commercial
- * license, as set out in <http://cesanta.com/products.html>.
- */
-
-#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */
-
-#include "frozen.h"
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#if !defined(WEAK)
-#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32)
-#define WEAK __attribute__((weak))
-#else
-#define WEAK
-#endif
-#endif
-
-#ifdef _WIN32
-#define snprintf cs_win_snprintf
-#define vsnprintf cs_win_vsnprintf
-int cs_win_snprintf(char *str, size_t size, const char *format, ...);
-int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap);
-#if _MSC_VER >= 1700
-#include <stdint.h>
-#else
-typedef _int64 int64_t;
-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 */
-
-#define INT64_FMT PRId64
-#define UINT64_FMT PRIu64
-
-#ifndef va_copy
-#define va_copy(x, y) x = y
-#endif
-
-#ifndef JSON_MAX_PATH_LEN
-#define JSON_MAX_PATH_LEN 60
-#endif
-
-struct frozen {
- const char *end;
- const char *cur;
-
- const char *cur_name;
- size_t cur_name_len;
-
- /* For callback API */
- char path[JSON_MAX_PATH_LEN];
- size_t path_len;
- void *callback_data;
- json_walk_callback_t callback;
-};
-
-struct fstate {
- const char *ptr;
- size_t path_len;
-};
-
-#define SET_STATE(fr, ptr, str, len) \
- struct fstate fstate = {(ptr), (fr)->path_len}; \
- 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)}; \
- \
- /* Call the callback with the given value and current name */ \
- (fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \
- (fr)->path, &t); \
- \
- /* Reset the name */ \
- (fr)->cur_name = NULL; \
- (fr)->cur_name_len = 0; \
- } \
- } while (0)
-
-static int 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;
- }
-
- return n;
-}
-
-static void 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);
-
-#define EXPECT(cond, err_code) \
- do { \
- if (!(cond)) return (err_code); \
- } while (0)
-
-#define TRY(expr) \
- do { \
- int _n = expr; \
- if (_n < 0) return _n; \
- } while (0)
-
-#define END_OF_STRING (-1)
-
-static int left(const struct frozen *f) {
- return f->end - f->cur;
-}
-
-static int is_space(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 int cur(struct frozen *f) {
- 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);
- if (ch == expected) {
- f->cur++;
- return 0;
- }
- return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
-}
-
-static int is_alpha(int ch) {
- return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
-}
-
-static int is_digit(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 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])
- ? 5
- : JSON_STRING_INVALID;
- case '"':
- case '\\':
- case '/':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- return len < 2 ? JSON_STRING_INCOMPLETE : 1;
- default:
- return JSON_STRING_INVALID;
- }
-}
-
-/* identifier = letter { letter | digit | '_' } */
-static int parse_identifier(struct frozen *f) {
- EXPECT(is_alpha(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++;
- }
- 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) {
- if ((ch & 0x80) == 0) return 1;
- switch (ch & 0xf0) {
- case 0xf0:
- return 4;
- case 0xe0:
- return 3;
- default:
- return 2;
- }
-}
-
-/* string = '"' { quoted_printable_chars } '"' */
-static int parse_string(struct frozen *f) {
- int n, ch = 0, len = 0;
- TRY(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);
- EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */
- EXPECT(len < left(f), JSON_STRING_INCOMPLETE);
- if (ch == '\\') {
- EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n);
- len += n;
- } else if (ch == '"') {
- truncate_path(f, fstate.path_len);
- CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr);
- f->cur++;
- break;
- };
- }
- }
- return ch == '"' ? 0 : JSON_STRING_INCOMPLETE;
-}
-
-/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */
-static int parse_number(struct frozen *f) {
- int ch = 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++;
- 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++;
- }
- truncate_path(f, fstate.path_len);
- CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr);
- return 0;
-}
-
-/* array = '[' [ value { ',' value } ] ']' */
-static int 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);
- {
- SET_STATE(f, f->cur - 1, "", 0);
- while (cur(f) != ']') {
- snprintf(buf, sizeof(buf), "[%d]", i);
- i++;
- current_path_len = 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(test_and_skip(f, ']'));
- truncate_path(f, fstate.path_len);
- CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr);
- }
- }
- return 0;
-}
-
-static int expect(struct frozen *f, const char *s, int len,
- enum json_token_type tok_type) {
- int i, n = 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);
-
- CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr);
-
- return 0;
-}
-
-/* value = 'null' | 'true' | 'false' | number | string | array | object */
-static int parse_value(struct frozen *f) {
- int ch = cur(f);
-
- switch (ch) {
- case '"':
- TRY(parse_string(f));
- break;
- case '{':
- TRY(parse_object(f));
- break;
- case '[':
- TRY(parse_array(f));
- break;
- case 'n':
- TRY(expect(f, "null", 4, JSON_TYPE_NULL));
- break;
- case 't':
- TRY(expect(f, "true", 4, JSON_TYPE_TRUE));
- break;
- case 'f':
- TRY(expect(f, "false", 5, JSON_TYPE_FALSE));
- break;
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- TRY(parse_number(f));
- break;
- default:
- return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
- }
-
- return 0;
-}
-
-/* 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));
- } else if (ch == '"') {
- TRY(parse_string(f));
- } else {
- return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID;
- }
- return 0;
-}
-
-/* pair = key ':' value */
-static int parse_pair(struct frozen *f) {
- int current_path_len;
- const char *tok;
- skip_whitespaces(f);
- tok = f->cur;
- TRY(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);
- }
- TRY(test_and_skip(f, ':'));
- TRY(parse_value(f));
- truncate_path(f, current_path_len);
- return 0;
-}
-
-/* object = '{' pair { ',' pair } '}' */
-static int parse_object(struct frozen *f) {
- TRY(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);
- }
- }
- return 0;
-}
-
-static int 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);
-}
-
-int json_escape(struct json_out *out, const char *p, size_t len) WEAK;
-int json_escape(struct json_out *out, const char *p, size_t len) {
- size_t i, cl, n = 0;
- const char *hex_digits = "0123456789abcdef";
- const char *specials = "btnvfr";
-
- for (i = 0; i < len; i++) {
- unsigned char ch = ((unsigned char *) p)[i];
- if (ch == '"' || ch == '\\') {
- n += out->printer(out, "\\", 1);
- n += out->printer(out, p + i, 1);
- } else if (ch >= '\b' && ch <= '\r') {
- n += out->printer(out, "\\", 1);
- n += out->printer(out, &specials[ch - '\b'], 1);
- } else if (isprint(ch)) {
- n += out->printer(out, p + i, 1);
- } else if ((cl = get_utf8_char_len(ch)) == 1) {
- n += out->printer(out, "\\u00", 4);
- n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1);
- n += out->printer(out, &hex_digits[ch % 0xf], 1);
- } else {
- n += out->printer(out, p + i, cl);
- i += cl - 1;
- }
- }
-
- return n;
-}
-
-int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK;
-int json_printer_buf(struct json_out *out, const char *buf, size_t len) {
- size_t avail = out->u.buf.size - out->u.buf.len;
- size_t n = len < avail ? len : avail;
- memcpy(out->u.buf.buf + out->u.buf.len, buf, n);
- out->u.buf.len += n;
- if (out->u.buf.size > 0) {
- size_t idx = out->u.buf.len;
- if (idx >= out->u.buf.size) idx = out->u.buf.size - 1;
- out->u.buf.buf[idx] = '\0';
- }
- return len;
-}
-
-int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK;
-int json_printer_file(struct json_out *out, const char *buf, size_t len) {
- return fwrite(buf, 1, len, out->u.fp);
-}
-
-static int b64idx(int c) {
- if (c < 26) {
- return c + 'A';
- } else if (c < 52) {
- return c - 26 + 'a';
- } else if (c < 62) {
- return c - 52 + '0';
- } else {
- return c == 62 ? '+' : '/';
- }
-}
-
-static int b64rev(int c) {
- if (c >= 'A' && c <= 'Z') {
- return c - 'A';
- } else if (c >= 'a' && c <= 'z') {
- return c + 26 - 'a';
- } else if (c >= '0' && c <= '9') {
- return c + 52 - '0';
- } else if (c == '+') {
- return 62;
- } else if (c == '/') {
- return 63;
- } else {
- return 64;
- }
-}
-
-static 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;
- for (i = 0; i < n; i += 3) {
- int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0;
- buf[0] = b64idx(a >> 2);
- buf[1] = b64idx((a & 3) << 4 | (b >> 4));
- buf[2] = b64idx((b & 15) << 2 | (c >> 6));
- buf[3] = b64idx(c & 63);
- if (i + 1 >= n) buf[2] = '=';
- if (i + 2 >= n) buf[3] = '=';
- len += out->printer(out, buf, sizeof(buf));
- }
- return len;
-}
-
-static int b64dec(const char *src, int n, char *dst) {
- const char *end = src + n;
- int len = 0;
- while (src + 3 < end) {
- int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]),
- d = b64rev(src[3]);
- dst[len++] = (a << 2) | (b >> 4);
- if (src[2] != '=') {
- dst[len++] = (b << 4) | (c >> 2);
- if (src[3] != '=') {
- dst[len++] = (c << 6) | d;
- }
- }
- src += 4;
- }
- return len;
-}
-
-int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK;
-int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
- int len = 0;
- const char *quote = "\"", *null = "null";
- va_list ap;
- va_copy(ap, xap);
-
- while (*fmt != '\0') {
- if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) {
- len += out->printer(out, fmt, 1);
- fmt++;
- } else if (fmt[0] == '%') {
- char buf[21];
- size_t skip = 2;
-
- if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) {
- int64_t val = va_arg(ap, int64_t);
- const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT;
- snprintf(buf, sizeof(buf), fmt2, val);
- len += out->printer(out, buf, strlen(buf));
- skip += 2;
- } else if (fmt[1] == 'z' && fmt[2] == 'u') {
- size_t val = va_arg(ap, size_t);
- snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val);
- len += out->printer(out, buf, strlen(buf));
- skip += 1;
- } else if (fmt[1] == 'M') {
- json_printf_callback_t f = va_arg(ap, json_printf_callback_t);
- len += f(out, &ap);
- } else if (fmt[1] == 'B') {
- int val = va_arg(ap, int);
- const char *str = val ? "true" : "false";
- len += out->printer(out, str, strlen(str));
- } else if (fmt[1] == 'H') {
- const char *hex = "0123456789abcdef";
- int i, n = va_arg(ap, int);
- const unsigned char *p = va_arg(ap, const unsigned char *);
- len += out->printer(out, quote, 1);
- for (i = 0; i < n; i++) {
- len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1);
- len += out->printer(out, &hex[p[i] & 0xf], 1);
- }
- len += out->printer(out, quote, 1);
- } else if (fmt[1] == 'V') {
- 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);
- } else if (fmt[1] == 'Q' ||
- (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) {
- size_t l = 0;
- const char *p;
-
- if (fmt[1] == '.') {
- l = (size_t) va_arg(ap, int);
- skip += 2;
- }
- p = va_arg(ap, char *);
-
- if (p == NULL) {
- len += out->printer(out, null, 4);
- } else {
- if (fmt[1] == 'Q') {
- l = strlen(p);
- }
- len += out->printer(out, quote, 1);
- len += json_escape(out, p, l);
- len += out->printer(out, quote, 1);
- }
- } else {
- /*
- * we delegate printing to the system printf.
- * The goal here is to delegate all modifiers parsing to the system
- * printf, as you can see below we still have to parse the format
- * types.
- *
- * Currently, %s with strings longer than 20 chars will require
- * double-buffering (an auxiliary buffer will be allocated from heap).
- * TODO(dfrank): reimplement %s and %.*s in order to avoid that.
- */
-
- const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789";
- size_t n = strspn(fmt + 1, end_of_format_specifier);
- char *pbuf = buf;
- size_t need_len;
- char fmt2[20];
- va_list sub_ap;
- strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : 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)) {
- /*
- * 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);
- }
-
- /*
- * however we need to parse the type ourselves in order to advance
- * the va_list by the correct amount; there is no portable way to
- * inherit the advancement made by vprintf.
- * 32-bit (linux or windows) passes va_list by value.
- */
- if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) ||
- (n + 1 == 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 *);
- } else {
- switch (fmt2[n]) {
- case 'u':
- case 'd':
- (void) va_arg(ap, int);
- break;
- case 'g':
- case 'f':
- (void) va_arg(ap, double);
- break;
- case 'p':
- (void) va_arg(ap, void *);
- break;
- default:
- /* many types are promoted to int */
- (void) va_arg(ap, int);
- }
- }
-
- len += out->printer(out, pbuf, strlen(pbuf));
- skip = n + 1;
-
- /* If buffer was allocated from heap, free it */
- if (pbuf != buf) {
- free(pbuf);
- pbuf = NULL;
- }
- }
- fmt += skip;
- } else if (*fmt == '_' || is_alpha(*fmt)) {
- len += out->printer(out, quote, 1);
- while (*fmt == '_' || is_alpha(*fmt) || is_digit(*fmt)) {
- len += out->printer(out, fmt, 1);
- fmt++;
- }
- len += out->printer(out, quote, 1);
- } else {
- fmt++;
- }
- }
- va_end(ap);
-
- return len;
-}
-
-int json_printf(struct json_out *out, const char *fmt, ...) WEAK;
-int json_printf(struct json_out *out, const char *fmt, ...) {
- int n;
- va_list ap;
- va_start(ap, fmt);
- n = json_vprintf(out, fmt, ap);
- va_end(ap);
- return n;
-}
-
-int json_printf_array(struct json_out *out, va_list *ap) WEAK;
-int json_printf_array(struct json_out *out, va_list *ap) {
- int len = 0;
- char *arr = va_arg(*ap, char *);
- size_t i, arr_size = va_arg(*ap, size_t);
- size_t elem_size = va_arg(*ap, size_t);
- const char *fmt = va_arg(*ap, char *);
- len += json_printf(out, "[", 1);
- for (i = 0; arr != NULL && i < arr_size / elem_size; i++) {
- union {
- int64_t i;
- double d;
- } val;
- memcpy(&val, arr + i * elem_size,
- elem_size > sizeof(val) ? sizeof(val) : elem_size);
- if (i > 0) len += json_printf(out, ", ");
- if (strchr(fmt, 'f') != NULL) {
- len += json_printf(out, fmt, val.d);
- } else {
- len += json_printf(out, fmt, val.i);
- }
- }
- len += json_printf(out, "]", 1);
- return len;
-}
-
-#ifdef _WIN32
-int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) WEAK;
-int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
- int res = _vsnprintf(str, size, format, ap);
- va_end(ap);
- if (res >= size) {
- str[size - 1] = '\0';
- }
- return res;
-}
-
-int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK;
-int cs_win_snprintf(char *str, size_t size, const char *format, ...) {
- int res;
- va_list ap;
- va_start(ap, format);
- res = vsnprintf(str, size, format, ap);
- va_end(ap);
- return res;
-}
-#endif /* _WIN32 */
-
-int json_walk(const char *json_string, int json_string_length,
- json_walk_callback_t callback, void *callback_data) WEAK;
-int json_walk(const char *json_string, int json_string_length,
- json_walk_callback_t callback, void *callback_data) {
- struct frozen frozen;
-
- memset(&frozen, 0, sizeof(frozen));
- frozen.end = json_string + json_string_length;
- frozen.cur = json_string;
- frozen.callback_data = callback_data;
- frozen.callback = callback;
-
- TRY(doit(&frozen));
-
- return frozen.cur - json_string;
-}
-
-struct scan_array_info {
- char path[JSON_MAX_PATH_LEN];
- struct json_token *token;
-};
-
-static void json_scanf_array_elem_cb(void *callback_data, const char *name,
- size_t name_len, const char *path,
- const struct json_token *token) {
- struct scan_array_info *info = (struct scan_array_info *) callback_data;
-
- (void) name;
- (void) name_len;
-
- if (strcmp(path, info->path) == 0) {
- *info->token = *token;
- }
-}
-
-int json_scanf_array_elem(const char *s, int len, const char *path, int idx,
- struct json_token *token) WEAK;
-int json_scanf_array_elem(const char *s, int len, const char *path, int idx,
- struct json_token *token) {
- struct scan_array_info info;
- info.token = token;
- 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;
-}
-
-struct json_scanf_info {
- int num_conversions;
- char *path;
- const char *fmt;
- void *target;
- void *user_data;
- int type;
-};
-
-int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK;
-int json_unescape(const char *src, int slen, char *dst, int dlen) {
- char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p;
- const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t";
-
- while (src < send) {
- if (*src == '\\') {
- if (++src >= send) return JSON_STRING_INCOMPLETE;
- if (*src == 'u') {
- /* TODO(lsm): \uXXXX escapes drag utf8 lib... Do it at some stage */
- return JSON_STRING_INVALID;
- } else if ((p = (char *) strchr(esc1, *src)) != NULL) {
- if (dst < dend) *dst = esc2[p - esc1];
- } else {
- return JSON_STRING_INVALID;
- }
- } else {
- if (dst < dend) *dst = *src;
- }
- dst++;
- src++;
- }
-
- return dst - orig_dst;
-}
-
-static void json_scanf_cb(void *callback_data, const char *name,
- size_t name_len, const char *path,
- const struct json_token *token) {
- struct json_scanf_info *info = (struct json_scanf_info *) callback_data;
-
- (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;
- * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START
- */
- return;
- }
-
- switch (info->type) {
- case 'B':
- info->num_conversions++;
- *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);
- break;
- case 'M': {
- union {
- void *p;
- json_scanner_t f;
- } u = {info->target};
- info->num_conversions++;
- u.f(token->ptr, token->len, info->user_data);
- break;
- }
- case 'Q': {
- char **dst = (char **) info->target;
- if (token->type == JSON_TYPE_NULL) {
- *dst = NULL;
- } else {
- int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0);
- if (unescaped_len >= 0 &&
- (*dst = (char *) malloc(unescaped_len + 1)) != NULL) {
- info->num_conversions++;
- json_unescape(token->ptr, token->len, *dst, unescaped_len);
- (*dst)[unescaped_len] = '\0';
- }
- }
- break;
- }
- case 'H': {
- char **dst = (char **) info->user_data;
- int i, len = token->len / 2;
- *(int *) info->target = len;
- if ((*dst = (char *) malloc(len + 1)) != NULL) {
- for (i = 0; i < len; i++) {
- (*dst)[i] = hexdec(token->ptr + 2 * i);
- }
- (*dst)[len] = '\0';
- info->num_conversions++;
- }
- break;
- }
- case 'V': {
- char **dst = (char **) info->target;
- int len = token->len * 4 / 3 + 2;
- if ((*dst = (char *) malloc(len + 1)) != NULL) {
- int n = b64dec(token->ptr, token->len, *dst);
- (*dst)[n] = '\0';
- *(int *) info->user_data = n;
- info->num_conversions++;
- }
- break;
- }
- case 'T':
- info->num_conversions++;
- *(struct json_token *) info->target = *token;
- break;
- default:
- info->num_conversions += sscanf(token->ptr, info->fmt, info->target);
- break;
- }
-}
-
-int json_vscanf(const char *s, int len, const char *fmt, va_list ap) WEAK;
-int json_vscanf(const char *s, int len, const char *fmt, va_list ap) {
- char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20];
- int i = 0;
- char *p = NULL;
- struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0};
-
- while (fmt[i] != '\0') {
- if (fmt[i] == '{') {
- strcat(path, ".");
- i++;
- } else if (fmt[i] == '}') {
- if ((p = strrchr(path, '.')) != NULL) *p = '\0';
- i++;
- } else if (fmt[i] == '%') {
- info.target = va_arg(ap, void *);
- info.type = fmt[i + 1];
- switch (fmt[i + 1]) {
- case 'M':
- case 'V':
- case 'H':
- info.user_data = va_arg(ap, void *);
- /* FALLTHROUGH */
- case 'B':
- case 'Q':
- case 'T':
- i += 2;
- break;
- default: {
- const char *delims = ", \t\r\n]}";
- int conv_len = strcspn(fmt + i + 1, delims) + 1;
- snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", conv_len, fmt + i);
- 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) {
- 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]);
- i += key_len + strspn(fmt + i + key_len, delims);
- } else {
- i++;
- }
- }
- return info.num_conversions;
-}
-
-int json_scanf(const char *str, int len, const char *fmt, ...) WEAK;
-int json_scanf(const char *str, int len, const char *fmt, ...) {
- int result;
- va_list ap;
- va_start(ap, fmt);
- result = json_vscanf(str, len, fmt, ap);
- va_end(ap);
- return result;
-}
diff --git a/frozen.h b/frozen.h
@@ -1,220 +0,0 @@
-/*
- * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
- * Copyright (c) 2013 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/>.
- *
- * 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.
- *
- * Alternatively, you can license this library under a commercial
- * license, as set out in <http://cesanta.com/products.html>.
- */
-
-#ifndef CS_FROZEN_FROZEN_H_
-#define CS_FROZEN_FROZEN_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-
-/* JSON token type */
-enum json_token_type {
- JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */
- JSON_TYPE_STRING,
- JSON_TYPE_NUMBER,
- JSON_TYPE_TRUE,
- JSON_TYPE_FALSE,
- JSON_TYPE_NULL,
- JSON_TYPE_OBJECT_START,
- JSON_TYPE_OBJECT_END,
- JSON_TYPE_ARRAY_START,
- JSON_TYPE_ARRAY_END,
-
- JSON_TYPES_CNT
-};
-
-/*
- * Structure containing token type and value. Used in `json_walk()` and
- * `json_scanf()` with the format specifier `%T`.
- */
-struct json_token {
- const char *ptr; /* Points to the beginning of the value */
- int len; /* Value length */
- enum json_token_type type; /* Type of the token, possible values are above */
-};
-
-#define JSON_INVALID_TOKEN \
- { 0, 0, JSON_TYPE_INVALID }
-
-/* Error codes */
-#define JSON_STRING_INVALID -1
-#define JSON_STRING_INCOMPLETE -2
-
-/*
- * Callback-based SAX-like API.
- *
- * Property name and length is given only if it's available: i.e. if current
- * event is an object's property. In other cases, `name` is `NULL`. For
- * example, name is never given:
- * - For the first value in the JSON string;
- * - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END
- *
- * E.g. for the input `{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }`,
- * the sequence of callback invocations will be as follows:
- *
- * - type: JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULL
- * - type: JSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123"
- * - type: JSON_TYPE_ARRAY_START, name: "bar", path: ".bar", value: NULL
- * - type: JSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1"
- * - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2"
- * - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL
- * - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true"
- * - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\":
- *true }"
- * - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, {
- *\"baz\": true } ]"
- * - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123,
- *\"bar\": [ 1, 2, { \"baz\": true } ] }"
- */
-typedef void (*json_walk_callback_t)(void *callback_data, const char *name,
- size_t name_len, const char *path,
- const struct json_token *token);
-
-/*
- * Parse `json_string`, invoking `callback` in a way similar to SAX parsers;
- * see `json_walk_callback_t`.
- */
-int json_walk(const char *json_string, int json_string_length,
- json_walk_callback_t callback, void *callback_data);
-
-/*
- * JSON generation API.
- * struct json_out abstracts output, allowing alternative printing plugins.
- */
-struct json_out {
- int (*printer)(struct json_out *, const char *str, size_t len);
- union {
- struct {
- char *buf;
- size_t size;
- size_t len;
- } buf;
- void *data;
- FILE *fp;
- } u;
-};
-
-extern int json_printer_buf(struct json_out *, const char *, size_t);
-extern int json_printer_file(struct json_out *, const char *, size_t);
-
-#define JSON_OUT_BUF(buf, len) \
- { \
- json_printer_buf, { \
- { buf, len, 0 } \
- } \
- }
-#define JSON_OUT_FILE(fp) \
- { \
- json_printer_file, { \
- { (void *) fp, 0, 0 } \
- } \
- }
-
-typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap);
-
-/*
- * Generate formatted output into a given sting buffer.
- * This is a superset of printf() function, with extra format specifiers:
- * - `%B` print json boolean, `true` or `false`. Accepts an `int`.
- * - `%Q` print quoted escaped string or `null`. Accepts a `const char *`.
- * - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *`
- * - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`.
- * - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`.
- * - `%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
- * supplied buffer, that is an indicator of overflow. In the overflow case,
- * overflown bytes are not printed.
- */
-int json_printf(struct json_out *, const char *fmt, ...);
-int json_vprintf(struct json_out *, 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.
- */
-int json_printf_array(struct json_out *, va_list *ap);
-
-/*
- * Scan JSON string `str`, performing scanf-like conversions according to `fmt`.
- * This is a `scanf()` - like function, with following differences:
- *
- * 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`.
- * - %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.
- * Result string is base64-decoded, malloced and NUL-terminated.
- * The length of result string is stored in `int *` placeholder.
- * Caller must free() the result.
- * - %H: consumes `int *`, `char **`.
- * Expects a hex-encoded string, e.g. "fa014f".
- * Result string is hex-decoded, malloced and NUL-terminated.
- * The length of the result string is stored in `int *` placeholder.
- * Caller must free() the result.
- * - %M: consumes custom scanning function pointer and
- * `void *user_data` parameter - see json_scanner_t definition.
- * - %T: consumes `struct json_token *`, fills it out with matched token.
- *
- * Return number of elements successfully scanned & converted.
- * Negative number means scan error.
- */
-int json_scanf(const char *str, int str_len, const char *fmt, ...);
-int json_vscanf(const char *str, int str_len, const char *fmt, va_list ap);
-
-/* json_scanf's %M handler */
-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.
- */
-int json_scanf_array_elem(const char *s, int len, const char *path, int index,
- struct json_token *token);
-
-/*
- * Unescape JSON-encoded string src,slen into dst, dlen.
- * src and dst may overlap.
- * If destination buffer is too small (or zero-length), result string is not
- * written but the length is counted nevertheless (similar to snprintf).
- * Return the length of unescaped string in bytes.
- */
-int json_unescape(const char *src, int slen, char *dst, int dlen);
-
-/*
- * Escape a string `str`, `str_len` into the printer `out`.
- * Return the number of bytes printed.
- */
-int json_escape(struct json_out *out, const char *str, size_t str_len);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* CS_FROZEN_FROZEN_H_ */
diff --git a/gs.c b/gs.c
@@ -6,8 +6,8 @@
#include <string.h>
#include <unistd.h>
-#include "arg.h"
-#include "frozen.h"
+#include "extern/arg.h"
+#include "extern/frozen.h"
#include "util.h"
/* defines */