Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
842bc2128b | ||
|
|
ca6d26a1c2 | ||
|
|
d3959a8ce7 | ||
|
|
f243930b68 | ||
|
|
15d992cb6a | ||
|
|
59c58ea26c | ||
|
|
f95bb423a3 | ||
|
|
9448ed3ef7 | ||
|
|
7c7a1ed01e | ||
|
|
325101e525 | ||
|
|
1b02f3546c | ||
|
|
cd96049d10 | ||
|
|
a83cd30c31 | ||
|
|
a00988f663 | ||
|
|
86dc1d629b | ||
|
|
b67e130fda | ||
|
|
4cd777712b | ||
|
|
79009e62c1 | ||
|
|
76999799ed | ||
|
|
22af193a51 | ||
|
|
951d091f07 | ||
|
|
185e107d24 | ||
|
|
ca7703fbd1 | ||
|
|
12cd4e8c09 | ||
|
|
bad16ea52a | ||
|
|
30015bde90 | ||
|
|
1e00cd58a5 | ||
|
|
40bb7bf437 | ||
|
|
9d648a87cb | ||
|
|
8de850be95 | ||
|
|
19588c2d69 | ||
|
|
9c5a8430db | ||
|
|
afc9c1a23a | ||
|
|
cbacac5975 | ||
|
|
b3e1fe2ec5 | ||
|
|
7728716759 | ||
|
|
b7bf96996f | ||
|
|
6d8c287032 | ||
|
|
ab3764ed0a | ||
|
|
5406c2b3d3 | ||
|
|
743af38e7f | ||
|
|
9d16ec755c | ||
|
|
04d550b02e | ||
|
|
55d2566539 | ||
|
|
9cc6fbe580 | ||
|
|
827a01937f | ||
|
|
0f62dac627 | ||
|
|
7ee974e91c | ||
|
|
c288188a0f | ||
|
|
057ba29a27 | ||
|
|
234ee47281 | ||
|
|
98a8c1aebf | ||
|
|
42e546d065 | ||
|
|
6ffb04f0d4 | ||
|
|
fd259ff68c | ||
|
|
ab2d93b724 | ||
|
|
9611780907 | ||
|
|
93c5892bc3 | ||
|
|
1095ca956b | ||
|
|
05654bfcba | ||
|
|
89d09813c3 | ||
|
|
5ac4914642 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@ aclocal.m4
|
||||
autom4te.cache
|
||||
config.guess
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
|
||||
64
CHANGES
Normal file
64
CHANGES
Normal file
@@ -0,0 +1,64 @@
|
||||
Version 1.1.2, released 2009-11-08
|
||||
|
||||
* Fix a bug where an error message was not produced if the input file
|
||||
could not be opened in json_load_file()
|
||||
* Fix an assertion failure in decoder caused by a minus sign without a
|
||||
digit after it
|
||||
* Remove an unneeded include for stdint.h in jansson.h
|
||||
|
||||
|
||||
Version 1.1.1, released 2009-10-26
|
||||
|
||||
* All documentation files were not distributed with v1.1; build
|
||||
documentation in make distcheck to prevent this in the future
|
||||
* Fix v1.1 release date in CHANGES
|
||||
|
||||
|
||||
Version 1.1, released 2009-10-20
|
||||
|
||||
* API additions and improvements:
|
||||
- Extend array and object APIs
|
||||
- Add functions to modify integer, real and string values
|
||||
- Improve argument validation
|
||||
- Use unsigned int instead of uint32_t for encoding flags
|
||||
* Enhance documentation
|
||||
- Add getting started guide and tutorial
|
||||
- Fix some typos
|
||||
- General clarifications and cleanup
|
||||
* Check for integer and real overflows and underflows in decoder
|
||||
* Make singleton values thread-safe (true, false and null)
|
||||
* Enhance circular reference handling
|
||||
* Don't define -std=c99 in AM_CFLAGS
|
||||
* Add C++ guards to jansson.h
|
||||
* Minor performance and portability improvements
|
||||
* Expand test coverage
|
||||
|
||||
|
||||
Version 1.0.4, released 2009-10-11
|
||||
|
||||
* Relax Autoconf version requirement to 2.59
|
||||
* Make Jansson compile on platforms where plain char is unsigned
|
||||
* Fix API tests for object
|
||||
|
||||
|
||||
Version 1.0.3, released 2009-09-14
|
||||
|
||||
* Check for integer and real overflows and underflows in decoder
|
||||
* Use the Python json module for tests, or simplejson if the json
|
||||
module is not found
|
||||
* Distribute changelog (this file)
|
||||
|
||||
|
||||
Version 1.0.2, released 2009-09-08
|
||||
|
||||
* Handle EOF correctly in decoder
|
||||
|
||||
|
||||
Version 1.0.1, released 2009-09-04
|
||||
|
||||
* Fixed broken json_is_boolean()
|
||||
|
||||
|
||||
Version 1.0, released 2009-08-25
|
||||
|
||||
* Initial release
|
||||
@@ -1,2 +1,7 @@
|
||||
EXTRA_DIST = LICENSE README.rst
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst
|
||||
SUBDIRS = doc src test
|
||||
|
||||
distcheck-hook:
|
||||
sphinx-build -b html -W \
|
||||
$(distdir)/doc \
|
||||
$(distdir)/_build/doc/.build/html
|
||||
|
||||
@@ -6,14 +6,14 @@ data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Good documentation
|
||||
- Comprehensive documentation
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
- Full Unicode support (UTF-8)
|
||||
|
||||
- Extensive test suite
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
Jansson is licensed under the `MIT license`_; see LICENSE in the
|
||||
source distribution for details.
|
||||
|
||||
|
||||
59
config.h.in
59
config.h.in
@@ -1,59 +0,0 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#undef LT_OBJDIR
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.63])
|
||||
AC_INIT([jansson], [1.0.1], [petri@digip.org])
|
||||
AC_PREREQ([2.59])
|
||||
AC_INIT([jansson], [1.1.2], [petri@digip.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
EXTRA_DIST = conf.py apiref.rst index.rst ext/refcounting.py
|
||||
EXTRA_DIST = \
|
||||
conf.py apiref.rst gettingstarted.rst github_commits.c index.rst \
|
||||
tutorial.rst ext/refcounting.py
|
||||
|
||||
clean-local:
|
||||
rm -rf .build
|
||||
rm -f ext/refcounting.pyc
|
||||
|
||||
260
doc/apiref.rst
260
doc/apiref.rst
@@ -1,3 +1,5 @@
|
||||
.. _apiref:
|
||||
|
||||
*************
|
||||
API Reference
|
||||
*************
|
||||
@@ -41,6 +43,12 @@ Objects of :ctype:`json_t` are always used through a pointer. There
|
||||
are APIs for querying the type, manipulating the reference count, and
|
||||
for constructing and manipulating values of different types.
|
||||
|
||||
Unless noted otherwise, all API functions return an error value if an
|
||||
error occurs. Depending on the function's signature, the error value
|
||||
is either *NULL* or -1. Invalid arguments or invalid input are
|
||||
apparent sources for errors. Memory allocation and I/O operations may
|
||||
also cause errors.
|
||||
|
||||
|
||||
Type
|
||||
----
|
||||
@@ -80,8 +88,8 @@ functions:
|
||||
.. cfunction:: int json_typeof(const json_t *json)
|
||||
|
||||
Return the type of the JSON value (a :ctype:`json_type` cast to
|
||||
:ctype:`int`). This function is actually implemented as a macro for
|
||||
speed.
|
||||
:ctype:`int`). *json* MUST NOT be *NULL*. This function is actually
|
||||
implemented as a macro for speed.
|
||||
|
||||
.. cfunction:: json_is_object(const json_t *json)
|
||||
json_is_array(const json_t *json)
|
||||
@@ -93,19 +101,22 @@ functions:
|
||||
json_is_null(const json_t *json)
|
||||
|
||||
These functions (actually macros) return true (non-zero) for values
|
||||
of the given type, and false (zero) for values of other types.
|
||||
of the given type, and false (zero) for values of other types and
|
||||
for *NULL*.
|
||||
|
||||
.. cfunction:: json_is_number(const json_t *json)
|
||||
|
||||
Returns true for values of types :const:`JSON_INTEGER` and
|
||||
:const:`JSON_REAL`, and false for other types.
|
||||
:const:`JSON_REAL`, and false for other types and for *NULL*.
|
||||
|
||||
.. cfunction:: json_is_boolean(const json_t *json)
|
||||
|
||||
Returns true for types :const:`JSON_TRUE` and :const:`JSON_FALSE`,
|
||||
and false for values of other types.
|
||||
and false for values of other types and for *NULL*.
|
||||
|
||||
|
||||
.. _apiref-reference-count:
|
||||
|
||||
Reference Count
|
||||
---------------
|
||||
|
||||
@@ -121,7 +132,8 @@ The following functions are used to manipulate the reference count.
|
||||
|
||||
.. cfunction:: json_t *json_incref(json_t *json)
|
||||
|
||||
Increment the reference count of *json*.
|
||||
Increment the reference count of *json* if it's not non-*NULL*.
|
||||
Returns *json*.
|
||||
|
||||
.. cfunction:: void json_decref(json_t *json)
|
||||
|
||||
@@ -151,29 +163,60 @@ will return a new or borrowed reference or steal a reference to its
|
||||
argument.
|
||||
|
||||
|
||||
Circular References
|
||||
-------------------
|
||||
|
||||
A circular reference is created when an object or an array is,
|
||||
directly or indirectly, inserted inside itself. The direct case is
|
||||
simple::
|
||||
|
||||
json_t *obj = json_object();
|
||||
json_object_set(obj, "foo", obj);
|
||||
|
||||
Jansson will refuse to do this, and :cfunc:`json_object_set()` (and
|
||||
all the other such functions for objects and arrays) will return with
|
||||
an error status. The indirect case is the dangerous one::
|
||||
|
||||
json_t *arr1 = json_array(), *arr2 = json_array();
|
||||
json_array_append(arr1, arr2);
|
||||
json_array_append(arr2, arr1);
|
||||
|
||||
In this example, the array ``arr2`` is contained in the array
|
||||
``arr1``, and vice versa. Jansson cannot check for this kind of
|
||||
indirect circular references without a performance hit, so it's up to
|
||||
the user to avoid them.
|
||||
|
||||
If a circular reference is created, the memory consumed by the values
|
||||
cannot be freed by :cfunc:`json_decref()`. The reference counts never
|
||||
drops to zero because the values are keeping the circular reference to
|
||||
themselves. Moreover, trying to encode the values with any of the
|
||||
encoding functions will fail. The encoder detects circular references
|
||||
and returns an error status.
|
||||
|
||||
|
||||
True, False and Null
|
||||
====================
|
||||
|
||||
These values are implemented as singletons, so each of these functions
|
||||
returns the same value each time.
|
||||
|
||||
.. cfunction:: json_t *json_true(void)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a value of the type :const:`JSON_TRUE`, or *NULL* on
|
||||
error.
|
||||
Returns the JSON true value.
|
||||
|
||||
.. cfunction:: json_t *json_false(void)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a value of the type :const:`JSON_FALSE`, or *NULL* on
|
||||
error.
|
||||
Returns the JSON false value.
|
||||
|
||||
.. cfunction:: json_t *json_null(void)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a value of the type :const:`JSON_NULL`, or *NULL* on
|
||||
error.
|
||||
Returns the JSON null value.
|
||||
|
||||
|
||||
String
|
||||
@@ -183,13 +226,21 @@ String
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_STRING`, or *NULL* on
|
||||
error. *value* must be a valid UTF-8 encoded Unicode string.
|
||||
Returns a new JSON string, or *NULL* on error. *value* must be a
|
||||
valid UTF-8 encoded Unicode string.
|
||||
|
||||
.. cfunction:: const char *json_string_value(const json_t *json)
|
||||
.. cfunction:: const char *json_string_value(const json_t *string)
|
||||
|
||||
Returns the associated value of a :const:`JSON_STRING` value as a
|
||||
null terminated UTF-8 encoded string.
|
||||
Returns the associated value of *string* as a null terminated UTF-8
|
||||
encoded string, or *NULL* if *string* is not a JSON string.
|
||||
|
||||
.. cfunction:: int json_string_set(const json_t *string, const char *value)
|
||||
|
||||
Sets the associated value of *string* to *value*. *value* must be a
|
||||
valid UTF-8 encoded Unicode string. Returns 0 on success and -1 on
|
||||
error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
|
||||
Number
|
||||
@@ -199,33 +250,46 @@ Number
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_INTEGER`, or *NULL* on
|
||||
error.
|
||||
Returns a new JSON integer, or *NULL* on error.
|
||||
|
||||
.. cfunction:: int json_integer_value(const json_t *json)
|
||||
.. cfunction:: int json_integer_value(const json_t *integer)
|
||||
|
||||
Returns the associated integer value of values of the type
|
||||
:const:`JSON_INTEGER`, or 0 for values of other types.
|
||||
Returns the associated value of *integer*, or 0 if *json* is not a
|
||||
JSON integer.
|
||||
|
||||
.. cfunction:: int json_integer_set(const json_t *integer, int value)
|
||||
|
||||
Sets the associated value of *integer* to *value*. Returns 0 on
|
||||
success and -1 if *integer* is not a JSON integer.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: json_t *json_real(double value)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_REAL`, or *NULL* on
|
||||
error.
|
||||
Returns a new JSON real, or *NULL* on error.
|
||||
|
||||
.. cfunction:: double json_real_value(const json_t *json)
|
||||
.. cfunction:: double json_real_value(const json_t *real)
|
||||
|
||||
Returns the associated real value of values of the type
|
||||
:const:`JSON_INTEGER`, or 0 for values of other types.
|
||||
Returns the associated value of *real*, or 0.0 if *real* is not a
|
||||
JSON real.
|
||||
|
||||
.. cfunction:: int json_real_set(const json_t *real, double value)
|
||||
|
||||
Sets the associated value of *real* to *value*. Returns 0 on
|
||||
success and -1 if *real* is not a JSON real.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
In addition to the functions above, there's a common query function
|
||||
for integers and reals:
|
||||
|
||||
.. cfunction:: double json_number_value(const json_t *json)
|
||||
|
||||
Returns the value of either ``JSON_INTEGER`` or ``JSON_REAL``, cast
|
||||
to double regardless of the actual type.
|
||||
Returns the associated value of the JSON integer or JSON real
|
||||
*json*, cast to double regardless of the actual type. If *json* is
|
||||
neither JSON real nor JSON integer, 0.0 is returned.
|
||||
|
||||
|
||||
Array
|
||||
@@ -237,33 +301,90 @@ A JSON array is an ordered collection of other JSON values.
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_ARRAY`, or *NULL* on
|
||||
error. Initially, the array is empty.
|
||||
Returns a new JSON array, or *NULL* on error. Initially, the array
|
||||
is empty.
|
||||
|
||||
.. cfunction:: unsigned int json_array_size(const json_t *array)
|
||||
|
||||
Returns the number of elements in *array*.
|
||||
Returns the number of elements in *array*, or 0 if *array* is NULL
|
||||
or not a JSON array.
|
||||
|
||||
.. cfunction:: json_t *json_array_get(const json_t *array, unsigned int index)
|
||||
|
||||
.. refcounting:: borrow
|
||||
|
||||
Returns the element in *array* at position *index*, or *NULL* if
|
||||
*index* is out of range. The valid range for *index* is from 0 to
|
||||
the return value of :cfunc:`json_array_size()` minus 1.
|
||||
Returns the element in *array* at position *index*. The valid range
|
||||
for *index* is from 0 to the return value of
|
||||
:cfunc:`json_array_size()` minus 1. If *array* is not a JSON array,
|
||||
if *array* is *NULL*, or if *index* is out of range, *NULL* is
|
||||
returned.
|
||||
|
||||
.. cfunction:: int json_array_set(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Replaces the element in *array* at position *index* with *value*.
|
||||
Returns 0 on success, or -1 if *index* is out of range. The valid
|
||||
range for *index* is from 0 to the return value of
|
||||
:cfunc:`json_array_size()` minus 1.
|
||||
The valid range for *index* is from 0 to the return value of
|
||||
:cfunc:`json_array_size()` minus 1. Returns 0 on success and -1 on
|
||||
error.
|
||||
|
||||
.. cfunction:: int json_array_set_new(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Like :cfunc:`json_array_set()` but steals the reference to *value*.
|
||||
This is useful when *value* is newly created and not used after
|
||||
the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_append(json_t *array, json_t *value)
|
||||
|
||||
Appends *value* to the end of *array*, growing the size of *array*
|
||||
by 1. Returns 0 on success and -1 on error.
|
||||
|
||||
.. cfunction:: int json_array_append_new(json_t *array, json_t *value)
|
||||
|
||||
Like :cfunc:`json_array_append()` but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_insert(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Inserts *value* to *array* at position *index*, shifting the
|
||||
elements at *index* and after it one position towards the end of
|
||||
the array. Returns 0 on success and -1 on error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_insert_new(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Like :cfunc:`json_array_insert()` but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_remove(json_t *array, unsigned int index)
|
||||
|
||||
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.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_clear(json_t *array)
|
||||
|
||||
Removes all elements from *array*. Returns 0 on sucess and -1 on
|
||||
error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_extend(json_t *array, json_t *other_array)
|
||||
|
||||
Appends all elements in *other_array* to the end of *array*.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
|
||||
Object
|
||||
======
|
||||
@@ -275,8 +396,15 @@ Unicode string and the value is any JSON value.
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_OBJECT`, or *NULL* on
|
||||
error. Initially, the object is empty.
|
||||
Returns a new JSON object, or *NULL* on error. Initially, the
|
||||
object is empty.
|
||||
|
||||
.. cfunction:: unsigned int json_object_size(const json_t *object)
|
||||
|
||||
Returns the number of elements in *object*, or 0 if *object* is not
|
||||
a JSON object.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: json_t *json_object_get(const json_t *object, const char *key)
|
||||
|
||||
@@ -288,9 +416,17 @@ Unicode string and the value is any JSON value.
|
||||
.. cfunction:: int json_object_set(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Set the value of *key* to *value* in *object*. *key* must be a
|
||||
valid terminated UTF-8 encoded Unicode string. If there already is
|
||||
a value for *key*, it is replaced by the new value. Returns 0 on
|
||||
success and -1 on error.
|
||||
valid null terminated UTF-8 encoded Unicode string. If there
|
||||
already is a value for *key*, it is replaced by the new value.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. cfunction:: int json_object_set_new(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Like :cfunc:`json_object_set()` but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_object_del(json_t *object, const char *key)
|
||||
|
||||
@@ -298,6 +434,21 @@ Unicode string and the value is any JSON value.
|
||||
-1 if *key* was not found.
|
||||
|
||||
|
||||
.. cfunction:: 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.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_object_update(json_t *object, json_t *other)
|
||||
|
||||
Update *object* with the key-value pairs from *other*, overwriting
|
||||
existing keys. Returns 0 on success or -1 on error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
|
||||
The following functions implement an iteration protocol for objects:
|
||||
|
||||
.. cfunction:: void *json_object_iter(json_t *object)
|
||||
@@ -321,6 +472,20 @@ The following functions implement an iteration protocol for objects:
|
||||
|
||||
Extract the associated value from *iter*.
|
||||
|
||||
The iteration protocol can be used for example as follows::
|
||||
|
||||
/* obj is a JSON object */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
void *iter = json_object_iter(obj);
|
||||
while(iter)
|
||||
{
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
/* use key and value ... */
|
||||
iter = json_object_iter_next(obj, iter);
|
||||
}
|
||||
|
||||
|
||||
Encoding
|
||||
========
|
||||
@@ -343,18 +508,21 @@ can be ORed together to obtain *flags*.
|
||||
The following functions perform the actual JSON encoding. The result
|
||||
is in UTF-8.
|
||||
|
||||
.. cfunction:: char *json_dumps(const json_t *root, uint32_t flags)
|
||||
.. cfunction:: char *json_dumps(const json_t *root, unsigned long flags)
|
||||
|
||||
Returns the JSON representation of *root* as a string, or *NULL* on
|
||||
error. *flags* is described above. The return value must be freed
|
||||
by the caller using :cfunc:`free()`.
|
||||
|
||||
.. cfunction:: int json_dumpf(const json_t *root, FILE *output, uint32_t flags)
|
||||
.. cfunction:: int json_dumpf(const json_t *root, FILE *output, unsigned long flags)
|
||||
|
||||
Write the JSON representation of *root* to the stream *output*.
|
||||
*flags* is described above. Returns 0 on success and -1 on error.
|
||||
If an error occurs, something may have already been written to
|
||||
*output*. In this case, the output is undefined and most likely not
|
||||
valid JSON.
|
||||
|
||||
.. cfunction:: int json_dump_file(const json_t *json, const char *path, uint32_t flags)
|
||||
.. cfunction:: int json_dump_file(const json_t *json, const char *path, unsigned long flags)
|
||||
|
||||
Write the JSON representation of *root* to the file *path*. If
|
||||
*path* already exists, it is overwritten. *flags* is described
|
||||
|
||||
@@ -50,9 +50,9 @@ copyright = u'2009, Petri Lehtinen'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.0'
|
||||
version = '1.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.0.1'
|
||||
release = '1.1.2'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
104
doc/gettingstarted.rst
Normal file
104
doc/gettingstarted.rst
Normal file
@@ -0,0 +1,104 @@
|
||||
***************
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
Compiling and Installing Jansson
|
||||
================================
|
||||
|
||||
The Jansson source is available at
|
||||
http://www.digip.org/jansson/releases/.
|
||||
|
||||
Unpack the source tarball and change to the source directory:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
bunzip2 -c jansson-|release|.tar.bz2 | tar xf -
|
||||
cd jansson-|release|
|
||||
|
||||
The source uses GNU Autotools (autoconf_, automake_, libtool_), so
|
||||
compiling and installing is extremely simple::
|
||||
|
||||
./configure
|
||||
make
|
||||
make check
|
||||
make install
|
||||
|
||||
To change the destination directory (``/usr/local`` by default), use
|
||||
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
|
||||
--help`` for the list of all possible installation options. (There are
|
||||
no options to customize the resulting Jansson binary.)
|
||||
|
||||
The command ``make check`` runs the test suite distributed with
|
||||
Jansson. Python_ is required to run the tests. This step is not
|
||||
strictly necessary, but it may find possible problems that Jansson has
|
||||
on your platform. If any problems are found, please report them.
|
||||
|
||||
If you obtained the source from a Git repository (or any other source
|
||||
control system), there's no ``./configure`` script as it's not kept in
|
||||
version control. To create the script, Autotools needs to be
|
||||
bootstrapped. There are many ways to do this, but the easiest one is
|
||||
to use ``autoreconf``::
|
||||
|
||||
autoreconf -vi
|
||||
|
||||
This command creates the ``./configure`` script, which can then be
|
||||
used as described in the previous section.
|
||||
|
||||
.. _autoconf: http://www.gnu.org/software/autoconf/
|
||||
.. _automake: http://www.gnu.org/software/automake/
|
||||
.. _libtool: http://www.gnu.org/software/libtool/
|
||||
.. _Python: http://www.python.org/
|
||||
|
||||
|
||||
Installing Prebuilt Binary Packages
|
||||
-----------------------------------
|
||||
|
||||
Binary ``.deb`` packages for Ubuntu are available in `this PPA`_ at
|
||||
Launchpad_. Follow the instructions in the PPA ("Technical details
|
||||
about this PPA" link) to take the PPA into use. Then install the -dev
|
||||
package::
|
||||
|
||||
sudo apt-get install libjansson-dev
|
||||
|
||||
.. _this PPA: http://launchpad.net/~petri/+archive/ppa
|
||||
.. _Launchpad: http://launchpad.net/
|
||||
|
||||
|
||||
Building the Documentation
|
||||
--------------------------
|
||||
|
||||
(This subsection describes how to build the HTML documentation you are
|
||||
currently reading, so it can be safely skipped.)
|
||||
|
||||
Documentation is in the ``doc/`` subdirectory. It's written in
|
||||
reStructuredText_ with Sphinx_ annotations. To generate the HTML
|
||||
documentation, invoke::
|
||||
|
||||
cd doc/
|
||||
sphinx-build . .build/html
|
||||
|
||||
... and point your browser to ``.build/html/index.html``. Sphinx_ is
|
||||
required to generate the documentation.
|
||||
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx.pocoo.org/
|
||||
|
||||
|
||||
Compiling Programs Using Jansson
|
||||
================================
|
||||
|
||||
Jansson involves one C header file, :file:`jansson.h`, so it's enough
|
||||
to put the line
|
||||
|
||||
::
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
in the beginning of every source file that uses Jansson.
|
||||
|
||||
There's also just one library to link with, ``libjansson``. Compile and
|
||||
link the program as follows::
|
||||
|
||||
cc -o prog prog.c -ljansson
|
||||
164
doc/github_commits.c
Normal file
164
doc/github_commits.c
Normal file
@@ -0,0 +1,164 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_SIZE 256
|
||||
|
||||
/* Return the offset of the first newline in text or the length of
|
||||
text if there's no newline */
|
||||
static int newline_offset(const char *text)
|
||||
{
|
||||
const char *newline = strchr(text, '\n');
|
||||
if(!newline)
|
||||
return strlen(text);
|
||||
else
|
||||
return (int)(newline - text);
|
||||
}
|
||||
|
||||
struct write_result
|
||||
{
|
||||
char *data;
|
||||
int pos;
|
||||
};
|
||||
|
||||
static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
{
|
||||
struct write_result *result = (struct write_result *)stream;
|
||||
|
||||
if(result->pos + size * nmemb >= BUFFER_SIZE - 1)
|
||||
{
|
||||
fprintf(stderr, "error: too small buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(result->data + result->pos, ptr, size * nmemb);
|
||||
result->pos += size * nmemb;
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static char *request(const char *url)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode status;
|
||||
char *data;
|
||||
long code;
|
||||
|
||||
curl = curl_easy_init();
|
||||
data = malloc(BUFFER_SIZE);
|
||||
if(!curl || !data)
|
||||
return NULL;
|
||||
|
||||
struct write_result write_result = {
|
||||
.data = data,
|
||||
.pos = 0
|
||||
};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
|
||||
|
||||
status = curl_easy_perform(curl);
|
||||
if(status != 0)
|
||||
{
|
||||
fprintf(stderr, "error: unable to request data from %s:\n", url);
|
||||
fprintf(stderr, "%s\n", curl_easy_strerror(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
||||
if(code != 200)
|
||||
{
|
||||
fprintf(stderr, "error: server responded with code %ld\n", code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_global_cleanup();
|
||||
|
||||
/* zero-terminate the result */
|
||||
data[write_result.pos] = '\0';
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
|
||||
|
||||
text = request(url);
|
||||
if(!text)
|
||||
return 1;
|
||||
|
||||
root = json_loads(text, &error);
|
||||
free(text);
|
||||
|
||||
if(!root)
|
||||
{
|
||||
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message = json_object_get(commit, "message");
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message_text = json_string_value(message);
|
||||
printf("%.8s %.*s\n",
|
||||
json_string_value(id),
|
||||
newline_offset(message_text),
|
||||
message_text);
|
||||
}
|
||||
|
||||
json_decref(root);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,15 +1,39 @@
|
||||
Overview
|
||||
========
|
||||
Jansson Documentation
|
||||
=====================
|
||||
|
||||
This is the documentation for Jansson_ |release|, last updated |today|.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Jansson_ is a C library for encoding, decoding and manipulating JSON
|
||||
data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Comprehensive documentation
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
- Full Unicode support (UTF-8)
|
||||
|
||||
- Extensive test suite
|
||||
|
||||
Jansson is licensed under the `MIT license`_; see LICENSE in the
|
||||
source distribution for details.
|
||||
|
||||
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
|
||||
**Contents:**
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
gettingstarted
|
||||
tutorial
|
||||
apiref
|
||||
|
||||
|
||||
|
||||
275
doc/tutorial.rst
Normal file
275
doc/tutorial.rst
Normal file
@@ -0,0 +1,275 @@
|
||||
.. _tutorial:
|
||||
|
||||
********
|
||||
Tutorial
|
||||
********
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
In this tutorial, we create a program that fetches the latest commits
|
||||
of a repository in GitHub_ over the web. One of the response formats
|
||||
supported by `GitHub API`_ is JSON, so the result can be parsed using
|
||||
Jansson.
|
||||
|
||||
To stick to the the scope of this tutorial, we will only cover the the
|
||||
parts of the program related to handling JSON data. For the best user
|
||||
experience, the full source code is available:
|
||||
:download:`github_commits.c`. To compile it (on Unix-like systems with
|
||||
gcc), use the following command::
|
||||
|
||||
gcc -o github_commits github_commits.c -ljansson -lcurl
|
||||
|
||||
libcurl_ is used to communicate over the web, so it is required to
|
||||
compile the program.
|
||||
|
||||
The command line syntax is::
|
||||
|
||||
github_commits USER REPOSITORY
|
||||
|
||||
``USER`` is a GitHub user ID and ``REPOSITORY`` is the repository
|
||||
name. Please note that the GitHub API is rate limited, so if you run
|
||||
the program too many times within a short period of time, the sever
|
||||
starts to respond with an error.
|
||||
|
||||
.. _GitHub: http://github.com/
|
||||
.. _GitHub API: http://develop.github.com/
|
||||
.. _libcurl: http://curl.haxx.se/
|
||||
|
||||
|
||||
.. _tutorial-github-commits-api:
|
||||
|
||||
The GitHub Commits API
|
||||
======================
|
||||
|
||||
The `GitHub commits API`_ is used by sending HTTP requests to URLs
|
||||
starting with ``http://github.com/api/v2/json/commits/``. Our program
|
||||
only lists the latest commits, so the rest of the URL is
|
||||
``list/USER/REPOSITORY/BRANCH``, where ``USER``, ``REPOSITORY`` and
|
||||
``BRANCH`` are the GitHub user ID, the name of the repository, and the
|
||||
name of the branch whose commits are to be listed, respectively.
|
||||
|
||||
GitHub responds with a JSON object of the following form:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
{
|
||||
"commits": [
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
},
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
},
|
||||
<more commits...>
|
||||
]
|
||||
}
|
||||
|
||||
In our program, the HTTP request is sent using the following
|
||||
function::
|
||||
|
||||
static char *request(const char *url);
|
||||
|
||||
It takes the URL as a parameter, preforms a HTTP GET request, and
|
||||
returns a newly allocated string that contains the response body. If
|
||||
the request fails, an error message is printed to stderr and the
|
||||
return value is *NULL*. For full details, refer to :download:`the code
|
||||
<github_commits.c>`, as the actual implementation is not important
|
||||
here.
|
||||
|
||||
.. _GitHub commits API: http://develop.github.com/p/commits.html
|
||||
|
||||
.. _tutorial-the-program:
|
||||
|
||||
The Program
|
||||
===========
|
||||
|
||||
First the includes::
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
|
||||
Like all the programs using Jansson, we need to include
|
||||
:file:`jansson.h`.
|
||||
|
||||
The following definitions are used to build the GitHub commits API
|
||||
request URL::
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_SIZE 256
|
||||
|
||||
The following function is used when formatting the result to find the
|
||||
first newline in the commit message::
|
||||
|
||||
/* Return the offset of the first newline in text or the length of
|
||||
text if there's no newline */
|
||||
static int newline_offset(const char *text)
|
||||
{
|
||||
const char *newline = strchr(text, '\n');
|
||||
if(!newline)
|
||||
return strlen(text);
|
||||
else
|
||||
return (int)(newline - text);
|
||||
}
|
||||
|
||||
The main function follows. In the beginning, we first declare a bunch
|
||||
of variables and check the command line parameters::
|
||||
|
||||
unsigned int i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
Then we build the request URL using the user and repository names
|
||||
given as command line parameters::
|
||||
|
||||
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
|
||||
|
||||
This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above.
|
||||
Now we're ready to actually request the JSON data over the web::
|
||||
|
||||
text = request(url);
|
||||
if(!text)
|
||||
return 1;
|
||||
|
||||
If an error occurs, our function ``request`` prints the error and
|
||||
returns *NULL*, so it's enough to just return 1 from the main
|
||||
function.
|
||||
|
||||
Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
|
||||
as a response::
|
||||
|
||||
root = json_loads(text, &error);
|
||||
free(text);
|
||||
|
||||
if(!root)
|
||||
{
|
||||
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
We don't need the JSON text anymore, so we can free the ``text``
|
||||
variable right after decoding it. If :cfunc:`json_loads()` fails, it
|
||||
returns *NULL* and sets error information to the :ctype:`json_error_t`
|
||||
structure given as the second parameter. In this case, our program
|
||||
prints the error information out and returns 1 from the main function.
|
||||
|
||||
Now we're ready to extract the data out of the decoded JSON response.
|
||||
The structure of the response JSON was explained in section
|
||||
:ref:`tutorial-github-commits-api`.
|
||||
|
||||
First, we'll extract the ``commits`` array from the JSON response::
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
This is the array that contains objects describing latest commits in
|
||||
the repository. We check that the returned value really is an array.
|
||||
If the key ``commits`` doesn't exist, :cfunc:`json_object_get()`
|
||||
returns *NULL*, but :cfunc:`json_is_array()` handles this case, too.
|
||||
|
||||
Then we proceed to loop over all the commits in the array::
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
|
||||
The function :cfunc:`json_array_size()` returns the size of a JSON
|
||||
array. First, we again declare some variables and then extract the
|
||||
i'th element of the ``commits`` array using :cfunc:`json_array_get()`.
|
||||
We also check that the resulting value is a JSON object.
|
||||
|
||||
Next we'll extract the commit ID and commit message, and check that
|
||||
they both are JSON strings::
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message = json_object_get(commit, "message");
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
|
||||
And finally, we'll print the first 8 characters of the commit ID and
|
||||
the first line of the commit message. A C-style string is extracted
|
||||
from a JSON string using :cfunc:`json_string_value()`::
|
||||
|
||||
message_text = json_string_value(message);
|
||||
printf("%.8s %.*s\n",
|
||||
json_string_value(id),
|
||||
newline_offset(message_text),
|
||||
message_text);
|
||||
}
|
||||
|
||||
After sending the HTTP request, we decoded the JSON text using
|
||||
:cfunc:`json_loads()`, remember? It returns a *new reference* to the
|
||||
JSON value it decodes. When we're finished with the value, we'll need
|
||||
to decrease the reference count using :cfunc:`json_decref()`. This way
|
||||
Jansson can release the resources::
|
||||
|
||||
json_decref(root);
|
||||
return 0;
|
||||
|
||||
For a detailed explanation of reference counting in Jansson, see
|
||||
:ref:`apiref-reference-count` in :ref:`apiref`.
|
||||
|
||||
The program's ready, let's test it and view the latest commits in
|
||||
Jansson's repository::
|
||||
|
||||
$ ./github_commits akheron jansson
|
||||
86dc1d62 Fix indentation
|
||||
b67e130f json_dumpf: Document the output shortage on error
|
||||
4cd77771 Enhance handling of circular references
|
||||
79009e62 json_dumps: Close the strbuffer if dumping fails
|
||||
76999799 doc: Fix a small typo in apiref
|
||||
22af193a doc/Makefile.am: Remove *.pyc in clean
|
||||
951d091f Make integer, real and string mutable
|
||||
185e107d Don't use non-portable asprintf()
|
||||
ca7703fb Merge branch '1.0'
|
||||
12cd4e8c jansson 1.0.4
|
||||
<etc...>
|
||||
|
||||
|
||||
Conclusion
|
||||
==========
|
||||
|
||||
In this tutorial, we implemented a program that fetches the latest
|
||||
commits of a GitHub repository using the GitHub commits API. Jansson
|
||||
was used to decode the JSON response and to extract the commit data.
|
||||
|
||||
This tutorial only covered a small part of Jansson. For example, we
|
||||
did not create or manipulate JSON values at all. Proceed to
|
||||
:ref:`apiref` to explore all features of Jansson.
|
||||
@@ -13,6 +13,6 @@ libjansson_la_SOURCES = \
|
||||
utf.h \
|
||||
util.h \
|
||||
value.c
|
||||
libjansson_la_LDFLAGS = -version-info 0:1:0
|
||||
libjansson_la_LDFLAGS = -version-info 1:1:1
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra -Werror -std=c99
|
||||
AM_CFLAGS = -Wall -Wextra -Werror
|
||||
|
||||
96
src/dump.c
96
src/dump.c
@@ -9,11 +9,14 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
|
||||
#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
|
||||
@@ -36,22 +39,23 @@ static int dump_to_file(const char *buffer, int size, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_indent(uint32_t flags, int depth, dump_func dump, void *data)
|
||||
/* 256 spaces (the maximum indentation size) */
|
||||
static char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(unsigned long flags, int depth, dump_func dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
{
|
||||
char *ws_buffer;
|
||||
int ws_count = JSON_INDENT(flags) * depth;
|
||||
int i, ws_count = JSON_INDENT(flags);
|
||||
|
||||
if(dump("\n", 1, data))
|
||||
return -1;
|
||||
|
||||
if(ws_count == 0)
|
||||
return 0;
|
||||
|
||||
ws_buffer = alloca(ws_count);
|
||||
memset(ws_buffer, ' ', ws_count);
|
||||
return dump(ws_buffer, ws_count, data);
|
||||
for(i = 0; i < depth; i++)
|
||||
{
|
||||
if(dump(whitespace, ws_count, data))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -70,7 +74,7 @@ static int dump_string(const char *str, dump_func dump, void *data)
|
||||
char seq[7];
|
||||
int length;
|
||||
|
||||
while(*end && *end != '\\' && *end != '"' && (*end < 0 || *end > 0x1F))
|
||||
while(*end && *end != '\\' && *end != '"' && (unsigned char)*end > 0x1F)
|
||||
end++;
|
||||
|
||||
if(end != str) {
|
||||
@@ -111,7 +115,7 @@ static int dump_string(const char *str, dump_func dump, void *data)
|
||||
return dump("\"", 1, data);
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
dump_func dump, void *data)
|
||||
{
|
||||
switch(json_typeof(json)) {
|
||||
@@ -126,30 +130,26 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
|
||||
case JSON_INTEGER:
|
||||
{
|
||||
char *buffer;
|
||||
int size, ret;
|
||||
char buffer[MAX_INTEGER_STR_LENGTH];
|
||||
int size;
|
||||
|
||||
size = asprintf(&buffer, "%d", json_integer_value(json));
|
||||
if(size == -1)
|
||||
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
|
||||
if(size >= MAX_INTEGER_STR_LENGTH)
|
||||
return -1;
|
||||
|
||||
ret = dump(buffer, size, data);
|
||||
free(buffer);
|
||||
return ret;
|
||||
return dump(buffer, size, data);
|
||||
}
|
||||
|
||||
case JSON_REAL:
|
||||
{
|
||||
char *buffer;
|
||||
int size, ret;
|
||||
char buffer[MAX_REAL_STR_LENGTH];
|
||||
int size;
|
||||
|
||||
size = asprintf(&buffer, "%.17f", json_real_value(json));
|
||||
if(size == -1)
|
||||
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%0.17f", json_real_value(json));
|
||||
if(size >= MAX_REAL_STR_LENGTH)
|
||||
return -1;
|
||||
|
||||
ret = dump(buffer, size, data);
|
||||
free(buffer);
|
||||
return ret;
|
||||
return dump(buffer, size, data);
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
@@ -158,7 +158,16 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
int i;
|
||||
int n = json_array_size(json);
|
||||
int n;
|
||||
json_array_t *array;
|
||||
|
||||
/* detect circular references */
|
||||
array = json_to_array(json);
|
||||
if(array->visited)
|
||||
return -1;
|
||||
array->visited = 1;
|
||||
|
||||
n = json_array_size(json);
|
||||
|
||||
if(dump("[", 1, data))
|
||||
return -1;
|
||||
@@ -184,12 +193,23 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
}
|
||||
|
||||
case JSON_OBJECT:
|
||||
{
|
||||
void *iter = json_object_iter((json_t *)json);
|
||||
json_object_t *object;
|
||||
void *iter;
|
||||
|
||||
/* detect circular references */
|
||||
object = json_to_object(json);
|
||||
if(object->visited)
|
||||
return -1;
|
||||
object->visited = 1;
|
||||
|
||||
iter = json_object_iter((json_t *)json);
|
||||
|
||||
if(dump("{", 1, data))
|
||||
return -1;
|
||||
@@ -222,6 +242,8 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
|
||||
iter = next;
|
||||
}
|
||||
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
}
|
||||
|
||||
@@ -232,7 +254,7 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
}
|
||||
|
||||
|
||||
char *json_dumps(const json_t *json, uint32_t flags)
|
||||
char *json_dumps(const json_t *json, unsigned long flags)
|
||||
{
|
||||
strbuffer_t strbuff;
|
||||
char *result;
|
||||
@@ -241,13 +263,17 @@ char *json_dumps(const json_t *json, uint32_t flags)
|
||||
return NULL;
|
||||
|
||||
if(strbuffer_init(&strbuff))
|
||||
return NULL;
|
||||
|
||||
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff))
|
||||
return NULL;
|
||||
|
||||
if(dump_to_strbuffer("\n", 1, (void *)&strbuff))
|
||||
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(dump_to_strbuffer("\n", 1, (void *)&strbuff)) {
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = strdup(strbuffer_value(&strbuff));
|
||||
strbuffer_close(&strbuff);
|
||||
@@ -255,7 +281,7 @@ char *json_dumps(const json_t *json, uint32_t flags)
|
||||
return result;
|
||||
}
|
||||
|
||||
int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
|
||||
int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
|
||||
{
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
@@ -265,7 +291,7 @@ int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
|
||||
return dump_to_file("\n", 1, (void *)output);
|
||||
}
|
||||
|
||||
int json_dump_file(const json_t *json, const char *path, uint32_t flags)
|
||||
int json_dump_file(const json_t *json, const char *path, unsigned long flags)
|
||||
{
|
||||
int result;
|
||||
|
||||
|
||||
@@ -133,6 +133,23 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hashtable_do_clear(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
|
||||
for(list = hashtable->list.next; list != &hashtable->list; list = next)
|
||||
{
|
||||
next = list->next;
|
||||
pair = list_to_pair(list);
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
free(pair);
|
||||
}
|
||||
}
|
||||
|
||||
static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
@@ -220,19 +237,7 @@ int hashtable_init(hashtable_t *hashtable,
|
||||
|
||||
void hashtable_close(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
for(list = hashtable->list.next; list != &hashtable->list; list = next)
|
||||
{
|
||||
next = list->next;
|
||||
pair = list_to_pair(list);
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
free(pair);
|
||||
}
|
||||
|
||||
hashtable_do_clear(hashtable);
|
||||
free(hashtable->buckets);
|
||||
}
|
||||
|
||||
@@ -292,6 +297,22 @@ int hashtable_del(hashtable_t *hashtable, const void *key)
|
||||
return hashtable_do_del(hashtable, key, hash);
|
||||
}
|
||||
|
||||
void hashtable_clear(hashtable_t *hashtable)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
hashtable_do_clear(hashtable);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
}
|
||||
|
||||
list_init(&hashtable->list);
|
||||
hashtable->size = 0;
|
||||
}
|
||||
|
||||
void *hashtable_iter(hashtable_t *hashtable)
|
||||
{
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
|
||||
@@ -13,8 +13,8 @@ typedef int (*key_cmp_fn)(const void *key1, const void *key2);
|
||||
typedef void (*free_fn)(void *key);
|
||||
|
||||
struct hashtable_list {
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
};
|
||||
|
||||
struct hashtable_pair {
|
||||
@@ -134,6 +134,15 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
|
||||
*/
|
||||
int hashtable_del(hashtable_t *hashtable, const void *key);
|
||||
|
||||
/**
|
||||
* hashtable_clear - Clear hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
*
|
||||
* Removes all items from the hashtable.
|
||||
*/
|
||||
void hashtable_clear(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_iter - Iterate over hashtable
|
||||
*
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
#define JANSSON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* types */
|
||||
|
||||
@@ -54,7 +57,7 @@ json_t *json_null(void);
|
||||
|
||||
static inline json_t *json_incref(json_t *json)
|
||||
{
|
||||
if(json)
|
||||
if(json && json->refcount != (unsigned int)-1)
|
||||
++json->refcount;
|
||||
return json;
|
||||
}
|
||||
@@ -64,31 +67,66 @@ void json_delete(json_t *json);
|
||||
|
||||
static inline void json_decref(json_t *json)
|
||||
{
|
||||
if(json && --json->refcount == 0)
|
||||
if(json && json->refcount != (unsigned int)-1 && --json->refcount == 0)
|
||||
json_delete(json);
|
||||
}
|
||||
|
||||
|
||||
/* getters, setters, manipulation */
|
||||
|
||||
unsigned int json_object_size(const json_t *object);
|
||||
json_t *json_object_get(const json_t *object, const char *key);
|
||||
int json_object_set(json_t *object, const char *key, json_t *value);
|
||||
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||
int json_object_del(json_t *object, const char *key);
|
||||
int json_object_clear(json_t *object);
|
||||
int json_object_update(json_t *object, json_t *other);
|
||||
void *json_object_iter(json_t *object);
|
||||
void *json_object_iter_next(json_t *object, void *iter);
|
||||
const char *json_object_iter_key(void *iter);
|
||||
json_t *json_object_iter_value(void *iter);
|
||||
|
||||
static inline
|
||||
int json_object_set(json_t *object, const char *key, json_t *value)
|
||||
{
|
||||
return json_object_set_new(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
unsigned int json_array_size(const json_t *array);
|
||||
json_t *json_array_get(const json_t *array, unsigned int index);
|
||||
int json_array_set(json_t *array, unsigned int index, json_t *value);
|
||||
int json_array_append(json_t *array, json_t *value);
|
||||
int json_array_set_new(json_t *array, unsigned int index, json_t *value);
|
||||
int json_array_append_new(json_t *array, json_t *value);
|
||||
int json_array_insert_new(json_t *array, unsigned int index, json_t *value);
|
||||
int json_array_remove(json_t *array, unsigned int index);
|
||||
int json_array_clear(json_t *array);
|
||||
int json_array_extend(json_t *array, json_t *other);
|
||||
|
||||
const char *json_string_value(const json_t *json);
|
||||
int json_integer_value(const json_t *json);
|
||||
double json_real_value(const json_t *json);
|
||||
static inline
|
||||
int json_array_set(json_t *array, unsigned int index, json_t *value)
|
||||
{
|
||||
return json_array_set_new(array, index, json_incref(value));
|
||||
}
|
||||
|
||||
static inline
|
||||
int json_array_append(json_t *array, json_t *value)
|
||||
{
|
||||
return json_array_append_new(array, json_incref(value));
|
||||
}
|
||||
|
||||
static inline
|
||||
int json_array_insert(json_t *array, unsigned int index, json_t *value)
|
||||
{
|
||||
return json_array_insert_new(array, index, json_incref(value));
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *string);
|
||||
int json_integer_value(const json_t *integer);
|
||||
double json_real_value(const json_t *real);
|
||||
double json_number_value(const json_t *json);
|
||||
|
||||
int json_string_set(const json_t *string, const char *value);
|
||||
int json_integer_set(const json_t *integer, int value);
|
||||
int json_real_set(const json_t *real, double value);
|
||||
|
||||
|
||||
/* loading, printing */
|
||||
|
||||
@@ -105,8 +143,12 @@ json_t *json_load_file(const char *path, json_error_t *error);
|
||||
|
||||
#define JSON_INDENT(n) (n & 0xFF)
|
||||
|
||||
char *json_dumps(const json_t *json, uint32_t flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, uint32_t flags);
|
||||
int json_dump_file(const json_t *json, const char *path, uint32_t flags);
|
||||
char *json_dumps(const json_t *json, unsigned long flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
|
||||
int json_dump_file(const json_t *json, const char *path, unsigned long flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,8 +8,48 @@
|
||||
#ifndef JANSSON_PRIVATE_H
|
||||
#define JANSSON_PRIVATE_H
|
||||
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
int visited;
|
||||
} json_object_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
unsigned int size;
|
||||
unsigned int entries;
|
||||
json_t **table;
|
||||
int visited;
|
||||
} json_array_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
double value;
|
||||
} json_real_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
int value;
|
||||
} json_integer_t;
|
||||
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
int json_object_set_nocheck(json_t *json, const char *key, json_t *value);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
97
src/load.c
97
src/load.c
@@ -8,11 +8,11 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
@@ -134,10 +134,7 @@ static char stream_get(stream_t *stream, json_error_t *error)
|
||||
|
||||
c = stream->buffer[0];
|
||||
|
||||
if(c == EOF && stream->eof(stream->data))
|
||||
return EOF;
|
||||
|
||||
if(c < 0)
|
||||
if((unsigned char)c >= 0x80 && c != (char)EOF)
|
||||
{
|
||||
/* multi-byte UTF-8 sequence */
|
||||
int i, count;
|
||||
@@ -257,17 +254,17 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
lex->value.string = NULL;
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
/* skip the " */
|
||||
c = lex_get_save(lex, error);
|
||||
|
||||
while(c != '"') {
|
||||
if(c == EOF) {
|
||||
if(c == (char)EOF) {
|
||||
lex_unget_unsave(lex, c);
|
||||
if(lex_eof(lex))
|
||||
error_set(error, lex, "premature end of input");
|
||||
goto out;
|
||||
}
|
||||
|
||||
else if(0 <= c && c <= 0x1F) {
|
||||
else if((unsigned char)c <= 0x1F) {
|
||||
/* control character */
|
||||
lex_unget_unsave(lex, c);
|
||||
if(c == '\n')
|
||||
@@ -341,9 +338,10 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
|
||||
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
|
||||
/* valid second surrogate */
|
||||
value = ((value - 0xD800) << 10) +
|
||||
(value2 - 0xDC00) +
|
||||
0x10000;
|
||||
value =
|
||||
((value - 0xD800) << 10) +
|
||||
(value2 - 0xDC00) +
|
||||
0x10000;
|
||||
}
|
||||
else {
|
||||
/* invalid second surrogate */
|
||||
@@ -402,10 +400,11 @@ out:
|
||||
free(lex->value.string);
|
||||
}
|
||||
|
||||
static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
static int lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
{
|
||||
const char *saved_text;
|
||||
char *end;
|
||||
double value;
|
||||
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
@@ -419,21 +418,37 @@ static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else /* c != '0' */ {
|
||||
else if(isdigit(c)) {
|
||||
c = lex_get_save(lex, error);
|
||||
while(isdigit(c))
|
||||
c = lex_get_save(lex, error);
|
||||
}
|
||||
else {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(c != '.' && c != 'E' && c != 'e') {
|
||||
long value;
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
lex->token = TOKEN_INTEGER;
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
lex->value.integer = strtol(saved_text, &end, 10);
|
||||
value = strtol(saved_text, &end, 10);
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
return;
|
||||
if((value == LONG_MAX && errno == ERANGE) || value > INT_MAX) {
|
||||
error_set(error, lex, "too big integer");
|
||||
goto out;
|
||||
}
|
||||
else if((value == LONG_MIN && errno == ERANGE) || value < INT_MIN) {
|
||||
error_set(error, lex, "too big negative integer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lex->token = TOKEN_INTEGER;
|
||||
lex->value.integer = (int)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(c == '.') {
|
||||
@@ -463,14 +478,29 @@ static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
}
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
lex->token = TOKEN_REAL;
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
lex->value.real = strtod(saved_text, &end);
|
||||
value = strtod(saved_text, &end);
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
if(value == 0 && errno == ERANGE) {
|
||||
error_set(error, lex, "real number underflow");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Cannot test for +/-HUGE_VAL because the HUGE_VAL constant is
|
||||
only defined in C99 mode. So let's trust in sole errno. */
|
||||
else if(errno == ERANGE) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lex->token = TOKEN_REAL;
|
||||
lex->value.real = value;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
@@ -480,8 +510,8 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
strbuffer_clear(&lex->saved_text);
|
||||
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
|
||||
c = lex_get(lex, error);
|
||||
@@ -493,7 +523,7 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
c = lex_get(lex, error);
|
||||
}
|
||||
|
||||
if(c == EOF) {
|
||||
if(c == (char)EOF) {
|
||||
if(lex_eof(lex))
|
||||
lex->token = TOKEN_EOF;
|
||||
else
|
||||
@@ -509,8 +539,10 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
else if(c == '"')
|
||||
lex_scan_string(lex, error);
|
||||
|
||||
else if(isdigit(c) || c == '-')
|
||||
lex_scan_number(lex, c, error);
|
||||
else if(isdigit(c) || c == '-') {
|
||||
if(lex_scan_number(lex, c, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
else if(isupper(c) || islower(c)) {
|
||||
/* eat up the whole identifier for clearer error messages */
|
||||
@@ -544,6 +576,17 @@ out:
|
||||
return lex->token;
|
||||
}
|
||||
|
||||
static char *lex_steal_string(lex_t *lex)
|
||||
{
|
||||
char *result = NULL;
|
||||
if(lex->token == TOKEN_STRING)
|
||||
{
|
||||
result = lex->value.string;
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
|
||||
{
|
||||
stream_init(&lex->stream, get, eof, data);
|
||||
@@ -587,7 +630,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = strdup(lex->value.string);
|
||||
key = lex_steal_string(lex);
|
||||
if(!key)
|
||||
return NULL;
|
||||
|
||||
@@ -706,7 +749,7 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
break;
|
||||
|
||||
case '{':
|
||||
json = parse_object(lex, error);
|
||||
json = parse_object(lex, error);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
@@ -825,6 +868,8 @@ json_t *json_load_file(const char *path, json_error_t *error)
|
||||
json_t *result;
|
||||
FILE *fp;
|
||||
|
||||
error_init(error);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if(!fp)
|
||||
{
|
||||
|
||||
382
src/value.c
382
src/value.c
@@ -15,41 +15,6 @@
|
||||
#include "utf.h"
|
||||
#include "util.h"
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
} json_object_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
unsigned int size;
|
||||
unsigned int entries;
|
||||
json_t **table;
|
||||
} json_array_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
double value;
|
||||
} json_real_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
int value;
|
||||
} json_integer_t;
|
||||
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
static inline void json_init(json_t *json, json_type type)
|
||||
{
|
||||
@@ -98,6 +63,9 @@ json_t *json_object(void)
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
object->visited = 0;
|
||||
|
||||
return &object->json;
|
||||
}
|
||||
|
||||
@@ -107,6 +75,17 @@ static void json_delete_object(json_object_t *object)
|
||||
free(object);
|
||||
}
|
||||
|
||||
unsigned int json_object_size(const json_t *json)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
return object->hashtable.size;
|
||||
}
|
||||
|
||||
json_t *json_object_get(const json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
@@ -118,23 +97,43 @@ json_t *json_object_get(const json_t *json, const char *key)
|
||||
return hashtable_get(&object->hashtable, key);
|
||||
}
|
||||
|
||||
int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
|
||||
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_object(json) || json == value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
object = json_to_object(json);
|
||||
return hashtable_set(&object->hashtable, strdup(key), json_incref(value));
|
||||
|
||||
if(hashtable_set(&object->hashtable, strdup(key), value))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_object_set(json_t *json, const char *key, json_t *value)
|
||||
int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
if(!utf8_check_string(key, -1))
|
||||
return -1;
|
||||
return json_object_set_new_nocheck(json, key, json_incref(value));
|
||||
}
|
||||
|
||||
return json_object_set_nocheck(json, key, value);
|
||||
int json_object_set_new(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
if(!key || !utf8_check_string(key, -1))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return json_object_set_new_nocheck(json, key, value);
|
||||
}
|
||||
|
||||
int json_object_del(json_t *json, const char *key)
|
||||
@@ -148,6 +147,43 @@ int json_object_del(json_t *json, const char *key)
|
||||
return hashtable_del(&object->hashtable, key);
|
||||
}
|
||||
|
||||
int json_object_clear(json_t *json)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
hashtable_clear(&object->hashtable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_object_update(json_t *object, json_t *other)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
if(!json_is_object(object) || !json_is_object(other))
|
||||
return -1;
|
||||
|
||||
iter = json_object_iter(other);
|
||||
while(iter) {
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
|
||||
if(json_object_set(object, key, value))
|
||||
return -1;
|
||||
|
||||
iter = json_object_iter_next(other, iter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *json_object_iter(json_t *json)
|
||||
{
|
||||
json_object_t *object;
|
||||
@@ -193,12 +229,19 @@ json_t *json_array(void)
|
||||
{
|
||||
json_array_t *array = malloc(sizeof(json_array_t));
|
||||
if(!array)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&array->json, JSON_ARRAY);
|
||||
|
||||
array->entries = 0;
|
||||
array->size = 0;
|
||||
array->table = NULL;
|
||||
array->size = 8;
|
||||
|
||||
array->table = malloc(array->size * sizeof(json_t *));
|
||||
if(!array->table) {
|
||||
free(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
|
||||
return &array->json;
|
||||
}
|
||||
@@ -235,9 +278,143 @@ json_t *json_array_get(const json_t *json, unsigned int index)
|
||||
return array->table[index];
|
||||
}
|
||||
|
||||
int json_array_set(json_t *json, unsigned int index, json_t *value)
|
||||
int json_array_set_new(json_t *json, unsigned int index, json_t *value)
|
||||
{
|
||||
json_array_t *array;
|
||||
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_array(json) || json == value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
array = json_to_array(json);
|
||||
|
||||
if(index >= array->entries)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_decref(array->table[index]);
|
||||
array->table[index] = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void array_move(json_array_t *array, unsigned int dest,
|
||||
unsigned int src, unsigned int count)
|
||||
{
|
||||
memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *));
|
||||
}
|
||||
|
||||
static void array_copy(json_t **dest, unsigned int dpos,
|
||||
json_t **src, unsigned int spos,
|
||||
unsigned int count)
|
||||
{
|
||||
memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *));
|
||||
}
|
||||
|
||||
static json_t **json_array_grow(json_array_t *array,
|
||||
unsigned int amount,
|
||||
int copy)
|
||||
{
|
||||
unsigned int new_size;
|
||||
json_t **old_table, **new_table;
|
||||
|
||||
if(array->entries + amount <= array->size)
|
||||
return array->table;
|
||||
|
||||
old_table = array->table;
|
||||
|
||||
new_size = max(array->size + amount, array->size * 2);
|
||||
new_table = malloc(new_size * sizeof(json_t *));
|
||||
if(!new_table)
|
||||
return NULL;
|
||||
|
||||
array->size = new_size;
|
||||
array->table = new_table;
|
||||
|
||||
if(copy) {
|
||||
array_copy(array->table, 0, old_table, 0, array->entries);
|
||||
free(old_table);
|
||||
return array->table;
|
||||
}
|
||||
|
||||
return old_table;
|
||||
}
|
||||
|
||||
int json_array_append_new(json_t *json, json_t *value)
|
||||
{
|
||||
json_array_t *array;
|
||||
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_array(json) || json == value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
array = json_to_array(json);
|
||||
|
||||
if(!json_array_grow(array, 1, 1)) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
array->table[array->entries] = value;
|
||||
array->entries++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
|
||||
{
|
||||
json_array_t *array;
|
||||
json_t **old_table;
|
||||
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_array(json) || json == value) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
array = json_to_array(json);
|
||||
|
||||
if(index > array->entries) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
old_table = json_array_grow(array, 1, 0);
|
||||
if(!old_table) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(old_table != array->table) {
|
||||
array_copy(array->table, 0, old_table, 0, index);
|
||||
array_copy(array->table, index + 1, old_table, index,
|
||||
array->entries - index);
|
||||
free(old_table);
|
||||
}
|
||||
else
|
||||
array_move(array, index + 1, index, array->entries - index);
|
||||
|
||||
array->table[index] = value;
|
||||
array->entries++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_remove(json_t *json, unsigned int index)
|
||||
{
|
||||
json_array_t *array;
|
||||
|
||||
if(!json_is_array(json))
|
||||
return -1;
|
||||
array = json_to_array(json);
|
||||
@@ -246,28 +423,48 @@ int json_array_set(json_t *json, unsigned int index, json_t *value)
|
||||
return -1;
|
||||
|
||||
json_decref(array->table[index]);
|
||||
array->table[index] = json_incref(value);
|
||||
|
||||
array_move(array, index, index + 1, array->entries - index);
|
||||
array->entries--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_append(json_t *json, json_t *value)
|
||||
int json_array_clear(json_t *json)
|
||||
{
|
||||
json_array_t *array;
|
||||
unsigned int i;
|
||||
|
||||
if(!json_is_array(json))
|
||||
return -1;
|
||||
array = json_to_array(json);
|
||||
|
||||
if(array->entries == array->size) {
|
||||
array->size = max(8, array->size * 2);
|
||||
array->table = realloc(array->table, array->size * sizeof(json_t *));
|
||||
if(!array->table)
|
||||
return -1;
|
||||
}
|
||||
for(i = 0; i < array->entries; i++)
|
||||
json_decref(array->table[i]);
|
||||
|
||||
array->table[array->entries] = json_incref(value);
|
||||
array->entries++;
|
||||
array->entries = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_extend(json_t *json, json_t *other_json)
|
||||
{
|
||||
json_array_t *array, *other;
|
||||
unsigned int i;
|
||||
|
||||
if(!json_is_array(json) || !json_is_array(other_json))
|
||||
return -1;
|
||||
array = json_to_array(json);
|
||||
other = json_to_array(other_json);
|
||||
|
||||
if(!json_array_grow(array, other->entries, 1))
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < other->entries; i++)
|
||||
json_incref(other->table[i]);
|
||||
|
||||
array_copy(array->table, array->entries, other->table, 0, other->entries);
|
||||
|
||||
array->entries += other->entries;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -276,18 +473,28 @@ int json_array_append(json_t *json, json_t *value)
|
||||
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
{
|
||||
json_string_t *string = malloc(sizeof(json_string_t));
|
||||
json_string_t *string;
|
||||
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
string = malloc(sizeof(json_string_t));
|
||||
if(!string)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&string->json, JSON_STRING);
|
||||
|
||||
string->value = strdup(value);
|
||||
if(!string->value) {
|
||||
free(string);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &string->json;
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
{
|
||||
if(!utf8_check_string(value, -1))
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
return NULL;
|
||||
|
||||
return json_string_nocheck(value);
|
||||
@@ -301,6 +508,25 @@ const char *json_string_value(const json_t *json)
|
||||
return json_to_string(json)->value;
|
||||
}
|
||||
|
||||
int json_string_set(const json_t *json, const char *value)
|
||||
{
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
|
||||
if(!json_is_string(json) || !value || !utf8_check_string(value, -1))
|
||||
return -1;
|
||||
|
||||
dup = strdup(value);
|
||||
if(!dup)
|
||||
return -1;
|
||||
|
||||
string = json_to_string(json);
|
||||
free(string->value);
|
||||
string->value = dup;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void json_delete_string(json_string_t *string)
|
||||
{
|
||||
free(string->value);
|
||||
@@ -314,7 +540,7 @@ json_t *json_integer(int value)
|
||||
{
|
||||
json_integer_t *integer = malloc(sizeof(json_integer_t));
|
||||
if(!integer)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&integer->json, JSON_INTEGER);
|
||||
|
||||
integer->value = value;
|
||||
@@ -329,6 +555,16 @@ int json_integer_value(const json_t *json)
|
||||
return json_to_integer(json)->value;
|
||||
}
|
||||
|
||||
int json_integer_set(const json_t *json, int value)
|
||||
{
|
||||
if(!json_is_integer(json))
|
||||
return -1;
|
||||
|
||||
json_to_integer(json)->value = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void json_delete_integer(json_integer_t *integer)
|
||||
{
|
||||
free(integer);
|
||||
@@ -341,7 +577,7 @@ json_t *json_real(double value)
|
||||
{
|
||||
json_real_t *real = malloc(sizeof(json_real_t));
|
||||
if(!real)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&real->json, JSON_REAL);
|
||||
|
||||
real->value = value;
|
||||
@@ -356,7 +592,17 @@ double json_real_value(const json_t *json)
|
||||
return json_to_real(json)->value;
|
||||
}
|
||||
|
||||
static void json_delete_real (json_real_t *real)
|
||||
int json_real_set(const json_t *json, double value)
|
||||
{
|
||||
if(!json_is_real(json))
|
||||
return 0;
|
||||
|
||||
json_to_real(json)->value = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void json_delete_real(json_real_t *real)
|
||||
{
|
||||
free(real);
|
||||
}
|
||||
@@ -381,9 +627,9 @@ json_t *json_true(void)
|
||||
{
|
||||
static json_t the_true = {
|
||||
.type = JSON_TRUE,
|
||||
.refcount = 1
|
||||
.refcount = (unsigned int)1
|
||||
};
|
||||
return json_incref(&the_true);
|
||||
return &the_true;
|
||||
}
|
||||
|
||||
|
||||
@@ -391,9 +637,9 @@ json_t *json_false(void)
|
||||
{
|
||||
static json_t the_false = {
|
||||
.type = JSON_FALSE,
|
||||
.refcount = 1
|
||||
.refcount = (unsigned int)1
|
||||
};
|
||||
return json_incref(&the_false);
|
||||
return &the_false;
|
||||
}
|
||||
|
||||
|
||||
@@ -401,9 +647,9 @@ json_t *json_null(void)
|
||||
{
|
||||
static json_t the_null = {
|
||||
.type = JSON_NULL,
|
||||
.refcount = 1
|
||||
.refcount = (unsigned int)1
|
||||
};
|
||||
return json_incref(&the_null);
|
||||
return &the_null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
2
test/.gitignore
vendored
2
test/.gitignore
vendored
@@ -3,5 +3,7 @@ loads_dumps
|
||||
load_file_dump_file
|
||||
testlogs
|
||||
testprogs/test_array
|
||||
testprogs/test_load
|
||||
testprogs/test_number
|
||||
testprogs/test_object
|
||||
testprogs/test_simple
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
import simplejson
|
||||
import sys
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
def load(filename):
|
||||
try:
|
||||
@@ -17,14 +20,14 @@ def load(filename):
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
json = simplejson.load(jsonfile)
|
||||
jsondata = json.load(jsonfile)
|
||||
except ValueError, err:
|
||||
print "%s is malformed: %s" % (filename, err)
|
||||
sys.exit(1)
|
||||
finally:
|
||||
jsonfile.close()
|
||||
|
||||
return json
|
||||
return jsondata
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
|
||||
@@ -26,8 +26,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
count = fread(buffer, 1, BUFFER_SIZE, stdin);
|
||||
if(count < 0 || count >= BUFFER_SIZE) {
|
||||
fprintf(stderr, "unable to read input\n");
|
||||
return 1;
|
||||
fprintf(stderr, "unable to read input\n");
|
||||
return 1;
|
||||
}
|
||||
buffer[count] = '\0';
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ for testfile in $TESTFILES; do
|
||||
tmpdir="testlogs/`basename $testfile`"
|
||||
rm -rf $tmpdir
|
||||
mkdir -p $tmpdir
|
||||
${srcdir}/split-testfile.py $testfile $tmpdir | while read name; do
|
||||
if echo "$testfile" | grep -q -E -e '-strip$'; then
|
||||
opts="--strip"
|
||||
fi
|
||||
${srcdir}/split-testfile.py $opts $testfile $tmpdir | while read name; do
|
||||
run_test loadf_dumpf $tmpdir/$name
|
||||
run_test loads_dumps $tmpdir/$name
|
||||
run_test load_file_dump_file $tmpdir/$name
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
def strip_file(filename):
|
||||
with open(filename) as fobj:
|
||||
data = fobj.read()
|
||||
with open(filename, 'w') as fobj:
|
||||
fobj.write(data.strip())
|
||||
|
||||
def open_files(outdir, i, name):
|
||||
basename = '%02d_%s' % (i, name)
|
||||
@@ -16,12 +23,17 @@ def open_files(outdir, i, name):
|
||||
return open(input_path, 'w'), open(output_path, 'w')
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print 'usage: %s input-file output-directory' % sys.argv[0]
|
||||
parser = OptionParser('usage: %prog [options] inputfile outputdir')
|
||||
parser.add_option('--strip', help='strip whitespace from input',
|
||||
action='store_true', default=False)
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if len(args) != 2:
|
||||
parser.print_help()
|
||||
return 2
|
||||
|
||||
infile = os.path.normpath(sys.argv[1])
|
||||
outdir = os.path.normpath(sys.argv[2])
|
||||
infile = os.path.normpath(args[0])
|
||||
outdir = os.path.normpath(args[1])
|
||||
|
||||
if not os.path.exists(outdir):
|
||||
print >>sys.stderr, 'output directory %r does not exist!' % outdir
|
||||
@@ -37,6 +49,8 @@ def main():
|
||||
if input is not None and output is not None:
|
||||
input.close()
|
||||
output.close()
|
||||
if options.strip:
|
||||
strip_file(input.name)
|
||||
input, output = open_files(outdir, n, line[5:line.find(' ====\n')])
|
||||
current = input
|
||||
elif line == '====\n':
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
TESTFILES="${srcdir}/testdata/invalid ${srcdir}/testdata/invalid-unicode"
|
||||
TESTFILES="${srcdir}/testdata/invalid ${srcdir}/testdata/invalid-strip ${srcdir}/testdata/invalid-unicode"
|
||||
|
||||
run_test() {
|
||||
local prog=$1
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
TESTFILES="${srcdir}/testdata/valid"
|
||||
TESTFILES="${srcdir}/testdata/valid ${srcdir}/testdata/valid-strip"
|
||||
|
||||
run_test() {
|
||||
local prog=$1
|
||||
|
||||
2
test/testdata/Makefile.am
vendored
2
test/testdata/Makefile.am
vendored
@@ -1 +1 @@
|
||||
EXTRA_DIST = invalid invalid-unicode valid
|
||||
EXTRA_DIST = invalid invalid-strip invalid-unicode valid valid-strip
|
||||
|
||||
42
test/testdata/invalid
vendored
42
test/testdata/invalid
vendored
@@ -127,6 +127,21 @@ invalid token near '1e'
|
||||
====
|
||||
1
|
||||
invalid token near '1e'
|
||||
==== real-positive-overflow ====
|
||||
[123123e100000]
|
||||
====
|
||||
1
|
||||
real number overflow near '123123e100000'
|
||||
==== real-negative-overflow ====
|
||||
[-123123e100000]
|
||||
====
|
||||
1
|
||||
real number overflow near '-123123e100000'
|
||||
==== real-underflow ====
|
||||
[123e-10000000]
|
||||
====
|
||||
1
|
||||
real number underflow near '123e-10000000'
|
||||
==== integer-starting-with-zero ====
|
||||
[012]
|
||||
====
|
||||
@@ -137,12 +152,37 @@ invalid token near '0'
|
||||
====
|
||||
1
|
||||
invalid token near '-0'
|
||||
==== too-big-positive-integer ====
|
||||
[123123123123123]
|
||||
====
|
||||
1
|
||||
too big integer near '123123123123123'
|
||||
==== too-big-negative-integer ====
|
||||
[-123123123123123]
|
||||
====
|
||||
1
|
||||
too big negative integer near '-123123123123123'
|
||||
==== invalid-identifier ====
|
||||
[troo
|
||||
====
|
||||
1
|
||||
invalid token near 'troo'
|
||||
==== invalid-escap ====
|
||||
==== minus-sign-without-number ====
|
||||
[-foo]
|
||||
====
|
||||
1
|
||||
invalid token near '-'
|
||||
==== invalid-negative-integerr ====
|
||||
[-123foo]
|
||||
====
|
||||
1
|
||||
']' expected near 'foo'
|
||||
==== invalid-negative-real ====
|
||||
[-123.123foo]
|
||||
====
|
||||
1
|
||||
']' expected near 'foo'
|
||||
==== invalid-escape ====
|
||||
["\a <-- invalid escape"]
|
||||
====
|
||||
1
|
||||
|
||||
235
test/testdata/invalid-strip
vendored
Normal file
235
test/testdata/invalid-strip
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
==== empty ====
|
||||
====
|
||||
1
|
||||
'[' or '{' expected near end of file
|
||||
==== null ====
|
||||
null
|
||||
====
|
||||
1
|
||||
'[' or '{' expected near 'null'
|
||||
==== lone-open-brace ====
|
||||
{
|
||||
====
|
||||
1
|
||||
string or '}' expected near end of file
|
||||
==== lone-open-bracket ====
|
||||
[
|
||||
====
|
||||
1
|
||||
']' expected near end of file
|
||||
==== bracket-comma ====
|
||||
[,
|
||||
====
|
||||
1
|
||||
unexpected token near ','
|
||||
==== bracket-one-comma ====
|
||||
[1,
|
||||
====
|
||||
1
|
||||
']' expected near end of file
|
||||
==== unterminated-string ====
|
||||
["a
|
||||
====
|
||||
1
|
||||
premature end of input near '"a'
|
||||
==== unterminated-array ====
|
||||
["a"
|
||||
====
|
||||
1
|
||||
']' expected near end of file
|
||||
==== apostrophe ====
|
||||
['
|
||||
====
|
||||
1
|
||||
invalid token near '''
|
||||
==== brace-comma ====
|
||||
{,
|
||||
====
|
||||
1
|
||||
string or '}' expected near ','
|
||||
==== unterminated-empty-key ====
|
||||
{"
|
||||
====
|
||||
1
|
||||
premature end of input near '"'
|
||||
==== unterminated-key ====
|
||||
{"a
|
||||
====
|
||||
1
|
||||
premature end of input near '"a'
|
||||
==== object-no-colon ====
|
||||
{"a"
|
||||
====
|
||||
1
|
||||
':' expected near end of file
|
||||
==== object-apostrophes ====
|
||||
{'a'
|
||||
====
|
||||
1
|
||||
string or '}' expected near '''
|
||||
==== object-no-value ====
|
||||
{"a":
|
||||
====
|
||||
1
|
||||
unexpected token near end of file
|
||||
==== object-unterminated-value ====
|
||||
{"a":"a
|
||||
====
|
||||
1
|
||||
premature end of input near '"a'
|
||||
==== object-garbage-at-end ====
|
||||
{"a":"a" 123}
|
||||
====
|
||||
1
|
||||
'}' expected near '123'
|
||||
==== unterminated-object-and-array ====
|
||||
{[
|
||||
====
|
||||
1
|
||||
string or '}' expected near '['
|
||||
==== unterminated-array-and-object ====
|
||||
[{
|
||||
====
|
||||
1
|
||||
string or '}' expected near end of file
|
||||
==== object-in-unterminated-array ====
|
||||
[{}
|
||||
====
|
||||
1
|
||||
']' expected near end of file
|
||||
==== extra-comma-in-array ====
|
||||
[1,]
|
||||
====
|
||||
1
|
||||
unexpected token near ']'
|
||||
==== extra-command-in-multiline-array ====
|
||||
[1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]
|
||||
====
|
||||
6
|
||||
unexpected token near ']'
|
||||
==== real-truncated-at-point ====
|
||||
[1.]
|
||||
====
|
||||
1
|
||||
invalid token near '1.'
|
||||
==== real-truncated-at-e ====
|
||||
[1e]
|
||||
====
|
||||
1
|
||||
invalid token near '1e'
|
||||
==== real-garbage-after-e ====
|
||||
[1ea]
|
||||
====
|
||||
1
|
||||
invalid token near '1e'
|
||||
==== real-positive-overflow ====
|
||||
[123123e100000]
|
||||
====
|
||||
1
|
||||
real number overflow near '123123e100000'
|
||||
==== real-negative-overflow ====
|
||||
[-123123e100000]
|
||||
====
|
||||
1
|
||||
real number overflow near '-123123e100000'
|
||||
==== real-underflow ====
|
||||
[123e-10000000]
|
||||
====
|
||||
1
|
||||
real number underflow near '123e-10000000'
|
||||
==== integer-starting-with-zero ====
|
||||
[012]
|
||||
====
|
||||
1
|
||||
invalid token near '0'
|
||||
==== negative-integer-starting-with-zero ====
|
||||
[-012]
|
||||
====
|
||||
1
|
||||
invalid token near '-0'
|
||||
==== too-big-positive-integer ====
|
||||
[123123123123123]
|
||||
====
|
||||
1
|
||||
too big integer near '123123123123123'
|
||||
==== too-big-negative-integer ====
|
||||
[-123123123123123]
|
||||
====
|
||||
1
|
||||
too big negative integer near '-123123123123123'
|
||||
==== invalid-identifier ====
|
||||
[troo
|
||||
====
|
||||
1
|
||||
invalid token near 'troo'
|
||||
==== minus-sign-without-number ====
|
||||
[-foo]
|
||||
====
|
||||
1
|
||||
invalid token near '-'
|
||||
==== invalid-negative-integerr ====
|
||||
[-123foo]
|
||||
====
|
||||
1
|
||||
']' expected near 'foo'
|
||||
==== invalid-negative-real ====
|
||||
[-123.123foo]
|
||||
====
|
||||
1
|
||||
']' expected near 'foo'
|
||||
==== invalid-escape ====
|
||||
["\a <-- invalid escape"]
|
||||
====
|
||||
1
|
||||
invalid escape near '"\'
|
||||
==== tab-character-in-string ====
|
||||
[" <-- tab character"]
|
||||
====
|
||||
1
|
||||
control character 0x9 near '"'
|
||||
==== null-byte-in-string ====
|
||||
["\u0000 (null byte not allowed)"]
|
||||
====
|
||||
1
|
||||
\u0000 is not allowed
|
||||
==== truncated-unicode-surrogate ====
|
||||
["\uDADA (first surrogate without the second)"]
|
||||
====
|
||||
1
|
||||
invalid Unicode '\uDADA'
|
||||
==== invalid-second-surrogate ====
|
||||
["\uD888\u3210 (first surrogate and invalid second surrogate)"]
|
||||
====
|
||||
1
|
||||
invalid Unicode '\uD888\u3210'
|
||||
==== lone-second-surrogate ====
|
||||
["\uDFAA (second surrogate on it's own)"]
|
||||
====
|
||||
1
|
||||
invalid Unicode '\uDFAA'
|
||||
==== unicode-identifier ====
|
||||
å
|
||||
====
|
||||
1
|
||||
'[' or '{' expected near 'å'
|
||||
==== ascii-unicode-identifier ====
|
||||
aå
|
||||
====
|
||||
1
|
||||
'[' or '{' expected near 'a'
|
||||
==== garbage-at-the-end ====
|
||||
[1,2,3]foo
|
||||
====
|
||||
1
|
||||
end of file expected near 'foo'
|
||||
==== garbage-after-newline ====
|
||||
[1,2,3]
|
||||
foo
|
||||
====
|
||||
2
|
||||
end of file expected near 'foo'
|
||||
68
test/testdata/valid-strip
vendored
Normal file
68
test/testdata/valid-strip
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
==== empty-string ====
|
||||
[""]
|
||||
==== short-string ====
|
||||
["a"]
|
||||
==== simple-ascii-string ====
|
||||
["abcdefghijklmnopqrstuvwxyz1234567890 "]
|
||||
==== utf-8-string ====
|
||||
["€þıœəßð some utf-8 ĸʒ×ŋµåäö𝄞"]
|
||||
==== string-escapes ====
|
||||
["\"\\\/\b\f\n\r\t"]
|
||||
==== one-byte-utf-8 ====
|
||||
["\u002c one-byte UTF-8"]
|
||||
==== two-byte-utf-8 ====
|
||||
["\u0123 two-byte UTF-8"]
|
||||
==== three-byte-utf-8 ====
|
||||
["\u0821 three-byte UTF-8"]
|
||||
==== utf-surrogate-four-byte-encoding ====
|
||||
["\uD834\uDD1E surrogate, four-byte UTF-8"]
|
||||
==== escaped-utf-control-char ====
|
||||
["\u0012 escaped control character"]
|
||||
==== simple-int-0 ====
|
||||
[0]
|
||||
==== simple-int-1 ====
|
||||
[1]
|
||||
==== simple-int-123 ====
|
||||
[123]
|
||||
==== negative-zero ====
|
||||
[-0]
|
||||
==== negative-one ====
|
||||
[-1]
|
||||
==== negative-int ====
|
||||
[-123]
|
||||
==== simple-real ====
|
||||
[123.456789]
|
||||
==== real-exponent ====
|
||||
[123e45]
|
||||
==== real-capital-e ====
|
||||
[1E22]
|
||||
==== real-positive-exponent ====
|
||||
[1e+2]
|
||||
==== real-negative-exponent ====
|
||||
[1e-2]
|
||||
==== real-capital-e-positive-exponent ====
|
||||
[1E+2]
|
||||
==== real-capital-e-negative-exponent ====
|
||||
[1E-2]
|
||||
==== real-fraction-exponent ====
|
||||
[123.456e78]
|
||||
==== true ====
|
||||
[true]
|
||||
==== false ====
|
||||
[false]
|
||||
==== null ====
|
||||
[null]
|
||||
==== empty-array ====
|
||||
[]
|
||||
==== empty-object-in-array ====
|
||||
[{}]
|
||||
==== complex-array ====
|
||||
[1,2,3,4,
|
||||
"a", "b", "c",
|
||||
{"foo": "bar", "core": "dump"},
|
||||
true, false, true, true, null, false
|
||||
]
|
||||
==== empty-object ====
|
||||
{}
|
||||
==== simple-object ====
|
||||
{"a":[]}
|
||||
@@ -1,8 +1,10 @@
|
||||
check_PROGRAMS = test_array test_number test_object
|
||||
check_PROGRAMS = test_array test_load test_simple test_number test_object
|
||||
|
||||
test_array_SOURCES = test_array.c util.h
|
||||
test_load_SOURCES = test_load.c util.h
|
||||
test_simple_SOURCES = test_simple.c util.h
|
||||
test_number_SOURCES = test_number.c util.h
|
||||
test_object_SOURCES = test_number.c util.h
|
||||
test_object_SOURCES = test_object.c util.h
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void test_misc(void)
|
||||
{
|
||||
json_t *array, *five, *seven, *value;
|
||||
int i;
|
||||
@@ -19,14 +19,15 @@ int main()
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five)
|
||||
fail("unable to create integer");
|
||||
if(!seven)
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("empty array has nonzero size");
|
||||
|
||||
if(!json_array_append(array, NULL))
|
||||
fail("able to append NULL");
|
||||
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
|
||||
@@ -54,6 +55,9 @@ int main()
|
||||
if(json_array_set(array, 0, seven))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_array_set(array, 0, NULL))
|
||||
fail("able to set NULL");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("wrong array size");
|
||||
|
||||
@@ -85,9 +89,312 @@ int main()
|
||||
fail("got wrong value");
|
||||
}
|
||||
|
||||
if(json_array_set_new(array, 15, json_integer(123)))
|
||||
fail("unable to set new value");
|
||||
|
||||
value = json_array_get(array, 15);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_array_set_new works incorrectly");
|
||||
|
||||
if(!json_array_set_new(array, 15, NULL))
|
||||
fail("able to set_new NULL value");
|
||||
|
||||
if(json_array_append_new(array, json_integer(321)))
|
||||
fail("unable to append new value");
|
||||
|
||||
value = json_array_get(array, json_array_size(array) - 1);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 321)
|
||||
fail("json_array_append_new works incorrectly");
|
||||
|
||||
if(!json_array_append_new(array, NULL))
|
||||
fail("able to append_new NULL value");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_insert(void)
|
||||
{
|
||||
json_t *array, *five, *seven, *eleven, *value;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
eleven = json_integer(11);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven || !eleven)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
if(!json_array_insert(array, 1, five))
|
||||
fail("able to insert value out of bounds");
|
||||
|
||||
|
||||
if(json_array_insert(array, 0, five))
|
||||
fail("unable to insert value in an empty array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 1)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert(array, 1, seven))
|
||||
fail("unable to insert value at the end of an array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 1) != seven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert(array, 1, eleven))
|
||||
fail("unable to insert value in the middle of an array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 1) != eleven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 2) != seven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 3)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert_new(array, 2, json_integer(123)))
|
||||
fail("unable to insert value in the middle of an array");
|
||||
|
||||
value = json_array_get(array, 2);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_array_insert_new works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 4)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
for(i = 0; i < 20; i++) {
|
||||
if(json_array_insert(array, 0, seven))
|
||||
fail("unable to insert value at the begining of an array");
|
||||
}
|
||||
|
||||
for(i = 0; i < 20; i++) {
|
||||
if(json_array_get(array, i) != seven)
|
||||
fail("json_aray_insert works incorrectly");
|
||||
}
|
||||
|
||||
if(json_array_size(array) != 24)
|
||||
fail("array size is invalid after loop insertion");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(eleven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_remove(void)
|
||||
{
|
||||
json_t *array, *five, *seven;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five)
|
||||
fail("unable to create integer");
|
||||
if(!seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
if(!json_array_remove(array, 0))
|
||||
fail("able to remove an unexisting index");
|
||||
|
||||
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
|
||||
if(!json_array_remove(array, 1))
|
||||
fail("able to remove an unexisting index");
|
||||
|
||||
if(json_array_remove(array, 0))
|
||||
fail("unable to remove");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("array size is invalid after removing");
|
||||
|
||||
|
||||
if(json_array_append(array, five) ||
|
||||
json_array_append(array, seven) ||
|
||||
json_array_append(array, five) ||
|
||||
json_array_append(array, seven))
|
||||
fail("unable to append");
|
||||
|
||||
if(json_array_remove(array, 2))
|
||||
fail("unable to remove");
|
||||
|
||||
if(json_array_size(array) != 3)
|
||||
fail("array size is invalid after removing");
|
||||
|
||||
if(json_array_get(array, 0) != five ||
|
||||
json_array_get(array, 1) != seven ||
|
||||
json_array_get(array, 2) != seven)
|
||||
fail("remove works incorrectly");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_clear(void)
|
||||
{
|
||||
json_t *array, *five, *seven;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
}
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array, seven))
|
||||
fail("unable to append");
|
||||
}
|
||||
|
||||
if(json_array_size(array) != 20)
|
||||
fail("array size is invalid after appending");
|
||||
|
||||
if(json_array_clear(array))
|
||||
fail("unable to clear");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("array size is invalid after clearing");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_extend(void)
|
||||
{
|
||||
json_t *array1, *array2, *five, *seven;
|
||||
int i;
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array1, five))
|
||||
fail("unable to append");
|
||||
}
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array2, seven))
|
||||
fail("unable to append");
|
||||
}
|
||||
|
||||
if(json_array_size(array1) != 10 || json_array_size(array2) != 10)
|
||||
fail("array size is invalid after appending");
|
||||
|
||||
if(json_array_extend(array1, array2))
|
||||
fail("unable to extend");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_get(array1, i) != five)
|
||||
fail("invalid array contents after extending");
|
||||
}
|
||||
for(i = 10; i < 20; i++) {
|
||||
if(json_array_get(array1, i) != seven)
|
||||
fail("invalid array contents after extending");
|
||||
}
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
}
|
||||
|
||||
static void test_circular()
|
||||
{
|
||||
json_t *array1, *array2;
|
||||
|
||||
/* the simple cases are checked */
|
||||
|
||||
array1 = json_array();
|
||||
if(!array1)
|
||||
fail("unable to create array");
|
||||
|
||||
if(json_array_append(array1, array1) == 0)
|
||||
fail("able to append self");
|
||||
|
||||
if(json_array_insert(array1, 0, array1) == 0)
|
||||
fail("able to insert self");
|
||||
|
||||
if(json_array_append_new(array1, json_true()))
|
||||
fail("failed to append true");
|
||||
|
||||
if(json_array_set(array1, 0, array1) == 0)
|
||||
fail("able to set self");
|
||||
|
||||
json_decref(array1);
|
||||
|
||||
|
||||
/* create circular references */
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create array");
|
||||
|
||||
if(json_array_append(array1, array2) ||
|
||||
json_array_append(array2, array1))
|
||||
fail("unable to append");
|
||||
|
||||
/* circularity is detected when dumping */
|
||||
if(json_dumps(array1, 0) != NULL)
|
||||
fail("able to dump circulars");
|
||||
|
||||
/* decref twice to deal with the circular references */
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
json_decref(array1);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
test_misc();
|
||||
test_insert();
|
||||
test_remove();
|
||||
test_clear();
|
||||
test_extend();
|
||||
test_circular();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
24
test/testprogs/test_load.c
Normal file
24
test/testprogs/test_load.c
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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"
|
||||
|
||||
int main()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_load_file("/path/to/nonexistent/file.json", &error);
|
||||
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;
|
||||
}
|
||||
@@ -6,11 +6,171 @@
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void test_clear()
|
||||
{
|
||||
json_t *object, *ten;
|
||||
|
||||
object = json_object();
|
||||
ten = json_integer(10);
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!ten)
|
||||
fail("unable to create integer");
|
||||
|
||||
if(json_object_set(object, "a", ten) ||
|
||||
json_object_set(object, "b", ten) ||
|
||||
json_object_set(object, "c", ten) ||
|
||||
json_object_set(object, "d", ten) ||
|
||||
json_object_set(object, "e", ten))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size");
|
||||
|
||||
json_object_clear(object);
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("invalid size after clear");
|
||||
|
||||
json_decref(ten);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_update()
|
||||
{
|
||||
json_t *object, *other, *nine, *ten;
|
||||
|
||||
object = json_object();
|
||||
other = json_object();
|
||||
|
||||
nine = json_integer(9);
|
||||
ten = json_integer(10);
|
||||
|
||||
if(!object || !other)
|
||||
fail("unable to create object");
|
||||
if(!nine || !ten)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
/* update an empty object with an empty object */
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an emtpy object with an empty object");
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_size(other) != 0)
|
||||
fail("invalid size for updater after update");
|
||||
|
||||
|
||||
/* update an empty object with a nonempty object */
|
||||
|
||||
if(json_object_set(other, "a", ten) ||
|
||||
json_object_set(other, "b", ten) ||
|
||||
json_object_set(other, "c", ten) ||
|
||||
json_object_set(other, "d", ten) ||
|
||||
json_object_set(other, "e", ten))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an empty object");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != ten ||
|
||||
json_object_get(object, "b") != ten ||
|
||||
json_object_get(object, "c") != ten ||
|
||||
json_object_get(object, "d") != ten ||
|
||||
json_object_get(object, "e") != ten)
|
||||
fail("update works incorrectly");
|
||||
|
||||
|
||||
/* perform the same update again */
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an empty object");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != ten ||
|
||||
json_object_get(object, "b") != ten ||
|
||||
json_object_get(object, "c") != ten ||
|
||||
json_object_get(object, "d") != ten ||
|
||||
json_object_get(object, "e") != ten)
|
||||
fail("update works incorrectly");
|
||||
|
||||
|
||||
/* update a nonempty object with a nonempty object with both old
|
||||
and new keys */
|
||||
|
||||
if(json_object_clear(other))
|
||||
fail("clear failed");
|
||||
|
||||
if(json_object_set(other, "a", nine) ||
|
||||
json_object_set(other, "b", nine) ||
|
||||
json_object_set(other, "f", nine) ||
|
||||
json_object_set(other, "g", nine) ||
|
||||
json_object_set(other, "h", nine))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update a nonempty object");
|
||||
|
||||
if(json_object_size(object) != 8)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != nine ||
|
||||
json_object_get(object, "b") != nine ||
|
||||
json_object_get(object, "f") != nine ||
|
||||
json_object_get(object, "g") != nine ||
|
||||
json_object_get(object, "h") != nine)
|
||||
fail("update works incorrectly");
|
||||
|
||||
json_decref(nine);
|
||||
json_decref(ten);
|
||||
json_decref(other);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_circular()
|
||||
{
|
||||
json_t *object1, *object2;
|
||||
|
||||
object1 = json_object();
|
||||
object2 = json_object();
|
||||
if(!object1 || !object2)
|
||||
fail("unable to create object");
|
||||
|
||||
/* the simple case is checked */
|
||||
if(json_object_set(object1, "a", object1) == 0)
|
||||
fail("able to set self");
|
||||
|
||||
/* create circular references */
|
||||
if(json_object_set(object1, "a", object2) ||
|
||||
json_object_set(object2, "a", object1))
|
||||
fail("unable to set value");
|
||||
|
||||
/* circularity is detected when dumping */
|
||||
if(json_dumps(object1, 0) != NULL)
|
||||
fail("able to dump circulars");
|
||||
|
||||
/* decref twice to deal with the circular references */
|
||||
json_decref(object1);
|
||||
json_decref(object2);
|
||||
json_decref(object1);
|
||||
}
|
||||
|
||||
static void test_misc()
|
||||
{
|
||||
json_t *object, *string, *other_string, *value;
|
||||
void *iter;
|
||||
|
||||
object = json_object();
|
||||
string = json_string("test");
|
||||
@@ -18,9 +178,7 @@ int main()
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!string)
|
||||
fail("unable to create string");
|
||||
if(!other_string)
|
||||
if(!string || !other_string)
|
||||
fail("unable to create string");
|
||||
|
||||
if(json_object_get(object, "a"))
|
||||
@@ -29,6 +187,23 @@ int main()
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_object_set(object, NULL, string))
|
||||
fail("able to set NULL key");
|
||||
|
||||
if(!json_object_set(object, "a", NULL))
|
||||
fail("able to set NULL value");
|
||||
|
||||
iter = json_object_iter(object);
|
||||
if(!iter)
|
||||
fail("unable to get iterator");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "a"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != string)
|
||||
fail("iterating failed: wrong value");
|
||||
if(json_object_iter_next(object, iter) != NULL)
|
||||
fail("able to iterate over the end");
|
||||
|
||||
/* invalid UTF-8 in key */
|
||||
if(!json_object_set(object, "a\xefz", string))
|
||||
fail("able to set invalid unicode key");
|
||||
@@ -39,7 +214,7 @@ int main()
|
||||
if(value != string)
|
||||
fail("got different value than what was added");
|
||||
|
||||
/* "a", "lp" and "px" collide with a five-bucket hashtable */
|
||||
/* "a", "lp" and "px" collide in a five-bucket hashtable */
|
||||
if(json_object_set(object, "b", string) ||
|
||||
json_object_set(object, "lp", string) ||
|
||||
json_object_set(object, "px", string))
|
||||
@@ -73,7 +248,7 @@ int main()
|
||||
fail("unable to delete an existing key");
|
||||
|
||||
|
||||
/* add many keys to rehashing */
|
||||
/* add many keys to initiate rehashing */
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
@@ -93,9 +268,31 @@ int main()
|
||||
if(json_object_set(object, "e", string))
|
||||
fail("unable to set value");
|
||||
|
||||
|
||||
if(json_object_set_new(object, "foo", json_integer(123)))
|
||||
fail("unable to set new value");
|
||||
|
||||
value = json_object_get(object, "foo");
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_object_set_new works incorrectly");
|
||||
|
||||
if(!json_object_set_new(object, NULL, json_integer(432)))
|
||||
fail("able to set_new NULL key");
|
||||
|
||||
if(!json_object_set_new(object, "foo", NULL))
|
||||
fail("able to set_new NULL value");
|
||||
|
||||
json_decref(string);
|
||||
json_decref(other_string);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_misc();
|
||||
test_clear();
|
||||
test_update();
|
||||
test_circular();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
127
test/testprogs/test_simple.c
Normal file
127
test/testprogs/test_simple.c
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <string.h>
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
/* Call the simple functions not covered by other tests of the public API */
|
||||
int main()
|
||||
{
|
||||
json_t *value;
|
||||
|
||||
value = json_integer(1);
|
||||
if(json_typeof(value) != JSON_INTEGER)
|
||||
fail("json_typeof failed");
|
||||
|
||||
if(json_is_object(value))
|
||||
fail("json_is_object failed");
|
||||
|
||||
if(json_is_array(value))
|
||||
fail("json_is_array failed");
|
||||
|
||||
if(json_is_string(value))
|
||||
fail("json_is_string failed");
|
||||
|
||||
if(!json_is_integer(value))
|
||||
fail("json_is_integer failed");
|
||||
|
||||
if(json_is_real(value))
|
||||
fail("json_is_real failed");
|
||||
|
||||
if(!json_is_number(value))
|
||||
fail("json_is_number failed");
|
||||
|
||||
if(json_is_true(value))
|
||||
fail("json_is_true failed");
|
||||
|
||||
if(json_is_false(value))
|
||||
fail("json_is_false failed");
|
||||
|
||||
if(json_is_boolean(value))
|
||||
fail("json_is_boolean failed");
|
||||
|
||||
if(json_is_null(value))
|
||||
fail("json_is_null failed");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
|
||||
value = json_string("foo");
|
||||
if(!value)
|
||||
fail("json_string failed");
|
||||
if(strcmp(json_string_value(value), "foo"))
|
||||
fail("invalid string value");
|
||||
|
||||
if(json_string_set(value, "bar"))
|
||||
fail("json_string_set failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
fail("invalid string value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_string(NULL);
|
||||
if(value)
|
||||
fail("json_string(NULL) failed");
|
||||
|
||||
/* invalid UTF-8 */
|
||||
value = json_string("a\xefz");
|
||||
if(value)
|
||||
fail("json_string(<invalid utf-8>) failed");
|
||||
|
||||
|
||||
value = json_integer(123);
|
||||
if(!value)
|
||||
fail("json_integer failed");
|
||||
if(json_integer_value(value) != 123)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 123.0)
|
||||
fail("invalid number value");
|
||||
|
||||
if(json_integer_set(value, 321))
|
||||
fail("json_integer_set failed");
|
||||
if(json_integer_value(value) != 321)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 321.0)
|
||||
fail("invalid number value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_real(123.123);
|
||||
if(!value)
|
||||
fail("json_real failed");
|
||||
if(json_real_value(value) != 123.123)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 123.123)
|
||||
fail("invalid number value");
|
||||
|
||||
if(json_real_set(value, 321.321))
|
||||
fail("json_real_set failed");
|
||||
if(json_real_value(value) != 321.321)
|
||||
fail("invalid real value");
|
||||
if(json_number_value(value) != 321.321)
|
||||
fail("invalid number value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_true();
|
||||
if(!value)
|
||||
fail("json_true failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_false();
|
||||
if(!value)
|
||||
fail("json_false failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_null();
|
||||
if(!value)
|
||||
fail("json_null failed");
|
||||
json_decref(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -8,10 +8,13 @@
|
||||
#ifndef TESTPROGS_UTIL_H
|
||||
#define TESTPROGS_UTIL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define fail(msg) \
|
||||
do { \
|
||||
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \
|
||||
return 1; \
|
||||
fprintf(stderr, "%s:%s:%d: %s\n", \
|
||||
__FILE__, __FUNCTION__, __LINE__, msg); \
|
||||
exit(1); \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user