summaryrefslogtreecommitdiff
path: root/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'json.c')
-rw-r--r--json.c70
1 files changed, 49 insertions, 21 deletions
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 ***********************/