Tanguy Pruvot
10 years ago
22 changed files with 2274 additions and 755 deletions
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
*.h.in |
||||
*.h.in~ |
||||
libtool |
||||
libjansson.a |
@ -1,18 +1,20 @@
@@ -1,18 +1,20 @@
|
||||
|
||||
noinst_LIBRARIES = libjansson.a |
||||
|
||||
libjansson_a_SOURCES = \
|
||||
config.h \
|
||||
dump.c \
|
||||
hashtable.c \
|
||||
hashtable.h \
|
||||
jansson.h \
|
||||
jansson_private.h \
|
||||
load.c \
|
||||
strbuffer.c \
|
||||
strbuffer.h \
|
||||
utf.c \
|
||||
utf.h \
|
||||
util.h \
|
||||
value.c |
||||
|
||||
|
||||
noinst_LIBRARIES = libjansson.a |
||||
|
||||
libjansson_a_SOURCES = \
|
||||
config.h \
|
||||
dump.c \
|
||||
error.c \
|
||||
hashtable.c hashtable.h \
|
||||
jansson.h \
|
||||
jansson_config.h \
|
||||
jansson_private.h \
|
||||
load.c \
|
||||
memory.c \
|
||||
pack_unpack.c \
|
||||
strbuffer.c strbuffer.h \
|
||||
strconv.c \
|
||||
utf.c utf.h \
|
||||
util.h \
|
||||
value.c |
||||
|
||||
|
@ -1,73 +1,140 @@
@@ -1,73 +1,140 @@
|
||||
/* config.h. Generated from config.h.in by configure. */ |
||||
/* config.h.in. Generated from configure.ac by autoheader. */ |
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */ |
||||
#define HAVE_DLFCN_H 1 |
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */ |
||||
#define HAVE_INTTYPES_H 1 |
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */ |
||||
#define HAVE_MEMORY_H 1 |
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */ |
||||
#define HAVE_STDINT_H 1 |
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */ |
||||
#define HAVE_STDLIB_H 1 |
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */ |
||||
#define HAVE_STRINGS_H 1 |
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */ |
||||
#define HAVE_STRING_H 1 |
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */ |
||||
#define HAVE_SYS_STAT_H 1 |
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */ |
||||
#define HAVE_SYS_TYPES_H 1 |
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */ |
||||
#define HAVE_UNISTD_H 1 |
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/ |
||||
#define LT_OBJDIR ".libs/" |
||||
|
||||
/* Name of package */ |
||||
#define PACKAGE "jansson" |
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */ |
||||
#define PACKAGE_BUGREPORT "petri@digip.org" |
||||
|
||||
/* Define to the full name of this package. */ |
||||
#define PACKAGE_NAME "jansson" |
||||
|
||||
/* Define to the full name and version of this package. */ |
||||
#define PACKAGE_STRING "jansson 1.3" |
||||
|
||||
/* Define to the one symbol short name of this package. */ |
||||
#define PACKAGE_TARNAME "jansson" |
||||
|
||||
/* Define to the home page for this package. */ |
||||
#define PACKAGE_URL "" |
||||
|
||||
/* Define to the version of this package. */ |
||||
#define PACKAGE_VERSION "1.3" |
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */ |
||||
#define STDC_HEADERS 1 |
||||
|
||||
/* Version number of package */ |
||||
#define VERSION "1.3" |
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */ |
||||
#ifndef __cplusplus |
||||
/* #undef inline */ |
||||
#endif |
||||
|
||||
/* Define to the type of a signed integer type of width exactly 32 bits if
|
||||
such a type exists and the standard includes do not define it. */ |
||||
/* #undef int32_t */ |
||||
/* config.h. Generated from config.h.in by configure. */ |
||||
/* config.h.in. Generated from configure.ac by autoheader. */ |
||||
|
||||
/* Define to 1 if gcc's __atomic builtins are available */ |
||||
/* #undef HAVE_ATOMIC_BUILTINS */ |
||||
|
||||
/* Define to 1 if you have the `close' function. */ |
||||
#define HAVE_CLOSE 1 |
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */ |
||||
/* #undef HAVE_DLFCN_H */ |
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */ |
||||
/* #undef HAVE_ENDIAN_H */ |
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */ |
||||
#define HAVE_FCNTL_H 1 |
||||
|
||||
/* Define to 1 if you have the `getpid' function. */ |
||||
#define HAVE_GETPID 1 |
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */ |
||||
#define HAVE_GETTIMEOFDAY 1 |
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */ |
||||
#define HAVE_INTTYPES_H 1 |
||||
|
||||
/* Define to 1 if you have the `localeconv' function. */ |
||||
#define HAVE_LOCALECONV 1 |
||||
|
||||
/* Define to 1 if you have the <locale.h> header file. */ |
||||
#define HAVE_LOCALE_H 1 |
||||
|
||||
/* Define to 1 if the system has the type `long long int'. */ |
||||
#define HAVE_LONG_LONG_INT 1 |
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */ |
||||
#define HAVE_MEMORY_H 1 |
||||
|
||||
/* Define to 1 if you have the `open' function. */ |
||||
#define HAVE_OPEN 1 |
||||
|
||||
/* Define to 1 if you have the `read' function. */ |
||||
#define HAVE_READ 1 |
||||
|
||||
/* Define to 1 if you have the <sched.h> header file. */ |
||||
#define HAVE_SCHED_H 1 |
||||
|
||||
/* Define to 1 if you have the `sched_yield' function. */ |
||||
/* #undef HAVE_SCHED_YIELD */ |
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */ |
||||
#define HAVE_STDINT_H 1 |
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */ |
||||
#define HAVE_STDLIB_H 1 |
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */ |
||||
#define HAVE_STRINGS_H 1 |
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */ |
||||
#define HAVE_STRING_H 1 |
||||
|
||||
/* Define to 1 if you have the `strtoll' function. */ |
||||
#define HAVE_STRTOLL 1 |
||||
|
||||
/* Define to 1 if gcc's __sync builtins are available */ |
||||
#define HAVE_SYNC_BUILTINS 1 |
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */ |
||||
#define HAVE_SYS_PARAM_H 1 |
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */ |
||||
#define HAVE_SYS_STAT_H 1 |
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */ |
||||
#define HAVE_SYS_TIME_H 1 |
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */ |
||||
#define HAVE_SYS_TYPES_H 1 |
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */ |
||||
#define HAVE_UNISTD_H 1 |
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/ |
||||
#define LT_OBJDIR ".libs/" |
||||
|
||||
/* Name of package */ |
||||
#define PACKAGE "jansson" |
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */ |
||||
#define PACKAGE_BUGREPORT "petri@digip.org" |
||||
|
||||
/* Define to the full name of this package. */ |
||||
#define PACKAGE_NAME "jansson" |
||||
|
||||
/* Define to the full name and version of this package. */ |
||||
#define PACKAGE_STRING "jansson 2.6" |
||||
|
||||
/* Define to the one symbol short name of this package. */ |
||||
#define PACKAGE_TARNAME "jansson" |
||||
|
||||
/* Define to the home page for this package. */ |
||||
#define PACKAGE_URL "" |
||||
|
||||
/* Define to the version of this package. */ |
||||
#define PACKAGE_VERSION "2.6" |
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */ |
||||
#define STDC_HEADERS 1 |
||||
|
||||
/* Define to 1 if /dev/urandom should be used for seeding the hash function */ |
||||
#define USE_URANDOM 1 |
||||
|
||||
/* Define to 1 if CryptGenRandom should be used for seeding the hash function
|
||||
*/ |
||||
#define USE_WINDOWS_CRYPTOAPI 1 |
||||
|
||||
/* Version number of package */ |
||||
#define VERSION "2.6" |
||||
|
||||
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the |
||||
#define below would cause a syntax error. */ |
||||
/* #undef _UINT32_T */ |
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */ |
||||
#ifndef __cplusplus |
||||
/* #undef inline */ |
||||
#endif |
||||
|
||||
/* Define to the type of a signed integer type of width exactly 32 bits if
|
||||
such a type exists and the standard includes do not define it. */ |
||||
/* #undef int32_t */ |
||||
|
||||
/* Define to the type of an unsigned integer type of width exactly 32 bits if
|
||||
such a type exists and the standard includes do not define it. */ |
||||
/* #undef uint32_t */ |
||||
|
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
AC_PREREQ([2.60]) |
||||
AC_INIT([jansson], [2.6], [petri@digip.org]) |
||||
|
||||
AC_CONFIG_MACRO_DIR([m4]) |
||||
|
||||
AM_INIT_AUTOMAKE([1.10 foreign]) |
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) |
||||
|
||||
AC_CONFIG_SRCDIR([value.c]) |
||||
AC_CONFIG_HEADERS([config.h]) |
||||
|
||||
# Checks for programs. |
||||
AC_PROG_CC |
||||
AC_PROG_LIBTOOL |
||||
AM_CONDITIONAL([GCC], [test x$GCC = xyes]) |
||||
|
||||
# Checks for libraries. |
||||
|
||||
# Checks for header files. |
||||
AC_CHECK_HEADERS([endian.h fcntl.h locale.h sched.h unistd.h sys/param.h sys/stat.h sys/time.h sys/types.h]) |
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics. |
||||
AC_TYPE_INT32_T |
||||
AC_TYPE_UINT32_T |
||||
AC_TYPE_LONG_LONG_INT |
||||
|
||||
AC_C_INLINE |
||||
case $ac_cv_c_inline in |
||||
yes) json_inline=inline;; |
||||
no) json_inline=;; |
||||
*) json_inline=$ac_cv_c_inline;; |
||||
esac |
||||
AC_SUBST([json_inline]) |
||||
|
||||
# Checks for library functions. |
||||
AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strtoll]) |
||||
|
||||
AC_MSG_CHECKING([for gcc __sync builtins]) |
||||
have_sync_builtins=no |
||||
AC_TRY_LINK( |
||||
[], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1);], |
||||
[have_sync_builtins=yes], |
||||
) |
||||
if test "x$have_sync_builtins" = "xyes"; then |
||||
AC_DEFINE([HAVE_SYNC_BUILTINS], [1], |
||||
[Define to 1 if gcc's __sync builtins are available]) |
||||
fi |
||||
AC_MSG_RESULT([$have_sync_builtins]) |
||||
|
||||
AC_MSG_CHECKING([for gcc __atomic builtins]) |
||||
have_atomic_builtins=no |
||||
AC_TRY_LINK( |
||||
[], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_ACQ_REL); __atomic_load_n(&v, __ATOMIC_ACQUIRE);], |
||||
[have_atomic_builtins=yes], |
||||
) |
||||
if test "x$have_atomic_builtins" = "xyes"; then |
||||
AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1], |
||||
[Define to 1 if gcc's __atomic builtins are available]) |
||||
fi |
||||
AC_MSG_RESULT([$have_atomic_builtins]) |
||||
|
||||
case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in |
||||
yesyes) json_have_long_long=1;; |
||||
*) json_have_long_long=0;; |
||||
esac |
||||
AC_SUBST([json_have_long_long]) |
||||
|
||||
case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in |
||||
yesyes) json_have_localeconv=1;; |
||||
*) json_have_localeconv=0;; |
||||
esac |
||||
AC_SUBST([json_have_localeconv]) |
||||
|
||||
# Features |
||||
AC_ARG_ENABLE([urandom], |
||||
[AS_HELP_STRING([--disable-urandom], |
||||
[Don't use /dev/urandom to seed the hash function])], |
||||
[use_urandom=$enableval], [use_urandom=yes]) |
||||
|
||||
if test "x$use_urandom" = xyes; then |
||||
AC_DEFINE([USE_URANDOM], [1], |
||||
[Define to 1 if /dev/urandom should be used for seeding the hash function]) |
||||
fi |
||||
|
||||
AC_ARG_ENABLE([windows-cryptoapi], |
||||
[AS_HELP_STRING([--disable-windows-cryptoapi], |
||||
[Don't use CryptGenRandom to seed the hash function])], |
||||
[use_windows_cryptoapi=$enableval], [use_windows_cryptoapi=yes]) |
||||
|
||||
if test "x$use_windows_cryptoapi" = xyes; then |
||||
AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1], |
||||
[Define to 1 if CryptGenRandom should be used for seeding the hash function]) |
||||
fi |
||||
|
||||
AC_CONFIG_FILES([ |
||||
Makefile |
||||
]) |
||||
AC_OUTPUT |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
#include <string.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; |
||||
error->position = 0; |
||||
if(source) |
||||
jsonp_error_set_source(error, source); |
||||
else |
||||
error->source[0] = '\0'; |
||||
} |
||||
} |
||||
|
||||
void jsonp_error_set_source(json_error_t *error, const char *source) |
||||
{ |
||||
size_t length; |
||||
|
||||
if(!error || !source) |
||||
return; |
||||
|
||||
length = strlen(source); |
||||
if(length < JSON_ERROR_SOURCE_LENGTH) |
||||
strcpy(error->source, source); |
||||
else { |
||||
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; |
||||
strcpy(error->source, "..."); |
||||
strcpy(error->source + 3, source + extra); |
||||
} |
||||
} |
||||
|
||||
void jsonp_error_set(json_error_t *error, int line, int column, |
||||
size_t position, const char *msg, ...) |
||||
{ |
||||
va_list ap; |
||||
|
||||
va_start(ap, msg); |
||||
jsonp_error_vset(error, line, column, position, msg, ap); |
||||
va_end(ap); |
||||
} |
||||
|
||||
void jsonp_error_vset(json_error_t *error, int line, int column, |
||||
size_t position, const char *msg, va_list ap) |
||||
{ |
||||
if(!error) |
||||
return; |
||||
|
||||
if(error->text[0] != '\0') { |
||||
/* error already set */ |
||||
return; |
||||
} |
||||
|
||||
error->line = line; |
||||
error->column = column; |
||||
error->position = position; |
||||
|
||||
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap); |
||||
error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; |
||||
} |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 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. |
||||
* |
||||
* |
||||
* This file specifies a part of the site-specific configuration for |
||||
* Jansson, namely those things that affect the public API in |
||||
* jansson.h. |
||||
* |
||||
* The configure script copies this file to jansson_config.h and |
||||
* replaces @var@ substitutions by values that fit your system. If you |
||||
* cannot run the configure script, you can do the value substitution |
||||
* by hand. |
||||
*/ |
||||
|
||||
#ifndef JANSSON_CONFIG_H |
||||
#define JANSSON_CONFIG_H |
||||
/* If your compiler supports the inline keyword in C, JSON_INLINE is
|
||||
defined to `inline', otherwise empty. In C++, the inline is always |
||||
supported. */ |
||||
|
||||
#ifdef _MSC_VER |
||||
#define inline __inline |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
#define JSON_INLINE inline |
||||
#else |
||||
#define JSON_INLINE inline |
||||
#endif |
||||
|
||||
/* If your compiler supports the `long long` type and the strtoll()
|
||||
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, |
||||
otherwise to 0. */ |
||||
#define JSON_INTEGER_IS_LONG_LONG 1 |
||||
|
||||
/* If locale.h and localeconv() are available, define to 1,
|
||||
otherwise to 0. */ |
||||
#define JSON_HAVE_LOCALECONV 1 |
||||
|
||||
#endif |
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||
* Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net> |
||||
* |
||||
* Jansson is free software; you can redistribute it and/or modify it |
||||
* under the terms of the MIT license. See LICENSE for details. |
||||
*/ |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include "jansson.h" |
||||
#include "jansson_private.h" |
||||
|
||||
/* memory function pointers */ |
||||
static json_malloc_t do_malloc = malloc; |
||||
static json_free_t do_free = free; |
||||
|
||||
void *jsonp_malloc(size_t size) |
||||
{ |
||||
if(!size) |
||||
return NULL; |
||||
|
||||
return (*do_malloc)(size); |
||||
} |
||||
|
||||
void jsonp_free(void *ptr) |
||||
{ |
||||
if(!ptr) |
||||
return; |
||||
|
||||
(*do_free)(ptr); |
||||
} |
||||
|
||||
char *jsonp_strdup(const char *str) |
||||
{ |
||||
char *new_str; |
||||
size_t len; |
||||
|
||||
len = strlen(str); |
||||
if(len == (size_t)-1) |
||||
return NULL; |
||||
|
||||
new_str = jsonp_malloc(len + 1); |
||||
if(!new_str) |
||||
return NULL; |
||||
|
||||
memcpy(new_str, str, len + 1); |
||||
return new_str; |
||||
} |
||||
|
||||
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) |
||||
{ |
||||
do_malloc = malloc_fn; |
||||
do_free = free_fn; |
||||
} |
@ -0,0 +1,762 @@
@@ -0,0 +1,762 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org> |
||||
* Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca> |
||||
* |
||||
* Jansson is free software; you can redistribute it and/or modify |
||||
* it under the terms of the MIT license. See LICENSE for details. |
||||
*/ |
||||
|
||||
#include <string.h> |
||||
#include "jansson.h" |
||||
#include "jansson_private.h" |
||||
#include "utf.h" |
||||
|
||||
typedef struct { |
||||
int line; |
||||
int column; |
||||
size_t pos; |
||||
char token; |
||||
} token_t; |
||||
|
||||
typedef struct { |
||||
const char *start; |
||||
const char *fmt; |
||||
token_t prev_token; |
||||
token_t token; |
||||
token_t next_token; |
||||
json_error_t *error; |
||||
size_t flags; |
||||
int line; |
||||
int column; |
||||
size_t pos; |
||||
} scanner_t; |
||||
|
||||
#define token(scanner) ((scanner)->token.token) |
||||
|
||||
static const char * const type_names[] = { |
||||
"object", |
||||
"array", |
||||
"string", |
||||
"integer", |
||||
"real", |
||||
"true", |
||||
"false", |
||||
"null" |
||||
}; |
||||
|
||||
#define type_name(x) type_names[json_typeof(x)] |
||||
|
||||
static const char unpack_value_starters[] = "{[siIbfFOon"; |
||||
|
||||
|
||||
static void scanner_init(scanner_t *s, json_error_t *error, |
||||
size_t flags, const char *fmt) |
||||
{ |
||||
s->error = error; |
||||
s->flags = flags; |
||||
s->fmt = s->start = fmt; |
||||
memset(&s->prev_token, 0, sizeof(token_t)); |
||||
memset(&s->token, 0, sizeof(token_t)); |
||||
memset(&s->next_token, 0, sizeof(token_t)); |
||||
s->line = 1; |
||||
s->column = 0; |
||||
s->pos = 0; |
||||
} |
||||
|
||||
static void next_token(scanner_t *s) |
||||
{ |
||||
const char *t; |
||||
s->prev_token = s->token; |
||||
|
||||
if(s->next_token.line) { |
||||
s->token = s->next_token; |
||||
s->next_token.line = 0; |
||||
return; |
||||
} |
||||
|
||||
t = s->fmt; |
||||
s->column++; |
||||
s->pos++; |
||||
|
||||
/* skip space and ignored chars */ |
||||
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { |
||||
if(*t == '\n') { |
||||
s->line++; |
||||
s->column = 1; |
||||
} |
||||
else |
||||
s->column++; |
||||
|
||||
s->pos++; |
||||
t++; |
||||
} |
||||
|
||||
s->token.token = *t; |
||||
s->token.line = s->line; |
||||
s->token.column = s->column; |
||||
s->token.pos = s->pos; |
||||
|
||||
t++; |
||||
s->fmt = t; |
||||
} |
||||
|
||||
static void prev_token(scanner_t *s) |
||||
{ |
||||
s->next_token = s->token; |
||||
s->token = s->prev_token; |
||||
} |
||||
|
||||
static void set_error(scanner_t *s, const char *source, const char *fmt, ...) |
||||
{ |
||||
va_list ap; |
||||
va_start(ap, fmt); |
||||
|
||||
jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, |
||||
fmt, ap); |
||||
|
||||
jsonp_error_set_source(s->error, source); |
||||
|
||||
va_end(ap); |
||||
} |
||||
|
||||
static json_t *pack(scanner_t *s, va_list *ap); |
||||
|
||||
|
||||
/* ours will be set to 1 if jsonp_free() must be called for the result
|
||||
afterwards */ |
||||
static char *read_string(scanner_t *s, va_list *ap, |
||||
const char *purpose, int *ours) |
||||
{ |
||||
char t; |
||||
strbuffer_t strbuff; |
||||
const char *str; |
||||
size_t length; |
||||
char *result; |
||||
|
||||
next_token(s); |
||||
t = token(s); |
||||
prev_token(s); |
||||
|
||||
if(t != '#' && t != '+') { |
||||
/* Optimize the simple case */ |
||||
str = va_arg(*ap, const char *); |
||||
|
||||
if(!str) { |
||||
set_error(s, "<args>", "NULL string argument"); |
||||
return NULL; |
||||
} |
||||
|
||||
if(!utf8_check_string(str, -1)) { |
||||
set_error(s, "<args>", "Invalid UTF-8 %s", purpose); |
||||
return NULL; |
||||
} |
||||
|
||||
*ours = 0; |
||||
return (char *)str; |
||||
} |
||||
|
||||
strbuffer_init(&strbuff); |
||||
|
||||
while(1) { |
||||
str = va_arg(*ap, const char *); |
||||
if(!str) { |
||||
set_error(s, "<args>", "NULL string argument"); |
||||
strbuffer_close(&strbuff); |
||||
return NULL; |
||||
} |
||||
|
||||
next_token(s); |
||||
|
||||
if(token(s) == '#') { |
||||
length = va_arg(*ap, int); |
||||
} |
||||
else { |
||||
prev_token(s); |
||||
length = strlen(str); |
||||
} |
||||
|
||||
if(strbuffer_append_bytes(&strbuff, str, length) == -1) { |
||||
set_error(s, "<internal>", "Out of memory"); |
||||
strbuffer_close(&strbuff); |
||||
return NULL; |
||||
} |
||||
|
||||
next_token(s); |
||||
if(token(s) != '+') { |
||||
prev_token(s); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
result = strbuffer_steal_value(&strbuff); |
||||
|
||||
if(!utf8_check_string(result, -1)) { |
||||
set_error(s, "<args>", "Invalid UTF-8 %s", purpose); |
||||
return NULL; |
||||
} |
||||
|
||||
*ours = 1; |
||||
return result; |
||||
} |
||||
|
||||
static json_t *pack_object(scanner_t *s, va_list *ap) |
||||
{ |
||||
json_t *object = json_object(); |
||||
next_token(s); |
||||
|
||||
while(token(s) != '}') { |
||||
char *key; |
||||
int ours; |
||||
json_t *value; |
||||
|
||||
if(!token(s)) { |
||||
set_error(s, "<format>", "Unexpected end of format string"); |
||||
goto error; |
||||
} |
||||
|
||||
if(token(s) != 's') { |
||||
set_error(s, "<format>", "Expected format 's', got '%c'", token(s)); |
||||
goto error; |
||||
} |
||||
|
||||
key = read_string(s, ap, "object key", &ours); |
||||
if(!key) |
||||
goto error; |
||||
|
||||
next_token(s); |
||||
|
||||
value = pack(s, ap); |
||||
if(!value) |
||||
goto error; |
||||
|
||||
if(json_object_set_new_nocheck(object, key, value)) { |
||||
if(ours) |
||||
jsonp_free(key); |
||||
|
||||
set_error(s, "<internal>", "Unable to add key \"%s\"", key); |
||||
goto error; |
||||
} |
||||
|
||||
if(ours) |
||||
jsonp_free(key); |
||||
|
||||
next_token(s); |
||||
} |
||||
|
||||
return object; |
||||
|
||||
error: |
||||
json_decref(object); |
||||
return NULL; |
||||
} |
||||
|
||||
static json_t *pack_array(scanner_t *s, va_list *ap) |
||||
{ |
||||
json_t *array = json_array(); |
||||
next_token(s); |
||||
|
||||
while(token(s) != ']') { |
||||
json_t *value; |
||||
|
||||
if(!token(s)) { |
||||
set_error(s, "<format>", "Unexpected end of format string"); |
||||
goto error; |
||||
} |
||||
|
||||
value = pack(s, ap); |
||||
if(!value) |
||||
goto error; |
||||
|
||||
if(json_array_append_new(array, value)) { |
||||
set_error(s, "<internal>", "Unable to append to array"); |
||||
goto error; |
||||
} |
||||
|
||||
next_token(s); |
||||
} |
||||
return array; |
||||
|
||||
error: |
||||
json_decref(array); |
||||
return NULL; |
||||
} |
||||
|
||||
static json_t *pack(scanner_t *s, va_list *ap) |
||||
{ |
||||
switch(token(s)) { |
||||
case '{': |
||||
return pack_object(s, ap); |
||||
|
||||
case '[': |
||||
return pack_array(s, ap); |
||||
|
||||
case 's': { /* string */ |
||||
char *str; |
||||
int ours; |
||||
json_t *result; |
||||
|
||||
str = read_string(s, ap, "string", &ours); |
||||
if(!str) |
||||
return NULL; |
||||
|
||||
result = json_string_nocheck(str); |
||||
if(ours) |
||||
jsonp_free(str); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
case 'n': /* null */ |
||||
return json_null(); |
||||
|
||||
case 'b': /* boolean */ |
||||
return va_arg(*ap, int) ? json_true() : json_false(); |
||||
|
||||
case 'i': /* integer from int */ |
||||
return json_integer(va_arg(*ap, int)); |
||||
|
||||
case 'I': /* integer from json_int_t */ |
||||
return json_integer(va_arg(*ap, json_int_t)); |
||||
|
||||
case 'f': /* real */ |
||||
return json_real(va_arg(*ap, double)); |
||||
|
||||
case 'O': /* a json_t object; increments refcount */ |
||||
return json_incref(va_arg(*ap, json_t *)); |
||||
|
||||
case 'o': /* a json_t object; doesn't increment refcount */ |
||||
return va_arg(*ap, json_t *); |
||||
|
||||
default: |
||||
set_error(s, "<format>", "Unexpected format character '%c'", |
||||
token(s)); |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
static int unpack(scanner_t *s, json_t *root, va_list *ap); |
||||
|
||||
static int unpack_object(scanner_t *s, json_t *root, va_list *ap) |
||||
{ |
||||
int ret = -1; |
||||
int strict = 0; |
||||
|
||||
/* Use a set (emulated by a hashtable) to check that all object
|
||||
keys are accessed. Checking that the correct number of keys |
||||
were accessed is not enough, as the same key can be unpacked |
||||
multiple times. |
||||
*/ |
||||
hashtable_t key_set; |
||||
|
||||
if(hashtable_init(&key_set)) { |
||||
set_error(s, "<internal>", "Out of memory"); |
||||
return -1; |
||||
} |
||||
|
||||
if(root && !json_is_object(root)) { |
||||
set_error(s, "<validation>", "Expected object, got %s", |
||||
type_name(root)); |
||||
goto out; |
||||
} |
||||
next_token(s); |
||||
|
||||
while(token(s) != '}') { |
||||
const char *key; |
||||
json_t *value; |
||||
int opt = 0; |
||||
|
||||
if(strict != 0) { |
||||
set_error(s, "<format>", "Expected '}' after '%c', got '%c'", |
||||
(strict == 1 ? '!' : '*'), token(s)); |
||||
goto out; |
||||
} |
||||
|
||||
if(!token(s)) { |
||||
set_error(s, "<format>", "Unexpected end of format string"); |
||||
goto out; |
||||
} |
||||
|
||||
if(token(s) == '!' || token(s) == '*') { |
||||
strict = (token(s) == '!' ? 1 : -1); |
||||
next_token(s); |
||||
continue; |
||||
} |
||||
|
||||
if(token(s) != 's') { |
||||
set_error(s, "<format>", "Expected format 's', got '%c'", token(s)); |
||||
goto out; |
||||
} |
||||
|
||||
key = va_arg(*ap, const char *); |
||||
if(!key) { |
||||
set_error(s, "<args>", "NULL object key"); |
||||
goto out; |
||||
} |
||||
|
||||
next_token(s); |
||||
|
||||
if(token(s) == '?') { |
||||
opt = 1; |
||||
next_token(s); |
||||
} |
||||
|
||||
if(!root) { |
||||
/* skipping */ |
||||
value = NULL; |
||||
} |
||||
else { |
||||
value = json_object_get(root, key); |
||||
if(!value && !opt) { |
||||
set_error(s, "<validation>", "Object item not found: %s", key); |
||||
goto out; |
||||
} |
||||
} |
||||
|
||||
if(unpack(s, value, ap)) |
||||
goto out; |
||||
|
||||
hashtable_set(&key_set, key, 0, json_null()); |
||||
next_token(s); |
||||
} |
||||
|
||||
if(strict == 0 && (s->flags & JSON_STRICT)) |
||||
strict = 1; |
||||
|
||||
if(root && strict == 1 && key_set.size != json_object_size(root)) { |
||||
long diff = (long)json_object_size(root) - (long)key_set.size; |
||||
set_error(s, "<validation>", "%li object item(s) left unpacked", diff); |
||||
goto out; |
||||
} |
||||
|
||||
ret = 0; |
||||
|
||||
out: |
||||
hashtable_close(&key_set); |
||||
return ret; |
||||
} |
||||
|
||||
static int unpack_array(scanner_t *s, json_t *root, va_list *ap) |
||||
{ |
||||
size_t i = 0; |
||||
int strict = 0; |
||||
|
||||
if(root && !json_is_array(root)) { |
||||
set_error(s, "<validation>", "Expected array, got %s", type_name(root)); |
||||
return -1; |
||||
} |
||||
next_token(s); |
||||
|
||||
while(token(s) != ']') { |
||||
json_t *value; |
||||
|
||||
if(strict != 0) { |
||||
set_error(s, "<format>", "Expected ']' after '%c', got '%c'", |
||||
(strict == 1 ? '!' : '*'), |
||||
token(s)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!token(s)) { |
||||
set_error(s, "<format>", "Unexpected end of format string"); |
||||
return -1; |
||||
} |
||||
|
||||
if(token(s) == '!' || token(s) == '*') { |
||||
strict = (token(s) == '!' ? 1 : -1); |
||||
next_token(s); |
||||
continue; |
||||
} |
||||
|
||||
if(!strchr(unpack_value_starters, token(s))) { |
||||
set_error(s, "<format>", "Unexpected format character '%c'", |
||||
token(s)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!root) { |
||||
/* skipping */ |
||||
value = NULL; |
||||
} |
||||
else { |
||||
value = json_array_get(root, i); |
||||
if(!value) { |
||||
set_error(s, "<validation>", "Array index %lu out of range", |
||||
(unsigned long)i); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
if(unpack(s, value, ap)) |
||||
return -1; |
||||
|
||||
next_token(s); |
||||
i++; |
||||
} |
||||
|
||||
if(strict == 0 && (s->flags & JSON_STRICT)) |
||||
strict = 1; |
||||
|
||||
if(root && strict == 1 && i != json_array_size(root)) { |
||||
long diff = (long)json_array_size(root) - (long)i; |
||||
set_error(s, "<validation>", "%li array item(s) left unpacked", diff); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int unpack(scanner_t *s, json_t *root, va_list *ap) |
||||
{ |
||||
switch(token(s)) |
||||
{ |
||||
case '{': |
||||
return unpack_object(s, root, ap); |
||||
|
||||
case '[': |
||||
return unpack_array(s, root, ap); |
||||
|
||||
case 's': |
||||
if(root && !json_is_string(root)) { |
||||
set_error(s, "<validation>", "Expected string, got %s", |
||||
type_name(root)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) { |
||||
const char **target; |
||||
|
||||
target = va_arg(*ap, const char **); |
||||
if(!target) { |
||||
set_error(s, "<args>", "NULL string argument"); |
||||
return -1; |
||||
} |
||||
|
||||
if(root) |
||||
*target = json_string_value(root); |
||||
} |
||||
return 0; |
||||
|
||||
case 'i': |
||||
if(root && !json_is_integer(root)) { |
||||
set_error(s, "<validation>", "Expected integer, got %s", |
||||
type_name(root)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) { |
||||
int *target = va_arg(*ap, int*); |
||||
if(root) |
||||
*target = (int)json_integer_value(root); |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
case 'I': |
||||
if(root && !json_is_integer(root)) { |
||||
set_error(s, "<validation>", "Expected integer, got %s", |
||||
type_name(root)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) { |
||||
json_int_t *target = va_arg(*ap, json_int_t*); |
||||
if(root) |
||||
*target = json_integer_value(root); |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
case 'b': |
||||
if(root && !json_is_boolean(root)) { |
||||
set_error(s, "<validation>", "Expected true or false, got %s", |
||||
type_name(root)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) { |
||||
int *target = va_arg(*ap, int*); |
||||
if(root) |
||||
*target = json_is_true(root); |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
case 'f': |
||||
if(root && !json_is_real(root)) { |
||||
set_error(s, "<validation>", "Expected real, got %s", |
||||
type_name(root)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) { |
||||
double *target = va_arg(*ap, double*); |
||||
if(root) |
||||
*target = json_real_value(root); |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
case 'F': |
||||
if(root && !json_is_number(root)) { |
||||
set_error(s, "<validation>", "Expected real or integer, got %s", |
||||
type_name(root)); |
||||
return -1; |
||||
} |
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) { |
||||
double *target = va_arg(*ap, double*); |
||||
if(root) |
||||
*target = json_number_value(root); |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
case 'O': |
||||
if(root && !(s->flags & JSON_VALIDATE_ONLY)) |
||||
json_incref(root); |
||||
/* Fall through */ |
||||
|
||||
case 'o': |
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) { |
||||
json_t **target = va_arg(*ap, json_t**); |
||||
if(root) |
||||
*target = root; |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
case 'n': |
||||
/* Never assign, just validate */ |
||||
if(root && !json_is_null(root)) { |
||||
set_error(s, "<validation>", "Expected null, got %s", |
||||
type_name(root)); |
||||
return -1; |
||||
} |
||||
return 0; |
||||
|
||||
default: |
||||
set_error(s, "<format>", "Unexpected format character '%c'", |
||||
token(s)); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
json_t *json_vpack_ex(json_error_t *error, size_t flags, |
||||
const char *fmt, va_list ap) |
||||
{ |
||||
scanner_t s; |
||||
va_list ap_copy; |
||||
json_t *value; |
||||
|
||||
if(!fmt || !*fmt) { |
||||
jsonp_error_init(error, "<format>"); |
||||
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); |
||||
return NULL; |
||||
} |
||||
jsonp_error_init(error, NULL); |
||||
|
||||
scanner_init(&s, error, flags, fmt); |
||||
next_token(&s); |
||||
|
||||
va_copy(ap_copy, ap); |
||||
value = pack(&s, &ap_copy); |
||||
va_end(ap_copy); |
||||
|
||||
if(!value) |
||||
return NULL; |
||||
|
||||
next_token(&s); |
||||
if(token(&s)) { |
||||
json_decref(value); |
||||
set_error(&s, "<format>", "Garbage after format string"); |
||||
return NULL; |
||||
} |
||||
|
||||
return value; |
||||
} |
||||
|
||||
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) |
||||
{ |
||||
json_t *value; |
||||
va_list ap; |
||||
|
||||
va_start(ap, fmt); |
||||
value = json_vpack_ex(error, flags, fmt, ap); |
||||
va_end(ap); |
||||
|
||||
return value; |
||||
} |
||||
|
||||
json_t *json_pack(const char *fmt, ...) |
||||
{ |
||||
json_t *value; |
||||
va_list ap; |
||||
|
||||
va_start(ap, fmt); |
||||
value = json_vpack_ex(NULL, 0, fmt, ap); |
||||
va_end(ap); |
||||
|
||||
return value; |
||||
} |
||||
|
||||
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, |
||||
const char *fmt, va_list ap) |
||||
{ |
||||
scanner_t s; |
||||
va_list ap_copy; |
||||
|
||||
if(!root) { |
||||
jsonp_error_init(error, "<root>"); |
||||
jsonp_error_set(error, -1, -1, 0, "NULL root value"); |
||||
return -1; |
||||
} |
||||
|
||||
if(!fmt || !*fmt) { |
||||
jsonp_error_init(error, "<format>"); |
||||
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); |
||||
return -1; |
||||
} |
||||
jsonp_error_init(error, NULL); |
||||
|
||||
scanner_init(&s, error, flags, fmt); |
||||
next_token(&s); |
||||
|
||||
va_copy(ap_copy, ap); |
||||
if(unpack(&s, root, &ap_copy)) { |
||||
va_end(ap_copy); |
||||
return -1; |
||||
} |
||||
va_end(ap_copy); |
||||
|
||||
next_token(&s); |
||||
if(token(&s)) { |
||||
set_error(&s, "<format>", "Garbage after format string"); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) |
||||
{ |
||||
int ret; |
||||
va_list ap; |
||||
|
||||
va_start(ap, fmt); |
||||
ret = json_vunpack_ex(root, error, flags, fmt, ap); |
||||
va_end(ap); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int json_unpack(json_t *root, const char *fmt, ...) |
||||
{ |
||||
int ret; |
||||
va_list ap; |
||||
|
||||
va_start(ap, fmt); |
||||
ret = json_vunpack_ex(root, NULL, 0, fmt, ap); |
||||
va_end(ap); |
||||
|
||||
return ret; |
||||
} |
@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
|
||||
#include <assert.h> |
||||
#include <errno.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include "jansson_private.h" |
||||
#include "strbuffer.h" |
||||
|
||||
/* need config.h to get the correct snprintf */ |
||||
#ifdef HAVE_CONFIG_H |
||||
#include <config.h> |
||||
#endif |
||||
|
||||
#if JSON_HAVE_LOCALECONV |
||||
#include <locale.h> |
||||
|
||||
/*
|
||||
- This code assumes that the decimal separator is exactly one |
||||
character. |
||||
|
||||
- If setlocale() is called by another thread between the call to |
||||
localeconv() and the call to sprintf() or strtod(), the result may |
||||
be wrong. setlocale() is not thread-safe and should not be used |
||||
this way. Multi-threaded programs should use uselocale() instead. |
||||
*/ |
||||
|
||||
static void to_locale(strbuffer_t *strbuffer) |
||||
{ |
||||
const char *point; |
||||
char *pos; |
||||
|
||||
point = localeconv()->decimal_point; |
||||
if(*point == '.') { |
||||
/* No conversion needed */ |
||||
return; |
||||
} |
||||
|
||||
pos = strchr(strbuffer->value, '.'); |
||||
if(pos) |
||||
*pos = *point; |
||||
} |
||||
|
||||
static void from_locale(char *buffer) |
||||
{ |
||||
const char *point; |
||||
char *pos; |
||||
|
||||
point = localeconv()->decimal_point; |
||||
if(*point == '.') { |
||||
/* No conversion needed */ |
||||
return; |
||||
} |
||||
|
||||
pos = strchr(buffer, *point); |
||||
if(pos) |
||||
*pos = '.'; |
||||
} |
||||
#endif |
||||
|
||||
int jsonp_strtod(strbuffer_t *strbuffer, double *out) |
||||
{ |
||||
double value; |
||||
char *end; |
||||
|
||||
#if JSON_HAVE_LOCALECONV |
||||
to_locale(strbuffer); |
||||
#endif |
||||
|
||||
errno = 0; |
||||
value = strtod(strbuffer->value, &end); |
||||
assert(end == strbuffer->value + strbuffer->length); |
||||
|
||||
if(errno == ERANGE && value != 0) { |
||||
/* Overflow */ |
||||
return -1; |
||||
} |
||||
|
||||
*out = value; |
||||
return 0; |
||||
} |
||||
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value) |
||||
{ |
||||
int ret; |
||||
char *start, *end; |
||||
size_t length; |
||||
|
||||
ret = snprintf(buffer, size, "%.17g", value); |
||||
if(ret < 0) |
||||
return -1; |
||||
|
||||
length = (size_t)ret; |
||||
if(length >= size) |
||||
return -1; |
||||
|
||||
#if JSON_HAVE_LOCALECONV |
||||
from_locale(buffer); |
||||
#endif |
||||
|
||||
/* Make sure there's a dot or 'e' in the output. Otherwise
|
||||
a real is converted to an integer when decoding */ |
||||
if(strchr(buffer, '.') == NULL && |
||||
strchr(buffer, 'e') == NULL) |
||||
{ |
||||
if(length + 3 >= size) { |
||||
/* No space to append ".0" */ |
||||
return -1; |
||||
} |
||||
buffer[length] = '.'; |
||||
buffer[length + 1] = '0'; |
||||
buffer[length + 2] = '\0'; |
||||
length += 2; |
||||
} |
||||
|
||||
/* Remove leading '+' from positive exponent. Also remove leading
|
||||
zeros from exponents (added by some printf() implementations) */ |
||||
start = strchr(buffer, 'e'); |
||||
if(start) { |
||||
start++; |
||||
end = start + 1; |
||||
|
||||
if(*start == '-') |
||||
start++; |
||||
|
||||
while(*end == '0') |
||||
end++; |
||||
|
||||
if(end != start) { |
||||
memmove(start, end, length - (size_t)(end - buffer)); |
||||
length -= (size_t)(end - start); |
||||
} |
||||
} |
||||
|
||||
return (int)length; |
||||
} |
Loading…
Reference in new issue