|  | /* | 
|  | * Copyright (c) 2010 Serge A. Zaitsev | 
|  | * | 
|  | * 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. | 
|  | * | 
|  | * Slightly modified by AK to not assume 0 terminated input. | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include "jsmn.h" | 
|  |  | 
|  | /* | 
|  | * Allocates a fresh unused token from the token pool. | 
|  | */ | 
|  | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, | 
|  | jsmntok_t *tokens, size_t num_tokens) | 
|  | { | 
|  | jsmntok_t *tok; | 
|  |  | 
|  | if ((unsigned)parser->toknext >= num_tokens) | 
|  | return NULL; | 
|  | tok = &tokens[parser->toknext++]; | 
|  | tok->start = tok->end = -1; | 
|  | tok->size = 0; | 
|  | return tok; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Fills token type and boundaries. | 
|  | */ | 
|  | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, | 
|  | int start, int end) | 
|  | { | 
|  | token->type = type; | 
|  | token->start = start; | 
|  | token->end = end; | 
|  | token->size = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Fills next available token with JSON primitive. | 
|  | */ | 
|  | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, | 
|  | size_t len, | 
|  | jsmntok_t *tokens, size_t num_tokens) | 
|  | { | 
|  | jsmntok_t *token; | 
|  | int start; | 
|  |  | 
|  | start = parser->pos; | 
|  |  | 
|  | for (; parser->pos < len; parser->pos++) { | 
|  | switch (js[parser->pos]) { | 
|  | #ifndef JSMN_STRICT | 
|  | /* | 
|  | * In strict mode primitive must be followed by "," | 
|  | * or "}" or "]" | 
|  | */ | 
|  | case ':': | 
|  | #endif | 
|  | case '\t': | 
|  | case '\r': | 
|  | case '\n': | 
|  | case ' ': | 
|  | case ',': | 
|  | case ']': | 
|  | case '}': | 
|  | goto found; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { | 
|  | parser->pos = start; | 
|  | return JSMN_ERROR_INVAL; | 
|  | } | 
|  | } | 
|  | #ifdef JSMN_STRICT | 
|  | /* | 
|  | * In strict mode primitive must be followed by a | 
|  | * comma/object/array. | 
|  | */ | 
|  | parser->pos = start; | 
|  | return JSMN_ERROR_PART; | 
|  | #endif | 
|  |  | 
|  | found: | 
|  | token = jsmn_alloc_token(parser, tokens, num_tokens); | 
|  | if (token == NULL) { | 
|  | parser->pos = start; | 
|  | return JSMN_ERROR_NOMEM; | 
|  | } | 
|  | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); | 
|  | parser->pos--; /* parent sees closing brackets */ | 
|  | return JSMN_SUCCESS; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Fills next token with JSON string. | 
|  | */ | 
|  | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, | 
|  | size_t len, | 
|  | jsmntok_t *tokens, size_t num_tokens) | 
|  | { | 
|  | jsmntok_t *token; | 
|  | int start = parser->pos; | 
|  |  | 
|  | /* Skip starting quote */ | 
|  | parser->pos++; | 
|  |  | 
|  | for (; parser->pos < len; parser->pos++) { | 
|  | char c = js[parser->pos]; | 
|  |  | 
|  | /* Quote: end of string */ | 
|  | if (c == '\"') { | 
|  | token = jsmn_alloc_token(parser, tokens, num_tokens); | 
|  | if (token == NULL) { | 
|  | parser->pos = start; | 
|  | return JSMN_ERROR_NOMEM; | 
|  | } | 
|  | jsmn_fill_token(token, JSMN_STRING, start+1, | 
|  | parser->pos); | 
|  | return JSMN_SUCCESS; | 
|  | } | 
|  |  | 
|  | /* Backslash: Quoted symbol expected */ | 
|  | if (c == '\\') { | 
|  | parser->pos++; | 
|  | switch (js[parser->pos]) { | 
|  | /* Allowed escaped symbols */ | 
|  | case '\"': | 
|  | case '/': | 
|  | case '\\': | 
|  | case 'b': | 
|  | case 'f': | 
|  | case 'r': | 
|  | case 'n': | 
|  | case 't': | 
|  | break; | 
|  | /* Allows escaped symbol \uXXXX */ | 
|  | case 'u': | 
|  | /* TODO */ | 
|  | break; | 
|  | /* Unexpected symbol */ | 
|  | default: | 
|  | parser->pos = start; | 
|  | return JSMN_ERROR_INVAL; | 
|  | } | 
|  | } | 
|  | } | 
|  | parser->pos = start; | 
|  | return JSMN_ERROR_PART; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Parse JSON string and fill tokens. | 
|  | */ | 
|  | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, | 
|  | jsmntok_t *tokens, unsigned int num_tokens) | 
|  | { | 
|  | jsmnerr_t r; | 
|  | int i; | 
|  | jsmntok_t *token; | 
|  |  | 
|  | for (; parser->pos < len; parser->pos++) { | 
|  | char c; | 
|  | jsmntype_t type; | 
|  |  | 
|  | c = js[parser->pos]; | 
|  | switch (c) { | 
|  | case '{': | 
|  | case '[': | 
|  | token = jsmn_alloc_token(parser, tokens, num_tokens); | 
|  | if (token == NULL) | 
|  | return JSMN_ERROR_NOMEM; | 
|  | if (parser->toksuper != -1) | 
|  | tokens[parser->toksuper].size++; | 
|  | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); | 
|  | token->start = parser->pos; | 
|  | parser->toksuper = parser->toknext - 1; | 
|  | break; | 
|  | case '}': | 
|  | case ']': | 
|  | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); | 
|  | for (i = parser->toknext - 1; i >= 0; i--) { | 
|  | token = &tokens[i]; | 
|  | if (token->start != -1 && token->end == -1) { | 
|  | if (token->type != type) | 
|  | return JSMN_ERROR_INVAL; | 
|  | parser->toksuper = -1; | 
|  | token->end = parser->pos + 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | /* Error if unmatched closing bracket */ | 
|  | if (i == -1) | 
|  | return JSMN_ERROR_INVAL; | 
|  | for (; i >= 0; i--) { | 
|  | token = &tokens[i]; | 
|  | if (token->start != -1 && token->end == -1) { | 
|  | parser->toksuper = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case '\"': | 
|  | r = jsmn_parse_string(parser, js, len, tokens, | 
|  | num_tokens); | 
|  | if (r < 0) | 
|  | return r; | 
|  | if (parser->toksuper != -1) | 
|  | tokens[parser->toksuper].size++; | 
|  | break; | 
|  | case '\t': | 
|  | case '\r': | 
|  | case '\n': | 
|  | case ':': | 
|  | case ',': | 
|  | case ' ': | 
|  | break; | 
|  | #ifdef JSMN_STRICT | 
|  | /* | 
|  | * In strict mode primitives are: | 
|  | * numbers and booleans. | 
|  | */ | 
|  | case '-': | 
|  | case '0': | 
|  | case '1': | 
|  | case '2': | 
|  | case '3': | 
|  | case '4': | 
|  | case '5': | 
|  | case '6': | 
|  | case '7': | 
|  | case '8': | 
|  | case '9': | 
|  | case 't': | 
|  | case 'f': | 
|  | case 'n': | 
|  | #else | 
|  | /* | 
|  | * In non-strict mode every unquoted value | 
|  | * is a primitive. | 
|  | */ | 
|  | /*FALL THROUGH */ | 
|  | default: | 
|  | #endif | 
|  | r = jsmn_parse_primitive(parser, js, len, tokens, | 
|  | num_tokens); | 
|  | if (r < 0) | 
|  | return r; | 
|  | if (parser->toksuper != -1) | 
|  | tokens[parser->toksuper].size++; | 
|  | break; | 
|  |  | 
|  | #ifdef JSMN_STRICT | 
|  | /* Unexpected char in strict mode */ | 
|  | default: | 
|  | return JSMN_ERROR_INVAL; | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = parser->toknext - 1; i >= 0; i--) { | 
|  | /* Unmatched opened object or array */ | 
|  | if (tokens[i].start != -1 && tokens[i].end == -1) | 
|  | return JSMN_ERROR_PART; | 
|  | } | 
|  |  | 
|  | return JSMN_SUCCESS; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Creates a new parser based over a given  buffer with an array of tokens | 
|  | * available. | 
|  | */ | 
|  | void jsmn_init(jsmn_parser *parser) | 
|  | { | 
|  | parser->pos = 0; | 
|  | parser->toknext = 0; | 
|  | parser->toksuper = -1; | 
|  | } | 
|  |  | 
|  | const char *jsmn_strerror(jsmnerr_t err) | 
|  | { | 
|  | switch (err) { | 
|  | case JSMN_ERROR_NOMEM: | 
|  | return "No enough tokens"; | 
|  | case JSMN_ERROR_INVAL: | 
|  | return "Invalid character inside JSON string"; | 
|  | case JSMN_ERROR_PART: | 
|  | return "The string is not a full JSON packet, more bytes expected"; | 
|  | case JSMN_SUCCESS: | 
|  | return "Success"; | 
|  | default: | 
|  | return "Unknown json error"; | 
|  | } | 
|  | } |