summaryrefslogtreecommitdiff
path: root/json.c
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2009-07-10 15:12:01 +0000
committerEric S. Raymond <esr@thyrsus.com>2009-07-10 15:12:01 +0000
commitcb49ed8c5ff2e3c6bab8a0c6d51994f7dbe57dcc (patch)
treed817e403c8d2e5c738bd776c582d06a28d52d3a5 /json.c
parent062b0673abaccbb9e50b28baa9fcb65b21290d89 (diff)
downloadgpsd-cb49ed8c5ff2e3c6bab8a0c6d51994f7dbe57dcc.tar.gz
Scratch JSON parser. No unit test yet, that gets built next.
Diffstat (limited to 'json.c')
-rw-r--r--json.c131
1 files changed, 131 insertions, 0 deletions
diff --git a/json.c b/json.c
new file mode 100644
index 00000000..97c5db5f
--- /dev/null
+++ b/json.c
@@ -0,0 +1,131 @@
+/* json.c - parse JSON into fixed-extent data structures */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "json.h"
+
+int parse_json(char *cp, struct json_attr_t *attrs)
+{
+ enum {init, await_attr, in_attr, await_value,
+ in_val_string, in_val_token, post_val} state = 0;
+ char attrbuf[JSON_ATTR_MAX+1], *pattr = NULL;
+ char valbuf[JSON_VAL_MAX+1], *pval = NULL;
+ struct json_attr_t *cursor;
+
+ /* stuff fields with defaults in case they're omitted in the JSON input */
+ for (cursor = attrs; cursor->attribute != NULL; cursor++)
+ switch(cursor->type)
+ {
+ case integer:
+ *(cursor->addr.integer) = cursor->dflt.integer;
+ break;
+ case real:
+ *(cursor->addr.real) = cursor->dflt.real;
+ break;
+ case string:
+ *(cursor->addr.string)[0] = '\0';
+ break;
+ case boolean:
+ *(cursor->addr.boolean) = cursor->dflt.boolean;
+ break;
+ }
+
+ /* parse input JSON */
+ for (; *cp; cp++) {
+ switch (state)
+ {
+ case init:
+ if (isspace(*cp))
+ continue;
+ else if (*cp == '{')
+ state = await_attr;
+ else
+ return -1; /* non-whitespace when expecting object start */
+ break;
+ case await_attr:
+ if (isspace(*cp))
+ continue;
+ else if (*cp == '"') {
+ state = in_attr;
+ pattr = attrbuf;
+ } else if (*cp == '}')
+ break;
+ else
+ return -2; /* non-whitespace while expecting attribute */
+ break;
+ case in_attr:
+ if (*cp == '"') {
+ *pattr++ = '\0';
+ for (cursor = attrs; cursor->attribute != NULL; cursor++)
+ if (strcmp(cursor->attribute, attrbuf)==0)
+ break;
+ if (cursor->attribute == NULL)
+ return -3; /* unknown attribute name */
+ state = await_value;
+ pval = valbuf;
+ } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1)
+ return -4; /* attribute name too long */
+ else
+ *pattr = *cp;
+ break;
+ case await_value:
+ if (isspace(*cp) || *cp == ':')
+ continue;
+ else if (*cp == '"') {
+ state = in_val_string;
+ pval = valbuf;
+ } else {
+ state = in_val_token;
+ pval = valbuf;
+ }
+ break;
+ case in_val_string:
+ if (*cp == '"') {
+ state = post_val;
+ } else if (pval > valbuf + JSON_VAL_MAX - 1)
+ return -5; /* value too long */
+ else
+ *pval++ = *cp;
+ break;
+ case in_val_token:
+ if (isspace(*cp) || *cp == ',' || *cp == '}') {
+ state = post_val;
+ if (*cp == '}')
+ --cp;
+ } else if (pval > valbuf + JSON_VAL_MAX - 1)
+ return -5; /* value too long */
+ else
+ *pval++ = *cp;
+ break;
+ case post_val:
+ switch(cursor->type)
+ {
+ case integer:
+ *(cursor->addr.integer) = atoi(valbuf);
+ break;
+ case real:
+ *(cursor->addr.real) = atof(valbuf);
+ break;
+ case string:
+ (void)strcpy(*(cursor->addr.string), valbuf);
+ break;
+ case boolean:
+ *(cursor->addr.boolean) = (bool)!strcmp(valbuf, "true");
+ break;
+ }
+ if (isspace(*cp))
+ continue;
+ else if (*cp == ',')
+ state = await_attr;
+ else if (*cp == '}')
+ return 0;
+ else
+ return -6; /* garbage while expecting comma or } */
+ break;
+ }
+ }
+
+ return 0;
+}