Merge commit 'v2.4' into HEAD
authorSam hartman <hartmans@painless-security.com>
Wed, 17 Apr 2013 11:26:04 +0000 (12:26 +0100)
committerSam hartman <hartmans@painless-security.com>
Wed, 17 Apr 2013 11:26:04 +0000 (12:26 +0100)
Conflicts:
Makefile.am

78 files changed:
.gitignore
CHANGES
LICENSE
Makefile.am
README.rst
configure.ac
doc/Makefile.am
doc/apiref.rst
doc/conf.py
doc/conformance.rst
doc/ext/refcounting.py
doc/gettingstarted.rst
doc/github_commits.c
doc/index.rst
doc/portability.rst [new file with mode: 0644]
doc/tutorial.rst
src/Makefile.am
src/dump.c
src/error.c
src/hashtable.c
src/hashtable.h
src/jansson.def [new file with mode: 0644]
src/jansson.h
src/jansson_config.h.in
src/jansson_private.h
src/load.c
src/memory.c
src/pack_unpack.c
src/strbuffer.c
src/strbuffer.h
src/strconv.c [new file with mode: 0644]
src/utf.c
src/utf.h
src/value.c
test/.gitignore
test/bin/json_process.c
test/run-suites
test/scripts/run-tests.sh
test/scripts/valgrind.sh
test/suites/api/Makefile.am
test/suites/api/check-exports
test/suites/api/run
test/suites/api/test_array.c
test/suites/api/test_copy.c
test/suites/api/test_dump.c
test/suites/api/test_dump_callback.c [new file with mode: 0644]
test/suites/api/test_equal.c
test/suites/api/test_load.c
test/suites/api/test_load_callback.c [new file with mode: 0644]
test/suites/api/test_loadb.c [new file with mode: 0644]
test/suites/api/test_memory_funcs.c
test/suites/api/test_number.c
test/suites/api/test_object.c
test/suites/api/test_pack.c
test/suites/api/test_simple.c
test/suites/api/test_unpack.c
test/suites/api/util.h
test/suites/encoding-flags/compact-array/env
test/suites/encoding-flags/compact-object/env
test/suites/encoding-flags/ensure-ascii/env
test/suites/encoding-flags/indent-array/env
test/suites/encoding-flags/indent-compact-array/env
test/suites/encoding-flags/indent-compact-object/env
test/suites/encoding-flags/indent-object/env
test/suites/encoding-flags/preserve-order/env
test/suites/encoding-flags/run
test/suites/encoding-flags/sort-keys/env
test/suites/invalid-unicode/run
test/suites/invalid/run
test/suites/valid/real-capital-e/output
test/suites/valid/real-exponent/output
test/suites/valid/real-fraction-exponent/output
test/suites/valid/run
win32/jansson_config.h [moved from src/jansson_config.h.win32 with 69% similarity]
win32/vs2010/jansson.sln [new file with mode: 0644]
win32/vs2010/jansson.vcxproj [new file with mode: 0644]
win32/vs2010/jansson.vcxproj.filters [new file with mode: 0644]
win32/vs2010/jansson.vcxproj.user [new file with mode: 0644]

index 3b4bffe..9189a93 100644 (file)
@@ -25,3 +25,4 @@ stamp-h1
 *.pyc
 *.pc
 /src/jansson_config.h
+*.exe
diff --git a/CHANGES b/CHANGES
index d68018b..4c7cccc 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,232 @@
+Version 2.4
+===========
+
+Released 2012-09-23
+
+* New features:
+
+  - Add `json_boolean()` macro that returns the JSON true or false
+    value based on its argument (#86).
+
+  - Add `json_load_callback()` that calls a callback function
+    repeatedly to read the JSON input (#57).
+
+  - Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of
+    ``/`` with ``\/``.
+
+* Bug fixes:
+
+  - Check for and reject NaN and Inf values for reals. Encoding these
+    values resulted in invalid JSON.
+
+  - Fix `json_real_set()` to return -1 on error.
+
+* Build:
+
+  - Jansson now builds on Windows with Visual Studio 2010, and
+    includes solution and project files in ``win32/vs2010/``
+    directory.
+
+  - Fix build warnings (#77, #78).
+
+  - Add ``-no-undefined`` to LDFLAGS (#90).
+
+* Tests:
+
+  - Fix the symbol exports test on Linux/PPC64 (#88).
+
+* Documentation:
+
+  - Fix typos (#73, #84).
+
+
+Version 2.3.1
+=============
+
+Released 2012-04-20
+
+* Build issues:
+
+  - Only use ``long long`` if ``strtoll()`` is also available.
+
+* Documentation:
+
+  - Fix the names of library version constants in documentation. (#52)
+
+  - Change the tutorial to use GitHub API v3. (#65)
+
+* Tests:
+
+  - Make some tests locale independent. (#51)
+
+  - Distribute the library exports test in the tarball.
+
+  - Make test run on shells that don't support the ``export FOO=bar``
+    syntax.
+
+
+Version 2.3
+===========
+
+Released 2012-01-27
+
+* New features:
+
+  - `json_unpack()` and friends: Add support for optional object keys
+    with the ``{s?o}`` syntax.
+
+  - Add `json_object_update_existing()` and
+    `json_object_update_missing()`, for updating only existing keys or
+    only adding missing keys to an object. (#37)
+
+  - Add `json_object_foreach()` for more convenient iteration over
+    objects. (#45, #46)
+
+  - When decoding JSON, write the number of bytes that were read from
+    input to ``error.position`` also on success. This is handy with
+    ``JSON_DISABLE_EOF_CHECK``.
+
+  - Add support for decoding any JSON value, not just arrays or
+    objects. The support is enabled with the new ``JSON_DECODE_ANY``
+    flag. Patch by Andrea Marchesini. (#4)
+
+* Bug fixes
+
+  - Avoid problems with object's serial number growing too big. (#40,
+    #41)
+
+  - Decoding functions now return NULL if the first argument is NULL.
+    Patch by Andrea Marchesini.
+
+  - Include ``jansson_config.h.win32`` in the distribution tarball.
+
+  - Remove ``+`` and leading zeros from exponents in the encoder.
+    (#39)
+
+  - Make Jansson build and work on MinGW. (#39, #38)
+
+* Documentation
+
+  - Note that the same JSON values must not be encoded in parallel by
+    separate threads. (#42)
+
+  - Document MinGW support.
+
+
+Version 2.2.1
+=============
+
+Released 2011-10-06
+
+* Bug fixes:
+
+  - Fix real number encoding and decoding under non-C locales. (#32)
+
+  - Fix identifier decoding under non-UTF-8 locales. (#35)
+
+  - `json_load_file()`: Open the input file in binary mode for maximum
+    compatiblity.
+
+* Documentation:
+
+  - Clarify the lifecycle of the result of the ``s`` fromat of
+    `json_unpack()`. (#31)
+
+  - Add some portability info. (#36)
+
+  - Little clarifications here and there.
+
+* Other:
+
+  - Some style fixes, issues detected by static analyzers.
+
+
+Version 2.2
+===========
+
+Released 2011-09-03
+
+* New features:
+
+  - `json_dump_callback()`: Pass the encoder output to a callback
+    function in chunks.
+
+* Bug fixes:
+
+  - `json_string_set()`: Check that target is a string and value is
+    not NULL.
+
+* Other:
+
+  - Documentation typo fixes and clarifications.
+
+
+Version 2.1
+===========
+
+Released 2011-06-10
+
+* New features:
+
+  - `json_loadb()`: Decode a string with a given size, useful if the
+    string is not null terminated.
+
+  - Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
+    value. By default, only arrays and objects can be encoded. (#19)
+
+  - Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
+    error if any JSON object in the input contins duplicate keys. (#3)
+
+  - Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
+    valid JSON input. This allows other data after the JSON data.
+
+* Bug fixes:
+
+  - Fix an additional memory leak when memory allocation fails in
+    `json_object_set()` and friends.
+
+  - Clear errno before calling `strtod()` for better portability. (#27)
+
+* Building:
+
+  - Avoid set-but-not-used warning/error in a test. (#20)
+
+* Other:
+
+  - Minor clarifications to documentation.
+
+
+Version 2.0.1
+=============
+
+Released 2011-03-31
+
+* Bug fixes:
+
+  - Replace a few `malloc()` and `free()` calls with their
+    counterparts that support custom memory management.
+
+  - Fix object key hashing in json_unpack() strict checking mode.
+
+  - Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
+
+  - Fix `json_object_size()` return value.
+
+  - Fix a few compilation issues.
+
+* Portability:
+
+  - Enhance portability of `va_copy()`.
+
+  - Test framework portability enhancements.
+
+* Documentation:
+
+  - Distribute ``doc/upgrading.rst`` with the source tarball.
+
+  - Build documentation in strict mode in ``make distcheck``.
+
+
 Version 2.0
 ===========
 
diff --git a/LICENSE b/LICENSE
index 6d70a1b..dfa8686 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
index 7e962db..5b4fb98 100644 (file)
@@ -1,4 +1,5 @@
-EXTRA_DIST = CHANGES LICENSE README.rst jansson.spec
+EXTRA_DIST = CHANGES LICENSE README.rst win32 jansson.spec
+
 SUBDIRS = doc src test
 
 # "make distcheck" builds the dvi target, so use it to check that the
index cbb171f..4607125 100644 (file)
@@ -33,8 +33,8 @@ To run the test suite, invoke::
    $ make check
 
 If the source has been checked out from a Git repository, the
-./configure script has to be generated fist. The easiest way is to use
-autoreconf::
+./configure script has to be generated first. The easiest way is to
+use autoreconf::
 
    $ autoreconf -i
 
index 6ff975e..3b595a4 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ([2.60])
-AC_INIT([jansson], [2.0], [petri@digip.org])
+AC_INIT([jansson], [2.4], [petri@digip.org])
 
 AM_INIT_AUTOMAKE([1.10 foreign])
 
@@ -14,16 +14,11 @@ AM_CONDITIONAL([GCC], [test x$GCC = xyes])
 # Checks for libraries.
 
 # Checks for header files.
+AC_CHECK_HEADERS([locale.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_TYPE_INT32_T
-
 AC_TYPE_LONG_LONG_INT
-case $ac_cv_type_long_long_int in
-     yes) json_have_long_long=1;;
-     *) json_have_long_long=0;;
-esac
-AC_SUBST([json_have_long_long])
 
 AC_C_INLINE
 case $ac_cv_c_inline in
@@ -34,6 +29,19 @@ esac
 AC_SUBST([json_inline])
 
 # Checks for library functions.
+AC_CHECK_FUNCS([strtoll localeconv])
+
+case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
+     yesyes) json_have_long_long=1;;
+     *) json_have_long_long=0;;
+esac
+AC_SUBST([json_have_long_long])
+
+case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
+     yesyes) json_have_localeconv=1;;
+     *) json_have_localeconv=0;;
+esac
+AC_SUBST([json_have_localeconv])
 
 AC_CONFIG_FILES([
         jansson.pc
index 6c79b54..5069623 100644 (file)
@@ -1,6 +1,6 @@
 EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst            \
-       gettingstarted.rst github_commits.c index.rst tutorial.rst      \
-       upgrading.rst ext/refcounting.py
+       gettingstarted.rst github_commits.c index.rst portability.rst   \
+       tutorial.rst upgrading.rst ext/refcounting.py
 
 SPHINXBUILD = sphinx-build
 SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
index 91239c9..6defc3a 100644 (file)
@@ -41,7 +41,7 @@ set to zero.
 The following preprocessor constants specify the current version of
 the library:
 
-``JANSSON_VERSION_MAJOR``, ``JANSSON_VERSION_MINOR``, ``JANSSON_VERSION_MICRO``
+``JANSSON_MAJOR_VERSION``, ``JANSSON_MINOR_VERSION``, ``JANSSON_MICRO_VERSION``
   Integers specifying the major, minor and micro versions,
   respectively.
 
@@ -255,8 +255,8 @@ 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.
+These three values are implemented as singletons, so the returned
+pointers won't change between invocations of these functions.
 
 .. function:: json_t *json_true(void)
 
@@ -270,6 +270,17 @@ returns the same value each time.
 
    Returns the JSON false value.
 
+.. function:: json_t *json_boolean(val)
+
+   .. refcounting:: new
+
+   Returns JSON false if ``val`` is zero, and JSON true otherwise.
+   This is a macro, and equivalent to ``val ? json_true() :
+   json_false()``.
+
+   .. versionadded:: 2.4
+
+
 .. function:: json_t *json_null(void)
 
    .. refcounting:: new
@@ -481,12 +492,13 @@ A JSON array is an ordered collection of other JSON values.
 
    Removes the element in *array* at position *index*, shifting the
    elements after *index* one position towards the start of the array.
-   Returns 0 on success and -1 on error.
+   Returns 0 on success and -1 on error. The reference count of the
+   removed value is decremented.
 
 .. function:: int json_array_clear(json_t *array)
 
    Removes all elements from *array*. Returns 0 on sucess and -1 on
-   error.
+   error. The reference count of all removed values are decremented.
 
 .. function:: int json_array_extend(json_t *array, json_t *other_array)
 
@@ -549,21 +561,70 @@ Unicode string and the value is any JSON value.
 .. function:: int json_object_del(json_t *object, const char *key)
 
    Delete *key* from *object* if it exists. Returns 0 on success, or
-   -1 if *key* was not found.
-
+   -1 if *key* was not found. The reference count of the removed value
+   is decremented.
 
 .. function:: int json_object_clear(json_t *object)
 
    Remove all elements from *object*. Returns 0 on success and -1 if
-   *object* is not a JSON object.
+   *object* is not a JSON object. The reference count of all removed
+   values are decremented.
 
 .. function:: int json_object_update(json_t *object, json_t *other)
 
    Update *object* with the key-value pairs from *other*, overwriting
    existing keys. Returns 0 on success or -1 on error.
 
+.. function:: int json_object_update_existing(json_t *object, json_t *other)
+
+   Like :func:`json_object_update()`, but only the values of existing
+   keys are updated. No new keys are created. Returns 0 on success or
+   -1 on error.
+
+   .. versionadded:: 2.3
+
+.. function:: int json_object_update_missing(json_t *object, json_t *other)
+
+   Like :func:`json_object_update()`, but only new keys are created.
+   The value of any existing key is not changed. Returns 0 on success
+   or -1 on error.
+
+   .. versionadded:: 2.3
+
+The following macro can be used to iterate through all key-value pairs
+in an object.
+
+.. function:: json_object_foreach(object, key, value)
+
+   Iterate over every key-value pair of ``object``, running the block
+   of code that follows each time with the proper values set to
+   variables ``key`` and ``value``, of types :type:`const char *` and
+   :type:`json_t *` respectively. Example::
+
+       /* obj is a JSON object */
+       const char *key;
+       json_t *value;
+
+       json_object_foreach(obj, key, value) {
+           /* block of code that uses key and value */
+       }
+
+   The items are not returned in any particular order.
+
+   This macro expands to an ordinary ``for`` statement upon
+   preprocessing, so its performance is equivalent to that of
+   hand-written iteration code using the object iteration protocol
+   (see below). The main advantage of this macro is that it abstracts
+   away the complexity behind iteration, and makes for shorter, more
+   concise code.
+
+   .. versionadded:: 2.3
 
-The following functions implement an iteration protocol for objects:
+
+The following functions implement an iteration protocol for objects,
+allowing to iterate through all key-value pairs in an object. The
+items are not returned in any particular order, as this would require
+sorting due to the internal hashtable implementation.
 
 .. function:: void *json_object_iter(json_t *object)
 
@@ -605,11 +666,21 @@ The following functions implement an iteration protocol for objects:
    *value*. This is useful when *value* is newly created and not used
    after the call.
 
+.. function:: void *json_object_key_to_iter(const char *key)
+
+   Like :func:`json_object_iter_at()`, but much faster. Only works for
+   values returned by :func:`json_object_iter_key()`. Using other keys
+   will lead to segfaults. This function is used internally to
+   implement :func:`json_object_foreach`.
+
+   .. versionadded:: 2.3
+
 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)
    {
@@ -670,7 +741,9 @@ and pass a pointer to a function. Example::
    }
 
 Also note that if the call succeeded (``json != NULL`` in the above
-example), the contents of ``error`` are unspecified.
+example), the contents of ``error`` are generally left unspecified.
+The decoding functions write to the ``position`` member also on
+success. See :ref:`apiref-decoding` for more info.
 
 All functions also accept *NULL* as the :type:`json_error_t` pointer,
 in which case no error information is returned to the caller.
@@ -680,8 +753,10 @@ Encoding
 ========
 
 This sections describes the functions that can be used to encode
-values to JSON. Only objects and arrays can be encoded, since they are
-the only valid "root" values of a JSON text.
+values to JSON. By default, only objects and arrays can be encoded
+directly, since they are the only valid *root* values of a JSON text.
+To encode any JSON value, use the ``JSON_ENCODE_ANY`` flag (see
+below).
 
 By default, the output has no newlines, and spaces are used between
 array and object elements for a readable output. This behavior can be
@@ -696,9 +771,9 @@ can be ORed together to obtain *flags*.
 ``JSON_INDENT(n)``
    Pretty-print the result, using newlines between array and object
    items, and indenting with *n* spaces. The valid range for *n* is
-   between 0 and 32, other values result in an undefined output. If
-   ``JSON_INDENT`` is not used or *n* is 0, no newlines are inserted
-   between array and object items.
+   between 0 and 31 (inclusive), other values result in an undefined
+   output. If ``JSON_INDENT`` is not used or *n* is 0, no newlines are
+   inserted between array and object items.
 
 ``JSON_COMPACT``
    This flag enables a compact representation, i.e. sets the separator
@@ -722,6 +797,23 @@ can be ORed together to obtain *flags*.
    example, decoding a JSON text and then encoding with this flag
    preserves the order of object keys.
 
+``JSON_ENCODE_ANY``
+   Specifying this flag makes it possible to encode any JSON value on
+   its own. Without it, only objects and arrays can be passed as the
+   *root* value to the encoding functions.
+
+   **Note:** Encoding any value may be useful in some scenarios, but
+   it's generally discouraged as it violates strict compatiblity with
+   :rfc:`4627`. If you use this flag, don't expect interoperatibility
+   with other JSON systems.
+
+   .. versionadded:: 2.1
+
+``JSON_ESCAPE_SLASH``
+   Escape the ``/`` characters in strings with ``\/``.
+
+   .. versionadded:: 2.4
+
 The following functions perform the actual JSON encoding. The result
 is in UTF-8.
 
@@ -745,6 +837,30 @@ is in UTF-8.
    *path* already exists, it is overwritten. *flags* is described
    above. Returns 0 on success and -1 on error.
 
+.. type:: json_dump_callback_t
+
+   A typedef for a function that's called by
+   :func:`json_dump_callback()`::
+
+       typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
+
+   *buffer* points to a buffer containing a chunk of output, *size* is
+   the length of the buffer, and *data* is the corresponding
+   :func:`json_dump_callback()` argument passed through.
+
+   On error, the function should return -1 to stop the encoding
+   process. On success, it should return 0.
+
+   .. versionadded:: 2.2
+
+.. function:: int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
+
+   Call *callback* repeatedly, passing a chunk of the JSON
+   representation of *root* each time. *flags* is described above.
+   Returns 0 on success and -1 on error.
+
+   .. versionadded:: 2.2
+
 
 .. _apiref-decoding:
 
@@ -756,20 +872,88 @@ text to the Jansson representation of JSON data. The JSON
 specification requires that a JSON text is either a serialized array
 or object, and this requirement is also enforced with the following
 functions. In other words, the top level value in the JSON text being
-decoded must be either array or object.
+decoded must be either array or object. To decode any JSON value, use
+the ``JSON_DECODE_ANY`` flag (see below).
 
 See :ref:`rfc-conformance` for a discussion on Jansson's conformance
 to the JSON specification. It explains many design decisions that
 affect especially the behavior of the decoder.
 
+Each function takes a *flags* parameter that can be used to control
+the behavior of the decoder. Its default value is 0. The following
+macros can be ORed together to obtain *flags*.
+
+``JSON_REJECT_DUPLICATES``
+   Issue a decoding error if any JSON object in the input text
+   contains duplicate keys. Without this flag, the value of the last
+   occurence of each key ends up in the result. Key equivalence is
+   checked byte-by-byte, without special Unicode comparison
+   algorithms.
+
+   .. versionadded:: 2.1
+
+``JSON_DECODE_ANY``
+   By default, the decoder expects an array or object as the input.
+   With this flag enabled, the decoder accepts any valid JSON value.
+
+   **Note:** Decoding any value may be useful in some scenarios, but
+   it's generally discouraged as it violates strict compatiblity with
+   :rfc:`4627`. If you use this flag, don't expect interoperatibility
+   with other JSON systems.
+
+   .. versionadded:: 2.3
+
+``JSON_DISABLE_EOF_CHECK``
+   By default, the decoder expects that its whole input constitutes a
+   valid JSON text, and issues an error if there's extra data after
+   the otherwise valid JSON input. With this flag enabled, the decoder
+   stops after decoding a valid JSON array or object, and thus allows
+   extra data after the JSON text.
+
+   Normally, reading will stop when the last ``]`` or ``}`` in the
+   JSON input is encountered. If both ``JSON_DISABLE_EOF_CHECK`` and
+   ``JSON_DECODE_ANY`` flags are used, the decoder may read one extra
+   UTF-8 code unit (up to 4 bytes of input). For example, decoding
+   ``4true`` correctly decodes the integer 4, but also reads the
+   ``t``. For this reason, if reading multiple consecutive values that
+   are not arrays or objects, they should be separated by at least one
+   whitespace character.
+
+   .. versionadded:: 2.1
+
+Each function also takes an optional :type:`json_error_t` parameter
+that is filled with error information if decoding fails. It's also
+updated on success; the number of bytes of input read is written to
+its ``position`` field. This is especially useful when using
+``JSON_DISABLE_EOF_CHECK`` to read multiple consecutive JSON texts.
+
+.. versionadded:: 2.3
+   Number of bytes of input read is written to the ``position`` field
+   of the :type:`json_error_t` structure.
+
+If no error or position information is needed, you can pass *NULL*.
+
+The following functions perform the actual JSON decoding.
+
 .. function:: json_t *json_loads(const char *input, size_t flags, json_error_t *error)
 
    .. refcounting:: new
 
    Decodes the JSON string *input* and returns the array or object it
    contains, or *NULL* on error, in which case *error* is filled with
-   information about the error. *flags* is currently unused, and
-   should be set to 0.
+   information about the error. *flags* is described above.
+
+.. function:: json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
+
+   .. refcounting:: new
+
+   Decodes the JSON string *buffer*, whose length is *buflen*, and
+   returns the array or object it contains, or *NULL* on error, in
+   which case *error* is filled with information about the error. This
+   is similar to :func:`json_loads()` except that the string doesn't
+   need to be null-terminated. *flags* is described above.
+
+   .. versionadded:: 2.1
 
 .. function:: json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
 
@@ -777,8 +961,18 @@ affect especially the behavior of the decoder.
 
    Decodes the JSON text in stream *input* and returns the array or
    object it contains, or *NULL* on error, in which case *error* is
-   filled with information about the error. *flags* is currently
-   unused, and should be set to 0.
+   filled with information about the error. *flags* is described
+   above.
+
+   This function will start reading the input from whatever position
+   the input file was, without attempting to seek first. If an error
+   occurs, the file position will be left indeterminate. On success,
+   the file position will be at EOF, unless ``JSON_DISABLE_EOF_CHECK``
+   flag was used. In this case, the file position will be at the first
+   character after the last ``]`` or ``}`` in the JSON input. This
+   allows calling :func:`json_loadf()` on the same ``FILE`` object
+   multiple times, if the input consists of consecutive JSON texts,
+   possibly separated by whitespace.
 
 .. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
 
@@ -786,8 +980,36 @@ affect especially the behavior of the decoder.
 
    Decodes the JSON text in file *path* and returns the array or
    object it contains, or *NULL* on error, in which case *error* is
-   filled with information about the error. *flags* is currently
-   unused, and should be set to 0.
+   filled with information about the error. *flags* is described
+   above.
+
+.. type:: json_load_callback_t
+
+   A typedef for a function that's called by
+   :func:`json_load_callback()` to read a chunk of input data::
+
+       typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
+
+   *buffer* points to a buffer of *buflen* bytes, and *data* is the
+   corresponding :func:`json_load_callback()` argument passed through.
+
+   On error, the function should return ``(size_t)-1`` to abort the
+   decoding process. When there's no data left, it should return 0 to
+   report that the end of input has been reached.
+
+   .. versionadded:: 2.4
+
+.. function:: json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error)
+
+   .. refcounting:: new
+
+   Decodes the JSON text produced by repeated calls to *callback*, and
+   returns the array or object it contains, or *NULL* on error, in
+   which case *error* is filled with information about the error.
+   *data* is passed through to *callback* on each call. *flags* is
+   described above.
+
+   .. versionadded:: 2.4
 
 
 .. _apiref-pack:
@@ -795,14 +1017,14 @@ affect especially the behavior of the decoder.
 Building Values
 ===============
 
-This sectinon describes functions that help to create, or *pack*,
+This section describes functions that help to create, or *pack*,
 complex JSON values, especially nested objects and arrays. Value
 building is based on a *format string* that is used to tell the
 functions about the expected arguments.
 
 For example, the format string ``"i"`` specifies a single integer
 value, while the format string ``"[ssb]"`` or the equivalent ``"[s, s,
-b]"`` specifies an array value with two integers and a boolean as its
+b]"`` specifies an array value with two strings and a boolean as its
 items::
 
     /* Create the JSON integer 42 */
@@ -837,7 +1059,7 @@ denotes the C type that is expected as the corresponding argument.
 ``o`` (any value) [json_t \*]
     Output any given JSON value as-is. If the value is added to an
     array or object, the reference to the value passed to ``o`` is
-    stealed by the container.
+    stolen by the container.
 
 ``O`` (any value) [json_t \*]
     Like ``o``, but the argument's reference count is incremented.
@@ -857,6 +1079,8 @@ denotes the C type that is expected as the corresponding argument.
     fourth, etc. format character represent a value. Any value may be
     an object or array, i.e. recursive value building is supported.
 
+Whitespace, ``:`` and ``,`` are ignored.
+
 The following functions compose the value building API:
 
 .. function:: json_t *json_pack(const char *fmt, ...)
@@ -887,13 +1111,13 @@ More examples::
   json_pack("{}");
 
   /* Build the JSON object {"foo": 42, "bar": 7} */
-  json_pack("{sisb}", "foo", 42, "bar", 7);
+  json_pack("{sisi}", "foo", 42, "bar", 7);
 
   /* Like above, ':', ',' and whitespace are ignored */
-  json_pack("{s:i, s:b}", "foo", 42, "bar", 7);
+  json_pack("{s:i, s:i}", "foo", 42, "bar", 7);
 
   /* Build the JSON array [[1, 2], {"cool": true}] */
-  json_pack("[[i,i],{s:b]]", 1, 2, "cool", 1);
+  json_pack("[[i,i],{s:b}]", 1, 2, "cool", 1);
 
 
 .. _apiref-unpack:
@@ -918,7 +1142,9 @@ type whose address should be passed.
 
 ``s`` (string) [const char \*]
     Convert a JSON string to a pointer to a NULL terminated UTF-8
-    string.
+    string. The resulting string is extracted by using
+    :func:`json_string_value()` internally, so it exists as long as
+    there are still references to the corresponding JSON string.
 
 ``n`` (null)
     Expect a JSON null value. Nothing is extracted.
@@ -962,6 +1188,11 @@ type whose address should be passed.
     ``fmt`` may contain objects and arrays as values, i.e. recursive
     value extraction is supporetd.
 
+    .. versionadded:: 2.3
+       Any ``s`` representing a key may be suffixed with a ``?`` to
+       make the key optional. If the key is not found, nothing is
+       extracted. See below for an example.
+
 ``!``
     This special format character is used to enable the check that
     all object and array items are accessed, on a per-value basis. It
@@ -976,6 +1207,8 @@ type whose address should be passed.
     or object as the last format character before the closing bracket
     or brace.
 
+Whitespace, ``:`` and ``,`` are ignored.
+
 The following functions compose the parsing and validation API:
 
 .. function:: int json_unpack(json_t *root, const char *fmt, ...)
@@ -992,6 +1225,20 @@ The following functions compose the parsing and validation API:
    behaviour of the unpacker, see below for the flags. Returns 0 on
    success and -1 on failure.
 
+.. note::
+
+   The first argument of all unpack functions is ``json_t *root``
+   instead of ``const json_t *root``, because the use of ``O`` format
+   character causes the reference count of ``root``, or some value
+   reachable from ``root``, to be increased. Furthermore, the ``o``
+   format character may be used to extract a value as-is, which allows
+   modifying the structure or contents of a value reachable from
+   ``root``.
+
+   If the ``O`` and ``o`` format characters are not used, it's
+   perfectly safe to cast a ``const json_t *`` variable to plain
+   ``json_t *`` when used with these functions.
+
 The following unpacking flags are available:
 
 ``JSON_STRICT``
@@ -1028,6 +1275,13 @@ Examples::
     json_unpack(root, "[ii!]", &myint1, &myint2);
     /* returns -1 for failed validation */
 
+    /* root is an empty JSON object */
+    int myint = 0, myint2 = 0;
+    json_unpack(root, "{s?i, s?[ii]}",
+                "foo", &myint1,
+                "bar", &myint2, &myint3);
+    /* myint1, myint2 or myint3 is no touched as "foo" and "bar" don't exist */
+
 
 Equality
 ========
@@ -1096,6 +1350,8 @@ copied in a recursive fashion.
    Returns a deep copy of *value*, or *NULL* on error.
 
 
+.. _apiref-custom-memory-allocation:
+
 Custom Memory Allocation
 ========================
 
@@ -1164,5 +1420,5 @@ JSON structures by zeroing all memory when freed::
 For more information about the issues of storing sensitive data in
 memory, see
 http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html.
-The page also examplains the :func:`guaranteed_memset()` function used
+The page also explains the :func:`guaranteed_memset()` function used
 in the example and gives a sample implementation for it.
index c8911ab..6010904 100644 (file)
@@ -41,16 +41,16 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'Jansson'
-copyright = u'2009-2011, Petri Lehtinen'
+copyright = u'2009-2012, Petri Lehtinen'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '2.0'
+version = '2.4'
 # The full version, including alpha/beta/rc tags.
-release = '2.0'
+release = version
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
index 6359f22..6f4c929 100644 (file)
@@ -5,8 +5,7 @@ RFC Conformance
 ***************
 
 JSON is specified in :rfc:`4627`, *"The application/json Media Type
-for JavaScript Object Notation (JSON)"*. This chapter discusses
-Jansson's conformance to this specification.
+for JavaScript Object Notation (JSON)"*.
 
 Character Encoding
 ==================
@@ -30,6 +29,12 @@ error::
 
 All other Unicode codepoints U+0001 through U+10FFFF are allowed.
 
+Unicode normalization or any other transformation is never performed
+on any strings (string values or object keys). When checking for
+equivalence of strings or object keys, the comparison is performed
+byte by byte between the original UTF-8 representations of the
+strings.
+
 Numbers
 =======
 
index 5bf4aab..4bf48cb 100644 (file)
@@ -19,7 +19,7 @@
 
        <description of the json_object function>
 
-    :copyright: Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+    :copyright: Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
     :license: MIT, see LICENSE for details.
 """
 
index eb5f683..f30e1e7 100644 (file)
@@ -10,8 +10,8 @@ Compiling and Installing Jansson
 The Jansson source is available at
 http://www.digip.org/jansson/releases/.
 
-Unix-like systems
------------------
+Unix-like systems (including MinGW)
+-----------------------------------
 
 Unpack the source tarball and change to the source directory:
 
@@ -54,22 +54,25 @@ used as described above.
 .. _libtool: http://www.gnu.org/software/libtool/
 
 
+Windows
+-------
+
+Jansson can be built with Visual Studio 2010 (and probably newer
+versions, too). The solution and project files are in the
+``win32/vs2010/`` directory in the source distribution.
+
+
 Other Systems
 -------------
 
-On Windows and other non Unix-like systems, you may be unable to run
-the ``./configure`` script. In this case, follow these steps. All the
-files mentioned can be found in the ``src/`` directory.
+On non Unix-like systems, you may be unable to run the ``./configure``
+script. In this case, follow these steps. All the files mentioned can
+be found in the ``src/`` directory.
 
-1. Create ``jansson_config.h``. This file has some platform-specific
+1. Create ``jansson_config.h`` (which has some platform-specific
    parameters that are normally filled in by the ``./configure``
-   script:
-
-   - On Windows, rename ``jansson_config.h.win32`` to ``jansson_config.h``.
-
-   - On other systems, edit ``jansson_config.h.in``, replacing all
-     ``@variable@`` placeholders, and rename the file to
-     ``jansson_config.h``.
+   script). Edit ``jansson_config.h.in``, replacing all ``@variable@``
+   placeholders, and rename the file to ``jansson_config.h``.
 
 2. Make ``jansson.h`` and ``jansson_config.h`` available to the
    compiler, so that they can be found when compiling programs that
index 9ba36b7..dde1be5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -13,7 +13,7 @@
 
 #define BUFFER_SIZE  (256 * 1024)  /* 256 KB */
 
-#define URL_FORMAT   "http://github.com/api/v2/json/commits/list/%s/%s/master"
+#define URL_FORMAT   "https://api.github.com/repos/%s/%s/commits"
 #define URL_SIZE     256
 
 /* Return the offset of the first newline in text or the length of
@@ -102,7 +102,6 @@ int main(int argc, char *argv[])
 
     json_t *root;
     json_error_t error;
-    json_t *commits;
 
     if(argc != 3)
     {
@@ -126,29 +125,35 @@ int main(int argc, char *argv[])
         return 1;
     }
 
-    commits = json_object_get(root, "commits");
-    if(!json_is_array(commits))
+    if(!json_is_array(root))
     {
-        fprintf(stderr, "error: commits is not an array\n");
+        fprintf(stderr, "error: root is not an array\n");
         return 1;
     }
 
-    for(i = 0; i < json_array_size(commits); i++)
+    for(i = 0; i < json_array_size(root); i++)
     {
-        json_t *commit, *id, *message;
+        json_t *data, *sha, *commit, *message;
         const char *message_text;
 
-        commit = json_array_get(commits, i);
-        if(!json_is_object(commit))
+        data = json_array_get(root, i);
+        if(!json_is_object(data))
+        {
+            fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
+            return 1;
+        }
+
+        sha = json_object_get(data, "sha");
+        if(!json_is_string(sha))
         {
-            fprintf(stderr, "error: commit %d is not an object\n", i + 1);
+            fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1);
             return 1;
         }
 
-        id = json_object_get(commit, "id");
-        if(!json_is_string(id))
+        commit = json_object_get(data, "commit");
+        if(!json_is_object(commit))
         {
-            fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
+            fprintf(stderr, "error: commit %d: commit is not an object\n", i + 1);
             return 1;
         }
 
@@ -161,7 +166,7 @@ int main(int argc, char *argv[])
 
         message_text = json_string_value(message);
         printf("%.8s %.*s\n",
-               json_string_value(id),
+               json_string_value(sha),
                newline_offset(message_text),
                message_text);
     }
index b5a3be8..1f3f8ef 100644 (file)
@@ -22,6 +22,11 @@ data. Its main features and design principles are:
 Jansson is licensed under the `MIT license`_; see LICENSE in the
 source distribution for details.
 
+Jansson is used in production and its API is stable. It works on
+numerous platforms, including numerous Unix like systems and Windows.
+It's suitable for use on any system, including desktop, server, and
+small embedded systems.
+
 
 .. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
 .. _Jansson: http://www.digip.org/jansson/
@@ -36,6 +41,7 @@ Contents
    upgrading
    tutorial
    conformance
+   portability
    apiref
    changes
 
diff --git a/doc/portability.rst b/doc/portability.rst
new file mode 100644 (file)
index 0000000..dc6c5eb
--- /dev/null
@@ -0,0 +1,52 @@
+***********
+Portability
+***********
+
+Thread safety
+-------------
+
+Jansson is thread safe and has no mutable global state. The only
+exception are the memory allocation functions, that should be set at
+most once, and only on program startup. See
+:ref:`apiref-custom-memory-allocation`.
+
+There's no locking performed inside Jansson's code, so a multithreaded
+program must perform its own locking if JSON values are shared by
+multiple threads. Jansson's reference counting semantics may make this
+a bit harder than it seems, as it's possible to have a reference to a
+value that's also stored inside a list or object. Modifying the
+container (adding or removing values) may trigger concurrent access to
+such values, as containers manage the reference count of their
+contained values. Bugs involving concurrent incrementing or
+decrementing of deference counts may be hard to track.
+
+The encoding functions (:func:`json_dumps()` and friends) track
+reference loops by modifying the internal state of objects and arrays.
+For this reason, encoding functions must not be run on the same JSON
+values in two separate threads at the same time. As already noted
+above, be especially careful if two arrays or objects share their
+contained values with another array or object.
+
+If you want to make sure that two JSON value hierarchies do not
+contain shared values, use :func:`json_deep_copy()` to make copies.
+
+Locale
+------
+
+Jansson works fine under any locale.
+
+However, if the host program is multithreaded and uses ``setlocale()``
+to switch the locale in one thread while Jansson is currently encoding
+or decoding JSON data in another thread, the result may be wrong or
+the program may even crash.
+
+Jansson uses locale specific functions for certain string conversions
+in the encoder and decoder, and then converts the locale specific
+values to/from the JSON representation. This fails if the locale
+changes between the string conversion and the locale-to-JSON
+conversion. This can only happen in multithreaded programs that use
+``setlocale()``, because ``setlocale()`` switches the locale for all
+running threads, not only the thread that calls ``setlocale()``.
+
+If your program uses ``setlocale()`` as described above, consider
+using the thread-safe ``uselocale()`` instead.
index dd7ae19..79bd3b5 100644 (file)
@@ -7,9 +7,8 @@ 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.
+of a repository in GitHub_ over the web. `GitHub API`_ uses 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
@@ -31,42 +30,44 @@ 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/
+.. _GitHub: https://github.com/
+.. _GitHub API: http://developer.github.com/
 .. _libcurl: http://curl.haxx.se/
 
 
 .. _tutorial-github-commits-api:
 
-The GitHub Commits API
-======================
+The GitHub Repo 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.
+The `GitHub Repo Commits API`_ is used by sending HTTP requests to
+URLs like ``https://api.github.com/repos/USER/REPOSITORY/commits``,
+where ``USER`` and ``REPOSITORY`` are the GitHub user ID and the name
+of the repository whose commits are to be listed, respectively.
 
-GitHub responds with a JSON object of the following form:
+GitHub responds with a JSON array of the following form:
 
 .. code-block:: none
 
-    {
-        "commits": [
-            {
-                "id": "<the commit ID>",
+    [
+        {
+            "sha": "<the commit ID>",
+            "commit": {
                 "message": "<the commit message>",
-                <more fields, not important to this tutorial>
+                <more fields, not important to this tutorial...>
             },
-            {
-                "id": "<the commit ID>",
+            <more fields...>
+        },
+        {
+            "sha": "<the commit ID>",
+            "commit": {
                 "message": "<the commit message>",
-                <more fields, not important to this tutorial>
+                <more fields...>
             },
-            <more commits...>
-        ]
-    }
+            <more fields...>
+        },
+        <more commits...>
+    ]
 
 In our program, the HTTP request is sent using the following
 function::
@@ -80,7 +81,7 @@ 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
+.. _GitHub Repo Commits API: http://developer.github.com/v3/repos/commits/
 
 .. _tutorial-the-program:
 
@@ -95,10 +96,10 @@ First the includes::
 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::
+The following definitions are used to build the GitHub API request
+URL::
 
-   #define URL_FORMAT   "http://github.com/api/v2/json/commits/list/%s/%s/master"
+   #define URL_FORMAT   "https://api.github.com/repos/%s/%s/commits"
    #define URL_SIZE     256
 
 The following function is used when formatting the result to find the
@@ -118,20 +119,21 @@ first newline in the commit message::
 The main function follows. In the beginning, we first declare a bunch
 of variables and check the command line parameters::
 
-    size_t i;
-    char *text;
-    char url[URL_SIZE];
+    int main(int argc, char *argv[])
+    {
+        size_t i;
+        char *text;
+        char url[URL_SIZE];
 
-    json_t *root;
-    json_error_t error;
-    json_t *commits;
+        json_t *root;
+        json_error_t error;
 
-    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;
-    }
+        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::
@@ -171,47 +173,49 @@ 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::
+We check that the returned value really is an array::
 
-    commits = json_object_get(root, "commits");
-    if(!json_is_array(commits))
+    if(!json_is_array(root))
     {
-        fprintf(stderr, "error: commits is not an array\n");
+        fprintf(stderr, "error: root 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, :func:`json_object_get()`
-returns *NULL*, but :func:`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++)
+    for(i = 0; i < json_array_size(root); i++)
     {
-        json_t *commit, *id, *message;
+        json_t *data, *sha, *commit, *message;
         const char *message_text;
 
-        commit = json_array_get(commits, i);
-        if(!json_is_object(commit))
+        data = json_array_get(root, i);
+        if(!json_is_object(data))
         {
-            fprintf(stderr, "error: commit %d is not an object\n", i + 1);
+            fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
             return 1;
         }
     ...
 
 The function :func:`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 :func:`json_array_get()`.
+i'th element of the ``root`` array using :func:`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::
+Next we'll extract the commit ID (a hexadecimal SHA-1 sum),
+intermediate commit info object, and the commit message from that
+object. We also do proper type checks::
+
+        sha = json_object_get(data, "sha");
+        if(!json_is_string(sha))
+        {
+            fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1);
+            return 1;
+        }
 
-        id = json_object_get(commit, "id");
-        if(!json_is_string(id))
+        commit = json_object_get(data, "commit");
+        if(!json_is_object(commit))
         {
-            fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
+            fprintf(stderr, "error: commit %d: commit is not an object\n", i + 1);
             return 1;
         }
 
@@ -250,25 +254,27 @@ 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...>
+    1581f26a Merge branch '2.3'
+    aabfd493 load: Change buffer_pos to be a size_t
+    bd72efbd load: Avoid unexpected behaviour in macro expansion
+    e8fd3e30 Document and tweak json_load_callback()
+    873eddaf Merge pull request #60 from rogerz/contrib
+    bd2c0c73 Ignore the binary test_load_callback
+    17a51a4b Merge branch '2.3'
+    09c39adc Add json_load_callback to the list of exported symbols
+    cbb80baf Merge pull request #57 from rogerz/contrib
+    040bd7b0 Add json_load_callback()
+    2637faa4 Make test stripping locale independent
+    <...>
 
 
 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.
+commits of a GitHub repository using the GitHub Repo 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
index 4f2a58d..9d040f6 100644 (file)
@@ -1,3 +1,5 @@
+EXTRA_DIST = jansson.def
+
 include_HEADERS = jansson.h jansson_config.h
 
 lib_LTLIBRARIES = libjansson.la
@@ -12,12 +14,14 @@ libjansson_la_SOURCES = \
        pack_unpack.c \
        strbuffer.c \
        strbuffer.h \
+       strconv.c \
        utf.c \
        utf.h \
        value.c
 libjansson_la_LDFLAGS = \
+       -no-undefined \
        -export-symbols-regex '^json_' \
-       -version-info 4:0:0
+       -version-info 8:0:4
 
 if GCC
 # These flags are gcc specific
index 9c01366..2c7dee9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -11,7 +11,7 @@
 #include <string.h>
 #include <assert.h>
 
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 #include "strbuffer.h"
 #include "utf.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
-{
-    char *buffer;
-    int length;
-    int size;
+struct object_key {
+    size_t serial;
+    const char *key;
 };
 
-static int dump_to_strbuffer(const char *buffer, int size, void *data)
+static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
 {
     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
 }
 
-static int dump_to_file(const char *buffer, int size, void *data)
+static int dump_to_file(const char *buffer, size_t size, void *data)
 {
     FILE *dest = (FILE *)data;
     if(fwrite(buffer, size, 1, dest) != 1)
@@ -44,7 +40,7 @@ static int dump_to_file(const char *buffer, int size, void *data)
 /* 32 spaces (the maximum indentation size) */
 static char whitespace[] = "                                ";
 
-static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
+static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
 {
     if(JSON_INDENT(flags) > 0)
     {
@@ -66,7 +62,7 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void
     return 0;
 }
 
-static int dump_string(const char *str, int ascii, dump_func dump, void *data)
+static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)
 {
     const char *pos, *end;
     int32_t codepoint;
@@ -91,8 +87,12 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
             if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
                 break;
 
+            /* slash */
+            if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
+                break;
+
             /* non-ASCII */
-            if(ascii && codepoint > 0x7F)
+            if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
                 break;
 
             pos = end;
@@ -106,7 +106,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
         if(end == pos)
             break;
 
-        /* handle \, ", and control codes */
+        /* handle \, /, ", and control codes */
         length = 2;
         switch(codepoint)
         {
@@ -117,6 +117,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
             case '\n': text = "\\n"; break;
             case '\r': text = "\\r"; break;
             case '\t': text = "\\t"; break;
+            case '/':  text = "\\/"; break;
             default:
             {
                 /* codepoint is in BMP */
@@ -155,21 +156,21 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
 
 static int object_key_compare_keys(const void *key1, const void *key2)
 {
-    return strcmp((*(const object_key_t **)key1)->key,
-                  (*(const object_key_t **)key2)->key);
+    return strcmp(((const struct object_key *)key1)->key,
+                  ((const struct object_key *)key2)->key);
 }
 
 static int object_key_compare_serials(const void *key1, const void *key2)
 {
-    return (*(const object_key_t **)key1)->serial -
-           (*(const object_key_t **)key2)->serial;
+    size_t a = ((const struct object_key *)key1)->serial;
+    size_t b = ((const struct object_key *)key2)->serial;
+
+    return a < b ? -1 : a == b ? 0 : 1;
 }
 
 static int do_dump(const json_t *json, size_t flags, int depth,
-                   dump_func dump, void *data)
+                   json_dump_callback_t dump, void *data)
 {
-    int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
-
     switch(json_typeof(json)) {
         case JSON_NULL:
             return dump("null", 4, data);
@@ -188,7 +189,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
             size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
                             "%" JSON_INTEGER_FORMAT,
                             json_integer_value(json));
-            if(size >= MAX_INTEGER_STR_LENGTH)
+            if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
                 return -1;
 
             return dump(buffer, size, data);
@@ -198,31 +199,17 @@ static int do_dump(const json_t *json, size_t flags, int depth,
         {
             char buffer[MAX_REAL_STR_LENGTH];
             int size;
+            double value = json_real_value(json);
 
-            size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
-                            json_real_value(json));
-            if(size >= MAX_REAL_STR_LENGTH)
+            size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
+            if(size < 0)
                 return -1;
 
-            /* Make sure there's a dot or 'e' in the output. Otherwise
-               a real is converted to an integer when decoding */
-            if(strchr(buffer, '.') == NULL &&
-               strchr(buffer, 'e') == NULL)
-            {
-                if(size + 2 >= MAX_REAL_STR_LENGTH) {
-                    /* No space to append ".0" */
-                    return -1;
-                }
-                buffer[size] = '.';
-                buffer[size + 1] = '0';
-                size += 2;
-            }
-
             return dump(buffer, size, data);
         }
 
         case JSON_STRING:
-            return dump_string(json_string_value(json), ascii, dump, data);
+            return dump_string(json_string_value(json), dump, data, flags);
 
         case JSON_ARRAY:
         {
@@ -308,19 +295,20 @@ static int do_dump(const json_t *json, size_t flags, int depth,
 
             if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
             {
-                const object_key_t **keys;
+                struct object_key *keys;
                 size_t size, i;
                 int (*cmp_func)(const void *, const void *);
 
                 size = json_object_size(json);
-                keys = jsonp_malloc(size * sizeof(object_key_t *));
+                keys = jsonp_malloc(size * sizeof(struct object_key));
                 if(!keys)
                     goto object_error;
 
                 i = 0;
                 while(iter)
                 {
-                    keys[i] = jsonp_object_iter_fullkey(iter);
+                    keys[i].serial = hashtable_iter_serial(iter);
+                    keys[i].key = json_object_iter_key(iter);
                     iter = json_object_iter_next((json_t *)json, iter);
                     i++;
                 }
@@ -331,18 +319,18 @@ static int do_dump(const json_t *json, size_t flags, int depth,
                 else
                     cmp_func = object_key_compare_serials;
 
-                qsort(keys, size, sizeof(object_key_t *), cmp_func);
+                qsort(keys, size, sizeof(struct object_key), cmp_func);
 
                 for(i = 0; i < size; i++)
                 {
                     const char *key;
                     json_t *value;
 
-                    key = keys[i]->key;
+                    key = keys[i].key;
                     value = json_object_get(json, key);
                     assert(value);
 
-                    dump_string(key, ascii, dump, data);
+                    dump_string(key, dump, data, flags);
                     if(dump(separator, separator_length, data) ||
                        do_dump(value, flags, depth + 1, dump, data))
                     {
@@ -379,7 +367,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
                 {
                     void *next = json_object_iter_next((json_t *)json, iter);
 
-                    dump_string(json_object_iter_key(iter), ascii, dump, data);
+                    dump_string(json_object_iter_key(iter), dump, data, flags);
                     if(dump(separator, separator_length, data) ||
                        do_dump(json_object_iter_value(iter), flags, depth + 1,
                                dump, data))
@@ -415,35 +403,26 @@ static int do_dump(const json_t *json, size_t flags, int depth,
     }
 }
 
-
 char *json_dumps(const json_t *json, size_t flags)
 {
     strbuffer_t strbuff;
     char *result;
 
-    if(!json_is_array(json) && !json_is_object(json))
-        return NULL;
-
     if(strbuffer_init(&strbuff))
         return NULL;
 
-    if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
-        strbuffer_close(&strbuff);
-        return NULL;
-    }
+    if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
+        result = NULL;
+    else
+        result = jsonp_strdup(strbuffer_value(&strbuff));
 
-    result = jsonp_strdup(strbuffer_value(&strbuff));
     strbuffer_close(&strbuff);
-
     return result;
 }
 
 int json_dumpf(const json_t *json, FILE *output, size_t flags)
 {
-    if(!json_is_array(json) && !json_is_object(json))
-        return -1;
-
-    return do_dump(json, flags, 0, dump_to_file, (void *)output);
+    return json_dump_callback(json, dump_to_file, (void *)output, flags);
 }
 
 int json_dump_file(const json_t *json, const char *path, size_t flags)
@@ -459,3 +438,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
     fclose(output);
     return result;
 }
+
+int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
+{
+    if(!(flags & JSON_ENCODE_ANY)) {
+        if(!json_is_array(json) && !json_is_object(json))
+           return -1;
+    }
+
+    return do_dump(json, flags, 0, callback, data);
+}
index a7c8cbb..a544a59 100644 (file)
@@ -59,4 +59,5 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
     error->position = position;
 
     vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
+    error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
 }
index de25c21..76cf69b 100644 (file)
@@ -1,11 +1,12 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  * This library is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  */
 
 #include <stdlib.h>
+#include <string.h>
 #include <jansson_config.h>   /* for JSON_INLINE */
 #include "jansson_private.h"  /* for container_of() */
 #include "hashtable.h"
@@ -16,6 +17,23 @@ typedef struct hashtable_bucket bucket_t;
 
 #define list_to_pair(list_)  container_of(list_, pair_t, list)
 
+/* From http://www.cse.yorku.ca/~oz/hash.html */
+static size_t hash_str(const void *ptr)
+{
+    const char *str = (const char *)ptr;
+
+    size_t hash = 5381;
+    size_t c;
+
+    while((c = (size_t)*str))
+    {
+        hash = ((hash << 5) + hash) + c;
+        str++;
+    }
+
+    return hash;
+}
+
 static JSON_INLINE void list_init(list_t *list)
 {
     list->next = list;
@@ -62,7 +80,6 @@ static size_t primes[] = {
     12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
     805306457, 1610612741
 };
-static const size_t num_primes = sizeof(primes) / sizeof(size_t);
 
 static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
 {
@@ -71,7 +88,7 @@ static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
 
 
 static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
-                                   const void *key, size_t hash)
+                                   const char *key, size_t hash)
 {
     list_t *list;
     pair_t *pair;
@@ -83,7 +100,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
     while(1)
     {
         pair = list_to_pair(list);
-        if(pair->hash == hash && hashtable->cmp_keys(pair->key, key))
+        if(pair->hash == hash && strcmp(pair->key, key) == 0)
             return pair;
 
         if(list == bucket->last)
@@ -97,7 +114,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
 
 /* returns 0 on success, -1 if key was not found */
 static int hashtable_do_del(hashtable_t *hashtable,
-                            const void *key, size_t hash)
+                            const char *key, size_t hash)
 {
     pair_t *pair;
     bucket_t *bucket;
@@ -120,13 +137,9 @@ static int hashtable_do_del(hashtable_t *hashtable,
         bucket->last = pair->list.prev;
 
     list_remove(&pair->list);
+    json_decref(pair->value);
 
-    if(hashtable->free_key)
-        hashtable->free_key(pair->key);
-    if(hashtable->free_value)
-        hashtable->free_value(pair->value);
-
-    free(pair);
+    jsonp_free(pair);
     hashtable->size--;
 
     return 0;
@@ -141,10 +154,7 @@ static void hashtable_do_clear(hashtable_t *hashtable)
     {
         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);
+        json_decref(pair->value);
         jsonp_free(pair);
     }
 }
@@ -184,31 +194,7 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
 }
 
 
-hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                              free_fn free_key, free_fn free_value)
-{
-    hashtable_t *hashtable = jsonp_malloc(sizeof(hashtable_t));
-    if(!hashtable)
-        return NULL;
-
-    if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
-    {
-        jsonp_free(hashtable);
-        return NULL;
-    }
-
-    return hashtable;
-}
-
-void hashtable_destroy(hashtable_t *hashtable)
-{
-    hashtable_close(hashtable);
-    jsonp_free(hashtable);
-}
-
-int hashtable_init(hashtable_t *hashtable,
-                   key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                   free_fn free_key, free_fn free_value)
+int hashtable_init(hashtable_t *hashtable)
 {
     size_t i;
 
@@ -220,11 +206,6 @@ int hashtable_init(hashtable_t *hashtable,
 
     list_init(&hashtable->list);
 
-    hashtable->hash_key = hash_key;
-    hashtable->cmp_keys = cmp_keys;
-    hashtable->free_key = free_key;
-    hashtable->free_value = free_value;
-
     for(i = 0; i < num_buckets(hashtable); i++)
     {
         hashtable->buckets[i].first = hashtable->buckets[i].last =
@@ -240,7 +221,9 @@ void hashtable_close(hashtable_t *hashtable)
     jsonp_free(hashtable->buckets);
 }
 
-int hashtable_set(hashtable_t *hashtable, void *key, void *value)
+int hashtable_set(hashtable_t *hashtable,
+                  const char *key, size_t serial,
+                  json_t *value)
 {
     pair_t *pair;
     bucket_t *bucket;
@@ -251,28 +234,29 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value)
         if(hashtable_do_rehash(hashtable))
             return -1;
 
-    hash = hashtable->hash_key(key);
+    hash = hash_str(key);
     index = hash % num_buckets(hashtable);
     bucket = &hashtable->buckets[index];
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
 
     if(pair)
     {
-        if(hashtable->free_key)
-            hashtable->free_key(key);
-        if(hashtable->free_value)
-            hashtable->free_value(pair->value);
+        json_decref(pair->value);
         pair->value = value;
     }
     else
     {
-        pair = jsonp_malloc(sizeof(pair_t));
+        /* offsetof(...) returns the size of pair_t without the last,
+           flexible member. This way, the correct amount is
+           allocated. */
+        pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1);
         if(!pair)
             return -1;
 
-        pair->key = key;
-        pair->value = value;
         pair->hash = hash;
+        pair->serial = serial;
+        strcpy(pair->key, key);
+        pair->value = value;
         list_init(&pair->list);
 
         insert_to_bucket(hashtable, bucket, &pair->list);
@@ -282,13 +266,13 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value)
     return 0;
 }
 
-void *hashtable_get(hashtable_t *hashtable, const void *key)
+void *hashtable_get(hashtable_t *hashtable, const char *key)
 {
     pair_t *pair;
     size_t hash;
     bucket_t *bucket;
 
-    hash = hashtable->hash_key(key);
+    hash = hash_str(key);
     bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
 
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
@@ -298,9 +282,9 @@ void *hashtable_get(hashtable_t *hashtable, const void *key)
     return pair->value;
 }
 
-int hashtable_del(hashtable_t *hashtable, const void *key)
+int hashtable_del(hashtable_t *hashtable, const char *key)
 {
-    size_t hash = hashtable->hash_key(key);
+    size_t hash = hash_str(key);
     return hashtable_do_del(hashtable, key, hash);
 }
 
@@ -325,13 +309,13 @@ void *hashtable_iter(hashtable_t *hashtable)
     return hashtable_iter_next(hashtable, &hashtable->list);
 }
 
-void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
+void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
 {
     pair_t *pair;
     size_t hash;
     bucket_t *bucket;
 
-    hash = hashtable->hash_key(key);
+    hash = hash_str(key);
     bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
 
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
@@ -355,18 +339,22 @@ void *hashtable_iter_key(void *iter)
     return pair->key;
 }
 
+size_t hashtable_iter_serial(void *iter)
+{
+    pair_t *pair = list_to_pair((list_t *)iter);
+    return pair->serial;
+}
+
 void *hashtable_iter_value(void *iter)
 {
     pair_t *pair = list_to_pair((list_t *)iter);
     return pair->value;
 }
 
-void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
+void hashtable_iter_set(void *iter, json_t *value)
 {
     pair_t *pair = list_to_pair((list_t *)iter);
 
-    if(hashtable->free_value)
-        hashtable->free_value(pair->value);
-
+    json_decref(pair->value);
     pair->value = value;
 }
index 5aed14f..de1df26 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  * This library is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
@@ -8,20 +8,20 @@
 #ifndef HASHTABLE_H
 #define HASHTABLE_H
 
-typedef size_t (*key_hash_fn)(const void *key);
-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;
 };
 
+/* "pair" may be a bit confusing a name, but think of it as a
+   key-value pair. In this case, it just encodes some extra data,
+   too */
 struct hashtable_pair {
-    void *key;
-    void *value;
     size_t hash;
     struct hashtable_list list;
+    json_t *value;
+    size_t serial;
+    char key[1];
 };
 
 struct hashtable_bucket {
@@ -34,56 +34,23 @@ typedef struct hashtable {
     struct hashtable_bucket *buckets;
     size_t num_buckets;  /* index to primes[] */
     struct hashtable_list list;
-
-    key_hash_fn hash_key;
-    key_cmp_fn cmp_keys;  /* returns non-zero for equal keys */
-    free_fn free_key;
-    free_fn free_value;
 } hashtable_t;
 
-/**
- * hashtable_create - Create a hashtable object
- *
- * @hash_key: The key hashing function
- * @cmp_keys: The key compare function. Returns non-zero for equal and
- *     zero for unequal unequal keys
- * @free_key: If non-NULL, called for a key that is no longer referenced.
- * @free_value: If non-NULL, called for a value that is no longer referenced.
- *
- * Returns a new hashtable object that should be freed with
- * hashtable_destroy when it's no longer used, or NULL on failure (out
- * of memory).
- */
-hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                              free_fn free_key, free_fn free_value);
 
-/**
- * hashtable_destroy - Destroy a hashtable object
- *
- * @hashtable: The hashtable
- *
- * Destroys a hashtable created with hashtable_create().
- */
-void hashtable_destroy(hashtable_t *hashtable);
+#define hashtable_key_to_iter(key_) \
+    (&(container_of(key_, struct hashtable_pair, key)->list))
 
 /**
  * hashtable_init - Initialize a hashtable object
  *
  * @hashtable: The (statically allocated) hashtable object
- * @hash_key: The key hashing function
- * @cmp_keys: The key compare function. Returns non-zero for equal and
- *     zero for unequal unequal keys
- * @free_key: If non-NULL, called for a key that is no longer referenced.
- * @free_value: If non-NULL, called for a value that is no longer referenced.
  *
  * Initializes a statically allocated hashtable object. The object
  * should be cleared with hashtable_close when it's no longer used.
  *
  * Returns 0 on success, -1 on error (out of memory).
  */
-int hashtable_init(hashtable_t *hashtable,
-                   key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                   free_fn free_key, free_fn free_value);
+int hashtable_init(hashtable_t *hashtable);
 
 /**
  * hashtable_close - Release all resources used by a hashtable object
@@ -99,20 +66,19 @@ void hashtable_close(hashtable_t *hashtable);
  *
  * @hashtable: The hashtable object
  * @key: The key
+ * @serial: For addition order of keys
  * @value: The value
  *
  * If a value with the given key already exists, its value is replaced
- * with the new value.
- *
- * Key and value are "stealed" in the sense that hashtable frees them
- * automatically when they are no longer used. The freeing is
- * accomplished by calling free_key and free_value functions that were
- * supplied to hashtable_new. In case one or both of the free
- * functions is NULL, the corresponding item is not "stealed".
+ * with the new value. Value is "stealed" in the sense that hashtable
+ * doesn't increment its refcount but decreases the refcount when the
+ * value is no longer needed.
  *
  * Returns 0 on success, -1 on failure (out of memory).
  */
-int hashtable_set(hashtable_t *hashtable, void *key, void *value);
+int hashtable_set(hashtable_t *hashtable,
+                  const char *key, size_t serial,
+                  json_t *value);
 
 /**
  * hashtable_get - Get a value associated with a key
@@ -122,7 +88,7 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value);
  *
  * Returns value if it is found, or NULL otherwise.
  */
-void *hashtable_get(hashtable_t *hashtable, const void *key);
+void *hashtable_get(hashtable_t *hashtable, const char *key);
 
 /**
  * hashtable_del - Remove a value from the hashtable
@@ -132,7 +98,7 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
  *
  * Returns 0 on success, or -1 if the key was not found.
  */
-int hashtable_del(hashtable_t *hashtable, const void *key);
+int hashtable_del(hashtable_t *hashtable, const char *key);
 
 /**
  * hashtable_clear - Clear hashtable
@@ -169,7 +135,7 @@ void *hashtable_iter(hashtable_t *hashtable);
  * Like hashtable_iter() but returns an iterator pointing to a
  * specific key.
  */
-void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
+void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
 
 /**
  * hashtable_iter_next - Advance an iterator
@@ -190,6 +156,13 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
 void *hashtable_iter_key(void *iter);
 
 /**
+ * hashtable_iter_serial - Retrieve the serial number pointed to by an iterator
+ *
+ * @iter: The iterator
+ */
+size_t hashtable_iter_serial(void *iter);
+
+/**
  * hashtable_iter_value - Retrieve the value pointed by an iterator
  *
  * @iter: The iterator
@@ -202,6 +175,6 @@ void *hashtable_iter_value(void *iter);
  * @iter: The iterator
  * @value: The value to set
  */
-void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
+void hashtable_iter_set(void *iter, json_t *value);
 
 #endif
diff --git a/src/jansson.def b/src/jansson.def
new file mode 100644 (file)
index 0000000..6b2c8a7
--- /dev/null
@@ -0,0 +1,65 @@
+LIBRARY "jansson"
+
+EXPORTS
+    json_delete
+    json_true
+    json_false
+    json_null
+    json_string
+    json_string_nocheck
+    json_string_value
+    json_string_set
+    json_string_set_nocheck
+    json_integer
+    json_integer_value
+    json_integer_set
+    json_real
+    json_real_value
+    json_real_set
+    json_number_value
+    json_array
+    json_array_size
+    json_array_get
+    json_array_set_new
+    json_array_append_new
+    json_array_insert_new
+    json_array_remove
+    json_array_clear
+    json_array_extend
+    json_object
+    json_object_size
+    json_object_get
+    json_object_set_new
+    json_object_set_new_nocheck
+    json_object_del
+    json_object_clear
+    json_object_update
+    json_object_update_existing
+    json_object_update_missing
+    json_object_iter
+    json_object_iter_at
+    json_object_iter_next
+    json_object_iter_key
+    json_object_iter_value
+    json_object_iter_set_new
+    json_object_key_to_iter
+    json_dumps
+    json_dumpf
+    json_dump_file
+    json_dump_callback
+    json_loads
+    json_loadb
+    json_loadf
+    json_load_file
+    json_load_callback
+    json_equal
+    json_copy
+    json_deep_copy
+    json_pack
+    json_pack_ex
+    json_vpack_ex
+    json_unpack
+    json_unpack_ex
+    json_vunpack_ex
+    json_set_alloc_funcs
+
index 0ab0dfd..352c6ce 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -21,11 +21,11 @@ extern "C" {
 /* version */
 
 #define JANSSON_MAJOR_VERSION  2
-#define JANSSON_MINOR_VERSION  0
+#define JANSSON_MINOR_VERSION  4
 #define JANSSON_MICRO_VERSION  0
 
 /* Micro version is omitted if it's 0 */
-#define JANSSON_VERSION  "2.0"
+#define JANSSON_VERSION  "2.4"
 
 /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
    for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
@@ -53,7 +53,11 @@ typedef struct {
 } json_t;
 
 #if JSON_INTEGER_IS_LONG_LONG
+#ifdef _WIN32
+#define JSON_INTEGER_FORMAT "I64d"
+#else
 #define JSON_INTEGER_FORMAT "lld"
+#endif
 typedef long long json_int_t;
 #else
 #define JSON_INTEGER_FORMAT "ld"
@@ -82,6 +86,7 @@ json_t *json_integer(json_int_t value);
 json_t *json_real(double value);
 json_t *json_true(void);
 json_t *json_false(void);
+#define json_boolean(val)      ((val) ? json_true() : json_false())
 json_t *json_null(void);
 
 static JSON_INLINE
@@ -126,13 +131,21 @@ int json_object_set_new_nocheck(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);
+int json_object_update_existing(json_t *object, json_t *other);
+int json_object_update_missing(json_t *object, json_t *other);
 void *json_object_iter(json_t *object);
 void *json_object_iter_at(json_t *object, const char *key);
+void *json_object_key_to_iter(const char *key);
 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);
 int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
 
+#define json_object_foreach(object, key, value) \
+    for(key = json_object_iter_key(json_object_iter(object)); \
+        key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
+        key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
+
 static JSON_INLINE
 int json_object_set(json_t *object, const char *key, json_t *value)
 {
@@ -214,22 +227,37 @@ json_t *json_copy(json_t *value);
 json_t *json_deep_copy(json_t *value);
 
 
-/* loading, printing */
+/* decoding */
+
+#define JSON_REJECT_DUPLICATES 0x1
+#define JSON_DISABLE_EOF_CHECK 0x2
+#define JSON_DECODE_ANY        0x4
+
+typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
 
 json_t *json_loads(const char *input, size_t flags, json_error_t *error);
+json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
 json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
 json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
+json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error);
+
+
+/* encoding */
 
 #define JSON_INDENT(n)      (n & 0x1F)
 #define JSON_COMPACT        0x20
 #define JSON_ENSURE_ASCII   0x40
 #define JSON_SORT_KEYS      0x80
 #define JSON_PRESERVE_ORDER 0x100
+#define JSON_ENCODE_ANY     0x200
+#define JSON_ESCAPE_SLASH   0x400
+
+typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
 
 char *json_dumps(const json_t *json, size_t flags);
 int json_dumpf(const json_t *json, FILE *output, size_t flags);
 int json_dump_file(const json_t *json, const char *path, size_t flags);
-
+int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
 
 /* custom memory allocation */
 
index 7f519cd..a566e8b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010-2012 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.
 #define JSON_INLINE @json_inline@
 #endif
 
-/* If your compiler supports the `long long` type,
-   JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
+/* If your compiler supports the `long long` type and the strtoll()
+   library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
+   otherwise to 0. */
 #define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
 
+/* If locale.h and localeconv() are available, define to 1,
+   otherwise to 0. */
+#define JSON_HAVE_LOCALECONV @json_have_localeconv@
+
 #endif
index 9224b31..7d6d09d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -11,6 +11,7 @@
 #include <stddef.h>
 #include "jansson.h"
 #include "hashtable.h"
+#include "strbuffer.h"
 
 #define container_of(ptr_, type_, member_)  \
     ((type_ *)((char *)ptr_ - offsetof(type_, member_)))
@@ -66,16 +67,6 @@ typedef struct {
 #define json_to_real(json_)   container_of(json_, json_real_t, json)
 #define json_to_integer(json_) container_of(json_, json_integer_t, json)
 
-size_t jsonp_hash_key(const void *ptr);
-int jsonp_key_equal(const void *ptr1, const void *ptr2);
-
-typedef struct {
-    size_t serial;
-    char key[1];
-} object_key_t;
-
-const object_key_t *jsonp_object_iter_fullkey(void *iter);
-
 void jsonp_error_init(json_error_t *error, const char *source);
 void jsonp_error_set_source(json_error_t *error, const char *source);
 void jsonp_error_set(json_error_t *error, int line, int column,
@@ -83,9 +74,19 @@ void jsonp_error_set(json_error_t *error, int line, int column,
 void jsonp_error_vset(json_error_t *error, int line, int column,
                       size_t position, const char *msg, va_list ap);
 
+/* Locale independent string<->double conversions */
+int jsonp_strtod(strbuffer_t *strbuffer, double *out);
+int jsonp_dtostr(char *buffer, size_t size, double value);
+
 /* Wrappers for custom memory functions */
 void* jsonp_malloc(size_t size);
 void jsonp_free(void *ptr);
 char *jsonp_strdup(const char *str);
 
+/* Windows compatibility */
+#ifdef _WIN32
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
 #endif
index 3cc7d0f..d88a704 100644 (file)
@@ -1,12 +1,11 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
  */
 
 #define _GNU_SOURCE
-#include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
@@ -14,7 +13,7 @@
 #include <string.h>
 #include <assert.h>
 
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 #include "strbuffer.h"
 #include "utf.h"
 #define TOKEN_FALSE          260
 #define TOKEN_NULL           261
 
+/* Locale independent versions of isxxx() functions */
+#define l_isupper(c)  ('A' <= (c) && (c) <= 'Z')
+#define l_islower(c)  ('a' <= (c) && (c) <= 'z')
+#define l_isalpha(c)  (l_isupper(c) || l_islower(c))
+#define l_isdigit(c)  ('0' <= (c) && (c) <= '9')
+#define l_isxdigit(c) \
+    (l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f')
+
 /* Read one byte from stream, convert to unsigned char, then int, and
    return. return EOF on end of file. This corresponds to the
    behaviour of fgetc(). */
@@ -41,7 +48,7 @@ typedef struct {
     get_func get;
     void *data;
     char buffer[5];
-    int buffer_pos;
+    size_t buffer_pos;
     int state;
     int line;
     int column, last_column;
@@ -69,6 +76,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
 {
     va_list ap;
     char msg_text[JSON_ERROR_TEXT_LENGTH];
+    char msg_with_context[JSON_ERROR_TEXT_LENGTH];
 
     int line = -1, col = -1;
     size_t pos = 0;
@@ -79,12 +87,12 @@ static void error_set(json_error_t *error, const lex_t *lex,
 
     va_start(ap, msg);
     vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
+    msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
     va_end(ap);
 
     if(lex)
     {
         const char *saved_text = strbuffer_value(&lex->saved_text);
-        char msg_with_context[JSON_ERROR_TEXT_LENGTH];
 
         line = lex->stream.line;
         col = lex->stream.column;
@@ -95,6 +103,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
             if(lex->saved_text.length <= 20) {
                 snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                          "%s near '%s'", msg_text, saved_text);
+                msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
                 result = msg_with_context;
             }
         }
@@ -107,6 +116,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
             else {
                 snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                          "%s near end of file", msg_text);
+                msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
                 result = msg_with_context;
             }
         }
@@ -268,11 +278,11 @@ static int32_t decode_unicode_escape(const char *str)
     for(i = 1; i <= 4; i++) {
         char c = str[i];
         value <<= 4;
-        if(isdigit(c))
+        if(l_isdigit(c))
             value += c - '0';
-        else if(islower(c))
+        else if(l_islower(c))
             value += c - 'a' + 10;
-        else if(isupper(c))
+        else if(l_isupper(c))
             value += c - 'A' + 10;
         else
             assert(0);
@@ -317,7 +327,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
             if(c == 'u') {
                 c = lex_get_save(lex, error);
                 for(i = 0; i < 4; i++) {
-                    if(!isxdigit(c)) {
+                    if(!l_isxdigit(c)) {
                         error_set(error, lex, "invalid escape");
                         goto out;
                     }
@@ -437,7 +447,11 @@ out:
 }
 
 #if JSON_INTEGER_IS_LONG_LONG
+#ifdef _MSC_VER // Microsoft Visual Studio
+#define json_strtoint     _strtoi64
+#else
 #define json_strtoint     strtoll
+#endif
 #else
 #define json_strtoint     strtol
 #endif
@@ -455,14 +469,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 
     if(c == '0') {
         c = lex_get_save(lex, error);
-        if(isdigit(c)) {
+        if(l_isdigit(c)) {
             lex_unget_unsave(lex, c);
             goto out;
         }
     }
-    else if(isdigit(c)) {
+    else if(l_isdigit(c)) {
         c = lex_get_save(lex, error);
-        while(isdigit(c))
+        while(l_isdigit(c))
             c = lex_get_save(lex, error);
     }
     else {
@@ -496,14 +510,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 
     if(c == '.') {
         c = lex_get(lex, error);
-        if(!isdigit(c)) {
+        if(!l_isdigit(c)) {
             lex_unget(lex, c);
             goto out;
         }
         lex_save(lex, c);
 
         c = lex_get_save(lex, error);
-        while(isdigit(c))
+        while(l_isdigit(c))
             c = lex_get_save(lex, error);
     }
 
@@ -512,23 +526,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
         if(c == '+' || c == '-')
             c = lex_get_save(lex, error);
 
-        if(!isdigit(c)) {
+        if(!l_isdigit(c)) {
             lex_unget_unsave(lex, c);
             goto out;
         }
 
         c = lex_get_save(lex, error);
-        while(isdigit(c))
+        while(l_isdigit(c))
             c = lex_get_save(lex, error);
     }
 
     lex_unget_unsave(lex, c);
 
-    saved_text = strbuffer_value(&lex->saved_text);
-    value = strtod(saved_text, &end);
-    assert(end == saved_text + lex->saved_text.length);
-
-    if(errno == ERANGE && value != 0) {
+    if(jsonp_strtod(&lex->saved_text, &value)) {
         error_set(error, lex, "real number overflow");
         goto out;
     }
@@ -574,17 +584,17 @@ static int lex_scan(lex_t *lex, json_error_t *error)
     else if(c == '"')
         lex_scan_string(lex, error);
 
-    else if(isdigit(c) || c == '-') {
+    else if(l_isdigit(c) || c == '-') {
         if(lex_scan_number(lex, c, error))
             goto out;
     }
 
-    else if(isupper(c) || islower(c)) {
+    else if(l_isalpha(c)) {
         /* eat up the whole identifier for clearer error messages */
         const char *saved_text;
 
         c = lex_get_save(lex, error);
-        while(isupper(c) || islower(c))
+        while(l_isalpha(c))
             c = lex_get_save(lex, error);
         lex_unget_unsave(lex, c);
 
@@ -642,9 +652,9 @@ static void lex_close(lex_t *lex)
 
 /*** parser ***/
 
-static json_t *parse_value(lex_t *lex, json_error_t *error);
+static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
 
-static json_t *parse_object(lex_t *lex, json_error_t *error)
+static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
 {
     json_t *object = json_object();
     if(!object)
@@ -667,6 +677,14 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
         if(!key)
             return NULL;
 
+        if(flags & JSON_REJECT_DUPLICATES) {
+            if(json_object_get(object, key)) {
+                jsonp_free(key);
+                error_set(error, lex, "duplicate object key");
+                goto error;
+            }
+        }
+
         lex_scan(lex, error);
         if(lex->token != ':') {
             jsonp_free(key);
@@ -675,7 +693,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
         }
 
         lex_scan(lex, error);
-        value = parse_value(lex, error);
+        value = parse_value(lex, flags, error);
         if(!value) {
             jsonp_free(key);
             goto error;
@@ -709,7 +727,7 @@ error:
     return NULL;
 }
 
-static json_t *parse_array(lex_t *lex, json_error_t *error)
+static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
 {
     json_t *array = json_array();
     if(!array)
@@ -720,7 +738,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
         return array;
 
     while(lex->token) {
-        json_t *elem = parse_value(lex, error);
+        json_t *elem = parse_value(lex, flags, error);
         if(!elem)
             goto error;
 
@@ -749,7 +767,7 @@ error:
     return NULL;
 }
 
-static json_t *parse_value(lex_t *lex, json_error_t *error)
+static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
 {
     json_t *json;
 
@@ -782,11 +800,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
             break;
 
         case '{':
-            json = parse_object(lex, error);
+            json = parse_object(lex, flags, error);
             break;
 
         case '[':
-            json = parse_array(lex, error);
+            json = parse_array(lex, flags, error);
             break;
 
         case TOKEN_INVALID:
@@ -804,15 +822,37 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
     return json;
 }
 
-static json_t *parse_json(lex_t *lex, json_error_t *error)
+static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
 {
+    json_t *result;
+
     lex_scan(lex, error);
-    if(lex->token != '[' && lex->token != '{') {
-        error_set(error, lex, "'[' or '{' expected");
+    if(!(flags & JSON_DECODE_ANY)) {
+        if(lex->token != '[' && lex->token != '{') {
+            error_set(error, lex, "'[' or '{' expected");
+            return NULL;
+        }
+    }
+
+    result = parse_value(lex, flags, error);
+    if(!result)
         return NULL;
+
+    if(!(flags & JSON_DISABLE_EOF_CHECK)) {
+        lex_scan(lex, error);
+        if(lex->token != TOKEN_EOF) {
+            error_set(error, lex, "end of file expected");
+            json_decref(result);
+            return NULL;
+        }
     }
 
-    return parse_value(lex, error);
+    if(error) {
+        /* Save the position even though there was no error */
+        error->position = lex->stream.position;
+    }
+
+    return result;
 }
 
 typedef struct
@@ -841,7 +881,12 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
     json_t *result;
     string_data_t stream_data;
 
-    (void)flags; /* unused */
+    jsonp_error_init(error, "<string>");
+
+    if (string == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
 
     stream_data.data = string;
     stream_data.pos = 0;
@@ -849,20 +894,53 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
     if(lex_init(&lex, string_get, (void *)&stream_data))
         return NULL;
 
-    jsonp_error_init(error, "<string>");
+    result = parse_json(&lex, flags, error);
 
-    result = parse_json(&lex, error);
-    if(!result)
-        goto out;
+    lex_close(&lex);
+    return result;
+}
+
+typedef struct
+{
+    const char *data;
+    size_t len;
+    size_t pos;
+} buffer_data_t;
+
+static int buffer_get(void *data)
+{
+    char c;
+    buffer_data_t *stream = data;
+    if(stream->pos >= stream->len)
+      return EOF;
+
+    c = stream->data[stream->pos];
+    stream->pos++;
+    return (unsigned char)c;
+}
+
+json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
+{
+    lex_t lex;
+    json_t *result;
+    buffer_data_t stream_data;
 
-    lex_scan(&lex, error);
-    if(lex.token != TOKEN_EOF) {
-        error_set(error, &lex, "end of file expected");
-        json_decref(result);
-        result = NULL;
+    jsonp_error_init(error, "<buffer>");
+
+    if (buffer == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
     }
 
-out:
+    stream_data.data = buffer;
+    stream_data.pos = 0;
+    stream_data.len = buflen;
+
+    if(lex_init(&lex, buffer_get, (void *)&stream_data))
+        return NULL;
+
+    result = parse_json(&lex, flags, error);
+
     lex_close(&lex);
     return result;
 }
@@ -872,10 +950,6 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
     lex_t lex;
     const char *source;
     json_t *result;
-    (void)flags; /* unused */
-
-    if(lex_init(&lex, (get_func)fgetc, input))
-        return NULL;
 
     if(input == stdin)
         source = "<stdin>";
@@ -884,18 +958,16 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
 
     jsonp_error_init(error, source);
 
-    result = parse_json(&lex, error);
-    if(!result)
-        goto out;
-
-    lex_scan(&lex, error);
-    if(lex.token != TOKEN_EOF) {
-        error_set(error, &lex, "end of file expected");
-        json_decref(result);
-        result = NULL;
+    if (input == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
     }
 
-out:
+    if(lex_init(&lex, (get_func)fgetc, input))
+        return NULL;
+
+    result = parse_json(&lex, flags, error);
+
     lex_close(&lex);
     return result;
 }
@@ -907,7 +979,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
 
     jsonp_error_init(error, path);
 
-    fp = fopen(path, "r");
+    if (path == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
+    fp = fopen(path, "rb");
     if(!fp)
     {
         error_set(error, NULL, "unable to open %s: %s",
@@ -920,3 +997,58 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
     fclose(fp);
     return result;
 }
+
+#define MAX_BUF_LEN 1024
+
+typedef struct
+{
+    char data[MAX_BUF_LEN];
+    size_t len;
+    size_t pos;
+    json_load_callback_t callback;
+    void *arg;
+} callback_data_t;
+
+static int callback_get(void *data)
+{
+    char c;
+    callback_data_t *stream = data;
+
+    if(stream->pos >= stream->len) {
+        stream->pos = 0;
+        stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg);
+        if(stream->len == 0 || stream->len == (size_t)-1)
+            return EOF;
+    }
+
+    c = stream->data[stream->pos];
+    stream->pos++;
+    return (unsigned char)c;
+}
+
+json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error)
+{
+    lex_t lex;
+    json_t *result;
+
+    callback_data_t stream_data;
+
+    memset(&stream_data, 0, sizeof(stream_data));
+    stream_data.callback = callback;
+    stream_data.arg = arg;
+
+    jsonp_error_init(error, "<callback>");
+
+    if (callback == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
+    if(lex_init(&lex, (get_func)callback_get, &stream_data))
+        return NULL;
+
+    result = parse_json(&lex, flags, error);
+
+    lex_close(&lex);
+    return result;
+}
index 127b5ac..543ecc4 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
- * Copyright (c) 2011 Basile Starynkevitch  <basile@starynkevitch.net>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
  *
  * Jansson is free software; you can redistribute it and/or modify it
  * under the terms of the MIT license. See LICENSE for details.
@@ -9,7 +9,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 
 /* memory function pointers */
index 5001764..39db9b8 100644 (file)
@@ -1,13 +1,13 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
- * Copyright (c) 2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  */
 
 #include <string.h>
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 #include "utf.h"
 
@@ -233,12 +233,12 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
     */
     hashtable_t key_set;
 
-    if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) {
+    if(hashtable_init(&key_set)) {
         set_error(s, "<internal>", "Out of memory");
         return -1;
     }
 
-    if(!json_is_object(root)) {
+    if(root && !json_is_object(root)) {
         set_error(s, "<validation>", "Expected object, got %s",
                   type_name(root));
         goto out;
@@ -248,6 +248,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
     while(s->token != '}') {
         const char *key;
         json_t *value;
+        int opt = 0;
 
         if(strict != 0) {
             set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
@@ -279,23 +280,34 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
 
         next_token(s);
 
-        value = json_object_get(root, key);
-        if(!value) {
-            set_error(s, "<validation>", "Object item not found: %s", key);
-            goto out;
+        if(s->token == '?') {
+            opt = 1;
+            next_token(s);
+        }
+
+        if(!root) {
+            /* skipping */
+            value = NULL;
+        }
+        else {
+            value = json_object_get(root, key);
+            if(!value && !opt) {
+                set_error(s, "<validation>", "Object item not found: %s", key);
+                goto out;
+            }
         }
 
         if(unpack(s, value, ap))
             goto out;
 
-        hashtable_set(&key_set, (void *)key, NULL);
+        hashtable_set(&key_set, key, 0, json_null());
         next_token(s);
     }
 
     if(strict == 0 && (s->flags & JSON_STRICT))
         strict = 1;
 
-    if(strict == 1 && key_set.size != json_object_size(root)) {
+    if(root && strict == 1 && key_set.size != json_object_size(root)) {
         long diff = (long)json_object_size(root) - (long)key_set.size;
         set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
         goto out;
@@ -313,7 +325,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
     size_t i = 0;
     int strict = 0;
 
-    if(!json_is_array(root)) {
+    if(root && !json_is_array(root)) {
         set_error(s, "<validation>", "Expected array, got %s", type_name(root));
         return -1;
     }
@@ -346,11 +358,17 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
             return -1;
         }
 
-        value = json_array_get(root, i);
-        if(!value) {
-            set_error(s, "<validation>", "Array index %lu out of range",
-                      (unsigned long)i);
-            return -1;
+        if(!root) {
+            /* skipping */
+            value = NULL;
+        }
+        else {
+            value = json_array_get(root, i);
+            if(!value) {
+                set_error(s, "<validation>", "Array index %lu out of range",
+                          (unsigned long)i);
+                return -1;
+            }
         }
 
         if(unpack(s, value, ap))
@@ -363,7 +381,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
     if(strict == 0 && (s->flags & JSON_STRICT))
         strict = 1;
 
-    if(strict == 1 && i != json_array_size(root)) {
+    if(root && strict == 1 && i != json_array_size(root)) {
         long diff = (long)json_array_size(root) - (long)i;
         set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
         return -1;
@@ -383,99 +401,118 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
             return unpack_array(s, root, ap);
 
         case 's':
-            if(!json_is_string(root)) {
+            if(root && !json_is_string(root)) {
                 set_error(s, "<validation>", "Expected string, got %s",
                           type_name(root));
                 return -1;
             }
 
             if(!(s->flags & JSON_VALIDATE_ONLY)) {
-                const char **str;
+                const char **target;
 
-                str = va_arg(*ap, const char **);
-                if(!str) {
+                target = va_arg(*ap, const char **);
+                if(!target) {
                     set_error(s, "<args>", "NULL string argument");
                     return -1;
                 }
 
-                *str = json_string_value(root);
+                if(root)
+                    *target = json_string_value(root);
             }
             return 0;
 
         case 'i':
-            if(!json_is_integer(root)) {
+            if(root && !json_is_integer(root)) {
                 set_error(s, "<validation>", "Expected integer, got %s",
                           type_name(root));
                 return -1;
             }
 
-            if(!(s->flags & JSON_VALIDATE_ONLY))
-                *va_arg(*ap, int*) = json_integer_value(root);
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                int *target = va_arg(*ap, int*);
+                if(root)
+                    *target = (int)json_integer_value(root);
+            }
 
             return 0;
 
         case 'I':
-            if(!json_is_integer(root)) {
+            if(root && !json_is_integer(root)) {
                 set_error(s, "<validation>", "Expected integer, got %s",
                           type_name(root));
                 return -1;
             }
 
-            if(!(s->flags & JSON_VALIDATE_ONLY))
-                *va_arg(*ap, json_int_t*) = json_integer_value(root);
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                json_int_t *target = va_arg(*ap, json_int_t*);
+                if(root)
+                    *target = json_integer_value(root);
+            }
 
             return 0;
 
         case 'b':
-            if(!json_is_boolean(root)) {
+            if(root && !json_is_boolean(root)) {
                 set_error(s, "<validation>", "Expected true or false, got %s",
                           type_name(root));
                 return -1;
             }
 
-            if(!(s->flags & JSON_VALIDATE_ONLY))
-                *va_arg(*ap, int*) = json_is_true(root);
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                int *target = va_arg(*ap, int*);
+                if(root)
+                    *target = json_is_true(root);
+            }
 
             return 0;
 
         case 'f':
-            if(!json_is_real(root)) {
+            if(root && !json_is_real(root)) {
                 set_error(s, "<validation>", "Expected real, got %s",
                           type_name(root));
                 return -1;
             }
 
-            if(!(s->flags & JSON_VALIDATE_ONLY))
-                *va_arg(*ap, double*) = json_real_value(root);
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                double *target = va_arg(*ap, double*);
+                if(root)
+                    *target = json_real_value(root);
+            }
 
             return 0;
 
         case 'F':
-            if(!json_is_number(root)) {
+            if(root && !json_is_number(root)) {
                 set_error(s, "<validation>", "Expected real or integer, got %s",
                           type_name(root));
                 return -1;
             }
 
-            if(!(s->flags & JSON_VALIDATE_ONLY))
-                *va_arg(*ap, double*) = json_number_value(root);
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                double *target = va_arg(*ap, double*);
+                if(root)
+                    *target = json_number_value(root);
+            }
 
             return 0;
 
         case 'O':
-            if(!(s->flags & JSON_VALIDATE_ONLY))
+            if(root && !(s->flags & JSON_VALIDATE_ONLY))
                 json_incref(root);
             /* Fall through */
 
         case 'o':
-            if(!(s->flags & JSON_VALIDATE_ONLY))
-                *va_arg(*ap, json_t**) = root;
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                json_t **target = va_arg(*ap, json_t**);
+                if(root)
+                    *target = root;
+            }
 
             return 0;
 
         case 'n':
             /* Never assign, just validate */
-            if(!json_is_null(root)) {
+            if(root && !json_is_null(root)) {
                 set_error(s, "<validation>", "Expected null, got %s",
                           type_name(root));
                 return -1;
index 758e95e..6d4edd6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
 
 #define STRBUFFER_MIN_SIZE  16
 #define STRBUFFER_FACTOR    2
+#define STRBUFFER_SIZE_MAX  ((size_t)-1)
 
 int strbuffer_init(strbuffer_t *strbuff)
 {
     strbuff->size = STRBUFFER_MIN_SIZE;
     strbuff->length = 0;
 
-    strbuff->value = malloc(strbuff->size);
+    strbuff->value = jsonp_malloc(strbuff->size);
     if(!strbuff->value)
         return -1;
 
@@ -30,7 +31,7 @@ int strbuffer_init(strbuffer_t *strbuff)
 
 void strbuffer_close(strbuffer_t *strbuff)
 {
-    free(strbuff->value);
+    jsonp_free(strbuff->value);
     strbuff->size = 0;
     strbuff->length = 0;
     strbuff->value = NULL;
@@ -64,13 +65,19 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
     return strbuffer_append_bytes(strbuff, &byte, 1);
 }
 
-int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
+int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
 {
-    if(strbuff->length + size >= strbuff->size)
+    if(size >= strbuff->size - strbuff->length)
     {
         size_t new_size;
         char *new_value;
 
+        /* avoid integer overflow */
+        if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
+            || size > STRBUFFER_SIZE_MAX - 1
+            || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
+            return -1;
+
         new_size = max(strbuff->size * STRBUFFER_FACTOR,
                        strbuff->length + size + 1);
 
index b21ef8b..23f8fff 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -10,8 +10,8 @@
 
 typedef struct {
     char *value;
-    int length;   /* bytes used */
-    int size;     /* bytes allocated */
+    size_t length;   /* bytes used */
+    size_t size;     /* bytes allocated */
 } strbuffer_t;
 
 int strbuffer_init(strbuffer_t *strbuff);
@@ -24,7 +24,7 @@ char *strbuffer_steal_value(strbuffer_t *strbuff);
 
 int strbuffer_append(strbuffer_t *strbuff, const char *string);
 int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
-int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
+int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
 
 char strbuffer_pop(strbuffer_t *strbuff);
 
diff --git a/src/strconv.c b/src/strconv.c
new file mode 100644 (file)
index 0000000..caa9ab8
--- /dev/null
@@ -0,0 +1,129 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "jansson_private.h"
+#include "strbuffer.h"
+
+#if JSON_HAVE_LOCALECONV
+#include <locale.h>
+
+/*
+  - This code assumes that the decimal separator is exactly one
+    character.
+
+  - If setlocale() is called by another thread between the call to
+    localeconv() and the call to sprintf() or strtod(), the result may
+    be wrong. setlocale() is not thread-safe and should not be used
+    this way. Multi-threaded programs should use uselocale() instead.
+*/
+
+static void to_locale(strbuffer_t *strbuffer)
+{
+    const char *point;
+    char *pos;
+
+    point = localeconv()->decimal_point;
+    if(*point == '.') {
+        /* No conversion needed */
+        return;
+    }
+
+    pos = strchr(strbuffer->value, '.');
+    if(pos)
+        *pos = *point;
+}
+
+static void from_locale(char *buffer)
+{
+    const char *point;
+    char *pos;
+
+    point = localeconv()->decimal_point;
+    if(*point == '.') {
+        /* No conversion needed */
+        return;
+    }
+
+    pos = strchr(buffer, *point);
+    if(pos)
+        *pos = '.';
+}
+#endif
+
+int jsonp_strtod(strbuffer_t *strbuffer, double *out)
+{
+    double value;
+    char *end;
+
+#if JSON_HAVE_LOCALECONV
+    to_locale(strbuffer);
+#endif
+
+    errno = 0;
+    value = strtod(strbuffer->value, &end);
+    assert(end == strbuffer->value + strbuffer->length);
+
+    if(errno == ERANGE && value != 0) {
+        /* Overflow */
+        return -1;
+    }
+
+    *out = value;
+    return 0;
+}
+
+int jsonp_dtostr(char *buffer, size_t size, double value)
+{
+    int ret;
+    char *start, *end;
+    size_t length;
+
+    ret = snprintf(buffer, size, "%.17g", value);
+    if(ret < 0)
+        return -1;
+
+    length = (size_t)ret;
+    if(length >= size)
+        return -1;
+
+#if JSON_HAVE_LOCALECONV
+    from_locale(buffer);
+#endif
+
+    /* Make sure there's a dot or 'e' in the output. Otherwise
+       a real is converted to an integer when decoding */
+    if(strchr(buffer, '.') == NULL &&
+       strchr(buffer, 'e') == NULL)
+    {
+        if(length + 3 >= size) {
+            /* No space to append ".0" */
+            return -1;
+        }
+        buffer[length] = '.';
+        buffer[length + 1] = '0';
+        buffer[length + 2] = '\0';
+        length += 2;
+    }
+
+    /* Remove leading '+' from positive exponent. Also remove leading
+       zeros from exponents (added by some printf() implementations) */
+    start = strchr(buffer, 'e');
+    if(start) {
+        start++;
+        end = start + 1;
+
+        if(*start == '-')
+            start++;
+
+        while(*end == '0')
+            end++;
+
+        if(end != start) {
+            memmove(start, end, length - (size_t)(end - buffer));
+            length -= (size_t)(end - start);
+        }
+    }
+
+    return (int)length;
+}
index f48c2e7..0359ee2 100644 (file)
--- a/src/utf.c
+++ b/src/utf.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
index 0c3ab31..2495cdd 100644 (file)
--- a/src/utf.h
+++ b/src/utf.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
index 937aa95..ba9908e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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 <stddef.h>
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 
-#include <jansson.h>
+#include "jansson.h"
 #include "hashtable.h"
 #include "jansson_private.h"
 #include "utf.h"
 
+/* Work around nonstandard isnan() and isinf() implementations */
+#ifndef isnan
+static JSON_INLINE int isnan(double x) { return x != x; }
+#endif
+#ifndef isinf
+static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
+#endif
 
 static JSON_INLINE void json_init(json_t *json, json_type type)
 {
@@ -26,39 +34,6 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
 
 /*** object ***/
 
-/* This macro just returns a pointer that's a few bytes backwards from
-   string. This makes it possible to pass a pointer to object_key_t
-   when only the string inside it is used, without actually creating
-   an object_key_t instance. */
-#define string_to_key(string)  container_of(string, object_key_t, key)
-
-size_t jsonp_hash_key(const void *ptr)
-{
-    const char *str = ((const object_key_t *)ptr)->key;
-
-    size_t hash = 5381;
-    size_t c;
-
-    while((c = (size_t)*str))
-    {
-        hash = ((hash << 5) + hash) + c;
-        str++;
-    }
-
-    return hash;
-}
-
-int jsonp_key_equal(const void *ptr1, const void *ptr2)
-{
-    return strcmp(((const object_key_t *)ptr1)->key,
-                  ((const object_key_t *)ptr2)->key) == 0;
-}
-
-static void value_decref(void *value)
-{
-    json_decref((json_t *)value);
-}
-
 json_t *json_object(void)
 {
     json_object_t *object = jsonp_malloc(sizeof(json_object_t));
@@ -66,9 +41,7 @@ json_t *json_object(void)
         return NULL;
     json_init(&object->json, JSON_OBJECT);
 
-    if(hashtable_init(&object->hashtable,
-                      jsonp_hash_key, jsonp_key_equal,
-                      jsonp_free, value_decref))
+    if(hashtable_init(&object->hashtable))
     {
         jsonp_free(object);
         return NULL;
@@ -105,35 +78,24 @@ json_t *json_object_get(const json_t *json, const char *key)
         return NULL;
 
     object = json_to_object(json);
-    return hashtable_get(&object->hashtable, string_to_key(key));
+    return hashtable_get(&object->hashtable, key);
 }
 
 int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
 {
     json_object_t *object;
-    object_key_t *k;
 
-    if(!key || !value)
+    if(!value)
         return -1;
 
-    if(!json_is_object(json) || json == value)
+    if(!key || !json_is_object(json) || json == value)
     {
         json_decref(value);
         return -1;
     }
     object = json_to_object(json);
 
-    /* offsetof(...) returns the size of object_key_t without the
-       last, flexible member. This way, the correct amount is
-       allocated. */
-    k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1);
-    if(!k)
-        return -1;
-
-    k->serial = object->serial++;
-    strcpy(k->key, key);
-
-    if(hashtable_set(&object->hashtable, k, value))
+    if(hashtable_set(&object->hashtable, key, object->serial++, value))
     {
         json_decref(value);
         return -1;
@@ -161,7 +123,7 @@ int json_object_del(json_t *json, const char *key)
         return -1;
 
     object = json_to_object(json);
-    return hashtable_del(&object->hashtable, string_to_key(key));
+    return hashtable_del(&object->hashtable, key);
 }
 
 int json_object_clear(json_t *json)
@@ -172,30 +134,56 @@ int json_object_clear(json_t *json)
         return -1;
 
     object = json_to_object(json);
+
     hashtable_clear(&object->hashtable);
+    object->serial = 0;
 
     return 0;
 }
 
 int json_object_update(json_t *object, json_t *other)
 {
-    void *iter;
+    const char *key;
+    json_t *value;
 
     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);
-
+    json_object_foreach(other, key, value) {
         if(json_object_set_nocheck(object, key, value))
             return -1;
+    }
+
+    return 0;
+}
+
+int json_object_update_existing(json_t *object, json_t *other)
+{
+    const char *key;
+    json_t *value;
+
+    if(!json_is_object(object) || !json_is_object(other))
+        return -1;
 
-        iter = json_object_iter_next(other, iter);
+    json_object_foreach(other, key, value) {
+        if(json_object_get(object, key))
+            json_object_set_nocheck(object, key, value);
+    }
+
+    return 0;
+}
+
+int json_object_update_missing(json_t *object, json_t *other)
+{
+    const char *key;
+    json_t *value;
+
+    if(!json_is_object(object) || !json_is_object(other))
+        return -1;
+
+    json_object_foreach(other, key, value) {
+        if(!json_object_get(object, key))
+            json_object_set_nocheck(object, key, value);
     }
 
     return 0;
@@ -220,7 +208,7 @@ void *json_object_iter_at(json_t *json, const char *key)
         return NULL;
 
     object = json_to_object(json);
-    return hashtable_iter_at(&object->hashtable, string_to_key(key));
+    return hashtable_iter_at(&object->hashtable, key);
 }
 
 void *json_object_iter_next(json_t *json, void *iter)
@@ -234,20 +222,12 @@ void *json_object_iter_next(json_t *json, void *iter)
     return hashtable_iter_next(&object->hashtable, iter);
 }
 
-const object_key_t *jsonp_object_iter_fullkey(void *iter)
-{
-    if(!iter)
-        return NULL;
-
-    return hashtable_iter_key(iter);
-}
-
 const char *json_object_iter_key(void *iter)
 {
     if(!iter)
         return NULL;
 
-    return jsonp_object_iter_fullkey(iter)->key;
+    return hashtable_iter_key(iter);
 }
 
 json_t *json_object_iter_value(void *iter)
@@ -260,38 +240,34 @@ json_t *json_object_iter_value(void *iter)
 
 int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
 {
-    json_object_t *object;
-
     if(!json_is_object(json) || !iter || !value)
         return -1;
 
-    object = json_to_object(json);
-    hashtable_iter_set(&object->hashtable, iter, value);
-
+    hashtable_iter_set(iter, value);
     return 0;
 }
 
+void *json_object_key_to_iter(const char *key)
+{
+    if(!key)
+        return NULL;
+
+    return hashtable_key_to_iter(key);
+}
+
 static int json_object_equal(json_t *object1, json_t *object2)
 {
-    void *iter;
+    const char *key;
+    json_t *value1, *value2;
 
     if(json_object_size(object1) != json_object_size(object2))
         return 0;
 
-    iter = json_object_iter(object1);
-    while(iter)
-    {
-        const char *key;
-        json_t *value1, *value2;
-
-        key = json_object_iter_key(iter);
-        value1 = json_object_iter_value(iter);
+    json_object_foreach(object1, key, value1) {
         value2 = json_object_get(object2, key);
 
         if(!json_equal(value1, value2))
             return 0;
-
-        iter = json_object_iter_next(object1, iter);
     }
 
     return 1;
@@ -300,50 +276,34 @@ static int json_object_equal(json_t *object1, json_t *object2)
 static json_t *json_object_copy(json_t *object)
 {
     json_t *result;
-    void *iter;
+
+    const char *key;
+    json_t *value;
 
     result = json_object();
     if(!result)
         return NULL;
 
-    iter = json_object_iter(object);
-    while(iter)
-    {
-        const char *key;
-        json_t *value;
-
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
+    json_object_foreach(object, key, value)
         json_object_set_nocheck(result, key, value);
 
-        iter = json_object_iter_next(object, iter);
-    }
-
     return result;
 }
 
 static json_t *json_object_deep_copy(json_t *object)
 {
     json_t *result;
-    void *iter;
+
+    const char *key;
+    json_t *value;
 
     result = json_object();
     if(!result)
         return NULL;
 
-    iter = json_object_iter(object);
-    while(iter)
-    {
-        const char *key;
-        json_t *value;
-
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
+    json_object_foreach(object, key, value)
         json_object_set_new_nocheck(result, key, json_deep_copy(value));
 
-        iter = json_object_iter_next(object, iter);
-    }
-
     return result;
 }
 
@@ -689,6 +649,9 @@ int json_string_set_nocheck(json_t *json, const char *value)
     char *dup;
     json_string_t *string;
 
+    if(!json_is_string(json) || !value)
+        return -1;
+
     dup = jsonp_strdup(value);
     if(!dup)
         return -1;
@@ -776,7 +739,12 @@ static json_t *json_integer_copy(json_t *integer)
 
 json_t *json_real(double value)
 {
-    json_real_t *real = jsonp_malloc(sizeof(json_real_t));
+    json_real_t *real;
+
+    if(isnan(value) || isinf(value))
+        return NULL;
+
+    real = jsonp_malloc(sizeof(json_real_t));
     if(!real)
         return NULL;
     json_init(&real->json, JSON_REAL);
@@ -795,8 +763,8 @@ double json_real_value(const json_t *json)
 
 int json_real_set(json_t *json, double value)
 {
-    if(!json_is_real(json))
-        return 0;
+    if(!json_is_real(json) || isnan(value) || isinf(value))
+        return -1;
 
     json_to_real(json)->value = value;
 
@@ -824,7 +792,7 @@ static json_t *json_real_copy(json_t *real)
 double json_number_value(const json_t *json)
 {
     if(json_is_integer(json))
-        return json_integer_value(json);
+        return (double)json_integer_value(json);
     else if(json_is_real(json))
         return json_real_value(json);
     else
index 7b11ea0..ae6c8f9 100644 (file)
@@ -4,11 +4,14 @@ suites/api/test_array
 suites/api/test_copy
 suites/api/test_cpp
 suites/api/test_dump
+suites/api/test_dump_callback
 suites/api/test_equal
 suites/api/test_load
+suites/api/test_loadb
 suites/api/test_memory_funcs
 suites/api/test_number
 suites/api/test_object
 suites/api/test_pack
 suites/api/test_simple
 suites/api/test_unpack
+suites/api/test_load_callback
index 1ed0c57..40183c7 100644 (file)
@@ -1,16 +1,31 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 #include <jansson.h>
 
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#if _WIN32
+#include <io.h>  /* for _setmode() */
+#include <fcntl.h>  /* for _O_BINARY */
+#endif
+
+#define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t')
+
 static int getenv_int(const char *name)
 {
     char *value, *end;
@@ -34,14 +49,14 @@ static const char *strip(char *str)
 {
     size_t length;
     char *result = str;
-    while(*result && isspace(*result))
+    while(*result && l_isspace(*result))
         result++;
 
     length = strlen(result);
     if(length == 0)
         return result;
 
-    while(isspace(result[length - 1]))
+    while(l_isspace(result[length - 1]))
         result[--length] = '\0';
 
     return result;
@@ -55,11 +70,22 @@ int main(int argc, char *argv[])
     json_t *json;
     json_error_t error;
 
+#if HAVE_SETLOCALE
+    setlocale(LC_ALL, "");
+#endif
+
     if(argc != 1) {
         fprintf(stderr, "usage: %s\n", argv[0]);
         return 2;
     }
 
+#ifdef _WIN32
+    /* On Windows, set stdout and stderr to binary mode to avoid
+       outputting DOS line terminators */
+    _setmode(_fileno(stdout), _O_BINARY);
+    _setmode(_fileno(stderr), _O_BINARY);
+#endif
+
     indent = getenv_int("JSON_INDENT");
     if(indent < 0 || indent > 255) {
         fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
index ea35424..4cbaa8b 100755 (executable)
@@ -22,11 +22,12 @@ fi
 
 [ -z "$STOP" ] && STOP=0
 
-export suites_srcdir=$top_srcdir/test/suites
-export suites_builddir=suites
-export scriptdir=$top_srcdir/test/scripts
-export logdir=logs
-export bindir=bin
+suites_srcdir=$top_srcdir/test/suites
+suites_builddir=suites
+scriptdir=$top_srcdir/test/scripts
+logdir=logs
+bindir=bin
+export suites_srcdir suites_builddir scriptdir logdir bindir
 
 passed=0
 failed=0
index adf34d4..39ebfc4 100644 (file)
@@ -1,8 +1,20 @@
-# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+# Copyright (c) 2009-2012 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.
 
+die() {
+    echo "$1" >&2
+    exit 1
+}
+
+[ -n "$1" ] || die "Usage: $0 suite-name"
+[ -n "$bindir" ] || die "Set bindir"
+[ -n "$logdir" ] || die "Set logdir"
+[ -n "$scriptdir" ] || die "Set scriptdir"
+[ -n "$suites_srcdir" ] || die "Set suites_srcdir"
+[ -n "$suites_builddir" ] || die "Set suites_builddir"
+
 json_process=$bindir/json_process
 
 suite_name=$1
@@ -10,7 +22,6 @@ suite_srcdir=$suites_srcdir/$suite_name
 suite_builddir=$suites_builddir/$suite_name
 suite_log=$logdir/$suite_name
 
-
 [ -z "$VERBOSE" ] && VERBOSE=0
 [ -z "$STOP" ] && STOP=0
 
index 0eb39f4..5c1c794 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+# Copyright (c) 2009-2012 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.
index 0021f93..9e60f48 100644 (file)
@@ -1,11 +1,14 @@
-EXTRA_DIST = run
+EXTRA_DIST = run check-exports
 
 check_PROGRAMS = \
        test_array \
        test_copy \
        test_dump \
+       test_dump_callback \
        test_equal \
        test_load \
+       test_loadb \
+       test_load_callback \
        test_memory_funcs \
        test_number \
        test_object \
@@ -16,7 +19,9 @@ check_PROGRAMS = \
 test_array_SOURCES = test_array.c util.h
 test_copy_SOURCES = test_copy.c util.h
 test_dump_SOURCES = test_dump.c util.h
+test_dump_callback_SOURCES = test_dump_callback.c util.h
 test_load_SOURCES = test_load.c util.h
+test_loadb_SOURCES = test_loadb.c util.h
 test_memory_funcs_SOURCES = test_memory_funcs.c util.h
 test_number_SOURCES = test_number.c util.h
 test_object_SOURCES = test_object.c util.h
index 3dc4a9e..1c2ba9a 100755 (executable)
@@ -1,98 +1,21 @@
 #!/bin/sh
-
-# This tests checks that the libjansson.so exports the correct
-# symbols.
-
-# The list of symbols that the shared object should export
-sort >$test_log/exports <<EOF
-json_delete
-json_true
-json_false
-json_null
-json_string
-json_string_nocheck
-json_string_value
-json_string_set
-json_string_set_nocheck
-json_integer
-json_integer_value
-json_integer_set
-json_real
-json_real_value
-json_real_set
-json_number_value
-json_array
-json_array_size
-json_array_get
-json_array_set_new
-json_array_append_new
-json_array_insert_new
-json_array_remove
-json_array_clear
-json_array_extend
-json_object
-json_object_size
-json_object_get
-json_object_set_new
-json_object_set_new_nocheck
-json_object_del
-json_object_clear
-json_object_update
-json_object_iter
-json_object_iter_at
-json_object_iter_next
-json_object_iter_key
-json_object_iter_value
-json_object_iter_set_new
-json_dumps
-json_dumpf
-json_dump_file
-json_loads
-json_loadf
-json_load_file
-json_equal
-json_copy
-json_deep_copy
-json_pack
-json_pack_ex
-json_vpack_ex
-json_unpack
-json_unpack_ex
-json_vunpack_ex
-json_set_alloc_funcs
-EOF
-
-# The list of functions are not exported in the library because they
-# are macros or static inline functions. This is only the make the
-# list complete, there are not used by the test.
-sort >$test_log/macros_or_inline <<EOF
-json_typeof
-json_incref
-json_decref
-json_is_object
-json_is_object
-json_is_array
-json_is_string
-json_is_integer
-json_is_real
-json_is_true
-json_is_false
-json_is_null
-json_is_number
-json_is_boolean
-json_array_set
-json_array_append
-json_array_insert
-json_object_set
-json_object_set_nocheck
-EOF
+#
+# This test checks that libjansson.so exports the correct symbols.
+#
 
 SOFILE="../src/.libs/libjansson.so"
 
+# The list of symbols, which the shared object should export, is read
+# from the def file, which is used in Windows builds
+grep 'json_' $top_srcdir/src/jansson.def \
+    | sed -e 's/ //g' \
+    | sort \
+    >$test_log/exports
+
 nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \
     || exit 77  # Skip if "nm -D" doesn't seem to work
 
-grep ' T ' $test_log/symbols | cut -d' ' -f3 | sort >$test_log/output
+grep ' [DT] ' $test_log/symbols | cut -d' ' -f3 | sort >$test_log/output
 
 if ! cmp -s $test_log/exports $test_log/output; then
     diff -u $test_log/exports $test_log/output >&2
index f1aed9b..3327e15 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+# Copyright (c) 2009-2012 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.
index 18b4652..4585db9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -387,7 +387,7 @@ static void test_circular()
 }
 
 
-int main()
+static void run_tests()
 {
     test_misc();
     test_insert();
@@ -395,6 +395,4 @@ int main()
     test_clear();
     test_extend();
     test_circular();
-
-    return 0;
 }
index 6310f69..5f22eb8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -307,7 +307,7 @@ static void test_deep_copy_object(void)
     json_decref(copy);
 }
 
-int main()
+static void run_tests()
 {
     test_copy_simple();
     test_deep_copy_simple();
@@ -315,5 +315,4 @@ int main()
     test_deep_copy_array();
     test_copy_object();
     test_deep_copy_object();
-    return 0;
 }
index 363dd10..8cd659c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -9,13 +9,13 @@
 #include <string.h>
 #include "util.h"
 
-int main()
+static void encode_twice()
 {
+    /* Encode an empty object/array, add an item, encode again */
+
     json_t *json;
     char *result;
 
-    /* Encode an empty object/array, add an item, encode again */
-
     json = json_object();
     result = json_dumps(json, 0);
     if(!result || strcmp(result, "{}"))
@@ -43,7 +43,10 @@ int main()
     free(result);
 
     json_decref(json);
+}
 
+static void circular_references()
+{
     /* Construct a JSON object/array with a circular reference:
 
        object: {"a": {"b": {"c": <circular reference to $.a>}}}
@@ -51,6 +54,10 @@ int main()
 
        Encode it, remove the circular reference and encode again.
     */
+
+    json_t *json;
+    char *result;
+
     json = json_object();
     json_object_set_new(json, "a", json_object());
     json_object_set_new(json_object_get(json, "a"), "b", json_object());
@@ -86,6 +93,74 @@ int main()
     free(result);
 
     json_decref(json);
+}
+
+static void encode_other_than_array_or_object()
+{
+    /* Encoding anything other than array or object should only
+     * succeed if the JSON_ENCODE_ANY flag is used */
+
+    json_t *json;
+    FILE *fp = NULL;
+    char *result;
+
+    json = json_string("foo");
+    if(json_dumps(json, 0) != NULL)
+        fail("json_dumps encoded a string!");
+    if(json_dumpf(json, fp, 0) == 0)
+        fail("json_dumpf encoded a string!");
+
+    result = json_dumps(json, JSON_ENCODE_ANY);
+    if(!result || strcmp(result, "\"foo\"") != 0)
+        fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
+
+    free(result);
+    json_decref(json);
+
+    json = json_integer(42);
+    if(json_dumps(json, 0) != NULL)
+        fail("json_dumps encoded an integer!");
+    if(json_dumpf(json, fp, 0) == 0)
+        fail("json_dumpf encoded an integer!");
+
+    result = json_dumps(json, JSON_ENCODE_ANY);
+    if(!result || strcmp(result, "42") != 0)
+        fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
+
+    free(result);
+    json_decref(json);
+
+
+}
+
+static void escape_slashes()
+{
+    /* Test dump escaping slashes */
+
+    json_t *json;
+    char *result;
+
+    json = json_object();
+    json_object_set_new(json, "url", json_string("https://github.com/akheron/jansson"));
+
+    result = json_dumps(json, 0);
+    if(!result || strcmp(result, "{\"url\": \"https://github.com/akheron/jansson\"}"))
+        fail("json_dumps failed to not escape slashes");
+
+    free(result);
+
+    result = json_dumps(json, JSON_ESCAPE_SLASH);
+    if(!result || strcmp(result, "{\"url\": \"https:\\/\\/github.com\\/akheron\\/jansson\"}"))
+        fail("json_dumps failed to escape slashes");
+
+    free(result);
+    json_decref(json);
+}
 
-    return 0;
+static void run_tests()
+{
+    encode_twice();
+    circular_references();
+    encode_other_than_array_or_object();
+    escape_slashes();
 }
diff --git a/test/suites/api/test_dump_callback.c b/test/suites/api/test_dump_callback.c
new file mode 100644 (file)
index 0000000..aa7296a
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <jansson.h>
+#include <string.h>
+#include <stdlib.h>
+#include "util.h"
+
+struct my_sink {
+    char *buf;
+    size_t off;
+    size_t cap;
+};
+
+static int my_writer(const char *buffer, size_t len, void *data) {
+    struct my_sink *s = data;
+    if (len > s->cap - s->off) {
+        return -1;
+    }
+    memcpy(s->buf + s->off, buffer, len);
+    s->off += len;
+    return 0;
+}
+
+static void run_tests()
+{
+    struct my_sink s;
+    json_t *json;
+    const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
+    char *dumped_to_string;
+
+    json = json_loads(str, 0, NULL);
+    if(!json) {
+        fail("json_loads failed");
+    }
+
+    dumped_to_string = json_dumps(json, 0);
+    if (!dumped_to_string) {
+        json_decref(json);
+        fail("json_dumps failed");
+    }
+
+    s.off = 0;
+    s.cap = strlen(dumped_to_string);
+    s.buf = malloc(s.cap);
+    if (!s.buf) {
+        json_decref(json);
+        free(dumped_to_string);
+        fail("malloc failed");
+    }
+
+    if (json_dump_callback(json, my_writer, &s, 0) == -1) {
+        json_decref(json);
+        free(dumped_to_string);
+        free(s.buf);
+        fail("json_dump_callback failed on an exact-length sink buffer");
+    }
+
+    if (strncmp(dumped_to_string, s.buf, s.off) != 0) {
+        json_decref(json);
+        free(dumped_to_string);
+        free(s.buf);
+        fail("json_dump_callback and json_dumps did not produce identical output");
+    }
+
+    s.off = 1;
+    if (json_dump_callback(json, my_writer, &s, 0) != -1) {
+        json_decref(json);
+        free(dumped_to_string);
+        free(s.buf);
+        fail("json_dump_callback succeeded on a short buffer when it should have failed");
+    }
+
+    json_decref(json);
+    free(dumped_to_string);
+    free(s.buf);
+}
index ba7ab43..d7e08d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -180,11 +180,10 @@ static void test_equal_complex()
     /* TODO: There's no negative test case here */
 }
 
-int main()
+static void run_tests()
 {
     test_equal_simple();
     test_equal_array();
     test_equal_object();
     test_equal_complex();
-    return 0;
 }
index b1679a3..30ba65c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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 "util.h"
 
-int main()
+static void file_not_found()
 {
     json_t *json;
     json_error_t error;
+    char *pos;
 
     json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
+    if(json)
+        fail("json_load_file returned non-NULL for a nonexistent file");
     if(error.line != -1)
         fail("json_load_file returned an invalid line number");
-    if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
+
+    /* The error message is locale specific, only check the beginning
+       of the error message. */
+
+    pos = strchr(error.text, ':');
+    if(!pos)
+        fail("json_load_file returne an invalid error message");
+
+    *pos = '\0';
+
+    if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0)
         fail("json_load_file returned an invalid error message");
+}
+
+static void reject_duplicates()
+{
+    json_error_t error;
+
+    if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
+        fail("json_loads did not detect a duplicate key");
+    check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
+}
+
+static void disable_eof_check()
+{
+    json_error_t error;
+    json_t *json;
+
+    const char *text = "{\"foo\": 1} garbage";
+
+    if(json_loads(text, 0, &error))
+        fail("json_loads did not detect garbage after JSON text");
+    check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
+
+    json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
+    if(!json)
+        fail("json_loads failed with JSON_DISABLE_EOF_CHECK");
+
+    json_decref(json);
+}
+
+static void decode_any()
+{
+    json_t *json;
+    json_error_t error;
 
-    return 0;
+    json = json_loads("\"foo\"", JSON_DECODE_ANY, &error);
+    if (!json || !json_is_string(json))
+        fail("json_load decoded any failed - string");
+    json_decref(json);
+
+    json = json_loads("42", JSON_DECODE_ANY, &error);
+    if (!json || !json_is_integer(json))
+        fail("json_load decoded any failed - integer");
+    json_decref(json);
+
+    json = json_loads("true", JSON_DECODE_ANY, &error);
+    if (!json || !json_is_true(json))
+        fail("json_load decoded any failed - boolean");
+    json_decref(json);
+
+    json = json_loads("null", JSON_DECODE_ANY, &error);
+    if (!json || !json_is_null(json))
+        fail("json_load decoded any failed - null");
+    json_decref(json);
+}
+
+static void load_wrong_args()
+{
+    json_t *json;
+    json_error_t error;
+
+    json = json_loads(NULL, 0, &error);
+    if (json)
+        fail("json_loads should return NULL if the first argument is NULL");
+
+    json = json_loadb(NULL, 0, 0, &error);
+    if (json)
+        fail("json_loadb should return NULL if the first argument is NULL");
+
+    json = json_loadf(NULL, 0, &error);
+    if (json)
+        fail("json_loadf should return NULL if the first argument is NULL");
+
+    json = json_load_file(NULL, 0, &error);
+    if (json)
+        fail("json_loadf should return NULL if the first argument is NULL");
+}
+
+static void position()
+{
+    json_t *json;
+    size_t flags = JSON_DISABLE_EOF_CHECK;
+    json_error_t error;
+
+    json = json_loads("{\"foo\": \"bar\"}", 0, &error);
+    if(error.position != 14)
+        fail("json_loads returned a wrong position");
+    json_decref(json);
+
+    json = json_loads("{\"foo\": \"bar\"} baz quux", flags, &error);
+    if(error.position != 14)
+        fail("json_loads returned a wrong position");
+    json_decref(json);
+}
+
+static void run_tests()
+{
+    file_not_found();
+    reject_duplicates();
+    disable_eof_check();
+    decode_any();
+    load_wrong_args();
+    position();
 }
diff --git a/test/suites/api/test_load_callback.c b/test/suites/api/test_load_callback.c
new file mode 100644 (file)
index 0000000..9449528
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <jansson.h>
+#include <string.h>
+#include <stdlib.h>
+#include "util.h"
+
+struct my_source {
+    const char *buf;
+    size_t off;
+    size_t cap;
+};
+
+static const char my_str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
+
+static size_t greedy_reader(void *buf, size_t buflen, void *arg)
+{
+    struct my_source *s = arg;
+    if (buflen > s->cap - s->off)
+        buflen = s->cap - s->off;
+    if (buflen > 0) {
+        memcpy(buf, s->buf + s->off, buflen);
+        s->off += buflen;
+        return buflen;
+    } else {
+        return 0;
+    }
+}
+
+static void run_tests()
+{
+    struct my_source s;
+    json_t *json;
+    json_error_t error;
+
+    s.off = 0;
+    s.cap = strlen(my_str);
+    s.buf = my_str;
+
+    json = json_load_callback(greedy_reader, &s, 0, &error);
+
+    if (!json)
+        fail("json_load_callback failed on a valid callback");
+    json_decref(json);
+
+    s.off = 0;
+    s.cap = strlen(my_str) - 1;
+    s.buf = my_str;
+
+    json = json_load_callback(greedy_reader, &s, 0, &error);
+    if (json) {
+        json_decref(json);
+        fail("json_load_callback should have failed on an incomplete stream, but it didn't");
+    }
+    if (strcmp(error.source, "<callback>") != 0) {
+        fail("json_load_callback returned an invalid error source");
+    }
+    if (strcmp(error.text, "']' expected near end of file") != 0) {
+        fail("json_load_callback returned an invalid error message for an unclosed top-level array");
+    }
+
+    json = json_load_callback(NULL, NULL, 0, &error);
+    if (json) {
+        json_decref(json);
+        fail("json_load_callback should have failed on NULL load callback, but it didn't");
+    }
+    if (strcmp(error.text, "wrong arguments") != 0) {
+        fail("json_load_callback returned an invalid error message for a NULL load callback");
+    }
+}
diff --git a/test/suites/api/test_loadb.c b/test/suites/api/test_loadb.c
new file mode 100644 (file)
index 0000000..d38c0d3
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <jansson.h>
+#include <string.h>
+#include "util.h"
+
+static void run_tests()
+{
+    json_t *json;
+    json_error_t error;
+    const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage";
+    size_t len = strlen(str) - strlen("garbage");
+
+    json = json_loadb(str, len, 0, &error);
+    if(!json) {
+        fail("json_loadb failed on a valid JSON buffer");
+    }
+    json_decref(json);
+
+    json = json_loadb(str, len - 1, 0, &error);
+    if (json) {
+        json_decref(json);
+        fail("json_loadb should have failed on an incomplete buffer, but it didn't");
+    }
+    if(error.line != 1) {
+        fail("json_loadb returned an invalid line number on fail");
+    }
+    if(strcmp(error.text, "']' expected near end of file") != 0) {
+        fail("json_loadb returned an invalid error message for an unclosed top-level array");
+    }
+}
index 1a6681f..4d6a81e 100644 (file)
@@ -39,7 +39,7 @@ static void test_simple()
     json_set_alloc_funcs(my_malloc, my_free);
     create_and_free_complex_object();
 
-    if(malloc_called != 27 || free_called != 27)
+    if(malloc_called != 20 || free_called != 20)
         fail("Custom allocation failed");
 }
 
@@ -55,14 +55,14 @@ static void *secure_malloc(size_t size)
     /* Store the memory area size in the beginning of the block */
     void *ptr = malloc(size + 8);
     *((size_t *)ptr) = size;
-    return ptr + 8;
+    return (char *)ptr + 8;
 }
 
 static void secure_free(void *ptr)
 {
     size_t size;
 
-    ptr -= 8;
+    ptr = (char *)ptr - 8;
     size = *((size_t *)ptr);
 
     /*guaranteed_*/memset(ptr, 0, size);
@@ -75,10 +75,8 @@ static void test_secure_funcs(void)
     create_and_free_complex_object();
 }
 
-int main()
+static void run_tests()
 {
     test_simple();
     test_secure_funcs();
-
-    return 0;
 }
index ff0741e..c256598 100644 (file)
@@ -1,14 +1,15 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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 <math.h>
 #include <jansson.h>
 #include "util.h"
 
-int main()
+static void run_tests()
 {
     json_t *integer, *real;
     int i;
@@ -40,5 +41,33 @@ int main()
     json_decref(integer);
     json_decref(real);
 
-    return 0;
+#ifdef NAN
+    real = json_real(NAN);
+    if(real != NULL)
+        fail("could construct a real from NaN");
+
+    real = json_real(1.0);
+    if(json_real_set(real, NAN) != -1)
+        fail("could set a real to NaN");
+
+    if(json_real_value(real) != 1.0)
+        fail("real value changed unexpectedly");
+
+    json_decref(real);
+#endif
+
+#ifdef INFINITY
+    real = json_real(INFINITY);
+    if(real != NULL)
+        fail("could construct a real from Inf");
+
+    real = json_real(1.0);
+    if(json_real_set(real, INFINITY) != -1)
+        fail("could set a real to Inf");
+
+    if(json_real_value(real) != 1.0)
+        fail("real value changed unexpectedly");
+
+    json_decref(real);
+#endif
 }
index 0499f76..b317745 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -139,6 +139,48 @@ static void test_update()
     json_decref(object);
 }
 
+static void test_conditional_updates()
+{
+    json_t *object, *other;
+
+    object = json_pack("{sisi}", "foo", 1, "bar", 2);
+    other = json_pack("{sisi}", "foo", 3, "baz", 4);
+
+    if(json_object_update_existing(object, other))
+        fail("json_object_update_existing failed");
+
+    if(json_object_size(object) != 2)
+        fail("json_object_update_existing added new items");
+
+    if(json_integer_value(json_object_get(object, "foo")) != 3)
+        fail("json_object_update_existing failed to update existing key");
+
+    if(json_integer_value(json_object_get(object, "bar")) != 2)
+        fail("json_object_update_existing updated wrong key");
+
+    json_decref(object);
+
+    object = json_pack("{sisi}", "foo", 1, "bar", 2);
+
+    if(json_object_update_missing(object, other))
+        fail("json_object_update_missing failed");
+
+    if(json_object_size(object) != 3)
+        fail("json_object_update_missing didn't add new items");
+
+    if(json_integer_value(json_object_get(object, "foo")) != 1)
+        fail("json_object_update_missing updated existing key");
+
+    if(json_integer_value(json_object_get(object, "bar")) != 2)
+        fail("json_object_update_missing updated wrong key");
+
+    if(json_integer_value(json_object_get(object, "baz")) != 4)
+        fail("json_object_update_missing didn't add new items");
+
+    json_decref(object);
+    json_decref(other);
+}
+
 static void test_circular()
 {
     json_t *object1, *object2;
@@ -437,15 +479,33 @@ static void test_preserve_order()
     json_decref(object);
 }
 
-int main()
+static void test_foreach()
+{
+    const char *key;
+    json_t *object1, *object2, *value;
+
+    object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
+    object2 = json_object();
+
+    json_object_foreach(object1, key, value)
+        json_object_set(object2, key, value);
+
+    if(!json_equal(object1, object2))
+        fail("json_object_foreach failed to iterate all key-value pairs");
+
+    json_decref(object1);
+    json_decref(object2);
+}
+
+static void run_tests()
 {
     test_misc();
     test_clear();
     test_update();
+    test_conditional_updates();
     test_circular();
     test_set_nocheck();
     test_iterators();
     test_preserve_order();
-
-    return 0;
+    test_foreach();
 }
index ccab051..c7e7251 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
- * Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
@@ -11,7 +11,7 @@
 #include <stdio.h>
 #include "util.h"
 
-int main()
+static void run_tests()
 {
     json_t *value;
     int i;
@@ -25,7 +25,7 @@ int main()
     value = json_pack("b", 1);
     if(!json_is_true(value))
         fail("json_pack boolean failed");
-    if(value->refcount != (ssize_t)-1)
+    if(value->refcount != (size_t)-1)
         fail("json_pack boolean refcount failed");
     json_decref(value);
 
@@ -33,7 +33,7 @@ int main()
     value = json_pack("b", 0);
     if(!json_is_false(value))
         fail("json_pack boolean failed");
-    if(value->refcount != (ssize_t)-1)
+    if(value->refcount != (size_t)-1)
         fail("json_pack boolean refcount failed");
     json_decref(value);
 
@@ -41,7 +41,7 @@ int main()
     value = json_pack("n");
     if(!json_is_null(value))
         fail("json_pack null failed");
-    if(value->refcount != (ssize_t)-1)
+    if(value->refcount != (size_t)-1)
         fail("json_pack null refcount failed");
     json_decref(value);
 
@@ -49,7 +49,7 @@ int main()
     value = json_pack("i", 1);
     if(!json_is_integer(value) || json_integer_value(value) != 1)
         fail("json_pack integer failed");
-    if(value->refcount != (ssize_t)1)
+    if(value->refcount != (size_t)1)
         fail("json_pack integer refcount failed");
     json_decref(value);
 
@@ -57,7 +57,7 @@ int main()
     value = json_pack("I", (json_int_t)555555);
     if(!json_is_integer(value) || json_integer_value(value) != 555555)
         fail("json_pack json_int_t failed");
-    if(value->refcount != (ssize_t)1)
+    if(value->refcount != (size_t)1)
         fail("json_pack integer refcount failed");
     json_decref(value);
 
@@ -65,7 +65,7 @@ int main()
     value = json_pack("f", 1.0);
     if(!json_is_real(value) || json_real_value(value) != 1.0)
         fail("json_pack real failed");
-    if(value->refcount != (ssize_t)1)
+    if(value->refcount != (size_t)1)
         fail("json_pack real refcount failed");
     json_decref(value);
 
@@ -73,7 +73,7 @@ int main()
     value = json_pack("s", "test");
     if(!json_is_string(value) || strcmp("test", json_string_value(value)))
         fail("json_pack string failed");
-    if(value->refcount != (ssize_t)1)
+    if(value->refcount != (size_t)1)
         fail("json_pack string refcount failed");
     json_decref(value);
 
@@ -81,7 +81,7 @@ int main()
     value = json_pack("{}", 1.0);
     if(!json_is_object(value) || json_object_size(value) != 0)
         fail("json_pack empty object failed");
-    if(value->refcount != (ssize_t)1)
+    if(value->refcount != (size_t)1)
         fail("json_pack empty object refcount failed");
     json_decref(value);
 
@@ -89,7 +89,7 @@ int main()
     value = json_pack("[]", 1.0);
     if(!json_is_array(value) || json_array_size(value) != 0)
         fail("json_pack empty list failed");
-    if(value->refcount != (ssize_t)1)
+    if(value->refcount != (size_t)1)
         fail("json_pack empty list failed");
     json_decref(value);
 
@@ -97,7 +97,7 @@ int main()
     value = json_pack("o", json_integer(1));
     if(!json_is_integer(value) || json_integer_value(value) != 1)
         fail("json_pack object failed");
-    if(value->refcount != (ssize_t)1)
+    if(value->refcount != (size_t)1)
         fail("json_pack integer refcount failed");
     json_decref(value);
 
@@ -105,7 +105,7 @@ int main()
     value = json_pack("O", json_integer(1));
     if(!json_is_integer(value) || json_integer_value(value) != 1)
         fail("json_pack object failed");
-    if(value->refcount != (ssize_t)2)
+    if(value->refcount != (size_t)2)
         fail("json_pack integer refcount failed");
     json_decref(value);
     json_decref(value);
@@ -116,7 +116,7 @@ int main()
         fail("json_pack array failed");
     if(!json_is_array(json_object_get(value, "foo")))
         fail("json_pack array failed");
-    if(json_object_get(value, "foo")->refcount != (ssize_t)1)
+    if(json_object_get(value, "foo")->refcount != (size_t)1)
         fail("json_pack object refcount failed");
     json_decref(value);
 
@@ -227,6 +227,4 @@ int main()
     if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
         fail("json_pack failed to catch invalid UTF-8 in a string");
     check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
-
-    return 0;
 }
index 8c71329..4e2bb2f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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 "util.h"
 
 /* Call the simple functions not covered by other tests of the public API */
-int main()
+static void run_tests()
 {
     json_t *value;
 
+    value = json_boolean(1);
+    if(!json_is_true(value))
+        fail("json_boolean(1) failed");
+    json_decref(value);
+
+    value = json_boolean(-123);
+    if(!json_is_true(value))
+        fail("json_boolean(-123) failed");
+    json_decref(value);
+
+    value = json_boolean(0);
+    if(!json_is_false(value))
+        fail("json_boolean(0) failed");
+    json_decref(value);
+
+
     value = json_integer(1);
     if(json_typeof(value) != JSON_INTEGER)
         fail("json_typeof failed");
@@ -180,6 +196,4 @@ int main()
     json_incref(value);
     if(value->refcount != (size_t)-1)
       fail("refcounting null works incorrectly");
-
-    return 0;
 }
index cfc68ea..b6c7b11 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
- * Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
@@ -11,7 +11,7 @@
 #include <stdio.h>
 #include "util.h"
 
-int main()
+static void run_tests()
 {
     json_t *j, *j2;
     int i1, i2, i3;
@@ -122,6 +122,13 @@ int main()
         fail("json_unpack simple array failed");
     json_decref(j);
 
+    /* object with many items & strict checking */
+    j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3);
+    rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3);
+    if(rv || i1 != 1 || i2 != 2 || i3 != 3)
+        fail("json_unpack object with many items failed");
+    json_decref(j);
+
     /*
      * Invalid cases
      */
@@ -285,7 +292,7 @@ int main()
     /* Unpack the same item twice */
     j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
     if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
-       fail("json_unpack object with strict validation failed");
+        fail("json_unpack object with strict validation failed");
     check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
     json_decref(j);
 
@@ -330,5 +337,37 @@ int main()
     check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
     json_decref(j);
 
-    return 0;
+    /* Optional values */
+    j = json_object();
+    i1 = 0;
+    if(json_unpack(j, "{s?i}", "foo", &i1))
+        fail("json_unpack failed for optional key");
+    if(i1 != 0)
+        fail("json_unpack unpacked an optional key");
+    json_decref(j);
+
+    i1 = 0;
+    j = json_pack("{si}", "foo", 42);
+    if(json_unpack(j, "{s?i}", "foo", &i1))
+        fail("json_unpack failed for an optional value");
+    if(i1 != 42)
+        fail("json_unpack failed to unpack an optional value");
+    json_decref(j);
+
+    j = json_object();
+    i1 = i2 = i3 = 0;
+    if(json_unpack(j, "{s?[ii]s?{s{si}}}",
+                   "foo", &i1, &i2,
+                   "bar", "baz", "quux", &i3))
+        fail("json_unpack failed for complex optional values");
+    if(i1 != 0 || i2 != 0 || i3 != 0)
+        fail("json_unpack unexpectedly unpacked something");
+    json_decref(j);
+
+    j = json_pack("{s{si}}", "foo", "bar", 42);
+    if(json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1))
+        fail("json_unpack failed for complex optional values");
+    if(i1 != 42)
+        fail("json_unpack failed to unpack");
+    json_decref(j);
 }
index 83be721..d8df972 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 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.
@@ -8,8 +8,16 @@
 #ifndef UTIL_H
 #define UTIL_H
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
 #include <jansson.h>
 
 #define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
         }                                                               \
     } while(0)
 
+
+static void run_tests();
+
+int main() {
+#ifdef HAVE_SETLOCALE
+    setlocale(LC_ALL, "");
+#endif
+    run_tests();
+    return 0;
+}
+
 #endif
index 6a0e0aa..1b7b3e3 100644 (file)
@@ -1 +1,2 @@
-export JSON_ENSURE_ASCII=1
+JSON_ENSURE_ASCII=1
+export JSON_ENSURE_ASCII
index 89c9f78..78fbfcc 100644 (file)
@@ -1,2 +1,3 @@
-export JSON_INDENT=4
-export JSON_COMPACT=1
+JSON_INDENT=4
+JSON_COMPACT=1
+export JSON_INDENT JSON_COMPACT
index 89c9f78..78fbfcc 100644 (file)
@@ -1,2 +1,3 @@
-export JSON_INDENT=4
-export JSON_COMPACT=1
+JSON_INDENT=4
+JSON_COMPACT=1
+export JSON_INDENT JSON_COMPACT
index ce3582d..4d9d206 100644 (file)
@@ -1 +1,2 @@
-export JSON_PRESERVE_ORDER=1
+JSON_PRESERVE_ORDER=1
+export JSON_PRESERVE_ORDER
index 24fca95..c49d259 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+# Copyright (c) 2009-2012 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.
index 00529e7..3ef24cb 100644 (file)
@@ -1 +1,2 @@
-export JSON_SORT_KEYS=1
+JSON_SORT_KEYS=1
+export JSON_SORT_KEYS
index 7cb3243..0565584 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+# Copyright (c) 2009-2012 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.
index f8394d7..d15eba3 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+# Copyright (c) 2009-2012 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.
index 88e90ce..9a739f2 100644 (file)
@@ -1 +1 @@
-[1e+22]
\ No newline at end of file
+[1e22]
\ No newline at end of file
index ac910d6..5ffc719 100644 (file)
@@ -1 +1 @@
-[1.2299999999999999e+47]
\ No newline at end of file
+[1.2299999999999999e47]
\ No newline at end of file
index 4b87bda..66a3c81 100644 (file)
@@ -1 +1 @@
-[1.23456e+80]
\ No newline at end of file
+[1.23456e80]
\ No newline at end of file
index fa594e6..170760b 100755 (executable)
@@ -1,11 +1,12 @@
 #!/bin/sh
 #
-# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+# Copyright (c) 2009-2012 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.
 
-export JSON_SORT_KEYS=1
+JSON_SORT_KEYS=1
+export JSON_SORT_KEYS
 
 is_test() {
     test -d $test_path
similarity index 69%
rename from src/jansson_config.h.win32
rename to win32/jansson_config.h
index 86c351d..fc8e5ea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010-2012 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.
 #ifdef __cplusplus
 #define JSON_INLINE inline
 #else
-#define JSON_INLINE 
+#define JSON_INLINE __inline
 #endif
 
-/* If your compiler supports the `long long` type,
-   JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
+/* If your compiler supports the `long long` type and the strtoll()
+   library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
+   otherwise to 0. */
 #define JSON_INTEGER_IS_LONG_LONG 1
 
+/* If locale.h and localeconv() are available, define to 1,
+   otherwise to 0. */
+#define JSON_HAVE_LOCALECONV 1
+
 #endif
diff --git a/win32/vs2010/jansson.sln b/win32/vs2010/jansson.sln
new file mode 100644 (file)
index 0000000..58f911b
--- /dev/null
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jansson", "jansson.vcxproj", "{76226D20-1972-4789-A595-EDACC7A76DC3}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Win32 = Debug|Win32
+               Release|Win32 = Release|Win32
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.ActiveCfg = Debug|Win32
+               {76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.Build.0 = Debug|Win32
+               {76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.ActiveCfg = Release|Win32
+               {76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.Build.0 = Release|Win32
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/win32/vs2010/jansson.vcxproj b/win32/vs2010/jansson.vcxproj
new file mode 100644 (file)
index 0000000..d5b2a87
--- /dev/null
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\dump.c" />
+    <ClCompile Include="..\..\src\error.c" />
+    <ClCompile Include="..\..\src\hashtable.c" />
+    <ClCompile Include="..\..\src\load.c" />
+    <ClCompile Include="..\..\src\memory.c" />
+    <ClCompile Include="..\..\src\pack_unpack.c" />
+    <ClCompile Include="..\..\src\strbuffer.c" />
+    <ClCompile Include="..\..\src\strconv.c" />
+    <ClCompile Include="..\..\src\utf.c" />
+    <ClCompile Include="..\..\src\value.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\hashtable.h" />
+    <ClInclude Include="..\..\src\jansson.h" />
+    <ClInclude Include="..\..\src\jansson_private.h" />
+    <ClInclude Include="..\..\src\strbuffer.h" />
+    <ClInclude Include="..\..\src\utf.h" />
+    <ClInclude Include="..\jansson_config.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{76226D20-1972-4789-A595-EDACC7A76DC3}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>jansson_dll</RootNamespace>
+    <ProjectName>jansson</ProjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>Output\$(Configuration)\</OutDir>
+    <IntDir>Build\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>Output\$(Configuration)\</OutDir>
+    <IntDir>Build\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ModuleDefinitionFile>../../src/jansson.def</ModuleDefinitionFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <ModuleDefinitionFile>../../src/jansson.def</ModuleDefinitionFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/win32/vs2010/jansson.vcxproj.filters b/win32/vs2010/jansson.vcxproj.filters
new file mode 100644 (file)
index 0000000..f8eee30
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\dump.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\error.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\hashtable.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\load.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\memory.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\pack_unpack.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\strbuffer.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\strconv.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\utf.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\value.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\hashtable.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\jansson.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\jansson_private.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\strbuffer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\utf.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\jansson_config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32/vs2010/jansson.vcxproj.user b/win32/vs2010/jansson.vcxproj.user
new file mode 100644 (file)
index 0000000..ace9a86
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file