|
@@ -1,12 +1,11 @@
|
|
|
/*
|
|
/*
|
|
|
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
|
|
|
|
|
|
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
|
|
|
*
|
|
*
|
|
|
* Jansson is free software; you can redistribute it and/or modify
|
|
* Jansson is free software; you can redistribute it and/or modify
|
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
|
-#include <ctype.h>
|
|
|
|
|
#include <errno.h>
|
|
#include <errno.h>
|
|
|
#include <limits.h>
|
|
#include <limits.h>
|
|
|
#include <stdio.h>
|
|
#include <stdio.h>
|
|
@@ -14,7 +13,7 @@
|
|
|
#include <string.h>
|
|
#include <string.h>
|
|
|
#include <assert.h>
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
-#include <jansson.h>
|
|
|
|
|
|
|
+#include "jansson.h"
|
|
|
#include "jansson_private.h"
|
|
#include "jansson_private.h"
|
|
|
#include "strbuffer.h"
|
|
#include "strbuffer.h"
|
|
|
#include "utf.h"
|
|
#include "utf.h"
|
|
@@ -32,6 +31,14 @@
|
|
|
#define TOKEN_FALSE 260
|
|
#define TOKEN_FALSE 260
|
|
|
#define TOKEN_NULL 261
|
|
#define TOKEN_NULL 261
|
|
|
|
|
|
|
|
|
|
+/* Locale independent versions of isxxx() functions */
|
|
|
|
|
+#define l_isupper(c) ('A' <= (c) && (c) <= 'Z')
|
|
|
|
|
+#define l_islower(c) ('a' <= (c) && (c) <= 'z')
|
|
|
|
|
+#define l_isalpha(c) (l_isupper(c) || l_islower(c))
|
|
|
|
|
+#define l_isdigit(c) ('0' <= (c) && (c) <= '9')
|
|
|
|
|
+#define l_isxdigit(c) \
|
|
|
|
|
+ (l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f')
|
|
|
|
|
+
|
|
|
/* Read one byte from stream, convert to unsigned char, then int, and
|
|
/* Read one byte from stream, convert to unsigned char, then int, and
|
|
|
return. return EOF on end of file. This corresponds to the
|
|
return. return EOF on end of file. This corresponds to the
|
|
|
behaviour of fgetc(). */
|
|
behaviour of fgetc(). */
|
|
@@ -41,7 +48,7 @@ typedef struct {
|
|
|
get_func get;
|
|
get_func get;
|
|
|
void *data;
|
|
void *data;
|
|
|
char buffer[5];
|
|
char buffer[5];
|
|
|
- int buffer_pos;
|
|
|
|
|
|
|
+ size_t buffer_pos;
|
|
|
int state;
|
|
int state;
|
|
|
int line;
|
|
int line;
|
|
|
int column, last_column;
|
|
int column, last_column;
|
|
@@ -69,6 +76,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
|
|
{
|
|
{
|
|
|
va_list ap;
|
|
va_list ap;
|
|
|
char msg_text[JSON_ERROR_TEXT_LENGTH];
|
|
char msg_text[JSON_ERROR_TEXT_LENGTH];
|
|
|
|
|
+ char msg_with_context[JSON_ERROR_TEXT_LENGTH];
|
|
|
|
|
|
|
|
int line = -1, col = -1;
|
|
int line = -1, col = -1;
|
|
|
size_t pos = 0;
|
|
size_t pos = 0;
|
|
@@ -79,12 +87,12 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
|
|
|
|
|
|
|
va_start(ap, msg);
|
|
va_start(ap, msg);
|
|
|
vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
|
vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
|
|
|
|
+ msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
|
|
va_end(ap);
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if(lex)
|
|
if(lex)
|
|
|
{
|
|
{
|
|
|
const char *saved_text = strbuffer_value(&lex->saved_text);
|
|
const char *saved_text = strbuffer_value(&lex->saved_text);
|
|
|
- char msg_with_context[JSON_ERROR_TEXT_LENGTH];
|
|
|
|
|
|
|
|
|
|
line = lex->stream.line;
|
|
line = lex->stream.line;
|
|
|
col = lex->stream.column;
|
|
col = lex->stream.column;
|
|
@@ -95,6 +103,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
|
|
if(lex->saved_text.length <= 20) {
|
|
if(lex->saved_text.length <= 20) {
|
|
|
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
|
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
|
|
"%s near '%s'", msg_text, saved_text);
|
|
"%s near '%s'", msg_text, saved_text);
|
|
|
|
|
+ msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
|
|
result = msg_with_context;
|
|
result = msg_with_context;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -107,6 +116,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
|
|
|
else {
|
|
else {
|
|
|
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
|
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
|
|
"%s near end of file", msg_text);
|
|
"%s near end of file", msg_text);
|
|
|
|
|
+ msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
|
|
|
result = msg_with_context;
|
|
result = msg_with_context;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -268,11 +278,11 @@ static int32_t decode_unicode_escape(const char *str)
|
|
|
for(i = 1; i <= 4; i++) {
|
|
for(i = 1; i <= 4; i++) {
|
|
|
char c = str[i];
|
|
char c = str[i];
|
|
|
value <<= 4;
|
|
value <<= 4;
|
|
|
- if(isdigit(c))
|
|
|
|
|
|
|
+ if(l_isdigit(c))
|
|
|
value += c - '0';
|
|
value += c - '0';
|
|
|
- else if(islower(c))
|
|
|
|
|
|
|
+ else if(l_islower(c))
|
|
|
value += c - 'a' + 10;
|
|
value += c - 'a' + 10;
|
|
|
- else if(isupper(c))
|
|
|
|
|
|
|
+ else if(l_isupper(c))
|
|
|
value += c - 'A' + 10;
|
|
value += c - 'A' + 10;
|
|
|
else
|
|
else
|
|
|
assert(0);
|
|
assert(0);
|
|
@@ -317,7 +327,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
|
|
if(c == 'u') {
|
|
if(c == 'u') {
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
for(i = 0; i < 4; i++) {
|
|
for(i = 0; i < 4; i++) {
|
|
|
- if(!isxdigit(c)) {
|
|
|
|
|
|
|
+ if(!l_isxdigit(c)) {
|
|
|
error_set(error, lex, "invalid escape");
|
|
error_set(error, lex, "invalid escape");
|
|
|
goto out;
|
|
goto out;
|
|
|
}
|
|
}
|
|
@@ -437,7 +447,11 @@ out:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if JSON_INTEGER_IS_LONG_LONG
|
|
#if JSON_INTEGER_IS_LONG_LONG
|
|
|
|
|
+#ifdef _MSC_VER // Microsoft Visual Studio
|
|
|
|
|
+#define json_strtoint _strtoi64
|
|
|
|
|
+#else
|
|
|
#define json_strtoint strtoll
|
|
#define json_strtoint strtoll
|
|
|
|
|
+#endif
|
|
|
#else
|
|
#else
|
|
|
#define json_strtoint strtol
|
|
#define json_strtoint strtol
|
|
|
#endif
|
|
#endif
|
|
@@ -455,14 +469,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
|
|
|
|
|
|
|
if(c == '0') {
|
|
if(c == '0') {
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
- if(isdigit(c)) {
|
|
|
|
|
|
|
+ if(l_isdigit(c)) {
|
|
|
lex_unget_unsave(lex, c);
|
|
lex_unget_unsave(lex, c);
|
|
|
goto out;
|
|
goto out;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- else if(isdigit(c)) {
|
|
|
|
|
|
|
+ else if(l_isdigit(c)) {
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
- while(isdigit(c))
|
|
|
|
|
|
|
+ while(l_isdigit(c))
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
@@ -496,14 +510,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
|
|
|
|
|
|
|
if(c == '.') {
|
|
if(c == '.') {
|
|
|
c = lex_get(lex, error);
|
|
c = lex_get(lex, error);
|
|
|
- if(!isdigit(c)) {
|
|
|
|
|
|
|
+ if(!l_isdigit(c)) {
|
|
|
lex_unget(lex, c);
|
|
lex_unget(lex, c);
|
|
|
goto out;
|
|
goto out;
|
|
|
}
|
|
}
|
|
|
lex_save(lex, c);
|
|
lex_save(lex, c);
|
|
|
|
|
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
- while(isdigit(c))
|
|
|
|
|
|
|
+ while(l_isdigit(c))
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -512,24 +526,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
|
|
if(c == '+' || c == '-')
|
|
if(c == '+' || c == '-')
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
|
|
|
|
|
- if(!isdigit(c)) {
|
|
|
|
|
|
|
+ if(!l_isdigit(c)) {
|
|
|
lex_unget_unsave(lex, c);
|
|
lex_unget_unsave(lex, c);
|
|
|
goto out;
|
|
goto out;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
- while(isdigit(c))
|
|
|
|
|
|
|
+ while(l_isdigit(c))
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
lex_unget_unsave(lex, c);
|
|
lex_unget_unsave(lex, c);
|
|
|
|
|
|
|
|
- saved_text = strbuffer_value(&lex->saved_text);
|
|
|
|
|
- errno = 0;
|
|
|
|
|
- value = strtod(saved_text, &end);
|
|
|
|
|
- assert(end == saved_text + lex->saved_text.length);
|
|
|
|
|
-
|
|
|
|
|
- if(errno == ERANGE && value != 0) {
|
|
|
|
|
|
|
+ if(jsonp_strtod(&lex->saved_text, &value)) {
|
|
|
error_set(error, lex, "real number overflow");
|
|
error_set(error, lex, "real number overflow");
|
|
|
goto out;
|
|
goto out;
|
|
|
}
|
|
}
|
|
@@ -575,17 +584,17 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
|
|
else if(c == '"')
|
|
else if(c == '"')
|
|
|
lex_scan_string(lex, error);
|
|
lex_scan_string(lex, error);
|
|
|
|
|
|
|
|
- else if(isdigit(c) || c == '-') {
|
|
|
|
|
|
|
+ else if(l_isdigit(c) || c == '-') {
|
|
|
if(lex_scan_number(lex, c, error))
|
|
if(lex_scan_number(lex, c, error))
|
|
|
goto out;
|
|
goto out;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- else if(isupper(c) || islower(c)) {
|
|
|
|
|
|
|
+ else if(l_isalpha(c)) {
|
|
|
/* eat up the whole identifier for clearer error messages */
|
|
/* eat up the whole identifier for clearer error messages */
|
|
|
const char *saved_text;
|
|
const char *saved_text;
|
|
|
|
|
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
- while(isupper(c) || islower(c))
|
|
|
|
|
|
|
+ while(l_isalpha(c))
|
|
|
c = lex_get_save(lex, error);
|
|
c = lex_get_save(lex, error);
|
|
|
lex_unget_unsave(lex, c);
|
|
lex_unget_unsave(lex, c);
|
|
|
|
|
|
|
@@ -818,9 +827,11 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
|
|
json_t *result;
|
|
json_t *result;
|
|
|
|
|
|
|
|
lex_scan(lex, error);
|
|
lex_scan(lex, error);
|
|
|
- if(lex->token != '[' && lex->token != '{') {
|
|
|
|
|
- error_set(error, lex, "'[' or '{' expected");
|
|
|
|
|
- return NULL;
|
|
|
|
|
|
|
+ if(!(flags & JSON_DECODE_ANY)) {
|
|
|
|
|
+ if(lex->token != '[' && lex->token != '{') {
|
|
|
|
|
+ error_set(error, lex, "'[' or '{' expected");
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
result = parse_value(lex, flags, error);
|
|
result = parse_value(lex, flags, error);
|
|
@@ -832,10 +843,15 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
|
|
if(lex->token != TOKEN_EOF) {
|
|
if(lex->token != TOKEN_EOF) {
|
|
|
error_set(error, lex, "end of file expected");
|
|
error_set(error, lex, "end of file expected");
|
|
|
json_decref(result);
|
|
json_decref(result);
|
|
|
- result = NULL;
|
|
|
|
|
|
|
+ return NULL;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if(error) {
|
|
|
|
|
+ /* Save the position even though there was no error */
|
|
|
|
|
+ error->position = lex->stream.position;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -865,13 +881,19 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
|
|
json_t *result;
|
|
json_t *result;
|
|
|
string_data_t stream_data;
|
|
string_data_t stream_data;
|
|
|
|
|
|
|
|
|
|
+ jsonp_error_init(error, "<string>");
|
|
|
|
|
+
|
|
|
|
|
+ if (string == NULL) {
|
|
|
|
|
+ error_set(error, NULL, "wrong arguments");
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
stream_data.data = string;
|
|
stream_data.data = string;
|
|
|
stream_data.pos = 0;
|
|
stream_data.pos = 0;
|
|
|
|
|
|
|
|
if(lex_init(&lex, string_get, (void *)&stream_data))
|
|
if(lex_init(&lex, string_get, (void *)&stream_data))
|
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
|
|
- jsonp_error_init(error, "<string>");
|
|
|
|
|
result = parse_json(&lex, flags, error);
|
|
result = parse_json(&lex, flags, error);
|
|
|
|
|
|
|
|
lex_close(&lex);
|
|
lex_close(&lex);
|
|
@@ -903,6 +925,13 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
|
|
|
json_t *result;
|
|
json_t *result;
|
|
|
buffer_data_t stream_data;
|
|
buffer_data_t stream_data;
|
|
|
|
|
|
|
|
|
|
+ jsonp_error_init(error, "<buffer>");
|
|
|
|
|
+
|
|
|
|
|
+ if (buffer == NULL) {
|
|
|
|
|
+ error_set(error, NULL, "wrong arguments");
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
stream_data.data = buffer;
|
|
stream_data.data = buffer;
|
|
|
stream_data.pos = 0;
|
|
stream_data.pos = 0;
|
|
|
stream_data.len = buflen;
|
|
stream_data.len = buflen;
|
|
@@ -910,7 +939,6 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
|
|
|
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
|
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
|
|
- jsonp_error_init(error, "<buffer>");
|
|
|
|
|
result = parse_json(&lex, flags, error);
|
|
result = parse_json(&lex, flags, error);
|
|
|
|
|
|
|
|
lex_close(&lex);
|
|
lex_close(&lex);
|
|
@@ -923,15 +951,21 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
|
|
const char *source;
|
|
const char *source;
|
|
|
json_t *result;
|
|
json_t *result;
|
|
|
|
|
|
|
|
- if(lex_init(&lex, (get_func)fgetc, input))
|
|
|
|
|
- return NULL;
|
|
|
|
|
-
|
|
|
|
|
if(input == stdin)
|
|
if(input == stdin)
|
|
|
source = "<stdin>";
|
|
source = "<stdin>";
|
|
|
else
|
|
else
|
|
|
source = "<stream>";
|
|
source = "<stream>";
|
|
|
|
|
|
|
|
jsonp_error_init(error, source);
|
|
jsonp_error_init(error, source);
|
|
|
|
|
+
|
|
|
|
|
+ if (input == NULL) {
|
|
|
|
|
+ error_set(error, NULL, "wrong arguments");
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(lex_init(&lex, (get_func)fgetc, input))
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+
|
|
|
result = parse_json(&lex, flags, error);
|
|
result = parse_json(&lex, flags, error);
|
|
|
|
|
|
|
|
lex_close(&lex);
|
|
lex_close(&lex);
|
|
@@ -945,7 +979,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
|
|
|
|
|
|
|
jsonp_error_init(error, path);
|
|
jsonp_error_init(error, path);
|
|
|
|
|
|
|
|
- fp = fopen(path, "r");
|
|
|
|
|
|
|
+ if (path == NULL) {
|
|
|
|
|
+ error_set(error, NULL, "wrong arguments");
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fp = fopen(path, "rb");
|
|
|
if(!fp)
|
|
if(!fp)
|
|
|
{
|
|
{
|
|
|
error_set(error, NULL, "unable to open %s: %s",
|
|
error_set(error, NULL, "unable to open %s: %s",
|
|
@@ -958,3 +997,58 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
|
|
fclose(fp);
|
|
fclose(fp);
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+#define MAX_BUF_LEN 1024
|
|
|
|
|
+
|
|
|
|
|
+typedef struct
|
|
|
|
|
+{
|
|
|
|
|
+ char data[MAX_BUF_LEN];
|
|
|
|
|
+ size_t len;
|
|
|
|
|
+ size_t pos;
|
|
|
|
|
+ json_load_callback_t callback;
|
|
|
|
|
+ void *arg;
|
|
|
|
|
+} callback_data_t;
|
|
|
|
|
+
|
|
|
|
|
+static int callback_get(void *data)
|
|
|
|
|
+{
|
|
|
|
|
+ char c;
|
|
|
|
|
+ callback_data_t *stream = data;
|
|
|
|
|
+
|
|
|
|
|
+ if(stream->pos >= stream->len) {
|
|
|
|
|
+ stream->pos = 0;
|
|
|
|
|
+ stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg);
|
|
|
|
|
+ if(stream->len == 0 || stream->len == (size_t)-1)
|
|
|
|
|
+ return EOF;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ c = stream->data[stream->pos];
|
|
|
|
|
+ stream->pos++;
|
|
|
|
|
+ return (unsigned char)c;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error)
|
|
|
|
|
+{
|
|
|
|
|
+ lex_t lex;
|
|
|
|
|
+ json_t *result;
|
|
|
|
|
+
|
|
|
|
|
+ callback_data_t stream_data;
|
|
|
|
|
+
|
|
|
|
|
+ memset(&stream_data, 0, sizeof(stream_data));
|
|
|
|
|
+ stream_data.callback = callback;
|
|
|
|
|
+ stream_data.arg = arg;
|
|
|
|
|
+
|
|
|
|
|
+ jsonp_error_init(error, "<callback>");
|
|
|
|
|
+
|
|
|
|
|
+ if (callback == NULL) {
|
|
|
|
|
+ error_set(error, NULL, "wrong arguments");
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(lex_init(&lex, (get_func)callback_get, &stream_data))
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+
|
|
|
|
|
+ result = parse_json(&lex, flags, error);
|
|
|
|
|
+
|
|
|
|
|
+ lex_close(&lex);
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|