123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /*
- * Copyright (c) 2010-2011 Florian Forster <ff at octo.it>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #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"
- #include "yajl_parser.h"
- #ifdef WIN32
- #define snprintf sprintf_s
- #endif
- #define STATUS_CONTINUE 1
- #define STATUS_ABORT 0
- struct stack_elem_s;
- typedef struct stack_elem_s stack_elem_t;
- struct stack_elem_s
- {
- char * key;
- yajl_val value;
- stack_elem_t *next;
- };
- struct context_s
- {
- stack_elem_t *stack;
- yajl_val root;
- char *errbuf;
- size_t errbuf_size;
- };
- typedef struct context_s context_t;
- #define RETURN_ERROR(ctx,retval,...) { \
- if ((ctx)->errbuf != NULL) \
- snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__); \
- return (retval); \
- }
- static yajl_val value_alloc (yajl_type type)
- {
- yajl_val v;
- v = malloc (sizeof (*v));
- if (v == NULL) return (NULL);
- memset (v, 0, sizeof (*v));
- v->type = type;
- return (v);
- }
- static void yajl_object_free (yajl_val v)
- {
- size_t i;
- if (!YAJL_IS_OBJECT(v)) return;
- for (i = 0; i < v->u.object.len; i++)
- {
- free((char *) v->u.object.keys[i]);
- v->u.object.keys[i] = NULL;
- yajl_tree_free (v->u.object.values[i]);
- v->u.object.values[i] = NULL;
- }
- free((void*) v->u.object.keys);
- free(v->u.object.values);
- free(v);
- }
- static void yajl_array_free (yajl_val v)
- {
- size_t i;
- if (!YAJL_IS_ARRAY(v)) return;
- for (i = 0; i < v->u.array.len; i++)
- {
- yajl_tree_free (v->u.array.values[i]);
- v->u.array.values[i] = NULL;
- }
- free(v->u.array.values);
- free(v);
- }
- /*
- * 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_val 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);
- }
- static yajl_val context_pop(context_t *ctx)
- {
- stack_elem_t *stack;
- yajl_val 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);
- }
- static int object_add_keyval(context_t *ctx,
- yajl_val obj, char *key, yajl_val value)
- {
- const char **tmpk;
- yajl_val *tmpv;
- /* 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 "obj" is an object in "context_add_value". */
- assert(YAJL_IS_OBJECT(obj));
- tmpk = realloc((void *) obj->u.object.keys, sizeof(*(obj->u.object.keys)) * (obj->u.object.len + 1));
- if (tmpk == NULL)
- RETURN_ERROR(ctx, ENOMEM, "Out of memory");
- obj->u.object.keys = tmpk;
- tmpv = realloc(obj->u.object.values, sizeof (*obj->u.object.values) * (obj->u.object.len + 1));
- if (tmpv == NULL)
- RETURN_ERROR(ctx, ENOMEM, "Out of memory");
- obj->u.object.values = tmpv;
- obj->u.object.keys[obj->u.object.len] = key;
- obj->u.object.values[obj->u.object.len] = value;
- obj->u.object.len++;
- return (0);
- }
- static int array_add_value (context_t *ctx,
- yajl_val array, yajl_val value)
- {
- yajl_val *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. */
- assert(YAJL_IS_ARRAY(array));
-
- tmp = realloc(array->u.array.values,
- sizeof(*(array->u.array.values)) * (array->u.array.len + 1));
- if (tmp == NULL)
- RETURN_ERROR(ctx, ENOMEM, "Out of memory");
- array->u.array.values = tmp;
- array->u.array.values[array->u.array.len] = value;
- array->u.array.len++;
- return 0;
- }
- /*
- * 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_val 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 (%#04x)",
- v->type);
- ctx->stack->key = v->u.string;
- v->u.string = NULL;
- free(v);
- return (0);
- }
- else /* if (ctx->key != NULL) */
- {
- char * 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 %#04x (not a composite type)",
- ctx->stack->value->type);
- }
- }
- static int handle_string (void *ctx,
- const unsigned char *string, size_t string_length)
- {
- yajl_val v;
- v = value_alloc (yajl_t_string);
- if (v == NULL)
- RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
- v->u.string = malloc (string_length + 1);
- if (v->u.string == NULL)
- {
- free (v);
- RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
- }
- memcpy(v->u.string, string, string_length);
- v->u.string[string_length] = 0;
- return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
- }
- static int handle_number (void *ctx, const char *string, size_t string_length)
- {
- yajl_val v;
- char *endptr;
- v = value_alloc(yajl_t_number);
- if (v == NULL)
- RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
- v->u.number.r = malloc(string_length + 1);
- if (v->u.number.r == NULL)
- {
- free(v);
- RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
- }
- memcpy(v->u.number.r, string, string_length);
- v->u.number.r[string_length] = 0;
- v->u.number.flags = 0;
- endptr = NULL;
- errno = 0;
- v->u.number.i = yajl_parse_integer((const unsigned char *) v->u.number.r,
- strlen(v->u.number.r));
- if ((errno == 0) && (endptr != NULL) && (*endptr == 0))
- v->u.number.flags |= YAJL_NUMBER_INT_VALID;
- endptr = NULL;
- errno = 0;
- v->u.number.ui = yajl_parse_unsigned_integer((const unsigned char *) v->u.number.r,
- strlen(v->u.number.r));
- if ((errno == 0) && (endptr != NULL) && (*endptr == 0))
- v->u.number.flags |= YAJL_NUMBER_UINT_VALID;
- endptr = NULL;
- errno = 0;
- v->u.number.d = strtod(v->u.number.r, &endptr);
- if ((errno == 0) && (endptr != NULL) && (*endptr == 0))
- v->u.number.flags |= YAJL_NUMBER_DOUBLE_VALID;
- return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
- }
- static int handle_start_map (void *ctx)
- {
- yajl_val v;
- v = value_alloc(yajl_t_object);
- if (v == NULL)
- RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
- v->u.object.keys = NULL;
- v->u.object.values = NULL;
- v->u.object.len = 0;
- return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
- }
- static int handle_end_map (void *ctx)
- {
- yajl_val v;
- v = context_pop (ctx);
- if (v == NULL)
- return (STATUS_ABORT);
- return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
- }
- static int handle_start_array (void *ctx)
- {
- yajl_val v;
- v = value_alloc(yajl_t_array);
- if (v == NULL)
- RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
- v->u.array.values = NULL;
- v->u.array.len = 0;
- return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
- }
- static int handle_end_array (void *ctx)
- {
- yajl_val v;
- v = context_pop (ctx);
- if (v == NULL)
- return (STATUS_ABORT);
- return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
- }
- static int handle_boolean (void *ctx, int boolean_value)
- {
- yajl_val v;
- v = value_alloc (boolean_value ? yajl_t_true : yajl_t_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);
- }
- static int handle_null (void *ctx)
- {
- yajl_val v;
- v = value_alloc (yajl_t_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);
- }
- /*
- * Public functions
- */
- yajl_val 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,
- /* unsigned 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_handle handle;
- yajl_status status;
- context_t ctx = { NULL, NULL, NULL, 0 };
- ctx.errbuf = error_buffer;
- ctx.errbuf_size = error_buffer_size;
- if (error_buffer != NULL)
- memset (error_buffer, 0, error_buffer_size);
- handle = yajl_alloc (&callbacks, NULL, &ctx);
- yajl_config(handle, yajl_allow_comments, 1);
- status = yajl_parse(handle,
- (unsigned char *) input,
- strlen (input));
- status = yajl_complete_parse (handle);
- if (status != yajl_status_ok) {
- if (error_buffer != NULL && error_buffer_size > 0) {
- snprintf(
- error_buffer, error_buffer_size, "%s",
- (char *) yajl_get_error(handle, 1,
- (const unsigned char *) input,
- strlen(input)));
- }
- yajl_free (handle);
- return NULL;
- }
- yajl_free (handle);
- return (ctx.root);
- }
- yajl_val yajl_tree_get(yajl_val n, const char ** path, yajl_type type)
- {
- if (!path) return NULL;
- while (n && *path) {
- unsigned int i;
- if (n->type != yajl_t_object) return NULL;
- for (i = 0; i < n->u.object.len; i++) {
- if (!strcmp(*path, n->u.object.keys[i])) {
- n = n->u.object.values[i];
- break;
- }
- }
- if (i == n->u.object.len) return NULL;
- path++;
- }
- if (n && type != yajl_t_any && type != n->type) n = NULL;
- return n;
- }
- void yajl_tree_free (yajl_val v)
- {
- if (v == NULL) return;
- if (YAJL_IS_STRING(v))
- {
- free(v->u.string);
- free(v);
- }
- else if (YAJL_IS_NUMBER(v))
- {
- free(v->u.number.r);
- free(v);
- }
- else if (YAJL_GET_OBJECT(v))
- {
- yajl_object_free(v);
- }
- else if (YAJL_GET_ARRAY(v))
- {
- yajl_array_free(v);
- }
- else /* if (yajl_t_true or yajl_t_false or yajl_t_null) */
- {
- free(v);
- }
- }
|