]> thyrsus.com Git - microjson.git/commitdiff
Shorten the name of the linkable module and headeer file.
authorEric S. Raymond <esr@thyrsus.com>
Tue, 23 Sep 2014 16:39:23 +0000 (12:39 -0400)
committerEric S. Raymond <esr@thyrsus.com>
Tue, 23 Sep 2014 16:39:23 +0000 (12:39 -0400)
Makefile
example1.c
example2.c
example3.c
microjson.asc
microjson.c [deleted file]
microjson.h [deleted file]
mjson.c [new file with mode: 0644]
mjson.h [new file with mode: 0644]
test_microjson.c

index addfc82494a2b1d5ca6fe8c97ed4c14ed3b0c252..57cf3c554255240133729ffea8474c8196fe420c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,19 +7,21 @@ CFLAGS = -O
 # Add DEBUG_ENABLE for the tracing code
 CFLAGS += -DDEBUG_ENABLE -g
 
-microjson.o: microjson.c microjson.h
+mjson.o: mjson.c mjson.h
 
-test_microjson: test_microjson.o microjson.o
-       $(CC) $(CFLAGS) -o test_microjson test_microjson.o microjson.o
+test_microjson: test_microjson.o mjson.o
+       $(CC) $(CFLAGS) -o test_microjson test_microjson.o mjson.o
 
 # Regression test
 check: test_microjson
        test_microjson
 
-# Worked examples
-example1: example1.c microjson.c
-example2: example2.c microjson.c
-example3: example3.c microjson.c
+make testbuild: mjson.o test_microjson example1 example2 example3
+
+# Worked examples.  These are essentially subsets of the regresion test.
+example1: example1.c mjson.c
+example2: example2.c mjson.c
+example3: example3.c mjson.c
 
 clean:
        rm -f microjson.o test_microjson.o test_microjson example[123]
@@ -36,5 +38,5 @@ microjson-$(VERSION).tar.gz: $(ALL)
 
 dist: microjson-$(VERSION).tar.gz
 
-release: microjson-$(VERS).tar.gz microjson.html
+release: microjson-$(VERS).tar.gz mjson.html
        shipper version=$(VERS) | sh -e -x
index 81d370ac78dbe8dd2507130d1b996162c7618d3c..949b2f403a7fe8be96a1fd6fda52fcd026e1bbc9 100644 (file)
@@ -8,7 +8,7 @@
 #include <stdlib.h>
 #include <stdbool.h>
 
-#include "microjson.h"
+#include "mjson.h"
 
 static bool flag1, flag2;
 static int count;
index 87806195bd1c1a8228bff9e0afc619d997564bb7..bfff4227298c86226ab971d93adadd85850aa94e 100644 (file)
@@ -6,7 +6,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "microjson.h"
+#include "mjson.h"
 
 #define MAXCHANNELS 72
 
index 9b4c8e171eaea7f8f8d7cde55f49f5525908ecac..6c2c786ee40cd3c28cfdd9cfb034f5a852c00470 100644 (file)
@@ -9,7 +9,7 @@
 #include <limits.h>
 #include <string.h>
 
-#include "microjson.h"
+#include "mjson.h"
 
 #define MAXUSERDEVS    4
 
index d8551ba9424664699cbfcd6d7b5f4247a6b75bba..61de65ecee0e68263421f663bba66f1a5eb6c222 100644 (file)
@@ -45,7 +45,7 @@ Here is nearly the simplest possible example:
 #include <stdlib.h>
 #include <stdbool.h>
 
-#include "microjson.h"
+#include "mjson.h"
 
 static bool flag1, flag2;
 static int count;
@@ -181,7 +181,7 @@ in this example:
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "microjson.h"
+#include "mjson.h"
 
 #define MAXCHANNELS 72
 
@@ -255,7 +255,7 @@ array of JSON objects directly into an array of C structs.
 #include <limits.h>
 #include <string.h>
 
-#include "microjson.h"
+#include "mjson.h"
 
 #define MAXUSERDEVS    4
 
diff --git a/microjson.c b/microjson.c
deleted file mode 100644 (file)
index 921c815..0000000
+++ /dev/null
@@ -1,966 +0,0 @@
-/****************************************************************************
-
-NAME
-   microjson.c - parse JSON into fixed-extent data structures
-
-DESCRIPTION
-   This module parses a large subset of JSON (JavaScript Object
-Notation).  Unlike more general JSON parsers, it doesn't use malloc(3)
-and doesn't support polymorphism; you need to give it a set of
-template structures describing the expected shape of the incoming
-JSON, and it will error out if that shape is not matched.  When the
-parse succeeds, attribute values will be extracted into static
-locations specified in the template structures.
-
-   The "shape" of a JSON object in the type signature of its
-attributes (and attribute values, and so on recursively down through
-all nestings of objects and arrays).  This parser is indifferent to
-the order of attributes at any level, but you have to tell it in
-advance what the type of each attribute value will be and where the
-parsed value will be stored. The template structures may supply
-default values to be used when an expected attribute is omitted.
-
-   The preceding paragraph told one fib.  A single attribute may
-actually have a span of multiple specifications with different
-syntactically distinguishable types (e.g. string vs. real vs. integer
-vs. boolean, but not signed integer vs. unsigned integer).  The parser
-will match the right spec against the actual data.
-
-   The dialect this parses has some limitations.  First, it cannot
-recognize the JSON "null" value. Second, all elements of an array must
-be of the same type. Third, some types (t_time, t_character) may not
-ve array elements (this restriction could be lifted)
-
-   There are separate entry points for beginning a parse of either
-JSON object or a JSON array. JSON "float" quantities are actually
-stored as doubles.
-
-   This parser processes object arrays in one of two different ways,
-defending on whether the array subtype is declared as object or
-structobject.
-
-   Object arrays take one base address per object subfield, and are
-mapped into parallel C arrays (one per subfield).  Strings are not
-supported in this kind of array, as they don't have a "natural" size
-to use as an offset multiplier.
-
-   Structobjects arrays are a way to parse a list of objects to a set
-of modifications to a corresponding array of C structs.  The trick is
-that the array object initialization has to specify both the C struct
-array's base address and the stride length (the size of the C struct).
-If you initialize the offset fields with the correct offsetof calls,
-everything will work. Strings are supported but all string storage
-has to be inline in the struct.
-
-PERMISSIONS
-   This file is Copyright (c) 2014 by Eric S. Raymond
-   BSD terms apply: see the file COPYING in the distribution root for details.
-
-***************************************************************************/
-
-/* The strptime prototype is not provided unless explicitly requested.
- * We also need to set the value high enough to signal inclusion of
- * newer features (like clock_gettime).  See the POSIX spec for more info:
- * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_02_01_02 */
-#define _XOPEN_SOURCE 600
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
-#include <errno.h>
-
-#include "microjson.h"
-
-#ifdef DEBUG_ENABLE
-static int debuglevel = 0;
-static FILE *debugfp;
-
-void json_enable_debug(int level, FILE * fp)
-/* control the level and destination of debug trace messages */
-{
-    debuglevel = level;
-    debugfp = fp;
-}
-
-static void json_trace(int errlevel, const char *fmt, ...)
-/* assemble command in printf(3) style */
-{
-    if (errlevel <= debuglevel) {
-       char buf[BUFSIZ];
-       va_list ap;
-
-       (void)strncpy(buf, "json: ", BUFSIZ-1);
-       buf[BUFSIZ-1] = '\0';
-       va_start(ap, fmt);
-       (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt,
-                       ap);
-       va_end(ap);
-
-       (void)fputs(buf, debugfp);
-    }
-}
-
-# define json_debug_trace(args) (void) json_trace args
-#else
-# define json_debug_trace(args) /*@i1@*/do { } while (0)
-#endif /* DEBUG_ENABLE */
-
-/*
- * Berkeley implementation of strtod(), inlined to avoid locale problems
- * with the decimal point and stripped down to an atof()-equivalent.
- */
-
-/*@-shiftimplementation +charint@*/
-static double safe_atof(const char *string)
-/* Takes a decimal ASCII floating-point number, optionally
- * preceded by white space.  Must have form "-I.FE-X",
- * where I is the integer part of the mantissa, F is
- * the fractional part of the mantissa, and X is the
- * exponent.  Either of the signs may be "+", "-", or
- * omitted.  Either I or F may be omitted, or both.
- * The decimal point isn't necessary unless F is
- * present.  The "E" may actually be an "e".  E and X
- * may both be omitted (but not just one).
- */
-{
-    static int maxExponent = 511;   /* Largest possible base 10 exponent.  Any
-                                    * exponent larger than this will already
-                                    * produce underflow or overflow, so there's
-                                    * no need to worry about additional digits.
-                                    */
-    static double powersOf10[] = {  /* Table giving binary powers of 10.  Entry */
-       10.,                    /* is 10^2^i.  Used to convert decimal */
-       100.,                   /* exponents into floating-point numbers. */
-       1.0e4,
-       1.0e8,
-       1.0e16,
-       1.0e32,
-       1.0e64,
-       1.0e128,
-       1.0e256
-    };
-
-    bool sign, expSign = false;
-    double fraction, dblExp, *d;
-    register const char *p;
-    register int c;
-    int exp = 0;               /* Exponent read from "EX" field. */
-    int fracExp = 0;           /* Exponent that derives from the fractional
-                                * part.  Under normal circumstatnces, it is
-                                * the negative of the number of digits in F.
-                                * However, if I is very long, the last digits
-                                * of I get dropped (otherwise a long I with a
-                                * large negative exponent could cause an
-                                * unnecessary overflow on I alone).  In this
-                                * case, fracExp is incremented one for each
-                                * dropped digit. */
-    int mantSize;              /* Number of digits in mantissa. */
-    int decPt;                 /* Number of mantissa digits BEFORE decimal
-                                * point. */
-    const char *pExp;          /* Temporarily holds location of exponent
-                                * in string. */
-
-    /*
-     * Strip off leading blanks and check for a sign.
-     */
-
-    p = string;
-    while (isspace((unsigned char) *p)) {
-       p += 1;
-    }
-    if (*p == '-') {
-       sign = true;
-       p += 1;
-    } else {
-       if (*p == '+') {
-           p += 1;
-       }
-       sign = false;
-    }
-
-    /*
-     * Count the number of digits in the mantissa (including the decimal
-     * point), and also locate the decimal point.
-     */
-
-    decPt = -1;
-    for (mantSize = 0; ; mantSize += 1)
-    {
-       c = *p;
-       if (!isdigit(c)) {
-           if ((c != '.') || (decPt >= 0)) {
-               break;
-           }
-           decPt = mantSize;
-       }
-       p += 1;
-    }
-
-    /*
-     * Now suck up the digits in the mantissa.  Use two integers to
-     * collect 9 digits each (this is faster than using floating-point).
-     * If the mantissa has more than 18 digits, ignore the extras, since
-     * they can't affect the value anyway.
-     */
-
-    pExp  = p;
-    p -= mantSize;
-    if (decPt < 0) {
-       decPt = mantSize;
-    } else {
-       mantSize -= 1;                  /* One of the digits was the point. */
-    }
-    if (mantSize > 18) {
-       fracExp = decPt - 18;
-       mantSize = 18;
-    } else {
-       fracExp = decPt - mantSize;
-    }
-    if (mantSize == 0) {
-       fraction = 0.0;
-       //p = string;
-       goto done;
-    } else {
-       int frac1, frac2;
-       frac1 = 0;
-       for ( ; mantSize > 9; mantSize -= 1)
-       {
-           c = *p;
-           p += 1;
-           if (c == '.') {
-               c = *p;
-               p += 1;
-           }
-           frac1 = 10*frac1 + (c - '0');
-       }
-       frac2 = 0;
-       for (; mantSize > 0; mantSize -= 1)
-       {
-           c = *p;
-           p += 1;
-           if (c == '.') {
-               c = *p;
-               p += 1;
-           }
-           frac2 = 10*frac2 + (c - '0');
-       }
-       fraction = (1.0e9 * frac1) + frac2;
-    }
-
-    /*
-     * Skim off the exponent.
-     */
-
-    p = pExp;
-    if ((*p == 'E') || (*p == 'e')) {
-       p += 1;
-       if (*p == '-') {
-           expSign = true;
-           p += 1;
-       } else {
-           if (*p == '+') {
-               p += 1;
-           }
-           expSign = false;
-       }
-       while (isdigit((unsigned char) *p)) {
-           exp = exp * 10 + (*p - '0');
-           p += 1;
-       }
-    }
-    if (expSign) {
-       exp = fracExp - exp;
-    } else {
-       exp = fracExp + exp;
-    }
-
-    /*
-     * Generate a floating-point number that represents the exponent.
-     * Do this by processing the exponent one bit at a time to combine
-     * many powers of 2 of 10. Then combine the exponent with the
-     * fraction.
-     */
-
-    if (exp < 0) {
-       expSign = true;
-       exp = -exp;
-    } else {
-       expSign = false;
-    }
-    if (exp > maxExponent) {
-       exp = maxExponent;
-       errno = ERANGE;
-    }
-    dblExp = 1.0;
-    for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
-       if (exp & 01) {
-           dblExp *= *d;
-       }
-    }
-    if (expSign) {
-       fraction /= dblExp;
-    } else {
-       fraction *= dblExp;
-    }
-
-done:
-    if (sign) {
-       return -fraction;
-    }
-    return fraction;
-}
-/*@+shiftimplementation -charint@*/
-
-#define MONTHSPERYEAR  12      /* months per calendar year */
-
-static time_t mkutctime(register struct tm * t)
-/* struct tm to seconds since Unix epoch */
-{
-    register int year;
-    register time_t result;
-    static const int cumdays[MONTHSPERYEAR] =
-       { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
-
-    /*@ +matchanyintegral @*/
-    year = 1900 + t->tm_year + t->tm_mon / MONTHSPERYEAR;
-    result = (year - 1970) * 365 + cumdays[t->tm_mon % MONTHSPERYEAR];
-    result += (year - 1968) / 4;
-    result -= (year - 1900) / 100;
-    result += (year - 1600) / 400;
-    if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) &&
-       (t->tm_mon % MONTHSPERYEAR) < 2)
-       result--;
-    result += t->tm_mday - 1;
-    result *= 24;
-    result += t->tm_hour;
-    result *= 60;
-    result += t->tm_min;
-    result *= 60;
-    result += t->tm_sec;
-    /*@ -matchanyintegral @*/
-    return (result);
-}
-
-static double iso8601_to_unix(/*@in@*/const char *isotime)
-/* ISO8601 UTC to Unix UTC */
-{
-    char *dp = NULL;
-    double usec;
-    struct tm tm;
-
-    /*@i1@*/ dp = strptime(isotime, "%Y-%m-%dT%H:%M:%S", &tm);
-    if (dp != NULL && *dp == '.')
-       usec = strtod(dp, NULL);
-    else
-       usec = 0;
-    return mkutctime(&tm) + usec;
-}
-
-/*@-immediatetrans -dependenttrans -usereleased -compdef@*/
-static /*@null@*/ char *json_target_address(const struct json_attr_t *cursor,
-                                            /*@null@*/
-                                            const struct json_array_t
-                                            *parent, int offset)
-{
-    char *targetaddr = NULL;
-    if (parent == NULL || parent->element_type != t_structobject) {
-       /* ordinary case - use the address in the cursor structure */
-       switch (cursor->type) {
-       case t_ignore:
-           targetaddr = NULL;
-           break;
-       case t_integer:
-           targetaddr = (char *)&cursor->addr.integer[offset];
-           break;
-       case t_uinteger:
-           targetaddr = (char *)&cursor->addr.uinteger[offset];
-           break;
-       case t_time:
-       case t_real:
-           targetaddr = (char *)&cursor->addr.real[offset];
-           break;
-       case t_string:
-           targetaddr = cursor->addr.string;
-           break;
-       case t_boolean:
-           targetaddr = (char *)&cursor->addr.boolean[offset];
-           break;
-       case t_character:
-           targetaddr = (char *)&cursor->addr.character[offset];
-           break;
-       default:
-           targetaddr = NULL;
-           break;
-       }
-    } else
-       /* tricky case - hacking a member in an array of structures */
-       targetaddr =
-           parent->arr.objects.base + (offset * parent->arr.objects.stride) +
-           cursor->addr.offset;
-    json_debug_trace((1, "Target address for %s (offset %d) is %p\n",
-                     cursor->attribute, offset, targetaddr));
-    return targetaddr;
-}
-
-/*@-immediatetrans -dependenttrans +usereleased +compdef@*/
-
-static int json_internal_read_object(const char *cp,
-                                    const struct json_attr_t *attrs,
-                                    /*@null@*/
-                                    const struct json_array_t *parent,
-                                    int offset,
-                                    /*@null@*/ const char **end)
-{
-    /*@ -nullstate -nullderef -mustfreefresh -nullpass -usedef @*/
-    enum
-    { init, await_attr, in_attr, await_value, in_val_string,
-       in_escape, in_val_token, post_val, post_array
-    } state = 0;
-#ifdef DEBUG_ENABLE
-    char *statenames[] = {
-       "init", "await_attr", "in_attr", "await_value", "in_val_string",
-       "in_escape", "in_val_token", "post_val", "post_array",
-    };
-#endif /* DEBUG_ENABLE */
-    char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL;
-    char valbuf[JSON_VAL_MAX + 1], *pval = NULL;
-    bool value_quoted = false;
-    char uescape[5];           /* enough space for 4 hex digits and a NUL */
-    const struct json_attr_t *cursor;
-    int substatus, n, maxlen = 0;
-    unsigned int u;
-    const struct json_enum_t *mp;
-    char *lptr;
-
-#ifdef S_SPLINT_S
-    /* prevents gripes about buffers not being completely defined */
-    memset(valbuf, '\0', sizeof(valbuf));
-    memset(attrbuf, '\0', sizeof(attrbuf));
-#endif /* S_SPLINT_S */
-
-    if (end != NULL)
-       *end = NULL;            /* give it a well-defined value on parse failure */
-
-    /* stuff fields with defaults in case they're omitted in the JSON input */
-    for (cursor = attrs; cursor->attribute != NULL; cursor++)
-       if (!cursor->nodefault) {
-           lptr = json_target_address(cursor, parent, offset);
-           if (lptr != NULL)
-               switch (cursor->type) {
-               case t_integer:
-                   memcpy(lptr, &cursor->dflt.integer, sizeof(int));
-                   break;
-               case t_uinteger:
-                   memcpy(lptr, &cursor->dflt.uinteger, sizeof(unsigned int));
-                   break;
-               case t_time:
-               case t_real:
-                   memcpy(lptr, &cursor->dflt.real, sizeof(double));
-                   break;
-               case t_string:
-                   if (parent != NULL
-                       && parent->element_type != t_structobject
-                       && offset > 0)
-                       return JSON_ERR_NOPARSTR;
-                   lptr[0] = '\0';
-                   break;
-               case t_boolean:
-                   memcpy(lptr, &cursor->dflt.boolean, sizeof(bool));
-                   break;
-               case t_character:
-                   lptr[0] = cursor->dflt.character;
-                   break;
-               case t_object:  /* silences a compiler warning */
-               case t_structobject:
-               case t_array:
-               case t_check:
-               case t_ignore:
-                   break;
-               }
-       }
-
-    json_debug_trace((1, "JSON parse of '%s' begins.\n", cp));
-
-    /* parse input JSON */
-    for (; *cp != '\0'; cp++) {
-       json_debug_trace((2, "State %-14s, looking at '%c' (%p)\n",
-                         statenames[state], *cp, cp));
-       switch (state) {
-       case init:
-           if (isspace((unsigned char) *cp))
-               continue;
-           else if (*cp == '{')
-               state = await_attr;
-           else {
-               json_debug_trace((1,
-                                 "Non-WS when expecting object start.\n"));
-               return JSON_ERR_OBSTART;
-           }
-           break;
-       case await_attr:
-           if (isspace((unsigned char) *cp))
-               continue;
-           else if (*cp == '"') {
-               state = in_attr;
-               pattr = attrbuf;
-           } else if (*cp == '}')
-               break;
-           else {
-               json_debug_trace((1, "Non-WS when expecting attribute.\n"));
-               return JSON_ERR_ATTRSTART;
-           }
-           break;
-       case in_attr:
-           if (pattr == NULL)
-               return JSON_ERR_NULLPTR;
-           if (*cp == '"') {
-               *pattr++ = '\0';
-               json_debug_trace((1, "Collected attribute name %s\n",
-                                 attrbuf));
-               for (cursor = attrs; cursor->attribute != NULL; cursor++) {
-                   json_debug_trace((2, "Checking against %s\n",
-                                     cursor->attribute));
-                   if (strcmp(cursor->attribute, attrbuf) == 0)
-                       break;
-               }
-               if (cursor->attribute == NULL) {
-                   json_debug_trace((1,
-                                     "Unknown attribute name '%s' (attributes begin with '%s').\n",
-                                     attrbuf, attrs->attribute));
-                   return JSON_ERR_BADATTR;
-               }
-               state = await_value;
-               if (cursor->type == t_string)
-                   maxlen = (int)cursor->len - 1;
-               else if (cursor->type == t_check)
-                   maxlen = (int)strlen(cursor->dflt.check);
-               else if (cursor->type == t_time || cursor->type == t_ignore)
-                   maxlen = JSON_VAL_MAX;
-               else if (cursor->map != NULL)
-                   maxlen = (int)sizeof(valbuf) - 1;
-               pval = valbuf;
-           } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) {
-               json_debug_trace((1, "Attribute name too long.\n"));
-               return JSON_ERR_ATTRLEN;
-           } else
-               *pattr++ = *cp;
-           break;
-       case await_value:
-           if (isspace((unsigned char) *cp) || *cp == ':')
-               continue;
-           else if (*cp == '[') {
-               if (cursor->type != t_array) {
-                   json_debug_trace((1,
-                                     "Saw [ when not expecting array.\n"));
-                   return JSON_ERR_NOARRAY;
-               }
-               substatus = json_read_array(cp, &cursor->addr.array, &cp);
-               if (substatus != 0)
-                   return substatus;
-               state = post_array;
-           } else if (cursor->type == t_array) {
-               json_debug_trace((1,
-                                 "Array element was specified, but no [.\n"));
-               return JSON_ERR_NOBRAK;
-           } else if (*cp == '"') {
-               value_quoted = true;
-               state = in_val_string;
-               pval = valbuf;
-           } else {
-               value_quoted = false;
-               state = in_val_token;
-               pval = valbuf;
-               *pval++ = *cp;
-           }
-           break;
-       case in_val_string:
-           if (pval == NULL)
-               return JSON_ERR_NULLPTR;
-           if (*cp == '\\')
-               state = in_escape;
-           else if (*cp == '"') {
-               *pval++ = '\0';
-               json_debug_trace((1, "Collected string value %s\n", valbuf));
-               state = post_val;
-           } else if (pval > valbuf + JSON_VAL_MAX - 1
-                      || pval > valbuf + maxlen) {
-               json_debug_trace((1, "String value too long.\n"));
-               return JSON_ERR_STRLONG;        /*  */
-           } else
-               *pval++ = *cp;
-           break;
-       case in_escape:
-           if (pval == NULL)
-               return JSON_ERR_NULLPTR;
-           switch (*cp) {
-           case 'b':
-               *pval++ = '\b';
-               break;
-           case 'f':
-               *pval++ = '\f';
-               break;
-           case 'n':
-               *pval++ = '\n';
-               break;
-           case 'r':
-               *pval++ = '\r';
-               break;
-           case 't':
-               *pval++ = '\t';
-               break;
-           case 'u':
-               for (n = 0; n < 4 && cp[n] != '\0'; n++)
-                   uescape[n] = *cp++;
-               --cp;
-               (void)sscanf(uescape, "%04x", &u);
-               *pval++ = (char)u;      /* will truncate values above 0xff */
-               break;
-           default:            /* handles double quote and solidus */
-               *pval++ = *cp;
-               break;
-           }
-           state = in_val_string;
-           break;
-       case in_val_token:
-           if (pval == NULL)
-               return JSON_ERR_NULLPTR;
-           if (isspace((unsigned char) *cp) || *cp == ',' || *cp == '}') {
-               *pval = '\0';
-               json_debug_trace((1, "Collected token value %s.\n", valbuf));
-               state = post_val;
-               if (*cp == '}' || *cp == ',')
-                   --cp;
-           } else if (pval > valbuf + JSON_VAL_MAX - 1) {
-               json_debug_trace((1, "Token value too long.\n"));
-               return JSON_ERR_TOKLONG;
-           } else
-               *pval++ = *cp;
-           break;
-       case post_val:
-           /*
-            * We know that cursor points at the first spec matching
-            * the current attribute.  We don't know that it's *the*
-            * correct spec; our dialect allows there to be any number
-            * of adjacent ones with the same attrname but different
-            * types.  Here's where we try to seek forward for a
-            * matching type/attr pair if we're not looking at one.
-            */
-           for (;;) {
-               int seeking = cursor->type;
-               if (value_quoted && (cursor->type == t_string || cursor->type == t_time))
-                   break;
-               if ((strcmp(valbuf, "true")==0 || strcmp(valbuf, "false")==0)
-                       && seeking == t_boolean)
-                   break;
-               if (isdigit((unsigned char) valbuf[0])) {
-                   bool decimal = strchr(valbuf, '.') != NULL;
-                   if (decimal && seeking == t_real)
-                       break;
-                   if (!decimal && (seeking == t_integer || seeking == t_uinteger))
-                       break;
-               }
-               if (cursor[1].attribute==NULL)  /* out of possiblities */
-                   break;
-               if (strcmp(cursor[1].attribute, attrbuf)!=0)
-                   break;
-               ++cursor;
-           }
-           if (value_quoted
-               && (cursor->type != t_string && cursor->type != t_character
-                   && cursor->type != t_check && cursor->type != t_time
-                   && cursor->type != t_ignore && cursor->map == 0)) {
-               json_debug_trace((1,
-                                 "Saw quoted value when expecting non-string.\n"));
-               return JSON_ERR_QNONSTRING;
-           }
-           if (!value_quoted
-               && (cursor->type == t_string || cursor->type == t_check
-                   || cursor->type == t_time || cursor->map != 0)) {
-               json_debug_trace((1,
-                                 "Didn't see quoted value when expecting string.\n"));
-               return JSON_ERR_NONQSTRING;
-           }
-           if (cursor->map != 0) {
-               for (mp = cursor->map; mp->name != NULL; mp++)
-                   if (strcmp(mp->name, valbuf) == 0) {
-                       goto foundit;
-                   }
-               json_debug_trace((1, "Invalid enumerated value string %s.\n",
-                                 valbuf));
-               return JSON_ERR_BADENUM;
-             foundit:
-               (void)snprintf(valbuf, sizeof(valbuf), "%d", mp->value);
-           }
-           lptr = json_target_address(cursor, parent, offset);
-           if (lptr != NULL)
-               switch (cursor->type) {
-               case t_integer:
-                   {
-                       int tmp = atoi(valbuf);
-                       memcpy(lptr, &tmp, sizeof(int));
-                   }
-                   break;
-               case t_uinteger:
-                   {
-                       unsigned int tmp = (unsigned int)atoi(valbuf);
-                       memcpy(lptr, &tmp, sizeof(unsigned int));
-                   }
-                   break;
-               case t_time:
-                   {
-                       double tmp = iso8601_to_unix(valbuf);
-                       memcpy(lptr, &tmp, sizeof(double));
-                   }
-                   break;
-               case t_real:
-                   {
-                       double tmp = safe_atof(valbuf);
-                       memcpy(lptr, &tmp, sizeof(double));
-                   }
-                   break;
-               case t_string:
-                   if (parent != NULL
-                       && parent->element_type != t_structobject
-                       && offset > 0)
-                       return JSON_ERR_NOPARSTR;
-                   (void)strncpy(lptr, valbuf, cursor->len);
-                   valbuf[sizeof(valbuf)-1] = '\0';
-                   break;
-               case t_boolean:
-                   {
-                       bool tmp = (strcmp(valbuf, "true") == 0);
-                       memcpy(lptr, &tmp, sizeof(bool));
-                   }
-                   break;
-               case t_character:
-                   if (strlen(valbuf) > 1)
-                       return JSON_ERR_STRLONG;
-                   else
-                       lptr[0] = valbuf[0];
-                   break;
-               case t_ignore:  /* silences a compiler warning */
-               case t_object:  /* silences a compiler warning */
-               case t_structobject:
-               case t_array:
-                   break;
-               case t_check:
-                   if (strcmp(cursor->dflt.check, valbuf) != 0) {
-                       json_debug_trace((1,
-                                         "Required attribute value %s not present.\n",
-                                         cursor->dflt.check));
-                       return JSON_ERR_CHECKFAIL;
-                   }
-                   break;
-               }
-           /*@fallthrough@*/
-       case post_array:
-           if (isspace((unsigned char) *cp))
-               continue;
-           else if (*cp == ',')
-               state = await_attr;
-           else if (*cp == '}') {
-               ++cp;
-               goto good_parse;
-           } else {
-               json_debug_trace((1, "Garbage while expecting comma or }\n"));
-               return JSON_ERR_BADTRAIL;
-           }
-           break;
-       }
-    }
-
-  good_parse:
-    /* in case there's another object following, consume trailing WS */
-    while (isspace((unsigned char) *cp))
-       ++cp;
-    if (end != NULL)
-       *end = cp;
-    json_debug_trace((1, "JSON parse ends.\n"));
-    return 0;
-    /*@ +nullstate +nullderef +mustfreefresh +nullpass +usedef @*/
-}
-
-int json_read_array(const char *cp, const struct json_array_t *arr,
-                   const char **end)
-{
-    /*@-nullstate -onlytrans@*/
-    int substatus, offset, arrcount;
-    char *tp;
-
-    if (end != NULL)
-       *end = NULL;            /* give it a well-defined value on parse failure */
-
-    json_debug_trace((1, "Entered json_read_array()\n"));
-
-    while (isspace((unsigned char) *cp))
-       cp++;
-    if (*cp != '[') {
-       json_debug_trace((1, "Didn't find expected array start\n"));
-       return JSON_ERR_ARRAYSTART;
-    } else
-       cp++;
-
-    tp = arr->arr.strings.store;
-    arrcount = 0;
-
-    /* Check for empty array */
-    while (isspace((unsigned char) *cp))
-       cp++;
-    if (*cp == ']')
-       goto breakout;
-
-    for (offset = 0; offset < arr->maxlen; offset++) {
-       char *ep = NULL;
-       json_debug_trace((1, "Looking at %s\n", cp));
-       switch (arr->element_type) {
-       case t_string:
-           if (isspace((unsigned char) *cp))
-               cp++;
-           if (*cp != '"')
-               return JSON_ERR_BADSTRING;
-           else
-               ++cp;
-           arr->arr.strings.ptrs[offset] = tp;
-           for (; tp - arr->arr.strings.store < arr->arr.strings.storelen;
-                tp++)
-               if (*cp == '"') {
-                   ++cp;
-                   *tp++ = '\0';
-                   goto stringend;
-               } else if (*cp == '\0') {
-                   json_debug_trace((1,
-                                     "Bad string syntax in string list.\n"));
-                   return JSON_ERR_BADSTRING;
-               } else {
-                   *tp = *cp++;
-               }
-           json_debug_trace((1, "Bad string syntax in string list.\n"));
-           return JSON_ERR_BADSTRING;
-         stringend:
-           break;
-       case t_object:
-       case t_structobject:
-           substatus =
-               json_internal_read_object(cp, arr->arr.objects.subtype, arr,
-                                         offset, &cp);
-           if (substatus != 0)
-               return substatus;
-           break;
-       case t_integer:
-           arr->arr.integers.store[offset] = (int)strtol(cp, &ep, 0);
-           if (ep == cp)
-               return JSON_ERR_BADNUM;
-           else
-               cp = ep;
-           break;
-       case t_uinteger:
-           arr->arr.uintegers.store[offset] = (unsigned int)strtoul(cp, &ep, 0);
-           if (ep == cp)
-               return JSON_ERR_BADNUM;
-           else
-               cp = ep;
-           break;
-       case t_real:
-           arr->arr.reals.store[offset] = strtod(cp, &ep);
-           if (ep == cp)
-               return JSON_ERR_BADNUM;
-           else
-               cp = ep;
-           break;
-       case t_boolean:
-           if (strncmp(cp, "true", 4) == 0) {
-               arr->arr.booleans.store[offset] = true;
-               cp += 4;
-           }
-           else if (strncmp(cp, "false", 5) == 0) {
-               arr->arr.booleans.store[offset] = false;
-               cp += 5;
-           }
-           break;
-       case t_time:
-       case t_character:
-       case t_array:
-       case t_check:
-       case t_ignore:
-           json_debug_trace((1, "Invalid array subtype.\n"));
-           return JSON_ERR_SUBTYPE;
-       }
-       arrcount++;
-       if (isspace((unsigned char) *cp))
-           cp++;
-       if (*cp == ']') {
-           json_debug_trace((1, "End of array found.\n"));
-           goto breakout;
-       } else if (*cp == ',')
-           cp++;
-       else {
-           json_debug_trace((1, "Bad trailing syntax on array.\n"));
-           return JSON_ERR_BADSUBTRAIL;
-       }
-    }
-    json_debug_trace((1, "Too many elements in array.\n"));
-    return JSON_ERR_SUBTOOLONG;
-  breakout:
-    if (arr->count != NULL)
-       *(arr->count) = arrcount;
-    if (end != NULL)
-       *end = cp;
-    /*@ -nullderef @*/
-    json_debug_trace((1, "leaving json_read_array() with %d elements\n",
-                     arrcount));
-    /*@ +nullderef @*/
-    return 0;
-    /*@+nullstate +onlytrans@*/
-}
-
-int json_read_object(const char *cp, const struct json_attr_t *attrs,
-                    /*@null@*/ const char **end)
-{
-    int st;
-
-    json_debug_trace((1, "json_read_object() sees '%s'\n", cp));
-    st = json_internal_read_object(cp, attrs, NULL, 0, end);
-    return st;
-}
-
-const /*@observer@*/ char *json_error_string(int err)
-{
-    const char *errors[] = {
-       "unknown error while parsing JSON",
-       "non-whitespace when expecting object start",
-       "non-whitespace when expecting attribute start",
-       "unknown attribute name",
-       "attribute name too long",
-       "saw [ when not expecting array",
-       "array element specified, but no [",
-       "string value too long",
-       "token value too long",
-       "garbage while expecting comma or } or ]",
-       "didn't find expected array start",
-       "error while parsing object array",
-       "too many array elements",
-       "garbage while expecting array comma",
-       "unsupported array element type",
-       "error while string parsing",
-       "check attribute not matched",
-       "can't support strings in parallel arrays",
-       "invalid enumerated value",
-       "saw quoted value when expecting nonstring",
-       "didn't see quoted value when expecting string",
-       "other data conversion error",
-       "unexpected null value or attribute pointer",
-    };
-
-    if (err <= 0 || err >= (int)(sizeof(errors) / sizeof(errors[0])))
-       return errors[0];
-    else
-       return errors[err];
-}
-
-/* end */
-
diff --git a/microjson.h b/microjson.h
deleted file mode 100644 (file)
index 8323d95..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/* Structures for JSON parsing using only fixed-extent memory
- *
- * This file is Copyright (c) 2014 by Eric S. Raymond.
- * BSD terms apply: see the file COPYING in the distribution root for details.
- */
-
-#include <stdbool.h>
-#include <ctype.h>
-
-#define NITEMS(x) (int)(sizeof(x)/sizeof(x[0]))
-
-typedef enum {t_integer, t_uinteger, t_real,
-             t_string, t_boolean, t_character,
-             t_time,
-             t_object, t_structobject, t_array,
-             t_check, t_ignore} 
-    json_type;
-
-struct json_enum_t {
-    char       *name;
-    int                value;
-};
-
-struct json_array_t {
-    json_type element_type;
-    union {
-       struct {
-           const struct json_attr_t *subtype;
-           char *base;
-           size_t stride;
-       } objects;
-       struct {
-           char **ptrs;
-           char *store;
-           int storelen;
-       } strings;
-       struct {
-           int *store;
-       } integers;
-       struct {
-           unsigned int *store;
-       } uintegers;
-       struct {
-           double *store;
-       } reals;
-       struct {
-           bool *store;
-       } booleans;
-    } arr;
-    int *count, maxlen;
-};
-
-struct json_attr_t {
-    char *attribute;
-    json_type type;
-    union {
-       int *integer;
-       unsigned int *uinteger;
-       double *real;
-       char *string;
-       bool *boolean;
-       char *character;
-       struct json_array_t array;
-       size_t offset;
-    } addr;
-    union {
-       int integer;
-       unsigned int uinteger;
-       double real;
-       bool boolean;
-       char character;
-       char *check;
-    } dflt;
-    size_t len;
-    const struct json_enum_t *map;
-    bool nodefault;
-};
-
-#define JSON_ATTR_MAX  31      /* max chars in JSON attribute name */
-#define JSON_VAL_MAX   512     /* max chars in JSON value part */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-int json_read_object(const char *, const struct json_attr_t *,
-                    /*@null@*/const char **);
-int json_read_array(const char *, const struct json_array_t *,
-                   /*@null@*/const char **);
-const /*@observer@*/char *json_error_string(int);
-
-void json_enable_debug(int, FILE *);
-#ifdef __cplusplus
-}
-#endif
-
-#define JSON_ERR_OBSTART       1       /* non-WS when expecting object start */
-#define JSON_ERR_ATTRSTART     2       /* non-WS when expecting attrib start */
-#define JSON_ERR_BADATTR       3       /* unknown attribute name */
-#define JSON_ERR_ATTRLEN       4       /* attribute name too long */
-#define JSON_ERR_NOARRAY       5       /* saw [ when not expecting array */
-#define JSON_ERR_NOBRAK        6       /* array element specified, but no [ */
-#define JSON_ERR_STRLONG       7       /* string value too long */
-#define JSON_ERR_TOKLONG       8       /* token value too long */
-#define JSON_ERR_BADTRAIL      9       /* garbage while expecting comma or } or ] */
-#define JSON_ERR_ARRAYSTART    10      /* didn't find expected array start */
-#define JSON_ERR_OBJARR        11      /* error while parsing object array */
-#define JSON_ERR_SUBTOOLONG    12      /* too many array elements */
-#define JSON_ERR_BADSUBTRAIL   13      /* garbage while expecting array comma */
-#define JSON_ERR_SUBTYPE       14      /* unsupported array element type */
-#define JSON_ERR_BADSTRING     15      /* error while string parsing */
-#define JSON_ERR_CHECKFAIL     16      /* check attribute not matched */
-#define JSON_ERR_NOPARSTR      17      /* can't support strings in parallel arrays */
-#define JSON_ERR_BADENUM       18      /* invalid enumerated value */
-#define JSON_ERR_QNONSTRING    19      /* saw quoted value when expecting nonstring */
-#define JSON_ERR_NONQSTRING    19      /* didn't see quoted value when expecting string */
-#define JSON_ERR_MISC          20      /* other data conversion error */
-#define JSON_ERR_BADNUM                21      /* error while parsing a numerical argument */
-#define JSON_ERR_NULLPTR       22      /* unexpected null value or attribute pointer */
-
-/*
- * Use the following macros to declare template initializers for structobject
- * arrays.  Writing the equivalents out by hand is error-prone.
- *
- * STRUCTOBJECT takes a structure name s, and a fieldname f in s.
- *
- * STRUCTARRAY takes the name of a structure array, a pointer to a an
- * initializer defining the subobject type, and the address of an integer to
- * store the length in.
- */
-#define STRUCTOBJECT(s, f)     .addr.offset = offsetof(s, f)
-#define STRUCTARRAY(a, e, n) \
-       .addr.array.element_type = t_structobject, \
-       .addr.array.arr.objects.subtype = e, \
-       .addr.array.arr.objects.base = (char*)a, \
-       .addr.array.arr.objects.stride = sizeof(a[0]), \
-       .addr.array.count = n, \
-       .addr.array.maxlen = (int)(sizeof(a)/sizeof(a[0]))
-
-/* json.h ends here */
diff --git a/mjson.c b/mjson.c
new file mode 100644 (file)
index 0000000..0bcef5e
--- /dev/null
+++ b/mjson.c
@@ -0,0 +1,966 @@
+/****************************************************************************
+
+NAME
+   mjson.c - parse JSON into fixed-extent data structures
+
+DESCRIPTION
+   This module parses a large subset of JSON (JavaScript Object
+Notation).  Unlike more general JSON parsers, it doesn't use malloc(3)
+and doesn't support polymorphism; you need to give it a set of
+template structures describing the expected shape of the incoming
+JSON, and it will error out if that shape is not matched.  When the
+parse succeeds, attribute values will be extracted into static
+locations specified in the template structures.
+
+   The "shape" of a JSON object in the type signature of its
+attributes (and attribute values, and so on recursively down through
+all nestings of objects and arrays).  This parser is indifferent to
+the order of attributes at any level, but you have to tell it in
+advance what the type of each attribute value will be and where the
+parsed value will be stored. The template structures may supply
+default values to be used when an expected attribute is omitted.
+
+   The preceding paragraph told one fib.  A single attribute may
+actually have a span of multiple specifications with different
+syntactically distinguishable types (e.g. string vs. real vs. integer
+vs. boolean, but not signed integer vs. unsigned integer).  The parser
+will match the right spec against the actual data.
+
+   The dialect this parses has some limitations.  First, it cannot
+recognize the JSON "null" value. Second, all elements of an array must
+be of the same type. Third, some types (t_time, t_character) may not
+ve array elements (this restriction could be lifted)
+
+   There are separate entry points for beginning a parse of either
+JSON object or a JSON array. JSON "float" quantities are actually
+stored as doubles.
+
+   This parser processes object arrays in one of two different ways,
+defending on whether the array subtype is declared as object or
+structobject.
+
+   Object arrays take one base address per object subfield, and are
+mapped into parallel C arrays (one per subfield).  Strings are not
+supported in this kind of array, as they don't have a "natural" size
+to use as an offset multiplier.
+
+   Structobjects arrays are a way to parse a list of objects to a set
+of modifications to a corresponding array of C structs.  The trick is
+that the array object initialization has to specify both the C struct
+array's base address and the stride length (the size of the C struct).
+If you initialize the offset fields with the correct offsetof calls,
+everything will work. Strings are supported but all string storage
+has to be inline in the struct.
+
+PERMISSIONS
+   This file is Copyright (c) 2014 by Eric S. Raymond
+   BSD terms apply: see the file COPYING in the distribution root for details.
+
+***************************************************************************/
+
+/* The strptime prototype is not provided unless explicitly requested.
+ * We also need to set the value high enough to signal inclusion of
+ * newer features (like clock_gettime).  See the POSIX spec for more info:
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_02_01_02 */
+#define _XOPEN_SOURCE 600
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "mjson.h"
+
+#ifdef DEBUG_ENABLE
+static int debuglevel = 0;
+static FILE *debugfp;
+
+void json_enable_debug(int level, FILE * fp)
+/* control the level and destination of debug trace messages */
+{
+    debuglevel = level;
+    debugfp = fp;
+}
+
+static void json_trace(int errlevel, const char *fmt, ...)
+/* assemble command in printf(3) style */
+{
+    if (errlevel <= debuglevel) {
+       char buf[BUFSIZ];
+       va_list ap;
+
+       (void)strncpy(buf, "json: ", BUFSIZ-1);
+       buf[BUFSIZ-1] = '\0';
+       va_start(ap, fmt);
+       (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt,
+                       ap);
+       va_end(ap);
+
+       (void)fputs(buf, debugfp);
+    }
+}
+
+# define json_debug_trace(args) (void) json_trace args
+#else
+# define json_debug_trace(args) /*@i1@*/do { } while (0)
+#endif /* DEBUG_ENABLE */
+
+/*
+ * Berkeley implementation of strtod(), inlined to avoid locale problems
+ * with the decimal point and stripped down to an atof()-equivalent.
+ */
+
+/*@-shiftimplementation +charint@*/
+static double safe_atof(const char *string)
+/* Takes a decimal ASCII floating-point number, optionally
+ * preceded by white space.  Must have form "-I.FE-X",
+ * where I is the integer part of the mantissa, F is
+ * the fractional part of the mantissa, and X is the
+ * exponent.  Either of the signs may be "+", "-", or
+ * omitted.  Either I or F may be omitted, or both.
+ * The decimal point isn't necessary unless F is
+ * present.  The "E" may actually be an "e".  E and X
+ * may both be omitted (but not just one).
+ */
+{
+    static int maxExponent = 511;   /* Largest possible base 10 exponent.  Any
+                                    * exponent larger than this will already
+                                    * produce underflow or overflow, so there's
+                                    * no need to worry about additional digits.
+                                    */
+    static double powersOf10[] = {  /* Table giving binary powers of 10.  Entry */
+       10.,                    /* is 10^2^i.  Used to convert decimal */
+       100.,                   /* exponents into floating-point numbers. */
+       1.0e4,
+       1.0e8,
+       1.0e16,
+       1.0e32,
+       1.0e64,
+       1.0e128,
+       1.0e256
+    };
+
+    bool sign, expSign = false;
+    double fraction, dblExp, *d;
+    register const char *p;
+    register int c;
+    int exp = 0;               /* Exponent read from "EX" field. */
+    int fracExp = 0;           /* Exponent that derives from the fractional
+                                * part.  Under normal circumstatnces, it is
+                                * the negative of the number of digits in F.
+                                * However, if I is very long, the last digits
+                                * of I get dropped (otherwise a long I with a
+                                * large negative exponent could cause an
+                                * unnecessary overflow on I alone).  In this
+                                * case, fracExp is incremented one for each
+                                * dropped digit. */
+    int mantSize;              /* Number of digits in mantissa. */
+    int decPt;                 /* Number of mantissa digits BEFORE decimal
+                                * point. */
+    const char *pExp;          /* Temporarily holds location of exponent
+                                * in string. */
+
+    /*
+     * Strip off leading blanks and check for a sign.
+     */
+
+    p = string;
+    while (isspace((unsigned char) *p)) {
+       p += 1;
+    }
+    if (*p == '-') {
+       sign = true;
+       p += 1;
+    } else {
+       if (*p == '+') {
+           p += 1;
+       }
+       sign = false;
+    }
+
+    /*
+     * Count the number of digits in the mantissa (including the decimal
+     * point), and also locate the decimal point.
+     */
+
+    decPt = -1;
+    for (mantSize = 0; ; mantSize += 1)
+    {
+       c = *p;
+       if (!isdigit(c)) {
+           if ((c != '.') || (decPt >= 0)) {
+               break;
+           }
+           decPt = mantSize;
+       }
+       p += 1;
+    }
+
+    /*
+     * Now suck up the digits in the mantissa.  Use two integers to
+     * collect 9 digits each (this is faster than using floating-point).
+     * If the mantissa has more than 18 digits, ignore the extras, since
+     * they can't affect the value anyway.
+     */
+
+    pExp  = p;
+    p -= mantSize;
+    if (decPt < 0) {
+       decPt = mantSize;
+    } else {
+       mantSize -= 1;                  /* One of the digits was the point. */
+    }
+    if (mantSize > 18) {
+       fracExp = decPt - 18;
+       mantSize = 18;
+    } else {
+       fracExp = decPt - mantSize;
+    }
+    if (mantSize == 0) {
+       fraction = 0.0;
+       //p = string;
+       goto done;
+    } else {
+       int frac1, frac2;
+       frac1 = 0;
+       for ( ; mantSize > 9; mantSize -= 1)
+       {
+           c = *p;
+           p += 1;
+           if (c == '.') {
+               c = *p;
+               p += 1;
+           }
+           frac1 = 10*frac1 + (c - '0');
+       }
+       frac2 = 0;
+       for (; mantSize > 0; mantSize -= 1)
+       {
+           c = *p;
+           p += 1;
+           if (c == '.') {
+               c = *p;
+               p += 1;
+           }
+           frac2 = 10*frac2 + (c - '0');
+       }
+       fraction = (1.0e9 * frac1) + frac2;
+    }
+
+    /*
+     * Skim off the exponent.
+     */
+
+    p = pExp;
+    if ((*p == 'E') || (*p == 'e')) {
+       p += 1;
+       if (*p == '-') {
+           expSign = true;
+           p += 1;
+       } else {
+           if (*p == '+') {
+               p += 1;
+           }
+           expSign = false;
+       }
+       while (isdigit((unsigned char) *p)) {
+           exp = exp * 10 + (*p - '0');
+           p += 1;
+       }
+    }
+    if (expSign) {
+       exp = fracExp - exp;
+    } else {
+       exp = fracExp + exp;
+    }
+
+    /*
+     * Generate a floating-point number that represents the exponent.
+     * Do this by processing the exponent one bit at a time to combine
+     * many powers of 2 of 10. Then combine the exponent with the
+     * fraction.
+     */
+
+    if (exp < 0) {
+       expSign = true;
+       exp = -exp;
+    } else {
+       expSign = false;
+    }
+    if (exp > maxExponent) {
+       exp = maxExponent;
+       errno = ERANGE;
+    }
+    dblExp = 1.0;
+    for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
+       if (exp & 01) {
+           dblExp *= *d;
+       }
+    }
+    if (expSign) {
+       fraction /= dblExp;
+    } else {
+       fraction *= dblExp;
+    }
+
+done:
+    if (sign) {
+       return -fraction;
+    }
+    return fraction;
+}
+/*@+shiftimplementation -charint@*/
+
+#define MONTHSPERYEAR  12      /* months per calendar year */
+
+static time_t mkutctime(register struct tm * t)
+/* struct tm to seconds since Unix epoch */
+{
+    register int year;
+    register time_t result;
+    static const int cumdays[MONTHSPERYEAR] =
+       { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+    /*@ +matchanyintegral @*/
+    year = 1900 + t->tm_year + t->tm_mon / MONTHSPERYEAR;
+    result = (year - 1970) * 365 + cumdays[t->tm_mon % MONTHSPERYEAR];
+    result += (year - 1968) / 4;
+    result -= (year - 1900) / 100;
+    result += (year - 1600) / 400;
+    if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) &&
+       (t->tm_mon % MONTHSPERYEAR) < 2)
+       result--;
+    result += t->tm_mday - 1;
+    result *= 24;
+    result += t->tm_hour;
+    result *= 60;
+    result += t->tm_min;
+    result *= 60;
+    result += t->tm_sec;
+    /*@ -matchanyintegral @*/
+    return (result);
+}
+
+static double iso8601_to_unix(/*@in@*/const char *isotime)
+/* ISO8601 UTC to Unix UTC */
+{
+    char *dp = NULL;
+    double usec;
+    struct tm tm;
+
+    /*@i1@*/ dp = strptime(isotime, "%Y-%m-%dT%H:%M:%S", &tm);
+    if (dp != NULL && *dp == '.')
+       usec = strtod(dp, NULL);
+    else
+       usec = 0;
+    return mkutctime(&tm) + usec;
+}
+
+/*@-immediatetrans -dependenttrans -usereleased -compdef@*/
+static /*@null@*/ char *json_target_address(const struct json_attr_t *cursor,
+                                            /*@null@*/
+                                            const struct json_array_t
+                                            *parent, int offset)
+{
+    char *targetaddr = NULL;
+    if (parent == NULL || parent->element_type != t_structobject) {
+       /* ordinary case - use the address in the cursor structure */
+       switch (cursor->type) {
+       case t_ignore:
+           targetaddr = NULL;
+           break;
+       case t_integer:
+           targetaddr = (char *)&cursor->addr.integer[offset];
+           break;
+       case t_uinteger:
+           targetaddr = (char *)&cursor->addr.uinteger[offset];
+           break;
+       case t_time:
+       case t_real:
+           targetaddr = (char *)&cursor->addr.real[offset];
+           break;
+       case t_string:
+           targetaddr = cursor->addr.string;
+           break;
+       case t_boolean:
+           targetaddr = (char *)&cursor->addr.boolean[offset];
+           break;
+       case t_character:
+           targetaddr = (char *)&cursor->addr.character[offset];
+           break;
+       default:
+           targetaddr = NULL;
+           break;
+       }
+    } else
+       /* tricky case - hacking a member in an array of structures */
+       targetaddr =
+           parent->arr.objects.base + (offset * parent->arr.objects.stride) +
+           cursor->addr.offset;
+    json_debug_trace((1, "Target address for %s (offset %d) is %p\n",
+                     cursor->attribute, offset, targetaddr));
+    return targetaddr;
+}
+
+/*@-immediatetrans -dependenttrans +usereleased +compdef@*/
+
+static int json_internal_read_object(const char *cp,
+                                    const struct json_attr_t *attrs,
+                                    /*@null@*/
+                                    const struct json_array_t *parent,
+                                    int offset,
+                                    /*@null@*/ const char **end)
+{
+    /*@ -nullstate -nullderef -mustfreefresh -nullpass -usedef @*/
+    enum
+    { init, await_attr, in_attr, await_value, in_val_string,
+       in_escape, in_val_token, post_val, post_array
+    } state = 0;
+#ifdef DEBUG_ENABLE
+    char *statenames[] = {
+       "init", "await_attr", "in_attr", "await_value", "in_val_string",
+       "in_escape", "in_val_token", "post_val", "post_array",
+    };
+#endif /* DEBUG_ENABLE */
+    char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL;
+    char valbuf[JSON_VAL_MAX + 1], *pval = NULL;
+    bool value_quoted = false;
+    char uescape[5];           /* enough space for 4 hex digits and a NUL */
+    const struct json_attr_t *cursor;
+    int substatus, n, maxlen = 0;
+    unsigned int u;
+    const struct json_enum_t *mp;
+    char *lptr;
+
+#ifdef S_SPLINT_S
+    /* prevents gripes about buffers not being completely defined */
+    memset(valbuf, '\0', sizeof(valbuf));
+    memset(attrbuf, '\0', sizeof(attrbuf));
+#endif /* S_SPLINT_S */
+
+    if (end != NULL)
+       *end = NULL;            /* give it a well-defined value on parse failure */
+
+    /* stuff fields with defaults in case they're omitted in the JSON input */
+    for (cursor = attrs; cursor->attribute != NULL; cursor++)
+       if (!cursor->nodefault) {
+           lptr = json_target_address(cursor, parent, offset);
+           if (lptr != NULL)
+               switch (cursor->type) {
+               case t_integer:
+                   memcpy(lptr, &cursor->dflt.integer, sizeof(int));
+                   break;
+               case t_uinteger:
+                   memcpy(lptr, &cursor->dflt.uinteger, sizeof(unsigned int));
+                   break;
+               case t_time:
+               case t_real:
+                   memcpy(lptr, &cursor->dflt.real, sizeof(double));
+                   break;
+               case t_string:
+                   if (parent != NULL
+                       && parent->element_type != t_structobject
+                       && offset > 0)
+                       return JSON_ERR_NOPARSTR;
+                   lptr[0] = '\0';
+                   break;
+               case t_boolean:
+                   memcpy(lptr, &cursor->dflt.boolean, sizeof(bool));
+                   break;
+               case t_character:
+                   lptr[0] = cursor->dflt.character;
+                   break;
+               case t_object:  /* silences a compiler warning */
+               case t_structobject:
+               case t_array:
+               case t_check:
+               case t_ignore:
+                   break;
+               }
+       }
+
+    json_debug_trace((1, "JSON parse of '%s' begins.\n", cp));
+
+    /* parse input JSON */
+    for (; *cp != '\0'; cp++) {
+       json_debug_trace((2, "State %-14s, looking at '%c' (%p)\n",
+                         statenames[state], *cp, cp));
+       switch (state) {
+       case init:
+           if (isspace((unsigned char) *cp))
+               continue;
+           else if (*cp == '{')
+               state = await_attr;
+           else {
+               json_debug_trace((1,
+                                 "Non-WS when expecting object start.\n"));
+               return JSON_ERR_OBSTART;
+           }
+           break;
+       case await_attr:
+           if (isspace((unsigned char) *cp))
+               continue;
+           else if (*cp == '"') {
+               state = in_attr;
+               pattr = attrbuf;
+           } else if (*cp == '}')
+               break;
+           else {
+               json_debug_trace((1, "Non-WS when expecting attribute.\n"));
+               return JSON_ERR_ATTRSTART;
+           }
+           break;
+       case in_attr:
+           if (pattr == NULL)
+               return JSON_ERR_NULLPTR;
+           if (*cp == '"') {
+               *pattr++ = '\0';
+               json_debug_trace((1, "Collected attribute name %s\n",
+                                 attrbuf));
+               for (cursor = attrs; cursor->attribute != NULL; cursor++) {
+                   json_debug_trace((2, "Checking against %s\n",
+                                     cursor->attribute));
+                   if (strcmp(cursor->attribute, attrbuf) == 0)
+                       break;
+               }
+               if (cursor->attribute == NULL) {
+                   json_debug_trace((1,
+                                     "Unknown attribute name '%s' (attributes begin with '%s').\n",
+                                     attrbuf, attrs->attribute));
+                   return JSON_ERR_BADATTR;
+               }
+               state = await_value;
+               if (cursor->type == t_string)
+                   maxlen = (int)cursor->len - 1;
+               else if (cursor->type == t_check)
+                   maxlen = (int)strlen(cursor->dflt.check);
+               else if (cursor->type == t_time || cursor->type == t_ignore)
+                   maxlen = JSON_VAL_MAX;
+               else if (cursor->map != NULL)
+                   maxlen = (int)sizeof(valbuf) - 1;
+               pval = valbuf;
+           } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) {
+               json_debug_trace((1, "Attribute name too long.\n"));
+               return JSON_ERR_ATTRLEN;
+           } else
+               *pattr++ = *cp;
+           break;
+       case await_value:
+           if (isspace((unsigned char) *cp) || *cp == ':')
+               continue;
+           else if (*cp == '[') {
+               if (cursor->type != t_array) {
+                   json_debug_trace((1,
+                                     "Saw [ when not expecting array.\n"));
+                   return JSON_ERR_NOARRAY;
+               }
+               substatus = json_read_array(cp, &cursor->addr.array, &cp);
+               if (substatus != 0)
+                   return substatus;
+               state = post_array;
+           } else if (cursor->type == t_array) {
+               json_debug_trace((1,
+                                 "Array element was specified, but no [.\n"));
+               return JSON_ERR_NOBRAK;
+           } else if (*cp == '"') {
+               value_quoted = true;
+               state = in_val_string;
+               pval = valbuf;
+           } else {
+               value_quoted = false;
+               state = in_val_token;
+               pval = valbuf;
+               *pval++ = *cp;
+           }
+           break;
+       case in_val_string:
+           if (pval == NULL)
+               return JSON_ERR_NULLPTR;
+           if (*cp == '\\')
+               state = in_escape;
+           else if (*cp == '"') {
+               *pval++ = '\0';
+               json_debug_trace((1, "Collected string value %s\n", valbuf));
+               state = post_val;
+           } else if (pval > valbuf + JSON_VAL_MAX - 1
+                      || pval > valbuf + maxlen) {
+               json_debug_trace((1, "String value too long.\n"));
+               return JSON_ERR_STRLONG;        /*  */
+           } else
+               *pval++ = *cp;
+           break;
+       case in_escape:
+           if (pval == NULL)
+               return JSON_ERR_NULLPTR;
+           switch (*cp) {
+           case 'b':
+               *pval++ = '\b';
+               break;
+           case 'f':
+               *pval++ = '\f';
+               break;
+           case 'n':
+               *pval++ = '\n';
+               break;
+           case 'r':
+               *pval++ = '\r';
+               break;
+           case 't':
+               *pval++ = '\t';
+               break;
+           case 'u':
+               for (n = 0; n < 4 && cp[n] != '\0'; n++)
+                   uescape[n] = *cp++;
+               --cp;
+               (void)sscanf(uescape, "%04x", &u);
+               *pval++ = (char)u;      /* will truncate values above 0xff */
+               break;
+           default:            /* handles double quote and solidus */
+               *pval++ = *cp;
+               break;
+           }
+           state = in_val_string;
+           break;
+       case in_val_token:
+           if (pval == NULL)
+               return JSON_ERR_NULLPTR;
+           if (isspace((unsigned char) *cp) || *cp == ',' || *cp == '}') {
+               *pval = '\0';
+               json_debug_trace((1, "Collected token value %s.\n", valbuf));
+               state = post_val;
+               if (*cp == '}' || *cp == ',')
+                   --cp;
+           } else if (pval > valbuf + JSON_VAL_MAX - 1) {
+               json_debug_trace((1, "Token value too long.\n"));
+               return JSON_ERR_TOKLONG;
+           } else
+               *pval++ = *cp;
+           break;
+       case post_val:
+           /*
+            * We know that cursor points at the first spec matching
+            * the current attribute.  We don't know that it's *the*
+            * correct spec; our dialect allows there to be any number
+            * of adjacent ones with the same attrname but different
+            * types.  Here's where we try to seek forward for a
+            * matching type/attr pair if we're not looking at one.
+            */
+           for (;;) {
+               int seeking = cursor->type;
+               if (value_quoted && (cursor->type == t_string || cursor->type == t_time))
+                   break;
+               if ((strcmp(valbuf, "true")==0 || strcmp(valbuf, "false")==0)
+                       && seeking == t_boolean)
+                   break;
+               if (isdigit((unsigned char) valbuf[0])) {
+                   bool decimal = strchr(valbuf, '.') != NULL;
+                   if (decimal && seeking == t_real)
+                       break;
+                   if (!decimal && (seeking == t_integer || seeking == t_uinteger))
+                       break;
+               }
+               if (cursor[1].attribute==NULL)  /* out of possiblities */
+                   break;
+               if (strcmp(cursor[1].attribute, attrbuf)!=0)
+                   break;
+               ++cursor;
+           }
+           if (value_quoted
+               && (cursor->type != t_string && cursor->type != t_character
+                   && cursor->type != t_check && cursor->type != t_time
+                   && cursor->type != t_ignore && cursor->map == 0)) {
+               json_debug_trace((1,
+                                 "Saw quoted value when expecting non-string.\n"));
+               return JSON_ERR_QNONSTRING;
+           }
+           if (!value_quoted
+               && (cursor->type == t_string || cursor->type == t_check
+                   || cursor->type == t_time || cursor->map != 0)) {
+               json_debug_trace((1,
+                                 "Didn't see quoted value when expecting string.\n"));
+               return JSON_ERR_NONQSTRING;
+           }
+           if (cursor->map != 0) {
+               for (mp = cursor->map; mp->name != NULL; mp++)
+                   if (strcmp(mp->name, valbuf) == 0) {
+                       goto foundit;
+                   }
+               json_debug_trace((1, "Invalid enumerated value string %s.\n",
+                                 valbuf));
+               return JSON_ERR_BADENUM;
+             foundit:
+               (void)snprintf(valbuf, sizeof(valbuf), "%d", mp->value);
+           }
+           lptr = json_target_address(cursor, parent, offset);
+           if (lptr != NULL)
+               switch (cursor->type) {
+               case t_integer:
+                   {
+                       int tmp = atoi(valbuf);
+                       memcpy(lptr, &tmp, sizeof(int));
+                   }
+                   break;
+               case t_uinteger:
+                   {
+                       unsigned int tmp = (unsigned int)atoi(valbuf);
+                       memcpy(lptr, &tmp, sizeof(unsigned int));
+                   }
+                   break;
+               case t_time:
+                   {
+                       double tmp = iso8601_to_unix(valbuf);
+                       memcpy(lptr, &tmp, sizeof(double));
+                   }
+                   break;
+               case t_real:
+                   {
+                       double tmp = safe_atof(valbuf);
+                       memcpy(lptr, &tmp, sizeof(double));
+                   }
+                   break;
+               case t_string:
+                   if (parent != NULL
+                       && parent->element_type != t_structobject
+                       && offset > 0)
+                       return JSON_ERR_NOPARSTR;
+                   (void)strncpy(lptr, valbuf, cursor->len);
+                   valbuf[sizeof(valbuf)-1] = '\0';
+                   break;
+               case t_boolean:
+                   {
+                       bool tmp = (strcmp(valbuf, "true") == 0);
+                       memcpy(lptr, &tmp, sizeof(bool));
+                   }
+                   break;
+               case t_character:
+                   if (strlen(valbuf) > 1)
+                       return JSON_ERR_STRLONG;
+                   else
+                       lptr[0] = valbuf[0];
+                   break;
+               case t_ignore:  /* silences a compiler warning */
+               case t_object:  /* silences a compiler warning */
+               case t_structobject:
+               case t_array:
+                   break;
+               case t_check:
+                   if (strcmp(cursor->dflt.check, valbuf) != 0) {
+                       json_debug_trace((1,
+                                         "Required attribute value %s not present.\n",
+                                         cursor->dflt.check));
+                       return JSON_ERR_CHECKFAIL;
+                   }
+                   break;
+               }
+           /*@fallthrough@*/
+       case post_array:
+           if (isspace((unsigned char) *cp))
+               continue;
+           else if (*cp == ',')
+               state = await_attr;
+           else if (*cp == '}') {
+               ++cp;
+               goto good_parse;
+           } else {
+               json_debug_trace((1, "Garbage while expecting comma or }\n"));
+               return JSON_ERR_BADTRAIL;
+           }
+           break;
+       }
+    }
+
+  good_parse:
+    /* in case there's another object following, consume trailing WS */
+    while (isspace((unsigned char) *cp))
+       ++cp;
+    if (end != NULL)
+       *end = cp;
+    json_debug_trace((1, "JSON parse ends.\n"));
+    return 0;
+    /*@ +nullstate +nullderef +mustfreefresh +nullpass +usedef @*/
+}
+
+int json_read_array(const char *cp, const struct json_array_t *arr,
+                   const char **end)
+{
+    /*@-nullstate -onlytrans@*/
+    int substatus, offset, arrcount;
+    char *tp;
+
+    if (end != NULL)
+       *end = NULL;            /* give it a well-defined value on parse failure */
+
+    json_debug_trace((1, "Entered json_read_array()\n"));
+
+    while (isspace((unsigned char) *cp))
+       cp++;
+    if (*cp != '[') {
+       json_debug_trace((1, "Didn't find expected array start\n"));
+       return JSON_ERR_ARRAYSTART;
+    } else
+       cp++;
+
+    tp = arr->arr.strings.store;
+    arrcount = 0;
+
+    /* Check for empty array */
+    while (isspace((unsigned char) *cp))
+       cp++;
+    if (*cp == ']')
+       goto breakout;
+
+    for (offset = 0; offset < arr->maxlen; offset++) {
+       char *ep = NULL;
+       json_debug_trace((1, "Looking at %s\n", cp));
+       switch (arr->element_type) {
+       case t_string:
+           if (isspace((unsigned char) *cp))
+               cp++;
+           if (*cp != '"')
+               return JSON_ERR_BADSTRING;
+           else
+               ++cp;
+           arr->arr.strings.ptrs[offset] = tp;
+           for (; tp - arr->arr.strings.store < arr->arr.strings.storelen;
+                tp++)
+               if (*cp == '"') {
+                   ++cp;
+                   *tp++ = '\0';
+                   goto stringend;
+               } else if (*cp == '\0') {
+                   json_debug_trace((1,
+                                     "Bad string syntax in string list.\n"));
+                   return JSON_ERR_BADSTRING;
+               } else {
+                   *tp = *cp++;
+               }
+           json_debug_trace((1, "Bad string syntax in string list.\n"));
+           return JSON_ERR_BADSTRING;
+         stringend:
+           break;
+       case t_object:
+       case t_structobject:
+           substatus =
+               json_internal_read_object(cp, arr->arr.objects.subtype, arr,
+                                         offset, &cp);
+           if (substatus != 0)
+               return substatus;
+           break;
+       case t_integer:
+           arr->arr.integers.store[offset] = (int)strtol(cp, &ep, 0);
+           if (ep == cp)
+               return JSON_ERR_BADNUM;
+           else
+               cp = ep;
+           break;
+       case t_uinteger:
+           arr->arr.uintegers.store[offset] = (unsigned int)strtoul(cp, &ep, 0);
+           if (ep == cp)
+               return JSON_ERR_BADNUM;
+           else
+               cp = ep;
+           break;
+       case t_real:
+           arr->arr.reals.store[offset] = strtod(cp, &ep);
+           if (ep == cp)
+               return JSON_ERR_BADNUM;
+           else
+               cp = ep;
+           break;
+       case t_boolean:
+           if (strncmp(cp, "true", 4) == 0) {
+               arr->arr.booleans.store[offset] = true;
+               cp += 4;
+           }
+           else if (strncmp(cp, "false", 5) == 0) {
+               arr->arr.booleans.store[offset] = false;
+               cp += 5;
+           }
+           break;
+       case t_time:
+       case t_character:
+       case t_array:
+       case t_check:
+       case t_ignore:
+           json_debug_trace((1, "Invalid array subtype.\n"));
+           return JSON_ERR_SUBTYPE;
+       }
+       arrcount++;
+       if (isspace((unsigned char) *cp))
+           cp++;
+       if (*cp == ']') {
+           json_debug_trace((1, "End of array found.\n"));
+           goto breakout;
+       } else if (*cp == ',')
+           cp++;
+       else {
+           json_debug_trace((1, "Bad trailing syntax on array.\n"));
+           return JSON_ERR_BADSUBTRAIL;
+       }
+    }
+    json_debug_trace((1, "Too many elements in array.\n"));
+    return JSON_ERR_SUBTOOLONG;
+  breakout:
+    if (arr->count != NULL)
+       *(arr->count) = arrcount;
+    if (end != NULL)
+       *end = cp;
+    /*@ -nullderef @*/
+    json_debug_trace((1, "leaving json_read_array() with %d elements\n",
+                     arrcount));
+    /*@ +nullderef @*/
+    return 0;
+    /*@+nullstate +onlytrans@*/
+}
+
+int json_read_object(const char *cp, const struct json_attr_t *attrs,
+                    /*@null@*/ const char **end)
+{
+    int st;
+
+    json_debug_trace((1, "json_read_object() sees '%s'\n", cp));
+    st = json_internal_read_object(cp, attrs, NULL, 0, end);
+    return st;
+}
+
+const /*@observer@*/ char *json_error_string(int err)
+{
+    const char *errors[] = {
+       "unknown error while parsing JSON",
+       "non-whitespace when expecting object start",
+       "non-whitespace when expecting attribute start",
+       "unknown attribute name",
+       "attribute name too long",
+       "saw [ when not expecting array",
+       "array element specified, but no [",
+       "string value too long",
+       "token value too long",
+       "garbage while expecting comma or } or ]",
+       "didn't find expected array start",
+       "error while parsing object array",
+       "too many array elements",
+       "garbage while expecting array comma",
+       "unsupported array element type",
+       "error while string parsing",
+       "check attribute not matched",
+       "can't support strings in parallel arrays",
+       "invalid enumerated value",
+       "saw quoted value when expecting nonstring",
+       "didn't see quoted value when expecting string",
+       "other data conversion error",
+       "unexpected null value or attribute pointer",
+    };
+
+    if (err <= 0 || err >= (int)(sizeof(errors) / sizeof(errors[0])))
+       return errors[0];
+    else
+       return errors[err];
+}
+
+/* end */
+
diff --git a/mjson.h b/mjson.h
new file mode 100644 (file)
index 0000000..8323d95
--- /dev/null
+++ b/mjson.h
@@ -0,0 +1,139 @@
+/* Structures for JSON parsing using only fixed-extent memory
+ *
+ * This file is Copyright (c) 2014 by Eric S. Raymond.
+ * BSD terms apply: see the file COPYING in the distribution root for details.
+ */
+
+#include <stdbool.h>
+#include <ctype.h>
+
+#define NITEMS(x) (int)(sizeof(x)/sizeof(x[0]))
+
+typedef enum {t_integer, t_uinteger, t_real,
+             t_string, t_boolean, t_character,
+             t_time,
+             t_object, t_structobject, t_array,
+             t_check, t_ignore} 
+    json_type;
+
+struct json_enum_t {
+    char       *name;
+    int                value;
+};
+
+struct json_array_t {
+    json_type element_type;
+    union {
+       struct {
+           const struct json_attr_t *subtype;
+           char *base;
+           size_t stride;
+       } objects;
+       struct {
+           char **ptrs;
+           char *store;
+           int storelen;
+       } strings;
+       struct {
+           int *store;
+       } integers;
+       struct {
+           unsigned int *store;
+       } uintegers;
+       struct {
+           double *store;
+       } reals;
+       struct {
+           bool *store;
+       } booleans;
+    } arr;
+    int *count, maxlen;
+};
+
+struct json_attr_t {
+    char *attribute;
+    json_type type;
+    union {
+       int *integer;
+       unsigned int *uinteger;
+       double *real;
+       char *string;
+       bool *boolean;
+       char *character;
+       struct json_array_t array;
+       size_t offset;
+    } addr;
+    union {
+       int integer;
+       unsigned int uinteger;
+       double real;
+       bool boolean;
+       char character;
+       char *check;
+    } dflt;
+    size_t len;
+    const struct json_enum_t *map;
+    bool nodefault;
+};
+
+#define JSON_ATTR_MAX  31      /* max chars in JSON attribute name */
+#define JSON_VAL_MAX   512     /* max chars in JSON value part */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int json_read_object(const char *, const struct json_attr_t *,
+                    /*@null@*/const char **);
+int json_read_array(const char *, const struct json_array_t *,
+                   /*@null@*/const char **);
+const /*@observer@*/char *json_error_string(int);
+
+void json_enable_debug(int, FILE *);
+#ifdef __cplusplus
+}
+#endif
+
+#define JSON_ERR_OBSTART       1       /* non-WS when expecting object start */
+#define JSON_ERR_ATTRSTART     2       /* non-WS when expecting attrib start */
+#define JSON_ERR_BADATTR       3       /* unknown attribute name */
+#define JSON_ERR_ATTRLEN       4       /* attribute name too long */
+#define JSON_ERR_NOARRAY       5       /* saw [ when not expecting array */
+#define JSON_ERR_NOBRAK        6       /* array element specified, but no [ */
+#define JSON_ERR_STRLONG       7       /* string value too long */
+#define JSON_ERR_TOKLONG       8       /* token value too long */
+#define JSON_ERR_BADTRAIL      9       /* garbage while expecting comma or } or ] */
+#define JSON_ERR_ARRAYSTART    10      /* didn't find expected array start */
+#define JSON_ERR_OBJARR        11      /* error while parsing object array */
+#define JSON_ERR_SUBTOOLONG    12      /* too many array elements */
+#define JSON_ERR_BADSUBTRAIL   13      /* garbage while expecting array comma */
+#define JSON_ERR_SUBTYPE       14      /* unsupported array element type */
+#define JSON_ERR_BADSTRING     15      /* error while string parsing */
+#define JSON_ERR_CHECKFAIL     16      /* check attribute not matched */
+#define JSON_ERR_NOPARSTR      17      /* can't support strings in parallel arrays */
+#define JSON_ERR_BADENUM       18      /* invalid enumerated value */
+#define JSON_ERR_QNONSTRING    19      /* saw quoted value when expecting nonstring */
+#define JSON_ERR_NONQSTRING    19      /* didn't see quoted value when expecting string */
+#define JSON_ERR_MISC          20      /* other data conversion error */
+#define JSON_ERR_BADNUM                21      /* error while parsing a numerical argument */
+#define JSON_ERR_NULLPTR       22      /* unexpected null value or attribute pointer */
+
+/*
+ * Use the following macros to declare template initializers for structobject
+ * arrays.  Writing the equivalents out by hand is error-prone.
+ *
+ * STRUCTOBJECT takes a structure name s, and a fieldname f in s.
+ *
+ * STRUCTARRAY takes the name of a structure array, a pointer to a an
+ * initializer defining the subobject type, and the address of an integer to
+ * store the length in.
+ */
+#define STRUCTOBJECT(s, f)     .addr.offset = offsetof(s, f)
+#define STRUCTARRAY(a, e, n) \
+       .addr.array.element_type = t_structobject, \
+       .addr.array.arr.objects.subtype = e, \
+       .addr.array.arr.objects.base = (char*)a, \
+       .addr.array.arr.objects.stride = sizeof(a[0]), \
+       .addr.array.count = n, \
+       .addr.array.maxlen = (int)(sizeof(a)/sizeof(a[0]))
+
+/* json.h ends here */
index 70c9cb255311e12257b1134fe162dd51d515d8c5..5b73ec629655b424979ff91b536bde6e76db43ca 100644 (file)
@@ -1,4 +1,4 @@
-/* test_microjson.c - unit test for JSON parsing into fixed-extent structures
+/* test_mjson.c - unit test for JSON parsing into fixed-extent structures
  *
  * This file is Copyright (c) 2010 by the GPSD project
  * BSD terms apply: see the file COPYING in the distribution root for details.
@@ -17,7 +17,7 @@
 #include <math.h>
 #include <stddef.h>
 
-#include "microjson.h"
+#include "mjson.h"
 
 /*
  * Many of these structures and examples were dissected out of the GPSD code.