--- /dev/null
+/* ZZJSON - Copyright (C) 2008 by Ivo van Poorten
+ * License: GNU Lesser General Public License version 2.1
+ */
+#ifndef ZZJSON_H
+#define ZZJSON_H
+
+#include <stdlib.h>
+
+/* Version: */
+
+#define ZZJSON_VERSION_MAJOR 1
+#define ZZJSON_VERSION_MINOR 1
+#define ZZJSON_VERSION_MICRO 0
+#define ZZJSON_VERSION_INT ( 1<<16 | 1<<8 | 0 )
+#define ZZJSON_IDENT "zzjson 1.1.0"
+
+/* Defines: */
+
+#define ZZJSON_ALLOW_EXTRA_COMMA 1
+#define ZZJSON_ALLOW_ILLEGAL_ESCAPE 2
+#define ZZJSON_ALLOW_CONTROL_CHARS 4
+#define ZZJSON_ALLOW_GARBAGE_AT_END 8
+#define ZZJSON_ALLOW_COMMENTS 16
+
+#define ZZJSON_VERY_LOOSE (-1)
+#define ZZJSON_VERY_STRICT 0
+
+/* Types: */
+
+/* needed by: pa = parser, pr = printer, f = free, q = query, c = create */
+typedef struct ZZJSON_CONFIG {
+ int strictness; // pa
+ void *ihandle; // pa
+ int (*getchar)(void *ihandle); // pa
+ int (*ungetchar)(int c, void *ihandle); // pa
+ void *(*malloc)(size_t size); // pa c
+ void *(*calloc)(size_t nmemb, size_t size); // pa c
+ void (*free)(void *ptr); // pa f c
+ void *(*realloc)(void *ptr, size_t size); // pa
+ void *ehandle; // pa pr c
+ void (*error)(void *ehandle, const char *format, ...); // pa pr c
+ void *ohandle; // pr
+ int (*print)(void *ohandle, const char *format, ...); // pr
+ int (*putchar)(int c, void *handle); // pr
+} ZZJSON_CONFIG;
+
+typedef enum ZZJSON_TYPE {
+ ZZJSON_NONE = 0,
+ ZZJSON_OBJECT,
+ ZZJSON_ARRAY,
+ ZZJSON_STRING,
+ ZZJSON_NUMBER_NEGINT,
+ ZZJSON_NUMBER_POSINT,
+ ZZJSON_NUMBER_DOUBLE,
+ ZZJSON_NULL,
+ ZZJSON_TRUE,
+ ZZJSON_FALSE
+} ZZJSON_TYPE;
+
+typedef struct ZZJSON {
+ ZZJSON_TYPE type;
+ union {
+ struct {
+ char *label;
+ struct ZZJSON *val;
+ } object;
+ struct {
+ struct ZZJSON *val;
+ } array;
+ struct {
+ char *string;
+ } string;
+ struct {
+ union {
+ unsigned long long ival;
+ double dval;
+ } val;
+ } number;
+ } value;
+ struct ZZJSON *next;
+} ZZJSON;
+
+/* Functions: */
+
+ZZJSON *zzjson_parse(ZZJSON_CONFIG *config);
+void zzjson_free(ZZJSON_CONFIG *config, ZZJSON *zzjson);
+int zzjson_print(ZZJSON_CONFIG *config, ZZJSON *zzjson);
+
+ZZJSON *zzjson_object_find_label(ZZJSON *zzjson, char *label);
+ZZJSON *zzjson_object_find_labels(ZZJSON *zzjson, ...); // end with , NULL
+unsigned int zzjson_object_count(ZZJSON *zzjson);
+unsigned int zzjson_array_count(ZZJSON *zzjson);
+
+ZZJSON *zzjson_create_true(ZZJSON_CONFIG *config);
+ZZJSON *zzjson_create_false(ZZJSON_CONFIG *config);
+ZZJSON *zzjson_create_null(ZZJSON_CONFIG *config);
+ZZJSON *zzjson_create_number_d(ZZJSON_CONFIG *config, double d);
+ZZJSON *zzjson_create_number_i(ZZJSON_CONFIG *config, long long i);
+ZZJSON *zzjson_create_string(ZZJSON_CONFIG *config, char *s);
+
+/* list of ZZJSON *'s and end with , NULL */
+ZZJSON *zzjson_create_array(ZZJSON_CONFIG *config, ...);
+
+/* list of char*,ZZJSON* pairs, end with , NULL */
+ZZJSON *zzjson_create_object(ZZJSON_CONFIG *config, ...);
+
+ZZJSON *zzjson_array_prepend(ZZJSON_CONFIG *config, ZZJSON *array,
+ ZZJSON *val);
+ZZJSON *zzjson_array_append (ZZJSON_CONFIG *config, ZZJSON *array,
+ ZZJSON *val);
+
+ZZJSON *zzjson_object_prepend(ZZJSON_CONFIG *config, ZZJSON *object,
+ char *label, ZZJSON *val);
+ZZJSON *zzjson_object_append (ZZJSON_CONFIG *config, ZZJSON *object,
+ char *label, ZZJSON *val);
+#endif
topdir = ../..
include ../lib/MCONFIG
-REQFLAGS += -I../gplinclude
+REQFLAGS += -I../gplinclude -I../gplinclude/zzjson
-GPLDIRS := . disk dmi vpd acpi
+GPLDIRS := . disk dmi vpd acpi zzjson
LIBOBJS := $(foreach dir,$(GPLDIRS),$(patsubst %.c,%.o,$(wildcard $(dir)/*.c)))
BINDIR = /usr/bin
--- /dev/null
+/* JSON Create ZZJSON structures
+ * ZZJSON - Copyright (C) 2008 by Ivo van Poorten
+ * License: GNU Lesser General Public License version 2.1
+ */
+
+#include "zzjson.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef CONFIG_NO_ERROR_MESSAGES
+#define ERROR(x...)
+#else
+#define ERROR(x...) config->error(config->ehandle, ##x)
+#endif
+#define MEMERROR() ERROR("out of memory")
+
+static ZZJSON *zzjson_create_templ(ZZJSON_CONFIG *config, ZZJSON_TYPE type) {
+ ZZJSON *zzjson = config->calloc(1, sizeof(ZZJSON));
+ if (!zzjson) MEMERROR();
+ else zzjson->type = type;
+ return zzjson;
+}
+
+ZZJSON *zzjson_create_true(ZZJSON_CONFIG *config) {
+ return zzjson_create_templ(config, ZZJSON_TRUE);
+}
+
+ZZJSON *zzjson_create_false(ZZJSON_CONFIG *config) {
+ return zzjson_create_templ(config, ZZJSON_FALSE);
+}
+
+ZZJSON *zzjson_create_null(ZZJSON_CONFIG *config) {
+ return zzjson_create_templ(config, ZZJSON_NULL);
+}
+
+ZZJSON *zzjson_create_number_d(ZZJSON_CONFIG *config, double d) {
+ ZZJSON *zzjson = zzjson_create_templ(config, ZZJSON_NUMBER_DOUBLE);
+ if (zzjson)
+ zzjson->value.number.val.dval = d;
+ return zzjson;
+}
+
+ZZJSON *zzjson_create_number_i(ZZJSON_CONFIG *config, long long i) {
+ ZZJSON *zzjson = zzjson_create_templ(config, ZZJSON_NUMBER_NEGINT);
+ if (zzjson) {
+ zzjson->type = i<0LL ? ZZJSON_NUMBER_NEGINT : ZZJSON_NUMBER_POSINT;
+ zzjson->value.number.val.ival = llabs(i);
+ }
+ return zzjson;
+}
+
+/* sdup mimics strdup, but avoids having another function pointer in config */
+static char *sdup(ZZJSON_CONFIG *config, char *s) {
+ size_t slen = strlen(s)+1;
+ char *scopy = config->malloc(slen);
+
+ if (!scopy) MEMERROR();
+ else memcpy(scopy, s, slen);
+ return scopy;
+}
+
+ZZJSON *zzjson_create_string(ZZJSON_CONFIG *config, char *s) {
+ ZZJSON *zzjson = NULL;
+ char *scopy;
+
+ if (!(scopy = sdup(config,s))) return zzjson;
+
+ if ((zzjson = zzjson_create_templ(config, ZZJSON_STRING)))
+ zzjson->value.string.string = scopy;
+ else
+ config->free(scopy);
+
+ return zzjson;
+}
+
+ZZJSON *zzjson_create_array(ZZJSON_CONFIG *config, ...) {
+ ZZJSON *zzjson, *retval, *val;
+ va_list ap;
+
+ if (!(zzjson = zzjson_create_templ(config, ZZJSON_ARRAY))) return zzjson;
+ retval = zzjson;
+
+ va_start(ap, config);
+ val = va_arg(ap, ZZJSON *);
+ while (val) {
+ zzjson->value.array.val = val;
+ val = va_arg(ap, ZZJSON *);
+
+ if (val) {
+ ZZJSON *next = zzjson_create_templ(config, ZZJSON_ARRAY);
+ if (!next) {
+ while (retval) {
+ next = retval->next;
+ config->free(retval);
+ retval = next;
+ }
+ break;
+ }
+ zzjson->next = next;
+ zzjson = next;
+ }
+ }
+ va_end(ap);
+ return retval;
+}
+
+ZZJSON *zzjson_create_object(ZZJSON_CONFIG *config, ...) {
+ ZZJSON *zzjson, *retval, *val;
+ char *label, *labelcopy;
+ va_list ap;
+
+ if (!(zzjson = zzjson_create_templ(config, ZZJSON_OBJECT))) return zzjson;
+ retval = zzjson;
+
+ va_start(ap, config);
+ label = va_arg(ap, char *);
+ while (label) {
+ val = va_arg(ap, ZZJSON *);
+ labelcopy = sdup(config, label);
+
+ if (!labelcopy) {
+ zzjson_free(config, retval);
+ retval = NULL;
+ break;
+ }
+
+ zzjson->value.object.label = labelcopy;
+ zzjson->value.object.val = val;
+
+ label = va_arg(ap, char *);
+
+ if (label) {
+ ZZJSON *next = zzjson_create_templ(config, ZZJSON_OBJECT);
+ if (!next) {
+ while (retval) {
+ next = retval->next;
+ config->free(retval->value.object.label);
+ config->free(retval);
+ retval = next;
+ }
+ break;
+ }
+ zzjson->next = next;
+ zzjson = next;
+ }
+ }
+ va_end(ap);
+ return retval;
+}
+
+ZZJSON *zzjson_array_prepend(ZZJSON_CONFIG *config, ZZJSON *array,
+ ZZJSON *val) {
+ ZZJSON *zzjson;
+
+ if (!array->value.array.val) { /* empty array */
+ array->value.array.val = val;
+ return array;
+ }
+
+ zzjson = zzjson_create_templ(config, ZZJSON_ARRAY);
+ if (zzjson) {
+ zzjson->value.array.val = val;
+ zzjson->next = array;
+ }
+ return zzjson;
+}
+
+ZZJSON *zzjson_array_append(ZZJSON_CONFIG *config, ZZJSON *array,
+ ZZJSON *val) {
+ ZZJSON *retval = array, *zzjson;
+
+ if (!array->value.array.val) { /* empty array */
+ array->value.array.val = val;
+ return array;
+ }
+
+ zzjson = zzjson_create_templ(config, ZZJSON_ARRAY);
+ if (!zzjson) return NULL;
+
+ while (array->next) array = array->next;
+
+ zzjson->value.array.val = val;
+ array->next = zzjson;
+
+ return retval;
+}
+
+ZZJSON *zzjson_object_prepend(ZZJSON_CONFIG *config, ZZJSON *object,
+ char *label, ZZJSON *val) {
+ ZZJSON *zzjson = NULL;
+ char *labelcopy = sdup(config, label);
+
+ if (!labelcopy) return zzjson;
+
+ if (!object->value.object.label) { /* empty object */
+ object->value.object.label = labelcopy;
+ object->value.object.val = val;
+ return object;
+ }
+
+ zzjson = zzjson_create_templ(config, ZZJSON_OBJECT);
+ if (zzjson) {
+ zzjson->value.object.label = labelcopy;
+ zzjson->value.object.val = val;
+ zzjson->next = object;
+ } else {
+ config->free(labelcopy);
+ }
+ return zzjson;
+}
+
+ZZJSON *zzjson_object_append(ZZJSON_CONFIG *config, ZZJSON *object,
+ char *label, ZZJSON *val) {
+ ZZJSON *retval = object, *zzjson = NULL;
+ char *labelcopy = sdup(config, label);
+
+ if (!labelcopy) return zzjson;
+
+ if (!object->value.object.label) { /* empty object */
+ object->value.object.label = labelcopy;
+ object->value.object.val = val;
+ return object;
+ }
+
+ zzjson = zzjson_create_templ(config, ZZJSON_OBJECT);
+ if (!zzjson) {
+ config->free(labelcopy);
+ return NULL;
+ }
+
+ while (object->next) object = object->next;
+
+ zzjson->value.object.label = labelcopy;
+ zzjson->value.object.val = val;
+ object->next = zzjson;
+
+ return retval;
+}
+
--- /dev/null
+/* JSON free
+ * ZZJSON - Copyright (C) 2008 by Ivo van Poorten
+ * License: GNU Lesser General Public License version 2.1
+ */
+
+#include "zzjson.h"
+
+void zzjson_free(ZZJSON_CONFIG *config, ZZJSON *zzjson) {
+ while (zzjson) {
+ ZZJSON *next;
+ switch(zzjson->type) {
+ case ZZJSON_OBJECT:
+ config->free(zzjson->value.object.label);
+ zzjson_free(config, zzjson->value.object.val);
+ break;
+ case ZZJSON_ARRAY:
+ zzjson_free(config, zzjson->value.array.val);
+ break;
+ case ZZJSON_STRING:
+ config->free(zzjson->value.string.string);
+ break;
+ default:
+ break;
+ }
+ next = zzjson->next;
+ config->free(zzjson);
+ zzjson = next;
+ }
+}
--- /dev/null
+/* JSON Parser
+ * ZZJSON - Copyright (C) 2008-2009 by Ivo van Poorten
+ * License: GNU Lesser General Public License version 2.1
+ */
+
+#include "zzjson.h"
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+#define GETC() config->getchar(config->ihandle)
+#define UNGETC(c) config->ungetchar(c, config->ihandle)
+#define SKIPWS() skipws(config)
+#ifdef CONFIG_NO_ERROR_MESSAGES
+#define ERROR(x...)
+#else
+#define ERROR(x...) config->error(config->ehandle, ##x)
+#endif
+#define MEMERROR() ERROR("out of memory")
+
+#define ALLOW_EXTRA_COMMA (config->strictness & ZZJSON_ALLOW_EXTRA_COMMA)
+#define ALLOW_ILLEGAL_ESCAPE (config->strictness & ZZJSON_ALLOW_ILLEGAL_ESCAPE)
+#define ALLOW_CONTROL_CHARS (config->strictness & ZZJSON_ALLOW_CONTROL_CHARS)
+#define ALLOW_GARBAGE_AT_END (config->strictness & ZZJSON_ALLOW_GARBAGE_AT_END)
+#define ALLOW_COMMENTS (config->strictness & ZZJSON_ALLOW_COMMENTS)
+
+static ZZJSON *parse_array(ZZJSON_CONFIG *config);
+static ZZJSON *parse_object(ZZJSON_CONFIG *config);
+
+static void skipws(ZZJSON_CONFIG *config) {
+ int d, c = GETC();
+morews:
+ while (isspace(c)) c = GETC();
+ if (!ALLOW_COMMENTS) goto endws;
+ if (c != '/') goto endws;
+ d = GETC();
+ if (d != '*') goto endws; /* pushing back c will generate a parse error */
+ c = GETC();
+morecomments:
+ while (c != '*') {
+ if (c == EOF) goto endws;
+ c = GETC();
+ }
+ c = GETC();
+ if (c != '/') goto morecomments;
+ c = GETC();
+ if (isspace(c) || c == '/') goto morews;
+endws:
+ UNGETC(c);
+}
+
+static char *parse_string(ZZJSON_CONFIG *config) {
+ unsigned int len = 16, pos = 0;
+ int c;
+ char *str = NULL;
+
+ SKIPWS();
+ c = GETC();
+ if (c != '"') {
+ ERROR("string: expected \" at the start");
+ return NULL;
+ }
+
+ str = config->malloc(len);
+ if (!str) {
+ MEMERROR();
+ return NULL;
+ }
+ c = GETC();
+ while (c > 0 && c != '"') {
+ if (!ALLOW_CONTROL_CHARS && c >= 0 && c <= 31) {
+ ERROR("string: control characters not allowed");
+ goto errout;
+ }
+ if (c == '\\') {
+ c = GETC();
+ switch (c) {
+ 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': {
+ UNGETC(c); /* ignore \uHHHH, copy verbatim */
+ c = '\\';
+ break;
+ }
+ case '\\': case '/': case '"':
+ break;
+ default:
+ if (!ALLOW_ILLEGAL_ESCAPE) {
+ ERROR("string: illegal escape character");
+ goto errout;
+ }
+ }
+ }
+ str[pos++] = c;
+ if (pos == len-1) {
+ void *tmp = str;
+ len *= 2;
+ str = config->realloc(str, len);
+ if (!str) {
+ MEMERROR();
+ str = tmp;
+ goto errout;
+ }
+ }
+ c = GETC();
+ }
+ if (c != '"') {
+ ERROR("string: expected \" at the end");
+ goto errout;
+ }
+ str[pos] = 0;
+ return str;
+
+errout:
+ config->free(str);
+ return NULL;
+}
+
+static ZZJSON *parse_string2(ZZJSON_CONFIG *config) {
+ ZZJSON *zzjson = NULL;
+ char *str;
+
+ str = parse_string(config);
+ if (str) {
+ zzjson = config->calloc(1, sizeof(ZZJSON));
+ if (!zzjson) {
+ MEMERROR();
+ config->free(str);
+ return NULL;
+ }
+ zzjson->type = ZZJSON_STRING;
+ zzjson->value.string.string = str;
+ }
+ return zzjson;
+}
+
+static ZZJSON *parse_number(ZZJSON_CONFIG *config) {
+ ZZJSON *zzjson;
+ unsigned long long ival = 0, expo = 0;
+ double dval = 0.0, frac = 0.0, fracshft = 10.0;
+ int c, dbl = 0, sign = 1, signexpo = 1;
+
+ SKIPWS();
+ c = GETC();
+ if (c == '-') {
+ sign = -1;
+ c = GETC();
+ }
+ if (c == '0') {
+ c = GETC();
+ goto skip;
+ }
+
+ if (!isdigit(c)) {
+ ERROR("number: digit expected");
+ return NULL;
+ }
+
+ while (isdigit(c)) {
+ ival *= 10;
+ ival += c - '0';
+ c = GETC();
+ }
+
+skip:
+ if (c != '.') goto skipfrac;
+
+ dbl = 1;
+
+ c = GETC();
+ if (!isdigit(c)) {
+ ERROR("number: digit expected");
+ return NULL;
+ }
+
+ while (isdigit(c)) {
+ frac += (double)(c - '0') / fracshft;
+ fracshft *= 10.0;
+ c = GETC();
+ }
+
+skipfrac:
+ if (c != 'e' && c != 'E') goto skipexpo;
+
+ dbl = 1;
+
+ c = GETC();
+ if (c == '+')
+ c = GETC();
+ else if (c == '-') {
+ signexpo = -1;
+ c = GETC();
+ }
+
+ if (!isdigit(c)) {
+ ERROR("number: digit expected");
+ return NULL;
+ }
+
+ while (isdigit(c)) {
+ expo *= 10;
+ expo += c - '0';
+ c = GETC();
+ }
+
+skipexpo:
+ UNGETC(c);
+
+ if (dbl) {
+ dval = sign * (long long) ival;
+ dval += sign * frac;
+ dval *= pow(10.0, (double) signexpo * expo);
+ }
+
+ zzjson = config->calloc(1, sizeof(ZZJSON));
+ if (!zzjson) {
+ MEMERROR();
+ return NULL;
+ }
+ if (dbl) {
+ zzjson->type = ZZJSON_NUMBER_DOUBLE;
+ zzjson->value.number.val.dval = dval;
+ } else {
+ zzjson->type = sign < 0 ? ZZJSON_NUMBER_NEGINT : ZZJSON_NUMBER_POSINT;
+ zzjson->value.number.val.ival = ival;
+ }
+
+ return zzjson;
+}
+
+static ZZJSON *parse_literal(ZZJSON_CONFIG *config, char *s, ZZJSON_TYPE t) {
+ char b[strlen(s)+1];
+ unsigned int i;
+
+ for (i=0; i<strlen(s); i++) b[i] = GETC();
+ b[i] = 0;
+
+ if (!strcmp(b,s)) {
+ ZZJSON *zzjson;
+ zzjson = config->calloc(1, sizeof(ZZJSON));
+ if (!zzjson) {
+ MEMERROR();
+ return NULL;
+ }
+ zzjson->type = t;
+ return zzjson;
+ }
+ ERROR("literal: expected %s", s);
+ return NULL;
+}
+
+static ZZJSON *parse_true(ZZJSON_CONFIG *config) {
+ return parse_literal(config, "true", ZZJSON_TRUE);
+}
+
+static ZZJSON *parse_false(ZZJSON_CONFIG *config) {
+ return parse_literal(config, "false", ZZJSON_FALSE);
+}
+
+static ZZJSON *parse_null(ZZJSON_CONFIG *config) {
+ return parse_literal(config, "null", ZZJSON_NULL);
+}
+
+static ZZJSON *parse_value(ZZJSON_CONFIG *config) {
+ ZZJSON *retval = NULL;
+ int c;
+
+ SKIPWS();
+ c = GETC();
+ UNGETC(c);
+ switch (c) {
+ case '"': retval = parse_string2(config); break;
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '-':
+ retval = parse_number(config); break;
+ case '{': retval = parse_object(config); break;
+ case '[': retval = parse_array(config); break;
+ case 't': retval = parse_true(config); break;
+ case 'f': retval = parse_false(config); break;
+ case 'n': retval = parse_null(config); break;
+ }
+
+ if (!retval) {
+ ERROR("value: invalid value");
+ return retval;
+ }
+
+ return retval;
+}
+
+static ZZJSON *parse_array(ZZJSON_CONFIG *config) {
+ ZZJSON *retval = NULL, **next = &retval;
+ int c;
+
+ SKIPWS();
+ c = GETC();
+ if (c != '[') {
+ ERROR("array: expected '['");
+ return NULL;
+ }
+
+ SKIPWS();
+ c = GETC();
+ while (c > 0 && c != ']') {
+ ZZJSON *zzjson = NULL, *val = NULL;
+
+ UNGETC(c);
+
+ SKIPWS();
+ val = parse_value(config);
+ if (!val) {
+ ERROR("array: value expected");
+ goto errout;
+ }
+
+ SKIPWS();
+ c = GETC();
+ if (c != ',' && c != ']') {
+ ERROR("array: expected ',' or ']'");
+errout_with_val:
+ zzjson_free(config, val);
+ goto errout;
+ }
+ if (c == ',') {
+ SKIPWS();
+ c = GETC();
+ if (c == ']' && !ALLOW_EXTRA_COMMA) {
+ ERROR("array: expected value after ','");
+ goto errout_with_val;
+ }
+ }
+ UNGETC(c);
+
+ zzjson = config->calloc(1, sizeof(ZZJSON));
+ if (!zzjson) {
+ MEMERROR();
+ zzjson_free(config, val);
+ goto errout_with_val;
+ }
+ zzjson->type = ZZJSON_ARRAY;
+ zzjson->value.array.val = val;
+ *next = zzjson;
+ next = &zzjson->next;
+
+ c = GETC();
+ }
+
+ if (c != ']') {
+ ERROR("array: expected ']'");
+ goto errout;
+ }
+
+ if (!retval) { /* empty array, [ ] */
+ retval = config->calloc(1, sizeof(ZZJSON));
+ if (!retval) {
+ MEMERROR();
+ return NULL;
+ }
+ retval->type = ZZJSON_ARRAY;
+ }
+
+ return retval;
+
+errout:
+ zzjson_free(config, retval);
+ return NULL;
+}
+
+static ZZJSON *parse_object(ZZJSON_CONFIG *config) {
+ ZZJSON *retval = NULL;
+ int c;
+ ZZJSON **next = &retval;
+
+ SKIPWS();
+ c = GETC();
+ if (c != '{') {
+ ERROR("object: expected '{'");
+ return NULL;
+ }
+
+ SKIPWS();
+ c = GETC();
+ while (c > 0 && c != '}') {
+ ZZJSON *zzjson = NULL, *val = NULL;
+ char *str;
+
+ UNGETC(c);
+
+ str = parse_string(config);
+ if (!str) {
+ ERROR("object: expected string");
+errout_with_str:
+ config->free(str);
+ goto errout;
+ }
+
+ SKIPWS();
+ c = GETC();
+ if (c != ':') {
+ ERROR("object: expected ':'");
+ goto errout_with_str;
+ }
+
+ SKIPWS();
+ val = parse_value(config);
+ if (!val) {
+ ERROR("object: value expected");
+ goto errout_with_str;
+ }
+
+ SKIPWS();
+ c = GETC();
+ if (c != ',' && c != '}') {
+ ERROR("object: expected ',' or '}'");
+errout_with_str_and_val:
+ zzjson_free(config, val);
+ goto errout_with_str;
+ }
+ if (c == ',') {
+ SKIPWS();
+ c = GETC();
+ if (c == '}' && !ALLOW_EXTRA_COMMA) {
+ ERROR("object: expected pair after ','");
+ goto errout_with_str_and_val;
+ }
+ }
+ UNGETC(c);
+
+ zzjson = config->calloc(1, sizeof(ZZJSON));
+ if (!zzjson) {
+ MEMERROR();
+ goto errout_with_str_and_val;
+ }
+ zzjson->type = ZZJSON_OBJECT;
+ zzjson->value.object.label = str;
+ zzjson->value.object.val = val;
+ *next = zzjson;
+ next = &zzjson->next;
+
+ c = GETC();
+ }
+
+ if (c != '}') {
+ ERROR("object: expected '}'");
+ goto errout;
+ }
+
+ if (!retval) { /* empty object, { } */
+ retval = config->calloc(1, sizeof(ZZJSON));
+ if (!retval) {
+ MEMERROR();
+ return NULL;
+ }
+ retval->type = ZZJSON_OBJECT;
+ }
+
+ return retval;
+
+errout:
+ zzjson_free(config, retval);
+ return NULL;
+}
+
+ZZJSON *zzjson_parse(ZZJSON_CONFIG *config) {
+ ZZJSON *retval;
+ int c;
+
+ SKIPWS();
+ c = GETC();
+ UNGETC(c);
+ if (c == '[') retval = parse_array(config);
+ else if (c == '{') retval = parse_object(config);
+ else { ERROR("expected '[' or '{'"); return NULL; }
+
+ if (!retval) return NULL;
+
+ SKIPWS();
+ c = GETC();
+ if (c >= 0 && !ALLOW_GARBAGE_AT_END) {
+ ERROR("parse: garbage at end of file");
+ zzjson_free(config, retval);
+ return NULL;
+ }
+
+ return retval;
+}
--- /dev/null
+/* JSON Printer
+ * ZZJSON - Copyright (C) 2008 by Ivo van Poorten
+ * License: GNU Lesser General Public License version 2.1
+ */
+
+#include "zzjson.h"
+
+#define PRINT(fmt...) if (config->print(config->ohandle, ##fmt) < 0) return -1;
+#define PUTC(c) if (config->putchar(c, config->ohandle) < 0) return -1;
+#define INC 4
+
+static int print_string(ZZJSON_CONFIG *config, char *s) {
+ int c, bs;
+ if (!s) return 0;
+ while ((c = *s++)) {
+ bs = 1;
+ switch (c) {
+// case '/': // useless escape of forward slash
+ case '\\':
+ if (*s == 'u') bs = 0; // copy \uHHHH verbatim
+ break;
+ 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;
+ default: bs = 0; break;
+ }
+ if (bs) PUTC('\\');
+ PUTC(c);
+ }
+ return 0;
+}
+
+static int zzjson_print2(ZZJSON_CONFIG *config, ZZJSON *zzjson,
+ unsigned int indent, unsigned int objval) {
+ char c = 0, d = 0;
+ if (!zzjson) return -1;
+
+ switch(zzjson->type) {
+ case ZZJSON_OBJECT: c = '{'; d = '}'; break;
+ case ZZJSON_ARRAY: c = '['; d = ']'; break;
+ default: break;
+ }
+
+ if (c) PRINT("%s%*s%c", indent ? "\n" : "", indent, "", c);
+
+ while (zzjson) {
+ switch(zzjson->type) {
+ case ZZJSON_OBJECT:
+ if (zzjson->value.object.val) {
+ PRINT("\n%*s\"", indent+INC, "");
+ if (print_string(config, zzjson->value.object.label) < 0)
+ return -1;
+ PRINT("\" :");
+ if (zzjson_print2(config, zzjson->value.object.val,
+ indent+INC, 1) < 0) return -1;
+ }
+ break;
+ case ZZJSON_ARRAY:
+ if (zzjson->value.array.val)
+ if (zzjson_print2(config, zzjson->value.array.val,
+ indent+INC, 0) < 0) return -1;
+ break;
+ case ZZJSON_STRING:
+ PRINT(objval ? " \"" : "\n%*s\"", indent, "");
+ if (print_string(config, zzjson->value.string.string)<0) return -1;
+ PUTC('"');
+ break;
+ case ZZJSON_FALSE:
+ PRINT(objval ? " false" : "\n%*sfalse", indent, "");
+ break;
+ case ZZJSON_NULL:
+ PRINT(objval ? " null" : "\n%*snull", indent, "");
+ break;
+ case ZZJSON_TRUE:
+ PRINT(objval ? " true" : "\n%*strue", indent, "");
+ break;
+ case ZZJSON_NUMBER_NEGINT:
+ case ZZJSON_NUMBER_POSINT:
+ case ZZJSON_NUMBER_DOUBLE:
+ PRINT(objval ? " " : "\n%*s", indent, "");
+ if (zzjson->type == ZZJSON_NUMBER_DOUBLE) {
+ PRINT("%16.16e", zzjson->value.number.val.dval);
+ } else {
+ if (zzjson->type == ZZJSON_NUMBER_NEGINT) PUTC('-');
+ PRINT("%llu", zzjson->value.number.val.ival);
+ }
+ default:
+ break;
+ }
+ zzjson = zzjson->next;
+ if (zzjson) PUTC(',');
+ }
+
+ if (d) PRINT("\n%*s%c", indent, "", d);
+
+ return 0;
+}
+
+int zzjson_print(ZZJSON_CONFIG *config, ZZJSON *zzjson) {
+ int retval = zzjson_print2(config, zzjson, 0, 0);
+ if (retval >= 0) retval = config->putchar('\n', config->ohandle);
+#ifndef CONFIG_NO_ERROR_MESSAGES
+ if (retval < 0) config->error(config->ehandle, "print: unable to print");
+#endif
+ return retval;
+}
--- /dev/null
+/* JSON query
+ * ZZJSON - Copyright (C) 2008 by Ivo van Poorten
+ * License: GNU Lesser General Public License version 2.1
+ */
+
+#include "zzjson.h"
+#include <string.h>
+#include <stdarg.h>
+
+ZZJSON *zzjson_object_find_label(ZZJSON *zzjson, char *label) {
+ if (zzjson->type != ZZJSON_OBJECT) return NULL;
+
+ while (zzjson) {
+ char *string = zzjson->value.object.label;
+
+ if (zzjson->type != ZZJSON_OBJECT) return NULL;
+ if (!string) return NULL;
+
+ if (!strcmp(string, label))
+ return zzjson->value.object.val;
+ zzjson = zzjson->next;
+ }
+ return NULL;
+}
+
+ZZJSON *zzjson_object_find_labels(ZZJSON *zzjson, ...) {
+ va_list ap;
+ char *lbl;
+
+ va_start(ap, zzjson);
+ lbl = va_arg(ap, char *);
+ while (lbl) {
+ zzjson = zzjson_object_find_label(zzjson, lbl);
+ if (!zzjson) break;
+ lbl = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ return zzjson;
+}
+
+unsigned int zzjson_object_count(ZZJSON *zzjson) {
+ unsigned int count = 1;
+
+ if (zzjson->type != ZZJSON_OBJECT) return 0;
+ if (!zzjson->value.object.label) return 0; /* empty { } */
+
+ while ((zzjson = zzjson->next)) count++;
+
+ return count;
+}
+
+unsigned int zzjson_array_count(ZZJSON *zzjson) {
+ unsigned int count = 1;
+
+ if (zzjson->type != ZZJSON_ARRAY) return 0;
+ if (!zzjson->value.array.val) return 0; /* empty [ ] */
+
+ while ((zzjson = zzjson->next)) count++;
+
+ return count;
+}
+
--- /dev/null
+/* crude test program, used for testing during development
+ * ZZJSON - Copyright (C) 2008 by Ivo van Poorten
+ * License: GNU General Public License version 2 or later
+ */
+
+#include "zzjson.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+static void myerror(void *ehandle, const char *format, ...) {
+ va_list ap;
+ fprintf(ehandle, "error: ");
+ va_start(ap, format);
+ vfprintf(ehandle, format, ap);
+ va_end(ap);
+ fputc('\n', ehandle);
+}
+
+int main(int argc, char **argv) {
+ ZZJSON *zzjson, *tmp;
+ ZZJSON_CONFIG config = { ZZJSON_VERY_STRICT, NULL,
+ (int(*)(void*)) fgetc,
+ (int(*)(int,void*)) ungetc,
+ malloc, calloc, free, realloc,
+ stderr, myerror, stdout,
+ (int(*)(void*,const char*,...)) fprintf,
+ (int(*)(int,void*)) fputc };
+ FILE *fp;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s: usage: %s <json-file>\n", argv[0], argv[0]);
+ return 1;
+ }
+
+ if (!(fp = fopen(argv[1], "rb"))) {
+ fprintf(stderr, "%s: unable to open %s\n", argv[0], argv[1]);
+ return 1;
+ }
+ config.ihandle = fp;
+
+ zzjson = zzjson_parse(&config);
+
+ if (!zzjson) {
+ fprintf(stderr, "%s: unable to parse json file\n", argv[0]);
+ fprintf(stderr, "%s: filepos: %lli\n", argv[0], (long long) ftell(fp));
+ fclose(fp);
+ return 1;
+ }
+
+ fclose(fp);
+
+ zzjson_print(&config, zzjson);
+
+ {
+ ZZJSON *res;
+ res = zzjson_object_find_labels(zzjson, "one", "two", "three", NULL);
+
+ if (!res) fprintf(stderr, "snafu not found\n");
+ else {
+ fprintf(stderr, "snafu found: %s\n", res->value.string.string);
+ }
+ }
+
+ fprintf(stderr, "object count: %u\n", zzjson_object_count(zzjson));
+ fprintf(stderr, "array count: %u\n", zzjson_array_count(zzjson));
+
+ fprintf(stderr, "%s\n", ZZJSON_IDENT);
+ fprintf(stderr, "%i\n", ZZJSON_VERSION_INT);
+
+ do {
+ ZZJSON *tmp2;
+
+ tmp = zzjson_create_array(&config,
+ zzjson_create_number_d(&config, 3.14),
+ zzjson_create_number_i(&config, 1234LL),
+ zzjson_create_number_i(&config, -4321LL),
+ zzjson_create_true(&config),
+ zzjson_create_false(&config),
+ zzjson_create_null(&config),
+ zzjson_create_string(&config, "hello, world"),
+ zzjson_create_object(&config,
+ "picard", zzjson_create_string(&config, "jean-luc"),
+ "riker", zzjson_create_string(&config, "william t."),
+ NULL),
+ zzjson_create_object(&config, NULL),
+ zzjson_create_array(&config, NULL),
+ NULL );
+
+ if (!tmp) {
+ fprintf(stderr, "error during creation of json tree\n");
+ break;
+ }
+
+ tmp2 = zzjson_array_prepend(&config, tmp,
+ zzjson_create_string(&config, "prepended string"));
+
+ if (!tmp2) {
+ fprintf(stderr, "error during prepend\n");
+ break;
+ }
+ tmp = tmp2;
+
+ tmp2 = zzjson_array_append(&config, tmp,
+ zzjson_create_string(&config, "appended string (slow)"));
+
+ if (!tmp2) {
+ fprintf(stderr, "error during append\n");
+ break;
+ }
+ tmp = tmp2;
+
+ zzjson_print(&config, tmp);
+ } while(0);
+ if (tmp) zzjson_free(&config, tmp);
+
+ {
+ tmp = zzjson_create_array(&config, NULL); /* empty array */
+ tmp = zzjson_array_prepend(&config, tmp, zzjson_create_true(&config));
+ zzjson_print(&config, tmp);
+ zzjson_free(&config, tmp);
+ }
+
+ {
+ tmp = zzjson_create_object(&config, NULL); /* empty object */
+ tmp = zzjson_object_prepend(&config, tmp, "hello",
+ zzjson_create_string(&config, "world"));
+ tmp = zzjson_object_append(&config, tmp, "goodbye",
+ zzjson_create_string(&config, "cruel world"));
+ zzjson_print(&config, tmp);
+ zzjson_free(&config, tmp);
+ }
+
+ zzjson_free(&config, zzjson);
+
+ return 0;
+}