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@xxxxxxxxx * + * * + * 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@xxxxxxxxx * + * * + * 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@xxxxxxxxxxxxxxxxxxxxx + \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 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list