This is Unofficial EPICS BASE Doxygen Site
yajl_parser.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2007-2011, Lloyd Hilaiel <lloyd@hilaiel.com>
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 <limits.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <assert.h>
24 #include <math.h>
25 
26 #include "yajl_parse.h"
27 #include "yajl_lex.h"
28 #include "yajl_parser.h"
29 #include "yajl_encode.h"
30 #include "yajl_bytestack.h"
31 
32 #ifndef LLONG_MAX
33 #define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
34 #define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1)
35 #endif
36 
37 #define MAX_VALUE_TO_MULTIPLY ((LLONG_MAX / 10) + (LLONG_MAX % 10))
38 
39  /* same semantics as strtol */
40 long long
41 yajl_parse_integer(const unsigned char *number, size_t length)
42 {
43  long long ret = 0;
44  long sign = 1;
45  const unsigned char *pos = number;
46  if (*pos == '-') { pos++; sign = -1; }
47  if (*pos == '+') { pos++; }
48 
49  while (pos < number + length) {
50  if ( ret > MAX_VALUE_TO_MULTIPLY ) {
51  errno = ERANGE;
52  return sign == 1 ? LLONG_MAX : LLONG_MIN;
53  }
54  ret *= 10;
55  if (LLONG_MAX - ret < (*pos - '0')) {
56  errno = ERANGE;
57  return sign == 1 ? LLONG_MAX : LLONG_MIN;
58  }
59  ret += (*pos++ - '0');
60  }
61 
62  return sign * ret;
63 }
64 
65 unsigned char *
66 yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
67  size_t jsonTextLen, int verbose)
68 {
69  size_t offset = hand->bytesConsumed;
70  unsigned char * str;
71  const char * errorType = NULL;
72  const char * errorText = NULL;
73  char text[72];
74  const char * arrow = " (right here) ------^\n";
75 
77  errorType = "parse";
78  errorText = hand->parseError;
80  errorType = "lexical";
82  } else {
83  errorType = "unknown";
84  }
85 
86  {
87  size_t memneeded = 0;
88  memneeded += strlen(errorType);
89  memneeded += strlen(" error");
90  if (errorText != NULL) {
91  memneeded += strlen(": ");
92  memneeded += strlen(errorText);
93  }
94  str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2);
95  if (!str) return NULL;
96  str[0] = 0;
97  strcat((char *) str, errorType);
98  strcat((char *) str, " error");
99  if (errorText != NULL) {
100  strcat((char *) str, ": ");
101  strcat((char *) str, errorText);
102  }
103  strcat((char *) str, "\n");
104  }
105 
106  /* now we append as many spaces as needed to make sure the error
107  * falls at char 41, if verbose was specified */
108  if (verbose) {
109  size_t start, end, i;
110  size_t spacesNeeded;
111 
112  spacesNeeded = (offset < 30 ? 40 - offset : 10);
113  start = (offset >= 30 ? offset - 30 : 0);
114  end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30);
115 
116  for (i=0;i<spacesNeeded;i++) text[i] = ' ';
117 
118  for (;start < end;start++, i++) {
119  if (jsonText[start] != '\n' && jsonText[start] != '\r')
120  {
121  text[i] = jsonText[start];
122  }
123  else
124  {
125  text[i] = ' ';
126  }
127  }
128  assert(i <= 71);
129  text[i++] = '\n';
130  text[i] = 0;
131  {
132  char * newStr = (char *)
133  YA_MALLOC(&(hand->alloc), (unsigned int)(strlen((char *) str) +
134  strlen((char *) text) +
135  strlen(arrow) + 1));
136  if (newStr) {
137  newStr[0] = 0;
138  strcat((char *) newStr, (char *) str);
139  strcat((char *) newStr, text);
140  strcat((char *) newStr, arrow);
141  }
142  YA_FREE(&(hand->alloc), str);
143  str = (unsigned char *) newStr;
144  }
145  }
146  return str;
147 }
148 
149 /* check for client cancelation */
150 #define _CC_CHK(x) \
151  if (!(x)) { \
152  yajl_bs_set(hand->stateStack, yajl_state_parse_error); \
153  hand->parseError = \
154  "client cancelled parse via callback return value"; \
155  return yajl_status_client_canceled; \
156  }
157 
158 
161 {
162  yajl_status stat;
163  stat = yajl_do_parse(hand,(const unsigned char *) " ",1);
164 
165  if (stat != yajl_status_ok) return stat;
166 
167  switch(yajl_bs_current(hand->stateStack))
168  {
171  return yajl_status_error;
174  return yajl_status_ok;
175  default:
176  if (!(hand->flags & yajl_allow_partial_values))
177  {
179  hand->parseError = "premature EOF";
180  return yajl_status_error;
181  }
182  return yajl_status_ok;
183  }
184 }
185 
187 yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
188  size_t jsonTextLen)
189 {
190  yajl_tok tok;
191  const unsigned char * buf;
192  size_t bufLen;
193  size_t * offset = &(hand->bytesConsumed);
194 
195  *offset = 0;
196 
197  around_again:
198  switch (yajl_bs_current(hand->stateStack)) {
200  if (hand->flags & yajl_allow_multiple_values) {
202  goto around_again;
203  }
204  if (!(hand->flags & yajl_allow_trailing_garbage)) {
205  if (*offset != jsonTextLen) {
206  tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
207  offset, &buf, &bufLen);
208  if (tok != yajl_tok_eof) {
210  hand->parseError = "trailing garbage";
211  }
212  goto around_again;
213  }
214  }
215  return yajl_status_ok;
218  return yajl_status_error;
219  case yajl_state_start:
223  case yajl_state_array_start: {
224  /* for arrays and maps, we advance the state for this
225  * depth, then push the state of the next depth.
226  * If an error occurs during the parsing of the nesting
227  * enitity, the state at this level will not matter.
228  * a state that needs pushing will be anything other
229  * than state_start */
230 
231  yajl_state stateToPush = yajl_state_start;
232 
233  tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
234  offset, &buf, &bufLen);
235 
236  switch (tok) {
237  case yajl_tok_eof:
238  return yajl_status_ok;
239  case yajl_tok_error:
241  goto around_again;
242  case yajl_tok_string:
243  if (hand->callbacks && hand->callbacks->yajl_string) {
244  _CC_CHK(hand->callbacks->yajl_string(hand->ctx,
245  buf, bufLen));
246  }
247  break;
249  if (hand->callbacks && hand->callbacks->yajl_string) {
250  yajl_buf_clear(hand->decodeBuf);
251  yajl_string_decode(hand->decodeBuf, buf, bufLen);
253  hand->ctx, yajl_buf_data(hand->decodeBuf),
254  yajl_buf_len(hand->decodeBuf)));
255  }
256  break;
257  case yajl_tok_bool:
258  if (hand->callbacks && hand->callbacks->yajl_boolean) {
259  _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx,
260  *buf == 't'));
261  }
262  break;
263  case yajl_tok_null:
264  if (hand->callbacks && hand->callbacks->yajl_null) {
265  _CC_CHK(hand->callbacks->yajl_null(hand->ctx));
266  }
267  break;
268  case yajl_tok_left_brace:
269  if (hand->callbacks && hand->callbacks->yajl_start_map) {
270  _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx));
271  }
272  stateToPush = yajl_state_map_start;
273  break;
275  if (hand->callbacks && hand->callbacks->yajl_start_array) {
276  _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx));
277  }
278  stateToPush = yajl_state_array_start;
279  break;
280  case yajl_tok_integer:
281  if (hand->callbacks) {
282  if (hand->callbacks->yajl_number) {
284  hand->ctx,(const char *) buf, bufLen));
285  } else if (hand->callbacks->yajl_integer) {
286  long long int i = 0;
287  i = yajl_parse_integer(buf, bufLen);
288  if ((i == LLONG_MIN || i == LLONG_MAX) &&
289  errno == ERANGE)
290  {
291  yajl_bs_set(hand->stateStack,
293  hand->parseError = "integer overflow" ;
294  /* try to restore error offset */
295  if (*offset >= bufLen) *offset -= bufLen;
296  else *offset = 0;
297  goto around_again;
298  }
299  _CC_CHK(hand->callbacks->yajl_integer(hand->ctx,
300  i));
301  }
302  }
303  break;
304  case yajl_tok_double:
305  if (hand->callbacks) {
306  if (hand->callbacks->yajl_number) {
308  hand->ctx, (const char *) buf, bufLen));
309  } else if (hand->callbacks->yajl_double) {
310  double d = 0.0;
311  yajl_buf_clear(hand->decodeBuf);
312  yajl_buf_append(hand->decodeBuf, buf, bufLen);
313  buf = yajl_buf_data(hand->decodeBuf);
314  d = strtod((char *) buf, NULL);
315  if ((d == HUGE_VAL || d == -HUGE_VAL) &&
316  errno == ERANGE)
317  {
318  yajl_bs_set(hand->stateStack,
320  hand->parseError = "numeric (floating point) "
321  "overflow";
322  /* try to restore error offset */
323  if (*offset >= bufLen) *offset -= bufLen;
324  else *offset = 0;
325  goto around_again;
326  }
327  _CC_CHK(hand->callbacks->yajl_double(hand->ctx,
328  d));
329  }
330  }
331  break;
332  case yajl_tok_right_bracket: {
334  if (s == yajl_state_array_start ||
336  {
337  if (hand->callbacks &&
338  hand->callbacks->yajl_end_array)
339  {
340  _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx));
341  }
342  yajl_bs_pop(hand->stateStack);
343  goto around_again;
344  }
345  /* intentional fall-through */
346  }
347  case yajl_tok_colon:
348  case yajl_tok_comma:
351  hand->parseError =
352  "unallowed token at this point in JSON text";
353  goto around_again;
354  default:
356  hand->parseError = "invalid token, internal error";
357  goto around_again;
358  }
359  /* got a value. transition depends on the state we're in. */
360  {
362  if (s == yajl_state_start || s == yajl_state_got_value) {
364  } else if (s == yajl_state_map_need_val) {
366  } else {
368  }
369  }
370  if (stateToPush != yajl_state_start) {
371  yajl_bs_push(hand->stateStack, stateToPush);
372  }
373 
374  goto around_again;
375  }
378  /* only difference between these two states is that in
379  * start '}' is valid, whereas in need_key, we've parsed
380  * a comma, and a string key _must_ follow */
381  tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
382  offset, &buf, &bufLen);
383  switch (tok) {
384  case yajl_tok_eof:
385  return yajl_status_ok;
386  case yajl_tok_error:
388  goto around_again;
390  if (hand->callbacks && hand->callbacks->yajl_map_key) {
391  yajl_buf_clear(hand->decodeBuf);
392  yajl_string_decode(hand->decodeBuf, buf, bufLen);
393  buf = yajl_buf_data(hand->decodeBuf);
394  bufLen = yajl_buf_len(hand->decodeBuf);
395  }
396  /* intentional fall-through */
397  case yajl_tok_string:
398  if (hand->callbacks && hand->callbacks->yajl_map_key) {
399  _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf,
400  bufLen));
401  }
403  goto around_again;
404  case yajl_tok_right_brace: {
406  if (s == yajl_state_map_start ||
408  if (hand->callbacks && hand->callbacks->yajl_end_map) {
409  _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
410  }
411  yajl_bs_pop(hand->stateStack);
412  goto around_again;
413  }
414  }
415  default:
417  hand->parseError =
418  "invalid object key (must be a string)";
419  goto around_again;
420  }
421  }
422  case yajl_state_map_sep: {
423  tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
424  offset, &buf, &bufLen);
425  switch (tok) {
426  case yajl_tok_colon:
428  goto around_again;
429  case yajl_tok_eof:
430  return yajl_status_ok;
431  case yajl_tok_error:
433  goto around_again;
434  default:
436  hand->parseError = "object key and value must "
437  "be separated by a colon (':')";
438  goto around_again;
439  }
440  }
441  case yajl_state_map_got_val: {
442  tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
443  offset, &buf, &bufLen);
444  switch (tok) {
446  if (hand->callbacks && hand->callbacks->yajl_end_map) {
447  _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
448  }
449  yajl_bs_pop(hand->stateStack);
450  goto around_again;
451  case yajl_tok_comma:
453  goto around_again;
454  case yajl_tok_eof:
455  return yajl_status_ok;
456  case yajl_tok_error:
458  goto around_again;
459  default:
461  hand->parseError = "after key and value, inside map, "
462  "I expect ',' or '}'";
463  /* try to restore error offset */
464  if (*offset >= bufLen) *offset -= bufLen;
465  else *offset = 0;
466  goto around_again;
467  }
468  }
470  tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
471  offset, &buf, &bufLen);
472  switch (tok) {
474  if (hand->callbacks && hand->callbacks->yajl_end_array) {
475  _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx));
476  }
477  yajl_bs_pop(hand->stateStack);
478  goto around_again;
479  case yajl_tok_comma:
481  goto around_again;
482  case yajl_tok_eof:
483  return yajl_status_ok;
484  case yajl_tok_error:
486  goto around_again;
487  default:
489  hand->parseError =
490  "after array element, I expect ',' or ']'";
491  goto around_again;
492  }
493  }
494  }
495 
496  abort();
497  return yajl_status_error;
498 }
yajl_alloc_funcs alloc
Definition: yajl_parser.h:56
#define YA_MALLOC(afs, sz)
Definition: yajl_alloc.h:32
int(* yajl_null)(void *ctx)
Definition: yajl_parse.h:72
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
yajl_lex_error yajl_lex_get_error(yajl_lexer lexer)
Definition: yajl_lex.c:733
#define YA_FREE(afs, ptr)
Definition: yajl_alloc.h:33
int(* yajl_double)(void *ctx, double doubleVal)
Definition: yajl_parse.h:75
const char * yajl_lex_error_to_string(yajl_lex_error error)
Definition: yajl_lex.c:695
int i
Definition: scan.c:967
int(* yajl_integer)(void *ctx, long long integerVal)
Definition: yajl_parse.h:74
#define yajl_bs_push(obs, byte)
#define yajl_bs_set(obs, byte)
#define yajl_bs_current(obs)
yajl_state
Definition: yajl_parser.h:26
int(* yajl_number)(void *ctx, const char *numberVal, size_t numberLen)
Definition: yajl_parse.h:78
const char * parseError
Definition: yajl_parser.h:46
#define NULL
Definition: catime.c:38
int(* yajl_end_map)(void *ctx)
Definition: yajl_parse.h:89
#define str(v)
unsigned char * yajl_render_error_string(yajl_handle hand, const unsigned char *jsonText, size_t jsonTextLen, int verbose)
Definition: yajl_parser.c:66
yajl_status yajl_do_parse(yajl_handle hand, const unsigned char *jsonText, size_t jsonTextLen)
Definition: yajl_parser.c:187
size_t yajl_buf_len(yajl_buf buf)
Definition: yajl_buf.c:97
void yajl_buf_append(yajl_buf buf, const void *data, size_t len)
Definition: yajl_buf.c:75
yajl_lexer lexer
Definition: yajl_parser.h:45
size_t bytesConsumed
Definition: yajl_parser.h:50
Interface to YAJL&#39;s JSON stream parsing facilities.
#define LLONG_MIN
Definition: yajl_parser.c:34
int(* yajl_map_key)(void *ctx, const unsigned char *key, size_t stringLen)
Definition: yajl_parse.h:87
yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char *jsonText, size_t jsonTextLen, size_t *offset, const unsigned char **outBuf, size_t *outLen)
Definition: yajl_lex.c:503
#define MAX_VALUE_TO_MULTIPLY
Definition: yajl_parser.c:37
yajl_buf decodeBuf
Definition: yajl_parser.h:52
yajl_tok
Definition: yajl_lex.h:22
void yajl_buf_clear(yajl_buf buf)
Definition: yajl_buf.c:86
int(* yajl_start_array)(void *ctx)
Definition: yajl_parse.h:91
long long yajl_parse_integer(const unsigned char *number, size_t length)
Definition: yajl_parser.c:41
int(* yajl_start_map)(void *ctx)
Definition: yajl_parse.h:86
int(* yajl_end_array)(void *ctx)
Definition: yajl_parse.h:92
yajl_status
Definition: yajl_parse.h:32
void verbose(void)
Definition: verbose.c:27
#define _CC_CHK(x)
Definition: yajl_parser.c:150
yajl_status yajl_do_finish(yajl_handle hand)
Definition: yajl_parser.c:160
#define yajl_bs_pop(obs)
const yajl_callbacks * callbacks
Definition: yajl_parser.h:43
unsigned int flags
Definition: yajl_parser.h:58
const unsigned char * yajl_buf_data(yajl_buf buf)
Definition: yajl_buf.c:92
#define LLONG_MAX
Definition: yajl_parser.c:33
void yajl_string_decode(yajl_buf buf, const unsigned char *str, size_t len)
Definition: yajl_encode.c:116
yajl_bytestack stateStack
Definition: yajl_parser.h:54
int(* yajl_boolean)(void *ctx, int boolVal)
Definition: yajl_parse.h:73
int(* yajl_string)(void *ctx, const unsigned char *stringVal, size_t stringLen)
Definition: yajl_parse.h:83