improvements - json2tsv - JSON to TSV converter
(HTM) git clone git://git.codemadness.org/json2tsv
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 2f12ae56971af8acaa52357fe1042d37f71ffbd4
(DIR) parent 9a85d6c5f69749ac71c690e5b4d36b8e70b15d16
(HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Sun, 20 Oct 2019 14:30:24 +0200
improvements
- separate into more precise JSON types: primitives to: bool, null, number.
- separate JSON code into a reusable "library": json.c.
- remove errstr from parsejson(): just return an error code for out-of-memory
and a JSON parse error. The tool returns exit code 2 when out-of-memory.
- much more strict JSON parsing for incorrect input.
- make primitives a fixed buffer. The longest size data for primitives are
numbers and cannot be long anyway.
- improve README example (reddit).
Diffstat:
M Makefile | 26 +++++++++++++++++++++-----
M README | 35 ++++++++++++++++---------------
A json.c | 314 +++++++++++++++++++++++++++++++
A json.h | 26 ++++++++++++++++++++++++++
M json2tsv.c | 301 +------------------------------
5 files changed, 386 insertions(+), 316 deletions(-)
---
(DIR) diff --git a/Makefile b/Makefile
@@ -8,35 +8,51 @@ PREFIX = /usr/local
MANPREFIX = ${PREFIX}/man
DOCPREFIX = ${PREFIX}/share/doc/${NAME}
+RANLIB = ranlib
+
BIN = ${NAME}
SRC = ${BIN:=.c}
+HDR = json.h
MAN1 = ${BIN:=.1}
DOC = \
LICENSE\
README
+LIBJSON = libjson.a
+LIBJSONSRC = json.c
+LIBJSONOBJ = ${LIBJSONSRC:.c=.o}
+
+LIB = ${LIBJSON}
+
all: ${BIN}
-${BIN}: ${@:=.o}
+${BIN}: ${LIB} ${@:=.o}
-OBJ = ${SRC:.c=.o}
+OBJ = ${SRC:.c=.o} ${LIBJSONOBJ}
+
+${OBJ}: ${HDR}
.o:
- ${CC} ${LDFLAGS} -o $@ $<
+ ${CC} ${LDFLAGS} -o $@ $< ${LIB}
.c.o:
${CC} ${CFLAGS} ${CPPFLAGS} -o $@ -c $<
+${LIBJSON}: ${LIBJSONOBJ}
+ ${AR} rc $@ $?
+ ${RANLIB} $@
+
dist:
rm -rf "${NAME}-${VERSION}"
mkdir -p "${NAME}-${VERSION}"
- cp -f ${MAN1} ${DOC} ${SRC} Makefile "${NAME}-${VERSION}"
+ cp -f ${MAN1} ${DOC} ${HDR} \
+ ${SRC} ${LIBJSONSRC} Makefile "${NAME}-${VERSION}"
# make tarball
tar -cf - "${NAME}-${VERSION}" | gzip -c > "${NAME}-${VERSION}.tar.gz"
rm -rf "${NAME}-${VERSION}"
clean:
- rm -f ${BIN} ${OBJ}
+ rm -f ${BIN} ${OBJ} ${LIB}
install: all
# installing executable files.
(DIR) diff --git a/README b/README
@@ -16,10 +16,16 @@ The output format per line is:
The nodename and value are escaped (\n, \t and \\). Control-characters are
removed.
-The type can be: o (for object), a (for array), p (for primitive such as true,
-false, null, a number) or s (for string).
+The type field is a single byte and can be:
-Then filtering is easy using some awk script on the node "selector".
+ a for array
+ b for bool
+ n for number
+ o for object
+ s for string
+ ? for null
+
+Filtering on the first field "nodename" is easy using awk for example.
See the json2tsv(1) man page for the full documentation.
@@ -32,29 +38,24 @@ plain-text list using awk:
#!/bin/sh
-curl -s -H 'User-Agent:' 'https://old.reddit.com/.json' | \
+curl -s -H 'User-Agent:' 'https://old.reddit.com/.json?raw_json=1&limit=100' | \
json2tsv | \
-awk 'BEGIN {
- FS = OFS = "\t";
- n = 0;
- title = author = subreddit = "";
-}
+awk -F '\t' '
function show() {
- if (length(title) == 0)
+ if (length(o["title"]) == 0)
return;
- print n ". " title " by " author " in r/" subreddit;
- print url;
+ print n ". " o["title"] " by " o["author"] " in r/" o["subreddit"];
+ print o["url"];
print "";
}
$1 == ".data.children[].data" {
show();
n++;
- title = url = author = subreddit = "";
+ delete o;
+}
+$1 ~ /^\.data\.children\[\]\.data\.[a-zA-Z0-9_]*$/ {
+ o[substr($1, 23)] = $3;
}
-$1 == ".data.children[].data.url" { url = $3; }
-$1 == ".data.children[].data.title" { title = $3; }
-$1 == ".data.children[].data.author" { author = $3; }
-$1 == ".data.children[].data.subreddit" { subreddit = $3; }
END {
show();
}'
(DIR) diff --git a/json.c b/json.c
@@ -0,0 +1,314 @@
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define GETNEXT getchar
+
+#include "json.h"
+
+int
+codepointtoutf8(long r, char *s)
+{
+ if (r == 0) {
+ return 0; /* NUL byte */
+ } else if (r <= 0x7F) {
+ /* 1 byte: 0aaaaaaa */
+ s[0] = r;
+ return 1;
+ } else if (r <= 0x07FF) {
+ /* 2 bytes: 00000aaa aabbbbbb */
+ s[0] = 0xC0 | ((r & 0x0007C0) >> 6); /* 110aaaaa */
+ s[1] = 0x80 | (r & 0x00003F); /* 10bbbbbb */
+ return 2;
+ } else if (r <= 0xFFFF) {
+ /* 3 bytes: aaaabbbb bbcccccc */
+ s[0] = 0xE0 | ((r & 0x00F000) >> 12); /* 1110aaaa */
+ s[1] = 0x80 | ((r & 0x000FC0) >> 6); /* 10bbbbbb */
+ s[2] = 0x80 | (r & 0x00003F); /* 10cccccc */
+ return 3;
+ } else {
+ /* 4 bytes: 000aaabb bbbbcccc ccdddddd */
+ s[0] = 0xF0 | ((r & 0x1C0000) >> 18); /* 11110aaa */
+ s[1] = 0x80 | ((r & 0x03F000) >> 12); /* 10bbbbbb */
+ s[2] = 0x80 | ((r & 0x000FC0) >> 6); /* 10cccccc */
+ s[3] = 0x80 | (r & 0x00003F); /* 10dddddd */
+ return 4;
+ }
+}
+
+int
+hexdigit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return 10 + (c - 'a');
+ else if (c >= 'A' && c <= 'F')
+ return 10 + (c - 'A');
+ return 0;
+}
+
+int
+capacity(char **value, size_t *sz, size_t cur, size_t inc)
+{
+ size_t need, newsiz;
+ char *newp;
+
+ /* check for addition overflow */
+ if (cur > SIZE_MAX - inc) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ need = cur + inc;
+
+ if (need > *sz) {
+ if (need > SIZE_MAX / 2) {
+ newsiz = SIZE_MAX;
+ } else {
+ for (newsiz = *sz < 64 ? 64 : *sz; newsiz <= need; newsiz *= 2)
+ ;
+ }
+ if (!(newp = realloc(*value, newsiz)))
+ return -1; /* up to caller to free *value */
+ *value = newp;
+ *sz = newsiz;
+ }
+ return 0;
+}
+
+#define EXPECT_VALUE "{[\"-0123456789tfn"
+#define EXPECT_STRING "\""
+#define EXPECT_END "}],"
+#define EXPECT_NOTHING ""
+#define EXPECT_OBJECT_STRING EXPECT_STRING "}"
+#define EXPECT_ARRAY_VALUE EXPECT_VALUE "]"
+
+#define JSON_INVALID() do { ret = JSON_ERROR_INVALID; goto end; } while (0);
+
+int
+parsejson(void (*cb)(struct json_node *, size_t, const char *))
+{
+ struct json_node nodes[JSON_MAX_NODE_DEPTH] = { 0 };
+ size_t depth = 0, p = 0, len, sz = 0;
+ long cp, hi, lo;
+ char pri[128], *str = NULL;
+ int c, i, escape, iskey = 0, ret = JSON_ERROR_MEM;
+ const char *expect = EXPECT_VALUE;
+
+ if (capacity(&(nodes[0].name), &(nodes[0].namesiz), 0, 1) == -1)
+ goto end;
+ nodes[0].name[0] = '\0';
+
+ while (1) {
+ c = GETNEXT();
+handlechr:
+ if (c == EOF)
+ break;
+
+ if (c && strchr(" \t\n\r", c)) /* (no \v, \f, \b etc) */
+ continue;
+
+ if (!c || !strchr(expect, c))
+ JSON_INVALID();
+
+ switch (c) {
+ case ':':
+ /* not in an object or key in object is not a string */
+ if (!depth || nodes[depth - 1].type != TYPE_OBJECT ||
+ nodes[depth].type != TYPE_STRING)
+ JSON_INVALID();
+ iskey = 0;
+ expect = EXPECT_VALUE;
+ break;
+ case '"':
+ nodes[depth].type = TYPE_STRING;
+ escape = 0;
+ len = 0;
+ while (1) {
+ c = GETNEXT();
+chr:
+ /* EOF or control char: 0x7f is not defined as a control char in RFC8259 */
+ if (c < 0x20)
+ JSON_INVALID();
+
+ if (escape) {
+escchr:
+ escape = 0;
+ switch (c) {
+ case '"': /* FALLTHROUGH */
+ case '\\':
+ case '/': break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'u': /* hex hex hex hex */
+ if (capacity(&str, &sz, len, 4) == -1)
+ goto end;
+ for (i = 12, cp = 0; i >= 0; i -= 4) {
+ if ((c = GETNEXT()) == EOF || !isxdigit(c))
+ JSON_INVALID(); /* invalid codepoint */
+ cp |= (hexdigit(c) << i);
+ }
+ /* RFC8259 - 7. Strings - surrogates.
+ * 0xd800 - 0xdb7f - high surrogates */
+ if (cp >= 0xd800 && cp <= 0xdb7f) {
+ if ((c = GETNEXT()) != '\\') {
+ len += codepointtoutf8(cp, &str[len]);
+ goto chr;
+ }
+ if ((c = GETNEXT()) != 'u') {
+ len += codepointtoutf8(cp, &str[len]);
+ goto escchr;
+ }
+ for (hi = cp, i = 12, lo = 0; i >= 0; i -= 4) {
+ if ((c = GETNEXT()) == EOF || !isxdigit(c))
+ JSON_INVALID(); /* invalid codepoint */
+ lo |= (hexdigit(c) << i);
+ }
+ /* 0xdc00 - 0xdfff - low surrogates */
+ if (lo >= 0xdc00 && lo <= 0xdfff) {
+ cp = (hi << 10) + lo - 56613888; /* - offset */
+ } else {
+ /* handle graceful: raw invalid output bytes */
+ len += codepointtoutf8(hi, &str[len]);
+ if (capacity(&str, &sz, len, 4) == -1)
+ goto end;
+ len += codepointtoutf8(lo, &str[len]);
+ continue;
+ }
+ }
+ len += codepointtoutf8(cp, &str[len]);
+ continue;
+ default:
+ JSON_INVALID(); /* invalid escape char */
+ }
+ if (capacity(&str, &sz, len, 1) == -1)
+ goto end;
+ str[len++] = c;
+ } else if (c == '\\') {
+ escape = 1;
+ } else if (c == '"') {
+ if (capacity(&str, &sz, len, 1) == -1)
+ goto end;
+ str[len++] = '\0';
+
+ if (iskey) {
+ if (capacity(&(nodes[depth].name), &(nodes[depth].namesiz), len, 1) == -1)
+ goto end;
+ memcpy(nodes[depth].name, str, len);
+ } else {
+ cb(nodes, depth + 1, str);
+ }
+ break;
+ } else {
+ if (capacity(&str, &sz, len, 1) == -1)
+ goto end;
+ str[len++] = c;
+ }
+ }
+ if (iskey)
+ expect = ":";
+ else
+ expect = EXPECT_END;
+ break;
+ case '[':
+ case '{':
+ if (depth + 1 >= JSON_MAX_NODE_DEPTH)
+ JSON_INVALID(); /* too deep */
+
+ nodes[depth].index = 0;
+ nodes[depth].type = TYPE_OBJECT;
+ if (c == '{') {
+ iskey = 1;
+ nodes[depth].type = TYPE_OBJECT;
+ expect = EXPECT_OBJECT_STRING;
+ } else if (c == '[') {
+ nodes[depth].type = TYPE_ARRAY;
+ expect = EXPECT_ARRAY_VALUE;
+ }
+
+ cb(nodes, depth + 1, "");
+
+ depth++;
+ nodes[depth].index = 0;
+ if (capacity(&(nodes[depth].name), &(nodes[depth].namesiz), 0, 1) == -1)
+ goto end;
+ nodes[depth].name[0] = '\0';
+ break;
+ case ']':
+ case '}':
+ if (!depth ||
+ (c == ']' && nodes[depth - 1].type != TYPE_ARRAY) ||
+ (c == '}' && nodes[depth - 1].type != TYPE_OBJECT))
+ JSON_INVALID(); /* unbalanced nodes */
+
+ nodes[--depth].index++;
+ if (!depth)
+ expect = EXPECT_NOTHING;
+ else
+ expect = EXPECT_END;
+ break;
+ case ',':
+ nodes[depth - 1].index++;
+ if (nodes[depth - 1].type == TYPE_OBJECT) {
+ iskey = 1;
+ expect = EXPECT_STRING;
+ } else {
+ expect = EXPECT_VALUE;
+ }
+ break;
+ case 't': /* true */
+ if (GETNEXT() != 'r' || GETNEXT() != 'u' || GETNEXT() != 'e')
+ JSON_INVALID();
+ nodes[depth].type = TYPE_BOOL;
+ cb(nodes, depth + 1, "true");
+ expect = EXPECT_END;
+ break;
+ case 'f': /* false */
+ if (GETNEXT() != 'a' || GETNEXT() != 'l' || GETNEXT() != 's' || GETNEXT() != 'e')
+ JSON_INVALID();
+ nodes[depth].type = TYPE_BOOL;
+ cb(nodes, depth + 1, "false");
+ expect = EXPECT_END;
+ break;
+ case 'n': /* null */
+ if (GETNEXT() != 'u' || GETNEXT() != 'l' || GETNEXT() != 'l')
+ JSON_INVALID();
+ nodes[depth].type = TYPE_NULL;
+ cb(nodes, depth + 1, "null");
+ expect = EXPECT_END;
+ break;
+ default: /* number */
+ nodes[depth].type = TYPE_NUMBER;
+ p = 0;
+ pri[p++] = c;
+ expect = EXPECT_END;
+ while (1) {
+ c = GETNEXT();
+ if (!c || !strchr("0123456789eE+-.", c) ||
+ c == EOF || p + 1 >= sizeof(pri)) {
+ pri[p] = '\0';
+ cb(nodes, depth + 1, pri);
+ goto handlechr; /* do not read next char, handle this */
+ } else {
+ pri[p++] = c;
+ }
+ }
+ }
+ }
+ if (depth)
+ JSON_INVALID(); /* unbalanced nodes */
+
+ ret = 0; /* success */
+end:
+ for (depth = 0; depth < sizeof(nodes) / sizeof(nodes[0]); depth++)
+ free(nodes[depth].name);
+ free(str);
+
+ return ret;
+}
(DIR) diff --git a/json.h b/json.h
@@ -0,0 +1,26 @@
+#include <stdint.h>
+
+enum JSONType {
+ TYPE_ARRAY = 'a',
+ TYPE_OBJECT = 'o',
+ TYPE_STRING = 's',
+ TYPE_BOOL = 'b',
+ TYPE_NULL = '?',
+ TYPE_NUMBER = 'n'
+};
+
+enum JSONError {
+ JSON_ERROR_MEM = -2,
+ JSON_ERROR_INVALID = -1
+};
+
+#define JSON_MAX_NODE_DEPTH 64
+
+struct json_node {
+ enum JSONType type;
+ char *name;
+ size_t namesiz;
+ size_t index; /* count/index for array or object type */
+};
+
+int parsejson(void (*cb)(struct json_node *, size_t, const char *));
(DIR) diff --git a/json2tsv.c b/json2tsv.c
@@ -11,299 +11,10 @@
#define pledge(a,b) 0
#endif
-#define GETNEXT getchar
-
-enum JSONType {
- TYPE_PRIMITIVE = 'p',
- TYPE_STRING = 's',
- TYPE_ARRAY = 'a',
- TYPE_OBJECT = 'o'
-};
-
-#define JSON_MAX_NODE_DEPTH 64
-
-struct json_node {
- enum JSONType type;
- char *name;
- size_t namesiz;
- size_t index; /* count/index for array or object type */
-};
-
-const char *JSON_ERROR_ALLOC = "cannot allocate enough memory";
-const char *JSON_ERROR_BALANCE = "unbalanced nodes";
-const char *JSON_ERROR_CODEPOINT = "invalid codepoint";
-const char *JSON_ERROR_DEPTH = "max node depth reached";
-const char *JSON_ERROR_ESCAPE_CHAR = "unknown escape character in string";
-const char *JSON_ERROR_INVALID_CHAR = "invalid character in string";
-const char *JSON_ERROR_OBJECT_MEMBER = "object member, but not in an object";
+#include "json.h"
static int showindices = 0; /* -n flag: show indices count for arrays */
-int
-codepointtoutf8(long r, char *s)
-{
- if (r == 0) {
- return 0; /* NUL byte */
- } else if (r <= 0x7F) {
- /* 1 byte: 0aaaaaaa */
- s[0] = r;
- return 1;
- } else if (r <= 0x07FF) {
- /* 2 bytes: 00000aaa aabbbbbb */
- s[0] = 0xC0 | ((r & 0x0007C0) >> 6); /* 110aaaaa */
- s[1] = 0x80 | (r & 0x00003F); /* 10bbbbbb */
- return 2;
- } else if (r <= 0xFFFF) {
- /* 3 bytes: aaaabbbb bbcccccc */
- s[0] = 0xE0 | ((r & 0x00F000) >> 12); /* 1110aaaa */
- s[1] = 0x80 | ((r & 0x000FC0) >> 6); /* 10bbbbbb */
- s[2] = 0x80 | (r & 0x00003F); /* 10cccccc */
- return 3;
- } else {
- /* 4 bytes: 000aaabb bbbbcccc ccdddddd */
- s[0] = 0xF0 | ((r & 0x1C0000) >> 18); /* 11110aaa */
- s[1] = 0x80 | ((r & 0x03F000) >> 12); /* 10bbbbbb */
- s[2] = 0x80 | ((r & 0x000FC0) >> 6); /* 10cccccc */
- s[3] = 0x80 | (r & 0x00003F); /* 10dddddd */
- return 4;
- }
-}
-
-int
-hexdigit(int c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- else if (c >= 'a' && c <= 'f')
- return 10 + (c - 'a');
- else if (c >= 'A' && c <= 'F')
- return 10 + (c - 'A');
- return 0;
-}
-
-int
-capacity(char **value, size_t *sz, size_t cur, size_t inc)
-{
- size_t need, newsiz;
- char *newp;
-
- /* check for addition overflow */
- if (cur > SIZE_MAX - inc) {
- errno = EOVERFLOW;
- return -1;
- }
- need = cur + inc;
-
- if (need > *sz) {
- if (need > SIZE_MAX / 2) {
- newsiz = SIZE_MAX;
- } else {
- for (newsiz = *sz < 64 ? 64 : *sz; newsiz <= need; newsiz *= 2)
- ;
- }
- if (!(newp = realloc(*value, newsiz)))
- return -1; /* up to caller to free *value */
- *value = newp;
- *sz = newsiz;
- }
- return 0;
-}
-
-int
-parsejson(void (*cb)(struct json_node *, size_t, const char *), const char **errstr)
-{
- struct json_node nodes[JSON_MAX_NODE_DEPTH] = { 0 };
- size_t depth = 0, v = 0, vz = 0;
- long cp, hi, lo;
- int c, i, escape, ret = -1;
- char *value = NULL;
-
- *errstr = JSON_ERROR_ALLOC;
- if (capacity(&(nodes[0].name), &(nodes[0].namesiz), 0, 1) == -1)
- goto end;
- nodes[0].name[0] = '\0';
- nodes[depth].type = TYPE_PRIMITIVE;
-
- while ((c = GETNEXT()) != EOF) {
- /* not whitespace or control character */
- if (c <= 0x20 || c == 0x7f)
- continue;
-
- switch (c) {
- case ':':
- if (!depth || nodes[depth - 1].type != TYPE_OBJECT) {
- *errstr = JSON_ERROR_OBJECT_MEMBER;
- goto end;
- }
-
- if (capacity(&value, &vz, v, 1) == -1)
- goto end;
- value[v] = '\0';
- if (capacity(&(nodes[depth].name), &(nodes[depth].namesiz), v, 1) == -1)
- goto end;
- memcpy(nodes[depth].name, value, v);
- nodes[depth].name[v] = '\0';
- v = 0;
- nodes[depth].type = TYPE_PRIMITIVE;
- break;
- case '"':
- nodes[depth].type = TYPE_STRING;
- escape = 0;
- for (;;) {
- c = GETNEXT();
-chr:
- if (c < 0x20) {
- /* EOF or control char: 0x7f is not defined as a control char in RFC8259 */
- *errstr = JSON_ERROR_INVALID_CHAR;
- goto end;
- }
-
- if (escape) {
-escchr:
- escape = 0;
- switch (c) {
- case '"': /* FALLTHROUGH */
- case '\\':
- case '/': break;
- case 'b': c = '\b'; break;
- case 'f': c = '\f'; break;
- case 'n': c = '\n'; break;
- case 'r': c = '\r'; break;
- case 't': c = '\t'; break;
- case 'u': /* hex hex hex hex */
- if (capacity(&value, &vz, v, 4) == -1)
- goto end;
- for (i = 12, cp = 0; i >= 0; i -= 4) {
- if ((c = GETNEXT()) == EOF || !isxdigit(c)) {
- *errstr = JSON_ERROR_CODEPOINT;
- goto end;
- }
- cp |= (hexdigit(c) << i);
- }
- /* RFC8259 - 7. Strings - surrogates.
- * 0xd800 - 0xdb7f - high surrogates */
- if (cp >= 0xd800 && cp <= 0xdb7f) {
- if ((c = GETNEXT()) != '\\') {
- v += codepointtoutf8(cp, &value[v]);
- goto chr;
- }
- if ((c = GETNEXT()) != 'u') {
- v += codepointtoutf8(cp, &value[v]);
- goto escchr;
- }
- for (hi = cp, i = 12, lo = 0; i >= 0; i -= 4) {
- if ((c = GETNEXT()) == EOF || !isxdigit(c)) {
- *errstr = JSON_ERROR_CODEPOINT;
- goto end;
- }
- lo |= (hexdigit(c) << i);
- }
- /* 0xdc00 - 0xdfff - low surrogates */
- if (lo >= 0xdc00 && lo <= 0xdfff) {
- cp = (hi << 10) + lo - 56613888; /* - offset */
- } else {
- /* handle graceful: raw invalid output bytes */
- v += codepointtoutf8(hi, &value[v]);
- if (capacity(&value, &vz, v, 4) == -1)
- goto end;
- v += codepointtoutf8(lo, &value[v]);
- continue;
- }
- }
- v += codepointtoutf8(cp, &value[v]);
- continue;
- default:
- *errstr = JSON_ERROR_ESCAPE_CHAR;
- goto end;
- }
- if (capacity(&value, &vz, v, 1) == -1)
- goto end;
- value[v++] = c;
- } else if (c == '\\') {
- escape = 1;
- } else if (c == '"') {
- break;
- } else {
- if (capacity(&value, &vz, v, 1) == -1)
- goto end;
- value[v++] = c;
- }
- }
- if (capacity(&value, &vz, v, 1) == -1)
- goto end;
- value[v] = '\0';
- break;
- case '[':
- case '{':
- if (depth + 1 >= JSON_MAX_NODE_DEPTH) {
- *errstr = JSON_ERROR_DEPTH;
- goto end;
- }
-
- nodes[depth].index = 0;
- nodes[depth].type = c == '{' ? TYPE_OBJECT : TYPE_ARRAY;
-
- cb(nodes, depth + 1, "");
- v = 0;
-
- depth++;
- nodes[depth].index = 0;
- nodes[depth].type = TYPE_PRIMITIVE;
- if (capacity(&(nodes[depth].name), &(nodes[depth].namesiz), v, 1) == -1)
- goto end;
- nodes[depth].name[0] = '\0';
- break;
- case ']':
- case '}':
- case ',':
- if (v || nodes[depth].type == TYPE_STRING) {
- if (capacity(&value, &vz, v, 1) == -1)
- goto end;
- value[v] = '\0';
- cb(nodes, depth + 1, value);
- v = 0;
- }
- if (!depth ||
- (c == ']' && nodes[depth - 1].type != TYPE_ARRAY) ||
- (c == '}' && nodes[depth - 1].type != TYPE_OBJECT)) {
- *errstr = JSON_ERROR_BALANCE;
- goto end;
- }
-
- if (c == ']' || c == '}') {
- nodes[--depth].index++;
- } else if (c == ',') {
- nodes[depth - 1].index++;
- nodes[depth].type = TYPE_PRIMITIVE;
- }
- break;
- default:
- if (capacity(&value, &vz, v, 1) == -1)
- goto end;
- value[v++] = c;
- }
- }
- if (depth) {
- *errstr = JSON_ERROR_BALANCE;
- goto end;
- }
- if (v || nodes[depth].type == TYPE_STRING) {
- if (capacity(&value, &vz, v, 1) == -1)
- goto end;
- value[v] = '\0';
- cb(nodes, depth + 1, value);
- }
-
- ret = 0; /* success */
- *errstr = NULL;
-end:
- for (depth = 0; depth < sizeof(nodes) / sizeof(nodes[0]); depth++)
- free(nodes[depth].name);
- free(value);
-
- return ret;
-}
-
void
printvalue(const char *s)
{
@@ -356,8 +67,6 @@ processnode(struct json_node *nodes, size_t depth, const char *value)
int
main(int argc, char *argv[])
{
- const char *errstr;
-
if (pledge("stdio", NULL) == -1) {
fprintf(stderr, "pledge stdio: %s\n", strerror(errno));
return 1;
@@ -366,8 +75,12 @@ main(int argc, char *argv[])
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n')
showindices = 1;
- if (parsejson(processnode, &errstr) == -1) {
- fprintf(stderr, "error: %s\n", errstr);
+ switch (parsejson(processnode)) {
+ case JSON_ERROR_MEM:
+ fputs("error: cannot allocate enough memory\n", stderr);
+ return 2;
+ case JSON_ERROR_INVALID:
+ fputs("error: invalid JSON\n", stderr);
return 1;
}