Write number of bytes read to error position on successful decode
[jansson.git] / src / load.c
index 74e33fc..193db45 100644 (file)
@@ -6,13 +6,11 @@
  */
 
 #define _GNU_SOURCE
-#include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdarg.h>
 #include <assert.h>
 
 #include <jansson.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(). */
@@ -70,6 +76,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
 {
     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;
@@ -85,7 +92,6 @@ static void error_set(json_error_t *error, const lex_t *lex,
     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;
@@ -269,11 +275,11 @@ static int32_t decode_unicode_escape(const char *str)
     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);
@@ -318,7 +324,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
             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;
                     }
@@ -456,14 +462,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 
     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 {
@@ -497,14 +503,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 
     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);
     }
 
@@ -513,23 +519,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *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;
     }
@@ -575,17 +577,17 @@ static int lex_scan(lex_t *lex, json_error_t *error)
     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);
 
@@ -643,9 +645,9 @@ static void lex_close(lex_t *lex)
 
 /*** 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)
@@ -668,6 +670,14 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
         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);
@@ -676,7 +686,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
         }
 
         lex_scan(lex, error);
-        value = parse_value(lex, error);
+        value = parse_value(lex, flags, error);
         if(!value) {
             jsonp_free(key);
             goto error;
@@ -710,7 +720,7 @@ 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)
@@ -721,7 +731,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
         return array;
 
     while(lex->token) {
-        json_t *elem = parse_value(lex, error);
+        json_t *elem = parse_value(lex, flags, error);
         if(!elem)
             goto error;
 
@@ -750,7 +760,7 @@ 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;
 
@@ -783,11 +793,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
             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:
@@ -805,15 +815,37 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
     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;
+        }
     }
 
-    return parse_value(lex, error);
+    if(error) {
+        /* Save the position even though there was no error */
+        error->position = lex->stream.position;
+    }
+
+    return result;
 }
 
 typedef struct
@@ -840,27 +872,68 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
 {
     lex_t lex;
     json_t *result;
-    string_data_t stream_data = {string, 0};
+    string_data_t stream_data;
+
+    jsonp_error_init(error, "<string>");
 
-    (void)flags; /* unused */
+    if (string == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
+    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;
+}
+
+typedef struct
+{
+    const char *data;
+    size_t len;
+    size_t pos;
+} buffer_data_t;
 
-    lex_scan(&lex, error);
-    if(lex.token != TOKEN_EOF) {
-        error_set(error, &lex, "end of file expected");
-        json_decref(result);
-        result = NULL;
+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;
 }
@@ -870,10 +943,6 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
     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>";
@@ -882,18 +951,16 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
 
     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;
 }
@@ -905,7 +972,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
 
     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",