diff options
Diffstat (limited to 'jslinux-2019-12-21/tinyemu-2019-12-21/json.c')
| -rw-r--r-- | jslinux-2019-12-21/tinyemu-2019-12-21/json.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/json.c b/jslinux-2019-12-21/tinyemu-2019-12-21/json.c new file mode 100644 index 0000000..d1ebe82 --- /dev/null +++ b/jslinux-2019-12-21/tinyemu-2019-12-21/json.c @@ -0,0 +1,464 @@ +/* + * Pseudo JSON parser + * + * Copyright (c) 2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <inttypes.h> +#include <assert.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <ctype.h> + +#include "cutils.h" +#include "json.h" +#include "fs_utils.h" + +static JSONValue parse_string(const char **pp) +{ + char buf[4096], *q; + const char *p; + int c, h; + + q = buf; + p = *pp; + p++; + for(;;) { + c = *p++; + if (c == '\0' || c == '\n') { + return json_error_new("unterminated string"); + } else if (c == '\"') { + break; + } else if (c == '\\') { + c = *p++; + switch(c) { + case '\'': + case '\"': + case '\\': + goto add_char; + case 'n': + c = '\n'; + goto add_char; + case 'r': + c = '\r'; + goto add_char; + case 't': + c = '\t'; + goto add_char; + case 'x': + h = from_hex(*p++); + if (h < 0) + return json_error_new("invalig hex digit"); + c = h << 4; + h = from_hex(*p++); + if (h < 0) + return json_error_new("invalig hex digit"); + c |= h; + goto add_char; + default: + return json_error_new("unknown escape code"); + } + } else { + add_char: + if (q >= buf + sizeof(buf) - 1) + return json_error_new("string too long"); + *q++ = c; + } + } + *q = '\0'; + *pp = p; + return json_string_new(buf); +} + +static JSONProperty *json_object_get2(JSONObject *obj, const char *name) +{ + JSONProperty *f; + int i; + for(i = 0; i < obj->len; i++) { + f = &obj->props[i]; + if (!strcmp(f->name.u.str->data, name)) + return f; + } + return NULL; +} + +JSONValue json_object_get(JSONValue val, const char *name) +{ + JSONProperty *f; + JSONObject *obj; + + if (val.type != JSON_OBJ) + return json_undefined_new(); + obj = val.u.obj; + f = json_object_get2(obj, name); + if (!f) + return json_undefined_new(); + return f->value; +} + +int json_object_set(JSONValue val, const char *name, JSONValue prop_val) +{ + JSONObject *obj; + JSONProperty *f; + int new_size; + + if (val.type != JSON_OBJ) + return -1; + obj = val.u.obj; + f = json_object_get2(obj, name); + if (f) { + json_free(f->value); + f->value = prop_val; + } else { + if (obj->len >= obj->size) { + new_size = max_int(obj->len + 1, obj->size * 3 / 2); + obj->props = realloc(obj->props, new_size * sizeof(JSONProperty)); + obj->size = new_size; + } + f = &obj->props[obj->len++]; + f->name = json_string_new(name); + f->value = prop_val; + } + return 0; +} + +JSONValue json_array_get(JSONValue val, unsigned int idx) +{ + JSONArray *array; + + if (val.type != JSON_ARRAY) + return json_undefined_new(); + array = val.u.array; + if (idx < array->len) { + return array->tab[idx]; + } else { + return json_undefined_new(); + } +} + +int json_array_set(JSONValue val, unsigned int idx, JSONValue prop_val) +{ + JSONArray *array; + int new_size; + + if (val.type != JSON_ARRAY) + return -1; + array = val.u.array; + if (idx < array->len) { + json_free(array->tab[idx]); + array->tab[idx] = prop_val; + } else if (idx == array->len) { + if (array->len >= array->size) { + new_size = max_int(array->len + 1, array->size * 3 / 2); + array->tab = realloc(array->tab, new_size * sizeof(JSONValue)); + array->size = new_size; + } + array->tab[array->len++] = prop_val; + } else { + return -1; + } + return 0; +} + +const char *json_get_str(JSONValue val) +{ + if (val.type != JSON_STR) + return NULL; + return val.u.str->data; +} + +const char *json_get_error(JSONValue val) +{ + if (val.type != JSON_EXCEPTION) + return NULL; + return val.u.str->data; +} + +JSONValue json_string_new2(const char *str, int len) +{ + JSONValue val; + JSONString *str1; + + str1 = malloc(sizeof(JSONString) + len + 1); + str1->len = len; + memcpy(str1->data, str, len + 1); + val.type = JSON_STR; + val.u.str = str1; + return val; +} + +JSONValue json_string_new(const char *str) +{ + return json_string_new2(str, strlen(str)); +} + +JSONValue __attribute__((format(printf, 1, 2))) json_error_new(const char *fmt, ...) +{ + JSONValue val; + va_list ap; + char buf[256]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + val = json_string_new(buf); + val.type = JSON_EXCEPTION; + return val; +} + +JSONValue json_object_new(void) +{ + JSONValue val; + JSONObject *obj; + obj = mallocz(sizeof(JSONObject)); + val.type = JSON_OBJ; + val.u.obj = obj; + return val; +} + +JSONValue json_array_new(void) +{ + JSONValue val; + JSONArray *array; + array = mallocz(sizeof(JSONArray)); + val.type = JSON_ARRAY; + val.u.array = array; + return val; +} + +void json_free(JSONValue val) +{ + switch(val.type) { + case JSON_STR: + case JSON_EXCEPTION: + free(val.u.str); + break; + case JSON_INT: + case JSON_BOOL: + case JSON_NULL: + case JSON_UNDEFINED: + break; + case JSON_ARRAY: + { + JSONArray *array = val.u.array; + int i; + + for(i = 0; i < array->len; i++) { + json_free(array->tab[i]); + } + free(array); + } + break; + case JSON_OBJ: + { + JSONObject *obj = val.u.obj; + JSONProperty *f; + int i; + + for(i = 0; i < obj->len; i++) { + f = &obj->props[i]; + json_free(f->name); + json_free(f->value); + } + free(obj); + } + break; + default: + abort(); + } +} + +static void skip_spaces(const char **pp) +{ + const char *p; + p = *pp; + for(;;) { + if (isspace(*p)) { + p++; + } else if (p[0] == '/' && p[1] == '/') { + p += 2; + while (*p != '\0' && *p != '\n') + p++; + } else if (p[0] == '/' && p[1] == '*') { + p += 2; + while (*p != '\0' && (p[0] != '*' || p[1] != '/')) + p++; + if (*p != '\0') + p += 2; + } else { + break; + } + } + *pp = p; +} + +static inline BOOL is_ident_first(int c) +{ + return (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '_' || c == '$'; +} + +static int parse_ident(char *buf, int buf_size, const char **pp) +{ + char *q; + const char *p; + p = *pp; + q = buf; + *q++ = *p++; /* first char is already tested */ + while (is_ident_first(*p) || isdigit(*p)) { + if ((q - buf) >= buf_size - 1) + return -1; + *q++ = *p++; + } + *pp = p; + *q = '\0'; + return 0; +} + +JSONValue json_parse_value2(const char **pp) +{ + char buf[128]; + const char *p; + JSONValue val, val1, tag; + + p = *pp; + skip_spaces(&p); + if (*p == '\0') { + return json_error_new("unexpected end of file"); + } + if (isdigit(*p)) { + val = json_int32_new(strtol(p, (char **)&p, 0)); + } else if (*p == '"') { + val = parse_string(&p); + } else if (*p == '{') { + p++; + val = json_object_new(); + for(;;) { + skip_spaces(&p); + if (*p == '}') { + p++; + break; + } + if (*p == '"') { + tag = parse_string(&p); + if (json_is_error(tag)) + return tag; + } else if (is_ident_first(*p)) { + if (parse_ident(buf, sizeof(buf), &p) < 0) + goto invalid_prop; + tag = json_string_new(buf); + } else { + goto invalid_prop; + } + // printf("property: %s\n", json_get_str(tag)); + if (tag.u.str->len == 0) { + invalid_prop: + return json_error_new("Invalid property name"); + } + skip_spaces(&p); + if (*p != ':') { + return json_error_new("':' expected"); + } + p++; + + val1 = json_parse_value2(&p); + json_object_set(val, tag.u.str->data, val1); + + skip_spaces(&p); + if (*p == ',') { + p++; + } else if (*p != '}') { + return json_error_new("expecting ',' or '}'"); + } + } + } else if (*p == '[') { + int idx; + + p++; + val = json_array_new(); + idx = 0; + for(;;) { + skip_spaces(&p); + if (*p == ']') { + p++; + break; + } + val1 = json_parse_value2(&p); + json_array_set(val, idx++, val1); + + skip_spaces(&p); + if (*p == ',') { + p++; + } else if (*p != ']') { + return json_error_new("expecting ',' or ']'"); + } + } + } else if (is_ident_first(*p)) { + if (parse_ident(buf, sizeof(buf), &p) < 0) + goto unknown_id; + if (!strcmp(buf, "null")) { + val = json_null_new(); + } else if (!strcmp(buf, "true")) { + val = json_bool_new(TRUE); + } else if (!strcmp(buf, "false")) { + val = json_bool_new(FALSE); + } else { + unknown_id: + return json_error_new("unknown identifier: '%s'", buf); + } + } else { + return json_error_new("unexpected character"); + } + *pp = p; + return val; +} + +JSONValue json_parse_value(const char *p) +{ + JSONValue val; + val = json_parse_value2(&p); + if (json_is_error(val)) + return val; + skip_spaces(&p); + if (*p != '\0') { + json_free(val); + return json_error_new("unexpected characters at the end"); + } + return val; +} + +JSONValue json_parse_value_len(const char *p, int len) +{ + char *str; + JSONValue val; + str = malloc(len + 1); + memcpy(str, p, len); + str[len] = '\0'; + val = json_parse_value(str); + free(str); + return val; +} |
