Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c6cb42f17 | ||
|
|
ed06777937 | ||
|
|
6362032513 | ||
|
|
84f739036d | ||
|
|
0f358c8eaa | ||
|
|
f0d5c04734 | ||
|
|
fd56deb7dd | ||
|
|
d7ddbf3661 | ||
|
|
b6d0191e51 | ||
|
|
2a70d62251 | ||
|
|
909874b1b9 | ||
|
|
ff57dee13d | ||
|
|
e4cc77ce52 | ||
|
|
889f295958 | ||
|
|
68809cd913 | ||
|
|
910fb92267 | ||
|
|
f241e14cab | ||
|
|
7e9c293986 | ||
|
|
d43464a1ec | ||
|
|
c7079a25eb | ||
|
|
54f38d250c | ||
|
|
c7d543d36c | ||
|
|
7fab57dcef | ||
|
|
cd9757512d | ||
|
|
c0193bfb7f | ||
|
|
6e1f4bb560 | ||
|
|
1358d0bfac | ||
|
|
86d17a8dc2 | ||
|
|
3988ab2d27 | ||
|
|
f7f7bf5ab2 | ||
|
|
a76ba52f34 | ||
|
|
9febdf333c | ||
|
|
013b8b3f60 | ||
|
|
49fc708d4c | ||
|
|
92f6f0f22c | ||
|
|
e9e34f430e | ||
|
|
ab723c7fb5 | ||
|
|
636d5f60f9 | ||
|
|
c3492973e1 | ||
|
|
e20619e071 | ||
|
|
b44e2be032 | ||
|
|
76d6d700ad | ||
|
|
c96763215d | ||
|
|
4a76900bd7 | ||
|
|
056702e541 | ||
|
|
a5a43caa9a | ||
|
|
5456fc59ab | ||
|
|
af18578928 |
85
CHANGES
85
CHANGES
@@ -1,3 +1,86 @@
|
||||
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
|
||||
===========
|
||||
|
||||
Released 2011-09-03
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_dump_callback()`: Pass the encoder output to a callback
|
||||
function in chunks.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- `json_string_set()`: Check that target is a string and value is
|
||||
not NULL.
|
||||
|
||||
* Other:
|
||||
|
||||
- Documentation typo fixes and clarifications.
|
||||
|
||||
|
||||
Version 2.1
|
||||
===========
|
||||
|
||||
Released 2011-06-10
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_loadb()`: Decode a string with a given size, useful if the
|
||||
string is not null terminated.
|
||||
|
||||
- Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
|
||||
value. By default, only arrays and objects can be encoded. (#19)
|
||||
|
||||
- Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
|
||||
error if any JSON object in the input contins duplicate keys. (#3)
|
||||
|
||||
- Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
|
||||
valid JSON input. This allows other data after the JSON data.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix an additional memory leak when memory allocation fails in
|
||||
`json_object_set()` and friends.
|
||||
|
||||
- Clear errno before calling `strtod()` for better portability. (#27)
|
||||
|
||||
* Building:
|
||||
|
||||
- Avoid set-but-not-used warning/error in a test. (#20)
|
||||
|
||||
* Other:
|
||||
|
||||
- Minor clarifications to documentation.
|
||||
|
||||
|
||||
Version 2.0.1
|
||||
=============
|
||||
|
||||
@@ -10,7 +93,7 @@ Released 2011-03-31
|
||||
|
||||
- Fix object key hashing in json_unpack() strict checking mode.
|
||||
|
||||
- Fix the parentheses in JANSSON_VERSION_HEX macro.
|
||||
- Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
|
||||
|
||||
- Fix `json_object_size()` return value.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [2.0.1], [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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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)
|
||||
|
||||
155
doc/apiref.rst
155
doc/apiref.rst
@@ -481,12 +481,13 @@ A JSON array is an ordered collection of other JSON values.
|
||||
|
||||
Removes the element in *array* at position *index*, shifting the
|
||||
elements after *index* one position towards the start of the array.
|
||||
Returns 0 on success and -1 on error.
|
||||
Returns 0 on success and -1 on error. The reference count of the
|
||||
removed value is decremented.
|
||||
|
||||
.. function:: int json_array_clear(json_t *array)
|
||||
|
||||
Removes all elements from *array*. Returns 0 on sucess and -1 on
|
||||
error.
|
||||
error. The reference count of all removed values are decremented.
|
||||
|
||||
.. function:: int json_array_extend(json_t *array, json_t *other_array)
|
||||
|
||||
@@ -549,13 +550,14 @@ Unicode string and the value is any JSON value.
|
||||
.. function:: int json_object_del(json_t *object, const char *key)
|
||||
|
||||
Delete *key* from *object* if it exists. Returns 0 on success, or
|
||||
-1 if *key* was not found.
|
||||
|
||||
-1 if *key* was not found. The reference count of the removed value
|
||||
is decremented.
|
||||
|
||||
.. function:: int json_object_clear(json_t *object)
|
||||
|
||||
Remove all elements from *object*. Returns 0 on success and -1 if
|
||||
*object* is not a JSON object.
|
||||
*object* is not a JSON object. The reference count of all removed
|
||||
values are decremented.
|
||||
|
||||
.. function:: int json_object_update(json_t *object, json_t *other)
|
||||
|
||||
@@ -563,7 +565,10 @@ Unicode string and the value is any JSON value.
|
||||
existing keys. Returns 0 on success or -1 on error.
|
||||
|
||||
|
||||
The following functions implement an iteration protocol for objects:
|
||||
The following functions implement an iteration protocol for objects,
|
||||
allowing to iterate through all key-value pairs in an object. The
|
||||
items are not returned in any particular order, as this would require
|
||||
sorting due to the internal hashtable implementation.
|
||||
|
||||
.. function:: void *json_object_iter(json_t *object)
|
||||
|
||||
@@ -680,8 +685,10 @@ Encoding
|
||||
========
|
||||
|
||||
This sections describes the functions that can be used to encode
|
||||
values to JSON. Only objects and arrays can be encoded, since they are
|
||||
the only valid "root" values of a JSON text.
|
||||
values to JSON. By default, only objects and arrays can be encoded
|
||||
directly, since they are the only valid *root* values of a JSON text.
|
||||
To encode any JSON value, use the ``JSON_ENCODE_ANY`` flag (see
|
||||
below).
|
||||
|
||||
By default, the output has no newlines, and spaces are used between
|
||||
array and object elements for a readable output. This behavior can be
|
||||
@@ -696,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
|
||||
@@ -722,6 +729,19 @@ can be ORed together to obtain *flags*.
|
||||
example, decoding a JSON text and then encoding with this flag
|
||||
preserves the order of object keys.
|
||||
|
||||
``JSON_ENCODE_ANY``
|
||||
Specifying this flag makes it possible to encode any JSON value on
|
||||
its own. Without it, only objects and arrays can be passed as the
|
||||
*root* value to the encoding functions.
|
||||
|
||||
**Note:** Encoding any value may be useful in some scenarios, but
|
||||
it's generally discouraged as it violates strict compatiblity with
|
||||
:rfc:`4627`. If you use this flag, don't expect interoperatibility
|
||||
with other JSON systems. Even Jansson itself doesn't have any means
|
||||
to decode JSON texts whose root value is not object or array.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
The following functions perform the actual JSON encoding. The result
|
||||
is in UTF-8.
|
||||
|
||||
@@ -745,6 +765,30 @@ is in UTF-8.
|
||||
*path* already exists, it is overwritten. *flags* is described
|
||||
above. Returns 0 on success and -1 on error.
|
||||
|
||||
.. type:: json_dump_callback_t
|
||||
|
||||
A typedef for a function that's called by
|
||||
:func:`json_dump_callback()`::
|
||||
|
||||
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||
|
||||
*buffer* points to a buffer containing a chunk of output, *size* is
|
||||
the length of the buffer, and *data* is the corresponding
|
||||
:func:`json_dump_callback()` argument passed through.
|
||||
|
||||
On error, the function should return -1 to stop the encoding
|
||||
process. On success, it should return 0.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
.. function:: int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
|
||||
|
||||
Call *callback* repeatedly, passing a chunk of the JSON
|
||||
representation of *root* each time. *flags* is described above.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
|
||||
.. _apiref-decoding:
|
||||
|
||||
@@ -762,14 +806,49 @@ See :ref:`rfc-conformance` for a discussion on Jansson's conformance
|
||||
to the JSON specification. It explains many design decisions that
|
||||
affect especially the behavior of the decoder.
|
||||
|
||||
Each function takes a *flags* parameter that can be used to control
|
||||
the behavior of the decoder. Its default value is 0. The following
|
||||
macros can be ORed together to obtain *flags*.
|
||||
|
||||
``JSON_REJECT_DUPLICATES``
|
||||
Issue a decoding error if any JSON object in the input text
|
||||
contains duplicate keys. Without this flag, the value of the last
|
||||
occurence of each key ends up in the result. Key equivalence is
|
||||
checked byte-by-byte, without special Unicode comparison
|
||||
algorithms.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
``JSON_DISABLE_EOF_CHECK``
|
||||
By default, the decoder expects that its whole input constitutes a
|
||||
valid JSON text, and issues an error if there's extra data after
|
||||
the otherwise valid JSON input. With this flag enabled, the decoder
|
||||
stops after decoding a valid JSON array or object, and thus allows
|
||||
extra data after the JSON text.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
The following functions perform the actual JSON decoding.
|
||||
|
||||
.. function:: json_t *json_loads(const char *input, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON string *input* and returns the array or object it
|
||||
contains, or *NULL* on error, in which case *error* is filled with
|
||||
information about the error. *flags* is currently unused, and
|
||||
should be set to 0.
|
||||
information about the error. *flags* is described above.
|
||||
|
||||
.. function:: json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON string *buffer*, whose length is *buflen*, and
|
||||
returns the array or object it contains, or *NULL* on error, in
|
||||
which case *error* is filled with information about the error. This
|
||||
is similar to :func:`json_loads()` except that the string doesn't
|
||||
need to be null-terminated. *flags* is described above.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. function:: json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
|
||||
@@ -777,8 +856,18 @@ affect especially the behavior of the decoder.
|
||||
|
||||
Decodes the JSON text in stream *input* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
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)
|
||||
|
||||
@@ -786,8 +875,8 @@ affect especially the behavior of the decoder.
|
||||
|
||||
Decodes the JSON text in file *path* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
filled with information about the error. *flags* is described
|
||||
above.
|
||||
|
||||
|
||||
.. _apiref-pack:
|
||||
@@ -795,14 +884,14 @@ affect especially the behavior of the decoder.
|
||||
Building Values
|
||||
===============
|
||||
|
||||
This sectinon describes functions that help to create, or *pack*,
|
||||
This section describes functions that help to create, or *pack*,
|
||||
complex JSON values, especially nested objects and arrays. Value
|
||||
building is based on a *format string* that is used to tell the
|
||||
functions about the expected arguments.
|
||||
|
||||
For example, the format string ``"i"`` specifies a single integer
|
||||
value, while the format string ``"[ssb]"`` or the equivalent ``"[s, s,
|
||||
b]"`` specifies an array value with two integers and a boolean as its
|
||||
b]"`` specifies an array value with two strings and a boolean as its
|
||||
items::
|
||||
|
||||
/* Create the JSON integer 42 */
|
||||
@@ -837,7 +926,7 @@ denotes the C type that is expected as the corresponding argument.
|
||||
``o`` (any value) [json_t \*]
|
||||
Output any given JSON value as-is. If the value is added to an
|
||||
array or object, the reference to the value passed to ``o`` is
|
||||
stealed by the container.
|
||||
stolen by the container.
|
||||
|
||||
``O`` (any value) [json_t \*]
|
||||
Like ``o``, but the argument's reference count is incremented.
|
||||
@@ -887,10 +976,10 @@ More examples::
|
||||
json_pack("{}");
|
||||
|
||||
/* Build the JSON object {"foo": 42, "bar": 7} */
|
||||
json_pack("{sisb}", "foo", 42, "bar", 7);
|
||||
json_pack("{sisi}", "foo", 42, "bar", 7);
|
||||
|
||||
/* Like above, ':', ',' and whitespace are ignored */
|
||||
json_pack("{s:i, s:b}", "foo", 42, "bar", 7);
|
||||
json_pack("{s:i, s:i}", "foo", 42, "bar", 7);
|
||||
|
||||
/* Build the JSON array [[1, 2], {"cool": true}] */
|
||||
json_pack("[[i,i],{s:b]]", 1, 2, "cool", 1);
|
||||
@@ -918,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.
|
||||
@@ -992,6 +1083,20 @@ The following functions compose the parsing and validation API:
|
||||
behaviour of the unpacker, see below for the flags. Returns 0 on
|
||||
success and -1 on failure.
|
||||
|
||||
.. note::
|
||||
|
||||
The first argument of all unpack functions is ``json_t *root``
|
||||
instead of ``const json_t *root``, because the use of ``O`` format
|
||||
character causes the reference count of ``root``, or some value
|
||||
reachable from ``root``, to be increased. Furthermore, the ``o``
|
||||
format character may be used to extract a value as-is, which allows
|
||||
modifying the structure or contents of a value reachable from
|
||||
``root``.
|
||||
|
||||
If the ``O`` and ``o`` format character are not used, it's
|
||||
perfectly safe to cast a ``const json_t *`` variable to plain
|
||||
``json_t *`` when used with these functions.
|
||||
|
||||
The following unpacking flags are available:
|
||||
|
||||
``JSON_STRICT``
|
||||
@@ -1096,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
|
||||
========================
|
||||
|
||||
@@ -1164,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.0'
|
||||
version = '2.2.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.0.1'
|
||||
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
|
||||
==================
|
||||
@@ -30,6 +29,12 @@ error::
|
||||
|
||||
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
|
||||
|
||||
Unicode normalization or any other transformation is never performed
|
||||
on any strings (string values or object keys). When checking for
|
||||
equivalence of strings or object keys, the comparison is performed
|
||||
byte by byte between the original UTF-8 representations of the
|
||||
strings.
|
||||
|
||||
Numbers
|
||||
=======
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
42
doc/portability.rst
Normal file
42
doc/portability.rst
Normal file
@@ -0,0 +1,42 @@
|
||||
***********
|
||||
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 4:1:0
|
||||
-version-info 6:1:2
|
||||
|
||||
if GCC
|
||||
# These flags are gcc specific
|
||||
|
||||
61
src/dump.c
61
src/dump.c
@@ -19,8 +19,6 @@
|
||||
#define MAX_INTEGER_STR_LENGTH 100
|
||||
#define MAX_REAL_STR_LENGTH 100
|
||||
|
||||
typedef int (*dump_func)(const char *buffer, int size, void *data);
|
||||
|
||||
struct string
|
||||
{
|
||||
char *buffer;
|
||||
@@ -28,12 +26,12 @@ struct string
|
||||
int size;
|
||||
};
|
||||
|
||||
static int dump_to_strbuffer(const char *buffer, int size, void *data)
|
||||
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
|
||||
}
|
||||
|
||||
static int dump_to_file(const char *buffer, int size, void *data)
|
||||
static int dump_to_file(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
FILE *dest = (FILE *)data;
|
||||
if(fwrite(buffer, size, 1, dest) != 1)
|
||||
@@ -44,7 +42,7 @@ static int dump_to_file(const char *buffer, int size, void *data)
|
||||
/* 32 spaces (the maximum indentation size) */
|
||||
static char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
|
||||
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
{
|
||||
@@ -66,7 +64,7 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
static int dump_string(const char *str, int ascii, json_dump_callback_t dump, void *data)
|
||||
{
|
||||
const char *pos, *end;
|
||||
int32_t codepoint;
|
||||
@@ -166,7 +164,7 @@ static int object_key_compare_serials(const void *key1, const void *key2)
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
dump_func dump, void *data)
|
||||
json_dump_callback_t dump, void *data)
|
||||
{
|
||||
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
|
||||
|
||||
@@ -198,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);
|
||||
}
|
||||
|
||||
@@ -415,35 +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(!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(!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)
|
||||
@@ -459,3 +434,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
fclose(output);
|
||||
return result;
|
||||
}
|
||||
|
||||
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, 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, callback, data);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -21,11 +21,11 @@ extern "C" {
|
||||
/* version */
|
||||
|
||||
#define JANSSON_MAJOR_VERSION 2
|
||||
#define JANSSON_MINOR_VERSION 0
|
||||
#define JANSSON_MINOR_VERSION 2
|
||||
#define JANSSON_MICRO_VERSION 1
|
||||
|
||||
/* Micro version is omitted if it's 0 */
|
||||
#define JANSSON_VERSION "2.0.1"
|
||||
#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 >= ... */
|
||||
@@ -214,22 +214,32 @@ json_t *json_copy(json_t *value);
|
||||
json_t *json_deep_copy(json_t *value);
|
||||
|
||||
|
||||
/* loading, printing */
|
||||
/* decoding */
|
||||
|
||||
#define JSON_REJECT_DUPLICATES 0x1
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
|
||||
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
|
||||
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
|
||||
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||
|
||||
char *json_dumps(const json_t *json, size_t flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags);
|
||||
int json_dump_file(const json_t *json, const char *path, size_t flags);
|
||||
|
||||
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
|
||||
|
||||
/* custom memory allocation */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
154
src/load.c
154
src/load.c
@@ -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,23 +519,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
if(c == '+' || c == '-')
|
||||
c = lex_get_save(lex, error);
|
||||
|
||||
if(!isdigit(c)) {
|
||||
if(!l_isdigit(c)) {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
while(isdigit(c))
|
||||
while(l_isdigit(c))
|
||||
c = lex_get_save(lex, error);
|
||||
}
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
value = strtod(saved_text, &end);
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
if(errno == ERANGE && value != 0) {
|
||||
if(jsonp_strtod(&lex->saved_text, &value)) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
goto out;
|
||||
}
|
||||
@@ -574,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);
|
||||
|
||||
@@ -642,9 +645,9 @@ static void lex_close(lex_t *lex)
|
||||
|
||||
/*** parser ***/
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error);
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
|
||||
|
||||
static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *object = json_object();
|
||||
if(!object)
|
||||
@@ -667,6 +670,14 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
if(!key)
|
||||
return NULL;
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "duplicate object key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ':') {
|
||||
jsonp_free(key);
|
||||
@@ -675,7 +686,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
value = parse_value(lex, error);
|
||||
value = parse_value(lex, flags, error);
|
||||
if(!value) {
|
||||
jsonp_free(key);
|
||||
goto error;
|
||||
@@ -709,7 +720,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *array = json_array();
|
||||
if(!array)
|
||||
@@ -720,7 +731,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
return array;
|
||||
|
||||
while(lex->token) {
|
||||
json_t *elem = parse_value(lex, error);
|
||||
json_t *elem = parse_value(lex, flags, error);
|
||||
if(!elem)
|
||||
goto error;
|
||||
|
||||
@@ -749,7 +760,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *json;
|
||||
|
||||
@@ -782,11 +793,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
break;
|
||||
|
||||
case '{':
|
||||
json = parse_object(lex, error);
|
||||
json = parse_object(lex, flags, error);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
json = parse_array(lex, error);
|
||||
json = parse_array(lex, flags, error);
|
||||
break;
|
||||
|
||||
case TOKEN_INVALID:
|
||||
@@ -804,15 +815,30 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
return json;
|
||||
}
|
||||
|
||||
static json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *result;
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
error_set(error, lex, "'[' or '{' expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parse_value(lex, error);
|
||||
result = parse_value(lex, flags, error);
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
if(!(flags & JSON_DISABLE_EOF_CHECK)) {
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != TOKEN_EOF) {
|
||||
error_set(error, lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@@ -841,8 +867,6 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
json_t *result;
|
||||
string_data_t stream_data;
|
||||
|
||||
(void)flags; /* unused */
|
||||
|
||||
stream_data.data = string;
|
||||
stream_data.pos = 0;
|
||||
|
||||
@@ -850,19 +874,47 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
return NULL;
|
||||
|
||||
jsonp_error_init(error, "<string>");
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
typedef struct
|
||||
{
|
||||
const char *data;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
} buffer_data_t;
|
||||
|
||||
static int buffer_get(void *data)
|
||||
{
|
||||
char c;
|
||||
buffer_data_t *stream = data;
|
||||
if(stream->pos >= stream->len)
|
||||
return EOF;
|
||||
|
||||
c = stream->data[stream->pos];
|
||||
stream->pos++;
|
||||
return (unsigned char)c;
|
||||
}
|
||||
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
json_t *result;
|
||||
buffer_data_t stream_data;
|
||||
|
||||
stream_data.data = buffer;
|
||||
stream_data.pos = 0;
|
||||
stream_data.len = buflen;
|
||||
|
||||
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
jsonp_error_init(error, "<buffer>");
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
out:
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
@@ -872,7 +924,6 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
lex_t lex;
|
||||
const char *source;
|
||||
json_t *result;
|
||||
(void)flags; /* unused */
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
return NULL;
|
||||
@@ -883,19 +934,8 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
source = "<stream>";
|
||||
|
||||
jsonp_error_init(error, source);
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
@@ -907,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",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#define STRBUFFER_MIN_SIZE 16
|
||||
#define STRBUFFER_FACTOR 2
|
||||
#define STRBUFFER_SIZE_MAX ((size_t)-1)
|
||||
|
||||
int strbuffer_init(strbuffer_t *strbuff)
|
||||
{
|
||||
@@ -64,13 +65,19 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
|
||||
return strbuffer_append_bytes(strbuff, &byte, 1);
|
||||
}
|
||||
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
|
||||
{
|
||||
if(strbuff->length + size >= strbuff->size)
|
||||
if(size >= strbuff->size - strbuff->length)
|
||||
{
|
||||
size_t new_size;
|
||||
char *new_value;
|
||||
|
||||
/* avoid integer overflow */
|
||||
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|
||||
|| size > STRBUFFER_SIZE_MAX - 1
|
||||
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
|
||||
return -1;
|
||||
|
||||
new_size = max(strbuff->size * STRBUFFER_FACTOR,
|
||||
strbuff->length + size + 1);
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
typedef struct {
|
||||
char *value;
|
||||
int length; /* bytes used */
|
||||
int size; /* bytes allocated */
|
||||
size_t length; /* bytes used */
|
||||
size_t size; /* bytes allocated */
|
||||
} strbuffer_t;
|
||||
|
||||
int strbuffer_init(strbuffer_t *strbuff);
|
||||
@@ -24,7 +24,7 @@ char *strbuffer_steal_value(strbuffer_t *strbuff);
|
||||
|
||||
int strbuffer_append(strbuffer_t *strbuff, const char *string);
|
||||
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
|
||||
|
||||
char strbuffer_pop(strbuffer_t *strbuff);
|
||||
|
||||
|
||||
108
src/strconv.c
Normal file
108
src/strconv.c
Normal file
@@ -0,0 +1,108 @@
|
||||
#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;
|
||||
}
|
||||
@@ -139,7 +139,10 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
allocated. */
|
||||
k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1);
|
||||
if(!k)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
k->serial = object->serial++;
|
||||
strcpy(k->key, key);
|
||||
@@ -700,6 +703,9 @@ int json_string_set_nocheck(json_t *json, const char *value)
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
|
||||
if(!json_is_string(json) || !value)
|
||||
return -1;
|
||||
|
||||
dup = jsonp_strdup(value);
|
||||
if(!dup)
|
||||
return -1;
|
||||
|
||||
2
test/.gitignore
vendored
2
test/.gitignore
vendored
@@ -4,8 +4,10 @@ suites/api/test_array
|
||||
suites/api/test_copy
|
||||
suites/api/test_cpp
|
||||
suites/api/test_dump
|
||||
suites/api/test_dump_callback
|
||||
suites/api/test_equal
|
||||
suites/api/test_load
|
||||
suites/api/test_loadb
|
||||
suites/api/test_memory_funcs
|
||||
suites/api/test_number
|
||||
suites/api/test_object
|
||||
|
||||
@@ -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(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
|
||||
@@ -4,8 +4,10 @@ check_PROGRAMS = \
|
||||
test_array \
|
||||
test_copy \
|
||||
test_dump \
|
||||
test_dump_callback \
|
||||
test_equal \
|
||||
test_load \
|
||||
test_loadb \
|
||||
test_memory_funcs \
|
||||
test_number \
|
||||
test_object \
|
||||
@@ -16,7 +18,9 @@ check_PROGRAMS = \
|
||||
test_array_SOURCES = test_array.c util.h
|
||||
test_copy_SOURCES = test_copy.c util.h
|
||||
test_dump_SOURCES = test_dump.c util.h
|
||||
test_dump_callback_SOURCES = test_dump_callback.c util.h
|
||||
test_load_SOURCES = test_load.c util.h
|
||||
test_loadb_SOURCES = test_loadb.c util.h
|
||||
test_memory_funcs_SOURCES = test_memory_funcs.c util.h
|
||||
test_number_SOURCES = test_number.c util.h
|
||||
test_object_SOURCES = test_object.c util.h
|
||||
|
||||
@@ -47,9 +47,11 @@ json_object_iter_set_new
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
json_dump_callback
|
||||
json_loads
|
||||
json_loadf
|
||||
json_load_file
|
||||
json_loadb
|
||||
json_equal
|
||||
json_copy
|
||||
json_deep_copy
|
||||
|
||||
@@ -387,7 +387,7 @@ static void test_circular()
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_misc();
|
||||
test_insert();
|
||||
@@ -395,6 +395,4 @@ int main()
|
||||
test_clear();
|
||||
test_extend();
|
||||
test_circular();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ static void test_deep_copy_object(void)
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_copy_simple();
|
||||
test_deep_copy_simple();
|
||||
@@ -315,5 +315,4 @@ int main()
|
||||
test_deep_copy_array();
|
||||
test_copy_object();
|
||||
test_deep_copy_object();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void encode_twice()
|
||||
{
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
|
||||
json = json_object();
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{}"))
|
||||
@@ -43,7 +43,10 @@ int main()
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void circular_references()
|
||||
{
|
||||
/* Construct a JSON object/array with a circular reference:
|
||||
|
||||
object: {"a": {"b": {"c": <circular reference to $.a>}}}
|
||||
@@ -51,6 +54,10 @@ int main()
|
||||
|
||||
Encode it, remove the circular reference and encode again.
|
||||
*/
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
json = json_object();
|
||||
json_object_set_new(json, "a", json_object());
|
||||
json_object_set_new(json_object_get(json, "a"), "b", json_object());
|
||||
@@ -86,6 +93,49 @@ int main()
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void encode_other_than_array_or_object()
|
||||
{
|
||||
/* Encoding anything other than array or object should only
|
||||
* succeed if the JSON_ENCODE_ANY flag is used */
|
||||
|
||||
json_t *json;
|
||||
FILE *fp = NULL;
|
||||
char *result;
|
||||
|
||||
json = json_string("foo");
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded a string!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded a string!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "\"foo\"") != 0)
|
||||
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
json = json_integer(42);
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded an integer!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded an integer!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "42") != 0)
|
||||
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
encode_twice();
|
||||
circular_references();
|
||||
encode_other_than_array_or_object();
|
||||
}
|
||||
|
||||
81
test/suites/api/test_dump_callback.c
Normal file
81
test/suites/api/test_dump_callback.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 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.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "util.h"
|
||||
|
||||
struct my_sink {
|
||||
char *buf;
|
||||
size_t off;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
static int my_writer(const char *buffer, size_t len, void *data) {
|
||||
struct my_sink *s = data;
|
||||
if (len > s->cap - s->off) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->off, buffer, len);
|
||||
s->off += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
struct my_sink s;
|
||||
json_t *json;
|
||||
const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
|
||||
char *dumped_to_string;
|
||||
|
||||
json = json_loads(str, 0, NULL);
|
||||
if(!json) {
|
||||
fail("json_loads failed");
|
||||
}
|
||||
|
||||
dumped_to_string = json_dumps(json, 0);
|
||||
if (!dumped_to_string) {
|
||||
json_decref(json);
|
||||
fail("json_dumps failed");
|
||||
}
|
||||
|
||||
s.off = 0;
|
||||
s.cap = strlen(dumped_to_string);
|
||||
s.buf = malloc(s.cap);
|
||||
if (!s.buf) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
fail("malloc failed");
|
||||
}
|
||||
|
||||
if (json_dump_callback(json, my_writer, &s, 0) == -1) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
fail("json_dump_callback failed on an exact-length sink buffer");
|
||||
}
|
||||
|
||||
if (strncmp(dumped_to_string, s.buf, s.off) != 0) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
fail("json_dump_callback and json_dumps did not produce identical output");
|
||||
}
|
||||
|
||||
s.off = 1;
|
||||
if (json_dump_callback(json, my_writer, &s, 0) != -1) {
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
fail("json_dump_callback succeeded on a short buffer when it should have failed");
|
||||
}
|
||||
|
||||
json_decref(json);
|
||||
free(dumped_to_string);
|
||||
free(s.buf);
|
||||
}
|
||||
@@ -180,11 +180,10 @@ static void test_equal_complex()
|
||||
/* TODO: There's no negative test case here */
|
||||
}
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_equal_simple();
|
||||
test_equal_array();
|
||||
test_equal_object();
|
||||
test_equal_complex();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,16 +9,50 @@
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void file_not_found()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
|
||||
if(json)
|
||||
fail("json_load_file returned non-NULL for a nonexistent file");
|
||||
if(error.line != -1)
|
||||
fail("json_load_file returned an invalid line number");
|
||||
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
|
||||
fail("json_load_file returned an invalid error message");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reject_duplicates()
|
||||
{
|
||||
json_error_t error;
|
||||
|
||||
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
|
||||
fail("json_loads did not detect a duplicate key");
|
||||
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
|
||||
}
|
||||
|
||||
static void disable_eof_check()
|
||||
{
|
||||
json_error_t error;
|
||||
json_t *json;
|
||||
|
||||
const char *text = "{\"foo\": 1} garbage";
|
||||
|
||||
if(json_loads(text, 0, &error))
|
||||
fail("json_loads did not detect garbage after JSON text");
|
||||
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
|
||||
|
||||
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
|
||||
if(!json)
|
||||
fail("json_loads failed with JSON_DISABLE_EOF_CHECK");
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
file_not_found();
|
||||
reject_duplicates();
|
||||
disable_eof_check();
|
||||
}
|
||||
|
||||
36
test/suites/api/test_loadb.c
Normal file
36
test/suites/api/test_loadb.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 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.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage";
|
||||
size_t len = strlen(str) - strlen("garbage");
|
||||
|
||||
json = json_loadb(str, len, 0, &error);
|
||||
if(!json) {
|
||||
fail("json_loadb failed on a valid JSON buffer");
|
||||
}
|
||||
json_decref(json);
|
||||
|
||||
json = json_loadb(str, len - 1, 0, &error);
|
||||
if (json) {
|
||||
json_decref(json);
|
||||
fail("json_loadb should have failed on an incomplete buffer, but it didn't");
|
||||
}
|
||||
if(error.line != 1) {
|
||||
fail("json_loadb returned an invalid line number on fail");
|
||||
}
|
||||
if(strcmp(error.text, "']' expected near end of file") != 0) {
|
||||
fail("json_loadb returned an invalid error message for an unclosed top-level array");
|
||||
}
|
||||
}
|
||||
@@ -75,10 +75,8 @@ static void test_secure_funcs(void)
|
||||
create_and_free_complex_object();
|
||||
}
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_simple();
|
||||
test_secure_funcs();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *integer, *real;
|
||||
int i;
|
||||
@@ -39,6 +39,4 @@ int main()
|
||||
|
||||
json_decref(integer);
|
||||
json_decref(real);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ static void test_preserve_order()
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
test_misc();
|
||||
test_clear();
|
||||
@@ -446,6 +446,4 @@ int main()
|
||||
test_set_nocheck();
|
||||
test_iterators();
|
||||
test_preserve_order();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *value;
|
||||
int i;
|
||||
@@ -227,6 +227,4 @@ int main()
|
||||
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
|
||||
fail("json_pack failed to catch invalid UTF-8 in a string");
|
||||
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "util.h"
|
||||
|
||||
/* Call the simple functions not covered by other tests of the public API */
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *value;
|
||||
|
||||
@@ -180,6 +180,4 @@ int main()
|
||||
json_incref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting null works incorrectly");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void run_tests()
|
||||
{
|
||||
json_t *j, *j2;
|
||||
int i1, i2, i3;
|
||||
@@ -336,6 +336,4 @@ int main()
|
||||
fail("json_unpack nested array with strict validation failed");
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,16 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#if HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
|
||||
@@ -52,4 +60,15 @@
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
static void run_tests();
|
||||
|
||||
int main() {
|
||||
#ifdef HAVE_SETLOCALE
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
run_tests();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user