Import JSON parsing / formatting helper code based on code from
http://mjson.sourceforge.net/
with some API changes to better cope with libvirt's needs.
---
.x-sc_prohibit_strncpy | 1 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 11 +
src/util/json.c | 3916 ++++++++++++++++++++++++++++++++++++++++++++++
src/util/json.h | 313 ++++
5 files changed, 4242 insertions(+), 0 deletions(-)
create mode 100644 src/util/json.c
create mode 100644 src/util/json.h
diff --git a/.x-sc_prohibit_strncpy b/.x-sc_prohibit_strncpy
index 70e2300..806217e 100644
--- a/.x-sc_prohibit_strncpy
+++ b/.x-sc_prohibit_strncpy
@@ -1 +1,2 @@
+^src/util/json\.c$
^src/util/util\.c$
diff --git a/src/Makefile.am b/src/Makefile.am
index 9d9ab44..9660a99 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,7 @@ UTIL_SOURCES = \
util/event.c util/event.h \
util/hash.c util/hash.h \
util/iptables.c util/iptables.h \
+ util/json.c util/json.h \
util/logging.c util/logging.h \
util/memory.c util/memory.h \
util/pci.c util/pci.h \
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 52f1a05..7ef5abe 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -260,6 +260,17 @@ virRegisterDeviceMonitor;
virRegisterSecretDriver;
+# json.h
+json_tree_to_string;
+json_parse_document;
+json_new_string;
+json_new_object;
+json_free_value;
+json_insert_pair_into_object;
+json_insert_string_pair_into_object;
+json_find_first_label;
+
+
# logging.h
virLogMessage;
virLogGetNbFilters;
diff --git a/src/util/json.c b/src/util/json.c
new file mode 100644
index 0000000..ce3d99c
--- /dev/null
+++ b/src/util/json.c
@@ -0,0 +1,3916 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Rui Maciel *
+ * rui.maciel(a)gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "json.h"
+#include "memory.h"
+
+enum LEX_VALUE
+{ LEX_MORE = 0,
+ LEX_INVALID_CHARACTER,
+ LEX_TRUE,
+ LEX_FALSE,
+ LEX_NULL,
+ LEX_BEGIN_OBJECT,
+ LEX_END_OBJECT,
+ LEX_BEGIN_ARRAY,
+ LEX_END_ARRAY,
+ LEX_NAME_SEPARATOR,
+ LEX_VALUE_SEPARATOR,
+ LEX_STRING,
+ LEX_NUMBER,
+ LEX_ERROR,
+ LEX_MEMORY
+};
+
+
+/* rc_string part */
+
+#define RSTRING_INCSTEP 5
+#define RSTRING_DEFAULT 8
+
+enum rui_string_error_codes
+{ RS_MEMORY, RS_OK = 1, RS_UNKNOWN };
+
+typedef enum rui_string_error_codes rstring_code;
+
+
+static rcstring *
+rcs_create (size_t length)
+{
+ rcstring *rcs;
+ if (VIR_ALLOC(rcs) < 0)
+ return NULL;
+
+ rcs->max = length;
+ rcs->length = 0;
+
+ if (VIR_ALLOC_N(rcs->text, rcs->max + 1) < 0)
+ {
+ VIR_FREE(rcs);
+ return NULL;
+ }
+ rcs->text[0] = '\0';
+
+ return rcs;
+}
+
+
+static void
+rcs_free (rcstring ** rcs)
+{
+ if (*rcs != NULL)
+ {
+ VIR_FREE((*rcs)->text);
+ VIR_FREE(*rcs);
+ }
+
+}
+
+
+static rstring_code
+rcs_resize (rcstring * rcs, size_t length)
+{
+ if (VIR_REALLOC_N(rcs->text, length + 1) < 0)
+ {
+ VIR_FREE(rcs);
+ return RS_MEMORY;
+ }
+ rcs->max = length;
+ rcs->text[rcs->max] = '\0';
+ return RS_OK;
+}
+
+
+static rstring_code
+rcs_catcs (rcstring * pre, const char *pos, const size_t length)
+{
+ if (pre->max < pre->length + length)
+ {
+ if (rcs_resize (pre, pre->length + length + RSTRING_INCSTEP) != RS_OK)
+ return RS_MEMORY;
+ }
+ strncpy (pre->text + pre->length, pos, length);
+ pre->text[pre->length + length] = '\0';
+ pre->length += length;
+ return RS_OK;
+}
+
+
+static rstring_code
+rcs_catc (rcstring * pre, const char c)
+{
+ if (pre->max <= pre->length)
+ {
+ if (rcs_resize (pre, pre->max + RSTRING_INCSTEP) != RS_OK)
+ return RS_MEMORY;
+ }
+ pre->text[pre->length] = c;
+ pre->length++;
+ pre->text[pre->length] = '\0';
+ return RS_OK;
+}
+
+
+static char *
+rcs_unwrap (rcstring * rcs)
+{
+ char *out;
+
+ if (rcs->text == NULL)
+ out = NULL;
+ else
+ {
+ if (VIR_REALLOC_N(rcs->text, strlen(rcs->text)+1) < 0)
+ {} /* Ignore error to shrink */
+ out = rcs->text;
+ }
+
+ VIR_FREE(rcs);
+ return out;
+}
+
+
+
+static size_t
+rcs_length (rcstring * rcs)
+{
+ /*TODO account for UTF8 */
+ return rcs->length;
+}
+
+
+/* end of rc_string part */
+
+
+enum json_error
+json_stream_parse (FILE * file, json_t ** document)
+{
+ char buffer[1024]; /* hard-coded value */
+ unsigned int error = JSON_INCOMPLETE_DOCUMENT;
+
+ struct json_parsing_info state;
+
+ json_jpi_init (&state); /* initializes the json_parsing_info object */
+
+ while ((error == JSON_WAITING_FOR_EOF) || (error == JSON_INCOMPLETE_DOCUMENT))
+ {
+ if (fgets (buffer, 1024, file) != NULL)
+ {
+ switch (error = json_parse_fragment (&state, buffer))
+ {
+ case JSON_OK:
+ case JSON_WAITING_FOR_EOF:
+ case JSON_INCOMPLETE_DOCUMENT:
+ break;
+
+ default:
+ json_free_value (&state.cursor);
+ return error;
+ break;
+ }
+ }
+ else
+ {
+ if (error == JSON_WAITING_FOR_EOF)
+ error = JSON_OK;
+ else
+ {
+ /*TODO refine this error code */
+ error = JSON_UNKNOWN_PROBLEM;
+ }
+ }
+ }
+
+ if (error == JSON_OK)
+ {
+ *document = state.cursor;
+ }
+
+ return error;
+}
+
+
+json_t *
+json_new_value (const enum json_value_type type)
+{
+ json_t *new_object;
+ if (VIR_ALLOC(new_object) < 0)
+ return NULL;
+
+ /* initialize members */
+ new_object->text = NULL;
+ new_object->parent = NULL;
+ new_object->child = NULL;
+ new_object->child_end = NULL;
+ new_object->previous = NULL;
+ new_object->next = NULL;
+ new_object->type = type;
+ return new_object;
+}
+
+
+json_t *
+json_new_string (const char *text)
+{
+ json_t *new_object;
+ size_t length;
+
+ if (VIR_ALLOC(new_object) < 0)
+ return NULL;
+
+ /* initialize members */
+ length = strlen (text) + 1;
+ if (VIR_ALLOC_N(new_object->text, length) < 0)
+ {
+ VIR_FREE(new_object);
+ return NULL;
+ }
+ strncpy (new_object->text, text, length);
+ new_object->parent = NULL;
+ new_object->child = NULL;
+ new_object->child_end = NULL;
+ new_object->previous = NULL;
+ new_object->next = NULL;
+ new_object->type = JSON_STRING;
+ return new_object;
+}
+
+
+json_t *
+json_new_number (const char *text)
+{
+ json_t *new_object;
+ size_t length;
+
+ /* allocate memory for the new object */
+ if (VIR_ALLOC(new_object) < 0)
+ return NULL;
+
+ /* initialize members */
+ length = strlen (text) + 1;
+ if (VIR_ALLOC_N(new_object->text, length) < 0)
+ {
+ VIR_FREE(new_object);
+ return NULL;
+ }
+ strncpy (new_object->text, text, length);
+ new_object->parent = NULL;
+ new_object->child = NULL;
+ new_object->child_end = NULL;
+ new_object->previous = NULL;
+ new_object->next = NULL;
+ new_object->type = JSON_NUMBER;
+ return new_object;
+}
+
+
+json_t *
+json_new_object (void)
+{
+ return json_new_value (JSON_OBJECT);
+}
+
+
+json_t *
+json_new_array (void)
+{
+ return json_new_value (JSON_ARRAY);
+}
+
+
+json_t *
+json_new_null (void)
+{
+ return json_new_value (JSON_NULL);
+}
+
+
+json_t *
+json_new_true (void)
+{
+ return json_new_value (JSON_TRUE);
+}
+
+
+json_t *
+json_new_false (void)
+{
+ return json_new_value (JSON_FALSE);
+}
+
+
+void
+json_free_value (json_t ** value)
+{
+ /* free each and every child node */
+ if ((*value)->child != NULL)
+ {
+ json_t *i, *j;
+ i = (*value)->child_end;
+ while (i != NULL)
+ {
+ j = i->previous;
+ json_free_value (&i); /*TODO replace recursive solution with an
iterative one */
+ i = j;
+ }
+ }
+
+ /* fixing sibling linked list connections */
+ if ((*value)->previous && (*value)->next)
+ {
+ (*value)->previous->next = (*value)->next;
+ (*value)->next->previous = (*value)->previous;
+ }
+ else
+ {
+ if ((*value)->previous)
+ {
+ (*value)->previous->next = NULL;
+ }
+ if ((*value)->next)
+ {
+ (*value)->next->previous = NULL;
+ }
+ }
+
+ /*fixing parent node connections */
+ if ((*value)->parent)
+ {
+ /* fix the tree connection to the first node in the children's list */
+ if ((*value)->parent->child == (*value))
+ {
+ if ((*value)->next)
+ {
+ (*value)->parent->child = (*value)->next; /* the parent node
always points to the first node in the children linked list */
+ }
+ else
+ {
+ (*value)->parent->child = NULL;
+ }
+ }
+
+ /* fix the tree connection to the last node in the children's list */
+ if ((*value)->parent->child_end == (*value))
+ {
+ if ((*value)->previous)
+ {
+ (*value)->parent->child_end = (*value)->previous; /* the
parent node always points to the last node in the children linked list */
+ }
+ else
+ {
+ (*value)->parent->child_end = NULL;
+ }
+ }
+ }
+
+ /*finally, freeing the memory allocated for this value */
+ VIR_FREE((*value)->text);
+ VIR_FREE(*value);
+}
+
+
+enum json_error
+json_insert_child (json_t * parent, json_t * child)
+{
+ /*TODO change the child list from FIFO to LIFO, in order to get rid of the child_end
pointer */
+ /* enforce tree structure correctness */
+ switch (parent->type)
+ {
+ case JSON_STRING:
+ /* a string accepts every JSON type as a child value */
+ /* therefore, the sanity check must be performed on the child node */
+ switch (child->type)
+ {
+ case JSON_STRING:
+ case JSON_NUMBER:
+ case JSON_TRUE:
+ case JSON_FALSE:
+ case JSON_NULL:
+ if (child->child != NULL)
+ return JSON_BAD_TREE_STRUCTURE;
+ break;
+
+ case JSON_OBJECT:
+ case JSON_ARRAY:
+ break;
+
+ default:
+ return JSON_BAD_TREE_STRUCTURE; /* this part should never be reached */
+ break;
+ }
+ break;
+
+ case JSON_OBJECT: /* JSON objects may only accept JSON string objects which
already have child nodes of their own */
+ if (child->type != JSON_STRING)
+ return JSON_BAD_TREE_STRUCTURE;
+ break;
+
+ case JSON_ARRAY:
+ switch (child->type)
+ {
+ case JSON_STRING:
+ case JSON_TRUE:
+ case JSON_FALSE:
+ case JSON_NULL:
+ case JSON_NUMBER:
+ if (child->child)
+ return JSON_BAD_TREE_STRUCTURE;
+ break;
+
+ case JSON_OBJECT:
+ case JSON_ARRAY:
+ break;
+
+ default:
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ break;
+
+ default:
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+
+ child->parent = parent;
+ if (parent->child)
+ {
+ child->previous = parent->child_end;
+ parent->child_end->next = child;
+ parent->child_end = child;
+ }
+ else
+ {
+ parent->child = child;
+ parent->child_end = child;
+ }
+
+ return JSON_OK;
+}
+
+
+enum json_error
+json_insert_pair_into_object (json_t * parent, const char *text_label, json_t * value)
+{
+ enum json_error error;
+ json_t *label;
+
+ /* create label json_value */
+ label = json_new_string (text_label);
+ if (label == NULL)
+ return JSON_MEMORY;
+
+ /*insert value and check for error */
+ error = json_insert_child (label, value);
+ if (error != JSON_OK)
+ return error;
+ /*insert value and check for error */
+ error = json_insert_child (parent, label);
+ if (error != JSON_OK)
+ return error;
+
+ return JSON_OK;
+}
+
+enum json_error
+json_insert_string_pair_into_object (json_t * parent, const char *text_label, const char
*value)
+{
+ json_t *jval;
+ enum json_error ret;
+
+ if (!(jval = json_new_string(value)))
+ return JSON_MEMORY;
+
+ ret = json_insert_pair_into_object(parent, text_label, jval);
+ if (ret != JSON_OK)
+ json_free_value(&jval);
+
+ return ret;
+}
+
+enum json_error
+json_tree_to_string (json_t * root, char **text)
+{
+ json_t *cursor;
+ rcstring *output;
+
+ cursor = root;
+ /* set up the output and temporary rwstrings */
+ output = rcs_create (RSTRING_DEFAULT);
+
+ /* start the convoluted fun */
+state1: /* open value */
+ {
+ if ((cursor->previous) && (cursor != root)) /*if cursor is children
and not root than it is a followup sibling */
+ {
+ /* append comma */
+ if (rcs_catc (output, ',') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ switch (cursor->type)
+ {
+ case JSON_STRING:
+ /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1
+ 1 */
+ /* set the new output size */
+ if (rcs_catc (output, '\"') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc (output, '\"') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ if (cursor->parent != NULL)
+ {
+ if (cursor->parent->type == JSON_OBJECT) /* cursor is label in
label:value pair */
+ {
+ /* error checking: if parent is object and cursor is string then
cursor must have a single child */
+ if (cursor->child != NULL)
+ {
+ if (rcs_catc (output, ':') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ /* malformed document tree: label without value in label:value
pair */
+ rcs_free (&output);
+ text = NULL;
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ }
+ else /* does not have a parent */
+ {
+ if (cursor->child != NULL) /* is root label in label:value pair */
+ {
+ if (rcs_catc (output, ':') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ /* malformed document tree: label without value in label:value pair
*/
+ rcs_free (&output);
+ text = NULL;
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ break;
+
+ case JSON_NUMBER:
+ /* must not have any children */
+ /* set the new size */
+ if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ goto state2; /* close value */
+ break;
+
+ case JSON_OBJECT:
+ if (rcs_catc (output, '{') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ if (cursor->child)
+ {
+ cursor = cursor->child;
+ goto state1; /* open value */
+ }
+ else
+ {
+ goto state2; /* close value */
+ }
+ break;
+
+ case JSON_ARRAY:
+ if (rcs_catc (output, '[') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ if (cursor->child != NULL)
+ {
+ cursor = cursor->child;
+ goto state1;
+ }
+ else
+ {
+ goto state2; /* close value */
+ }
+ break;
+
+ case JSON_TRUE:
+ /* must not have any children */
+ if (rcs_catcs (output, "true", 4) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ goto state2; /* close value */
+ break;
+
+ case JSON_FALSE:
+ /* must not have any children */
+ if (rcs_catcs (output, "false", 5) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ goto state2; /* close value */
+ break;
+
+ case JSON_NULL:
+ /* must not have any children */
+ if (rcs_catcs (output, "null", 4) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ goto state2; /* close value */
+ break;
+
+ default:
+ goto error;
+ }
+ if (cursor->child)
+ {
+ cursor = cursor->child;
+ goto state1; /* open value */
+ }
+ else
+ {
+ /* does not have any children */
+ goto state2; /* close value */
+ }
+ }
+
+state2: /* close value */
+ {
+ switch (cursor->type)
+ {
+ case JSON_OBJECT:
+ if (rcs_catc (output, '}') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ break;
+
+ case JSON_ARRAY:
+ if (rcs_catc (output, ']') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ break;
+
+ case JSON_STRING:
+ break;
+ case JSON_NUMBER:
+ break;
+ case JSON_TRUE:
+ break;
+ case JSON_FALSE:
+ break;
+ case JSON_NULL:
+ break;
+ default:
+ goto error;
+ }
+ if ((cursor->parent == NULL) || (cursor == root))
+ {
+ goto end;
+ }
+ else if (cursor->next)
+ {
+ cursor = cursor->next;
+ goto state1; /* open value */
+ }
+ else
+ {
+ cursor = cursor->parent;
+ goto state2; /* close value */
+ }
+ }
+
+error:
+ {
+ rcs_free (&output);
+ return JSON_UNKNOWN_PROBLEM;
+ }
+
+end:
+ {
+ *text = rcs_unwrap (output);
+ return JSON_OK;
+ }
+}
+
+
+enum json_error
+json_stream_output (FILE * file, json_t * root)
+{
+ json_t *cursor;
+
+ cursor = root;
+ /* set up the output and temporary rwstrings */
+
+ /* start the convoluted fun */
+state1: /* open value */
+ {
+ if ((cursor->previous) && (cursor != root)) /*if cursor is children
and not root than it is a followup sibling */
+ {
+ /* append comma */
+ fprintf (file, ",");
+ }
+ switch (cursor->type)
+ {
+ case JSON_STRING:
+ /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1
+ 1 */
+ /* set the new output size */
+ fprintf (file, "\"%s\"", cursor->text);
+
+ if (cursor->parent != NULL)
+ {
+ if (cursor->parent->type == JSON_OBJECT) /* cursor is label in
label:value pair */
+ {
+ /* error checking: if parent is object and cursor is string then
cursor must have a single child */
+ if (cursor->child != NULL)
+ {
+ if (fprintf (file, ":") != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ /* malformed document tree: label without value in label:value
pair */
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ }
+ else /* does not have a parent */
+ {
+ if (cursor->child != NULL) /* is root label in label:value pair */
+ {
+ fprintf (file, ":");
+ }
+ else
+ {
+ /* malformed document tree: label without value in label:value pair
*/
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ break;
+
+ case JSON_NUMBER:
+ /* must not have any children */
+ /* set the new size */
+ fprintf (file, "%s", cursor->text);
+ goto state2; /* close value */
+ break;
+
+ case JSON_OBJECT:
+ fprintf (file, "{");
+
+ if (cursor->child)
+ {
+ cursor = cursor->child;
+ goto state1; /* open value */
+ }
+ else
+ {
+ goto state2; /* close value */
+ }
+ break;
+
+ case JSON_ARRAY:
+ fprintf (file, "[");
+
+ if (cursor->child != NULL)
+ {
+ cursor = cursor->child;
+ goto state1;
+ }
+ else
+ {
+ goto state2; /* close value */
+ }
+ break;
+
+ case JSON_TRUE:
+ /* must not have any children */
+ fprintf (file, "true");
+ goto state2; /* close value */
+ break;
+
+ case JSON_FALSE:
+ /* must not have any children */
+ fprintf (file, "false");
+ goto state2; /* close value */
+ break;
+
+ case JSON_NULL:
+ /* must not have any children */
+ fprintf (file, "null");
+ goto state2; /* close value */
+ break;
+
+ default:
+ goto error;
+ }
+ if (cursor->child)
+ {
+ cursor = cursor->child;
+ goto state1; /* open value */
+ }
+ else
+ {
+ /* does not have any children */
+ goto state2; /* close value */
+ }
+ }
+
+state2: /* close value */
+ {
+ switch (cursor->type)
+ {
+ case JSON_OBJECT:
+ fprintf (file, "}");
+ break;
+
+ case JSON_ARRAY:
+ fprintf (file, "]");
+ break;
+
+ case JSON_STRING:
+ break;
+ case JSON_NUMBER:
+ break;
+ case JSON_TRUE:
+ break;
+ case JSON_FALSE:
+ break;
+ case JSON_NULL:
+ break;
+ default:
+ goto error;
+ }
+ if ((cursor->parent == NULL) || (cursor == root))
+ {
+ goto end;
+ }
+ else if (cursor->next)
+ {
+ cursor = cursor->next;
+ goto state1; /* open value */
+ }
+ else
+ {
+ cursor = cursor->parent;
+ goto state2; /* close value */
+ }
+ }
+
+error:
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+
+end:
+ {
+ fprintf (file, "\n");
+ return JSON_OK;
+ }
+}
+
+
+void
+json_strip_white_spaces (char *text)
+{
+ size_t in, out, length;
+ int state;
+
+ in = 0;
+ out = 0;
+ length = strlen (text);
+ state = 0; /* possible states: 0 -> document, 1 -> inside a string */
+
+ while (in < length)
+ {
+ switch (text[in])
+ {
+ case '\x20': /* space */
+ case '\x09': /* horizontal tab */
+ case '\x0A': /* line feed or new line */
+ case '\x0D': /* Carriage return */
+ if (state == 1)
+ {
+ text[out++] = text[in];
+ }
+ break;
+
+ case '\"':
+ switch (state)
+ {
+ case 0: /* not inside a JSON string */
+ state = 1;
+ break;
+
+ case 1: /* inside a JSON string */
+ if (text[in - 1] != '\\')
+ {
+ state = 0;
+ }
+ break;
+ }
+ text[out++] = text[in];
+ break;
+
+ default:
+ text[out++] = text[in];
+ }
+ ++in;
+ }
+ text[out] = '\0';
+}
+
+
+char *
+json_format_string (const char *text)
+{
+ size_t pos = 0, text_length;
+ unsigned int indentation = 0; /* the current indentation level */
+ unsigned int i; /* loop iterator variable */
+ char loop;
+
+ rcstring *output;
+ text_length = strlen (text);
+
+ output = rcs_create (text_length);
+ while (pos < text_length)
+ {
+ switch (text[pos])
+ {
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+ pos++;
+ break;
+
+ case '{':
+ indentation++;
+ rcs_catcs (output, "{\n", 2);
+ for (i = 0; i < indentation; i++)
+ {
+ rcs_catc (output, '\t');
+ }
+ pos++;
+ break;
+
+ case '}':
+ indentation--;
+ rcs_catc (output, '\n');
+ for (i = 0; i < indentation; i++)
+ {
+ rcs_catc (output, '\t');
+ }
+ rcs_catc (output, '}');
+ pos++;
+ break;
+
+ case ':':
+ rcs_catcs (output, ": ", 2);
+ pos++;
+ break;
+
+ case ',':
+ rcs_catcs (output, ",\n", 2);
+ for (i = 0; i < indentation; i++)
+ {
+ rcs_catc (output, '\t');
+ }
+ pos++;
+ break;
+
+ case '\"': /* open string */
+ rcs_catc (output, text[pos]);
+ pos++;
+ loop = 1; /* inner string loop trigger is enabled */
+ while (loop)
+ {
+ if (text[pos] == '\\') /* escaped sequence */
+ {
+ rcs_catc (output, '\\');
+ pos++;
+ if (text[pos] == '\"') /* don't consider a
\" escaped sequence as an end of string */
+ {
+ rcs_catc (output, '\"');
+ pos++;
+ }
+ }
+ else if (text[pos] == '\"') /* reached end of string */
+ {
+ loop = 0;
+ }
+
+ rcs_catc (output, text[pos]);
+
+ pos++;
+ if (pos >= text_length)
+ {
+ loop = 0;
+ }
+ }
+ break;
+
+ default:
+ rcs_catc (output, text[pos]);
+ pos++;
+ break;
+ }
+ }
+
+ return rcs_unwrap (output);
+}
+
+
+char *
+json_escape (char *text)
+{
+ rcstring *output;
+ size_t i, length;
+ char buffer[6];
+
+ /* defining the temporary variables */
+ length = strlen (text);
+ output = rcs_create (length);
+ if (output == NULL)
+ return NULL;
+ for (i = 0; i < length; i++)
+ {
+ if (text[i] == '\\')
+ {
+ rcs_catcs (output, "\\\\", 2);
+ }
+ else if (text[i] == '\"')
+ {
+ rcs_catcs (output, "\\\"", 2);
+ }
+ else if (text[i] == '/')
+ {
+ rcs_catcs (output, "\\/", 2);
+ }
+ else if (text[i] == '\b')
+ {
+ rcs_catcs (output, "\\b", 2);
+ }
+ else if (text[i] == '\f')
+ {
+ rcs_catcs (output, "\\f", 2);
+ }
+ else if (text[i] == '\n')
+ {
+ rcs_catcs (output, "\\n", 2);
+ }
+ else if (text[i] == '\r')
+ {
+ rcs_catcs (output, "\\r", 2);
+ }
+ else if (text[i] == '\t')
+ {
+ rcs_catcs (output, "\\t", 2);
+ }
+ else if (text[i] < 0) /* non-BMP character */
+ {
+ rcs_catc (output, text[i]);
+ }
+ else if (text[i] < 0x20)
+ {
+ sprintf (buffer, "\\u%4.4x", text[i]);
+ rcs_catcs (output, buffer, 6);
+ }
+ else
+ {
+ rcs_catc (output, text[i]);
+ }
+ }
+ return rcs_unwrap (output);
+}
+
+
+void
+json_jpi_init (struct json_parsing_info *jpi)
+{
+ jpi->state = 0;
+ jpi->lex_state = 0;
+ jpi->lex_text = NULL;
+ jpi->p = NULL;
+ jpi->cursor = NULL;
+ jpi->string_length_limit_reached = 0;
+}
+
+
+static int
+lexer (const char *buffer, const char **p, unsigned int *state, rcstring ** text)
+{
+ if (*p == NULL)
+ *p = buffer;
+
+ while (**p != '\0')
+ {
+ switch (*state)
+ {
+
+ case 0: /* Root document */
+ {
+ switch (*(*p)++)
+ {
+ case '\x20': /* space */
+ case '\x09': /* horizontal tab */
+ case '\x0A': /* line feed or new line */
+ case '\x0D': /* Carriage return */
+ break;
+
+ case '{':
+ return LEX_BEGIN_OBJECT;
+ case '}':
+ return LEX_END_OBJECT;
+ case '[':
+ return LEX_BEGIN_ARRAY;
+ case ']':
+ return LEX_END_ARRAY;
+ case ':':
+ return LEX_NAME_SEPARATOR;
+ case ',':
+ return LEX_VALUE_SEPARATOR;
+
+ case '\"':
+ *text = rcs_create (RSTRING_DEFAULT);
+ if (*text == NULL)
+ return LEX_MEMORY;
+ *state = 1; /* inside a JSON string */
+ break;
+
+ case 't':
+ *state = 7; /* true: 1 */
+ break;
+
+ case 'f':
+ *state = 10; /* false: 1 */
+ break;
+
+ case 'n':
+ *state = 14; /* false: 1 */
+ break;
+
+ case '-':
+ *text = rcs_create (RSTRING_DEFAULT);
+ if (*text == NULL)
+ return LEX_MEMORY;
+ if (rcs_catc (*text, '-') != RS_OK)
+ return LEX_MEMORY;
+ *state = 17; /* number: '0' */
+ break;
+
+ case '0':
+ *text = rcs_create (RSTRING_DEFAULT);
+ if (*text == NULL)
+ return LEX_MEMORY;
+ if (rcs_catc (*text, '0') != RS_OK)
+ return LEX_MEMORY;
+ *state = 18; /* number: '0' */
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ *text = rcs_create (RSTRING_DEFAULT);
+ if (*text == NULL)
+ return LEX_MEMORY;
+ if (rcs_catc (*text, *(*p - 1)) != RS_OK)
+ return LEX_MEMORY;
+ *state = 19; /* number: decimal followup */
+ break;
+
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ }
+ }
+ break;
+
+ case 1: /* inside a JSON string */
+ {
+ switch (**p)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10: /* line feed */
+ case 11:
+ case 12:
+ case 13: /* carriage return */
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ /* ASCII control characters can only be present in a JSON string if they
are escaped. If not then the document is invalid */
+ return LEX_INVALID_CHARACTER;
+ break;
+
+ case '\"': /* close JSON string */
+ /* it is expected that, in the routine that calls this function, text is
set to NULL */
+ *state = 0;
+ ++*p;
+ return LEX_STRING;
+ break;
+
+ case '\\':
+ if (rcs_catc (*text, '\\') != RS_OK)
+ return LEX_MEMORY;
+ *state = 2; /* inside a JSON string: start escape sequence */
+ break;
+
+ default:
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ }
+ ++*p;
+ }
+ break;
+
+ case 2: /* inside a JSON string: start escape sequence */
+ {
+ switch (**p)
+ {
+ case '\\':
+ case '\"':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 1; /* inside a JSON string */
+ break;
+
+ case 'u':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 3; /* inside a JSON string: escape unicode */
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ }
+ ++*p;
+ }
+ break;
+
+ case 3: /*inside a JSON string: escape unicode */
+ {
+ if ((**p >= 'a') && (**p <= 'f'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 4; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= 'A') && (**p <= 'F'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 4; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= '0') && (**p <= '9'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 4; /* inside a JSON string: escape unicode */
+ }
+ else
+ return LEX_INVALID_CHARACTER;
+ ++*p;
+ }
+ break;
+
+ case 4: /* inside a JSON string: escape unicode */
+ {
+ if ((**p >= 'a') && (**p <= 'f'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 5; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= 'A') && (**p <= 'F'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 5; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= '0') && (**p <= '9'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 5; /* inside a JSON string: escape unicode */
+ }
+ else
+ return LEX_INVALID_CHARACTER;
+ ++*p;
+ }
+
+ case 5: /* inside a JSON string: escape unicode */
+ {
+ if ((**p >= 'a') && (**p <= 'f'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 6; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= 'A') && (**p <= 'F'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 6; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= '0') && (**p <= '9'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 6; /* inside a JSON string: escape unicode */
+ }
+ else
+ return LEX_INVALID_CHARACTER;
+ ++*p;
+ }
+ break;
+
+ case 6: /* inside a JSON string: escape unicode */
+ {
+ if ((**p >= 'a') && (**p <= 'f'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 1; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= 'A') && (**p <= 'F'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 1; /* inside a JSON string: escape unicode */
+ }
+ else if ((**p >= '0') && (**p <= '9'))
+ {
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ *state = 1; /* inside a JSON string: escape unicode */
+ }
+ else
+ return LEX_INVALID_CHARACTER;
+ ++*p;
+ }
+ break;
+
+ case 7: /* true: 1 */
+ {
+ switch (*(*p)++)
+ {
+ case 'r':
+ *state = 8;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 8: /* true: 2 */
+ {
+ switch (*(*p)++)
+ {
+ case 'u':
+ *state = 9;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 9: /* true: 3 */
+ {
+ switch (*(*p)++)
+ {
+ case 'e':
+ *state = 0;
+ return LEX_TRUE;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 10: /* false: 1 */
+ {
+ switch (*(*p)++)
+ {
+ case 'a':
+ *state = 11;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 11: /* false: 2 */
+ {
+ switch (*(*p)++)
+ {
+ case 'l':
+ *state = 12;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 12: /* false: 3 */
+ {
+ switch (*(*p)++)
+ {
+ case 's':
+ *state = 13;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 13: /* false: 4 */
+ {
+ switch (*(*p)++)
+ {
+ case 'e':
+ *state = 0;
+ return LEX_FALSE;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 14: /* null: 1 */
+ {
+ switch (*(*p)++)
+ {
+ case 'u':
+ *state = 15;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 15: /* null: 2 */
+ {
+ switch (*(*p)++)
+ {
+ case 'l':
+ *state = 16;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 16: /* null: 3 */
+ {
+ switch (*(*p)++)
+ {
+ case 'l':
+ *state = 0;
+ return LEX_NULL;
+ break;
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 17: /* number: minus sign */
+ {
+ switch (**p)
+ {
+ case '0':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 18; /* number: '0' */
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 19; /* number: decimal followup */
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 18: /* number: '0' */
+ {
+ switch (**p)
+ {
+ case '\x20': /* space */
+ case '\x09': /* horizontal tab */
+ case '\x0A': /* line feed or new line */
+ case '\x0D': /* Carriage return */
+ ++*p;
+ case ']':
+ case '}':
+ case ',':
+ *state = 0;
+ return LEX_NUMBER;
+ break;
+
+ case '.':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 20; /* number: frac start */
+ break;
+
+ case 'e':
+ case 'E':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 22; /* number: exp start */
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 19: /* number: int followup */
+ {
+ switch (**p)
+ {
+ case '\x20': /* space */
+ case '\x09': /* horizontal tab */
+ case '\x0A': /* line feed or new line */
+ case '\x0D': /* Carriage return */
+ ++*p;
+ case ']':
+ case '}':
+ case ',':
+ *state = 0;
+ return LEX_NUMBER;
+ break;
+
+ case '.':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 20; /* number: frac start */
+ break;
+
+ case 'e':
+ case 'E':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 22; /* number: exp start */
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 20: /* number: frac start */
+ {
+ switch (**p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 21; /* number: frac continue */
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 21: /* number: frac continue */
+ {
+ switch (**p)
+ {
+ case '\x20': /* space */
+ case '\x09': /* horizontal tab */
+ case '\x0A': /* line feed or new line */
+ case '\x0D': /* Carriage return */
+ ++*p;
+ case ']':
+ case '}':
+ case ',':
+ *state = 0;
+ return LEX_NUMBER;
+ break;
+
+ case 'e':
+ case 'E':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 22; /* number: exp start */
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 22: /* number: exp start */
+ {
+ switch (**p)
+ {
+ case '-':
+ case '+':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 23; /* number: exp continue */
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 24; /* number: exp end */
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 23: /* number: exp continue */
+ {
+ switch (**p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ *state = 24; /* number: exp end */
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ case 24: /* number: exp end */
+ {
+ switch (**p)
+ {
+ case '\x20': /* space */
+ case '\x09': /* horizontal tab */
+ case '\x0A': /* line feed or new line */
+ case '\x0D': /* Carriage return */
+ ++*p;
+ case ']':
+ case '}':
+ case ',':
+ *state = 0;
+ return LEX_NUMBER;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (rcs_catc (*text, **p) != RS_OK)
+ return LEX_MEMORY;
+ ++*p;
+ break;
+
+ default:
+ return LEX_INVALID_CHARACTER;
+ break;
+ }
+ }
+ break;
+
+ default:
+ printf ("*state missing: %d\n", *state);
+ return LEX_INVALID_CHARACTER;
+ }
+
+ }
+
+ *p = NULL;
+ return LEX_MORE;
+}
+
+
+enum json_error
+json_parse_fragment (struct json_parsing_info *info, const char *buffer)
+{
+ json_t *temp = NULL;
+
+ info->p = buffer;
+ while (*info->p != '\0')
+ {
+ switch (info->state)
+ {
+ case 0: /* starting point */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_BEGIN_OBJECT:
+ info->state = 1; /* begin object */
+ break;
+
+ case LEX_BEGIN_ARRAY:
+ info->state = 7; /* begin array */
+ break;
+
+ case LEX_INVALID_CHARACTER:
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 1: /* open object */
+ {
+ if (info->cursor == NULL)
+ {
+ if ((info->cursor = json_new_object ()) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ if ((temp = json_new_object ()) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ info->cursor = temp;
+ temp = NULL;
+ }
+ info->state = 2; /* just entered an object */
+ }
+ break;
+
+ case 2: /* opened object */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_STRING:
+ if ((temp = json_new_value (JSON_STRING)) == NULL)
+ return JSON_MEMORY;
+ temp->text = rcs_unwrap (info->lex_text), info->lex_text =
NULL;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ /*TODO return value according to the value returned from
json_insert_child() */
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ info->cursor = temp;
+ temp = NULL;
+ info->state = 5; /* label, pre label:value separator */
+ break;
+
+ case LEX_END_OBJECT:
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accept whitespaces
until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ switch (info->cursor->type)
+ {
+ case JSON_STRING:
+ info->cursor = info->cursor->parent;
+ if (info->cursor->type != JSON_OBJECT)
+ {
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ else
+ {
+ info->state = 3; /* finished adding a field to an
object */
+ }
+ break;
+
+ case JSON_ARRAY:
+ info->state = 9;
+ break;
+
+ default:
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ break;
+
+ case LEX_MORE:
+ return JSON_INCOMPLETE_DOCUMENT;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 3: /* finished adding a field to an object */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_VALUE_SEPARATOR:
+ info->state = 4; /* sibling, post-object */
+ break;
+
+ case LEX_END_OBJECT:
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* parse until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ switch (info->cursor->type)
+ {
+ case JSON_STRING:
+ info->cursor = info->cursor->parent;
+ if (info->cursor->type != JSON_OBJECT)
+ {
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ else
+ {
+ info->state = 3; /* finished adding a field to an
object */
+ }
+ break;
+
+ case JSON_ARRAY:
+ info->state = 9;
+ break;
+
+ default:
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ break;
+
+ case LEX_MORE:
+ return JSON_INCOMPLETE_DOCUMENT;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 4: /* sibling, post-object */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_STRING:
+ if ((temp = json_new_value (JSON_STRING)) == NULL)
+ return JSON_MEMORY;
+ temp->text = rcs_unwrap (info->lex_text), info->lex_text =
NULL;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ info->cursor = temp;
+ temp = NULL;
+ info->state = 5;
+ break;
+
+ case LEX_MORE:
+ return JSON_INCOMPLETE_DOCUMENT;
+ break;
+
+ case LEX_INVALID_CHARACTER:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 5: /* label, pre name separator */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_NAME_SEPARATOR:
+ info->state = 6; /* label, pos label:value separator */
+ break;
+
+ case LEX_MORE:
+ return JSON_INCOMPLETE_DOCUMENT;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 6: /* label, pos name separator */
+ {
+ unsigned int value; /* to avoid redundant code */
+
+ switch (value = lexer (buffer, &info->p, &info->lex_state,
& info->lex_text))
+ {
+ case LEX_STRING:
+ if ((temp = json_new_value (JSON_STRING)) == NULL)
+ return JSON_MEMORY;
+ temp->text = rcs_unwrap (info->lex_text), info->lex_text =
NULL;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ /*TODO specify the exact error message */
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accepts
whitespaces until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ }
+ temp = NULL;
+ info->state = 3; /* finished adding a field to an object */
+ break;
+
+ case LEX_NUMBER:
+ if ((temp = json_new_value (JSON_NUMBER)) == NULL)
+ return JSON_MEMORY;
+ temp->text = rcs_unwrap (info->lex_text), info->lex_text =
NULL;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ /*TODO specify the exact error message */
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accepts
whitespaces until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ }
+ temp = NULL;
+ info->state = 3; /* finished adding a field to an object */
+ break;
+
+ case LEX_TRUE:
+ if ((temp = json_new_value (JSON_TRUE)) == NULL)
+ return JSON_MEMORY;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ /*TODO specify the exact error message */
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accepts
whitespaces until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ }
+ temp = NULL;
+ info->state = 3; /* finished adding a field to an object */
+ break;
+
+ case LEX_FALSE:
+ if ((temp = json_new_value (JSON_FALSE)) == NULL)
+ return JSON_MEMORY;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ /*TODO specify the exact error message */
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accepts
whitespaces until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ }
+ temp = NULL;
+ info->state = 3; /* finished adding a field to an object */
+ break;
+
+ case LEX_NULL:
+ if ((temp = json_new_value (JSON_NULL)) == NULL)
+ return JSON_MEMORY;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ /*TODO specify the exact error message */
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accepts
whitespaces until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ }
+ temp = NULL;
+ info->state = 3; /* finished adding a field to an object */
+ break;
+
+ case LEX_BEGIN_OBJECT:
+ info->state = 1;
+ break;
+
+ case LEX_BEGIN_ARRAY:
+ info->state = 7;
+ break;
+
+ case LEX_MORE:
+ return JSON_INCOMPLETE_DOCUMENT;
+ break;
+
+ case LEX_MEMORY:
+ return JSON_MEMORY;
+ break;
+
+ case LEX_INVALID_CHARACTER:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 7: /* open array */
+ {
+ if (info->cursor == NULL)
+ {
+ if ((info->cursor = json_new_array ()) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ if ((temp = json_new_array ()) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ info->cursor = temp;
+ temp = NULL;
+ }
+ info->state = 8; /* just entered an array */
+ }
+ break;
+
+ case 8: /* just entered an array */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_STRING:
+ if ((temp = json_new_value (JSON_STRING)) == NULL)
+ return JSON_MEMORY;
+ temp->text = rcs_unwrap (info->lex_text), info->lex_text =
NULL;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ temp = NULL;
+ info->state = 9; /* label, pre label:value separator */
+ break;
+
+ case LEX_NUMBER:
+ if ((temp = json_new_value (JSON_NUMBER)) == NULL)
+ return JSON_MEMORY;
+ temp->text = rcs_unwrap (info->lex_text), info->lex_text =
NULL;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ temp = NULL;
+ info->state = 9; /* label, pre label:value separator */
+ break;
+
+ case LEX_TRUE:
+ if ((temp = json_new_value (JSON_TRUE)) == NULL)
+ return JSON_MEMORY;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ info->state = 9; /* label, pre label:value separator */
+ break;
+
+ case LEX_FALSE:
+ if ((temp = json_new_value (JSON_FALSE)) == NULL)
+ return JSON_MEMORY;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ info->state = 9; /* label, pre label:value separator */
+ break;
+
+ case LEX_NULL:
+ if ((temp = json_new_value (JSON_NULL)) == NULL)
+ return JSON_MEMORY;
+ if (json_insert_child (info->cursor, temp) != JSON_OK)
+ {
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ info->state = 9; /* label, pre label:value separator */
+ break;
+
+ case LEX_BEGIN_ARRAY:
+ info->state = 7; /* open array */
+ break;
+
+ case LEX_END_ARRAY:
+ if (info->cursor->parent == NULL)
+ {
+ /*TODO implement this */
+ info->state = 99; /* finished document. only accept whitespaces
until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ switch (info->cursor->type)
+ {
+ case JSON_STRING:
+ if (info->cursor->parent == NULL)
+ return JSON_BAD_TREE_STRUCTURE;
+ else
+ {
+ info->cursor = info->cursor->parent;
+ if (info->cursor->type != JSON_OBJECT)
+ {
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+
+ info->state = 3; /* followup to adding child to array
*/
+ }
+ break;
+
+ case JSON_ARRAY:
+ info->state = 9; /* followup to adding child to array */
+ break;
+
+ default:
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ break;
+
+ case LEX_BEGIN_OBJECT:
+ info->state = 1; /* open object */
+ break;
+
+ case LEX_MORE:
+ return JSON_INCOMPLETE_DOCUMENT;
+ break;
+
+ case LEX_INVALID_CHARACTER:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 9: /* followup to adding child to array */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_VALUE_SEPARATOR:
+ info->state = 8;
+ break;
+
+ case LEX_END_ARRAY:
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accept whitespaces
until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ switch (info->cursor->type)
+ {
+ case JSON_STRING:
+ if (info->cursor->parent == NULL)
+ {
+ info->state = 99; /* finished document. only accept
whitespaces until EOF */
+ }
+ else
+ {
+ info->cursor = info->cursor->parent;
+ if (info->cursor->type != JSON_OBJECT)
+ {
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ else
+ {
+ info->state = 3; /* followup to adding child to
array */
+ }
+ }
+ break;
+
+ case JSON_ARRAY:
+ info->state = 9; /* followup to adding child to array */
+ break;
+
+ default:
+ return JSON_BAD_TREE_STRUCTURE;
+ }
+ }
+ break;
+
+ case LEX_MORE:
+ return JSON_INCOMPLETE_DOCUMENT;
+ break;
+
+ default:
+ printf ("state %d: defaulted\n", info->state);
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ case 99: /* finished document. only accept whitespaces until EOF */
+ {
+ switch (lexer (buffer, &info->p, &info->lex_state, &
info->lex_text))
+ {
+ case LEX_MORE:
+ return JSON_WAITING_FOR_EOF;
+ break;
+
+ case LEX_MEMORY:
+ return JSON_MEMORY;
+ break;
+
+ default:
+ return JSON_MALFORMED_DOCUMENT;
+ break;
+ }
+ }
+ break;
+
+ default:
+ printf ("invalid parser state %d: defaulted\n", info->state);
+ return JSON_UNKNOWN_PROBLEM;
+ }
+ }
+ info->p = NULL;
+ if (info->state == 99)
+ return JSON_WAITING_FOR_EOF;
+ else
+ return JSON_INCOMPLETE_DOCUMENT;
+}
+
+
+
+enum json_error
+json_parse_document (json_t ** root, const char *text)
+{
+ enum json_error error;
+ struct json_parsing_info *jpi;
+
+ /* initialize the parsing structure */
+ if (VIR_ALLOC(jpi) < 0)
+ {
+ return JSON_MEMORY;
+ }
+ json_jpi_init (jpi);
+
+ error = json_parse_fragment (jpi, text);
+ if ((error == JSON_WAITING_FOR_EOF) || (error == JSON_OK))
+ {
+ *root = jpi->cursor;
+ VIR_FREE(jpi);
+ return JSON_OK;
+ }
+ else
+ {
+ VIR_FREE(jpi);
+ return error;
+ }
+}
+
+
+enum json_error
+json_saxy_parse (struct json_saxy_parser_status *jsps, struct json_saxy_functions *jsf,
char c)
+{
+ /*TODO handle a string instead of a single char */
+ /* temp variables */
+ rcstring *temp;
+
+ temp = NULL;
+
+ /* goto where we left off */
+ switch (jsps->state)
+ {
+ case 0: /* general state. everything goes. */
+ goto state0;
+ break;
+ case 1: /* parse string */
+ goto state1;
+ break;
+ case 2: /* parse string: escaped character */
+ goto state2;
+ break;
+ case 3: /* parse string: escaped unicode 1 */
+ goto state3;
+ break;
+ case 4: /* parse string: escaped unicode 2 */
+ goto state4;
+ break;
+ case 5: /* parse string: escaped unicode 3 */
+ goto state5;
+ break;
+ case 6: /* parse string: escaped unicode 4 */
+ goto state6;
+ break;
+ case 7: /* parse true: tr */
+ goto state7;
+ break;
+ case 8: /* parse true: tru */
+ goto state8;
+ break;
+ case 9: /* parse true: true */
+ goto state9;
+ break;
+ case 10: /* parse false: fa */
+ goto state10;
+ break;
+ case 11: /* parse false: fal */
+ goto state11;
+ break;
+ case 12: /* parse false: fals */
+ goto state12;
+ break;
+ case 13: /* parse false: false */
+ goto state13;
+ break;
+ case 14: /* parse null: nu */
+ goto state14;
+ break;
+ case 15: /* parse null: nul */
+ goto state15;
+ break;
+ case 16: /* parse null: null */
+ goto state16;
+ break;
+ case 17: /* parse number: 0 */
+ goto state17;
+ break;
+ case 18: /* parse number: start fraccional part */
+ goto state18;
+ break;
+ case 19: /* parse number: fraccional part */
+ goto state19;
+ break;
+ case 20: /* parse number: start exponent part */
+ goto state20;
+ break;
+ case 21: /* parse number: exponent part */
+ goto state21;
+ break;
+ case 22: /* parse number: exponent sign part */
+ goto state22;
+ break;
+ case 23: /* parse number: start negative */
+ goto state23;
+ break;
+ case 24: /* parse number: decimal part */
+ goto state24;
+ break;
+ case 25: /* open object */
+ goto state25;
+ break;
+ case 26: /* close object/array */
+ goto state26;
+ break;
+ case 27: /* sibling followup */
+ goto state27;
+ break;
+
+ default: /* oops... this should never be reached */
+ return JSON_UNKNOWN_PROBLEM;
+ }
+
+state0: /* starting point */
+ {
+ switch (c)
+ {
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+ break;
+
+ case '\"': /* starting a string */
+ jsps->string_length_limit_reached = 0;
+ jsps->state = 1;
+ break;
+
+ case '{':
+ if (jsf->open_object != NULL)
+ jsf->open_object ();
+ jsps->state = 25; /*open object */
+ break;
+
+ case '}':
+ if (jsf->close_object != NULL)
+ jsf->close_object ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case '[':
+ if (jsf->open_array != NULL)
+ jsf->open_array ();
+/* jsps->state = 0; // redundant*/
+ break;
+
+ case ']':
+ if (jsf->close_array != NULL)
+ jsf->close_array ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case 't':
+ jsps->state = 7; /* parse true: tr */
+ break;
+
+ case 'f':
+ jsps->state = 10; /* parse false: fa */
+ break;
+
+ case 'n':
+ jsps->state = 14; /* parse null: nu */
+ break;
+
+ case ':':
+ if (jsf->label_value_separator != NULL)
+ jsf->label_value_separator ();
+/* jsps->state = 0; // redundant*/
+ break;
+
+ case ',':
+ if (jsf->sibling_separator != NULL)
+ jsf->sibling_separator ();
+ jsps->state = 27; /* sibling followup */
+ break;
+
+ case '0':
+ jsps->string_length_limit_reached = 0;
+ jsps->state = 17; /* parse number: 0 */
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), '0') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ jsps->string_length_limit_reached = 0;
+ jsps->state = 24; /* parse number: decimal */
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ break;
+
+ case '-':
+ jsps->string_length_limit_reached = 0;
+ jsps->state = 23; /* number: */
+ jsps->temp = NULL;
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), '-') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state1: /* parse string */
+ {
+ switch (c)
+ {
+ case '\\':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1) /*
check if there is space for a two character escape sequence */
+ {
+ if (rcs_catc ((jsps->temp), '\\') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 2; /* parse string: escaped character */
+ break;
+
+ case '\"': /* end of string */
+ if ((jsps->temp) != NULL)
+ {
+ jsps->state = 0; /* starting point */
+ if (jsf->new_string != NULL)
+ jsf->new_string (((jsps->temp))->text); /*copied or
integral? */
+ rcs_free (& jsps->temp);
+ }
+ else
+ return JSON_UNKNOWN_PROBLEM;
+ break;
+
+ default:
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) /* check
if there is space for a two character escape sequence */
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ break;
+ }
+ return JSON_OK;
+ }
+
+state2: /* parse string: escaped character */
+ {
+ switch (c)
+ {
+ case '\"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ break;
+
+ case 'u':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 4)
+ {
+ if (rcs_catc ((jsps->temp), 'u') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 3; /* parse string: escaped unicode 1; */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state3: /* parse string: escaped unicode 1 */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 3)
+ {
+ if (rcs_catc ((jsps->temp), 'u') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 4; /* parse string. escaped unicode 2 */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ }
+ return JSON_OK;
+ }
+
+state4: /* parse string: escaped unicode 2 */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 2)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 5; /* parse string. escaped unicode 3 */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ }
+ return JSON_OK;
+ }
+
+state5: /* parse string: escaped unicode 3 */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 6; /* parse string. escaped unicode 4 */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ }
+ return JSON_OK;
+ }
+
+state6: /* parse string: escaped unicode 4 */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 1; /* parse string */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ }
+ return JSON_OK;
+ }
+
+state7: /* parse true: tr */
+ {
+ if (c != 'r')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 8; /* parse true: tru */
+ return JSON_OK;
+ }
+
+state8: /* parse true: tru */
+ {
+ if (c != 'u')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 9; /* parse true: true */
+ return JSON_OK;
+ }
+
+state9: /* parse true: true */
+ {
+ if (c != 'e')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 0; /* back to general state. */
+ if (jsf->new_true != NULL)
+ jsf->new_true ();
+ return JSON_OK;
+ }
+
+state10: /* parse false: fa */
+ {
+ if (c != 'a')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 11; /* parse true: fal */
+ return JSON_OK;
+ }
+
+state11: /* parse false: fal */
+ {
+ if (c != 'l')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 12; /* parse true: fals */
+ return JSON_OK;
+ }
+
+state12: /* parse false: fals */
+ {
+ if (c != 's')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 13; /* parse true: false */
+ return JSON_OK;
+ }
+
+state13: /* parse false: false */
+ {
+ if (c != 'e')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 0; /* general state. everything goes. */
+ if (jsf->new_false != NULL)
+ jsf->new_false ();
+ return JSON_OK;
+ }
+
+state14: /* parse null: nu */
+ {
+ if (c != 'u')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 15; /* parse null: nul */
+ return JSON_OK;
+ }
+
+state15: /* parse null: nul */
+ {
+ if (c != 'l')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 16; /* parse null: null */
+ return JSON_OK;
+ }
+
+state16: /* parse null: null */
+ {
+ if (c != 'l')
+ {
+ return JSON_ILLEGAL_CHARACTER;
+ }
+
+ jsps->state = 0; /* general state. everything goes. */
+ if (jsf->new_null != NULL)
+ jsf->new_null ();
+ return JSON_OK;
+ }
+
+state17: /* parse number: 0 */
+ {
+ switch (c)
+ {
+ case '.':
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), '.') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ jsps->state = 18; /* parse number: fraccional part */
+ break;
+
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ jsps->state = 0;
+ break;
+
+ case '}':
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->open_object != NULL)
+ jsf->close_object ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case ']':
+
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->open_object != NULL)
+ jsf->close_array ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case ',':
+
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->open_object != NULL)
+ jsf->label_value_separator ();
+ jsps->state = 27; /* sibling followup */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+
+ return JSON_OK;
+ }
+
+state18: /* parse number: start fraccional part */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 19; /* parse number: fractional part */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state19: /* parse number: fraccional part */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+/* jsps->state = 19; // parse number: fractional part*/
+ break;
+
+ case 'e':
+ case 'E':
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ jsps->state = 20; /* parse number: start exponent part */
+ break;
+
+
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ jsps->state = 0;
+ break;
+
+ case '}':
+
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->open_object != NULL)
+ jsf->close_object ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case ']':
+ if (jsf->new_number != NULL)
+ {
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ jsf->new_number ((jsps->temp)->text);
+ rcs_free (& jsps->temp);
+ }
+ else
+ {
+ rcs_free (& jsps->temp);
+ jsps->temp = NULL;
+ }
+ if (jsf->open_object != NULL)
+ jsf->close_array ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case ',':
+
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->label_value_separator != NULL)
+ jsf->label_value_separator ();
+ jsps->state = 27; /* sibling followup */
+ break;
+
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state20: /* parse number: start exponent part */
+ {
+ switch (c)
+ {
+ case '+':
+ case '-':
+ jsps->string_length_limit_reached = 0;
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ jsps->state = 22; /* parse number: exponent sign part */
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 21; /* parse number: exponent part */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state21: /* parse number: exponent part */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+ {
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+/* jsps->state = 21; // parse number: exponent
part*/
+ break;
+
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ jsps->state = 0;
+ break;
+
+ case '}':
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->open_object != NULL)
+ jsf->close_object ();
+ jsps->state = 26; /* close object */
+ break;
+
+ case ']':
+ if (jsf->new_number != NULL)
+ {
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ jsf->new_number ((jsps->temp)->text);
+ VIR_FREE(jsps->temp);
+ }
+ else
+ {
+ VIR_FREE(jsps->temp);
+ jsps->temp = NULL;
+ }
+ if (jsf->open_object != NULL)
+ jsf->close_array ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case ',':
+ if (jsf->new_number != NULL)
+ {
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ jsf->new_number ((jsps->temp)->text);
+ VIR_FREE(jsps->temp);
+ }
+ else
+ {
+ free (jsps->temp);
+ jsps->temp = NULL;
+ }
+ if (jsf->label_value_separator != NULL)
+ jsf->label_value_separator ();
+ jsps->state = 27; /* sibling followup */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state22: /* parse number: start exponent part */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+ {
+ rcs_catc ((jsps->temp), c);
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ jsps->state = 21; /* parse number: exponent part */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state23: /* parse number: start negative */
+ {
+ switch (c)
+ {
+ case '0':
+ rcs_catc ((jsps->temp), c);
+ jsps->state = 17; /* parse number: 0 */
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+ {
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+ }
+ jsps->state = 24; /* parse number: start decimal part */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state24: /* parse number: decimal part */
+ {
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!jsps->string_length_limit_reached)
+ {
+ if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+ {
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ }
+ else
+ {
+ jsps->string_length_limit_reached = 1;
+ }
+ }
+/* jsps->state = 24; // parse number: decimal
part*/
+ break;
+
+ case '.':
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), '.') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ jsps->state = 18; /* parse number: start exponent part */
+ break;
+
+ case 'e':
+ case 'E':
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+
+ jsps->string_length_limit_reached = 0; /* reset to accept the exponential
part */
+ jsps->state = 20; /* parse number: start exponent part */
+ break;
+
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ jsps->state = 0;
+ break;
+
+ case '}':
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->open_object != NULL)
+ jsf->close_object ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case ']':
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->open_object != NULL)
+ jsf->close_array ();
+ jsps->state = 26; /* close object/array */
+ break;
+
+ case ',':
+ if ((jsps->temp) == NULL)
+ return JSON_MEMORY;
+ if (jsf->new_number != NULL)
+ {
+ jsf->new_number ((jsps->temp)->text);
+ }
+ rcs_free (& jsps->temp);
+
+ if (jsf->label_value_separator != NULL)
+ jsf->label_value_separator ();
+ jsps->state = 27; /* sibling followup */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state25: /* open object */
+ {
+ switch (c)
+ {
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+ break;
+
+ case '\"':
+ jsps->temp = NULL;
+ jsps->state = 1;
+ break;
+
+ case '}':
+ if (jsf->close_object != NULL)
+ jsf->close_object ();
+ jsps->state = 26; /* close object */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state26: /* close object/array */
+ {
+ switch (c)
+ {
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+ break;
+
+ case '}':
+ if (jsf->close_object != NULL)
+ jsf->close_object ();
+/* jsp->state = 26; // close object*/
+ break;
+
+ case ']':
+ if (jsf->close_array != NULL)
+ jsf->close_array ();
+/* jsps->state = 26; // close object/array*/
+ break;
+
+ case ',':
+ if (jsf->sibling_separator != NULL)
+ jsf->sibling_separator ();
+ jsps->state = 27; /* sibling followup */
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+state27: /* sibling followup */
+ {
+ switch (c)
+ {
+ case '\x20':
+ case '\x09':
+ case '\x0A':
+ case '\x0D': /* JSON insignificant white spaces */
+ break;
+
+ case '\"':
+ jsps->state = 1;
+ jsps->temp = NULL;
+ break;
+
+ case '{':
+ if (jsf->open_object != NULL)
+ jsf->open_object ();
+ jsps->state = 25; /*open object */
+ break;
+
+ case '[':
+ if (jsf->open_array != NULL)
+ jsf->open_array ();
+/* jsps->state = 0; // redundant*/
+ break;
+
+ case 't':
+ jsps->state = 7; /* parse true: tr */
+ break;
+
+ case 'f':
+ jsps->state = 10; /* parse false: fa */
+ break;
+
+ case 'n':
+ jsps->state = 14; /* parse null: nu */
+ break;
+
+ case '0':
+ jsps->state = 17; /* parse number: 0 */
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), '0') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ jsps->state = 24; /* parse number: decimal */
+ if ((jsps->temp = rcs_create (5)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), c) != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ break;
+
+ case '-':
+ jsps->state = 23; /* number: */
+ if ((jsps->temp = rcs_create (RSTRING_DEFAULT)) == NULL)
+ {
+ return JSON_MEMORY;
+ }
+ if (rcs_catc ((jsps->temp), '-') != RS_OK)
+ {
+ return JSON_MEMORY;
+ }
+ break;
+
+ default:
+ return JSON_ILLEGAL_CHARACTER;
+ break;
+ }
+ return JSON_OK;
+ }
+
+ return JSON_UNKNOWN_PROBLEM;
+}
+
+
+enum json_error
+json_find_first_label (const json_t * object, const char *text_label, json_t **value)
+{
+ json_t *cursor;
+
+ for (cursor = object->child; cursor != NULL; cursor = cursor->next)
+ {
+ if (STREQ(cursor->text, text_label)) {
+ *value = cursor;
+ return JSON_OK;
+ }
+ }
+ *value = NULL;
+ return JSON_UNKNOWN_PROBLEM;
+}
diff --git a/src/util/json.h b/src/util/json.h
new file mode 100644
index 0000000..42dbcf7
--- /dev/null
+++ b/src/util/json.h
@@ -0,0 +1,313 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Rui Maciel *
+ * rui.maciel(a)gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+/** @file json.h A small library that helps deal with JSON-encoded information
+ \ingroup JSON
+
+ \note error handling is only in a very rudimentary form.
+ \author Rui Maciel rui_maciel(a)users.sourceforge.net
+ \version v1.1
+*/
+
+#include <stdint.h>
+#include <stdio.h>
+
+#ifndef JSON_H
+#define JSON_H
+
+#define JSON_MAX_STRING_LENGTH SIZE_MAX-1
+
+struct rui_cstring
+{
+ char *text; /*<! char c-string */
+ size_t length; /*<! put in place to avoid strlen() calls */
+ size_t max; /*<! usable memory allocated to text minus the space for the nul
character */
+};
+
+typedef struct rui_cstring rcstring;
+
+/**
+ The descriptions of the json_value node type
+**/
+ enum json_value_type
+ { JSON_STRING = 0, JSON_NUMBER, JSON_OBJECT, JSON_ARRAY, JSON_TRUE, JSON_FALSE,
JSON_NULL };
+
+/**
+ The error messages produced by the JSON parsers
+**/
+ enum json_error
+ {
+ JSON_OK = 1, /*!< everything went smoothly */
+ JSON_INCOMPLETE_DOCUMENT, /*!< the parsed document didn't ended */
+ JSON_WAITING_FOR_EOF, /*!< A complete JSON document tree was already finished
but needs to get to EOF. Other characters beyond whitespaces produce errors */
+ JSON_MALFORMED_DOCUMENT, /* the JSON document which was fed to this parser is
malformed */
+ JSON_INCOMPATIBLE_TYPE, /*!< the currently parsed type does not belong here
*/
+ JSON_MEMORY, /*!< an error occurred when allocating memory */
+ JSON_ILLEGAL_CHARACTER, /*!< the currently parsed character does not belong
here */
+ JSON_BAD_TREE_STRUCTURE, /*!< the document tree structure is malformed */
+ JSON_MAXIMUM_LENGTH, /*!< the parsed string reached the maximum allowed size
*/
+ JSON_UNKNOWN_PROBLEM /*!< some random, unaccounted problem occurred */
+ };
+
+
+/**
+ The JSON document tree node, which is a basic JSON type
+**/
+ typedef struct json_value
+ {
+ enum json_value_type type; /*!< the type of node */
+ char *text; /*!< The text stored by the node. It stores UTF-8 strings and is
used exclusively by the JSON_STRING and JSON_NUMBER node types */
+
+ /* FIFO queue data */
+ struct json_value *next; /*!< The pointer pointing to the next element in the
FIFO sibling list */
+ struct json_value *previous; /*!< The pointer pointing to the previous element
in the FIFO sibling list */
+ struct json_value *parent; /*!< The pointer pointing to the parent node in the
document tree */
+ struct json_value *child; /*!< The pointer pointing to the first child node in
the document tree */
+ struct json_value *child_end; /*!< The pointer pointing to the last child node
in the document tree */
+ } json_t;
+
+
+/**
+ The structure holding all information needed to resume parsing
+**/
+ struct json_parsing_info
+ {
+ unsigned int state; /*!< the state where the parsing was left on the last
parser run */
+ unsigned int lex_state;
+ rcstring *lex_text;
+ const char *p;
+ int string_length_limit_reached; /*!< flag informing if the string limit
length defined by JSON_MAX_STRING_LENGTH was reached */
+ json_t *cursor; /*!< pointers to nodes belonging to the document tree which
aid the document parsing */
+ };
+
+
+/**
+ The structure which holds the pointers to the functions that will be called by the
saxy parser whenever their evens are triggered
+**/
+ struct json_saxy_functions
+ {
+ int (*open_object) (void);
+ int (*close_object) (void);
+ int (*open_array) (void);
+ int (*close_array) (void);
+ int (*new_string) (char *text);
+ int (*new_number) (char *text);
+ int (*new_true) (void);
+ int (*new_false) (void);
+ int (*new_null) (void);
+ int (*label_value_separator) (void);
+ int (*sibling_separator) (void);
+ };
+
+
+/**
+ The structure holding the information needed for json_saxy_parse to resume parsing
+**/
+ struct json_saxy_parser_status
+ {
+ unsigned int state; /*!< current parser state */
+ int string_length_limit_reached; /*!< flag informing if the string limit
length defined by JSON_MAX_STRING_LENGTH was reached */
+ rcstring *temp; /*!< temporary string which will be used to build up parsed
strings between parser runs. */
+ };
+
+
+/**
+ Buils a json_t document by parsing an open file
+ @param file a pointer to an object controlling a stream, returned by fopen()
+ @param document a reference to a json_t pointer, set to NULL, which will store the
parsed document
+ @return a json_error error code according to how the parsing operation went.
+**/
+ enum json_error json_stream_parse (FILE * file, json_t ** document);
+
+
+/**
+ Creates a new JSON value and defines it's type
+ @param type the value's type
+ @return a pointer to the newly created value structure
+**/
+ json_t *json_new_value (const enum json_value_type type);
+
+
+/**
+ Creates a new JSON string and defines it's text
+ @param text the value's text
+ @return a pointer to the newly created JSON string value
+**/
+ json_t *json_new_string (const char *text);
+
+
+/**
+ Creates a new JSON number and defines it's text. The user is responsible for the
number string's correctness
+ @param text the value's number
+ @return a pointer to the newly created JSON string value
+**/
+ json_t *json_new_number (const char *text);
+
+
+/**
+ Creates a new JSON object
+ @return a pointer to the newly created JSON object value
+**/
+ json_t *json_new_object (void);
+
+
+/**
+ Creates a new JSON array
+ @return a pointer to the newly created JSON array value
+**/
+ json_t *json_new_array (void);
+
+
+/**
+ Creates a new JSON null
+ @return a pointer to the newly created JSON null value
+**/
+ json_t *json_new_null (void);
+
+
+/**
+ Creates a new JSON true
+ @return a pointer to the newly created JSON true value
+**/
+ json_t *json_new_true (void);
+
+
+/**
+ Creates a new JSON false
+ @return a pointer to the newly created JSON false value
+**/
+ json_t *json_new_false (void);
+
+
+/**
+ Frees the memory appointed to the value fed as the parameter, as well as all the child
nodes
+ @param value the root node of the tree being freed
+**/
+ void json_free_value (json_t ** value);
+
+
+/**
+ Inserts a child node into a parent node, as well as performs some document tree
integrity checks.
+ @param parent the parent node
+ @param child the node being added as a child to parent
+ @return the error code corresponding to the operation result
+**/
+ enum json_error json_insert_child (json_t * parent, json_t * child);
+
+
+/**
+ Inserts a label:value pair into a parent node, as well as performs some document tree
integrity checks.
+ @param parent the parent node
+ @param text_label a char string which serves as the label in the label:value pair
+ @param value the value in the label:value pair
+ @return the error code corresponding to the operation result
+**/
+ enum json_error json_insert_pair_into_object (json_t * parent, const char
*text_label, json_t * value);
+
+ enum json_error json_insert_string_pair_into_object (json_t * parent, const char
*text_label, const char *value);
+
+
+/**
+ Produces a JSON markup text document from a document tree
+ @param root The document's root node
+ @param text a pointer to a char string that will hold the JSON document text.
+ @return a json_error code describing how the operation went
+**/
+ enum json_error json_tree_to_string (json_t * root, char **text);
+
+
+/**
+ Produces a JSON markup text document from a json_t document tree to a text stream
+ @param file a opened file stream
+ @param root The document's root node
+ @return a json_error code describing how the operation went
+**/
+ enum json_error json_stream_output (FILE * file, json_t * root);
+
+
+/**
+ Strips all JSON white spaces from the text string
+ @param text a char string holding a JSON document or document snippet
+**/
+ void json_strip_white_spaces (char *text);
+
+
+/**
+ Formats a JSON markup text contained in the given string
+ @param text a JSON formatted document
+ @return a pointer to a char string holding the formated document
+**/
+ char *json_format_string (const char *text);
+
+
+/**
+ Outputs a new UTF8 c-string which replaces all characters that must be escaped with
their respective escaped versions
+ @param text an UTF8 char text string
+ @return an UTF-8 c-string holding the same text string but with escaped characters
+**/
+ char *json_escape (char *text);
+
+
+/**
+ This function takes care of the tedious task of initializing any instance of
+ struct json_parsing_info
+ @param jpi a pointer to a struct json_parsing_info instance
+**/
+ void json_jpi_init (struct json_parsing_info *jpi);
+
+
+/**
+ Produces a document tree sequentially from a JSON markup text fragment
+ @param info the information necessary to resume parsing any incomplete document
+ @param buffer a null-terminated c-string containing a JSON document fragment
+ @return a code describing how the operation ended up
+**/
+ enum json_error json_parse_fragment (struct json_parsing_info *info, const char
*buffer);
+
+
+/**
+ Produces a document tree from a JSON markup text string that contains a complete
document
+ @param root a reference to a pointer to a json_t type. The function allocates memory
to the passed pointer and sets up the value
+ @param text a c-string containing a complete JSON text document
+ @return a pointer to the new document tree or NULL if some error occurred
+**/
+ enum json_error json_parse_document (json_t ** root, const char *text);
+
+
+/**
+ Function to perform a SAX-like parsing of any JSON document or document fragment that
is passed to it
+ @param jsps a structure holding the status information of the current parser
+ @param jsf a structure holding the function pointers to the event functions
+ @param c the character to be parsed
+ @return a json_error code informing how the parsing went
+**/
+ enum json_error json_saxy_parse (struct json_saxy_parser_status *jsps, struct
json_saxy_functions *jsf, char c);
+
+
+/**
+ Searches through the object's children for a label holding the text text_label
+ @param object a json_value of type JSON_OBJECT
+ @param text_label the c-string to search for through the object's child labels
+ @return a pointer to the first label holding a text equal to text_label or NULL if
there is no such label or if object has no children
+**/
+ enum json_error json_find_first_label (const json_t * object, const char *text_label,
json_t **value);
+
+
+#endif
--
1.6.2.5