Commit 51986e15 authored by Alessandro Ghedini's avatar Alessandro Ghedini

Imported Upstream version 2.2.1

parent e8f98fcf
Version 2.2.1
=============
Released 2011-10-06
* Bug fixes:
- Fix real number encoding and decoding under non-C locales. (#32)
- Fix identifier decoding under non-UTF-8 locales. (#35)
- `json_load_file()`: Open the input file in binary mode for maximum
compatiblity.
* Documentation:
- Clarify the lifecycle of the result of the ``s`` fromat of
`json_unpack()`. (#31)
- Add some portability info. (#36)
- Little clarifications here and there.
* Other:
- Some style fixes, issues detected by static analyzers.
Version 2.2
===========
......
......@@ -216,6 +216,7 @@ htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
json_have_localeconv = @json_have_localeconv@
json_have_long_long = @json_have_long_long@
json_inline = @json_inline@
libdir = @libdir@
......
......@@ -6,12 +6,21 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `localeconv' function. */
#undef HAVE_LOCALECONV
/* Define to 1 if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* Define to 1 if the system has the type `long long int'. */
#undef HAVE_LONG_LONG_INT
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
......
This diff is collapsed.
AC_PREREQ([2.60])
AC_INIT([jansson], [2.2], [petri@digip.org])
AC_INIT([jansson], [2.2.1], [petri@digip.org])
AM_INIT_AUTOMAKE([1.10 foreign])
......@@ -14,6 +14,7 @@ AM_CONDITIONAL([GCC], [test x$GCC = xyes])
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([locale.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_INT32_T
......@@ -34,6 +35,12 @@ esac
AC_SUBST([json_inline])
# Checks for library functions.
AC_CHECK_FUNCS([setlocale localeconv])
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])
AC_CONFIG_FILES([
jansson.pc
......
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst tutorial.rst \
upgrading.rst ext/refcounting.py
gettingstarted.rst github_commits.c index.rst portability.rst \
tutorial.rst upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
......
......@@ -137,6 +137,7 @@ htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
json_have_localeconv = @json_have_localeconv@
json_have_long_long = @json_have_long_long@
json_inline = @json_inline@
libdir = @libdir@
......@@ -160,8 +161,8 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst tutorial.rst \
upgrading.rst ext/refcounting.py
gettingstarted.rst github_commits.c index.rst portability.rst \
tutorial.rst upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
......
......@@ -703,9 +703,9 @@ can be ORed together to obtain *flags*.
``JSON_INDENT(n)``
Pretty-print the result, using newlines between array and object
items, and indenting with *n* spaces. The valid range for *n* is
between 0 and 32, other values result in an undefined output. If
``JSON_INDENT`` is not used or *n* is 0, no newlines are inserted
between array and object items.
between 0 and 31 (inclusive), other values result in an undefined
output. If ``JSON_INDENT`` is not used or *n* is 0, no newlines are
inserted between array and object items.
``JSON_COMPACT``
This flag enables a compact representation, i.e. sets the separator
......@@ -859,6 +859,16 @@ The following functions perform the actual JSON decoding.
filled with information about the error. *flags* is described
above.
This function will start reading the input from whatever position
the input file was, without attempting to seek first. If an error
occurs, the file position will be left indeterminate. On success,
the file position will be at EOF, unless ``JSON_DISABLE_EOF_CHECK``
flag was used. In this case, the file position will be at the first
character after the last ``]`` or ``}`` in the JSON input. This
allows calling :func:`json_loadf()` on the same ``FILE`` object
multiple times, if the input consists of consecutive JSON texts,
possibly separated by whitespace.
.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
.. refcounting:: new
......@@ -997,7 +1007,9 @@ type whose address should be passed.
``s`` (string) [const char \*]
Convert a JSON string to a pointer to a NULL terminated UTF-8
string.
string. The resulting string is extracted by using
:func:`json_string_value()` internally, so it exists as long as
there are still references to the corresponding JSON string.
``n`` (null)
Expect a JSON null value. Nothing is extracted.
......@@ -1189,6 +1201,8 @@ copied in a recursive fashion.
Returns a deep copy of *value*, or *NULL* on error.
.. _apiref-custom-memory-allocation:
Custom Memory Allocation
========================
......@@ -1257,5 +1271,5 @@ JSON structures by zeroing all memory when freed::
For more information about the issues of storing sensitive data in
memory, see
http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html.
The page also examplains the :func:`guaranteed_memset()` function used
The page also explains the :func:`guaranteed_memset()` function used
in the example and gives a sample implementation for it.
......@@ -48,9 +48,9 @@ copyright = u'2009-2011, Petri Lehtinen'
# built documents.
#
# The short X.Y version.
version = '2.2'
version = '2.2.1'
# The full version, including alpha/beta/rc tags.
release = '2.2'
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......
......@@ -5,8 +5,7 @@ RFC Conformance
***************
JSON is specified in :rfc:`4627`, *"The application/json Media Type
for JavaScript Object Notation (JSON)"*. This chapter discusses
Jansson's conformance to this specification.
for JavaScript Object Notation (JSON)"*.
Character Encoding
==================
......
......@@ -22,6 +22,11 @@ data. Its main features and design principles are:
Jansson is licensed under the `MIT license`_; see LICENSE in the
source distribution for details.
Jansson is used in production and its API is stable. It works on
numerous platforms, including numerous Unix like systems and Windows.
It's suitable for use on any system, including desktop, server, and
small embedded systems.
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
.. _Jansson: http://www.digip.org/jansson/
......@@ -36,6 +41,7 @@ Contents
upgrading
tutorial
conformance
portability
apiref
changes
......
***********
Portability
***********
Thread safety
-------------
Jansson is thread safe and has no mutable global state. The only
exception are the memory allocation functions, that should be set at
most once, and only on program startup. See
:ref:`apiref-custom-memory-allocation`.
There's no locking performed inside Jansson's code, so a multithreaded
program must perform its own locking if JSON values are shared by
multiple threads. Jansson's reference counting semantics may make this
a bit harder than it seems, as it's possible to have a reference to a
value that's also stored inside a list or object. Modifying the
container (adding or removing values) may trigger concurrent access to
such values, as containers manage the reference count of their
contained values. Bugs involving concurrent incrementing or
decrementing of deference counts may be hard to track.
Locale
------
Jansson works fine under any locale.
However, if the host program is multithreaded and uses ``setlocale()``
to switch the locale in one thread while Jansson is currently encoding
or decoding JSON data in another thread, the result may be wrong or
the program may even crash.
Jansson uses locale specific functions for certain string conversions
in the encoder and decoder, and then converts the locale specific
values to/from the JSON representation. This fails if the locale
changes between the string conversion and the locale-to-JSON
conversion. This can only happen in multithreaded programs that use
``setlocale()``, because ``setlocale()`` switches the locale for all
running threads, not only the thread that calls ``setlocale()``.
If your program uses ``setlocale()`` as described above, consider
using the thread-safe ``uselocale()`` instead.
......@@ -12,12 +12,13 @@ libjansson_la_SOURCES = \
pack_unpack.c \
strbuffer.c \
strbuffer.h \
strconv.c \
utf.c \
utf.h \
value.c
libjansson_la_LDFLAGS = \
-export-symbols-regex '^json_' \
-version-info 6:0:2
-version-info 6:1:2
if GCC
# These flags are gcc specific
......
......@@ -71,7 +71,8 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libjansson_la_LIBADD =
am_libjansson_la_OBJECTS = dump.lo error.lo hashtable.lo load.lo \
memory.lo pack_unpack.lo strbuffer.lo utf.lo value.lo
memory.lo pack_unpack.lo strbuffer.lo strconv.lo utf.lo \
value.lo
libjansson_la_OBJECTS = $(am_libjansson_la_OBJECTS)
libjansson_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
......@@ -186,6 +187,7 @@ htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
json_have_localeconv = @json_have_localeconv@
json_have_long_long = @json_have_long_long@
json_inline = @json_inline@
libdir = @libdir@
......@@ -221,13 +223,14 @@ libjansson_la_SOURCES = \
pack_unpack.c \
strbuffer.c \
strbuffer.h \
strconv.c \
utf.c \
utf.h \
value.c
libjansson_la_LDFLAGS = \
-export-symbols-regex '^json_' \
-version-info 6:0:2
-version-info 6:1:2
# These flags are gcc specific
......@@ -315,6 +318,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pack_unpack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strbuffer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strconv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/value.Plo@am__quote@
......
......@@ -196,26 +196,12 @@ static int do_dump(const json_t *json, size_t flags, int depth,
{
char buffer[MAX_REAL_STR_LENGTH];
int size;
double value = json_real_value(json);
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
json_real_value(json));
if(size >= MAX_REAL_STR_LENGTH)
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
if(size < 0)
return -1;
/* 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(size + 2 >= MAX_REAL_STR_LENGTH) {
/* No space to append ".0" */
return -1;
}
buffer[size] = '.';
buffer[size + 1] = '0';
size += 2;
}
return dump(buffer, size, data);
}
......@@ -413,39 +399,26 @@ static int do_dump(const json_t *json, size_t flags, int depth,
}
}
char *json_dumps(const json_t *json, size_t flags)
{
strbuffer_t strbuff;
char *result;
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return NULL;
}
if(strbuffer_init(&strbuff))
return NULL;
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
strbuffer_close(&strbuff);
return NULL;
}
if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
result = NULL;
else
result = jsonp_strdup(strbuffer_value(&strbuff));
result = jsonp_strdup(strbuffer_value(&strbuff));
strbuffer_close(&strbuff);
return result;
}
int json_dumpf(const json_t *json, FILE *output, size_t flags)
{
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, dump_to_file, (void *)output);
return json_dump_callback(json, dump_to_file, (void *)output, flags);
}
int json_dump_file(const json_t *json, const char *path, size_t flags)
......
......@@ -62,7 +62,6 @@ static size_t primes[] = {
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
805306457, 1610612741
};
static const size_t num_primes = sizeof(primes) / sizeof(size_t);
static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
{
......
......@@ -22,10 +22,10 @@ extern "C" {
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 2
#define JANSSON_MICRO_VERSION 0
#define JANSSON_MICRO_VERSION 1
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.2"
#define JANSSON_VERSION "2.2.1"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
......
......@@ -31,4 +31,8 @@
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
......@@ -31,4 +31,8 @@
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
#endif
......@@ -11,6 +11,7 @@
#include <stddef.h>
#include "jansson.h"
#include "hashtable.h"
#include "strbuffer.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
......@@ -83,6 +84,10 @@ void jsonp_error_set(json_error_t *error, int line, int column,
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap);
/* Locale independent string<->double conversions */
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
int jsonp_dtostr(char *buffer, size_t size, double value);
/* Wrappers for custom memory functions */
void* jsonp_malloc(size_t size);
void jsonp_free(void *ptr);
......
......@@ -6,7 +6,6 @@
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
......@@ -32,6 +31,14 @@
#define TOKEN_FALSE 260
#define TOKEN_NULL 261
/* Locale independent versions of isxxx() functions */
#define l_isupper(c) ('A' <= c && c <= 'Z')
#define l_islower(c) ('a' <= c && c <= 'z')
#define l_isalpha(c) (l_isupper(c) || l_islower(c))
#define l_isdigit(c) ('0' <= c && c <= '9')
#define l_isxdigit(c) \
(l_isdigit(c) || 'A' <= c || c <= 'F' || 'a' <= c || c <= 'f')
/* Read one byte from stream, convert to unsigned char, then int, and
return. return EOF on end of file. This corresponds to the
behaviour of fgetc(). */
......@@ -69,6 +76,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
{
va_list ap;
char msg_text[JSON_ERROR_TEXT_LENGTH];
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
int line = -1, col = -1;
size_t pos = 0;
......@@ -84,7 +92,6 @@ static void error_set(json_error_t *error, const lex_t *lex,
if(lex)
{
const char *saved_text = strbuffer_value(&lex->saved_text);
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
line = lex->stream.line;
col = lex->stream.column;
......@@ -268,11 +275,11 @@ static int32_t decode_unicode_escape(const char *str)
for(i = 1; i <= 4; i++) {
char c = str[i];
value <<= 4;
if(isdigit(c))
if(l_isdigit(c))
value += c - '0';
else if(islower(c))
else if(l_islower(c))
value += c - 'a' + 10;
else if(isupper(c))
else if(l_isupper(c))
value += c - 'A' + 10;
else
assert(0);
......@@ -317,7 +324,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
if(c == 'u') {
c = lex_get_save(lex, error);
for(i = 0; i < 4; i++) {
if(!isxdigit(c)) {
if(!l_isxdigit(c)) {
error_set(error, lex, "invalid escape");
goto out;
}
......@@ -455,14 +462,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '0') {
c = lex_get_save(lex, error);
if(isdigit(c)) {
if(l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
}
else if(isdigit(c)) {
else if(l_isdigit(c)) {
c = lex_get_save(lex, error);
while(isdigit(c))
while(l_isdigit(c))
c = lex_get_save(lex, error);
}
else {
......@@ -496,14 +503,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '.') {
c = lex_get(lex, error);
if(!isdigit(c)) {
if(!l_isdigit(c)) {
lex_unget(lex, c);
goto out;
}
lex_save(lex, c);
c = lex_get_save(lex, error);
while(isdigit(c))
while(l_isdigit(c))
c = lex_get_save(lex, error);
}
......@@ -512,24 +519,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '+' || c == '-')
c = lex_get_save(lex, error);
if(!isdigit(c)) {
if(!l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
c = lex_get_save(lex, error);
while(isdigit(c))
while(l_isdigit(c))
c = lex_get_save(lex, error);
}
lex_unget_unsave(lex, c);
saved_text = strbuffer_value(&lex->saved_text);
errno = 0;
value = strtod(saved_text, &end);
assert(end == saved_text + lex->saved_text.length);
if(errno == ERANGE && value != 0) {
if(jsonp_strtod(&lex->saved_text, &value)) {
error_set(error, lex, "real number overflow");
goto out;
}
......@@ -575,17 +577,17 @@ static int lex_scan(lex_t *lex, json_error_t *error)
else if(c == '"')
lex_scan_string(lex, error);
else if(isdigit(c) || c == '-') {
else if(l_isdigit(c) || c == '-') {
if(lex_scan_number(lex, c, error))
goto out;
}
else if(isupper(c) || islower(c)) {
else if(l_isalpha(c)) {
/* eat up the whole identifier for clearer error messages */
const char *saved_text;
c = lex_get_save(lex, error);
while(isupper(c) || islower(c))
while(l_isalpha(c))
c = lex_get_save(lex, error);
lex_unget_unsave(lex, c);
......@@ -945,7 +947,7 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
jsonp_error_init(error, path);
fp = fopen(path, "r");
fp = fopen(path, "rb");
if(!fp)
{
error_set(error, NULL, "unable to open %s: %s",
......
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "jansson_private.h"
#include "strbuffer.h"
#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;
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 + 2 >= size) {
/* No space to append ".0" */
return -1;
}
buffer[length] = '.';
buffer[length + 1] = '0';
length += 2;
}
return (int)length;
}
......@@ -179,6 +179,7 @@ htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
json_have_localeconv = @json_have_localeconv@
json_have_long_long = @json_have_long_long@
json_inline = @json_inline@
libdir = @libdir@
......
......@@ -157,6 +157,7 @@ htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
json_have_localeconv = @json_have_localeconv@
json_have_long_long = @json_have_long_long@
json_inline = @json_inline@
libdir = @libdir@
......
......@@ -5,12 +5,20 @@
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jansson.h>
#if HAVE_LOCALE_H
#include <locale.h>
#endif
static int getenv_int(const char *name)
{
char *value, *end;
......@@ -55,6 +63,10 @@ int main(int argc, char *argv[])
json_t *json;
json_error_t error;
#if HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
if(argc != 1) {
fprintf(