Enhance error reporting
This patch adds two new fields to the json_error_t struct: column and source. It also adds functions to populate json_error_t internally. The column field is not currently used, but it will be utilized in the decoder and pack/unpack functions.
This commit is contained in:
parent
818baf5fdb
commit
5422a862de
@ -701,14 +701,24 @@ affect especially the behavior of the decoder.
|
||||
.. member:: const char *text
|
||||
|
||||
The error message (in UTF-8), or an empty string if a message is
|
||||
not available. This is actually a fixed-length character array,
|
||||
but should be considered constant.
|
||||
not available.
|
||||
|
||||
.. member:: int line
|
||||
|
||||
The line number on which the error occurred, or -1 if this
|
||||
information is not available.
|
||||
|
||||
.. member:: int column
|
||||
|
||||
The character column on which the error occurred, or -1 if this
|
||||
information is not available.
|
||||
|
||||
.. member:: const char *source
|
||||
|
||||
Source of the error. This is (a part of) the file name when
|
||||
using :func:`json_load_file()`, or a special identifier in angle
|
||||
brackets otherwise (e.g. ``<string>``).
|
||||
|
||||
The normal use of :type:`json_error_t` is to allocate it on the
|
||||
stack, and pass a pointer to a decoding function. Example::
|
||||
|
||||
|
@ -3,6 +3,7 @@ include_HEADERS = jansson.h jansson_config.h
|
||||
lib_LTLIBRARIES = libjansson.la
|
||||
libjansson_la_SOURCES = \
|
||||
dump.c \
|
||||
error.c \
|
||||
hashtable.c \
|
||||
hashtable.h \
|
||||
jansson_private.h \
|
||||
|
38
src/error.c
Normal file
38
src/error.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "jansson_private.h"
|
||||
|
||||
void jsonp_error_init(json_error_t *error, const char *source)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
error->text[0] = '\0';
|
||||
error->line = -1;
|
||||
error->column = -1;
|
||||
|
||||
strncpy(error->source, source, JSON_ERROR_SOURCE_LENGTH);
|
||||
error->source[JSON_ERROR_SOURCE_LENGTH - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if(!error)
|
||||
return;
|
||||
|
||||
if(error->text[0] != '\0') {
|
||||
/* error already set */
|
||||
return;
|
||||
}
|
||||
|
||||
error->line = line;
|
||||
error->column = column;
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
@ -87,11 +87,14 @@ void json_decref(json_t *json)
|
||||
|
||||
/* error reporting */
|
||||
|
||||
#define JSON_ERROR_TEXT_LENGTH 160
|
||||
#define JSON_ERROR_TEXT_LENGTH 160
|
||||
#define JSON_ERROR_SOURCE_LENGTH 80
|
||||
|
||||
typedef struct {
|
||||
char text[JSON_ERROR_TEXT_LENGTH];
|
||||
int line;
|
||||
int column;
|
||||
char source[JSON_ERROR_SOURCE_LENGTH];
|
||||
} json_error_t;
|
||||
|
||||
|
||||
|
@ -63,4 +63,8 @@ typedef struct {
|
||||
|
||||
const object_key_t *jsonp_object_iter_fullkey(void *iter);
|
||||
|
||||
void jsonp_error_init(json_error_t *error, const char *source);
|
||||
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
const char *msg, ...);
|
||||
|
||||
#endif
|
||||
|
58
src/load.c
58
src/load.c
@ -60,54 +60,46 @@ typedef struct {
|
||||
|
||||
/*** error reporting ***/
|
||||
|
||||
static void error_init(json_error_t *error)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
error->text[0] = '\0';
|
||||
error->line = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void error_set(json_error_t *error, const lex_t *lex,
|
||||
const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char text[JSON_ERROR_TEXT_LENGTH];
|
||||
char msg_text[JSON_ERROR_TEXT_LENGTH];
|
||||
|
||||
if(!error || error->text[0] != '\0') {
|
||||
/* error already set */
|
||||
int line = -1, col = -1;
|
||||
const char *result = msg_text;
|
||||
|
||||
if(!error)
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf(text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(lex)
|
||||
{
|
||||
const char *saved_text = strbuffer_value(&lex->saved_text);
|
||||
error->line = lex->line;
|
||||
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
|
||||
|
||||
line = lex->line;
|
||||
|
||||
if(saved_text && saved_text[0])
|
||||
{
|
||||
if(lex->saved_text.length <= 20) {
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near '%s'", text, saved_text);
|
||||
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near '%s'", msg_text, saved_text);
|
||||
result = msg_with_context;
|
||||
}
|
||||
else
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near end of file", text);
|
||||
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near end of file", msg_text);
|
||||
result = msg_with_context;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error->line = -1;
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
|
||||
}
|
||||
|
||||
jsonp_error_set(error, line, col, "%s", result);
|
||||
}
|
||||
|
||||
|
||||
@ -774,8 +766,6 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
|
||||
static json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
{
|
||||
error_init(error);
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
error_set(error, lex, "'[' or '{' expected");
|
||||
@ -822,6 +812,8 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
if(lex_init(&lex, string_get, string_eof, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
jsonp_error_init(error, "<string>");
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
@ -841,12 +833,20 @@ out:
|
||||
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, (eof_func)feof, input))
|
||||
return NULL;
|
||||
|
||||
if(input == stdin)
|
||||
source = "<stdin>";
|
||||
else
|
||||
source = "<stream>";
|
||||
|
||||
jsonp_error_init(error, source);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
@ -868,7 +868,7 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
json_t *result;
|
||||
FILE *fp;
|
||||
|
||||
error_init(error);
|
||||
jsonp_error_init(error, path);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if(!fp)
|
||||
|
100
src/variadic.c
100
src/variadic.c
@ -13,31 +13,6 @@
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
|
||||
static void error_init(json_error_t *error)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
error->text[0] = '\0';
|
||||
error->line = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void error_set(json_error_t *error, const int line, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if(!error || error->text[0] != '\0') {
|
||||
/* error already set */
|
||||
return;
|
||||
}
|
||||
|
||||
error->line = line;
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
int fmt_length = strlen(fmt);
|
||||
va_list ap;
|
||||
@ -63,7 +38,7 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
stack = calloc(fmt_length, sizeof(json_t *));
|
||||
free_list = calloc(fmt_length, sizeof(json_t *));
|
||||
|
||||
error_init(error);
|
||||
jsonp_error_init(error, "");
|
||||
|
||||
if(!stack || !free_list)
|
||||
goto out;
|
||||
@ -81,7 +56,7 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
case ',': /* Element spacer */
|
||||
if(!root)
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Unexpected COMMA precedes root element!");
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -89,7 +64,7 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(!cur)
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Unexpected COMMA outside a list or object!");
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -97,7 +72,8 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(key)
|
||||
{
|
||||
error_set(error, line, "Expected KEY, got COMMA!");
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Expected KEY, got COMMA!");
|
||||
root = NULL;
|
||||
goto out;
|
||||
}
|
||||
@ -106,16 +82,18 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
case ':': /* Key/value separator */
|
||||
if(!key)
|
||||
{
|
||||
error_set(error, line, "Got key/value separator without "
|
||||
"a key preceding it!");
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Got key/value separator without "
|
||||
"a key preceding it!");
|
||||
root = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!json_is_object(cur))
|
||||
{
|
||||
error_set(error, line, "Got a key/value separator "
|
||||
"(':') outside an object!");
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Got a key/value separator "
|
||||
"(':') outside an object!");
|
||||
root = NULL;
|
||||
goto out;
|
||||
}
|
||||
@ -127,7 +105,8 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(key)
|
||||
{
|
||||
error_set(error, line, "OBJECT or ARRAY ended with an "
|
||||
jsonp_error_set(error, line, -1,
|
||||
"OBJECT or ARRAY ended with an "
|
||||
"incomplete key/value pair!");
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -135,7 +114,7 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(depth <= 0)
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Too many close-brackets '%c'", *fmt);
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -143,7 +122,7 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(*fmt == ']' && !json_is_array(cur))
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Stray close-array ']' character");
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -151,7 +130,7 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(*fmt == '}' && !json_is_object(cur))
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Stray close-object '}' character");
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -173,7 +152,7 @@ json_t *json_pack(json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(!s)
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Refusing to handle a NULL string");
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -221,8 +200,8 @@ obj_common: free_list[free_count++] = obj;
|
||||
if(json_is_object(cur)) {
|
||||
if(!key)
|
||||
{
|
||||
error_set(error, line,
|
||||
"Expected key, got identifier '%c'!", *fmt);
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Expected key, got identifier '%c'!", *fmt);
|
||||
root = NULL;
|
||||
goto out;
|
||||
}
|
||||
@ -241,7 +220,8 @@ obj_common: free_list[free_count++] = obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
error_set(error, line, "Can't figure out where to attach "
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Can't figure out where to attach "
|
||||
"'%c' object!", *fmt);
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -261,7 +241,7 @@ obj_common: free_list[free_count++] = obj;
|
||||
va_end(ap);
|
||||
|
||||
if(depth != 0) {
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Missing object or array close-brackets in format string");
|
||||
root = NULL;
|
||||
goto out;
|
||||
@ -301,13 +281,13 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
int fmt_length = strlen(fmt);
|
||||
|
||||
error_init(error);
|
||||
jsonp_error_init(error, "");
|
||||
|
||||
/* Allocation provisioned for worst case */
|
||||
stack = calloc(fmt_length, sizeof(json_t *));
|
||||
if(!stack)
|
||||
{
|
||||
error_set(error, line, "Out of memory!");
|
||||
jsonp_error_set(error, line, -1, "Out of memory!");
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -335,7 +315,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(!cur)
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Unexpected COMMA outside a list or object!");
|
||||
rv = -1;
|
||||
goto out;
|
||||
@ -343,7 +323,8 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(key)
|
||||
{
|
||||
error_set(error, line, "Expected KEY, got COMMA!");
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Expected KEY, got COMMA!");
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -352,7 +333,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
case ':': /* Key/value separator */
|
||||
if(!json_is_object(cur) || !key)
|
||||
{
|
||||
error_set(error, line, "Unexpected ':'");
|
||||
jsonp_error_set(error, line, -1, "Unexpected ':'");
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -369,7 +350,8 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
{
|
||||
if(!key)
|
||||
{
|
||||
error_set(error, line, "Objects can't be keys");
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Objects can't be keys");
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -419,28 +401,28 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
|
||||
if(json_is_array(cur) && *fmt!=']')
|
||||
{
|
||||
error_set(error, line, "Missing ']'");
|
||||
jsonp_error_set(error, line, -1, "Missing ']'");
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(json_is_object(cur) && *fmt!='}')
|
||||
{
|
||||
error_set(error, line, "Missing '}'");
|
||||
jsonp_error_set(error, line, -1, "Missing '}'");
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(key)
|
||||
{
|
||||
error_set(error, line, "Unexpected '%c'", *fmt);
|
||||
jsonp_error_set(error, line, -1, "Unexpected '%c'", *fmt);
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(depth <= 0)
|
||||
{
|
||||
error_set(error, line, "Unexpected '%c'", *fmt);
|
||||
jsonp_error_set(error, line, -1, "Unexpected '%c'", *fmt);
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -474,7 +456,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
{
|
||||
if(!key)
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Only strings may be used as keys!");
|
||||
rv = -1;
|
||||
goto out;
|
||||
@ -492,7 +474,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
}
|
||||
else
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Unsure how to retrieve JSON object '%c'",
|
||||
*fmt);
|
||||
rv = -1;
|
||||
@ -504,7 +486,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
case 's':
|
||||
if(!json_is_string(obj))
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Type mismatch! Object wasn't a string.");
|
||||
rv = -2;
|
||||
goto out;
|
||||
@ -515,7 +497,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
case 'i':
|
||||
if(!json_is_integer(obj))
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Type mismatch! Object wasn't an integer.");
|
||||
rv = -2;
|
||||
goto out;
|
||||
@ -526,7 +508,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
case 'b':
|
||||
if(!json_is_boolean(obj))
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Type mismatch! Object wasn't a boolean.");
|
||||
rv = -2;
|
||||
goto out;
|
||||
@ -537,7 +519,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
case 'f':
|
||||
if(!json_is_number(obj))
|
||||
{
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Type mismatch! Object wasn't a real.");
|
||||
rv = -2;
|
||||
goto out;
|
||||
@ -560,7 +542,7 @@ int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
|
||||
break;
|
||||
|
||||
default:
|
||||
error_set(error, line,
|
||||
jsonp_error_set(error, line, -1,
|
||||
"Unknown format character '%c'", *fmt);
|
||||
rv = -1;
|
||||
goto out;
|
||||
|
Loading…
Reference in New Issue
Block a user