zzjson: Adding zzjson 1.1.0
authorErwan Velu <erwanaliasr1@gmail.com>
Fri, 18 Mar 2011 20:56:58 +0000 (21:56 +0100)
committerErwan Velu <erwanaliasr1@gmail.com>
Fri, 18 Mar 2011 20:56:58 +0000 (21:56 +0100)
com32/gplinclude/zzjson/zzjson.h [new file with mode: 0644]
com32/gpllib/Makefile
com32/gpllib/zzjson/zzjson_create.c [new file with mode: 0644]
com32/gpllib/zzjson/zzjson_free.c [new file with mode: 0644]
com32/gpllib/zzjson/zzjson_parse.c [new file with mode: 0644]
com32/gpllib/zzjson/zzjson_print.c [new file with mode: 0644]
com32/gpllib/zzjson/zzjson_query.c [new file with mode: 0644]
com32/gpllib/zzjson/zzjson_test.c [new file with mode: 0644]

diff --git a/com32/gplinclude/zzjson/zzjson.h b/com32/gplinclude/zzjson/zzjson.h
new file mode 100644 (file)
index 0000000..d4b32e1
--- /dev/null
@@ -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 <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
index a174061..8e7fb43 100644 (file)
@@ -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 (file)
index 0000000..7e6bd5b
--- /dev/null
@@ -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 <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;
+}
+
diff --git a/com32/gpllib/zzjson/zzjson_free.c b/com32/gpllib/zzjson/zzjson_free.c
new file mode 100644 (file)
index 0000000..01dfd24
--- /dev/null
@@ -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 (file)
index 0000000..8353a6c
--- /dev/null
@@ -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 <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;
+}
diff --git a/com32/gpllib/zzjson/zzjson_print.c b/com32/gpllib/zzjson/zzjson_print.c
new file mode 100644 (file)
index 0000000..c222eb6
--- /dev/null
@@ -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 (file)
index 0000000..35ba7b7
--- /dev/null
@@ -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 <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;
+}
+
diff --git a/com32/gpllib/zzjson/zzjson_test.c b/com32/gpllib/zzjson/zzjson_test.c
new file mode 100644 (file)
index 0000000..143b24b
--- /dev/null
@@ -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 <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;
+}