int json_read_object(const char *cp, const struct json_attr_t *attrs, const char **end) { int st; json_debug_trace((1, "json_read_object() sees '%s'\n", cp)); st = json_internal_read_object(cp, attrs, NULL, 0, end); return st; }
static char *json_target_address(const struct json_attr_t *cursor, const struct json_array_t *parent, int offset) { char *targetaddr = NULL; if (parent == NULL || parent->element_type != t_structobject) { /* ordinary case - use the address in the cursor structure */ switch (cursor->type) { case t_ignore: targetaddr = NULL; break; case t_integer: targetaddr = (char *)&cursor->addr.integer[offset]; break; case t_uinteger: targetaddr = (char *)&cursor->addr.uinteger[offset]; break; case t_short: targetaddr = (char *)&cursor->addr.shortint[offset]; break; case t_ushort: targetaddr = (char *)&cursor->addr.ushortint[offset]; break; case t_time: case t_real: targetaddr = (char *)&cursor->addr.real[offset]; break; case t_string: targetaddr = cursor->addr.string; break; case t_boolean: targetaddr = (char *)&cursor->addr.boolean[offset]; break; case t_character: targetaddr = (char *)&cursor->addr.character[offset]; break; default: targetaddr = NULL; break; } } else /* tricky case - hacking a member in an array of structures */ targetaddr = parent->arr.objects.base + (offset * parent->arr.objects.stride) + cursor->addr.offset; json_debug_trace((1, "Target address for %s (offset %d) is %p\n", cursor->attribute, offset, targetaddr)); return targetaddr; }
int json_read_array(const char *cp, const struct json_array_t *arr, const char **end) { int substatus, offset, arrcount; char *tp; if (end != NULL) *end = NULL; /* give it a well-defined value on parse failure */ json_debug_trace((1, "Entered json_read_array()\n")); while (isspace((unsigned char) *cp)) cp++; if (*cp != '[') { json_debug_trace((1, "Didn't find expected array start\n")); return JSON_ERR_ARRAYSTART; } else cp++; tp = arr->arr.strings.store; arrcount = 0; /* Check for empty array */ while (isspace((unsigned char) *cp)) cp++; if (*cp == ']') goto breakout; for (offset = 0; offset < arr->maxlen; offset++) { #ifndef JSON_MINIMAL char *ep = NULL; #endif /* JSON_MINIMAL */ json_debug_trace((1, "Looking at %s\n", cp)); switch (arr->element_type) { case t_string: if (isspace((unsigned char) *cp)) cp++; if (*cp != '"') return JSON_ERR_BADSTRING; else ++cp; arr->arr.strings.ptrs[offset] = tp; for (; tp - arr->arr.strings.store < arr->arr.strings.storelen; tp++) if (*cp == '"') { ++cp; *tp++ = '\0'; goto stringend; } else if (*cp == '\0') { json_debug_trace((1, "Bad string syntax in string list.\n")); return JSON_ERR_BADSTRING; } else { *tp = *cp++; } json_debug_trace((1, "Bad string syntax in string list.\n")); return JSON_ERR_BADSTRING; stringend: break; case t_object: case t_structobject: substatus = json_internal_read_object(cp, arr->arr.objects.subtype, arr, offset, &cp); if (substatus != 0) { #ifndef JSON_MINIMAL if (end != NULL) end = &cp; #endif /* JSON_MINIMAL */ return substatus; } break; case t_integer: #ifndef JSON_MINIMAL arr->arr.integers.store[offset] = (int)strtol(cp, &ep, 0); if (ep == cp) return JSON_ERR_BADNUM; else cp = ep; break; #endif /* JSON_MINIMAL */ case t_uinteger: #ifndef JSON_MINIMAL arr->arr.uintegers.store[offset] = (unsigned int)strtoul(cp, &ep, 0); if (ep == cp) return JSON_ERR_BADNUM; else cp = ep; break; #endif /* JSON_MINIMAL */ case t_short: #ifndef JSON_MINIMAL arr->arr.shorts.store[offset] = (short)strtol(cp, &ep, 0); if (ep == cp) return JSON_ERR_BADNUM; else cp = ep; break; #endif /* JSON_MINIMAL */ case t_ushort: #ifndef JSON_MINIMAL arr->arr.ushorts.store[offset] = (unsigned short)strtoul(cp, &ep, 0); if (ep == cp) return JSON_ERR_BADNUM; else cp = ep; break; #endif /* JSON_MINIMAL */ case t_time: #ifndef JSON_MINIMAL if (*cp != '"') return JSON_ERR_BADSTRING; else ++cp; arr->arr.reals.store[offset] = iso8601_to_unix((char *)cp); if (arr->arr.reals.store[offset] >= HUGE_VAL) return JSON_ERR_BADNUM; while (*cp && *cp != '"') cp++; if (*cp != '"') return JSON_ERR_BADSTRING; else ++cp; break; #endif /* JSON_MINIMAL */ case t_real: #ifndef JSON_MINIMAL arr->arr.reals.store[offset] = strtod(cp, &ep); if (ep == cp) return JSON_ERR_BADNUM; else cp = ep; break; #endif /* JSON_MINIMAL */ case t_boolean: #ifndef JSON_MINIMAL if (str_starts_with(cp, "true")) { arr->arr.booleans.store[offset] = true; cp += 4; } else if (str_starts_with(cp, "false")) { arr->arr.booleans.store[offset] = false; cp += 5; } break; #endif /* JSON_MINIMAL */ case t_character: case t_array: case t_check: case t_ignore: json_debug_trace((1, "Invalid array subtype.\n")); return JSON_ERR_SUBTYPE; } arrcount++; if (isspace((unsigned char) *cp)) cp++; if (*cp == ']') { json_debug_trace((1, "End of array found.\n")); goto breakout; } else if (*cp == ',') cp++; else { json_debug_trace((1, "Bad trailing syntax on array.\n")); return JSON_ERR_BADSUBTRAIL; } } json_debug_trace((1, "Too many elements in array.\n")); #ifndef JSON_MINIMAL if (end != NULL) *end = cp; #endif /* JSON_MINIMAL */ return JSON_ERR_SUBTOOLONG; breakout: if (arr->count != NULL) *(arr->count) = arrcount; if (end != NULL) *end = cp; json_debug_trace((1, "leaving json_read_array() with %d elements\n", arrcount)); return 0; }
static int json_internal_read_object(const char *cp, const struct json_attr_t *attrs, const struct json_array_t *parent, int offset, const char **end) { enum { init, await_attr, in_attr, await_value, in_val_string, in_escape, in_val_token, post_val, post_array } state = 0; #ifdef CLIENTDEBUG_ENABLE char *statenames[] = { "init", "await_attr", "in_attr", "await_value", "in_val_string", "in_escape", "in_val_token", "post_val", "post_array", }; #endif /* CLIENTDEBUG_ENABLE */ char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL; char valbuf[JSON_VAL_MAX + 1], *pval = NULL; bool value_quoted = false; char uescape[5]; /* enough space for 4 hex digits and a NUL */ const struct json_attr_t *cursor; int substatus, n, maxlen = 0; unsigned int u; const struct json_enum_t *mp; char *lptr; if (end != NULL) *end = NULL; /* give it a well-defined value on parse failure */ /* stuff fields with defaults in case they're omitted in the JSON input */ for (cursor = attrs; cursor->attribute != NULL; cursor++) if (!cursor->nodefault) { lptr = json_target_address(cursor, parent, offset); if (lptr != NULL) switch (cursor->type) { case t_integer: memcpy(lptr, &cursor->dflt.integer, sizeof(int)); break; case t_uinteger: memcpy(lptr, &cursor->dflt.uinteger, sizeof(unsigned int)); break; case t_short: memcpy(lptr, &cursor->dflt.shortint, sizeof(short)); break; case t_ushort: memcpy(lptr, &cursor->dflt.ushortint, sizeof(unsigned short)); break; case t_time: case t_real: memcpy(lptr, &cursor->dflt.real, sizeof(double)); break; case t_string: if (parent != NULL && parent->element_type != t_structobject && offset > 0) return JSON_ERR_NOPARSTR; lptr[0] = '\0'; break; case t_boolean: memcpy(lptr, &cursor->dflt.boolean, sizeof(bool)); break; case t_character: lptr[0] = cursor->dflt.character; break; case t_object: /* silences a compiler warning */ case t_structobject: case t_array: case t_check: case t_ignore: break; } } json_debug_trace((1, "JSON parse of '%s' begins.\n", cp)); /* parse input JSON */ for (; *cp != '\0'; cp++) { json_debug_trace((2, "State %-14s, looking at '%c' (%p)\n", statenames[state], *cp, cp)); switch (state) { case init: if (isspace((unsigned char) *cp)) continue; else if (*cp == '{') state = await_attr; else { json_debug_trace((1, "Non-WS when expecting object start.\n")); #ifndef JSON_MINIMAL if (end != NULL) *end = cp; #endif /* JSON_MINIMAL */ return JSON_ERR_OBSTART; } break; case await_attr: if (isspace((unsigned char) *cp)) continue; else if (*cp == '"') { state = in_attr; pattr = attrbuf; #ifndef JSON_MINIMAL if (end != NULL) *end = cp; #endif /* JSON_MINIMAL */ } else if (*cp == '}') break; else { json_debug_trace((1, "Non-WS when expecting attribute.\n")); #ifndef JSON_MINIMAL if (end != NULL) *end = cp; #endif /* JSON_MINIMAL */ return JSON_ERR_ATTRSTART; } break; case in_attr: if (pattr == NULL) /* don't update end here, leave at attribute start */ return JSON_ERR_NULLPTR; if (*cp == '"') { *pattr++ = '\0'; json_debug_trace((1, "Collected attribute name %s\n", attrbuf)); for (cursor = attrs; cursor->attribute != NULL; cursor++) { json_debug_trace((2, "Checking against %s\n", cursor->attribute)); if (strcmp(cursor->attribute, attrbuf) == 0) break; } if (cursor->attribute == NULL) { json_debug_trace((1, "Unknown attribute name '%s' (attributes begin with '%s').\n", attrbuf, attrs->attribute)); /* don't update end here, leave at attribute start */ return JSON_ERR_BADATTR; } state = await_value; if (cursor->type == t_string) maxlen = (int)cursor->len - 1; else if (cursor->type == t_check) maxlen = (int)strlen(cursor->dflt.check); else if (cursor->type == t_time || cursor->type == t_ignore) maxlen = JSON_VAL_MAX; else if (cursor->map != NULL) maxlen = (int)sizeof(valbuf) - 1; pval = valbuf; } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) { json_debug_trace((1, "Attribute name too long.\n")); /* don't update end here, leave at attribute start */ return JSON_ERR_ATTRLEN; } else *pattr++ = *cp; break; case await_value: if (isspace((unsigned char) *cp) || *cp == ':') continue; else if (*cp == '[') { if (cursor->type != t_array) { json_debug_trace((1, "Saw [ when not expecting array.\n")); #ifndef JSON_MINIMAL if (end != NULL) *end = cp; #endif /* JSON_MINIMAL */ return JSON_ERR_NOARRAY; } substatus = json_read_array(cp, &cursor->addr.array, &cp); if (substatus != 0) return substatus; state = post_array; } else if (cursor->type == t_array) { json_debug_trace((1, "Array element was specified, but no [.\n")); #ifndef JSON_MINIMAL if (end != NULL) *end = cp; #endif /* JSON_MINIMAL */ return JSON_ERR_NOBRAK; } else if (*cp == '"') { value_quoted = true; state = in_val_string; pval = valbuf; } else { value_quoted = false; state = in_val_token; pval = valbuf; *pval++ = *cp; } break; case in_val_string: if (pval == NULL) /* don't update end here, leave at value start */ return JSON_ERR_NULLPTR; if (*cp == '\\') state = in_escape; else if (*cp == '"') { *pval++ = '\0'; json_debug_trace((1, "Collected string value %s\n", valbuf)); state = post_val; } else if (pval > valbuf + JSON_VAL_MAX - 1 || pval > valbuf + maxlen) { json_debug_trace((1, "String value too long.\n")); /* don't update end here, leave at value start */ return JSON_ERR_STRLONG; /* */ } else *pval++ = *cp; break; case in_escape: if (pval == NULL) /* don't update end here, leave at value start */ return JSON_ERR_NULLPTR; switch (*cp) { case 'b': *pval++ = '\b'; break; case 'f': *pval++ = '\f'; break; case 'n': *pval++ = '\n'; break; case 'r': *pval++ = '\r'; break; case 't': *pval++ = '\t'; break; case 'u': for (n = 0; n < 4 && cp[n] != '\0'; n++) uescape[n] = *cp++; --cp; (void)sscanf(uescape, "%04x", &u); *pval++ = (char)u; /* will truncate values above 0xff */ break; default: /* handles double quote and solidus */ *pval++ = *cp; break; } state = in_val_string; break; case in_val_token: if (pval == NULL) /* don't update end here, leave at value start */ return JSON_ERR_NULLPTR; if (isspace((unsigned char) *cp) || *cp == ',' || *cp == '}') { *pval = '\0'; json_debug_trace((1, "Collected token value %s.\n", valbuf)); state = post_val; if (*cp == '}' || *cp == ',') --cp; } else if (pval > valbuf + JSON_VAL_MAX - 1) { json_debug_trace((1, "Token value too long.\n")); /* don't update end here, leave at value start */ return JSON_ERR_TOKLONG; } else *pval++ = *cp; break; /* coverity[unterminated_case] */ case post_val: /* * We know that cursor points at the first spec matching * the current attribute. We don't know that it's *the* * correct spec; our dialect allows there to be any number * of adjacent ones with the same attrname but different * types. Here's where we try to seek forward for a * matching type/attr pair if we're not looking at one. */ for (;;) { int seeking = cursor->type; if (value_quoted && (cursor->type == t_string || cursor->type == t_time)) break; if ((strcmp(valbuf, "true")==0 || strcmp(valbuf, "false")==0) && seeking == t_boolean) break; if (isdigit((unsigned char) valbuf[0])) { bool decimal = strchr(valbuf, '.') != NULL; if (decimal && seeking == t_real) break; if (!decimal && (seeking == t_integer || seeking == t_uinteger)) break; } if (cursor[1].attribute==NULL) /* out of possiblities */ break; if (strcmp(cursor[1].attribute, attrbuf)!=0) break; ++cursor; } if (value_quoted && (cursor->type != t_string && cursor->type != t_character && cursor->type != t_check && cursor->type != t_time && cursor->type != t_ignore && cursor->map == 0)) { json_debug_trace((1, "Saw quoted value when expecting non-string.\n")); return JSON_ERR_QNONSTRING; } if (!value_quoted && (cursor->type == t_string || cursor->type == t_check || cursor->type == t_time || cursor->map != 0)) { json_debug_trace((1, "Didn't see quoted value when expecting string.\n")); return JSON_ERR_NONQSTRING; } if (cursor->map != 0) { for (mp = cursor->map; mp->name != NULL; mp++) if (strcmp(mp->name, valbuf) == 0) { goto foundit; } json_debug_trace((1, "Invalid enumerated value string %s.\n", valbuf)); return JSON_ERR_BADENUM; foundit: (void)snprintf(valbuf, sizeof(valbuf), "%d", mp->value); } lptr = json_target_address(cursor, parent, offset); if (lptr != NULL) switch (cursor->type) { case t_integer: { int tmp = atoi(valbuf); memcpy(lptr, &tmp, sizeof(int)); } break; case t_uinteger: { unsigned int tmp = (unsigned int)atoi(valbuf); memcpy(lptr, &tmp, sizeof(unsigned int)); } break; case t_short: { short tmp = atoi(valbuf); memcpy(lptr, &tmp, sizeof(short)); } break; case t_ushort: { unsigned short tmp = (unsigned int)atoi(valbuf); memcpy(lptr, &tmp, sizeof(unsigned short)); } break; case t_time: { double tmp = iso8601_to_unix(valbuf); memcpy(lptr, &tmp, sizeof(double)); } break; case t_real: { double tmp = safe_atof(valbuf); memcpy(lptr, &tmp, sizeof(double)); } break; case t_string: if (parent != NULL && parent->element_type != t_structobject && offset > 0) return JSON_ERR_NOPARSTR; (void)strlcpy(lptr, valbuf, cursor->len); break; case t_boolean: { bool tmp = (strcmp(valbuf, "true") == 0); memcpy(lptr, &tmp, sizeof(bool)); } break; case t_character: if (strlen(valbuf) > 1) /* don't update end here, leave at value start */ return JSON_ERR_STRLONG; else lptr[0] = valbuf[0]; break; case t_ignore: /* silences a compiler warning */ case t_object: /* silences a compiler warning */ case t_structobject: case t_array: break; case t_check: if (strcmp(cursor->dflt.check, valbuf) != 0) { json_debug_trace((1, "Required attribute value %s not present.\n", cursor->dflt.check)); /* don't update end here, leave at start of attribute */ return JSON_ERR_CHECKFAIL; } break; } case post_array: if (isspace((unsigned char) *cp)) continue; else if (*cp == ',') state = await_attr; else if (*cp == '}') { ++cp; goto good_parse; } else { json_debug_trace((1, "Garbage while expecting comma or }\n")); #ifndef JSON_MINIMAL if (end != NULL) *end = cp; #endif /* JSON_MINIMAL */ return JSON_ERR_BADTRAIL; } break; } } good_parse: /* in case there's another object following, consume trailing WS */ while (isspace((unsigned char) *cp)) ++cp; if (end != NULL) *end = cp; json_debug_trace((1, "JSON parse ends.\n")); return 0; }
int json_read_array(const char *cp, const struct json_array_t *arr, const char **end) { /*@-nullstate -onlytrans@*/ int substatus, offset, arrcount; char *tp; if (end != NULL) *end = NULL; /* give it a well-defined value on parse failure */ json_debug_trace((1, "Entered json_read_array()\n")); while (isspace(*cp)) cp++; if (*cp != '[') { json_debug_trace((1, "Didn't find expected array start\n")); return JSON_ERR_ARRAYSTART; } else cp++; tp = arr->arr.strings.store; arrcount = 0; /* Check for empty array */ while (isspace(*cp)) cp++; if (*cp == ']') goto breakout; for (offset = 0; offset < arr->maxlen; offset++) { json_debug_trace((1, "Looking at %s\n", cp)); switch (arr->element_type) { case t_string: if (isspace(*cp)) cp++; if (*cp != '"') return JSON_ERR_BADSTRING; else ++cp; arr->arr.strings.ptrs[offset] = tp; for (; tp - arr->arr.strings.store < arr->arr.strings.storelen; tp++) if (*cp == '"') { ++cp; *tp++ = '\0'; goto stringend; } else if (*cp == '\0') { json_debug_trace((1, "Bad string syntax in string list.\n")); return JSON_ERR_BADSTRING; } else { *tp = *cp++; } json_debug_trace((1, "Bad string syntax in string list.\n")); return JSON_ERR_BADSTRING; stringend: break; case t_object: case t_structobject: substatus = json_internal_read_object(cp, arr->arr.objects.subtype, arr, offset, &cp); if (substatus != 0) return substatus; break; case t_integer: case t_uinteger: case t_time: case t_real: case t_boolean: case t_character: case t_array: case t_check: json_debug_trace((1, "Invalid array subtype.\n")); return JSON_ERR_SUBTYPE; } arrcount++; if (isspace(*cp)) cp++; if (*cp == ']') { json_debug_trace((1, "End of array found.\n")); goto breakout; } else if (*cp == ',') cp++; else { json_debug_trace((1, "Bad trailing syntax on array.\n")); return JSON_ERR_BADSUBTRAIL; } } json_debug_trace((1, "Too many elements in array.\n")); return JSON_ERR_SUBTOOLONG; breakout: if (arr->count != NULL) *(arr->count) = arrcount; if (end != NULL) *end = cp; /*@ -nullderef @*/ json_debug_trace((1, "leaving json_read_array() with %d elements\n", arrcount)); /*@ +nullderef @*/ return 0; /*@+nullstate +onlytrans@*/ }