From e8f4dc86cb87b38ee19b8574dadbbe6629f35ef0 Mon Sep 17 00:00:00 2001 From: Joey Adams Date: Mon, 14 Jun 2010 02:29:58 -0400 Subject: Implemented/tested parser for basic JSONPath(ish) patterns. --- json.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 21 deletions(-) (limited to 'json.c') diff --git a/json.c b/json.c index d530edd..1cc8628 100644 --- a/json.c +++ b/json.c @@ -349,9 +349,10 @@ ascend: static json_node *decode_leaf(const char **sp); static json_node *decode_number(const char **sp); -static char *decode_string(const char **sp, size_t *length); -/* decode_string has a different signature than its friends - because it's also used to parse object member keys. */ +char *json_decode_string(const char **sp, size_t *length, bool strict); +/* json_decode_string has a different signature than its friends + because it's also used to parse object member keys. + It's also useful outside of json.c, such as in jsonpath.c . */ bool json_validate(const char *str) { @@ -382,7 +383,7 @@ item: /* Expect a value */ if (parent && parent->type == JSON_OBJECT) { /* Parse member key string. */ - key = decode_string(&s, &key_length); + key = json_decode_string(&s, &key_length, true); if (!key) goto failed; @@ -482,7 +483,7 @@ static json_node *decode_leaf(const char **sp) if (c == '"') { size_t length; - char *str = decode_string(sp, &length); + char *str = json_decode_string(sp, &length, true); if (str) { json_node *node = json_mknode(JSON_STRING); @@ -573,24 +574,32 @@ static json_node *decode_number(const char **sp) return json_mknumber(start, end - start); } -static char *decode_string(const char **sp, size_t *length) +char *json_decode_string(const char **sp, size_t *length, bool strict) { const char *s = *sp; String(ret); char *out; size_t size; + char quote; + + Assert(length != NULL); - if (*s++ != '"') - return NULL; - - while (*s && *s != '"') { + quote = *s++; + if (strict) { + if (quote != '"') + return NULL; + } else { + if (quote != '"' && quote != '\'') + return NULL; + } + + while (*s && *s != quote) { unsigned char c = *s++; unsigned int uc, lc; if (c == '\\') { c = *s++; switch (c) { - case '"': case '\\': case '/': break; @@ -635,7 +644,11 @@ static char *decode_string(const char **sp, size_t *length) continue; /* Continue the enclosing while loop to skip the str_append below. */ default: /* Invalid escape */ - goto failed; + if (c == quote) + break; + if (!strict && (c == '"' || c == '\'')) + break; + goto failed; /* Invalid escape */ } } else if (c <= 0x1F) { /* Control characters not allowed in string literals. */ @@ -693,22 +706,21 @@ json_type json_text_type(const char *str, size_t nbytes) /****************************** Encoding *****************************/ -static bool encode_string(String out, const char *string, size_t length) +static bool encode_string(String out, const char *string, size_t length, char quote) { const char *s = string; const char *e = s + length; - - if (!utf8_validate(string, length)) + + if (!utf8_validate(string, length) || quote == '\\') return false; - - string_append_char(out, '"'); + + string_append_char(out, quote); while (s < e) { unsigned char c = *s++; unsigned char e; switch (c) { - case '"': e = '"'; break; case '\\': e = '\\'; break; case '\b': e = 'b'; break; case '\f': e = 'f'; break; @@ -716,6 +728,10 @@ static bool encode_string(String out, const char *string, size_t length) case '\r': e = 'r'; break; case '\t': e = 't'; break; default: { + if (c == quote) { + e = quote; + break; + } if (c < 0x1F || (c >= 0x80 && json_escape_unicode)) { /* Encode using \u.... */ unsigned int uc, lc; @@ -754,7 +770,7 @@ static bool encode_string(String out, const char *string, size_t length) string_append_char(out, e); } - string_append_char(out, '"'); + string_append_char(out, quote); return true; } @@ -799,7 +815,7 @@ begin: /* Encode entire node, or (if it's an array or object) the beginning of it. */ if (node->key) { - if (!encode_string(ret, node->key, node->key_length)) + if (!encode_string(ret, node->key, node->key_length, '"')) goto failed; string_append_char(ret, ':'); } @@ -819,7 +835,7 @@ begin_nokey: txt = "false"; break; case JSON_STRING: - if (!encode_string(ret, node->v.string.str, node->v.string.length)) + if (!encode_string(ret, node->v.string.str, node->v.string.length, '"')) goto failed; break; case JSON_NUMBER: @@ -870,6 +886,18 @@ failed: /* Handle error. */ return NULL; } +char *json_encode_string(const char *str, size_t length, char quote) +{ + String(ret); + + if (!encode_string(ret, str, length, quote)) { + string_free(ret); + return NULL; + } + + return string_buffer(ret); +} + /************************ Liberal JSON support ***********************/ -- cgit v1.2.3