refactor to make it easier to use as a "library". handle errors more gracefully - 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 f831a4b76c10d7efcb72effd18332f3deac7a4fa
(DIR) parent 111c9cc55cbabc01fa14e8b7694be0ef940d1702
(HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
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(-)
---
(DIR) 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 **errstr)
{
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 char *))
case ':':
nodes[depth].type = TYPE_PRIMITIVE;
if (v) {
- if (!depth || nodes[depth - 1].type != TYPE_OBJECT)
- fatal("object member, but not in an object\n");
+ if (!depth || nodes[depth - 1].type != TYPE_OBJECT) {
+ *errstr = "object member, but not in an object";
+ goto end;
+ }
value[v] = '\0';
- capacity(&(nodes[depth].name), &(nodes[depth].namesiz), v, 1);
+ 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;
@@ -149,43 +153,43 @@ parsejson(void (*cb)(struct json_node *, size_t, const char *))
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'u': /* hex hex hex hex */
- if ((c = GETNEXT()) == EOF || !isxdigit(c))
- fatal("invalid codepoint\n");
- cp = (hexdigit(c) << 12);
- if ((c = GETNEXT()) == EOF || !isxdigit(c))
- fatal("invalid codepoint\n");
- cp |= (hexdigit(c) << 8);
- if ((c = GETNEXT()) == EOF || !isxdigit(c))
- fatal("invalid codepoint\n");
- cp |= (hexdigit(c) << 4);
- if ((c = GETNEXT()) == EOF || !isxdigit(c))
- fatal("invalid codepoint\n");
- cp |= (hexdigit(c));
-
- capacity(&value, &vz, v, 5);
+ for (i = 12, cp = 0; i >= 0; i -= 4) {
+ if ((c = GETNEXT()) == EOF || !isxdigit(c)) {
+ *errstr = "invalid codepoint";
+ goto end;
+ }
+ cp |= (hexdigit(c) << i);
+ }
+ if (capacity(&value, &vz, v, 5) == -1)
+ goto end;
v += codepointtoutf8(cp, &value[v]);
continue;
default:
continue; /* ignore unknown escape char */
}
- 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_ARRAY;
@@ -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), v, 1);
+ if (capacity(&(nodes[depth].name), &(nodes[depth].namesiz), v, 1) == -1)
+ 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 char *))
}
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 char *))
}
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 char *value)
}
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 char *value)
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;
}