root/src/yajl-lib/yajl_tree.c

Revision bd6d64f84276dfd6f5c69702a74e5dbc691b389c, 13.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

import yajl and switch the httptrap check to use it

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2010-2011  Florian Forster  <ff at octo.it>
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <assert.h>
22
23 #include "yajl_tree.h"
24 #include "yajl_parse.h"
25
26 #include "yajl_parser.h"
27
28 #if defined(_WIN32) || defined(WIN32)
29 #define snprintf sprintf_s
30 #endif
31
32 #define STATUS_CONTINUE 1
33 #define STATUS_ABORT    0
34
35 struct stack_elem_s;
36 typedef struct stack_elem_s stack_elem_t;
37 struct stack_elem_s
38 {
39     char * key;
40     yajl_val value;
41     stack_elem_t *next;
42 };
43
44 struct context_s
45 {
46     stack_elem_t *stack;
47     yajl_val root;
48     char *errbuf;
49     size_t errbuf_size;
50 };
51 typedef struct context_s context_t;
52
53 #define RETURN_ERROR(ctx,retval,...) {                                  \
54         if ((ctx)->errbuf != NULL)                                      \
55             snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__);  \
56         return (retval);                                                \
57     }
58
59 static yajl_val value_alloc (yajl_type type)
60 {
61     yajl_val v;
62
63     v = malloc (sizeof (*v));
64     if (v == NULL) return (NULL);
65     memset (v, 0, sizeof (*v));
66     v->type = type;
67
68     return (v);
69 }
70
71 static void yajl_object_free (yajl_val v)
72 {
73     size_t i;
74
75     if (!YAJL_IS_OBJECT(v)) return;
76
77     for (i = 0; i < v->u.object.len; i++)
78     {
79         free((char *) v->u.object.keys[i]);
80         v->u.object.keys[i] = NULL;
81         yajl_tree_free (v->u.object.values[i]);
82         v->u.object.values[i] = NULL;
83     }
84
85     free((void*) v->u.object.keys);
86     free(v->u.object.values);
87     free(v);
88 }
89
90 static void yajl_array_free (yajl_val v)
91 {
92     size_t i;
93
94     if (!YAJL_IS_ARRAY(v)) return;
95
96     for (i = 0; i < v->u.array.len; i++)
97     {
98         yajl_tree_free (v->u.array.values[i]);
99         v->u.array.values[i] = NULL;
100     }
101
102     free(v->u.array.values);
103     free(v);
104 }
105
106 /*
107  * Parsing nested objects and arrays is implemented using a stack. When a new
108  * object or array starts (a curly or a square opening bracket is read), an
109  * appropriate value is pushed on the stack. When the end of the object is
110  * reached (an appropriate closing bracket has been read), the value is popped
111  * off the stack and added to the enclosing object using "context_add_value".
112  */
113 static int context_push(context_t *ctx, yajl_val v)
114 {
115     stack_elem_t *stack;
116
117     stack = malloc (sizeof (*stack));
118     if (stack == NULL)
119         RETURN_ERROR (ctx, ENOMEM, "Out of memory");
120     memset (stack, 0, sizeof (*stack));
121
122     assert ((ctx->stack == NULL)
123             || YAJL_IS_OBJECT (v)
124             || YAJL_IS_ARRAY (v));
125
126     stack->value = v;
127     stack->next = ctx->stack;
128     ctx->stack = stack;
129
130     return (0);
131 }
132
133 static yajl_val context_pop(context_t *ctx)
134 {
135     stack_elem_t *stack;
136     yajl_val v;
137
138     if (ctx->stack == NULL)
139         RETURN_ERROR (ctx, NULL, "context_pop: "
140                       "Bottom of stack reached prematurely");
141
142     stack = ctx->stack;
143     ctx->stack = stack->next;
144
145     v = stack->value;
146
147     free (stack);
148
149     return (v);
150 }
151
152 static int object_add_keyval(context_t *ctx,
153                              yajl_val obj, char *key, yajl_val value)
154 {
155     const char **tmpk;
156     yajl_val *tmpv;
157
158     /* We're checking for NULL in "context_add_value" or its callers. */
159     assert (ctx != NULL);
160     assert (obj != NULL);
161     assert (key != NULL);
162     assert (value != NULL);
163
164     /* We're assuring that "obj" is an object in "context_add_value". */
165     assert(YAJL_IS_OBJECT(obj));
166
167     tmpk = realloc((void *) obj->u.object.keys, sizeof(*(obj->u.object.keys)) * (obj->u.object.len + 1));
168     if (tmpk == NULL)
169         RETURN_ERROR(ctx, ENOMEM, "Out of memory");
170     obj->u.object.keys = tmpk;
171
172     tmpv = realloc(obj->u.object.values, sizeof (*obj->u.object.values) * (obj->u.object.len + 1));
173     if (tmpv == NULL)
174         RETURN_ERROR(ctx, ENOMEM, "Out of memory");
175     obj->u.object.values = tmpv;
176
177     obj->u.object.keys[obj->u.object.len] = key;
178     obj->u.object.values[obj->u.object.len] = value;
179     obj->u.object.len++;
180
181     return (0);
182 }
183
184 static int array_add_value (context_t *ctx,
185                             yajl_val array, yajl_val value)
186 {
187     yajl_val *tmp;
188
189     /* We're checking for NULL pointers in "context_add_value" or its
190      * callers. */
191     assert (ctx != NULL);
192     assert (array != NULL);
193     assert (value != NULL);
194
195     /* "context_add_value" will only call us with array values. */
196     assert(YAJL_IS_ARRAY(array));
197    
198     tmp = realloc(array->u.array.values,
199                   sizeof(*(array->u.array.values)) * (array->u.array.len + 1));
200     if (tmp == NULL)
201         RETURN_ERROR(ctx, ENOMEM, "Out of memory");
202     array->u.array.values = tmp;
203     array->u.array.values[array->u.array.len] = value;
204     array->u.array.len++;
205
206     return 0;
207 }
208
209 /*
210  * Add a value to the value on top of the stack or the "root" member in the
211  * context if the end of the parsing process is reached.
212  */
213 static int context_add_value (context_t *ctx, yajl_val v)
214 {
215     /* We're checking for NULL values in all the calling functions. */
216     assert (ctx != NULL);
217     assert (v != NULL);
218
219     /*
220      * There are three valid states in which this function may be called:
221      *   - There is no value on the stack => This is the only value. This is the
222      *     last step done when parsing a document. We assign the value to the
223      *     "root" member and return.
224      *   - The value on the stack is an object. In this case store the key on the
225      *     stack or, if the key has already been read, add key and value to the
226      *     object.
227      *   - The value on the stack is an array. In this case simply add the value
228      *     and return.
229      */
230     if (ctx->stack == NULL)
231     {
232         assert (ctx->root == NULL);
233         ctx->root = v;
234         return (0);
235     }
236     else if (YAJL_IS_OBJECT (ctx->stack->value))
237     {
238         if (ctx->stack->key == NULL)
239         {
240             if (!YAJL_IS_STRING (v))
241                 RETURN_ERROR (ctx, EINVAL, "context_add_value: "
242                               "Object key is not a string (%#04x)",
243                               v->type);
244
245             ctx->stack->key = v->u.string;
246             v->u.string = NULL;
247             free(v);
248             return (0);
249         }
250         else /* if (ctx->key != NULL) */
251         {
252             char * key;
253
254             key = ctx->stack->key;
255             ctx->stack->key = NULL;
256             return (object_add_keyval (ctx, ctx->stack->value, key, v));
257         }
258     }
259     else if (YAJL_IS_ARRAY (ctx->stack->value))
260     {
261         return (array_add_value (ctx, ctx->stack->value, v));
262     }
263     else
264     {
265         RETURN_ERROR (ctx, EINVAL, "context_add_value: Cannot add value to "
266                       "a value of type %#04x (not a composite type)",
267                       ctx->stack->value->type);
268     }
269 }
270
271 static int handle_string (void *ctx,
272                           const unsigned char *string, size_t string_length)
273 {
274     yajl_val v;
275
276     v = value_alloc (yajl_t_string);
277     if (v == NULL)
278         RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
279
280     v->u.string = malloc (string_length + 1);
281     if (v->u.string == NULL)
282     {
283         free (v);
284         RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
285     }
286     memcpy(v->u.string, string, string_length);
287     v->u.string[string_length] = 0;
288
289     return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
290 }
291
292 static int handle_number (void *ctx, const char *string, size_t string_length)
293 {
294     yajl_val v;
295     char *endptr;
296
297     v = value_alloc(yajl_t_number);
298     if (v == NULL)
299         RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
300
301     v->u.number.r = malloc(string_length + 1);
302     if (v->u.number.r == NULL)
303     {
304         free(v);
305         RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
306     }
307     memcpy(v->u.number.r, string, string_length);
308     v->u.number.r[string_length] = 0;
309
310     v->u.number.flags = 0;
311
312     errno = 0;
313     v->u.number.i = yajl_parse_integer((const unsigned char *) v->u.number.r,
314                                        strlen(v->u.number.r));
315     if (errno == 0)
316         v->u.number.flags |= YAJL_NUMBER_INT_VALID;
317
318     endptr = NULL;
319     errno = 0;
320     v->u.number.d = strtod(v->u.number.r, &endptr);
321     if ((errno == 0) && (endptr != NULL) && (*endptr == 0))
322         v->u.number.flags |= YAJL_NUMBER_DOUBLE_VALID;
323
324     return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
325 }
326
327 static int handle_start_map (void *ctx)
328 {
329     yajl_val v;
330
331     v = value_alloc(yajl_t_object);
332     if (v == NULL)
333         RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
334
335     v->u.object.keys = NULL;
336     v->u.object.values = NULL;
337     v->u.object.len = 0;
338
339     return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
340 }
341
342 static int handle_end_map (void *ctx)
343 {
344     yajl_val v;
345
346     v = context_pop (ctx);
347     if (v == NULL)
348         return (STATUS_ABORT);
349
350     return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
351 }
352
353 static int handle_start_array (void *ctx)
354 {
355     yajl_val v;
356
357     v = value_alloc(yajl_t_array);
358     if (v == NULL)
359         RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
360
361     v->u.array.values = NULL;
362     v->u.array.len = 0;
363
364     return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
365 }
366
367 static int handle_end_array (void *ctx)
368 {
369     yajl_val v;
370
371     v = context_pop (ctx);
372     if (v == NULL)
373         return (STATUS_ABORT);
374
375     return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
376 }
377
378 static int handle_boolean (void *ctx, int boolean_value)
379 {
380     yajl_val v;
381
382     v = value_alloc (boolean_value ? yajl_t_true : yajl_t_false);
383     if (v == NULL)
384         RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
385
386     return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
387 }
388
389 static int handle_null (void *ctx)
390 {
391     yajl_val v;
392
393     v = value_alloc (yajl_t_null);
394     if (v == NULL)
395         RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
396
397     return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
398 }
399
400 /*
401  * Public functions
402  */
403 yajl_val yajl_tree_parse (const char *input,
404                           char *error_buffer, size_t error_buffer_size)
405 {
406     static const yajl_callbacks callbacks =
407         {
408             /* null        = */ handle_null,
409             /* boolean     = */ handle_boolean,
410             /* integer     = */ NULL,
411             /* double      = */ NULL,
412             /* number      = */ handle_number,
413             /* string      = */ handle_string,
414             /* start map   = */ handle_start_map,
415             /* map key     = */ handle_string,
416             /* end map     = */ handle_end_map,
417             /* start array = */ handle_start_array,
418             /* end array   = */ handle_end_array
419         };
420
421     yajl_handle handle;
422     yajl_status status;
423     char * internal_err_str;
424         context_t ctx = { NULL, NULL, NULL, 0 };
425
426         ctx.errbuf = error_buffer;
427         ctx.errbuf_size = error_buffer_size;
428
429     if (error_buffer != NULL)
430         memset (error_buffer, 0, error_buffer_size);
431
432     handle = yajl_alloc (&callbacks, NULL, &ctx);
433     yajl_config(handle, yajl_allow_comments, 1);
434
435     status = yajl_parse(handle,
436                         (unsigned char *) input,
437                         strlen (input));
438     status = yajl_complete_parse (handle);
439     if (status != yajl_status_ok) {
440         if (error_buffer != NULL && error_buffer_size > 0) {
441                internal_err_str = (char *) yajl_get_error(handle, 1,
442                      (const unsigned char *) input,
443                      strlen(input));
444              snprintf(error_buffer, error_buffer_size, "%s", internal_err_str);
445              YA_FREE(&(handle->alloc), internal_err_str);
446         }
447         yajl_free (handle);
448         return NULL;
449     }
450
451     yajl_free (handle);
452     return (ctx.root);
453 }
454
455 yajl_val yajl_tree_get(yajl_val n, const char ** path, yajl_type type)
456 {
457     if (!path) return NULL;
458     while (n && *path) {
459         unsigned int i;
460         int len;
461
462         if (n->type != yajl_t_object) return NULL;
463         len = n->u.object.len;
464         for (i = 0; i < len; i++) {
465             if (!strcmp(*path, n->u.object.keys[i])) {
466                 n = n->u.object.values[i];
467                 break;
468             }
469         }
470         if (i == len) return NULL;
471         path++;
472     }
473     if (n && type != yajl_t_any && type != n->type) n = NULL;
474     return n;
475 }
476
477 void yajl_tree_free (yajl_val v)
478 {
479     if (v == NULL) return;
480
481     if (YAJL_IS_STRING(v))
482     {
483         free(v->u.string);
484         free(v);
485     }
486     else if (YAJL_IS_NUMBER(v))
487     {
488         free(v->u.number.r);
489         free(v);
490     }
491     else if (YAJL_GET_OBJECT(v))
492     {
493         yajl_object_free(v);
494     }
495     else if (YAJL_GET_ARRAY(v))
496     {
497         yajl_array_free(v);
498     }
499     else /* if (yajl_t_true or yajl_t_false or yajl_t_null) */
500     {
501         free(v);
502     }
503 }
Note: See TracBrowser for help on using the browser.