1    | /*
2    |  * Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
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 "yajl/yajl_parse.h"
18   | #include "yajl_lex.h"
19   | #include "yajl_parser.h"
20   | #include "yajl_alloc.h"
21   | 
22   | #include <stdlib.h>
23   | #include <string.h>
24   | #include <stdarg.h>
25   | #include <assert.h>
26   | 
27   | const char *
28   | yajl_status_to_string(yajl_status stat)
29   | {
30   |     const char * statStr = "unknown";
31   |     switch (stat) {
32   |         case yajl_status_ok:
33   |             statStr = "ok, no error";
34   |             break;
35   |         case yajl_status_client_canceled:
36   |             statStr = "client canceled parse";
37   |             break;
38   |         case yajl_status_error:
39   |             statStr = "parse error";
40   |             break;
41   |     }
42   |     return statStr;
43   | }
44   | 
45   | /*+
46   |  * allocate a parser handle
47   |  *
48   |  *  \param callbacks  a yajl callbacks structure specifying the
49   |  *                    functions to call when different JSON entities
50   |  *                    are encountered in the input text.  May be NULL,
51   |  *                    which is only useful for validation.
52   |  *
53   |  *  \param afs        memory allocation functions, may be NULL for to use
54   |  *                    C runtime library routines (malloc and friends) 
55   |  *
56   |  *  \param ctx        a context pointer that will be passed to callbacks.
57   |  +*/
58   | yajl_handle
59   | yajl_alloc(const yajl_callbacks * callbacks,
60   |            yajl_alloc_funcs * afs,
61   |            void * ctx)
62   | {
63   |     yajl_handle hand = NULL;
64   |     yajl_alloc_funcs afsBuffer;
65   | 
66   |     /* first order of business is to set up memory allocation routines */
67   |     if (afs != NULL) {
68   |         if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
69   |         {
70   |             return NULL;
71   |         }
72   |     } else {
73   |         yajl_set_default_alloc_funcs(&afsBuffer);
74   |         afs = &afsBuffer;
75   |     }
76   | 
77   |     hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
78   | 
79   |     /* copy in pointers to allocation routines */
80   |     hand->alloc = *afs;
81   | 
82   |     hand->callbacks = callbacks;
83   |     hand->ctx = ctx;
84   |     hand->lexer = NULL;
85   |     hand->bytesConsumed = 0;
86   |     hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
87   |     hand->flags	    = 0;
88   |     yajl_bs_init(hand->stateStack, &(hand->alloc));
89   |     yajl_bs_push(hand->stateStack, yajl_state_start);
90   | 
91   |     return hand;
92   | }
93   | 
94   | /*+
95   |  * allow the modification of parser options subsequent to handle allocation (via
96   |  * yajl_alloc)
97   |  *
98   |  *  \returns zero in case of errors, non-zero otherwise
99   |  +*/
100  | int
101  | yajl_config(yajl_handle h, yajl_option opt, ...)
102  | {
103  |     int rv = 1;
104  |     va_list ap;
105  |     va_start(ap, opt);
106  | 
107  |     switch(opt) {
108  |         case yajl_allow_comments:
109  |         case yajl_dont_validate_strings:
110  |         case yajl_allow_trailing_garbage:
111  |         case yajl_allow_multiple_values:
112  |         case yajl_allow_partial_values:
113  |             if (va_arg(ap, int)) h->flags |= opt;
114  |             else h->flags &= ~opt;
115  |             break;
116  |         default:
117  |             rv = 0;
118  |     }
119  |     va_end(ap);
120  | 
121  |     return rv;
122  | }
123  | 
124  | /*+ free a parser handle +*/
125  | void
126  | yajl_free(yajl_handle handle)
127  | {
128  |     yajl_bs_free(handle->stateStack);
129  |     yajl_buf_free(handle->decodeBuf);
130  |     if (handle->lexer) {
131  |         yajl_lex_free(handle->lexer);
132  |         handle->lexer = NULL;
133  |     }
134  |     YA_FREE(&(handle->alloc), handle);
135  | }
136  | 
137  | /*+
138  |  * Parse some json!
139  |  *
140  |  *  \param hand - a handle to the json parser allocated with yajl_alloc
141  |  *
142  |  *  \param jsonText - a pointer to the UTF8 json text to be parsed
143  |  *
144  |  *  \param jsonTextLength - the length, in bytes, of input text
145  |  +*/
146  | yajl_status
147  | yajl_parse(yajl_handle hand, const unsigned char * jsonText,
148  |            size_t jsonTextLen)
149  | {
150  |     yajl_status status;
151  | 
152  |     /* lazy allocation of the lexer */
153  |     if (hand->lexer == NULL) {
154  |         hand->lexer = yajl_lex_alloc(&(hand->alloc),
155  |                                      (int) hand->flags & yajl_allow_comments,
156  |                                      !(hand->flags & yajl_dont_validate_strings));
157  |     }
158  | 
159  |     status = yajl_do_parse(hand, jsonText, jsonTextLen);
160  |     return status;
161  | }
162  | 
163  | 
164  | /*+
165  |  * Parse any remaining buffered json.
166  |  *
167  |  * Since yajl is a stream-based parser, without an explicit end of input, yajl
168  |  * sometimes can't decide if content at the end of the stream is valid or not.
169  |  * For example, if "1" has been fed in, yajl can't know whether another digit is
170  |  * next or some character that would terminate the integer token.
171  |  *
172  |  *  \param hand - a handle to the json parser allocated with yajl_alloc
173  |  +*/
174  | yajl_status
175  | yajl_complete_parse(yajl_handle hand)
176  | {
177  |     /* The lexer is lazy allocated in the first call to parse.  If parse is
178  |      * never called, then no data was provided to parse at all.  This is a
179  |      * "premature EOF" error unless yajl_allow_partial_values is specified.
180  |      * allocating the lexer now is the simplest possible way to handle this
181  |      * case while preserving all the other semantics of the parser
182  |      * (multiple values, partial values, etc). */
183  |     if (hand->lexer == NULL) {
184  |         hand->lexer = yajl_lex_alloc(&(hand->alloc),
185  |                                      (int) hand->flags & yajl_allow_comments,
186  |                                      !(hand->flags & yajl_dont_validate_strings));
187  |     }
188  | 
189  |     return yajl_do_finish(hand);
190  | }
191  | 
192  | /*+
193  |  * get an error string describing the state of the parse.
194  |  *
195  |  * If verbose is non-zero, the message will include the JSON text where the
196  |  * error occurred, along with an arrow pointing to the specific char.
197  |  *
198  |  *  \returns A dynamically allocated string will be returned which should be
199  |  *  freed with yajl_free_error
200  |  +*/
201  | unsigned char *
202  | yajl_get_error(yajl_handle hand, int verbose,
203  |                const unsigned char * jsonText, size_t jsonTextLen)
204  | {
205  |     return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose);
206  | }
207  | 
208  | /*+
209  |  * get the amount of data consumed from the last chunk passed to YAJL.
210  |  *
211  |  * In the case of a successful parse this can help you understand if
212  |  * the entire buffer was consumed (which will allow you to handle
213  |  * "junk at end of input").
214  |  *
215  |  * In the event an error is encountered during parsing, this function
216  |  * affords the client a way to get the offset into the most recent
217  |  * chunk where the error occurred.  0 will be returned if no error
218  |  * was encountered.
219  |  +*/
220  | size_t
221  | yajl_get_bytes_consumed(yajl_handle hand)
222  | {
223  |     if (!hand) return 0;
224  |     else return hand->bytesConsumed;
225  | }
226  | 
227  | 
228  | /*+ free an error returned from yajl_get_error +*/
229  | void
230  | yajl_free_error(yajl_handle hand, unsigned char * str)
231  | {
232  |     /* use memory allocation functions if set */
233  |     YA_FREE(&(hand->alloc), str);
234  | }
235  | 
236  | /* XXX: add utility routines to parse from file */