/*
- * 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
* it under the terms of the MIT license. See LICENSE for details.
*/
#define _GNU_SOURCE
-#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#define TOKEN_FALSE 260
#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
return. return EOF on end of file. This corresponds to the
behaviour of fgetc(). */
get_func get;
void *data;
char buffer[5];
- int buffer_pos;
+ size_t buffer_pos;
int state;
int line;
int column, last_column;
{
va_list ap;
char msg_text[JSON_ERROR_TEXT_LENGTH];
+ char msg_with_context[JSON_ERROR_TEXT_LENGTH];
int line = -1, col = -1;
size_t pos = 0;
if(lex)
{
const char *saved_text = strbuffer_value(&lex->saved_text);
- char msg_with_context[JSON_ERROR_TEXT_LENGTH];
line = lex->stream.line;
col = lex->stream.column;
for(i = 1; i <= 4; i++) {
char c = str[i];
value <<= 4;
- if(isdigit(c))
+ if(l_isdigit(c))
value += c - '0';
- else if(islower(c))
+ else if(l_islower(c))
value += c - 'a' + 10;
- else if(isupper(c))
+ else if(l_isupper(c))
value += c - 'A' + 10;
else
assert(0);
if(c == 'u') {
c = lex_get_save(lex, error);
for(i = 0; i < 4; i++) {
- if(!isxdigit(c)) {
+ if(!l_isxdigit(c)) {
error_set(error, lex, "invalid escape");
goto out;
}
if(c == '0') {
c = lex_get_save(lex, error);
- if(isdigit(c)) {
+ if(l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
}
- else if(isdigit(c)) {
+ else if(l_isdigit(c)) {
c = lex_get_save(lex, error);
- while(isdigit(c))
+ while(l_isdigit(c))
c = lex_get_save(lex, error);
}
else {
if(c == '.') {
c = lex_get(lex, error);
- if(!isdigit(c)) {
+ if(!l_isdigit(c)) {
lex_unget(lex, c);
goto out;
}
lex_save(lex, c);
c = lex_get_save(lex, error);
- while(isdigit(c))
+ while(l_isdigit(c))
c = lex_get_save(lex, error);
}
if(c == '+' || c == '-')
c = lex_get_save(lex, error);
- if(!isdigit(c)) {
+ if(!l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
c = lex_get_save(lex, error);
- while(isdigit(c))
+ while(l_isdigit(c))
c = lex_get_save(lex, error);
}
lex_unget_unsave(lex, c);
- saved_text = strbuffer_value(&lex->saved_text);
- 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");
goto out;
}
else if(c == '"')
lex_scan_string(lex, error);
- else if(isdigit(c) || c == '-') {
+ else if(l_isdigit(c) || c == '-') {
if(lex_scan_number(lex, c, error))
goto out;
}
- else if(isupper(c) || islower(c)) {
+ else if(l_isalpha(c)) {
/* eat up the whole identifier for clearer error messages */
const char *saved_text;
c = lex_get_save(lex, error);
- while(isupper(c) || islower(c))
+ while(l_isalpha(c))
c = lex_get_save(lex, error);
lex_unget_unsave(lex, c);
/*** parser ***/
-static json_t *parse_value(lex_t *lex, json_error_t *error);
+static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
-static json_t *parse_object(lex_t *lex, json_error_t *error)
+static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *object = json_object();
if(!object)
if(!key)
return NULL;
+ if(flags & JSON_REJECT_DUPLICATES) {
+ if(json_object_get(object, key)) {
+ jsonp_free(key);
+ error_set(error, lex, "duplicate object key");
+ goto error;
+ }
+ }
+
lex_scan(lex, error);
if(lex->token != ':') {
jsonp_free(key);
}
lex_scan(lex, error);
- value = parse_value(lex, error);
+ value = parse_value(lex, flags, error);
if(!value) {
jsonp_free(key);
goto error;
return NULL;
}
-static json_t *parse_array(lex_t *lex, json_error_t *error)
+static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *array = json_array();
if(!array)
return array;
while(lex->token) {
- json_t *elem = parse_value(lex, error);
+ json_t *elem = parse_value(lex, flags, error);
if(!elem)
goto error;
return NULL;
}
-static json_t *parse_value(lex_t *lex, json_error_t *error)
+static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *json;
break;
case '{':
- json = parse_object(lex, error);
+ json = parse_object(lex, flags, error);
break;
case '[':
- json = parse_array(lex, error);
+ json = parse_array(lex, flags, error);
break;
case TOKEN_INVALID:
return json;
}
-static json_t *parse_json(lex_t *lex, json_error_t *error)
+static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
{
+ json_t *result;
+
lex_scan(lex, error);
- if(lex->token != '[' && lex->token != '{') {
- error_set(error, lex, "'[' or '{' expected");
+ if(!(flags & JSON_DECODE_ANY)) {
+ if(lex->token != '[' && lex->token != '{') {
+ error_set(error, lex, "'[' or '{' expected");
+ return NULL;
+ }
+ }
+
+ result = parse_value(lex, flags, error);
+ if(!result)
return NULL;
+
+ if(!(flags & JSON_DISABLE_EOF_CHECK)) {
+ lex_scan(lex, error);
+ if(lex->token != TOKEN_EOF) {
+ error_set(error, lex, "end of file expected");
+ json_decref(result);
+ return NULL;
+ }
+ }
+
+ if(error) {
+ /* Save the position even though there was no error */
+ error->position = lex->stream.position;
}
- return parse_value(lex, error);
+ return result;
}
typedef struct
{
lex_t lex;
json_t *result;
- string_data_t stream_data = {string, 0};
+ string_data_t stream_data;
+
+ jsonp_error_init(error, "<string>");
+
+ if (string == NULL) {
+ error_set(error, NULL, "wrong arguments");
+ return NULL;
+ }
- (void)flags; /* unused */
+ stream_data.data = string;
+ stream_data.pos = 0;
if(lex_init(&lex, string_get, (void *)&stream_data))
return NULL;
- jsonp_error_init(error, "<string>");
+ result = parse_json(&lex, flags, error);
- result = parse_json(&lex, error);
- if(!result)
- goto out;
+ lex_close(&lex);
+ return result;
+}
- lex_scan(&lex, error);
- if(lex.token != TOKEN_EOF) {
- error_set(error, &lex, "end of file expected");
- json_decref(result);
- result = NULL;
+typedef struct
+{
+ const char *data;
+ size_t len;
+ size_t pos;
+} buffer_data_t;
+
+static int buffer_get(void *data)
+{
+ char c;
+ buffer_data_t *stream = data;
+ if(stream->pos >= stream->len)
+ return EOF;
+
+ c = stream->data[stream->pos];
+ stream->pos++;
+ return (unsigned char)c;
+}
+
+json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
+{
+ lex_t lex;
+ json_t *result;
+ buffer_data_t stream_data;
+
+ jsonp_error_init(error, "<buffer>");
+
+ if (buffer == NULL) {
+ error_set(error, NULL, "wrong arguments");
+ return NULL;
}
-out:
+ stream_data.data = buffer;
+ stream_data.pos = 0;
+ stream_data.len = buflen;
+
+ if(lex_init(&lex, buffer_get, (void *)&stream_data))
+ return NULL;
+
+ result = parse_json(&lex, flags, error);
+
lex_close(&lex);
return result;
}
lex_t lex;
const char *source;
json_t *result;
- (void)flags; /* unused */
-
- if(lex_init(&lex, (get_func)fgetc, input))
- return NULL;
if(input == stdin)
source = "<stdin>";
jsonp_error_init(error, source);
- result = parse_json(&lex, error);
- if(!result)
- goto out;
-
- lex_scan(&lex, error);
- if(lex.token != TOKEN_EOF) {
- error_set(error, &lex, "end of file expected");
- json_decref(result);
- result = NULL;
+ if (input == NULL) {
+ error_set(error, NULL, "wrong arguments");
+ return NULL;
}
-out:
+ if(lex_init(&lex, (get_func)fgetc, input))
+ return NULL;
+
+ result = parse_json(&lex, flags, error);
+
lex_close(&lex);
return result;
}
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)
{
error_set(error, NULL, "unable to open %s: %s",
fclose(fp);
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;
+}