[libvirt] [PATCH 14/15] Import basic JSON parsing / formatting helper code

Daniel P. Berrange berrange at redhat.com
Tue Nov 3 19:50:08 UTC 2009


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 at 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 at 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 at 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




More information about the libvir-list mailing list