Tanguy Pruvot
10 years ago
22 changed files with 2274 additions and 755 deletions
@ -0,0 +1,4 @@ |
|||||||
|
*.h.in |
||||||
|
*.h.in~ |
||||||
|
libtool |
||||||
|
libjansson.a |
@ -1,18 +1,20 @@ |
|||||||
|
|
||||||
noinst_LIBRARIES = libjansson.a |
noinst_LIBRARIES = libjansson.a |
||||||
|
|
||||||
libjansson_a_SOURCES = \
|
libjansson_a_SOURCES = \
|
||||||
config.h \
|
config.h \
|
||||||
dump.c \
|
dump.c \
|
||||||
hashtable.c \
|
error.c \
|
||||||
hashtable.h \
|
hashtable.c hashtable.h \
|
||||||
jansson.h \
|
jansson.h \
|
||||||
jansson_private.h \
|
jansson_config.h \
|
||||||
load.c \
|
jansson_private.h \
|
||||||
strbuffer.c \
|
load.c \
|
||||||
strbuffer.h \
|
memory.c \
|
||||||
utf.c \
|
pack_unpack.c \
|
||||||
utf.h \
|
strbuffer.c strbuffer.h \
|
||||||
util.h \
|
strconv.c \
|
||||||
value.c |
utf.c utf.h \
|
||||||
|
util.h \
|
||||||
|
value.c |
||||||
|
|
||||||
|
@ -1,73 +1,140 @@ |
|||||||
/* config.h. Generated from config.h.in by configure. */ |
/* config.h. Generated from config.h.in by configure. */ |
||||||
/* config.h.in. Generated from configure.ac by autoheader. */ |
/* config.h.in. Generated from configure.ac by autoheader. */ |
||||||
|
|
||||||
/* Define to 1 if you have the <dlfcn.h> header file. */ |
/* Define to 1 if gcc's __atomic builtins are available */ |
||||||
#define HAVE_DLFCN_H 1 |
/* #undef HAVE_ATOMIC_BUILTINS */ |
||||||
|
|
||||||
/* Define to 1 if you have the <inttypes.h> header file. */ |
/* Define to 1 if you have the `close' function. */ |
||||||
#define HAVE_INTTYPES_H 1 |
#define HAVE_CLOSE 1 |
||||||
|
|
||||||
/* Define to 1 if you have the <memory.h> header file. */ |
/* Define to 1 if you have the <dlfcn.h> header file. */ |
||||||
#define HAVE_MEMORY_H 1 |
/* #undef HAVE_DLFCN_H */ |
||||||
|
|
||||||
/* Define to 1 if you have the <stdint.h> header file. */ |
/* Define to 1 if you have the <endian.h> header file. */ |
||||||
#define HAVE_STDINT_H 1 |
/* #undef HAVE_ENDIAN_H */ |
||||||
|
|
||||||
/* Define to 1 if you have the <stdlib.h> header file. */ |
/* Define to 1 if you have the <fcntl.h> header file. */ |
||||||
#define HAVE_STDLIB_H 1 |
#define HAVE_FCNTL_H 1 |
||||||
|
|
||||||
/* Define to 1 if you have the <strings.h> header file. */ |
/* Define to 1 if you have the `getpid' function. */ |
||||||
#define HAVE_STRINGS_H 1 |
#define HAVE_GETPID 1 |
||||||
|
|
||||||
/* Define to 1 if you have the <string.h> header file. */ |
/* Define to 1 if you have the `gettimeofday' function. */ |
||||||
#define HAVE_STRING_H 1 |
#define HAVE_GETTIMEOFDAY 1 |
||||||
|
|
||||||
/* Define to 1 if you have the <sys/stat.h> header file. */ |
/* Define to 1 if you have the <inttypes.h> header file. */ |
||||||
#define HAVE_SYS_STAT_H 1 |
#define HAVE_INTTYPES_H 1 |
||||||
|
|
||||||
/* Define to 1 if you have the <sys/types.h> header file. */ |
/* Define to 1 if you have the `localeconv' function. */ |
||||||
#define HAVE_SYS_TYPES_H 1 |
#define HAVE_LOCALECONV 1 |
||||||
|
|
||||||
/* Define to 1 if you have the <unistd.h> header file. */ |
/* Define to 1 if you have the <locale.h> header file. */ |
||||||
#define HAVE_UNISTD_H 1 |
#define HAVE_LOCALE_H 1 |
||||||
|
|
||||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
/* Define to 1 if the system has the type `long long int'. */ |
||||||
*/ |
#define HAVE_LONG_LONG_INT 1 |
||||||
#define LT_OBJDIR ".libs/" |
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */ |
||||||
/* Name of package */ |
#define HAVE_MEMORY_H 1 |
||||||
#define PACKAGE "jansson" |
|
||||||
|
/* Define to 1 if you have the `open' function. */ |
||||||
/* Define to the address where bug reports for this package should be sent. */ |
#define HAVE_OPEN 1 |
||||||
#define PACKAGE_BUGREPORT "petri@digip.org" |
|
||||||
|
/* Define to 1 if you have the `read' function. */ |
||||||
/* Define to the full name of this package. */ |
#define HAVE_READ 1 |
||||||
#define PACKAGE_NAME "jansson" |
|
||||||
|
/* Define to 1 if you have the <sched.h> header file. */ |
||||||
/* Define to the full name and version of this package. */ |
#define HAVE_SCHED_H 1 |
||||||
#define PACKAGE_STRING "jansson 1.3" |
|
||||||
|
/* Define to 1 if you have the `sched_yield' function. */ |
||||||
/* Define to the one symbol short name of this package. */ |
/* #undef HAVE_SCHED_YIELD */ |
||||||
#define PACKAGE_TARNAME "jansson" |
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */ |
||||||
/* Define to the home page for this package. */ |
#define HAVE_STDINT_H 1 |
||||||
#define PACKAGE_URL "" |
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */ |
||||||
/* Define to the version of this package. */ |
#define HAVE_STDLIB_H 1 |
||||||
#define PACKAGE_VERSION "1.3" |
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */ |
||||||
/* Define to 1 if you have the ANSI C header files. */ |
#define HAVE_STRINGS_H 1 |
||||||
#define STDC_HEADERS 1 |
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */ |
||||||
/* Version number of package */ |
#define HAVE_STRING_H 1 |
||||||
#define VERSION "1.3" |
|
||||||
|
/* Define to 1 if you have the `strtoll' function. */ |
||||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
#define HAVE_STRTOLL 1 |
||||||
calls it, or to nothing if 'inline' is not supported under any name. */ |
|
||||||
#ifndef __cplusplus |
/* Define to 1 if gcc's __sync builtins are available */ |
||||||
/* #undef inline */ |
#define HAVE_SYNC_BUILTINS 1 |
||||||
#endif |
|
||||||
|
/* Define to 1 if you have the <sys/param.h> header file. */ |
||||||
/* Define to the type of a signed integer type of width exactly 32 bits if
|
#define HAVE_SYS_PARAM_H 1 |
||||||
such a type exists and the standard includes do not define it. */ |
|
||||||
/* #undef int32_t */ |
/* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
#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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
#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