diff options
author | Lloyd Hilaiel <lloyd@hilaiel.com> | 2011-04-22 14:11:24 -0600 |
---|---|---|
committer | Lloyd Hilaiel <lloyd@hilaiel.com> | 2011-04-22 14:11:24 -0600 |
commit | d2ec58b70b429b908d9f395ef378443b6101153b (patch) | |
tree | 207162f20d8d97eafe0c9e899d9ffad32c36c0c2 | |
parent | 9538bda17bef60642f2ed3ff2f6bfd29785c36df (diff) | |
parent | a7ab7633a603c6c3e31bb54a45da2afbb54a98c9 (diff) | |
download | yajl-d2ec58b70b429b908d9f395ef378443b6101153b.tar.gz |
Merge branch 'parsetree' of https://github.com/octo/yajl into parsetree
-rw-r--r-- | src/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/api/yajl_tree.h | 262 | ||||
-rw-r--r-- | src/yajl_tree.c | 528 |
3 files changed, 792 insertions, 2 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5fc116f..479b405 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,10 +14,10 @@ SET (SRCS yajl.c yajl_lex.c yajl_parser.c yajl_buf.c yajl_encode.c yajl_gen.c yajl_alloc.c - yajl_version.c + yajl_tree.c yajl_version.c ) SET (HDRS yajl_parser.h yajl_lex.h yajl_buf.h yajl_encode.h yajl_alloc.h) -SET (PUB_HDRS api/yajl_parse.h api/yajl_gen.h api/yajl_common.h) +SET (PUB_HDRS api/yajl_parse.h api/yajl_gen.h api/yajl_common.h api/yajl_tree.h) # useful when fixing lexer bugs. #ADD_DEFINITIONS(-DYAJL_LEXER_DEBUG) diff --git a/src/api/yajl_tree.h b/src/api/yajl_tree.h new file mode 100644 index 0000000..7c056f6 --- /dev/null +++ b/src/api/yajl_tree.h @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2010 Florian Forster <ff at octo.it> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_tree.h + * + * Parses JSON data and returns the data in tree form. + * + * \author Florian Forster + * \date August 2010 + */ + +#ifndef YAJL_TREE_H +#define YAJL_TREE_H 1 + +#include <stdint.h> +#include <inttypes.h> + +#include <yajl/yajl_common.h> + +/* Forward declaration, because "yajl_value_object_t" and "yajl_value_array_t" + * contain "yajl_value_t" and "yajl_value_t" can be an object or an array. */ +struct yajl_value_s; +typedef struct yajl_value_s yajl_value_t; + +/** Structure describing a JSON string. */ +struct yajl_value_string_s +{ + /** Null terminated string. */ + char *value; +}; +typedef struct yajl_value_string_s yajl_value_string_t; + +#define YAJL_NUMBER_INT_VALID 0x01 +#define YAJL_NUMBER_DOUBLE_VALID 0x02 +/** Structure describing a JSON number. */ +struct yajl_value_number_s +{ + /** Holds the raw value of the number, in string form. */ + char *value_raw; + /** Holds the integer value of the number, if possible. */ + int64_t value_int; + /** Holds the double value of the number, if possible. */ + double value_double; + /** Signals whether the \em value_int and \em value_double members are + * valid. See \c YAJL_NUMBER_INT_VALID and \c YAJL_NUMBER_DOUBLE_VALID. */ + unsigned int flags; +}; +typedef struct yajl_value_number_s yajl_value_number_t; + +/** + * Structure describing a JSON object. + * + * \sa yajl_value_array_s + */ +struct yajl_value_object_s +{ + /** Array of keys in the JSON object. */ + yajl_value_t **keys; + /** Array of values in the JSON object. */ + yajl_value_t **values; + /** Number of key-value-pairs in the JSON object. */ + size_t children_num; +}; +typedef struct yajl_value_object_s yajl_value_object_t; + +/** + * Structure describing a JSON array. + * + * \sa yajl_value_object_s + */ +struct yajl_value_array_s +{ + /** Array of elements in the JSON array. */ + yajl_value_t **values; + /** Number of elements in the JSON array. */ + size_t values_num; +}; +typedef struct yajl_value_array_s yajl_value_array_t; + +#define YAJL_TYPE_STRING 1 +#define YAJL_TYPE_NUMBER 2 +#define YAJL_TYPE_OBJECT 3 +#define YAJL_TYPE_ARRAY 4 +#define YAJL_TYPE_TRUE 5 +#define YAJL_TYPE_FALSE 6 +#define YAJL_TYPE_NULL 7 + +/** + * Struct describing a general JSON value. + * + * Each value is one of the seven types above. For "string", "number", + * "object", and "array" additional data is available in the "data" union. Use + * the "YAJL_IS_*" and "YAJL_TO_*" macros below to check for the correct type + * and cast the struct. + * + * \sa yajl_value_string_t, yajl_value_number_t, yajl_value_object_t, + * yajl_value_array_t + */ +struct yajl_value_s +{ + /** Type of the value contained. Use the "YAJL_IS_*" macors to check for a + * specific type. */ + uint8_t type; + /** Type-specific data. Use the "YAJL_TO_*" macros to access these + * members. */ + union + { + yajl_value_string_t string; + yajl_value_number_t number; + yajl_value_object_t object; + yajl_value_array_t array; + } data; +}; + +/** + * Parse a string. + * + * Parses an null-terminated string containing JSON data and returns a pointer + * to the top-level value (root of the parse tree). + * + * \param input Pointer to a null-terminated string containing + * JSON data. + * \param error_buffer Pointer to a buffer in which an error message will + * be stored if \em yajl_tree_parse fails, or + * \c NULL. The buffer will be initialized before + * parsing, so its content will be destroyed even if + * \em yajl_tree_parse succeeds. + * \param error_buffer_size Size of the memory area pointed to by + * \em error_buffer_size. If \em error_buffer_size is + * \c NULL, this argument is ignored. + * + * \returns Pointer to the top-level value or \c NULL on error. The memory + * pointed to must be freed using \em yajl_tree_free. In case of an error, a + * null terminated message describing the error in more detail is stored in + * \em error_buffer if it is not \c NULL. + */ +YAJL_API yajl_value_t *yajl_tree_parse (const char *input, + char *error_buffer, size_t error_buffer_size); + +/** + * Free a parse tree. + * + * Recursively frees a pointer returned by "yajl_tree_parse". + * + * \param v Pointer to a JSON value returned by "yajl_tree_parse". Passing NULL + * is valid and results in a no-op. + */ +YAJL_API void yajl_tree_free (yajl_value_t *v); + +/** + * Checks if value is a string. + * + * Returns true if the value is a string, false otherwise. + */ +#define YAJL_IS_STRING(v) (((v) != NULL) && ((v)->type == YAJL_TYPE_STRING)) + +/** + * Checks if value is a number. + * + * Returns true if the value is a number, false otherwise. + */ +#define YAJL_IS_NUMBER(v) (((v) != NULL) && ((v)->type == YAJL_TYPE_NUMBER)) + +/** + * Checks if value is an object. + * + * Returns true if the value is a object, false otherwise. + */ +#define YAJL_IS_OBJECT(v) (((v) != NULL) && ((v)->type == YAJL_TYPE_OBJECT)) + +/** + * Checks if value is an array. + * + * Returns true if the value is a array, false otherwise. + */ +#define YAJL_IS_ARRAY(v) (((v) != NULL) && ((v)->type == YAJL_TYPE_ARRAY )) + +/** + * Checks if value is true. + * + * Returns true if the value is a boolean and true, false otherwise. + */ +#define YAJL_IS_TRUE(v) (((v) != NULL) && ((v)->type == YAJL_TYPE_TRUE )) + +/** + * Checks if value is false. + * + * Returns true if the value is a boolean and false, false otherwise. + */ +#define YAJL_IS_FALSE(v) (((v) != NULL) && ((v)->type == YAJL_TYPE_FALSE )) + +/** + * Checks if value is null. + * + * Returns true if the value is null, false otherwise. + */ +#define YAJL_IS_NULL(v) (((v) != NULL) && ((v)->type == YAJL_TYPE_NULL )) + +/** + * Convert value to string. + * + * Returns a pointer to a yajl_value_string_t or NULL if the value is not a + * string. + */ +#define YAJL_TO_STRING(v) (YAJL_IS_STRING(v) ? &(v)->data.string : NULL) + +/** + * Convert value to number. + * + * Returns a pointer to a yajl_value_number_t or NULL if the value is not a + * number. + */ +#define YAJL_TO_NUMBER(v) (YAJL_IS_NUMBER(v) ? &(v)->data.number : NULL) + +/** + * Convert value to object. + * + * Returns a pointer to a yajl_value_object_t or NULL if the value is not an + * object. + */ +#define YAJL_TO_OBJECT(v) (YAJL_IS_OBJECT(v) ? &(v)->data.object : NULL) + +/** + * Convert value to array. + * + * Returns a pointer to a yajl_value_array_t or NULL if the value is not an + * array. + */ +#define YAJL_TO_ARRAY(v) (YAJL_IS_ARRAY(v) ? &(v)->data.array : NULL) + +#endif /* YAJL_TREE_H */ +/* vim: set sw=2 sts=2 et : */ diff --git a/src/yajl_tree.c b/src/yajl_tree.c new file mode 100644 index 0000000..a4603c2 --- /dev/null +++ b/src/yajl_tree.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2010 Florian Forster <ff at octo.it> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "api/yajl_tree.h" +#include "api/yajl_parse.h" + +#define STATUS_CONTINUE 1 +#define STATUS_ABORT 0 + +struct stack_elem_s; +typedef struct stack_elem_s stack_elem_t; +struct stack_elem_s +{ + yajl_value_t *key; + yajl_value_t *value; + stack_elem_t *next; +}; + +struct context_s +{ + stack_elem_t *stack; + yajl_value_t *root; + char *errbuf; + size_t errbuf_size; +}; +typedef struct context_s context_t; + +#define RETURN_ERROR(ctx,retval,...) do { \ + if ((ctx)->errbuf != NULL) \ + snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__); \ + return (retval); \ +} while (0) \ + +static yajl_value_t *value_alloc (uint8_t type) /* {{{ */ +{ + yajl_value_t *v; + + v = malloc (sizeof (*v)); + if (v == NULL) + return (NULL); + + memset (v, 0, sizeof (*v)); + v->type = type; + + return (v); +} /* }}} yajl_value_t *value_alloc */ + +static void yajl_object_free (yajl_value_t *v) /* {{{ */ +{ + yajl_value_object_t *o; + size_t i; + + o = YAJL_TO_OBJECT (v); + if (o == NULL) + return; + + for (i = 0; i < o->children_num; i++) + { + yajl_tree_free (o->keys[i]); + o->keys[i] = NULL; + yajl_tree_free (o->values[i]); + o->values[i] = NULL; + } + + free (o->keys); + free (o->values); + free (v); +} /* }}} void yajl_object_free */ + +static void yajl_array_free (yajl_value_t *v) /* {{{ */ +{ + yajl_value_array_t *a; + size_t i; + + a = YAJL_TO_ARRAY (v); + if (a == NULL) + return; + + for (i = 0; i < a->values_num; i++) + { + yajl_tree_free (a->values[i]); + a->values[i] = NULL; + } + + free (a->values); + free (v); +} /* }}} void yajl_array_free */ + +/* + * Parsing nested objects and arrays is implemented using a stack. When a new + * object or array starts (a curly or a square opening bracket is read), an + * appropriate value is pushed on the stack. When the end of the object is + * reached (an appropriate closing bracket has been read), the value is popped + * off the stack and added to the enclosing object using "context_add_value". + */ +static int context_push (context_t *ctx, yajl_value_t *v) /* {{{ */ +{ + stack_elem_t *stack; + + stack = malloc (sizeof (*stack)); + if (stack == NULL) + RETURN_ERROR (ctx, ENOMEM, "Out of memory"); + memset (stack, 0, sizeof (*stack)); + + assert ((ctx->stack == NULL) + || YAJL_IS_OBJECT (v) + || YAJL_IS_ARRAY (v)); + + stack->value = v; + stack->next = ctx->stack; + ctx->stack = stack; + + return (0); +} /* }}} int context_push */ + +static yajl_value_t *context_pop (context_t *ctx) /* {{{ */ +{ + stack_elem_t *stack; + yajl_value_t *v; + + if (ctx->stack == NULL) + RETURN_ERROR (ctx, NULL, "context_pop: " + "Bottom of stack reached prematurely"); + + stack = ctx->stack; + ctx->stack = stack->next; + + v = stack->value; + + free (stack); + + return (v); +} /* }}} yajl_value_t *context_pop */ + +static int object_add_keyval (context_t *ctx, /* {{{ */ + yajl_value_t *obj, yajl_value_t *key, yajl_value_t *value) +{ + yajl_value_object_t *o; + yajl_value_t **tmp; + + /* We're checking for NULL in "context_add_value" or its callers. */ + assert (ctx != NULL); + assert (obj != NULL); + assert (key != NULL); + assert (value != NULL); + + /* We're assuring that the key is a string in "context_add_value". */ + assert (YAJL_IS_STRING (key)); + + /* We're assuring that "obj" is an object in "context_add_value". */ + o = YAJL_TO_OBJECT (obj); + assert (o != NULL); + + tmp = realloc (o->keys, sizeof (*o->keys) * (o->children_num + 1)); + if (tmp == NULL) + RETURN_ERROR (ctx, ENOMEM, "Out of memory"); + o->keys = tmp; + + tmp = realloc (o->values, sizeof (*o->values) * (o->children_num + 1)); + if (tmp == NULL) + RETURN_ERROR (ctx, ENOMEM, "Out of memory"); + o->values = tmp; + + o->keys[o->children_num] = key; + o->values[o->children_num] = value; + o->children_num++; + + return (0); +} /* }}} int object_add_keyval */ + +static int array_add_value (context_t *ctx, /* {{{ */ + yajl_value_t *array, yajl_value_t *value) +{ + yajl_value_array_t *a; + yajl_value_t **tmp; + + /* We're checking for NULL pointers in "context_add_value" or its + * callers. */ + assert (ctx != NULL); + assert (array != NULL); + assert (value != NULL); + + /* "context_add_value" will only call us with array values. */ + a = YAJL_TO_ARRAY (array); + assert (a != NULL); + + tmp = realloc (a->values, sizeof (*a->values) * (a->values_num + 1)); + if (tmp == NULL) + RETURN_ERROR (ctx, ENOMEM, "Out of memory"); + a->values = tmp; + a->values[a->values_num] = value; + a->values_num++; + + return (0); +} /* }}} int array_add_value */ + +/* + * Add a value to the value on top of the stack or the "root" member in the + * context if the end of the parsing process is reached. + */ +static int context_add_value (context_t *ctx, yajl_value_t *v) /* {{{ */ +{ + /* We're checking for NULL values in all the calling functions. */ + assert (ctx != NULL); + assert (v != NULL); + + /* + * There are three valid states in which this function may be called: + * - There is no value on the stack => This is the only value. This is the + * last step done when parsing a document. We assign the value to the + * "root" member and return. + * - The value on the stack is an object. In this case store the key on the + * stack or, if the key has already been read, add key and value to the + * object. + * - The value on the stack is an array. In this case simply add the value + * and return. + */ + if (ctx->stack == NULL) + { + assert (ctx->root == NULL); + ctx->root = v; + return (0); + } + else if (YAJL_IS_OBJECT (ctx->stack->value)) + { + if (ctx->stack->key == NULL) + { + if (!YAJL_IS_STRING (v)) + RETURN_ERROR (ctx, EINVAL, "context_add_value: " + "Object key is not a string (%#04"PRIx8")", + v->type); + + ctx->stack->key = v; + return (0); + } + else /* if (ctx->key != NULL) */ + { + yajl_value_t *key; + + key = ctx->stack->key; + ctx->stack->key = NULL; + return (object_add_keyval (ctx, ctx->stack->value, key, v)); + } + } + else if (YAJL_IS_ARRAY (ctx->stack->value)) + { + return (array_add_value (ctx, ctx->stack->value, v)); + } + else + { + RETURN_ERROR (ctx, EINVAL, "context_add_value: Cannot add value to " + "a value of type %#04"PRIx8" (not a composite type)", + ctx->stack->value->type); + } +} /* }}} int context_add_value */ + +static int handle_string (void *ctx, /* {{{ */ + const unsigned char *string, unsigned int string_length) +{ + yajl_value_t *v; + yajl_value_string_t *s; + + v = value_alloc (YAJL_TYPE_STRING); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + s = YAJL_TO_STRING (v); + + s->value = malloc (string_length + 1); + if (s->value == NULL) + { + free (v); + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + } + memcpy (s->value, string, string_length); + s->value[string_length] = 0; + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_string */ + +static int handle_number (void *ctx, /* {{{ */ + const char *string, unsigned int string_length) +{ + yajl_value_t *v; + yajl_value_number_t *n; + char *endptr; + + v = value_alloc (YAJL_TYPE_STRING); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + n = YAJL_TO_NUMBER (v); + + n->value_raw = malloc (string_length + 1); + if (n->value_raw == NULL) + { + free (v); + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + } + memcpy (n->value_raw, string, string_length); + n->value_raw[string_length] = 0; + + n->flags = 0; + + endptr = NULL; + errno = 0; + n->value_int = (int64_t) strtoll (n->value_raw, &endptr, /* base = */ 10); + if ((errno == 0) && (endptr != NULL) && (*endptr == 0)) + n->flags |= YAJL_NUMBER_INT_VALID; + + endptr = NULL; + errno = 0; + n->value_double = strtod (n->value_raw, &endptr); + if ((errno == 0) && (endptr != NULL) && (*endptr == 0)) + n->flags |= YAJL_NUMBER_DOUBLE_VALID; + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_number */ + +static int handle_start_map (void *ctx) /* {{{ */ +{ + yajl_value_t *v; + yajl_value_object_t *o; + + v = value_alloc (YAJL_TYPE_OBJECT); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + o = YAJL_TO_OBJECT (v); + o->keys = NULL; + o->values = NULL; + o->children_num = 0; + + return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_start_map */ + +static int handle_end_map (void *ctx) /* {{{ */ +{ + yajl_value_t *v; + + v = context_pop (ctx); + if (v == NULL) + return (STATUS_ABORT); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_end_map */ + +static int handle_start_array (void *ctx) /* {{{ */ +{ + yajl_value_t *v; + yajl_value_array_t *a; + + v = value_alloc (YAJL_TYPE_ARRAY); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + a = YAJL_TO_ARRAY (v); + a->values = NULL; + a->values_num = 0; + + return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_start_array */ + +static int handle_end_array (void *ctx) /* {{{ */ +{ + yajl_value_t *v; + + v = context_pop (ctx); + if (v == NULL) + return (STATUS_ABORT); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_end_array */ + +static int handle_boolean (void *ctx, int boolean_value) /* {{{ */ +{ + yajl_value_t *v; + + v = value_alloc (boolean_value ? YAJL_TYPE_TRUE : YAJL_TYPE_FALSE); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_boolean */ + +static int handle_null (void *ctx) /* {{{ */ +{ + yajl_value_t *v; + + v = value_alloc (YAJL_TYPE_NULL); + if (v == NULL) + RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); + + return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); +} /* }}} int handle_null */ + +/* + * Public functions + */ +yajl_value_t *yajl_tree_parse (const char *input, /* {{{ */ + char *error_buffer, size_t error_buffer_size) +{ + static const yajl_callbacks callbacks = + { + /* null = */ handle_null, + /* boolean = */ handle_boolean, + /* integer = */ NULL, + /* double = */ NULL, + /* number = */ handle_number, + /* string = */ handle_string, + /* start map = */ handle_start_map, + /* map key = */ handle_string, + /* end map = */ handle_end_map, + /* start array = */ handle_start_array, + /* end array = */ handle_end_array + }; + + yajl_parser_config parser_config = + { + /* allowComments = */ 1, + /* checkUTF8 = */ 0 + }; + + context_t ctx = + { + /* key = */ NULL, + /* stack = */ NULL, + /* errbuf = */ error_buffer, + /* errbuf_size = */ error_buffer_size + }; + + yajl_handle handle; + yajl_status status; + + if (error_buffer != NULL) + memset (error_buffer, 0, error_buffer_size); + + handle = yajl_alloc (&callbacks, &parser_config, + /* alloc funcs = */ NULL, &ctx); + + status = yajl_parse (handle, + (unsigned char *) input, + (unsigned int) strlen (input)); + if (status != yajl_status_ok) + return (NULL); + + status = yajl_parse_complete (handle); + if (status != yajl_status_ok) + return (NULL); + + yajl_free (handle); + return (ctx.root); +} /* }}} yajl_value_t *yajl_tree_parse */ + +void yajl_tree_free (yajl_value_t *v) /* {{{ */ +{ + if (v == NULL) + return; + + if (YAJL_IS_STRING (v)) + { + yajl_value_string_t *s = YAJL_TO_STRING (v); + + free (s->value); + free (v); + + return; + } + else if (YAJL_IS_NUMBER (v)) + { + yajl_value_number_t *n = YAJL_TO_NUMBER (v); + + free (n->value_raw); + free (v); + + return; + } + else if (YAJL_TO_OBJECT (v)) + { + yajl_object_free (v); + return; + } + else if (YAJL_TO_ARRAY (v)) + { + yajl_array_free (v); + return; + } + else /* if (YAJL_TYPE_TRUE or YAJL_TYPE_FALSE or YAJL_TYPE_NULL) */ + { + free (v); + return; + } +} /* void yajl_tree_free */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ |