1 /****************************************************************************
4 json.c - parse JSON into fixed-extent data structures
7 This module parses a large subset of JSON (JavaScript Object
8 Notation). Unlike more general JSON parsers, it doesn't use malloc(3)
9 and doesn't support polymorphism; you need to give it a set of
10 template structures describing the expected shape of the incoming
11 JSON, and it will error out if that shape is not matched. When the
12 parse succeds, attribute values will be extracted into static
13 locations specified in the template structures.
15 The "shape" of a JSON object in the type signature of its
16 attributes (and attribute values, and so on recursively down through
17 all nestings of objects and arrays). This parser is indifferent to
18 the order of attributes at any level, but you have to tell it in
19 advance what the type of each attribute value will be and where the
20 parsed value will be stored. The template structures may supply
21 default values to be used when an expected attribute is omitted.
23 The dialect this parses has some limitations. First, it cannot
24 recognize the JSON "null" value. Secondly, arrays may only have
25 objects or strings - not reals or integers or floats - as elements
26 (this limitation could be easily removed if required). Third, all
27 elements of an array must be of the same type.
29 There are separata entry points for beginning a parse of either
30 JSON object or a JSON array. JSON "float" quantities are actually
33 This parser processes object arrays in one of two different ways,
34 defending on whether the array subtype is declared as object or
37 Object arrays take one base address per object subfield, and are
38 mapped into parallel C arrays (one per subfield). Strings are not
39 supported in this kind of array, as the don't have a "natural" size
40 to use as an offset multiplier.
42 Structobjects arrays are a way to parse a list of objects to a set
43 of modifications to a corresponding array of C structs. The trick is
44 that the array object initialization has to specify both the C struct
45 array's base address and the stride length (the size of the C struct).
46 If you initialize the offset fields with the correct offsetof() calls,
47 everything will work. Strings are suppported but all string storage
48 has to be inline in the struct.
51 This file is Copyright (c) 2010 by the GPSD project
52 BSD terms apply: see the file COPYING in the distribution root for details.
54 ***************************************************************************/
60 #include "gpsd_config.h" /* for strlcpy() prototype */
63 #ifdef CLIENTDEBUG_ENABLE
64 static int debuglevel = 0;
67 void json_enable_debug(int level, FILE * fp)
68 /* control the level and destination of debug trace messages */
74 static void json_trace(int errlevel, const char *fmt, ...)
75 /* assemble command in printf(3) style */
77 if (errlevel <= debuglevel) {
81 (void)strlcpy(buf, "json: ", BUFSIZ);
83 (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt,
87 (void)fputs(buf, debugfp);
91 # define json_debug_trace(args) (void) json_trace args
93 # define json_debug_trace(args) /*@i1@*/do { } while (0)
94 #endif /* CLIENTDEBUG_ENABLE */
96 /*@-immediatetrans -dependenttrans -usereleased -compdef@*/
97 static /*@null@*/ char *json_target_address(const struct json_attr_t *cursor,
99 const struct json_array_t
102 char *targetaddr = NULL;
103 if (parent == NULL || parent->element_type != t_structobject) {
104 /* ordinary case - use the address in the cursor structure */
105 switch (cursor->type) {
107 targetaddr = (char *)&cursor->addr.integer[offset];
110 targetaddr = (char *)&cursor->addr.uinteger[offset];
113 targetaddr = (char *)&cursor->addr.real[offset];
116 targetaddr = cursor->addr.string;
119 targetaddr = (char *)&cursor->addr.boolean[offset];
122 targetaddr = (char *)&cursor->addr.character[offset];
129 /* tricky case - hacking a member in an array of structures */
131 parent->arr.objects.base + (offset * parent->arr.objects.stride) +
133 json_debug_trace((1, "Target address for %s (offset %d) is %p\n",
134 cursor->attribute, offset, targetaddr));
138 /*@-immediatetrans -dependenttrans +usereleased +compdef@*/
140 static int json_internal_read_object(const char *cp,
141 const struct json_attr_t *attrs,
143 const struct json_array_t *parent,
145 /*@null@*/ const char **end)
147 /*@ -nullstate -nullderef -mustfreefresh -nullpass -usedef @*/
149 { init, await_attr, in_attr, await_value, in_val_string,
150 in_escape, in_val_token, post_val, post_array
152 #ifdef CLIENTDEBUG_ENABLE
153 char *statenames[] = {
154 "init", "await_attr", "in_attr", "await_value", "in_val_string",
155 "in_escape", "in_val_token", "post_val", "post_array",
157 #endif /* CLIENTDEBUG_ENABLE */
158 char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL;
159 char valbuf[JSON_VAL_MAX + 1], *pval = NULL;
160 bool value_quoted = false;
161 char uescape[5]; /* enough space for 4 hex digits and a NUL */
162 const struct json_attr_t *cursor;
163 int substatus, n, maxlen = 0;
165 const struct json_enum_t *mp;
169 /* prevents gripes about buffers not being completely defined */
170 memset(valbuf, '\0', sizeof(valbuf));
171 memset(attrbuf, '\0', sizeof(attrbuf));
172 #endif /* S_SPLINT_S */
175 *end = NULL; /* give it a well-defined value on parse failure */
177 /* stuff fields with defaults in case they're omitted in the JSON input */
178 for (cursor = attrs; cursor->attribute != NULL; cursor++)
179 if (!cursor->nodefault) {
180 lptr = json_target_address(cursor, parent, offset);
182 switch (cursor->type) {
184 *((int *)lptr) = cursor->dflt.integer;
187 *((unsigned int *)lptr) = cursor->dflt.uinteger;
190 *((double *)lptr) = cursor->dflt.real;
194 && parent->element_type != t_structobject
196 return JSON_ERR_NOPARSTR;
200 /* nullbool default says not to set the value at all */
202 if (cursor->dflt.boolean != nullbool)
203 *((bool *) lptr) = cursor->dflt.boolean;
207 lptr[0] = cursor->dflt.character;
209 case t_object: /* silences a compiler warning */
217 json_debug_trace((1, "JSON parse of '%s' begins.\n", cp));
219 /* parse input JSON */
220 for (; *cp != '\0'; cp++) {
221 json_debug_trace((2, "State %-14s, looking at '%c' (%p)\n",
222 statenames[state], *cp, cp));
231 "Non-WS when expecting object start.\n"));
232 return JSON_ERR_OBSTART;
238 else if (*cp == '"') {
241 } else if (*cp == '}')
244 json_debug_trace((1, "Non-WS when expecting attribute.\n"));
245 return JSON_ERR_ATTRSTART;
251 json_debug_trace((1, "Collected attribute name %s\n",
253 for (cursor = attrs; cursor->attribute != NULL; cursor++) {
254 json_debug_trace((2, "Checking against %s\n",
256 if (strcmp(cursor->attribute, attrbuf) == 0)
259 if (cursor->attribute == NULL) {
261 "Unknown attribute name '%s' (attributes begin with '%s').\n",
262 attrbuf, attrs->attribute));
263 return JSON_ERR_BADATTR;
266 if (cursor->type == t_string)
267 maxlen = (int)cursor->len - 1;
268 else if (cursor->type == t_check)
269 maxlen = (int)strlen(cursor->dflt.check);
270 else if (cursor->map != NULL)
271 maxlen = (int)sizeof(valbuf) - 1;
273 } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) {
274 json_debug_trace((1, "Attribute name too long.\n"));
275 return JSON_ERR_ATTRLEN;
280 if (isspace(*cp) || *cp == ':')
282 else if (*cp == '[') {
283 if (cursor->type != t_array) {
285 "Saw [ when not expecting array.\n"));
286 return JSON_ERR_NOARRAY;
288 substatus = json_read_array(cp, &cursor->addr.array, &cp);
292 } else if (cursor->type == t_array) {
294 "Array element was specified, but no [.\n"));
295 return JSON_ERR_NOBRAK;
296 } else if (*cp == '"') {
298 state = in_val_string;
301 value_quoted = false;
302 state = in_val_token;
310 else if (*cp == '"') {
312 json_debug_trace((1, "Collected string value %s\n", valbuf));
314 } else if (pval > valbuf + JSON_VAL_MAX - 1
315 || pval > valbuf + maxlen) {
316 json_debug_trace((1, "String value too long.\n"));
317 return JSON_ERR_STRLONG; /* */
339 for (n = 0; n < 4 && cp[n] != '\0'; n++)
342 (void)sscanf(uescape, "%04x", &u);
343 *pval++ = (char)u; /* will truncate values above 0xff */
345 default: /* handles double quote and solidus */
349 state = in_val_string;
352 if (isspace(*cp) || *cp == ',' || *cp == '}') {
354 json_debug_trace((1, "Collected token value %s.\n", valbuf));
356 if (*cp == '}' || *cp == ',')
358 } else if (pval > valbuf + JSON_VAL_MAX - 1) {
359 json_debug_trace((1, "Token value too long.\n"));
360 return JSON_ERR_TOKLONG;
366 && (cursor->type != t_string && cursor->type != t_character
367 && cursor->type != t_check && cursor->map == 0)) {
369 "Saw quoted value when expecting non-string.\n"));
370 return JSON_ERR_QNONSTRING;
373 && (cursor->type == t_string || cursor->type == t_check
374 || cursor->map != 0)) {
376 "Didn't see quoted value when expecting string.\n"));
377 return JSON_ERR_NONQSTRING;
379 if (cursor->map != 0) {
380 for (mp = cursor->map; mp->name != NULL; mp++)
381 if (strcmp(mp->name, valbuf) == 0) {
384 json_debug_trace((1, "Invalid enumerated value string %s.\n",
386 return JSON_ERR_BADENUM;
388 (void)snprintf(valbuf, sizeof(valbuf), "%d", mp->value);
390 lptr = json_target_address(cursor, parent, offset);
392 switch (cursor->type) {
394 *((int *)lptr) = atoi(valbuf);
397 *((unsigned int *)lptr) = (unsigned)atoi(valbuf);
400 *((double *)lptr) = atof(valbuf);
404 && parent->element_type != t_structobject
406 return JSON_ERR_NOPARSTR;
407 (void)strncpy(lptr, valbuf, cursor->len);
410 *((bool *) lptr) = (strcmp(valbuf, "true") == 0);
413 if (strlen(valbuf) > 1)
414 return JSON_ERR_STRLONG;
418 case t_object: /* silences a compiler warning */
423 if (strcmp(cursor->dflt.check, valbuf) != 0) {
425 "Required attribute vakue %s not present.\n",
426 cursor->dflt.check));
427 return JSON_ERR_CHECKFAIL;
437 else if (*cp == '}') {
441 json_debug_trace((1, "Garbage while expecting comma or }\n"));
442 return JSON_ERR_BADTRAIL;
449 /* in case there's another object following, consune trailing WS */
454 json_debug_trace((1, "JSON parse ends.\n"));
456 /*@ +nullstate +nullderef +mustfreefresh +nullpass +usedef @*/
459 int json_read_array(const char *cp, const struct json_array_t *arr,
462 /*@-nullstate -onlytrans@*/
463 int substatus, offset;
467 *end = NULL; /* give it a well-defined value on parse failure */
469 json_debug_trace((1, "Entered json_read_array()\n"));
474 json_debug_trace((1, "Didn't find expected array start\n"));
475 return JSON_ERR_ARRAYSTART;
479 tp = arr->arr.strings.store;
480 if (arr->count != NULL)
482 for (offset = 0; offset < arr->maxlen; offset++) {
483 json_debug_trace((1, "Looking at %s\n", cp));
484 switch (arr->element_type) {
489 return JSON_ERR_BADSTRING;
492 arr->arr.strings.ptrs[offset] = tp;
493 for (; tp - arr->arr.strings.store < arr->arr.strings.storelen;
499 } else if (*cp == '\0') {
501 "Bad string syntax in string list.\n"));
502 return JSON_ERR_BADSTRING;
506 json_debug_trace((1, "Bad string syntax in string list.\n"));
507 return JSON_ERR_BADSTRING;
513 json_internal_read_object(cp, arr->arr.objects.subtype, arr,
525 json_debug_trace((1, "Invalid array subtype.\n"));
526 return JSON_ERR_SUBTYPE;
528 if (arr->count != NULL)
533 json_debug_trace((1, "End of array found.\n"));
535 } else if (*cp == ',')
538 json_debug_trace((1, "Bad trailing syntax on array.\n"));
539 return JSON_ERR_BADSUBTRAIL;
542 json_debug_trace((1, "Too many elements in array.\n"));
543 return JSON_ERR_SUBTOOLONG;
548 json_debug_trace((1, "leaving json_read_array() with %d elements\n",
552 /*@+nullstate +onlytrans@*/
555 int json_read_object(const char *cp, const struct json_attr_t *attrs,
556 /*@null@*/ const char **end)
558 json_debug_trace((1, "json_read_object() sees '%s'\n", cp));
559 return json_internal_read_object(cp, attrs, NULL, 0, end);
562 const /*@observer@*/ char *json_error_string(int err)
564 const char *errors[] = {
565 "unknown error while parsing JSON",
566 "non-whitespace when expecting object start",
567 "non-whitespace when expecting attribute start",
568 "unknown attribute name",
569 "attribute name too long",
570 "saw [ when not expecting array",
571 "array element specified, but no [",
572 "string value too long",
573 "token value too long",
574 "garbage while expecting , or }",
575 "didn't find expected array start",
576 "error while parsing object array",
577 "too many array elements",
578 "garbage while expecting array comma",
579 "unsupported array element type",
580 "error while string parsing",
581 "check attribute not matched",
582 "can't support strings in parallel arrays",
583 "invalid enumerated value",
584 "saw quoted value when expecting nonstring",
585 "didn't see quoted value when expecting string",
586 "other data conversion error",
589 if (err <= 0 || err >= (int)(sizeof(errors) / sizeof(errors[0])))