From d830004bf61b18d9f819ab8de8d62f509b74d367 Mon Sep 17 00:00:00 2001 From: Erwan Velu Date: Fri, 18 Mar 2011 21:56:58 +0100 Subject: zzjson: Adding zzjson 1.1.0 --- com32/gplinclude/zzjson/zzjson.h | 116 +++++++++ com32/gpllib/Makefile | 4 +- com32/gpllib/zzjson/zzjson_create.c | 240 ++++++++++++++++++ com32/gpllib/zzjson/zzjson_free.c | 29 +++ com32/gpllib/zzjson/zzjson_parse.c | 490 ++++++++++++++++++++++++++++++++++++ com32/gpllib/zzjson/zzjson_print.c | 109 ++++++++ com32/gpllib/zzjson/zzjson_query.c | 63 +++++ com32/gpllib/zzjson/zzjson_test.c | 137 ++++++++++ 8 files changed, 1186 insertions(+), 2 deletions(-) create mode 100644 com32/gplinclude/zzjson/zzjson.h create mode 100644 com32/gpllib/zzjson/zzjson_create.c create mode 100644 com32/gpllib/zzjson/zzjson_free.c create mode 100644 com32/gpllib/zzjson/zzjson_parse.c create mode 100644 com32/gpllib/zzjson/zzjson_print.c create mode 100644 com32/gpllib/zzjson/zzjson_query.c create mode 100644 com32/gpllib/zzjson/zzjson_test.c diff --git a/com32/gplinclude/zzjson/zzjson.h b/com32/gplinclude/zzjson/zzjson.h new file mode 100644 index 00000000..d4b32e12 --- /dev/null +++ b/com32/gplinclude/zzjson/zzjson.h @@ -0,0 +1,116 @@ +/* ZZJSON - Copyright (C) 2008 by Ivo van Poorten + * License: GNU Lesser General Public License version 2.1 + */ +#ifndef ZZJSON_H +#define ZZJSON_H + +#include + +/* 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 diff --git a/com32/gpllib/Makefile b/com32/gpllib/Makefile index a1740610..8e7fb430 100644 --- a/com32/gpllib/Makefile +++ b/com32/gpllib/Makefile @@ -6,9 +6,9 @@ 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 diff --git a/com32/gpllib/zzjson/zzjson_create.c b/com32/gpllib/zzjson/zzjson_create.c new file mode 100644 index 00000000..7e6bd5bd --- /dev/null +++ b/com32/gpllib/zzjson/zzjson_create.c @@ -0,0 +1,240 @@ +/* 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 +#include +#include + +#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; +} + diff --git a/com32/gpllib/zzjson/zzjson_free.c b/com32/gpllib/zzjson/zzjson_free.c new file mode 100644 index 00000000..01dfd242 --- /dev/null +++ b/com32/gpllib/zzjson/zzjson_free.c @@ -0,0 +1,29 @@ +/* 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; + } +} diff --git a/com32/gpllib/zzjson/zzjson_parse.c b/com32/gpllib/zzjson/zzjson_parse.c new file mode 100644 index 00000000..8353a6cc --- /dev/null +++ b/com32/gpllib/zzjson/zzjson_parse.c @@ -0,0 +1,490 @@ +/* JSON Parser + * ZZJSON - Copyright (C) 2008-2009 by Ivo van Poorten + * License: GNU Lesser General Public License version 2.1 + */ + +#include "zzjson.h" +#include +#include +#include +#include + +#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; icalloc(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; +} diff --git a/com32/gpllib/zzjson/zzjson_print.c b/com32/gpllib/zzjson/zzjson_print.c new file mode 100644 index 00000000..c222eb6f --- /dev/null +++ b/com32/gpllib/zzjson/zzjson_print.c @@ -0,0 +1,109 @@ +/* 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; +} diff --git a/com32/gpllib/zzjson/zzjson_query.c b/com32/gpllib/zzjson/zzjson_query.c new file mode 100644 index 00000000..35ba7b79 --- /dev/null +++ b/com32/gpllib/zzjson/zzjson_query.c @@ -0,0 +1,63 @@ +/* JSON query + * ZZJSON - Copyright (C) 2008 by Ivo van Poorten + * License: GNU Lesser General Public License version 2.1 + */ + +#include "zzjson.h" +#include +#include + +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; +} + diff --git a/com32/gpllib/zzjson/zzjson_test.c b/com32/gpllib/zzjson/zzjson_test.c new file mode 100644 index 00000000..143b24b4 --- /dev/null +++ b/com32/gpllib/zzjson/zzjson_test.c @@ -0,0 +1,137 @@ +/* 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 +#include +#include + +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 \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; +} -- cgit v1.2.1