refactor to make it easier to use as a "library". handle errors more gracefully… | |
git clone git://git.codemadness.org/json2tsv | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit f831a4b76c10d7efcb72effd18332f3deac7a4fa | |
parent 111c9cc55cbabc01fa14e8b7694be0ef940d1702 | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Sat, 5 Oct 2019 22:30:49 +0200 | |
refactor to make it easier to use as a "library". handle errors more gracefully | |
Diffstat: | |
M json2tsv.c | 114 ++++++++++++++++++-----------… | |
1 file changed, 67 insertions(+), 47 deletions(-) | |
--- | |
diff --git a/json2tsv.c b/json2tsv.c | |
@@ -1,4 +1,5 @@ | |
#include <ctype.h> | |
+#include <errno.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
@@ -31,13 +32,6 @@ struct json_node { | |
static int showindices = 0; /* -n flag: show indices count for arrays */ | |
-void | |
-fatal(const char *s) | |
-{ | |
- fputs(s, stderr); | |
- exit(1); | |
-} | |
- | |
int | |
codepointtoutf8(long r, char *s) | |
{ | |
@@ -80,35 +74,42 @@ hexdigit(int c) | |
return 0; | |
} | |
-void | |
+int | |
capacity(char **value, size_t *sz, size_t cur, size_t inc) | |
{ | |
size_t need, newsiz; | |
+ char *newp; | |
/* check addition overflow */ | |
need = cur + inc; | |
- if (cur > SIZE_MAX - need || *sz > SIZE_MAX - 16384) | |
- fatal("overflow\n"); | |
+ if (cur > SIZE_MAX - need || *sz > SIZE_MAX - 16384) { | |
+ errno = EOVERFLOW; | |
+ return -1; | |
+ } | |
if (*sz == 0 || need > *sz) { | |
for (newsiz = *sz; newsiz < need; newsiz += 16384) | |
; | |
- if (!(*value = realloc(*value, newsiz))) | |
- fatal("realloc\n"); | |
+ if (!(newp = realloc(*value, newsiz))) | |
+ return -1; /* up to caller to free memory */ | |
+ *value = newp; | |
*sz = newsiz; | |
} | |
+ return 0; | |
} | |
-void | |
-parsejson(void (*cb)(struct json_node *, size_t, const char *)) | |
+int | |
+parsejson(void (*cb)(struct json_node *, size_t, const char *), const char **e… | |
{ | |
struct json_node nodes[JSON_MAX_NODE_DEPTH] = { 0 }; | |
- long cp; | |
size_t depth = 0, v = 0, vz = 0; | |
- int c, escape; | |
+ long cp; | |
+ int c, i, escape, ret = -1; | |
char *value = NULL; | |
- capacity(&(nodes[0].name), &(nodes[0].namesiz), 0, 1); | |
+ *errstr = "cannot allocate enough memory"; | |
+ if (capacity(&(nodes[0].name), &(nodes[0].namesiz), 0, 1) == -1) | |
+ goto end; | |
nodes[0].name[0] = '\0'; | |
while ((c = GETNEXT()) != EOF) { | |
@@ -120,10 +121,13 @@ parsejson(void (*cb)(struct json_node *, size_t, const ch… | |
case ':': | |
nodes[depth].type = TYPE_PRIMITIVE; | |
if (v) { | |
- if (!depth || nodes[depth - 1].type != TYPE_OB… | |
- fatal("object member, but not in an ob… | |
+ if (!depth || nodes[depth - 1].type != TYPE_OB… | |
+ *errstr = "object member, but not in a… | |
+ goto end; | |
+ } | |
value[v] = '\0'; | |
- capacity(&(nodes[depth].name), &(nodes[depth].… | |
+ if (capacity(&(nodes[depth].name), &(nodes[dep… | |
+ goto end; | |
memcpy(nodes[depth].name, value, v); | |
nodes[depth].name[v] = '\0'; | |
v = 0; | |
@@ -149,43 +153,43 @@ parsejson(void (*cb)(struct json_node *, size_t, const ch… | |
case 'r': c = '\r'; break; | |
case 't': c = '\t'; break; | |
case 'u': /* hex hex hex hex */ | |
- if ((c = GETNEXT()) == EOF || … | |
- fatal("invalid codepoi… | |
- cp = (hexdigit(c) << 12); | |
- if ((c = GETNEXT()) == EOF || … | |
- fatal("invalid codepoi… | |
- cp |= (hexdigit(c) << 8); | |
- if ((c = GETNEXT()) == EOF || … | |
- fatal("invalid codepoi… | |
- cp |= (hexdigit(c) << 4); | |
- if ((c = GETNEXT()) == EOF || … | |
- fatal("invalid codepoi… | |
- cp |= (hexdigit(c)); | |
- | |
- capacity(&value, &vz, v, 5); | |
+ for (i = 12, cp = 0; i >= 0; i… | |
+ if ((c = GETNEXT()) ==… | |
+ *errstr = "inv… | |
+ goto end; | |
+ } | |
+ cp |= (hexdigit(c) << … | |
+ } | |
+ if (capacity(&value, &vz, v, 5… | |
+ goto end; | |
v += codepointtoutf8(cp, &valu… | |
continue; | |
default: | |
continue; /* ignore unknown es… | |
} | |
- capacity(&value, &vz, v, 2); | |
+ if (capacity(&value, &vz, v, 2) == -1) | |
+ goto end; | |
value[v++] = c; | |
} else if (c == '\\') { | |
escape = 1; | |
} else if (c == '"') { | |
break; | |
} else { | |
- capacity(&value, &vz, v, 2); | |
+ if (capacity(&value, &vz, v, 2) == -1) | |
+ goto end; | |
value[v++] = c; | |
} | |
} | |
- capacity(&value, &vz, v, 1); | |
+ if (capacity(&value, &vz, v, 1) == -1) | |
+ goto end; | |
value[v] = '\0'; | |
break; | |
case '[': | |
case '{': | |
- if (depth + 1 >= JSON_MAX_NODE_DEPTH) | |
- fatal("max depth reached\n"); | |
+ if (depth + 1 >= JSON_MAX_NODE_DEPTH) { | |
+ *errstr = "max node depth reached"; | |
+ goto end; | |
+ } | |
nodes[depth].index = 0; | |
nodes[depth].type = c == '{' ? TYPE_OBJECT : TYPE_ARRA… | |
@@ -195,7 +199,8 @@ parsejson(void (*cb)(struct json_node *, size_t, const char… | |
depth++; | |
nodes[depth].index = 0; | |
- capacity(&(nodes[depth].name), &(nodes[depth].namesiz)… | |
+ if (capacity(&(nodes[depth].name), &(nodes[depth].name… | |
+ goto end; | |
nodes[depth].name[0] = '\0'; | |
nodes[depth].type = TYPE_PRIMITIVE; | |
break; | |
@@ -211,8 +216,10 @@ parsejson(void (*cb)(struct json_node *, size_t, const cha… | |
} | |
if (!depth || | |
(nodes[depth - 1].type == TYPE_OBJECT && c == ']')… | |
- (nodes[depth - 1].type == TYPE_ARRAY && c == '}')) | |
- fatal("unbalanced nodes\n"); | |
+ (nodes[depth - 1].type == TYPE_ARRAY && c == '}'))… | |
+ *errstr = "unbalanced nodes"; | |
+ goto end; | |
+ } | |
if (c == ']' || c == '}') { | |
nodes[--depth].index++; | |
@@ -222,14 +229,20 @@ parsejson(void (*cb)(struct json_node *, size_t, const ch… | |
} | |
break; | |
default: | |
- capacity(&value, &vz, v, 2); | |
+ if (capacity(&value, &vz, v, 2) == -1) | |
+ goto end; | |
value[v++] = c; | |
} | |
} | |
+ 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 | |
@@ -273,7 +286,7 @@ processnode(struct json_node *nodes, size_t depth, const ch… | |
} | |
switch (nodes[depth - 1].type) { | |
- case TYPE_UNKNOWN: fatal("unknown type\n"); return; | |
+ case TYPE_UNKNOWN: return; | |
case TYPE_ARRAY: fputs("\ta\t\n", stdout); return; | |
case TYPE_OBJECT: fputs("\to\t\n", stdout); return; | |
case TYPE_PRIMITIVE: fputs("\tp\t", stdout); break; | |
@@ -286,13 +299,20 @@ processnode(struct json_node *nodes, size_t depth, const … | |
int | |
main(int argc, char *argv[]) | |
{ | |
- if (pledge("stdio", NULL) == -1) | |
- fatal("pledge stdio\n"); | |
+ const char *errstr; | |
+ | |
+ if (pledge("stdio", NULL) == -1) { | |
+ fprintf(stderr, "pledge stdio: %s\n", strerror(errno)); | |
+ return 1; | |
+ } | |
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n') | |
showindices = 1; | |
- parsejson(processnode); | |
+ if (parsejson(processnode, &errstr) == -1) { | |
+ fprintf(stderr, "error: %s\n", errstr); | |
+ return 1; | |
+ } | |
return 0; | |
} |