diff options
Diffstat (limited to 'json-c/json_tokener.c')
-rw-r--r-- | json-c/json_tokener.c | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/json-c/json_tokener.c b/json-c/json_tokener.c new file mode 100644 index 0000000..c904f48 --- /dev/null +++ b/json-c/json_tokener.c @@ -0,0 +1,517 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark <michael@metaparadigm.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <string.h> + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_object.h" +#include "json_tokener.h" + + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + + +static const char* json_null_str = "null"; +static const char* json_true_str = "true"; +static const char* json_false_str = "false"; + +const char* json_tokener_errors[] = { + "success", + "continue", + "nesting to deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", +}; + + +struct json_tokener* json_tokener_new() +{ + struct json_tokener *tok = calloc(1, sizeof(struct json_tokener)); + tok->pb = printbuf_new(); + json_tokener_reset(tok); + return tok; +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if(tok) printbuf_free(tok->pb); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(char *str) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + obj = json_tokener_parse_ex(tok, str, -1); + if(tok->err != json_tokener_success) + obj = error_ptr(-tok->err); + json_tokener_free(tok); + return obj; +} + + +#if !HAVE_STRNDUP +/* CAW: compliant version of strndup() */ +char* strndup(const char* str, size_t n) +{ + if(str) { + size_t len = strlen(str); + size_t nn = min(len,n); + char* s = (char*)malloc(sizeof(char) * (nn + 1)); + + if(s) { + memcpy(s, str, nn); + s[nn] = '\0'; + } + + return s; + } + + return NULL; +} +#endif + + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + char *str, int len) +{ + struct json_object *obj = NULL; + char c; + + tok->char_offset = 0; + tok->err = json_tokener_success; + + do { + if(tok->char_offset == len) { + if(tok->depth == 0 && state == json_tokener_state_eatws && + saved_state == json_tokener_state_finish) + tok->err = json_tokener_success; + else + tok->err = json_tokener_continue; + goto out; + } + + c = *str; + redo_char: + switch(state) { + + case json_tokener_state_eatws: + if(isspace(c)) { + /* okay */ + } else if(c == '/') { + printbuf_reset(tok->pb); + printbuf_memappend(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + break; + case 'N': + case 'n': + state = json_tokener_state_null; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '"': + case '\'': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; +#if defined(__GNUC__) + case '0' ... '9': +#else + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +#endif + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_null: + printbuf_memappend(tok->pb, &c, 1); + if(strncasecmp(json_null_str, tok->pb->buf, + min(tok->st_pos+1, strlen(json_null_str))) == 0) { + if(tok->st_pos == strlen(json_null_str)) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + if(c == '*') state = json_tokener_state_comment_end; + printbuf_memappend(tok->pb, &c, 1); + break; + + case json_tokener_state_comment_eol: + if(c == '\n') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + printbuf_memappend(tok->pb, &c, 1); + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend(tok->pb, &c, 1); + if(c == '/') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + if(c == tok->quote_char) { + current = json_object_new_string(tok->pb->buf); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == '\\') { + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + } else { + printbuf_memappend(tok->pb, &c, 1); + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + if(c == 'b') printbuf_memappend(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend(tok->pb, "\t", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + if(strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4)); + if(tok->st_pos == 4) { + unsigned char utf_out[3]; + if (tok->ucs_char < 0x80) { + utf_out[0] = tok->ucs_char; + printbuf_memappend(tok->pb, (char*)utf_out, 1); + } else if (tok->ucs_char < 0x800) { + utf_out[0] = 0xc0 | (tok->ucs_char >> 6); + utf_out[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend(tok->pb, (char*)utf_out, 2); + } else { + utf_out[0] = 0xe0 | (tok->ucs_char >> 12); + utf_out[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + utf_out[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend(tok->pb, (char*)utf_out, 3); + } + state = saved_state; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_boolean: + printbuf_memappend(tok->pb, &c, 1); + if(strncasecmp(json_true_str, tok->pb->buf, + min(tok->st_pos+1, strlen(json_true_str))) == 0) { + if(tok->st_pos == strlen(json_true_str)) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if(strncasecmp(json_false_str, tok->pb->buf, + min(tok->st_pos+1, strlen(json_false_str))) == 0) { + if(tok->st_pos == strlen(json_false_str)) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_number: + if(c && strchr(json_number_chars, c)) { + printbuf_memappend(tok->pb, &c, 1); + if(c == '.' || c == 'e' || c == 'E') tok->is_double = 1; + } else { + int numi; + double numd; + if(!tok->is_double && sscanf(tok->pb->buf, "%d", &numi) == 1) { + current = json_object_new_int(numi); + } else if(tok->is_double && sscanf(tok->pb->buf, "%lf", &numd) == 1) { + current = json_object_new_double(numd); + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= JSON_TOKENER_MAX_DEPTH-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + json_object_array_add(current, obj); + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + if(c == tok->quote_char) { + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + } else if(c == '\\') { + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + } else { + printbuf_memappend(tok->pb, &c, 1); + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= JSON_TOKENER_MAX_DEPTH-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + str++; + tok->char_offset++; + } while(c); + + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + + out: + if(tok->err == json_tokener_success) return json_object_get(current); + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} |