Merge pull request #90 from luke-jr/patch-1
authorPetri Lehtinen <petri@digip.org>
Wed, 19 Sep 2012 04:11:26 +0000 (21:11 -0700)
committerPetri Lehtinen <petri@digip.org>
Wed, 19 Sep 2012 04:11:26 +0000 (21:11 -0700)
Add -no-undefined to LDFLAGS

334 files changed:
.gitignore
CHANGES
LICENSE
Makefile.am
README.rst
configure.ac
doc/.gitignore
doc/Makefile.am
doc/README
doc/apiref.rst
doc/changes.rst [new file with mode: 0644]
doc/conf.py
doc/conformance.rst [new file with mode: 0644]
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
doc/upgrading.rst [new file with mode: 0644]
jansson.pc.in [new file with mode: 0644]
src/Makefile.am
src/dump.c
src/error.c [new file with mode: 0644]
src/hashtable.c
src/hashtable.h
src/jansson.def [new file with mode: 0644]
src/jansson.h
src/jansson_config.h.in [new file with mode: 0644]
src/jansson_private.h
src/load.c
src/memory.c [new file with mode: 0644]
src/pack_unpack.c [new file with mode: 0644]
src/strbuffer.c
src/strbuffer.h
src/strconv.c [new file with mode: 0644]
src/utf.c
src/utf.h
src/util.h [deleted file]
src/value.c
test/.gitignore
test/Makefile.am
test/bin/Makefile.am [new file with mode: 0644]
test/bin/json_process.c [new file with mode: 0644]
test/json-compare.py [deleted file]
test/load_file_dump_file.c [deleted file]
test/loadf_dumpf.c [deleted file]
test/loads_dumps.c [deleted file]
test/run-suites [new file with mode: 0755]
test/run-test [deleted file]
test/scripts/run-tests.sh [new file with mode: 0644]
test/scripts/valgrind.sh [new file with mode: 0644]
test/split-testfile.py [deleted file]
test/suites/Makefile.am [new file with mode: 0644]
test/suites/api/Makefile.am [new file with mode: 0644]
test/suites/api/check-exports [new file with mode: 0755]
test/suites/api/run [new file with mode: 0755]
test/suites/api/test_array.c [moved from test/testprogs/test_array.c with 99% similarity]
test/suites/api/test_copy.c [new file with mode: 0644]
test/suites/api/test_dump.c [new file with mode: 0644]
test/suites/api/test_dump_callback.c [new file with mode: 0644]
test/suites/api/test_equal.c [new file with mode: 0644]
test/suites/api/test_load.c [new file with mode: 0644]
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 [new file with mode: 0644]
test/suites/api/test_number.c [moved from test/testprogs/test_number.c with 51% similarity]
test/suites/api/test_object.c [moved from test/testprogs/test_object.c with 53% similarity]
test/suites/api/test_pack.c [new file with mode: 0644]
test/suites/api/test_simple.c [moved from test/testprogs/test_simple.c with 55% similarity]
test/suites/api/test_unpack.c [new file with mode: 0644]
test/suites/api/util.h [new file with mode: 0644]
test/suites/encoding-flags/array/input [new file with mode: 0644]
test/suites/encoding-flags/array/output [new file with mode: 0644]
test/suites/encoding-flags/compact-array/env [new file with mode: 0644]
test/suites/encoding-flags/compact-array/input [new file with mode: 0644]
test/suites/encoding-flags/compact-array/output [new file with mode: 0644]
test/suites/encoding-flags/compact-object/env [new file with mode: 0644]
test/suites/encoding-flags/compact-object/input [new file with mode: 0644]
test/suites/encoding-flags/compact-object/output [new file with mode: 0644]
test/suites/encoding-flags/ensure-ascii/env [new file with mode: 0644]
test/suites/encoding-flags/ensure-ascii/input [new file with mode: 0644]
test/suites/encoding-flags/ensure-ascii/output [new file with mode: 0644]
test/suites/encoding-flags/indent-array/env [new file with mode: 0644]
test/suites/encoding-flags/indent-array/input [new file with mode: 0644]
test/suites/encoding-flags/indent-array/output [new file with mode: 0644]
test/suites/encoding-flags/indent-compact-array/env [new file with mode: 0644]
test/suites/encoding-flags/indent-compact-array/input [new file with mode: 0644]
test/suites/encoding-flags/indent-compact-array/output [new file with mode: 0644]
test/suites/encoding-flags/indent-compact-object/env [new file with mode: 0644]
test/suites/encoding-flags/indent-compact-object/input [new file with mode: 0644]
test/suites/encoding-flags/indent-compact-object/output [new file with mode: 0644]
test/suites/encoding-flags/indent-object/env [new file with mode: 0644]
test/suites/encoding-flags/indent-object/input [new file with mode: 0644]
test/suites/encoding-flags/indent-object/output [new file with mode: 0644]
test/suites/encoding-flags/object/input [new file with mode: 0644]
test/suites/encoding-flags/object/output [new file with mode: 0644]
test/suites/encoding-flags/preserve-order/env [new file with mode: 0644]
test/suites/encoding-flags/preserve-order/input [new file with mode: 0644]
test/suites/encoding-flags/preserve-order/output [new file with mode: 0644]
test/suites/encoding-flags/run [new file with mode: 0755]
test/suites/encoding-flags/sort-keys/env [new file with mode: 0644]
test/suites/encoding-flags/sort-keys/input [new file with mode: 0644]
test/suites/encoding-flags/sort-keys/output [new file with mode: 0644]
test/suites/invalid-unicode/encoded-surrogate-half/error [new file with mode: 0644]
test/suites/invalid-unicode/encoded-surrogate-half/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-after-backslash/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-after-backslash/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-array/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-array/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-escape/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-escape/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-exponent/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-exponent/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-identifier/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-identifier/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-int/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-int/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/input [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-string/error [new file with mode: 0644]
test/suites/invalid-unicode/invalid-utf-8-in-string/input [new file with mode: 0644]
test/suites/invalid-unicode/lone-invalid-utf-8/error [new file with mode: 0644]
test/suites/invalid-unicode/lone-invalid-utf-8/input [new file with mode: 0644]
test/suites/invalid-unicode/lone-utf-8-continuation-byte/error [new file with mode: 0644]
test/suites/invalid-unicode/lone-utf-8-continuation-byte/input [new file with mode: 0644]
test/suites/invalid-unicode/not-in-unicode-range/error [new file with mode: 0644]
test/suites/invalid-unicode/not-in-unicode-range/input [new file with mode: 0644]
test/suites/invalid-unicode/overlong-3-byte-encoding/error [new file with mode: 0644]
test/suites/invalid-unicode/overlong-3-byte-encoding/input [new file with mode: 0644]
test/suites/invalid-unicode/overlong-4-byte-encoding/error [new file with mode: 0644]
test/suites/invalid-unicode/overlong-4-byte-encoding/input [new file with mode: 0644]
test/suites/invalid-unicode/overlong-ascii-encoding/error [new file with mode: 0644]
test/suites/invalid-unicode/overlong-ascii-encoding/input [new file with mode: 0644]
test/suites/invalid-unicode/restricted-utf-8/error [new file with mode: 0644]
test/suites/invalid-unicode/restricted-utf-8/input [new file with mode: 0644]
test/suites/invalid-unicode/run [new file with mode: 0755]
test/suites/invalid-unicode/truncated-utf-8/error [new file with mode: 0644]
test/suites/invalid-unicode/truncated-utf-8/input [new file with mode: 0644]
test/suites/invalid/apostrophe/error [new file with mode: 0644]
test/suites/invalid/apostrophe/input [new file with mode: 0644]
test/suites/invalid/ascii-unicode-identifier/error [new file with mode: 0644]
test/suites/invalid/ascii-unicode-identifier/input [new file with mode: 0644]
test/suites/invalid/brace-comma/error [new file with mode: 0644]
test/suites/invalid/brace-comma/input [new file with mode: 0644]
test/suites/invalid/bracket-comma/error [new file with mode: 0644]
test/suites/invalid/bracket-comma/input [new file with mode: 0644]
test/suites/invalid/bracket-one-comma/error.normal [new file with mode: 0644]
test/suites/invalid/bracket-one-comma/error.strip [new file with mode: 0644]
test/suites/invalid/bracket-one-comma/input [new file with mode: 0644]
test/suites/invalid/empty/error [new file with mode: 0644]
test/suites/invalid/empty/input [new file with mode: 0644]
test/suites/invalid/escaped-null-byte-in-string/error [new file with mode: 0644]
test/suites/invalid/escaped-null-byte-in-string/input [new file with mode: 0644]
test/suites/invalid/extra-comma-in-array/error [new file with mode: 0644]
test/suites/invalid/extra-comma-in-array/input [new file with mode: 0644]
test/suites/invalid/extra-comma-in-multiline-array/error [new file with mode: 0644]
test/suites/invalid/extra-comma-in-multiline-array/input [new file with mode: 0644]
test/suites/invalid/garbage-after-newline/error [new file with mode: 0644]
test/suites/invalid/garbage-after-newline/input [new file with mode: 0644]
test/suites/invalid/garbage-at-the-end/error [new file with mode: 0644]
test/suites/invalid/garbage-at-the-end/input [new file with mode: 0644]
test/suites/invalid/integer-starting-with-zero/error [new file with mode: 0644]
test/suites/invalid/integer-starting-with-zero/input [new file with mode: 0644]
test/suites/invalid/invalid-escape/error [new file with mode: 0644]
test/suites/invalid/invalid-escape/input [new file with mode: 0644]
test/suites/invalid/invalid-identifier/error [new file with mode: 0644]
test/suites/invalid/invalid-identifier/input [new file with mode: 0644]
test/suites/invalid/invalid-negative-integer/error [new file with mode: 0644]
test/suites/invalid/invalid-negative-integer/input [new file with mode: 0644]
test/suites/invalid/invalid-negative-real/error [new file with mode: 0644]
test/suites/invalid/invalid-negative-real/input [new file with mode: 0644]
test/suites/invalid/invalid-second-surrogate/error [new file with mode: 0644]
test/suites/invalid/invalid-second-surrogate/input [new file with mode: 0644]
test/suites/invalid/lone-open-brace/error.normal [new file with mode: 0644]
test/suites/invalid/lone-open-brace/error.strip [new file with mode: 0644]
test/suites/invalid/lone-open-brace/input [new file with mode: 0644]
test/suites/invalid/lone-open-bracket/error.normal [new file with mode: 0644]
test/suites/invalid/lone-open-bracket/error.strip [new file with mode: 0644]
test/suites/invalid/lone-open-bracket/input [new file with mode: 0644]
test/suites/invalid/lone-second-surrogate/error [new file with mode: 0644]
test/suites/invalid/lone-second-surrogate/input [new file with mode: 0644]
test/suites/invalid/minus-sign-without-number/error [new file with mode: 0644]
test/suites/invalid/minus-sign-without-number/input [new file with mode: 0644]
test/suites/invalid/negative-integer-starting-with-zero/error [new file with mode: 0644]
test/suites/invalid/negative-integer-starting-with-zero/input [new file with mode: 0644]
test/suites/invalid/null-byte-in-string/error [new file with mode: 0644]
test/suites/invalid/null-byte-in-string/input [new file with mode: 0644]
test/suites/invalid/null-byte-in-string/nostrip [new file with mode: 0644]
test/suites/invalid/null-byte-outside-string/error [new file with mode: 0644]
test/suites/invalid/null-byte-outside-string/input [new file with mode: 0644]
test/suites/invalid/null-byte-outside-string/nostrip [new file with mode: 0644]
test/suites/invalid/null/error [new file with mode: 0644]
test/suites/invalid/null/input [new file with mode: 0644]
test/suites/invalid/object-apostrophes/error [new file with mode: 0644]
test/suites/invalid/object-apostrophes/input [new file with mode: 0644]
test/suites/invalid/object-garbage-at-end/error [new file with mode: 0644]
test/suites/invalid/object-garbage-at-end/input [new file with mode: 0644]
test/suites/invalid/object-in-unterminated-array/error.normal [new file with mode: 0644]
test/suites/invalid/object-in-unterminated-array/error.strip [new file with mode: 0644]
test/suites/invalid/object-in-unterminated-array/input [new file with mode: 0644]
test/suites/invalid/object-no-colon/error.normal [new file with mode: 0644]
test/suites/invalid/object-no-colon/error.strip [new file with mode: 0644]
test/suites/invalid/object-no-colon/input [new file with mode: 0644]
test/suites/invalid/object-no-value/error.normal [new file with mode: 0644]
test/suites/invalid/object-no-value/error.strip [new file with mode: 0644]
test/suites/invalid/object-no-value/input [new file with mode: 0644]
test/suites/invalid/object-unterminated-value/error.normal [new file with mode: 0644]
test/suites/invalid/object-unterminated-value/error.strip [new file with mode: 0644]
test/suites/invalid/object-unterminated-value/input [new file with mode: 0644]
test/suites/invalid/real-garbage-after-e/error [new file with mode: 0644]
test/suites/invalid/real-garbage-after-e/input [new file with mode: 0644]
test/suites/invalid/real-negative-overflow/error [new file with mode: 0644]
test/suites/invalid/real-negative-overflow/input [new file with mode: 0644]
test/suites/invalid/real-positive-overflow/error [new file with mode: 0644]
test/suites/invalid/real-positive-overflow/input [new file with mode: 0644]
test/suites/invalid/real-truncated-at-e/error [new file with mode: 0644]
test/suites/invalid/real-truncated-at-e/input [new file with mode: 0644]
test/suites/invalid/real-truncated-at-point/error [new file with mode: 0644]
test/suites/invalid/real-truncated-at-point/input [new file with mode: 0644]
test/suites/invalid/run [new file with mode: 0755]
test/suites/invalid/tab-character-in-string/error [new file with mode: 0644]
test/suites/invalid/tab-character-in-string/input [new file with mode: 0644]
test/suites/invalid/too-big-negative-integer/error [new file with mode: 0644]
test/suites/invalid/too-big-negative-integer/input [new file with mode: 0644]
test/suites/invalid/too-big-positive-integer/error [new file with mode: 0644]
test/suites/invalid/too-big-positive-integer/input [new file with mode: 0644]
test/suites/invalid/truncated-unicode-surrogate/error [new file with mode: 0644]
test/suites/invalid/truncated-unicode-surrogate/input [new file with mode: 0644]
test/suites/invalid/unicode-identifier/error [new file with mode: 0644]
test/suites/invalid/unicode-identifier/input [new file with mode: 0644]
test/suites/invalid/unterminated-array-and-object/error.normal [new file with mode: 0644]
test/suites/invalid/unterminated-array-and-object/error.strip [new file with mode: 0644]
test/suites/invalid/unterminated-array-and-object/input [new file with mode: 0644]
test/suites/invalid/unterminated-array/error.normal [new file with mode: 0644]
test/suites/invalid/unterminated-array/error.strip [new file with mode: 0644]
test/suites/invalid/unterminated-array/input [new file with mode: 0644]
test/suites/invalid/unterminated-empty-key/error.normal [new file with mode: 0644]
test/suites/invalid/unterminated-empty-key/error.strip [new file with mode: 0644]
test/suites/invalid/unterminated-empty-key/input [new file with mode: 0644]
test/suites/invalid/unterminated-key/error.normal [new file with mode: 0644]
test/suites/invalid/unterminated-key/error.strip [new file with mode: 0644]
test/suites/invalid/unterminated-key/input [new file with mode: 0644]
test/suites/invalid/unterminated-object-and-array/error [new file with mode: 0644]
test/suites/invalid/unterminated-object-and-array/input [new file with mode: 0644]
test/suites/invalid/unterminated-string/error.normal [new file with mode: 0644]
test/suites/invalid/unterminated-string/error.strip [new file with mode: 0644]
test/suites/invalid/unterminated-string/input [new file with mode: 0644]
test/suites/valid/complex-array/input [new file with mode: 0644]
test/suites/valid/complex-array/output [new file with mode: 0644]
test/suites/valid/empty-array/input [new file with mode: 0644]
test/suites/valid/empty-array/output [new file with mode: 0644]
test/suites/valid/empty-object-in-array/input [new file with mode: 0644]
test/suites/valid/empty-object-in-array/output [new file with mode: 0644]
test/suites/valid/empty-object/input [new file with mode: 0644]
test/suites/valid/empty-object/output [new file with mode: 0644]
test/suites/valid/empty-string/input [new file with mode: 0644]
test/suites/valid/empty-string/output [new file with mode: 0644]
test/suites/valid/escaped-utf-control-char/input [new file with mode: 0644]
test/suites/valid/escaped-utf-control-char/output [new file with mode: 0644]
test/suites/valid/false/input [new file with mode: 0644]
test/suites/valid/false/output [new file with mode: 0644]
test/suites/valid/negative-int/input [new file with mode: 0644]
test/suites/valid/negative-int/output [new file with mode: 0644]
test/suites/valid/negative-one/input [new file with mode: 0644]
test/suites/valid/negative-one/output [new file with mode: 0644]
test/suites/valid/negative-zero/input [new file with mode: 0644]
test/suites/valid/negative-zero/output [new file with mode: 0644]
test/suites/valid/null/input [new file with mode: 0644]
test/suites/valid/null/output [new file with mode: 0644]
test/suites/valid/one-byte-utf-8/input [new file with mode: 0644]
test/suites/valid/one-byte-utf-8/output [new file with mode: 0644]
test/suites/valid/real-capital-e-negative-exponent/input [new file with mode: 0644]
test/suites/valid/real-capital-e-negative-exponent/output [new file with mode: 0644]
test/suites/valid/real-capital-e-positive-exponent/input [new file with mode: 0644]
test/suites/valid/real-capital-e-positive-exponent/output [new file with mode: 0644]
test/suites/valid/real-capital-e/input [new file with mode: 0644]
test/suites/valid/real-capital-e/output [new file with mode: 0644]
test/suites/valid/real-exponent/input [new file with mode: 0644]
test/suites/valid/real-exponent/output [new file with mode: 0644]
test/suites/valid/real-fraction-exponent/input [new file with mode: 0644]
test/suites/valid/real-fraction-exponent/output [new file with mode: 0644]
test/suites/valid/real-negative-exponent/input [new file with mode: 0644]
test/suites/valid/real-negative-exponent/output [new file with mode: 0644]
test/suites/valid/real-positive-exponent/input [new file with mode: 0644]
test/suites/valid/real-positive-exponent/output [new file with mode: 0644]
test/suites/valid/real-underflow/input [new file with mode: 0644]
test/suites/valid/real-underflow/output [new file with mode: 0644]
test/suites/valid/run [new file with mode: 0755]
test/suites/valid/short-string/input [new file with mode: 0644]
test/suites/valid/short-string/output [new file with mode: 0644]
test/suites/valid/simple-ascii-string/input [new file with mode: 0644]
test/suites/valid/simple-ascii-string/output [new file with mode: 0644]
test/suites/valid/simple-int-0/input [new file with mode: 0644]
test/suites/valid/simple-int-0/output [new file with mode: 0644]
test/suites/valid/simple-int-1/input [new file with mode: 0644]
test/suites/valid/simple-int-1/output [new file with mode: 0644]
test/suites/valid/simple-int-123/input [new file with mode: 0644]
test/suites/valid/simple-int-123/output [new file with mode: 0644]
test/suites/valid/simple-object/input [new file with mode: 0644]
test/suites/valid/simple-object/output [new file with mode: 0644]
test/suites/valid/simple-real/input [new file with mode: 0644]
test/suites/valid/simple-real/output [new file with mode: 0644]
test/suites/valid/string-escapes/input [new file with mode: 0644]
test/suites/valid/string-escapes/output [new file with mode: 0644]
test/suites/valid/three-byte-utf-8/input [new file with mode: 0644]
test/suites/valid/three-byte-utf-8/output [new file with mode: 0644]
test/suites/valid/true/input [new file with mode: 0644]
test/suites/valid/true/output [new file with mode: 0644]
test/suites/valid/two-byte-utf-8/input [new file with mode: 0644]
test/suites/valid/two-byte-utf-8/output [new file with mode: 0644]
test/suites/valid/utf-8-string/input [new file with mode: 0644]
test/suites/valid/utf-8-string/output [new file with mode: 0644]
test/suites/valid/utf-surrogate-four-byte-encoding/input [new file with mode: 0644]
test/suites/valid/utf-surrogate-four-byte-encoding/output [new file with mode: 0644]
test/test-api [deleted file]
test/test-invalid [deleted file]
test/test-valid [deleted file]
test/testdata/Makefile.am [deleted file]
test/testdata/invalid [deleted file]
test/testdata/invalid-strip [deleted file]
test/testdata/invalid-unicode [deleted file]
test/testdata/valid [deleted file]
test/testdata/valid-strip [deleted file]
test/testprogs/Makefile.am [deleted file]
test/testprogs/test_load.c [deleted file]
test/testprogs/util.h [deleted file]
win32/jansson_config.h [new file with mode: 0644]
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 a2baa14..9189a93 100644 (file)
@@ -1,3 +1,4 @@
+*~
 *.o
 *.a
 .libs
@@ -22,3 +23,6 @@ missing
 *.la
 stamp-h1
 *.pyc
+*.pc
+/src/jansson_config.h
+*.exe
diff --git a/CHANGES b/CHANGES
index 64ddd6f..2aaabba 100644 (file)
--- a/CHANGES
+++ b/CHANGES
-Version 1.1.2, released 2009-11-08
+Version 2.4 (in development)
+============================
+
+Released 2012-XX-XX
+
+* 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 ``\/``.
+
+* 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).
+
+* 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
+===========
+
+Released 2011-02-28
+
+This release is backwards incompatible with the 1.x release series.
+See the chapter "Upgrading from older versions" in documentation for
+details.
+
+* Backwards incompatible changes:
+
+  - Unify unsigned integer usage in the API: All occurences of
+    unsigned int and unsigned long have been replaced with size_t.
+
+  - Change JSON integer's underlying type to the widest signed integer
+    type available, i.e. long long if it's supported, otherwise long.
+    Add a typedef json_int_t that defines the type.
+
+  - Change the maximum indentation depth to 31 spaces in encoder. This
+    frees up bits from the flags parameter of encoding functions
+    `json_dumpf()`, `json_dumps()` and `json_dump_file()`.
+
+  - For future needs, add a flags parameter to all decoding functions
+    `json_loadf()`, `json_loads()` and `json_load_file()`.
+
+* New features
+
+  - `json_pack()`, `json_pack_ex()`, `json_vpack_ex()`: Create JSON
+    values based on a format string.
+
+  - `json_unpack()`, `json_unpack_ex()`, `json_vunpack_ex()`: Simple
+    value extraction and validation functionality based on a format
+    string.
+
+  - Add column, position and source fields to the ``json_error_t``
+    struct.
+
+  - Enhance error reporting in the decoder.
+
+  - ``JANSSON_VERSION`` et al.: Preprocessor constants that define the
+    library version.
+
+  - `json_set_alloc_funcs()`: Set custom memory allocation functions.
+
+* Fix many portability issues, especially on Windows.
+
+* Configuration
+
+  - Add file ``jansson_config.h`` that contains site specific
+    configuration. It's created automatically by the configure script,
+    or can be created by hand if the configure script cannot be used.
+    The file ``jansson_config.h.win32`` can be used without
+    modifications on Windows systems.
+
+  - Add a section to documentation describing how to build Jansson on
+    Windows.
+
+  - Documentation now requires Sphinx 1.0 or newer.
+
+
+Version 1.3
+===========
+
+Released 2010-06-13
+
+* New functions:
+
+  - `json_object_iter_set()`, `json_object_iter_set_new()`: Change
+    object contents while iterating over it.
+
+  - `json_object_iter_at()`: Return an iterator that points to a
+    specific object item.
+
+* New encoding flags:
+
+  - ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object
+    keys.
+
+* Bug fixes:
+
+  - Fix an error that occured when an array or object was first
+    encoded as empty, then populated with some data, and then
+    re-encoded
+
+  - Fix the situation like above, but when the first encoding resulted
+    in an error
+
+* Documentation:
+
+  - Clarify the documentation on reference stealing, providing an
+    example usage pattern
+
+
+Version 1.2.1
+=============
+
+Released 2010-04-03
+
+* Bug fixes:
+
+  - Fix reference counting on ``true``, ``false`` and ``null``
+  - Estimate real number underflows in decoder with 0.0 instead of
+    issuing an error
+
+* Portability:
+
+  - Make ``int32_t`` available on all systems
+  - Support compilers that don't have the ``inline`` keyword
+  - Require Autoconf 2.60 (for ``int32_t``)
+
+* Tests:
+
+  - Print test names correctly when ``VERBOSE=1``
+  - ``test/suites/api``: Fail when a test fails
+  - Enhance tests for iterators
+  - Enhance tests for decoding texts that contain null bytes
+
+* Documentation:
+
+  - Don't remove ``changes.rst`` in ``make clean``
+  - Add a chapter on RFC conformance
+
+
+Version 1.2
+===========
+
+Released 2010-01-21
+
+* New functions:
+
+  - `json_equal()`: Test whether two JSON values are equal
+  - `json_copy()` and `json_deep_copy()`: Make shallow and deep copies
+    of JSON values
+  - Add a version of all functions taking a string argument that
+    doesn't check for valid UTF-8: `json_string_nocheck()`,
+    `json_string_set_nocheck()`, `json_object_set_nocheck()`,
+    `json_object_set_new_nocheck()`
+
+* New encoding flags:
+
+  - ``JSON_SORT_KEYS``: Sort objects by key
+  - ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters
+  - ``JSON_COMPACT``: Use a compact representation with all unneeded
+    whitespace stripped
+
+* Bug fixes:
+
+  - Revise and unify whitespace usage in encoder: Add spaces between
+    array and object items, never append newline to output.
+  - Remove const qualifier from the ``json_t`` parameter in
+    `json_string_set()`, `json_integer_set()` and `json_real_set`.
+  - Use ``int32_t`` internally for representing Unicode code points
+    (int is not enough on all platforms)
+
+* Other changes:
+
+  - Convert ``CHANGES`` (this file) to reStructured text and add it to
+    HTML documentation
+  - The test system has been refactored. Python is no longer required
+    to run the tests.
+  - Documentation can now be built by invoking ``make html``
+  - Support for pkg-config
+
+
+Version 1.1.3
+=============
+
+Released 2009-12-18
+
+* Encode reals correctly, so that first encoding and then decoding a
+  real always produces the same value
+* Don't export private symbols in ``libjansson.so``
+
+
+Version 1.1.2
+=============
+
+Released 2009-11-08
 
 * Fix a bug where an error message was not produced if the input file
-  could not be opened in json_load_file()
+  could not be opened in `json_load_file()`
 * Fix an assertion failure in decoder caused by a minus sign without a
   digit after it
-* Remove an unneeded include for stdint.h in jansson.h
+* Remove an unneeded include of ``stdint.h`` in ``jansson.h``
+
 
+Version 1.1.1
+=============
 
-Version 1.1.1, released 2009-10-26
+Released 2009-10-26
 
 * All documentation files were not distributed with v1.1; build
   documentation in make distcheck to prevent this in the future
-* Fix v1.1 release date in CHANGES
+* Fix v1.1 release date in ``CHANGES``
 
 
-Version 1.1, released 2009-10-20
+Version 1.1
+===========
+
+Released 2009-10-20
 
 * API additions and improvements:
+
   - Extend array and object APIs
   - Add functions to modify integer, real and string values
   - Improve argument validation
-  - Use unsigned int instead of uint32_t for encoding flags
+  - Use unsigned int instead of ``uint32_t`` for encoding flags
+
 * Enhance documentation
+
   - Add getting started guide and tutorial
   - Fix some typos
   - General clarifications and cleanup
+
 * Check for integer and real overflows and underflows in decoder
-* Make singleton values thread-safe (true, false and null)
+* Make singleton values thread-safe (``true``, ``false`` and ``null``)
 * Enhance circular reference handling
-* Don't define -std=c99 in AM_CFLAGS
-* Add C++ guards to jansson.h
+* Don't define ``-std=c99`` in ``AM_CFLAGS``
+* Add C++ guards to ``jansson.h``
 * Minor performance and portability improvements
 * Expand test coverage
 
 
-Version 1.0.4, released 2009-10-11
+Version 1.0.4
+=============
+
+Released 2009-10-11
 
 * Relax Autoconf version requirement to 2.59
-* Make Jansson compile on platforms where plain char is unsigned
+* Make Jansson compile on platforms where plain ``char`` is unsigned
 * Fix API tests for object
 
 
-Version 1.0.3, released 2009-09-14
+Version 1.0.3
+=============
+
+Released 2009-09-14
 
 * Check for integer and real overflows and underflows in decoder
 * Use the Python json module for tests, or simplejson if the json
@@ -49,16 +462,25 @@ Version 1.0.3, released 2009-09-14
 * Distribute changelog (this file)
 
 
-Version 1.0.2, released 2009-09-08
+Version 1.0.2
+=============
+
+Released 2009-09-08
 
 * Handle EOF correctly in decoder
 
 
-Version 1.0.1, released 2009-09-04
+Version 1.0.1
+=============
+
+Released 2009-09-04
+
+* Fixed broken `json_is_boolean()`
 
-* Fixed broken json_is_boolean()
 
+Version 1.0
+===========
 
-Version 1.0, released 2009-08-25
+Released 2009-08-25
 
 * Initial release
diff --git a/LICENSE b/LICENSE
index 6c6dbed..dfa8686 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2009 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 e64f397..e1fd3ab 100644 (file)
@@ -1,7 +1,10 @@
 EXTRA_DIST = CHANGES LICENSE README.rst
 SUBDIRS = doc src test
 
-distcheck-hook:
-       sphinx-build -b html -W \
-               $(distdir)/doc \
-               $(distdir)/_build/doc/.build/html
+# "make distcheck" builds the dvi target, so use it to check that the
+# documentation is built correctly.
+dvi:
+       $(MAKE) SPHINXOPTS_EXTRA=-W html
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = jansson.pc
index ebd3941..4607125 100644 (file)
@@ -24,40 +24,36 @@ Compilation and Installation
 If you obtained a source tarball, just use the standard autotools
 commands::
 
-   $ ./configure && make && make install
-
-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::
-
-   $ autoreconf -i
+   $ ./configure
+   $ make
+   $ make install
 
 To run the test suite, invoke::
 
    $ make check
 
-Python_ is required to run the tests.
+If the source has been checked out from a Git repository, the
+./configure script has to be generated first. The easiest way is to
+use autoreconf::
+
+   $ autoreconf -i
 
 
 Documentation
 -------------
 
-Documentation is in the ``doc/`` subdirectory. It's written in
-reStructuredText_ with Sphinx_ annotations, so reading it in plain may
-be inconvenient. For this reason, prebuilt HTML documentation is
-available at http://www.digip.org/jansson/doc/.
+Prebuilt HTML documentation is available at
+http://www.digip.org/jansson/doc/.
 
-To generate HTML documentation yourself, invoke::
+The documentation source is in the ``doc/`` subdirectory. To generate
+HTML documentation, invoke::
 
-   cd doc/
-   sphinx-build . .build/html
+   $ make html
 
-... and point your browser to ``.build/html/index.html``. Sphinx_ is
-required to generate the documentation.
+Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
+1.0 or newer is required to generate the documentation.
 
 
 .. _Jansson: http://www.digip.org/jansson/
 .. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
-.. _Python: http://www.python.org/
-.. _reStructuredText: http://docutils.sourceforge.net/rst.html
 .. _Sphinx: http://sphinx.pocoo.org/
index 1a6f7e7..753a09b 100644 (file)
@@ -1,5 +1,5 @@
-AC_PREREQ([2.59])
-AC_INIT([jansson], [1.1.2], [petri@digip.org])
+AC_PREREQ([2.60])
+AC_INIT([jansson], [2.4-dev], [petri@digip.org])
 
 AM_INIT_AUTOMAKE([1.10 foreign])
 
@@ -9,21 +9,49 @@ AC_CONFIG_HEADERS([config.h])
 # Checks for programs.
 AC_PROG_CC
 AC_PROG_LIBTOOL
+AM_CONDITIONAL([GCC], [test x$GCC = xyes])
 
 # Checks for libraries.
 
 # Checks for header files.
+AC_CHECK_HEADERS([locale.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT32_T
+AC_TYPE_LONG_LONG_INT
+
+AC_C_INLINE
+case $ac_cv_c_inline in
+    yes) json_inline=inline;;
+    no) json_inline=;;
+    *) json_inline=$ac_cv_c_inline;;
+esac
+AC_SUBST([json_inline])
 
 # Checks for library functions.
+AC_CHECK_FUNCS([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
         Makefile
         doc/Makefile
         src/Makefile
+        src/jansson_config.h
         test/Makefile
-        test/testdata/Makefile
-        test/testprogs/Makefile
+        test/bin/Makefile
+        test/suites/Makefile
+        test/suites/api/Makefile
 ])
 AC_OUTPUT
index 30bcfa4..69fa449 100644 (file)
@@ -1 +1 @@
-.build/
+_build/
index b00ee56..5069623 100644 (file)
@@ -1,7 +1,20 @@
-EXTRA_DIST = \
-       conf.py apiref.rst gettingstarted.rst github_commits.c index.rst \
-       tutorial.rst ext/refcounting.py
+EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst            \
+       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)
+
+html-local:
+       $(SPHINXBUILD) -b html $(SPHINXOPTS) $(srcdir) _build/html
+
+install-html-local: html
+       mkdir -p $(DESTDIR)$(htmldir)
+       cp -r _build/html $(DESTDIR)$(htmldir)
+
+uninstall-local:
+       rm -rf $(DESTDIR)$(htmldir)
 
 clean-local:
-       rm -rf .build
+       rm -rf _build
        rm -f ext/refcounting.pyc
index 3b10d2a..930b3bf 100644 (file)
@@ -1,5 +1,5 @@
 To build the documentation, invoke
 
-    sphinx-build . .build/html
+    make html
 
-in this directory. Then point your browser to .build/html/index.html.
+Then point your browser to _build/html/index.html.
index 3ba5fbf..759d5bf 100644 (file)
@@ -17,9 +17,46 @@ All declarations are in :file:`jansson.h`, so it's enough to
 
 in each source file.
 
-All constants are prefixed ``JSON_`` and other identifiers with
-``json_``. Type names are suffixed with ``_t`` and ``typedef``\ 'd so
-that the ``struct`` keyword need not be used.
+All constants are prefixed with ``JSON_`` (except for those describing
+the library version, prefixed with ``JANSSON_``). Other identifiers
+are prefixed with ``json_``. Type names are suffixed with ``_t`` and
+``typedef``\ 'd so that the ``struct`` keyword need not be used.
+
+
+Library Version
+===============
+
+The Jansson version is of the form *A.B.C*, where *A* is the major
+version, *B* is the minor version and *C* is the micro version. If the
+micro version is zero, it's omitted from the version string, i.e. the
+version string is just *A.B*.
+
+When a new release only fixes bugs and doesn't add new features or
+functionality, the micro version is incremented. When new features are
+added in a backwards compatible way, the minor version is incremented
+and the micro version is set to zero. When there are backwards
+incompatible changes, the major version is incremented and others are
+set to zero.
+
+The following preprocessor constants specify the current version of
+the library:
+
+``JANSSON_MAJOR_VERSION``, ``JANSSON_MINOR_VERSION``, ``JANSSON_MICRO_VERSION``
+  Integers specifying the major, minor and micro versions,
+  respectively.
+
+``JANSSON_VERSION``
+  A string representation of the current version, e.g. ``"1.2.1"`` or
+  ``"1.3"``.
+
+``JANSSON_VERSION_HEX``
+  A 3-byte hexadecimal representation of the version, e.g.
+  ``0x010201`` for version 1.2.1 and ``0x010300`` for version 1.3.
+  This is useful in numeric comparisions, e.g.::
+
+      #if JANSSON_VERSION_HEX >= 0x010300
+      /* Code specific to version 1.3 and above */
+      #endif
 
 
 Value Representation
@@ -32,14 +69,14 @@ type, including themselves. For this reason, Jansson's type system is
 also dynamic in nature. There's one C type to represent all JSON
 values, and this structure knows the type of the JSON value it holds.
 
-.. ctype:: json_t
+.. type:: json_t
 
   This data structure is used throughout the library to represent all
   JSON values. It always contains the type of the JSON value it holds
   and the value's reference count. The rest depends on the type of the
   value.
 
-Objects of :ctype:`json_t` are always used through a pointer. There
+Objects of :type:`json_t` are always used through a pointer. There
 are APIs for querying the type, manipulating the reference count, and
 for constructing and manipulating values of different types.
 
@@ -56,42 +93,41 @@ Type
 The type of a JSON value is queried and tested using the following
 functions:
 
-.. ctype:: enum json_type
+.. type:: enum json_type
 
    The type of a JSON value. The following members are defined:
 
-   +-------------------------+
-   | :const:`JSON_OBJECT`    |
-   +-------------------------+
-   | :const:`JSON_ARRAY`     |
-   +-------------------------+
-   | :const:`JSON_STRING`    |
-   +-------------------------+
-   | :const:`JSON_INTEGER`   |
-   +-------------------------+
-   | :const:`JSON_REAL`      |
-   +-------------------------+
-   | :const:`JSON_TRUE`      |
-   +-------------------------+
-   | :const:`JSON_FALSE`     |
-   +-------------------------+
-   | :const:`JSON_NULL`      |
-   +-------------------------+
+   +--------------------+
+   | ``JSON_OBJECT``    |
+   +--------------------+
+   | ``JSON_ARRAY``     |
+   +--------------------+
+   | ``JSON_STRING``    |
+   +--------------------+
+   | ``JSON_INTEGER``   |
+   +--------------------+
+   | ``JSON_REAL``      |
+   +--------------------+
+   | ``JSON_TRUE``      |
+   +--------------------+
+   | ``JSON_FALSE``     |
+   +--------------------+
+   | ``JSON_NULL``      |
+   +--------------------+
 
    These correspond to JSON object, array, string, number, boolean and
    null. A number is represented by either a value of the type
-   :const:`JSON_INTEGER` or of the type :const:`JSON_REAL`. A true
-   boolean value is represented by a value of the type
-   :const:`JSON_TRUE` and false by a value of the type
-   :const:`JSON_FALSE`.
+   ``JSON_INTEGER`` or of the type ``JSON_REAL``. A true boolean value
+   is represented by a value of the type ``JSON_TRUE`` and false by a
+   value of the type ``JSON_FALSE``.
 
-.. cfunction:: int json_typeof(const json_t *json)
+.. function:: int json_typeof(const json_t *json)
 
-   Return the type of the JSON value (a :ctype:`json_type` cast to
-   :ctype:`int`). *json* MUST NOT be *NULL*. This function is actually
+   Return the type of the JSON value (a :type:`json_type` cast to
+   :type:`int`). *json* MUST NOT be *NULL*. This function is actually
    implemented as a macro for speed.
 
-.. cfunction:: json_is_object(const json_t *json)
+.. function:: json_is_object(const json_t *json)
                json_is_array(const json_t *json)
                json_is_string(const json_t *json)
                json_is_integer(const json_t *json)
@@ -104,15 +140,15 @@ functions:
    of the given type, and false (zero) for values of other types and
    for *NULL*.
 
-.. cfunction:: json_is_number(const json_t *json)
+.. function:: json_is_number(const json_t *json)
 
-   Returns true for values of types :const:`JSON_INTEGER` and
-   :const:`JSON_REAL`, and false for other types and for *NULL*.
+   Returns true for values of types ``JSON_INTEGER`` and
+   ``JSON_REAL``, and false for other types and for *NULL*.
 
-.. cfunction:: json_is_boolean(const json_t *json)
+.. function:: json_is_boolean(const json_t *json)
 
-   Returns true for types :const:`JSON_TRUE` and :const:`JSON_FALSE`,
-   and false for values of other types and for *NULL*.
+   Returns true for types ``JSON_TRUE`` and ``JSON_FALSE``, and false
+   for values of other types and for *NULL*.
 
 
 .. _apiref-reference-count:
@@ -130,15 +166,15 @@ value can be destroyed.
 
 The following functions are used to manipulate the reference count.
 
-.. cfunction:: json_t *json_incref(json_t *json)
+.. function:: json_t *json_incref(json_t *json)
 
    Increment the reference count of *json* if it's not non-*NULL*.
    Returns *json*.
 
-.. cfunction:: void json_decref(json_t *json)
+.. function:: void json_decref(json_t *json)
 
    Decrement the reference count of *json*. As soon as a call to
-   :cfunc:`json_decref()` drops the reference count to zero, the value
+   :func:`json_decref()` drops the reference count to zero, the value
    is destroyed and it can no longer be used.
 
 Functions creating new JSON values set the reference count to 1. These
@@ -146,17 +182,39 @@ functions are said to return a **new reference**. Other functions
 returning (existing) JSON values do not normally increase the
 reference count. These functions are said to return a **borrowed
 reference**. So, if the user will hold a reference to a value returned
-as a borrowed reference, he must call :cfunc:`json_incref`. As soon as
-the value is no longer needed, :cfunc:`json_decref` should be called
+as a borrowed reference, he must call :func:`json_incref`. As soon as
+the value is no longer needed, :func:`json_decref` should be called
 to release the reference.
 
 Normally, all functions accepting a JSON value as an argument will
 manage the reference, i.e. increase and decrease the reference count
 as needed. However, some functions **steal** the reference, i.e. they
-have the same result as if the user called :cfunc:`json_decref()` on
-the argument right after calling the function. These are usually
-convenience functions for adding new references to containers and not
-to worry about the reference count.
+have the same result as if the user called :func:`json_decref()` on
+the argument right after calling the function. These functions are
+suffixed with ``_new`` or have ``_new_`` somewhere in their name.
+
+For example, the following code creates a new JSON array and appends
+an integer to it::
+
+  json_t *array, *integer;
+
+  array = json_array();
+  integer = json_integer(42);
+
+  json_array_append(array, integer);
+  json_decref(integer);
+
+Note how the caller has to release the reference to the integer value
+by calling :func:`json_decref()`. By using a reference stealing
+function :func:`json_array_append_new()` instead of
+:func:`json_array_append()`, the code becomes much simpler::
+
+  json_t *array = json_array();
+  json_array_append_new(array, json_integer(42));
+
+In this case, the user doesn't have to explicitly release the
+reference to the integer value, as :func:`json_array_append_new()`
+steals the reference when appending the value to the array.
 
 In the following sections it is clearly documented whether a function
 will return a new or borrowed reference or steal a reference to its
@@ -173,7 +231,7 @@ simple::
   json_t *obj = json_object();
   json_object_set(obj, "foo", obj);
 
-Jansson will refuse to do this, and :cfunc:`json_object_set()` (and
+Jansson will refuse to do this, and :func:`json_object_set()` (and
 all the other such functions for objects and arrays) will return with
 an error status. The indirect case is the dangerous one::
 
@@ -187,32 +245,41 @@ indirect circular references without a performance hit, so it's up to
 the user to avoid them.
 
 If a circular reference is created, the memory consumed by the values
-cannot be freed by :cfunc:`json_decref()`. The reference counts never
-drops to zero because the values are keeping the circular reference to
-themselves. Moreover, trying to encode the values with any of the
-encoding functions will fail. The encoder detects circular references
-and returns an error status.
+cannot be freed by :func:`json_decref()`. The reference counts never
+drops to zero because the values are keeping the references to each
+other. Moreover, trying to encode the values with any of the encoding
+functions will fail. The encoder detects circular references and
+returns an error status.
 
 
 True, False and Null
 ====================
 
-These values are implemented as singletons, so each of these functions
-returns the same value each time.
+These three values are implemented as singletons, so the returned
+pointers won't change between invocations of these functions.
 
-.. cfunction:: json_t *json_true(void)
+.. function:: json_t *json_true(void)
 
    .. refcounting:: new
 
    Returns the JSON true value.
 
-.. cfunction:: json_t *json_false(void)
+.. function:: json_t *json_false(void)
 
    .. refcounting:: new
 
    Returns the JSON false value.
 
-.. cfunction:: json_t *json_null(void)
+.. 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()``.
+
+
+.. function:: json_t *json_null(void)
 
    .. refcounting:: new
 
@@ -222,70 +289,134 @@ returns the same value each time.
 String
 ======
 
-.. cfunction:: json_t *json_string(const char *value)
+Jansson uses UTF-8 as the character encoding. All JSON strings must be
+valid UTF-8 (or ASCII, as it's a subset of UTF-8). Normal null
+terminated C strings are used, so JSON strings may not contain
+embedded null characters. All other Unicode codepoints U+0001 through
+U+10FFFF are allowed.
+
+.. function:: json_t *json_string(const char *value)
 
    .. refcounting:: new
 
    Returns a new JSON string, or *NULL* on error. *value* must be a
    valid UTF-8 encoded Unicode string.
 
-.. cfunction:: const char *json_string_value(const json_t *string)
+.. function:: json_t *json_string_nocheck(const char *value)
+
+   .. refcounting:: new
+
+   Like :func:`json_string`, but doesn't check that *value* is valid
+   UTF-8. Use this function only if you are certain that this really
+   is the case (e.g. you have already checked it by other means).
+
+.. function:: const char *json_string_value(const json_t *string)
 
    Returns the associated value of *string* as a null terminated UTF-8
    encoded string, or *NULL* if *string* is not a JSON string.
 
-.. cfunction:: int json_string_set(const json_t *string, const char *value)
+   The retuned value is read-only and must not be modified or freed by
+   the user. It is valid as long as *string* exists, i.e. as long as
+   its reference count has not dropped to zero.
+
+.. function:: int json_string_set(const json_t *string, const char *value)
 
    Sets the associated value of *string* to *value*. *value* must be a
    valid UTF-8 encoded Unicode string. Returns 0 on success and -1 on
    error.
 
-   .. versionadded:: 1.1
+.. function:: int json_string_set_nocheck(const json_t *string, const char *value)
+
+   Like :func:`json_string_set`, but doesn't check that *value* is
+   valid UTF-8. Use this function only if you are certain that this
+   really is the case (e.g. you have already checked it by other
+   means).
 
 
 Number
 ======
 
-.. cfunction:: json_t *json_integer(int value)
+The JSON specification only contains one numeric type, "number". The C
+programming language has distinct types for integer and floating-point
+numbers, so for practical reasons Jansson also has distinct types for
+the two. They are called "integer" and "real", respectively. For more
+information, see :ref:`rfc-conformance`.
+
+.. type:: json_int_t
+
+   This is the C type that is used to store JSON integer values. It
+   represents the widest integer type available on your system. In
+   practice it's just a typedef of ``long long`` if your compiler
+   supports it, otherwise ``long``.
+
+   Usually, you can safely use plain ``int`` in place of
+   ``json_int_t``, and the implicit C integer conversion handles the
+   rest. Only when you know that you need the full 64-bit range, you
+   should use ``json_int_t`` explicitly.
+
+``JSON_INTEGER_IS_LONG_LONG``
+
+   This is a preprocessor variable that holds the value 1 if
+   :type:`json_int_t` is ``long long``, and 0 if it's ``long``. It
+   can be used as follows::
+
+       #if JSON_INTEGER_IS_LONG_LONG
+       /* Code specific for long long */
+       #else
+       /* Code specific for long */
+       #endif
+
+``JSON_INTEGER_FORMAT``
+
+   This is a macro that expands to a :func:`printf()` conversion
+   specifier that corresponds to :type:`json_int_t`, without the
+   leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro
+   is required because the actual type of :type:`json_int_t` can be
+   either ``long`` or ``long long``, and :func:`printf()` reuiqres
+   different length modifiers for the two.
+
+   Example::
+
+       json_int_t x = 123123123;
+       printf("x is %" JSON_INTEGER_FORMAT "\n", x);
+
+
+.. function:: json_t *json_integer(json_int_t value)
 
    .. refcounting:: new
 
    Returns a new JSON integer, or *NULL* on error.
 
-.. cfunction:: int json_integer_value(const json_t *integer)
+.. function:: json_int_t json_integer_value(const json_t *integer)
 
    Returns the associated value of *integer*, or 0 if *json* is not a
    JSON integer.
 
-.. cfunction:: int json_integer_set(const json_t *integer, int value)
+.. function:: int json_integer_set(const json_t *integer, json_int_t value)
 
    Sets the associated value of *integer* to *value*. Returns 0 on
    success and -1 if *integer* is not a JSON integer.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: json_t *json_real(double value)
+.. function:: json_t *json_real(double value)
 
    .. refcounting:: new
 
    Returns a new JSON real, or *NULL* on error.
 
-.. cfunction:: double json_real_value(const json_t *real)
+.. function:: double json_real_value(const json_t *real)
 
    Returns the associated value of *real*, or 0.0 if *real* is not a
    JSON real.
 
-.. cfunction:: int json_real_set(const json_t *real, double value)
+.. function:: int json_real_set(const json_t *real, double value)
 
    Sets the associated value of *real* to *value*. Returns 0 on
    success and -1 if *real* is not a JSON real.
 
-   .. versionadded:: 1.1
-
 In addition to the functions above, there's a common query function
 for integers and reals:
 
-.. cfunction:: double json_number_value(const json_t *json)
+.. function:: double json_number_value(const json_t *json)
 
    Returns the associated value of the JSON integer or JSON real
    *json*, cast to double regardless of the actual type. If *json* is
@@ -297,94 +428,81 @@ Array
 
 A JSON array is an ordered collection of other JSON values.
 
-.. cfunction:: json_t *json_array(void)
+.. function:: json_t *json_array(void)
 
    .. refcounting:: new
 
    Returns a new JSON array, or *NULL* on error. Initially, the array
    is empty.
 
-.. cfunction:: unsigned int json_array_size(const json_t *array)
+.. function:: size_t json_array_size(const json_t *array)
 
    Returns the number of elements in *array*, or 0 if *array* is NULL
    or not a JSON array.
 
-.. cfunction:: json_t *json_array_get(const json_t *array, unsigned int index)
+.. function:: json_t *json_array_get(const json_t *array, size_t index)
 
    .. refcounting:: borrow
 
    Returns the element in *array* at position *index*. The valid range
    for *index* is from 0 to the return value of
-   :cfunc:`json_array_size()` minus 1. If *array* is not a JSON array,
+   :func:`json_array_size()` minus 1. If *array* is not a JSON array,
    if *array* is *NULL*, or if *index* is out of range, *NULL* is
    returned.
 
-.. cfunction:: int json_array_set(json_t *array, unsigned int index, json_t *value)
+.. function:: int json_array_set(json_t *array, size_t index, json_t *value)
 
    Replaces the element in *array* at position *index* with *value*.
    The valid range for *index* is from 0 to the return value of
-   :cfunc:`json_array_size()` minus 1. Returns 0 on success and -1 on
+   :func:`json_array_size()` minus 1. Returns 0 on success and -1 on
    error.
 
-.. cfunction:: int json_array_set_new(json_t *array, unsigned int index, json_t *value)
+.. function:: int json_array_set_new(json_t *array, size_t index, json_t *value)
 
-   Like :cfunc:`json_array_set()` but steals the reference to *value*.
+   Like :func:`json_array_set()` but steals the reference to *value*.
    This is useful when *value* is newly created and not used after
    the call.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: int json_array_append(json_t *array, json_t *value)
+.. function:: int json_array_append(json_t *array, json_t *value)
 
    Appends *value* to the end of *array*, growing the size of *array*
    by 1. Returns 0 on success and -1 on error.
 
-.. cfunction:: int json_array_append_new(json_t *array, json_t *value)
+.. function:: int json_array_append_new(json_t *array, json_t *value)
 
-   Like :cfunc:`json_array_append()` but steals the reference to
+   Like :func:`json_array_append()` but steals the reference to
    *value*. This is useful when *value* is newly created and not used
    after the call.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: int json_array_insert(json_t *array, unsigned int index, json_t *value)
+.. function:: int json_array_insert(json_t *array, size_t index, json_t *value)
 
    Inserts *value* to *array* at position *index*, shifting the
    elements at *index* and after it one position towards the end of
    the array. Returns 0 on success and -1 on error.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: int json_array_insert_new(json_t *array, unsigned int index, json_t *value)
+.. function:: int json_array_insert_new(json_t *array, size_t index, json_t *value)
 
-   Like :cfunc:`json_array_insert()` but steals the reference to
+   Like :func:`json_array_insert()` but steals the reference to
    *value*. This is useful when *value* is newly created and not used
    after the call.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: int json_array_remove(json_t *array, unsigned int index)
+.. function:: int json_array_remove(json_t *array, size_t index)
 
    Removes the element in *array* at position *index*, shifting the
    elements after *index* one position towards the start of the array.
-   Returns 0 on success and -1 on error.
+   Returns 0 on success and -1 on error. The reference count of the
+   removed value is decremented.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: int json_array_clear(json_t *array)
+.. 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.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: int json_array_extend(json_t *array, json_t *other_array)
+.. function:: int json_array_extend(json_t *array, json_t *other_array)
 
    Appends all elements in *other_array* to the end of *array*.
    Returns 0 on success and -1 on error.
 
-   .. versionadded:: 1.1
-
 
 Object
 ======
@@ -392,91 +510,175 @@ Object
 A JSON object is a dictionary of key-value pairs, where the key is a
 Unicode string and the value is any JSON value.
 
-.. cfunction:: json_t *json_object(void)
+.. function:: json_t *json_object(void)
 
    .. refcounting:: new
 
    Returns a new JSON object, or *NULL* on error. Initially, the
    object is empty.
 
-.. cfunction:: unsigned int json_object_size(const json_t *object)
+.. function:: size_t json_object_size(const json_t *object)
 
    Returns the number of elements in *object*, or 0 if *object* is not
    a JSON object.
 
-   .. versionadded:: 1.1
-
-.. cfunction:: json_t *json_object_get(const json_t *object, const char *key)
+.. function:: json_t *json_object_get(const json_t *object, const char *key)
 
    .. refcounting:: borrow
 
    Get a value corresponding to *key* from *object*. Returns *NULL* if
    *key* is not found and on error.
 
-.. cfunction:: int json_object_set(json_t *object, const char *key, json_t *value)
+.. function:: int json_object_set(json_t *object, const char *key, json_t *value)
 
    Set the value of *key* to *value* in *object*. *key* must be a
    valid null terminated UTF-8 encoded Unicode string. If there
    already is a value for *key*, it is replaced by the new value.
    Returns 0 on success and -1 on error.
 
-.. cfunction:: int json_object_set_new(json_t *object, const char *key, json_t *value)
+.. function:: int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
+
+   Like :func:`json_object_set`, but doesn't check that *key* is
+   valid UTF-8. Use this function only if you are certain that this
+   really is the case (e.g. you have already checked it by other
+   means).
 
-   Like :cfunc:`json_object_set()` but steals the reference to
+.. function:: int json_object_set_new(json_t *object, const char *key, json_t *value)
+
+   Like :func:`json_object_set()` but steals the reference to
    *value*. This is useful when *value* is newly created and not used
    after the call.
 
-   .. versionadded:: 1.1
+.. function:: int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value)
 
-.. cfunction:: int json_object_del(json_t *object, const char *key)
+   Like :func:`json_object_set_new`, but doesn't check that *key* is
+   valid UTF-8. Use this function only if you are certain that this
+   really is the case (e.g. you have already checked it by other
+   means).
 
-   Delete *key* from *object* if it exists. Returns 0 on success, or
-   -1 if *key* was not found.
+.. 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. The reference count of the removed value
+   is decremented.
 
-.. cfunction:: int json_object_clear(json_t *object)
+.. 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.
-
-   .. versionadded:: 1.1
+   *object* is not a JSON object. The reference count of all removed
+   values are decremented.
 
-.. cfunction:: int json_object_update(json_t *object, json_t *other)
+.. 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.
 
-   .. versionadded:: 1.1
+.. 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.
 
-The following functions implement an iteration protocol for objects:
+   .. versionadded:: 2.3
 
-.. cfunction:: void *json_object_iter(json_t *object)
+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,
+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)
 
    Returns an opaque iterator which can be used to iterate over all
    key-value pairs in *object*, or *NULL* if *object* is empty.
 
-.. cfunction:: void *json_object_iter_next(json_t *object, void *iter)
+.. function:: void *json_object_iter_at(json_t *object, const char *key)
+
+   Like :func:`json_object_iter()`, but returns an iterator to the
+   key-value pair in *object* whose key is equal to *key*, or NULL if
+   *key* is not found in *object*. Iterating forward to the end of
+   *object* only yields all key-value pairs of the object if *key*
+   happens to be the first key in the underlying hash table.
+
+.. function:: void *json_object_iter_next(json_t *object, void *iter)
 
    Returns an iterator pointing to the next key-value pair in *object*
    after *iter*, or *NULL* if the whole object has been iterated
    through.
 
-.. cfunction:: const char *json_object_iter_key(void *iter)
+.. function:: const char *json_object_iter_key(void *iter)
 
    Extract the associated key from *iter*.
 
-.. cfunction:: json_t *json_object_iter_value(void *iter)
+.. function:: json_t *json_object_iter_value(void *iter)
 
    .. refcounting:: borrow
 
    Extract the associated value from *iter*.
 
+.. function:: int json_object_iter_set(json_t *object, void *iter, json_t *value)
+
+   Set the value of the key-value pair in *object*, that is pointed to
+   by *iter*, to *value*.
+
+.. function:: int json_object_iter_set_new(json_t *object, void *iter, json_t *value)
+
+   Like :func:`json_object_iter_set()`, but steals the reference to
+   *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)
    {
@@ -487,34 +689,139 @@ The iteration protocol can be used for example as follows::
    }
 
 
+Error reporting
+===============
+
+Jansson uses a single struct type to pass error information to the
+user. See sections :ref:`apiref-decoding`, :ref:`apiref-pack` and
+:ref:`apiref-unpack` for functions that pass error information using
+this struct.
+
+.. type:: json_error_t
+
+   .. member:: char text[]
+
+      The error message (in UTF-8), or an empty string if a message is
+      not available.
+
+   .. member:: char source[]
+
+      Source of the error. This can be (a part of) the file name or a
+      special identifier in angle brackers (e.g. ``<string>``).
+
+   .. member:: int line
+
+      The line number on which the error occurred.
+
+   .. member:: int column
+
+      The column on which the error occurred. Note that this is the
+      *character column*, not the byte column, i.e. a multibyte UTF-8
+      character counts as one column.
+
+   .. member:: size_t position
+
+      The position in bytes from the start of the input. This is
+      useful for debugging Unicode encoding problems.
+
+The normal use of :type:`json_error_t` is to allocate it on the stack,
+and pass a pointer to a function. Example::
+
+   int main() {
+       json_t *json;
+       json_error_t error;
+
+       json = json_load_file("/path/to/file.json", 0, &error);
+       if(!json) {
+           /* the error variable contains error information */
+       }
+       ...
+   }
+
+Also note that if the call succeeded (``json != NULL`` in the above
+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.
+
+
 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
+altered by using the ``JSON_INDENT`` and ``JSON_COMPACT`` flags
+described below. A newline is never appended to the end of the encoded
+JSON data.
 
 Each function takes a *flags* parameter that controls some aspects of
 how the data is encoded. Its default value is 0. The following macros
 can be ORed together to obtain *flags*.
 
 ``JSON_INDENT(n)``
-   Pretty-print the result, indenting arrays and objects by *n*
-   spaces. The valid range for *n* is between 0 and 255, other values
-   result in an undefined output. If ``JSON_INDENT`` is not used or
-   *n* is 0, no pretty-printing is done and the result is a compact
-   representation.
+   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 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
+   between array and object items to ``","`` and between object keys
+   and values to ``":"``. Without this flag, the corresponding
+   separators are ``", "`` and ``": "`` for more readable output.
+
+``JSON_ENSURE_ASCII``
+   If this flag is used, the output is guaranteed to consist only of
+   ASCII characters. This is achived by escaping all Unicode
+   characters outside the ASCII range.
+
+``JSON_SORT_KEYS``
+   If this flag is used, all the objects in output are sorted by key.
+   This is useful e.g. if two JSON texts are diffed or visually
+   compared.
+
+``JSON_PRESERVE_ORDER``
+   If this flag is used, object keys in the output are sorted into the
+   same order in which they were first inserted to the object. For
+   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.
 
-.. cfunction:: char *json_dumps(const json_t *root, unsigned long flags)
+.. function:: char *json_dumps(const json_t *root, size_t flags)
 
    Returns the JSON representation of *root* as a string, or *NULL* on
    error. *flags* is described above. The return value must be freed
-   by the caller using :cfunc:`free()`.
+   by the caller using :func:`free()`.
 
-.. cfunction:: int json_dumpf(const json_t *root, FILE *output, unsigned long flags)
+.. function:: int json_dumpf(const json_t *root, FILE *output, size_t flags)
 
    Write the JSON representation of *root* to the stream *output*.
    *flags* is described above. Returns 0 on success and -1 on error.
@@ -522,89 +829,594 @@ is in UTF-8.
    *output*. In this case, the output is undefined and most likely not
    valid JSON.
 
-.. cfunction:: int json_dump_file(const json_t *json, const char *path, unsigned long flags)
+.. function:: int json_dump_file(const json_t *json, const char *path, size_t flags)
 
    Write the JSON representation of *root* to the file *path*. If
    *path* already exists, it is overwritten. *flags* is described
    above. Returns 0 on success and -1 on error.
 
+.. type:: json_dump_callback_t
 
-Decoding
-========
-
-This sections describes the functions that can be used to decode JSON
-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.
+   A typedef for a function that's called by
+   :func:`json_dump_callback()`::
 
-The only supported character encoding is UTF-8 (which ASCII is a
-subset of).
+       typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
 
-.. ctype:: json_error_t
+   *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.
 
-   This data structure is used to return information on decoding
-   errors from the decoding functions. Its definition is repeated
-   here::
+   On error, the function should return -1 to stop the encoding
+   process. On success, it should return 0.
 
-      #define JSON_ERROR_TEXT_LENGTH  160
+   .. versionadded:: 2.2
 
-      typedef struct {
-          char text[JSON_ERROR_TEXT_LENGTH];
-          int line;
-      } json_error_t;
+.. function:: int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
 
-   *line* is the line number on which the error occurred, or -1 if
-   this information is not available. *text* contains the error
-   message (in UTF-8), or an empty string if a message is not
-   available.
+   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.
 
-   The normal usef of :ctype:`json_error_t` is to allocate it normally
-   on the stack, and pass a pointer to a decoding function. Example::
+   .. versionadded:: 2.2
 
-      int main() {
-          json_t *json;
-          json_error_t error;
 
-          json = json_load_file("/path/to/file.json", &error);
-          if(!json) {
-              /* the error variable contains error information */
-          }
-          ...
-      }
+.. _apiref-decoding:
 
-   Also note that if the decoding succeeded (``json != NULL`` in the
-   above example), the contents of ``error`` are unspecified.
+Decoding
+========
 
-   All decoding functions also accept *NULL* as the
-   :ctype:`json_error_t` pointer, in which case no error information
-   is returned to the caller.
+This sections describes the functions that can be used to decode JSON
+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. 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.
 
-.. cfunction:: json_t *json_loads(const char *input, json_error_t *error)
+.. 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. See above for discussion on the
-   *error* parameter.
+   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
 
-.. cfunction:: json_t *json_loadf(FILE *input, json_error_t *error)
+   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)
 
    .. refcounting:: new
 
    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. See above for discussion
-   on the *error* parameter.
+   filled with information about the error. *flags* is described
+   above.
 
-.. cfunction:: json_t *json_load_file(const char *path, json_error_t *error)
+   This function will start reading the input from whatever position
+   the input file was, without attempting to seek first. If an error
+   occurs, the file position will be left indeterminate. On success,
+   the file position will be at EOF, unless ``JSON_DISABLE_EOF_CHECK``
+   flag was used. In this case, the file position will be at the first
+   character after the last ``]`` or ``}`` in the JSON input. This
+   allows calling :func:`json_loadf()` on the same ``FILE`` object
+   multiple times, if the input consists of consecutive JSON texts,
+   possibly separated by whitespace.
+
+.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
 
    .. refcounting:: new
 
    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. See above for discussion
-   on the *error* parameter.
+   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:
+
+Building Values
+===============
+
+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 strings and a boolean as its
+items::
+
+    /* Create the JSON integer 42 */
+    json_pack("i", 42);
+
+    /* Create the JSON array ["foo", "bar", true] */
+    json_pack("[ssb]", "foo", "bar", 1);
+
+Here's the full list of format characters. The type in parentheses
+denotes the resulting JSON type, and the type in brackets (if any)
+denotes the C type that is expected as the corresponding argument.
+
+``s`` (string) [const char \*]
+    Convert a NULL terminated UTF-8 string to a JSON string.
+
+``n`` (null)
+    Output a JSON null value. No argument is consumed.
+
+``b`` (boolean) [int]
+    Convert a C :type:`int` to JSON boolean value. Zero is converted
+    to ``false`` and non-zero to ``true``.
+
+``i`` (integer) [int]
+    Convert a C :type:`int` to JSON integer.
+
+``I`` (integer) [json_int_t]
+    Convert a C :type:`json_int_t` to JSON integer.
+
+``f`` (real) [double]
+    Convert a C :type:`double` to JSON real.
+
+``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
+    stolen by the container.
+
+``O`` (any value) [json_t \*]
+    Like ``o``, but the argument's reference count is incremented.
+    This is useful if you pack into an array or object and want to
+    keep the reference for the JSON value consumed by ``O`` to
+    yourself.
+
+``[fmt]`` (array)
+    Build an array with contents from the inner format string. ``fmt``
+    may contain objects and arrays, i.e. recursive value building is
+    supported.
+
+``{fmt}`` (object)
+    Build an object with contents from the inner format string
+    ``fmt``. The first, third, etc. format character represent a key,
+    and must be ``s`` (as object keys are always strings). The second,
+    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, ...)
+
+   .. refcounting:: new
+
+   Build a new JSON value according to the format string *fmt*. For
+   each format character (except for ``{}[]n``), one argument is
+   consumed and used to build the corresponding value. Returns *NULL*
+   on error.
+
+.. function:: json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
+              json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap)
+
+   .. refcounting:: new
+
+   Like :func:`json_pack()`, but an in the case of an error, an error
+   message is written to *error*, if it's not *NULL*. The *flags*
+   parameter is currently unused and should be set to 0.
+
+   As only the errors in format string (and out-of-memory errors) can
+   be caught by the packer, these two functions are most likely only
+   useful for debugging format strings.
+
+More examples::
+
+  /* Build an empty JSON object */
+  json_pack("{}");
+
+  /* Build the JSON object {"foo": 42, "bar": 7} */
+  json_pack("{sisi}", "foo", 42, "bar", 7);
+
+  /* Like above, ':', ',' and whitespace are ignored */
+  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);
+
+
+.. _apiref-unpack:
+
+Parsing and Validating Values
+=============================
+
+This sectinon describes functions that help to validate complex values
+and extract, or *unpack*, data from them. Like :ref:`building values
+<apiref-pack>`, this is also based on format strings.
+
+While a JSON value is unpacked, the type specified in the format
+string is checked to match that of the JSON value. This is the
+validation part of the process. In addition to this, the unpacking
+functions can also check that all items of arrays and objects are
+unpacked. This check be enabled with the format character ``!`` or by
+using the flag ``JSON_STRICT``. See below for details.
+
+Here's the full list of format characters. The type in parentheses
+denotes the JSON type, and the type in brackets (if any) denotes the C
+type whose address should be passed.
+
+``s`` (string) [const char \*]
+    Convert a JSON string to a pointer to a NULL terminated UTF-8
+    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.
+
+``b`` (boolean) [int]
+    Convert a JSON boolean value to a C :type:`int`, so that ``true``
+    is converted to 1 and ``false`` to 0.
+
+``i`` (integer) [int]
+    Convert a JSON integer to C :type:`int`.
+
+``I`` (integer) [json_int_t]
+    Convert a JSON integer to C :type:`json_int_t`.
+
+``f`` (real) [double]
+    Convert a JSON real to C :type:`double`.
+
+``F`` (integer or real) [double]
+    Convert a JSON number (integer or real) to C :type:`double`.
+
+``o`` (any value) [json_t \*]
+    Store a JSON value with no conversion to a :type:`json_t` pointer.
+
+``O`` (any value) [json_t \*]
+    Like ``O``, but the JSON value's reference count is incremented.
+
+``[fmt]`` (array)
+    Convert each item in the JSON array according to the inner format
+    string. ``fmt`` may contain objects and arrays, i.e. recursive
+    value extraction is supporetd.
+
+``{fmt}`` (object)
+    Convert each item in the JSON object according to the inner format
+    string ``fmt``. The first, third, etc. format character represent
+    a key, and must be ``s``. The corresponding argument to unpack
+    functions is read as the object key. The second fourth, etc.
+    format character represent a value and is written to the address
+    given as the corresponding argument. **Note** that every other
+    argument is read from and every other is written to.
+
+    ``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
+    must appear inside an array or object as the last format character
+    before the closing bracket or brace. To enable the check globally,
+    use the ``JSON_STRICT`` unpacking flag.
+
+``*``
+    This special format character is the opposite of ``!``. If the
+    ``JSON_STRICT`` flag is used, ``*`` can be used to disable the
+    strict check on a per-value basis. It must appear inside an array
+    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, ...)
+
+   Validate and unpack the JSON value *root* according to the format
+   string *fmt*. Returns 0 on success and -1 on failure.
+
+.. function:: int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
+              int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap)
+
+   Validate and unpack the JSON value *root* according to the format
+   string *fmt*. If an error occurs and *error* is not *NULL*, write
+   error information to *error*. *flags* can be used to control the
+   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``
+    Enable the extra validation step checking that all object and
+    array items are unpacked. This is equivalent to appending the
+    format character ``!`` to the end of every array and object in the
+    format string.
+
+``JSON_VALIDATE_ONLY``
+    Don't extract any data, just validate the JSON value against the
+    given format string. Note that object keys must still be specified
+    after the format string.
+
+Examples::
+
+    /* root is the JSON integer 42 */
+    int myint;
+    json_unpack(root, "i", &myint);
+    assert(myint == 42);
+
+    /* root is the JSON object {"foo": "bar", "quux": true} */
+    const char *str;
+    int boolean;
+    json_unpack(root, "{s:s, s:b}", "foo", &str, "quux", &boolean);
+    assert(strcmp(str, "bar") == 0 && boolean == 1);
+
+    /* root is the JSON array [[1, 2], {"baz": null} */
+    json_error_t error;
+    json_unpack_ex(root, &error, JSON_VALIDATE_ONLY, "[[i,i], {s:n}]", "baz");
+    /* returns 0 for validation success, nothing is extracted */
+
+    /* root is the JSON array [1, 2, 3, 4, 5] */
+    int myint1, myint2;
+    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
+========
+
+Testing for equality of two JSON values cannot, in general, be
+achieved using the ``==`` operator. Equality in the terms of the
+``==`` operator states that the two :type:`json_t` pointers point to
+exactly the same JSON value. However, two JSON values can be equal not
+only if they are exactly the same value, but also if they have equal
+"contents":
+
+* Two integer or real values are equal if their contained numeric
+  values are equal. An integer value is never equal to a real value,
+  though.
+
+* Two strings are equal if their contained UTF-8 strings are equal,
+  byte by byte. Unicode comparison algorithms are not implemented.
+
+* Two arrays are equal if they have the same number of elements and
+  each element in the first array is equal to the corresponding
+  element in the second array.
+
+* Two objects are equal if they have exactly the same keys and the
+  value for each key in the first object is equal to the value of the
+  corresponding key in the second object.
+
+* Two true, false or null values have no "contents", so they are equal
+  if their types are equal. (Because these values are singletons,
+  their equality can actually be tested with ``==``.)
+
+The following function can be used to test whether two JSON values are
+equal.
+
+.. function:: int json_equal(json_t *value1, json_t *value2)
+
+   Returns 1 if *value1* and *value2* are equal, as defined above.
+   Returns 0 if they are inequal or one or both of the pointers are
+   *NULL*.
+
+
+Copying
+=======
+
+Because of reference counting, passing JSON values around doesn't
+require copying them. But sometimes a fresh copy of a JSON value is
+needed. For example, if you need to modify an array, but still want to
+use the original afterwards, you should take a copy of it first.
+
+Jansson supports two kinds of copying: shallow and deep. There is a
+difference between these methods only for arrays and objects. Shallow
+copying only copies the first level value (array or object) and uses
+the same child values in the copied value. Deep copying makes a fresh
+copy of the child values, too. Moreover, all the child values are deep
+copied in a recursive fashion.
+
+.. function:: json_t *json_copy(json_t *value)
+
+   .. refcounting:: new
+
+   Returns a shallow copy of *value*, or *NULL* on error.
+
+.. function:: json_t *json_deep_copy(json_t *value)
+
+   .. refcounting:: new
+
+   Returns a deep copy of *value*, or *NULL* on error.
+
+
+.. _apiref-custom-memory-allocation:
+
+Custom Memory Allocation
+========================
+
+By default, Jansson uses :func:`malloc()` and :func:`free()` for
+memory allocation. These functions can be overridden if custom
+behavior is needed.
+
+.. type:: json_malloc_t
+
+   A typedef for a function pointer with :func:`malloc()`'s
+   signature::
+
+       typedef void *(*json_malloc_t)(size_t);
+
+.. type:: json_free_t
+
+   A typedef for a function pointer with :func:`free()`'s
+   signature::
+
+       typedef void (*json_free_t)(void *);
+
+.. function:: void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
+
+   Use *malloc_fn* instead of :func:`malloc()` and *free_fn* instead
+   of :func:`free()`. This function has to be called before any other
+   Jansson's API functions to ensure that all memory operations use
+   the same functions.
+
+Examples:
+
+Use the `Boehm's conservative garbage collector`_ for memory
+operations::
+
+    json_set_alloc_funcs(GC_malloc, GC_free);
+
+.. _Boehm's conservative garbage collector: http://www.hpl.hp.com/personal/Hans_Boehm/gc/
+
+Allow storing sensitive data (e.g. passwords or encryption keys) in
+JSON structures by zeroing all memory when freed::
+
+    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;
+    }
+
+    static void secure_free(void *ptr)
+    {
+        size_t size;
+
+        ptr -= 8;
+        size = *((size_t *)ptr);
+
+        guaranteed_memset(ptr, 0, size);
+        free(ptr);
+    }
+
+    int main()
+    {
+        json_set_alloc_funcs(secure_malloc, secure_free);
+        /* ... */
+    }
+
+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 explains the :func:`guaranteed_memset()` function used
+in the example and gives a sample implementation for it.
diff --git a/doc/changes.rst b/doc/changes.rst
new file mode 100644 (file)
index 0000000..ea56843
--- /dev/null
@@ -0,0 +1,5 @@
+******************
+Changes in Jansson
+******************
+
+.. include:: ../CHANGES
index be257e1..35c5238 100644 (file)
@@ -1,13 +1,10 @@
 # -*- coding: utf-8 -*-
 #
 # Jansson documentation build configuration file, created by
-# sphinx-quickstart on Thu Jul 30 11:35:32 2009.
+# sphinx-quickstart on Sun Sep  5 21:47:20 2010.
 #
 # This file is execfile()d with the current directory set to its containing dir.
 #
-# The contents of this file are pickled, so don't put values in the namespace
-# that aren't pickleable (module imports are okay, they're removed automatically).
-#
 # Note that not all possible configuration values are present in this
 # autogenerated file.
 #
 # serve to show the default.
 
 import sys, os
-sys.path.insert(0, os.path.abspath('ext'))
 
-# If your extensions (or modules documented by autodoc) are in another directory,
+# If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
+sys.path.insert(0, os.path.abspath('ext'))
+
+# -- General configuration -----------------------------------------------------
 
-# General configuration
-# ---------------------
+# If your documentation needs a minimal Sphinx version, state it here.
+needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = ['refcounting']
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = []
+templates_path = ['_templates']
 
 # The suffix of source filenames.
 source_suffix = '.rst'
 
 # The encoding of source files.
-#source_encoding = 'utf-8'
+#source_encoding = 'utf-8-sig'
 
 # The master toctree document.
 master_doc = 'index'
 
 # General information about the project.
 project = u'Jansson'
-copyright = u'2009, 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 = '1.1'
+version = '2.4'
 # The full version, including alpha/beta/rc tags.
-release = '1.1.2'
+release = '2.4-dev'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -64,15 +62,13 @@ release = '1.1.2'
 # Else, today_fmt is used as the format for a strftime call.
 #today_fmt = '%B %d, %Y'
 
-# List of documents that shouldn't be included in the build.
-#unused_docs = []
-
-# List of directories, relative to source directory, that shouldn't be searched
-# for source files.
-exclude_trees = ['.build']
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+default_role = 'c:func'
+primary_domain = 'c'
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
 #add_function_parentheses = True
@@ -88,14 +84,23 @@ exclude_trees = ['.build']
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#html_theme = 'default'
 
-# Options for HTML output
-# -----------------------
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
 
-# The style sheet to use for HTML and HTML Help pages. A file of that name
-# must exist either in Sphinx' static/ path, or in one of the custom paths
-# given in html_static_path.
-html_style = 'default.css'
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
@@ -116,7 +121,7 @@ html_style = 'default.css'
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = []
+#html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
@@ -134,7 +139,7 @@ html_static_path = []
 #html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_use_modindex = True
+#html_domain_indices = True
 
 # If false, no index is generated.
 #html_use_index = True
@@ -142,23 +147,28 @@ html_static_path = []
 # If true, the index is split into individual pages for each letter.
 #html_split_index = False
 
-# If true, the reST sources are included in the HTML build as _sources/<name>.
-#html_copy_source = True
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
 #html_use_opensearch = ''
 
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
 
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'Janssondoc'
 
 
-# Options for LaTeX output
-# ------------------------
+# -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
 #latex_paper_size = 'letter'
@@ -167,10 +177,10 @@ htmlhelp_basename = 'Janssondoc'
 #latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, document class [howto/manual]).
+# (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'Jansson.tex', ur'Jansson Documentation',
-   ur'Petri Lehtinen', 'manual'),
+  ('index', 'Jansson.tex', u'Jansson Documentation',
+   u'Petri Lehtinen', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -181,6 +191,12 @@ latex_documents = [
 # not chapters.
 #latex_use_parts = False
 
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
 # Additional stuff for the LaTeX preamble.
 #latex_preamble = ''
 
@@ -188,4 +204,14 @@ latex_documents = [
 #latex_appendices = []
 
 # If false, no module index is generated.
-#latex_use_modindex = True
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'jansson', u'Jansson Documentation',
+     [u'Petri Lehtinen'], 1)
+]
diff --git a/doc/conformance.rst b/doc/conformance.rst
new file mode 100644 (file)
index 0000000..6f4c929
--- /dev/null
@@ -0,0 +1,111 @@
+.. _rfc-conformance:
+
+***************
+RFC Conformance
+***************
+
+JSON is specified in :rfc:`4627`, *"The application/json Media Type
+for JavaScript Object Notation (JSON)"*.
+
+Character Encoding
+==================
+
+Jansson only supports UTF-8 encoded JSON texts. It does not support or
+auto-detect any of the other encodings mentioned in the RFC, namely
+UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE. Pure ASCII is supported, as
+it's a subset of UTF-8.
+
+Strings
+=======
+
+JSON strings are mapped to C-style null-terminated character arrays,
+and UTF-8 encoding is used internally. Strings may not contain
+embedded null characters, not even escaped ones.
+
+For example, trying to decode the following JSON text leads to a parse
+error::
+
+    ["this string contains the null character: \u0000"]
+
+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
+=======
+
+Real vs. Integer
+----------------
+
+JSON makes no distinction between real and integer numbers; Jansson
+does. Real numbers are mapped to the ``double`` type and integers to
+the ``json_int_t`` type, which is a typedef of ``long long`` or
+``long``, depending on whether ``long long`` is supported by your
+compiler or not.
+
+A JSON number is considered to be a real number if its lexical
+representation includes one of ``e``, ``E``, or ``.``; regardless if
+its actual numeric value is a true integer (e.g., all of ``1E6``,
+``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but
+will be treated as real values).
+
+All other JSON numbers are considered integers.
+
+When encoding to JSON, real values are always represented
+with a fractional part; e.g., the ``double`` value 3.0 will be
+represented in JSON as ``3.0``, not ``3``.
+
+Overflow, Underflow & Precision
+-------------------------------
+
+Real numbers whose absolute values are too small to be represented in
+a C ``double`` will be silently estimated with 0.0. Thus, depending on
+platform, JSON numbers very close to zero such as 1E-999 may result in
+0.0.
+
+Real numbers whose absolute values are too large to be represented in
+a C ``double`` will result in an overflow error (a JSON decoding
+error). Thus, depending on platform, JSON numbers like 1E+999 or
+-1E+999 may result in a parsing error.
+
+Likewise, integer numbers whose absolute values are too large to be
+represented in the ``json_int_t`` type (see above) will result in an
+overflow error (a JSON decoding error). Thus, depending on platform,
+JSON numbers like 1000000000000000 may result in parsing error.
+
+Parsing JSON real numbers may result in a loss of precision. As long
+as overflow does not occur (i.e. a total loss of precision), the
+rounded approximate value is silently used. Thus the JSON number
+1.000000000000000005 may, depending on platform, result in the
+``double`` value 1.0.
+
+Signed zeros
+------------
+
+JSON makes no statement about what a number means; however Javascript
+(ECMAscript) does state that +0.0 and -0.0 must be treated as being
+distinct values, i.e. -0.0 |not-equal| 0.0. Jansson relies on the
+underlying floating point library in the C environment in which it is
+compiled. Therefore it is platform-dependent whether 0.0 and -0.0 will
+be distinct values. Most platforms that use the IEEE 754
+floating-point standard will support signed zeros.
+
+Note that this only applies to floating-point; neither JSON, C, or
+IEEE support the concept of signed integer zeros.
+
+.. |not-equal| unicode:: U+2260
+
+Types
+-----
+
+No support is provided in Jansson for any C numeric types other than
+``json_int_t`` and ``double``. This excludes things such as unsigned
+types, ``long double``, etc. Obviously, shorter types like ``short``,
+``int``, ``long`` (if ``json_int_t`` is ``long long``) and ``float``
+are implicitly handled via the ordinary C type coercion rules (subject
+to overflow semantics). Also, no support or hooks are provided for any
+supplemental "bignum" type add-on packages.
index 506b2ee..4bf48cb 100644 (file)
@@ -19,7 +19,7 @@
 
        <description of the json_object function>
 
-    :copyright: Copyright 2009 Petri Lehtinen <petri@digip.org>
+    :copyright: Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
     :license: MIT, see LICENSE for details.
 """
 
index 00dd16a..f30e1e7 100644 (file)
@@ -10,6 +10,9 @@ Compiling and Installing Jansson
 The Jansson source is available at
 http://www.digip.org/jansson/releases/.
 
+Unix-like systems (including MinGW)
+-----------------------------------
+
 Unpack the source tarball and change to the source directory:
 
 .. parsed-literal::
@@ -31,39 +34,53 @@ the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
 no options to customize the resulting Jansson binary.)
 
 The command ``make check`` runs the test suite distributed with
-Jansson. Python_ is required to run the tests. This step is not
-strictly necessary, but it may find possible problems that Jansson has
-on your platform. If any problems are found, please report them.
+Jansson. This step is not strictly necessary, but it may find possible
+problems that Jansson has on your platform. If any problems are found,
+please report them.
 
 If you obtained the source from a Git repository (or any other source
 control system), there's no ``./configure`` script as it's not kept in
-version control. To create the script, Autotools needs to be
+version control. To create the script, the build system needs to be
 bootstrapped. There are many ways to do this, but the easiest one is
 to use ``autoreconf``::
 
     autoreconf -vi
 
 This command creates the ``./configure`` script, which can then be
-used as described in the previous section.
+used as described above.
 
 .. _autoconf: http://www.gnu.org/software/autoconf/
 .. _automake: http://www.gnu.org/software/automake/
 .. _libtool: http://www.gnu.org/software/libtool/
-.. _Python: http://www.python.org/
 
 
-Installing Prebuilt Binary Packages
------------------------------------
+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
+-------------
 
-Binary ``.deb`` packages for Ubuntu are available in `this PPA`_ at
-Launchpad_. Follow the instructions in the PPA ("Technical details
-about this PPA" link) to take the PPA into use. Then install the -dev
-package::
+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.
 
-  sudo apt-get install libjansson-dev
+1. Create ``jansson_config.h`` (which has some platform-specific
+   parameters that are normally filled in by the ``./configure``
+   script). Edit ``jansson_config.h.in``, replacing all ``@variable@``
+   placeholders, and rename the file to ``jansson_config.h``.
 
-.. _this PPA: http://launchpad.net/~petri/+archive/ppa
-.. _Launchpad: http://launchpad.net/
+2. Make ``jansson.h`` and ``jansson_config.h`` available to the
+   compiler, so that they can be found when compiling programs that
+   use Jansson.
+
+3. Compile all the ``.c`` files (in the ``src/`` directory) into a
+   library file. Make the library available to the compiler, as in
+   step 2.
 
 
 Building the Documentation
@@ -76,18 +93,17 @@ Documentation is in the ``doc/`` subdirectory. It's written in
 reStructuredText_ with Sphinx_ annotations. To generate the HTML
 documentation, invoke::
 
-   cd doc/
-   sphinx-build . .build/html
+   make html
 
-... and point your browser to ``.build/html/index.html``. Sphinx_ is
-required to generate the documentation.
+and point your browser to ``doc/_build/html/index.html``. Sphinx_ 1.0
+or newer is required to generate the documentation.
 
 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
 .. _Sphinx: http://sphinx.pocoo.org/
 
 
-Compiling Programs Using Jansson
-================================
+Compiling Programs that Use Jansson
+===================================
 
 Jansson involves one C header file, :file:`jansson.h`, so it's enough
 to put the line
@@ -102,3 +118,9 @@ There's also just one library to link with, ``libjansson``. Compile and
 link the program as follows::
 
     cc -o prog prog.c -ljansson
+
+Starting from version 1.2, there's also support for pkg-config_::
+
+    cc -o prog prog.c `pkg-config --cflags --libs jansson`
+
+.. _pkg-config: http://pkg-config.freedesktop.org/
index 75b5d34..dde1be5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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
@@ -96,13 +96,12 @@ static char *request(const char *url)
 
 int main(int argc, char *argv[])
 {
-    unsigned int i;
+    size_t i;
     char *text;
     char url[URL_SIZE];
 
     json_t *root;
     json_error_t error;
-    json_t *commits;
 
     if(argc != 3)
     {
@@ -117,7 +116,7 @@ int main(int argc, char *argv[])
     if(!text)
         return 1;
 
-    root = json_loads(text, &error);
+    root = json_loads(text, 0, &error);
     free(text);
 
     if(!root)
@@ -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 1ae9bf3..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/
@@ -33,8 +38,12 @@ Contents
    :maxdepth: 2
 
    gettingstarted
+   upgrading
    tutorial
+   conformance
+   portability
    apiref
+   changes
 
 
 Indices and Tables
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 aaeb554..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::
 
-    unsigned int 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::
@@ -149,10 +151,10 @@ If an error occurs, our function ``request`` prints the error and
 returns *NULL*, so it's enough to just return 1 from the main
 function.
 
-Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
+Next we'll call :func:`json_loads()` to decode the JSON text we got
 as a response::
 
-    root = json_loads(text, &error);
+    root = json_loads(text, 0, &error);
     free(text);
 
     if(!root)
@@ -162,8 +164,8 @@ as a response::
     }
 
 We don't need the JSON text anymore, so we can free the ``text``
-variable right after decoding it. If :cfunc:`json_loads()` fails, it
-returns *NULL* and sets error information to the :ctype:`json_error_t`
+variable right after decoding it. If :func:`json_loads()` fails, it
+returns *NULL* and sets error information to the :type:`json_error_t`
 structure given as the second parameter. In this case, our program
 prints the error information out and returns 1 from the main function.
 
@@ -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, :cfunc:`json_object_get()`
-returns *NULL*, but :cfunc:`json_is_array()` handles this case, too.
-
 Then we proceed to loop over all the commits in the array::
 
-    for(i = 0; i < json_array_size(commits); i++)
+    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 :cfunc:`json_array_size()` returns the size of a JSON
+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 :cfunc:`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;
         }
 
@@ -225,7 +229,7 @@ they both are JSON strings::
 
 And finally, we'll print the first 8 characters of the commit ID and
 the first line of the commit message. A C-style string is extracted
-from a JSON string using :cfunc:`json_string_value()`::
+from a JSON string using :func:`json_string_value()`::
 
         message_text = json_string_value(message);
         printf("%.8s %.*s\n",
@@ -235,9 +239,9 @@ from a JSON string using :cfunc:`json_string_value()`::
     }
 
 After sending the HTTP request, we decoded the JSON text using
-:cfunc:`json_loads()`, remember? It returns a *new reference* to the
+:func:`json_loads()`, remember? It returns a *new reference* to the
 JSON value it decodes. When we're finished with the value, we'll need
-to decrease the reference count using :cfunc:`json_decref()`. This way
+to decrease the reference count using :func:`json_decref()`. This way
 Jansson can release the resources::
 
     json_decref(root);
@@ -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
diff --git a/doc/upgrading.rst b/doc/upgrading.rst
new file mode 100644 (file)
index 0000000..9b49046
--- /dev/null
@@ -0,0 +1,76 @@
+.. highlight:: c
+
+******************
+Upgrading from 1.x
+******************
+
+This chapter lists the backwards incompatible changes introduced in
+Jansson 2.0, and the steps that are needed for upgrading your code.
+
+**The incompatibilities are not dramatic.** The biggest change is that
+all decoding functions now require and extra parameter. Most programs
+can be modified to work with 2.0 by adding a ``0`` as the second
+parameter to all calls of :func:`json_loads()`, :func:`json_loadf()`
+and :func:`json_load_file()`.
+
+
+Compatibility
+=============
+
+Jansson 2.0 is backwards incompatible with the Jansson 1.x releases.
+It is ABI incompatible, i.e. all programs dynamically linking to the
+Jansson library need to be recompiled. It's also API incompatible,
+i.e. the source code of programs using Jansson 1.x may need
+modifications to make them compile against Jansson 2.0.
+
+All the 2.x releases are guaranteed to be backwards compatible for
+both ABI and API, so no recompilation or source changes are needed
+when upgrading from 2.x to 2.y.
+
+
+List of Incompatible Changes
+============================
+
+**Decoding flags**
+    For future needs, a ``flags`` parameter was added as the second
+    parameter to all decoding functions, i.e. :func:`json_loads()`,
+    :func:`json_loadf()` and :func:`json_load_file()`. All calls to
+    these functions need to be changed by adding a ``0`` as the second
+    argument. For example::
+
+        /* old code */
+        json_loads(input, &error);
+
+        /* new code */
+        json_loads(input, 0, &error);
+
+
+**Underlying type of JSON integers**
+    The underlying C type of JSON integers has been changed from
+    :type:`int` to the widest available signed integer type, i.e.
+    :type:`long long` or :type:`long`, depending on whether
+    :type:`long long` is supported on your system or not. This makes
+    the whole 64-bit integer range available on most modern systems.
+
+    ``jansson.h`` has a typedef :type:`json_int_t` to the underlying
+    integer type. :type:`int` should still be used in most cases when
+    dealing with smallish JSON integers, as the compiler handles
+    implicit type coercion. Only when the full 64-bit range is needed,
+    :type:`json_int_t` should be explicitly used.
+
+
+**Maximum encoder indentation depth**
+    The maximum argument of the ``JSON_INDENT()`` macro has been
+    changed from 255 to 31, to free up bits from the ``flags``
+    parameter of :func:`json_dumps()`, :func:`json_dumpf()` and
+    :func:`json_dump_file()`. If your code uses a bigger indentation
+    than 31, it needs to be changed.
+
+
+**Unsigned integers in API functions**
+    Version 2.0 unifies unsigned integer usage in the API. All uses of
+    :type:`unsigned int` and :type:`unsigned long` have been replaced
+    with :type:`size_t`. This includes flags, container sizes, etc.
+    This should not require source code changes, as both
+    :type:`unsigned int` and :type:`unsigned long` are usually
+    compatible with :type:`size_t`.
diff --git a/jansson.pc.in b/jansson.pc.in
new file mode 100644 (file)
index 0000000..d9bf4da
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: Jansson
+Description: Library for encoding, decoding and manipulating JSON data
+Version: @VERSION@
+Libs: -L${libdir} -ljansson
+Cflags: -I${includedir}
index 1da435d..376eea6 100644 (file)
@@ -1,21 +1,29 @@
-include_HEADERS = jansson.h
+EXTRA_DIST = jansson_config.h.win32
+
+include_HEADERS = jansson.h jansson_config.h
 
 lib_LTLIBRARIES = libjansson.la
 libjansson_la_SOURCES = \
        dump.c \
+       error.c \
        hashtable.c \
        hashtable.h \
        jansson_private.h \
        load.c \
+       memory.c \
+       pack_unpack.c \
        strbuffer.c \
        strbuffer.h \
+       strconv.c \
        utf.c \
        utf.h \
-       util.h \
        value.c
 libjansson_la_LDFLAGS = \
        -no-undefined \
        -export-symbols-regex '^json_' \
-       -version-info 1:1:1
+       -version-info 7:1:3
 
-AM_CFLAGS = -Wall -Wextra -Werror
+if GCC
+# These flags are gcc specific
+AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement -Werror
+endif
index 7dbc9f2..2c7dee9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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,29 +9,27 @@
 #include <stdio.h>
 #include <stdlib.h>
 #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)
@@ -39,10 +37,10 @@ static int dump_to_file(const char *buffer, int size, void *data)
     return 0;
 }
 
-/* 256 spaces (the maximum indentation size) */
-static char whitespace[] = "                                                                                                                                                                                                                                                                ";
+/* 32 spaces (the maximum indentation size) */
+static char whitespace[] = "                                ";
 
-static int dump_indent(unsigned long flags, int depth, 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)
     {
@@ -57,37 +55,60 @@ static int dump_indent(unsigned long flags, int depth, dump_func dump, void *dat
                 return -1;
         }
     }
+    else if(space && !(flags & JSON_COMPACT))
+    {
+        return dump(" ", 1, data);
+    }
     return 0;
 }
 
-static int dump_string(const char *str, dump_func dump, void *data)
+static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)
 {
-    const char *end;
+    const char *pos, *end;
+    int32_t codepoint;
 
     if(dump("\"", 1, data))
         return -1;
 
-    end = str;
+    end = pos = str;
     while(1)
     {
         const char *text;
-        char seq[7];
+        char seq[13];
         int length;
 
-        while(*end && *end != '\\' && *end != '"' && (unsigned char)*end > 0x1F)
-            end++;
+        while(*end)
+        {
+            end = utf8_iterate(pos, &codepoint);
+            if(!end)
+                return -1;
+
+            /* mandatory escape or control char */
+            if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
+                break;
+
+            /* slash */
+            if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
+                break;
 
-        if(end != str) {
-            if(dump(str, end - str, data))
+            /* non-ASCII */
+            if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
+                break;
+
+            pos = end;
+        }
+
+        if(pos != str) {
+            if(dump(str, pos - str, data))
                 return -1;
         }
 
-        if(!*end)
+        if(end == pos)
             break;
 
-        /* handle \, ", and control codes */
+        /* handle \, /, ", and control codes */
         length = 2;
-        switch(*end)
+        switch(codepoint)
         {
             case '\\': text = "\\\\"; break;
             case '\"': text = "\\\""; break;
@@ -96,11 +117,30 @@ static int dump_string(const char *str, dump_func dump, void *data)
             case '\n': text = "\\n"; break;
             case '\r': text = "\\r"; break;
             case '\t': text = "\\t"; break;
+            case '/':  text = "\\/"; break;
             default:
             {
-                sprintf(seq, "\\u00%02x", *end);
+                /* codepoint is in BMP */
+                if(codepoint < 0x10000)
+                {
+                    sprintf(seq, "\\u%04x", codepoint);
+                    length = 6;
+                }
+
+                /* not in BMP -> construct a UTF-16 surrogate pair */
+                else
+                {
+                    int32_t first, last;
+
+                    codepoint -= 0x10000;
+                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
+                    last = 0xDC00 | (codepoint & 0x003ff);
+
+                    sprintf(seq, "\\u%04x\\u%04x", first, last);
+                    length = 12;
+                }
+
                 text = seq;
-                length = 6;
                 break;
             }
         }
@@ -108,15 +148,28 @@ static int dump_string(const char *str, dump_func dump, void *data)
         if(dump(text, length, data))
             return -1;
 
-        end++;
-        str = end;
+        str = pos = end;
     }
 
     return dump("\"", 1, data);
 }
 
-static int do_dump(const json_t *json, unsigned long flags, int depth,
-                   dump_func dump, void *data)
+static int object_key_compare_keys(const void *key1, const void *key2)
+{
+    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)
+{
+    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,
+                   json_dump_callback_t dump, void *data)
 {
     switch(json_typeof(json)) {
         case JSON_NULL:
@@ -133,8 +186,10 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
             char buffer[MAX_INTEGER_STR_LENGTH];
             int size;
 
-            size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
-            if(size >= MAX_INTEGER_STR_LENGTH)
+            size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
+                            "%" JSON_INTEGER_FORMAT,
+                            json_integer_value(json));
+            if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
                 return -1;
 
             return dump(buffer, size, data);
@@ -144,16 +199,17 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
         {
             char buffer[MAX_REAL_STR_LENGTH];
             int size;
+            double value = json_real_value(json);
 
-            size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%0.17f", json_real_value(json));
-            if(size >= MAX_REAL_STR_LENGTH)
+            size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
+            if(size < 0)
                 return -1;
 
             return dump(buffer, size, data);
         }
 
         case JSON_STRING:
-            return dump_string(json_string_value(json), dump, data);
+            return dump_string(json_string_value(json), dump, data, flags);
 
         case JSON_ARRAY:
         {
@@ -164,87 +220,181 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
             /* detect circular references */
             array = json_to_array(json);
             if(array->visited)
-                return -1;
+                goto array_error;
             array->visited = 1;
 
             n = json_array_size(json);
 
             if(dump("[", 1, data))
-                return -1;
-            if(n == 0)
+                goto array_error;
+            if(n == 0) {
+                array->visited = 0;
                 return dump("]", 1, data);
-            if(dump_indent(flags, depth + 1, dump, data))
-                return -1;
+            }
+            if(dump_indent(flags, depth + 1, 0, dump, data))
+                goto array_error;
 
             for(i = 0; i < n; ++i) {
                 if(do_dump(json_array_get(json, i), flags, depth + 1,
                            dump, data))
-                    return -1;
+                    goto array_error;
 
                 if(i < n - 1)
                 {
                     if(dump(",", 1, data) ||
-                       dump_indent(flags, depth + 1, dump, data))
-                        return -1;
+                       dump_indent(flags, depth + 1, 1, dump, data))
+                        goto array_error;
                 }
                 else
                 {
-                    if(dump_indent(flags, depth, dump, data))
-                        return -1;
+                    if(dump_indent(flags, depth, 0, dump, data))
+                        goto array_error;
                 }
             }
 
             array->visited = 0;
             return dump("]", 1, data);
+
+        array_error:
+            array->visited = 0;
+            return -1;
         }
 
         case JSON_OBJECT:
         {
             json_object_t *object;
             void *iter;
+            const char *separator;
+            int separator_length;
+
+            if(flags & JSON_COMPACT) {
+                separator = ":";
+                separator_length = 1;
+            }
+            else {
+                separator = ": ";
+                separator_length = 2;
+            }
 
             /* detect circular references */
             object = json_to_object(json);
             if(object->visited)
-                return -1;
+                goto object_error;
             object->visited = 1;
 
             iter = json_object_iter((json_t *)json);
 
             if(dump("{", 1, data))
-                return -1;
-            if(!iter)
+                goto object_error;
+            if(!iter) {
+                object->visited = 0;
                 return dump("}", 1, data);
-            if(dump_indent(flags, depth + 1, dump, data))
-                return -1;
+            }
+            if(dump_indent(flags, depth + 1, 0, dump, data))
+                goto object_error;
 
-            while(iter)
+            if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
             {
-                void *next = json_object_iter_next((json_t *)json, iter);
+                struct object_key *keys;
+                size_t size, i;
+                int (*cmp_func)(const void *, const void *);
 
-                dump_string(json_object_iter_key(iter), dump, data);
-                if(dump(": ", 2, data) ||
-                   do_dump(json_object_iter_value(iter), flags, depth + 1,
-                           dump, data))
-                    return -1;
+                size = json_object_size(json);
+                keys = jsonp_malloc(size * sizeof(struct object_key));
+                if(!keys)
+                    goto object_error;
 
-                if(next)
+                i = 0;
+                while(iter)
                 {
-                    if(dump(",", 1, data) ||
-                       dump_indent(flags, depth + 1, dump, data))
-                        return -1;
+                    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++;
                 }
+                assert(i == size);
+
+                if(flags & JSON_SORT_KEYS)
+                    cmp_func = object_key_compare_keys;
                 else
+                    cmp_func = object_key_compare_serials;
+
+                qsort(keys, size, sizeof(struct object_key), cmp_func);
+
+                for(i = 0; i < size; i++)
                 {
-                    if(dump_indent(flags, depth, dump, data))
-                        return -1;
+                    const char *key;
+                    json_t *value;
+
+                    key = keys[i].key;
+                    value = json_object_get(json, key);
+                    assert(value);
+
+                    dump_string(key, dump, data, flags);
+                    if(dump(separator, separator_length, data) ||
+                       do_dump(value, flags, depth + 1, dump, data))
+                    {
+                        jsonp_free(keys);
+                        goto object_error;
+                    }
+
+                    if(i < size - 1)
+                    {
+                        if(dump(",", 1, data) ||
+                           dump_indent(flags, depth + 1, 1, dump, data))
+                        {
+                            jsonp_free(keys);
+                            goto object_error;
+                        }
+                    }
+                    else
+                    {
+                        if(dump_indent(flags, depth, 0, dump, data))
+                        {
+                            jsonp_free(keys);
+                            goto object_error;
+                        }
+                    }
                 }
 
-                iter = next;
+                jsonp_free(keys);
+            }
+            else
+            {
+                /* Don't sort keys */
+
+                while(iter)
+                {
+                    void *next = json_object_iter_next((json_t *)json, iter);
+
+                    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))
+                        goto object_error;
+
+                    if(next)
+                    {
+                        if(dump(",", 1, data) ||
+                           dump_indent(flags, depth + 1, 1, dump, data))
+                            goto object_error;
+                    }
+                    else
+                    {
+                        if(dump_indent(flags, depth, 0, dump, data))
+                            goto object_error;
+                    }
+
+                    iter = next;
+                }
             }
 
             object->visited = 0;
             return dump("}", 1, data);
+
+        object_error:
+            object->visited = 0;
+            return -1;
         }
 
         default:
@@ -253,45 +403,29 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
     }
 }
 
-
-char *json_dumps(const json_t *json, unsigned long flags)
+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(dump_to_strbuffer("\n", 1, (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 = strdup(strbuffer_value(&strbuff));
     strbuffer_close(&strbuff);
-
     return result;
 }
 
-int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
+int json_dumpf(const json_t *json, FILE *output, size_t flags)
 {
-    if(!json_is_array(json) && !json_is_object(json))
-        return -1;
-
-    if(do_dump(json, flags, 0, dump_to_file, (void *)output))
-        return -1;
-    return dump_to_file("\n", 1, (void *)output);
+    return json_dump_callback(json, dump_to_file, (void *)output, flags);
 }
 
-int json_dump_file(const json_t *json, const char *path, unsigned long flags)
+int json_dump_file(const json_t *json, const char *path, size_t flags)
 {
     int result;
 
@@ -304,3 +438,13 @@ int json_dump_file(const json_t *json, const char *path, unsigned long 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);
+}
diff --git a/src/error.c b/src/error.c
new file mode 100644 (file)
index 0000000..a544a59
--- /dev/null
@@ -0,0 +1,63 @@
+#include <string.h>
+#include "jansson_private.h"
+
+void jsonp_error_init(json_error_t *error, const char *source)
+{
+    if(error)
+    {
+        error->text[0] = '\0';
+        error->line = -1;
+        error->column = -1;
+        error->position = 0;
+        if(source)
+            jsonp_error_set_source(error, source);
+        else
+            error->source[0] = '\0';
+    }
+}
+
+void jsonp_error_set_source(json_error_t *error, const char *source)
+{
+    size_t length;
+
+    if(!error || !source)
+        return;
+
+    length = strlen(source);
+    if(length < JSON_ERROR_SOURCE_LENGTH)
+        strcpy(error->source, source);
+    else {
+        size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
+        strcpy(error->source, "...");
+        strcpy(error->source + 3, source + extra);
+    }
+}
+
+void jsonp_error_set(json_error_t *error, int line, int column,
+                     size_t position, const char *msg, ...)
+{
+    va_list ap;
+
+    va_start(ap, msg);
+    jsonp_error_vset(error, line, column, position, msg, ap);
+    va_end(ap);
+}
+
+void jsonp_error_vset(json_error_t *error, int line, int column,
+                      size_t position, const char *msg, va_list ap)
+{
+    if(!error)
+        return;
+
+    if(error->text[0] != '\0') {
+        /* error already set */
+        return;
+    }
+
+    error->line = line;
+    error->column = column;
+    error->position = position;
+
+    vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
+    error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
+}
index 05dc167..76cf69b 100644 (file)
@@ -1,29 +1,46 @@
 /*
- * Copyright (c) 2009 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"
 
 typedef struct hashtable_list list_t;
 typedef struct hashtable_pair pair_t;
 typedef struct hashtable_bucket bucket_t;
 
-#define container_of(ptr_, type_, member_)                      \
-    ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
-
 #define list_to_pair(list_)  container_of(list_, pair_t, list)
 
-static inline void list_init(list_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;
     list->prev = list;
 }
 
-static inline void list_insert(list_t *list, list_t *node)
+static JSON_INLINE void list_insert(list_t *list, list_t *node)
 {
     node->next = list;
     node->prev = list->prev;
@@ -31,13 +48,13 @@ static inline void list_insert(list_t *list, list_t *node)
     list->prev = node;
 }
 
-static inline void list_remove(list_t *list)
+static JSON_INLINE void list_remove(list_t *list)
 {
     list->prev->next = list->next;
     list->next->prev = list->prev;
 }
 
-static inline int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
+static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
 {
     return bucket->first == &hashtable->list && bucket->first == bucket->last;
 }
@@ -57,22 +74,21 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
     }
 }
 
-static unsigned int primes[] = {
+static size_t primes[] = {
     5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
     49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
     12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
     805306457, 1610612741
 };
-static const unsigned int num_primes = sizeof(primes) / sizeof(unsigned int);
 
-static inline unsigned int num_buckets(hashtable_t *hashtable)
+static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
 {
     return primes[hashtable->num_buckets];
 }
 
 
 static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
-                                   const void *key, unsigned int hash)
+                                   const char *key, size_t hash)
 {
     list_t *list;
     pair_t *pair;
@@ -84,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)
@@ -98,11 +114,11 @@ 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, unsigned int hash)
+                            const char *key, size_t hash)
 {
     pair_t *pair;
     bucket_t *bucket;
-    unsigned int index;
+    size_t index;
 
     index = hash % num_buckets(hashtable);
     bucket = &hashtable->buckets[index];
@@ -121,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;
@@ -142,11 +154,8 @@ 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);
-        free(pair);
+        json_decref(pair->value);
+        jsonp_free(pair);
     }
 }
 
@@ -154,14 +163,14 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
 {
     list_t *list, *next;
     pair_t *pair;
-    unsigned int i, index, new_size;
+    size_t i, index, new_size;
 
-    free(hashtable->buckets);
+    jsonp_free(hashtable->buckets);
 
     hashtable->num_buckets++;
     new_size = num_buckets(hashtable);
 
-    hashtable->buckets = malloc(new_size * sizeof(bucket_t));
+    hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
     if(!hashtable->buckets)
         return -1;
 
@@ -185,47 +194,18 @@ 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)
+int hashtable_init(hashtable_t *hashtable)
 {
-    hashtable_t *hashtable = malloc(sizeof(hashtable_t));
-    if(!hashtable)
-        return NULL;
-
-    if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
-    {
-        free(hashtable);
-        return NULL;
-    }
-
-    return hashtable;
-}
-
-void hashtable_destroy(hashtable_t *hashtable)
-{
-    hashtable_close(hashtable);
-    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)
-{
-    unsigned int i;
+    size_t i;
 
     hashtable->size = 0;
     hashtable->num_buckets = 0;  /* index to primes[] */
-    hashtable->buckets = malloc(num_buckets(hashtable) * sizeof(bucket_t));
+    hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t));
     if(!hashtable->buckets)
         return -1;
 
     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 =
@@ -238,50 +218,61 @@ int hashtable_init(hashtable_t *hashtable,
 void hashtable_close(hashtable_t *hashtable)
 {
     hashtable_do_clear(hashtable);
-    free(hashtable->buckets);
+    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;
-    unsigned int hash, index;
-
-    hash = hashtable->hash_key(key);
-
-    /* if the key already exists, delete it */
-    hashtable_do_del(hashtable, key, hash);
+    size_t hash, index;
 
     /* rehash if the load ratio exceeds 1 */
     if(hashtable->size >= num_buckets(hashtable))
         if(hashtable_do_rehash(hashtable))
             return -1;
 
-    pair = malloc(sizeof(pair_t));
-    if(!pair)
-        return -1;
-
-    pair->key = key;
-    pair->value = value;
-    pair->hash = hash;
-    list_init(&pair->list);
-
+    hash = hash_str(key);
     index = hash % num_buckets(hashtable);
     bucket = &hashtable->buckets[index];
+    pair = hashtable_find_pair(hashtable, bucket, key, hash);
 
-    insert_to_bucket(hashtable, bucket, &pair->list);
+    if(pair)
+    {
+        json_decref(pair->value);
+        pair->value = value;
+    }
+    else
+    {
+        /* 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->hash = hash;
+        pair->serial = serial;
+        strcpy(pair->key, key);
+        pair->value = value;
+        list_init(&pair->list);
 
-    hashtable->size++;
+        insert_to_bucket(hashtable, bucket, &pair->list);
+
+        hashtable->size++;
+    }
     return 0;
 }
 
-void *hashtable_get(hashtable_t *hashtable, const void *key)
+void *hashtable_get(hashtable_t *hashtable, const char *key)
 {
     pair_t *pair;
-    unsigned int hash;
+    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);
@@ -291,15 +282,15 @@ 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)
 {
-    unsigned int hash = hashtable->hash_key(key);
+    size_t hash = hash_str(key);
     return hashtable_do_del(hashtable, key, hash);
 }
 
 void hashtable_clear(hashtable_t *hashtable)
 {
-    unsigned int i;
+    size_t i;
 
     hashtable_do_clear(hashtable);
 
@@ -318,6 +309,22 @@ void *hashtable_iter(hashtable_t *hashtable)
     return hashtable_iter_next(hashtable, &hashtable->list);
 }
 
+void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
+{
+    pair_t *pair;
+    size_t hash;
+    bucket_t *bucket;
+
+    hash = hash_str(key);
+    bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
+
+    pair = hashtable_find_pair(hashtable, bucket, key, hash);
+    if(!pair)
+        return NULL;
+
+    return &pair->list;
+}
+
 void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
 {
     list_t *list = (list_t *)iter;
@@ -332,8 +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(void *iter, json_t *value)
+{
+    pair_t *pair = list_to_pair((list_t *)iter);
+
+    json_decref(pair->value);
+    pair->value = value;
+}
index 81a0af5..de1df26 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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 unsigned int (*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;
-    unsigned int hash;
+    size_t hash;
     struct hashtable_list list;
+    json_t *value;
+    size_t serial;
+    char key[1];
 };
 
 struct hashtable_bucket {
@@ -30,60 +30,27 @@ struct hashtable_bucket {
 };
 
 typedef struct hashtable {
-    unsigned int size;
+    size_t size;
     struct hashtable_bucket *buckets;
-    unsigned int num_buckets;  /* index to primes[] */
+    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
@@ -161,6 +127,17 @@ void hashtable_clear(hashtable_t *hashtable);
 void *hashtable_iter(hashtable_t *hashtable);
 
 /**
+ * hashtable_iter_at - Return an iterator at a specific key
+ *
+ * @hashtable: The hashtable object
+ * @key: The key that the iterator should point to
+ *
+ * Like hashtable_iter() but returns an iterator pointing to a
+ * specific key.
+ */
+void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
+
+/**
  * hashtable_iter_next - Advance an iterator
  *
  * @hashtable: The hashtable object
@@ -179,10 +156,25 @@ 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
  */
 void *hashtable_iter_value(void *iter);
 
+/**
+ * hashtable_iter_set - Set the value pointed by an iterator
+ *
+ * @iter: The iterator
+ * @value: The value to set
+ */
+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 ac6736f..1f1cc14 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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,11 +9,31 @@
 #define JANSSON_H
 
 #include <stdio.h>
+#include <stdlib.h>  /* for size_t */
+#include <stdarg.h>
+
+#include <jansson_config.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* version */
+
+#define JANSSON_MAJOR_VERSION  2
+#define JANSSON_MINOR_VERSION  3
+#define JANSSON_MICRO_VERSION  99
+
+/* Micro version is omitted if it's 0 */
+#define JANSSON_VERSION  "2.4-dev"
+
+/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
+   for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
+#define JANSSON_VERSION_HEX  ((JANSSON_MAJOR_VERSION << 16) |   \
+                              (JANSSON_MINOR_VERSION << 8)  |   \
+                              (JANSSON_MICRO_VERSION << 0))
+
+
 /* types */
 
 typedef enum {
@@ -29,9 +49,21 @@ typedef enum {
 
 typedef struct {
     json_type type;
-    unsigned long refcount;
+    size_t refcount;
 } 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"
+typedef long json_int_t;
+#endif /* JSON_INTEGER_IS_LONG_LONG */
+
 #define json_typeof(json)      ((json)->type)
 #define json_is_object(json)   (json && json_typeof(json) == JSON_OBJECT)
 #define json_is_array(json)    (json && json_typeof(json) == JSON_ARRAY)
@@ -49,15 +81,18 @@ typedef struct {
 json_t *json_object(void);
 json_t *json_array(void);
 json_t *json_string(const char *value);
-json_t *json_integer(int value);
+json_t *json_string_nocheck(const char *value);
+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 inline json_t *json_incref(json_t *json)
+static JSON_INLINE
+json_t *json_incref(json_t *json)
 {
-    if(json && json->refcount != (unsigned int)-1)
+    if(json && json->refcount != (size_t)-1)
         ++json->refcount;
     return json;
 }
@@ -65,87 +100,171 @@ static inline json_t *json_incref(json_t *json)
 /* do not call json_delete directly */
 void json_delete(json_t *json);
 
-static inline void json_decref(json_t *json)
+static JSON_INLINE
+void json_decref(json_t *json)
 {
-    if(json && json->refcount != (unsigned int)-1 && --json->refcount == 0)
+    if(json && json->refcount != (size_t)-1 && --json->refcount == 0)
         json_delete(json);
 }
 
 
+/* error reporting */
+
+#define JSON_ERROR_TEXT_LENGTH    160
+#define JSON_ERROR_SOURCE_LENGTH   80
+
+typedef struct {
+    int line;
+    int column;
+    int position;
+    char source[JSON_ERROR_SOURCE_LENGTH];
+    char text[JSON_ERROR_TEXT_LENGTH];
+} json_error_t;
+
+
 /* getters, setters, manipulation */
 
-unsigned int json_object_size(const json_t *object);
+size_t json_object_size(const json_t *object);
 json_t *json_object_get(const json_t *object, const char *key);
 int json_object_set_new(json_t *object, const char *key, json_t *value);
+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 inline
+static JSON_INLINE
 int json_object_set(json_t *object, const char *key, json_t *value)
 {
     return json_object_set_new(object, key, json_incref(value));
 }
 
-unsigned int json_array_size(const json_t *array);
-json_t *json_array_get(const json_t *array, unsigned int index);
-int json_array_set_new(json_t *array, unsigned int index, json_t *value);
+static JSON_INLINE
+int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
+{
+    return json_object_set_new_nocheck(object, key, json_incref(value));
+}
+
+static JSON_INLINE
+int json_object_iter_set(json_t *object, void *iter, json_t *value)
+{
+    return json_object_iter_set_new(object, iter, json_incref(value));
+}
+
+size_t json_array_size(const json_t *array);
+json_t *json_array_get(const json_t *array, size_t index);
+int json_array_set_new(json_t *array, size_t index, json_t *value);
 int json_array_append_new(json_t *array, json_t *value);
-int json_array_insert_new(json_t *array, unsigned int index, json_t *value);
-int json_array_remove(json_t *array, unsigned int index);
+int json_array_insert_new(json_t *array, size_t index, json_t *value);
+int json_array_remove(json_t *array, size_t index);
 int json_array_clear(json_t *array);
 int json_array_extend(json_t *array, json_t *other);
 
-static inline
-int json_array_set(json_t *array, unsigned int index, json_t *value)
+static JSON_INLINE
+int json_array_set(json_t *array, size_t index, json_t *value)
 {
     return json_array_set_new(array, index, json_incref(value));
 }
 
-static inline
+static JSON_INLINE
 int json_array_append(json_t *array, json_t *value)
 {
     return json_array_append_new(array, json_incref(value));
 }
 
-static inline
-int json_array_insert(json_t *array, unsigned int index, json_t *value)
+static JSON_INLINE
+int json_array_insert(json_t *array, size_t index, json_t *value)
 {
     return json_array_insert_new(array, index, json_incref(value));
 }
 
 const char *json_string_value(const json_t *string);
-int json_integer_value(const json_t *integer);
+json_int_t json_integer_value(const json_t *integer);
 double json_real_value(const json_t *real);
 double json_number_value(const json_t *json);
 
-int json_string_set(const json_t *string, const char *value);
-int json_integer_set(const json_t *integer, int value);
-int json_real_set(const json_t *real, double value);
+int json_string_set(json_t *string, const char *value);
+int json_string_set_nocheck(json_t *string, const char *value);
+int json_integer_set(json_t *integer, json_int_t value);
+int json_real_set(json_t *real, double value);
 
 
-/* loading, printing */
+/* pack, unpack */
 
-#define JSON_ERROR_TEXT_LENGTH  160
+json_t *json_pack(const char *fmt, ...);
+json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...);
+json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap);
 
-typedef struct {
-    char text[JSON_ERROR_TEXT_LENGTH];
-    int line;
-} json_error_t;
+#define JSON_VALIDATE_ONLY  0x1
+#define JSON_STRICT         0x2
+
+int json_unpack(json_t *root, const char *fmt, ...);
+int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...);
+int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap);
+
+
+/* equality */
+
+int json_equal(json_t *value1, json_t *value2);
+
+
+/* copying */
+
+json_t *json_copy(json_t *value);
+json_t *json_deep_copy(json_t *value);
+
+
+/* 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);
 
-json_t *json_loads(const char *input, json_error_t *error);
-json_t *json_loadf(FILE *input, json_error_t *error);
-json_t *json_load_file(const char *path, json_error_t *error);
+/* custom memory allocation */
 
-#define JSON_INDENT(n)   (n & 0xFF)
+typedef void *(*json_malloc_t)(size_t);
+typedef void (*json_free_t)(void *);
 
-char *json_dumps(const json_t *json, unsigned long flags);
-int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
-int json_dump_file(const json_t *json, const char *path, unsigned long flags);
+void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
 
 #ifdef __cplusplus
 }
diff --git a/src/jansson_config.h.in b/src/jansson_config.h.in
new file mode 100644 (file)
index 0000000..a566e8b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *
+ *
+ * This file specifies a part of the site-specific configuration for
+ * Jansson, namely those things that affect the public API in
+ * jansson.h.
+ *
+ * The configure script copies this file to jansson_config.h and
+ * replaces @var@ substitutions by values that fit your system. If you
+ * cannot run the configure script, you can do the value substitution
+ * by hand.
+ */
+
+#ifndef JANSSON_CONFIG_H
+#define JANSSON_CONFIG_H
+
+/* If your compiler supports the inline keyword in C, JSON_INLINE is
+   defined to `inline', otherwise empty. In C++, the inline is always
+   supported. */
+#ifdef __cplusplus
+#define JSON_INLINE inline
+#else
+#define JSON_INLINE @json_inline@
+#endif
+
+/* If your compiler supports the `long long` type and the strtoll()
+   library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
+   otherwise to 0. */
+#define JSON_INTEGER_IS_LONG_LONG @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 317f05a..7d6d09d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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,22 +8,40 @@
 #ifndef JANSSON_PRIVATE_H
 #define JANSSON_PRIVATE_H
 
+#include <stddef.h>
 #include "jansson.h"
 #include "hashtable.h"
+#include "strbuffer.h"
 
 #define container_of(ptr_, type_, member_)  \
-    ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
+    ((type_ *)((char *)ptr_ - offsetof(type_, member_)))
+
+/* On some platforms, max() may already be defined */
+#ifndef max
+#define max(a, b)  ((a) > (b) ? (a) : (b))
+#endif
+
+/* va_copy is a C99 feature. In C89 implementations, it's sometimes
+   available as __va_copy. If not, memcpy() should do the trick. */
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(a, b)  memcpy(&(a), &(b), sizeof(va_list))
+#endif
+#endif
 
 typedef struct {
     json_t json;
     hashtable_t hashtable;
+    size_t serial;
     int visited;
 } json_object_t;
 
 typedef struct {
     json_t json;
-    unsigned int size;
-    unsigned int entries;
+    size_t size;
+    size_t entries;
     json_t **table;
     int visited;
 } json_array_t;
@@ -40,7 +58,7 @@ typedef struct {
 
 typedef struct {
     json_t json;
-    int value;
+    json_int_t value;
 } json_integer_t;
 
 #define json_to_object(json_)  container_of(json_, json_object_t, json)
@@ -49,7 +67,26 @@ 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)
 
-int json_object_set_nocheck(json_t *json, const char *key, json_t *value);
-json_t *json_string_nocheck(const char *value);
+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,
+                     size_t position, const char *msg, ...);
+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 005e03c..d88a704 100644 (file)
@@ -1,25 +1,27 @@
 /*
- * Copyright (c) 2009 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>
 #include <stdlib.h>
 #include <string.h>
-#include <stdarg.h>
 #include <assert.h>
 
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 #include "strbuffer.h"
 #include "utf.h"
 
+#define STREAM_STATE_OK        0
+#define STREAM_STATE_EOF      -1
+#define STREAM_STATE_ERROR    -2
+
 #define TOKEN_INVALID         -1
 #define TOKEN_EOF              0
 #define TOKEN_STRING         256
 #define TOKEN_FALSE          260
 #define TOKEN_NULL           261
 
-/* read one byte from stream, return EOF on end of file */
+/* 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(). */
 typedef int (*get_func)(void *data);
 
-/* return non-zero if end of file has been reached */
-typedef int (*eof_func)(void *data);
-
 typedef struct {
     get_func get;
-    eof_func eof;
     void *data;
-    int stream_pos;
     char buffer[5];
-    int buffer_pos;
+    size_t buffer_pos;
+    int state;
+    int line;
+    int column, last_column;
+    size_t position;
 } stream_t;
 
-
 typedef struct {
     stream_t stream;
     strbuffer_t saved_text;
     int token;
-    int line, column;
     union {
         char *string;
-        int integer;
+        json_int_t integer;
         double real;
     } value;
 } lex_t;
 
+#define stream_to_lex(stream) container_of(stream, lex_t, stream)
 
-/*** error reporting ***/
 
-static void error_init(json_error_t *error)
-{
-    if(error)
-    {
-        error->text[0] = '\0';
-        error->line = -1;
-    }
-}
+/*** error reporting ***/
 
 static void error_set(json_error_t *error, const lex_t *lex,
                       const char *msg, ...)
 {
     va_list ap;
-    char text[JSON_ERROR_TEXT_LENGTH];
+    char msg_text[JSON_ERROR_TEXT_LENGTH];
+    char msg_with_context[JSON_ERROR_TEXT_LENGTH];
+
+    int line = -1, col = -1;
+    size_t pos = 0;
+    const char *result = msg_text;
 
-    if(!error || error->text[0] != '\0') {
-        /* error already set */
+    if(!error)
         return;
-    }
 
     va_start(ap, msg);
-    vsnprintf(text, JSON_ERROR_TEXT_LENGTH, msg, ap);
+    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);
-        error->line = lex->line;
+
+        line = lex->stream.line;
+        col = lex->stream.column;
+        pos = lex->stream.position;
+
         if(saved_text && saved_text[0])
         {
             if(lex->saved_text.length <= 20) {
-                snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
-                         "%s near '%s'", text, saved_text);
+                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;
             }
-            else
-                snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
         }
         else
         {
-            snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
-                     "%s near end of file", text);
+            if(lex->stream.state == STREAM_STATE_ERROR) {
+                /* No context for UTF-8 decoding errors */
+                result = msg_text;
+            }
+            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;
+            }
         }
     }
-    else
-    {
-        error->line = -1;
-        snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
-    }
+
+    jsonp_error_set(error, line, col, pos, "%s", result);
 }
 
 
 /*** lexical analyzer ***/
 
-void stream_init(stream_t *stream, get_func get, eof_func eof, void *data)
+static void
+stream_init(stream_t *stream, get_func get, void *data)
 {
     stream->get = get;
-    stream->eof = eof;
     stream->data = data;
-    stream->stream_pos = 0;
     stream->buffer[0] = '\0';
     stream->buffer_pos = 0;
+
+    stream->state = STREAM_STATE_OK;
+    stream->line = 1;
+    stream->column = 0;
+    stream->position = 0;
 }
 
-static char stream_get(stream_t *stream, json_error_t *error)
+static int stream_get(stream_t *stream, json_error_t *error)
 {
-    char c;
+    int c;
+
+    if(stream->state != STREAM_STATE_OK)
+        return stream->state;
 
     if(!stream->buffer[stream->buffer_pos])
     {
-        stream->buffer[0] = stream->get(stream->data);
-        stream->buffer_pos = 0;
+        c = stream->get(stream->data);
+        if(c == EOF) {
+            stream->state = STREAM_STATE_EOF;
+            return STREAM_STATE_EOF;
+        }
 
-        c = stream->buffer[0];
+        stream->buffer[0] = c;
+        stream->buffer_pos = 0;
 
-        if((unsigned char)c >= 0x80 && c != (char)EOF)
+        if(0x80 <= c && c <= 0xFF)
         {
             /* multi-byte UTF-8 sequence */
             int i, count;
@@ -148,33 +174,50 @@ static char stream_get(stream_t *stream, json_error_t *error)
             for(i = 1; i < count; i++)
                 stream->buffer[i] = stream->get(stream->data);
 
-            if(!utf8_check_full(stream->buffer, count))
+            if(!utf8_check_full(stream->buffer, count, NULL))
                 goto out;
 
-            stream->stream_pos += count;
             stream->buffer[count] = '\0';
         }
-        else {
+        else
             stream->buffer[1] = '\0';
-            stream->stream_pos++;
-        }
     }
 
-    return stream->buffer[stream->buffer_pos++];
+    c = stream->buffer[stream->buffer_pos++];
 
-out:
-    error_set(error, NULL, "unable to decode byte 0x%x at position %d",
-              (unsigned char)c, stream->stream_pos);
+    stream->position++;
+    if(c == '\n') {
+        stream->line++;
+        stream->last_column = stream->column;
+        stream->column = 0;
+    }
+    else if(utf8_check_first(c)) {
+        /* track the Unicode character column, so increment only if
+           this is the first character of a UTF-8 sequence */
+        stream->column++;
+    }
 
-    stream->buffer[0] = EOF;
-    stream->buffer[1] = '\0';
-    stream->buffer_pos = 1;
+    return c;
 
-    return EOF;
+out:
+    stream->state = STREAM_STATE_ERROR;
+    error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c);
+    return STREAM_STATE_ERROR;
 }
 
-static void stream_unget(stream_t *stream, char c)
+static void stream_unget(stream_t *stream, int c)
 {
+    if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR)
+        return;
+
+    stream->position--;
+    if(c == '\n') {
+        stream->line--;
+        stream->column = stream->last_column;
+    }
+    else if(utf8_check_first(c))
+        stream->column--;
+
     assert(stream->buffer_pos > 0);
     stream->buffer_pos--;
     assert(stream->buffer[stream->buffer_pos] == c);
@@ -186,29 +229,32 @@ static int lex_get(lex_t *lex, json_error_t *error)
     return stream_get(&lex->stream, error);
 }
 
-static int lex_eof(lex_t *lex)
-{
-    return lex->stream.eof(lex->stream.data);
-}
-
-static void lex_save(lex_t *lex, char c)
+static void lex_save(lex_t *lex, int c)
 {
     strbuffer_append_byte(&lex->saved_text, c);
 }
 
 static int lex_get_save(lex_t *lex, json_error_t *error)
 {
-    char c = stream_get(&lex->stream, error);
-    lex_save(lex, c);
+    int c = stream_get(&lex->stream, error);
+    if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR)
+        lex_save(lex, c);
     return c;
 }
 
-static void lex_unget_unsave(lex_t *lex, char c)
+static void lex_unget(lex_t *lex, int c)
 {
-    char d;
     stream_unget(&lex->stream, c);
-    d = strbuffer_pop(&lex->saved_text);
-    assert(c == d);
+}
+
+static void lex_unget_unsave(lex_t *lex, int c)
+{
+    if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) {
+        char d;
+        stream_unget(&lex->stream, c);
+        d = strbuffer_pop(&lex->saved_text);
+        assert(c == d);
+    }
 }
 
 static void lex_save_cached(lex_t *lex)
@@ -217,25 +263,26 @@ static void lex_save_cached(lex_t *lex)
     {
         lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]);
         lex->stream.buffer_pos++;
+        lex->stream.position++;
     }
 }
 
 /* assumes that str points to 'u' plus at least 4 valid hex digits */
-static int decode_unicode_escape(const char *str)
+static int32_t decode_unicode_escape(const char *str)
 {
     int i;
-    int value = 0;
+    int32_t value = 0;
 
     assert(str[0] == 'u');
 
     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);
@@ -246,7 +293,7 @@ static int decode_unicode_escape(const char *str)
 
 static void lex_scan_string(lex_t *lex, json_error_t *error)
 {
-    char c;
+    int c;
     const char *p;
     char *t;
     int i;
@@ -257,14 +304,15 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
     c = lex_get_save(lex, error);
 
     while(c != '"') {
-        if(c == (char)EOF) {
-            lex_unget_unsave(lex, c);
-            if(lex_eof(lex))
-                error_set(error, lex, "premature end of input");
+        if(c == STREAM_STATE_ERROR)
+            goto out;
+
+        else if(c == STREAM_STATE_EOF) {
+            error_set(error, lex, "premature end of input");
             goto out;
         }
 
-        else if((unsigned char)c <= 0x1F) {
+        else if(0 <= c && c <= 0x1F) {
             /* control character */
             lex_unget_unsave(lex, c);
             if(c == '\n')
@@ -279,8 +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)) {
-                        lex_unget_unsave(lex, c);
+                    if(!l_isxdigit(c)) {
                         error_set(error, lex, "invalid escape");
                         goto out;
                     }
@@ -291,7 +338,6 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
                     c == 'f' || c == 'n' || c == 'r' || c == 't')
                 c = lex_get_save(lex, error);
             else {
-                lex_unget_unsave(lex, c);
                 error_set(error, lex, "invalid escape");
                 goto out;
             }
@@ -307,7 +353,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
          - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
            are converted to 4 bytes
     */
-    lex->value.string = malloc(lex->saved_text.length + 1);
+    lex->value.string = jsonp_malloc(lex->saved_text.length + 1);
     if(!lex->value.string) {
         /* this is not very nice, since TOKEN_INVALID is returned */
         goto out;
@@ -325,7 +371,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
             if(*p == 'u') {
                 char buffer[4];
                 int length;
-                int value;
+                int32_t value;
 
                 value = decode_unicode_escape(p);
                 p += 5;
@@ -333,7 +379,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
                 if(0xD800 <= value && value <= 0xDBFF) {
                     /* surrogate pair */
                     if(*p == '\\' && *(p + 1) == 'u') {
-                        int value2 = decode_unicode_escape(++p);
+                        int32_t value2 = decode_unicode_escape(++p);
                         p += 5;
 
                         if(0xDC00 <= value2 && value2 <= 0xDFFF) {
@@ -397,10 +443,20 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
     return;
 
 out:
-    free(lex->value.string);
+    jsonp_free(lex->value.string);
 }
 
-static int lex_scan_number(lex_t *lex, char c, json_error_t *error)
+#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
+
+static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 {
     const char *saved_text;
     char *end;
@@ -413,52 +469,55 @@ static int lex_scan_number(lex_t *lex, char 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 {
-      lex_unget_unsave(lex, c);
-      goto out;
+        lex_unget_unsave(lex, c);
+        goto out;
     }
 
     if(c != '.' && c != 'E' && c != 'e') {
-        long value;
+        json_int_t value;
 
         lex_unget_unsave(lex, c);
 
         saved_text = strbuffer_value(&lex->saved_text);
-        value = strtol(saved_text, &end, 10);
-        assert(end == saved_text + lex->saved_text.length);
 
-        if((value == LONG_MAX && errno == ERANGE) || value > INT_MAX) {
-            error_set(error, lex, "too big integer");
-            goto out;
-        }
-        else if((value == LONG_MIN && errno == ERANGE) || value < INT_MIN) {
-            error_set(error, lex, "too big negative integer");
+        errno = 0;
+        value = json_strtoint(saved_text, &end, 10);
+        if(errno == ERANGE) {
+            if(value < 0)
+                error_set(error, lex, "too big negative integer");
+            else
+                error_set(error, lex, "too big integer");
             goto out;
         }
 
+        assert(end == saved_text + lex->saved_text.length);
+
         lex->token = TOKEN_INTEGER;
-        lex->value.integer = (int)value;
+        lex->value.integer = value;
         return 0;
     }
 
     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);
     }
 
@@ -467,30 +526,19 @@ static int lex_scan_number(lex_t *lex, char 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(value == 0 && errno == ERANGE) {
-        error_set(error, lex, "real number underflow");
-        goto out;
-    }
-
-    /* Cannot test for +/-HUGE_VAL because the HUGE_VAL constant is
-       only defined in C99 mode. So let's trust in sole errno. */
-    else if(errno == ERANGE) {
+    if(jsonp_strtod(&lex->saved_text, &value)) {
         error_set(error, lex, "real number overflow");
         goto out;
     }
@@ -505,29 +553,26 @@ out:
 
 static int lex_scan(lex_t *lex, json_error_t *error)
 {
-    char c;
+    int c;
 
     strbuffer_clear(&lex->saved_text);
 
     if(lex->token == TOKEN_STRING) {
-        free(lex->value.string);
+        jsonp_free(lex->value.string);
         lex->value.string = NULL;
     }
 
     c = lex_get(lex, error);
     while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
-    {
-        if(c == '\n')
-            lex->line++;
-
         c = lex_get(lex, error);
+
+    if(c == STREAM_STATE_EOF) {
+        lex->token = TOKEN_EOF;
+        goto out;
     }
 
-    if(c == (char)EOF) {
-        if(lex_eof(lex))
-            lex->token = TOKEN_EOF;
-        else
-            lex->token = TOKEN_INVALID;
+    if(c == STREAM_STATE_ERROR) {
+        lex->token = TOKEN_INVALID;
         goto out;
     }
 
@@ -539,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);
 
@@ -587,31 +632,29 @@ static char *lex_steal_string(lex_t *lex)
     return result;
 }
 
-static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
+static int lex_init(lex_t *lex, get_func get, void *data)
 {
-    stream_init(&lex->stream, get, eof, data);
+    stream_init(&lex->stream, get, data);
     if(strbuffer_init(&lex->saved_text))
         return -1;
 
     lex->token = TOKEN_INVALID;
-    lex->line = 1;
-
     return 0;
 }
 
 static void lex_close(lex_t *lex)
 {
     if(lex->token == TOKEN_STRING)
-        free(lex->value.string);
+        jsonp_free(lex->value.string);
     strbuffer_close(&lex->saved_text);
 }
 
 
 /*** 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)
@@ -634,28 +677,36 @@ 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 != ':') {
-            free(key);
+            jsonp_free(key);
             error_set(error, lex, "':' expected");
             goto error;
         }
 
         lex_scan(lex, error);
-        value = parse_value(lex, error);
+        value = parse_value(lex, flags, error);
         if(!value) {
-            free(key);
+            jsonp_free(key);
             goto error;
         }
 
         if(json_object_set_nocheck(object, key, value)) {
-            free(key);
+            jsonp_free(key);
             json_decref(value);
             goto error;
         }
 
         json_decref(value);
-        free(key);
+        jsonp_free(key);
 
         lex_scan(lex, error);
         if(lex->token != ',')
@@ -676,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)
@@ -687,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;
 
@@ -716,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;
 
@@ -749,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:
@@ -771,17 +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)
 {
-    error_init(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;
+        }
+    }
+
+    if(error) {
+        /* Save the position even though there was no error */
+        error->position = lex->stream.position;
     }
 
-    return parse_value(lex, error);
+    return result;
 }
 
 typedef struct
@@ -800,77 +871,120 @@ static int string_get(void *data)
     else
     {
         stream->pos++;
-        return c;
+        return (unsigned char)c;
     }
 }
 
-static int string_eof(void *data)
+json_t *json_loads(const char *string, size_t flags, json_error_t *error)
 {
-    string_data_t *stream = (string_data_t *)data;
-    return (stream->data[stream->pos] == '\0');
+    lex_t lex;
+    json_t *result;
+    string_data_t stream_data;
+
+    jsonp_error_init(error, "<string>");
+
+    if (string == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
+    stream_data.data = string;
+    stream_data.pos = 0;
+
+    if(lex_init(&lex, string_get, (void *)&stream_data))
+        return NULL;
+
+    result = parse_json(&lex, flags, error);
+
+    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_loads(const char *string, json_error_t *error)
+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;
 
-    string_data_t stream_data = {
-        .data = string,
-        .pos = 0
-    };
+    jsonp_error_init(error, "<buffer>");
 
-    if(lex_init(&lex, string_get, string_eof, (void *)&stream_data))
+    if (buffer == NULL) {
+        error_set(error, NULL, "wrong arguments");
         return NULL;
+    }
 
-    result = parse_json(&lex, error);
-    if(!result)
-        goto out;
+    stream_data.data = buffer;
+    stream_data.pos = 0;
+    stream_data.len = buflen;
 
-    lex_scan(&lex, error);
-    if(lex.token != TOKEN_EOF) {
-        error_set(error, &lex, "end of file expected");
-        json_decref(result);
-        result = NULL;
-    }
+    if(lex_init(&lex, buffer_get, (void *)&stream_data))
+        return NULL;
+
+    result = parse_json(&lex, flags, error);
 
-out:
     lex_close(&lex);
     return result;
 }
 
-json_t *json_loadf(FILE *input, json_error_t *error)
+json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
 {
     lex_t lex;
+    const char *source;
     json_t *result;
 
-    if(lex_init(&lex, (get_func)fgetc, (eof_func)feof, input))
-        return NULL;
+    if(input == stdin)
+        source = "<stdin>";
+    else
+        source = "<stream>";
 
-    result = parse_json(&lex, error);
-    if(!result)
-        goto out;
+    jsonp_error_init(error, source);
 
-    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;
 }
 
-json_t *json_load_file(const char *path, json_error_t *error)
+json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
 {
     json_t *result;
     FILE *fp;
 
-    error_init(error);
+    jsonp_error_init(error, path);
+
+    if (path == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
 
-    fp = fopen(path, "r");
+    fp = fopen(path, "rb");
     if(!fp)
     {
         error_set(error, NULL, "unable to open %s: %s",
@@ -878,8 +992,63 @@ json_t *json_load_file(const char *path, json_error_t *error)
         return NULL;
     }
 
-    result = json_loadf(fp, error);
+    result = json_loadf(fp, flags, 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;
+}
diff --git a/src/memory.c b/src/memory.c
new file mode 100644 (file)
index 0000000..543ecc4
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "jansson.h"
+#include "jansson_private.h"
+
+/* memory function pointers */
+static json_malloc_t do_malloc = malloc;
+static json_free_t do_free = free;
+
+void *jsonp_malloc(size_t size)
+{
+    if(!size)
+        return NULL;
+
+    return (*do_malloc)(size);
+}
+
+void jsonp_free(void *ptr)
+{
+    if(!ptr)
+        return;
+
+    (*do_free)(ptr);
+}
+
+char *jsonp_strdup(const char *str)
+{
+    char *new_str;
+
+    new_str = jsonp_malloc(strlen(str) + 1);
+    if(!new_str)
+        return NULL;
+
+    strcpy(new_str, str);
+    return new_str;
+}
+
+void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
+{
+    do_malloc = malloc_fn;
+    do_free = free_fn;
+}
diff --git a/src/pack_unpack.c b/src/pack_unpack.c
new file mode 100644 (file)
index 0000000..39db9b8
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * 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_private.h"
+#include "utf.h"
+
+typedef struct {
+    const char *start;
+    const char *fmt;
+    char token;
+    json_error_t *error;
+    size_t flags;
+    int line;
+    int column;
+} scanner_t;
+
+static const char *type_names[] = {
+    "object",
+    "array",
+    "string",
+    "integer",
+    "real",
+    "true",
+    "false",
+    "null"
+};
+
+#define type_name(x) type_names[json_typeof(x)]
+
+static const char *unpack_value_starters = "{[siIbfFOon";
+
+
+static void scanner_init(scanner_t *s, json_error_t *error,
+                         size_t flags, const char *fmt)
+{
+    s->error = error;
+    s->flags = flags;
+    s->fmt = s->start = fmt;
+    s->line = 1;
+    s->column = 0;
+}
+
+static void next_token(scanner_t *s)
+{
+    const char *t = s->fmt;
+    s->column++;
+
+    /* skip space and ignored chars */
+    while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
+        if(*t == '\n') {
+            s->line++;
+            s->column = 1;
+        }
+        else
+            s->column++;
+
+        t++;
+    }
+
+    s->token = *t;
+
+    t++;
+    s->fmt = t;
+}
+
+static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
+{
+    va_list ap;
+    size_t pos;
+    va_start(ap, fmt);
+
+    pos = (size_t)(s->fmt - s->start);
+    jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap);
+
+    jsonp_error_set_source(s->error, source);
+
+    va_end(ap);
+}
+
+static json_t *pack(scanner_t *s, va_list *ap);
+
+static json_t *pack_object(scanner_t *s, va_list *ap)
+{
+    json_t *object = json_object();
+    next_token(s);
+
+    while(s->token != '}') {
+        const char *key;
+        json_t *value;
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            goto error;
+        }
+
+        if(s->token != 's') {
+            set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
+            goto error;
+        }
+
+        key = va_arg(*ap, const char *);
+        if(!key) {
+            set_error(s, "<args>", "NULL object key");
+            goto error;
+        }
+
+        if(!utf8_check_string(key, -1)) {
+            set_error(s, "<args>", "Invalid UTF-8 in object key");
+            goto error;
+        }
+
+        next_token(s);
+
+        value = pack(s, ap);
+        if(!value)
+            goto error;
+
+        if(json_object_set_new_nocheck(object, key, value)) {
+            set_error(s, "<internal>", "Unable to add key \"%s\"", key);
+            goto error;
+        }
+
+        next_token(s);
+    }
+
+    return object;
+
+error:
+    json_decref(object);
+    return NULL;
+}
+
+static json_t *pack_array(scanner_t *s, va_list *ap)
+{
+    json_t *array = json_array();
+    next_token(s);
+
+    while(s->token != ']') {
+        json_t *value;
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            goto error;
+        }
+
+        value = pack(s, ap);
+        if(!value)
+            goto error;
+
+        if(json_array_append_new(array, value)) {
+            set_error(s, "<internal>", "Unable to append to array");
+            goto error;
+        }
+
+        next_token(s);
+    }
+    return array;
+
+error:
+    json_decref(array);
+    return NULL;
+}
+
+static json_t *pack(scanner_t *s, va_list *ap)
+{
+    switch(s->token) {
+        case '{':
+            return pack_object(s, ap);
+
+        case '[':
+            return pack_array(s, ap);
+
+        case 's': /* string */
+        {
+            const char *str = va_arg(*ap, const char *);
+            if(!str) {
+                set_error(s, "<args>", "NULL string argument");
+                return NULL;
+            }
+            if(!utf8_check_string(str, -1)) {
+                set_error(s, "<args>", "Invalid UTF-8 string");
+                return NULL;
+            }
+            return json_string_nocheck(str);
+        }
+
+        case 'n': /* null */
+            return json_null();
+
+        case 'b': /* boolean */
+            return va_arg(*ap, int) ? json_true() : json_false();
+
+        case 'i': /* integer from int */
+            return json_integer(va_arg(*ap, int));
+
+        case 'I': /* integer from json_int_t */
+            return json_integer(va_arg(*ap, json_int_t));
+
+        case 'f': /* real */
+            return json_real(va_arg(*ap, double));
+
+        case 'O': /* a json_t object; increments refcount */
+            return json_incref(va_arg(*ap, json_t *));
+
+        case 'o': /* a json_t object; doesn't increment refcount */
+            return va_arg(*ap, json_t *);
+
+        default:
+            set_error(s, "<format>", "Unexpected format character '%c'",
+                      s->token);
+            return NULL;
+    }
+}
+
+static int unpack(scanner_t *s, json_t *root, va_list *ap);
+
+static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
+{
+    int ret = -1;
+    int strict = 0;
+
+    /* Use a set (emulated by a hashtable) to check that all object
+       keys are accessed. Checking that the correct number of keys
+       were accessed is not enough, as the same key can be unpacked
+       multiple times.
+    */
+    hashtable_t key_set;
+
+    if(hashtable_init(&key_set)) {
+        set_error(s, "<internal>", "Out of memory");
+        return -1;
+    }
+
+    if(root && !json_is_object(root)) {
+        set_error(s, "<validation>", "Expected object, got %s",
+                  type_name(root));
+        goto out;
+    }
+    next_token(s);
+
+    while(s->token != '}') {
+        const char *key;
+        json_t *value;
+        int opt = 0;
+
+        if(strict != 0) {
+            set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
+                      (strict == 1 ? '!' : '*'), s->token);
+            goto out;
+        }
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            goto out;
+        }
+
+        if(s->token == '!' || s->token == '*') {
+            strict = (s->token == '!' ? 1 : -1);
+            next_token(s);
+            continue;
+        }
+
+        if(s->token != 's') {
+            set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
+            goto out;
+        }
+
+        key = va_arg(*ap, const char *);
+        if(!key) {
+            set_error(s, "<args>", "NULL object key");
+            goto out;
+        }
+
+        next_token(s);
+
+        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, key, 0, json_null());
+        next_token(s);
+    }
+
+    if(strict == 0 && (s->flags & JSON_STRICT))
+        strict = 1;
+
+    if(root && strict == 1 && key_set.size != json_object_size(root)) {
+        long diff = (long)json_object_size(root) - (long)key_set.size;
+        set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    hashtable_close(&key_set);
+    return ret;
+}
+
+static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
+{
+    size_t i = 0;
+    int strict = 0;
+
+    if(root && !json_is_array(root)) {
+        set_error(s, "<validation>", "Expected array, got %s", type_name(root));
+        return -1;
+    }
+    next_token(s);
+
+    while(s->token != ']') {
+        json_t *value;
+
+        if(strict != 0) {
+            set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
+                      (strict == 1 ? '!' : '*'),
+                      s->token);
+            return -1;
+        }
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            return -1;
+        }
+
+        if(s->token == '!' || s->token == '*') {
+            strict = (s->token == '!' ? 1 : -1);
+            next_token(s);
+            continue;
+        }
+
+        if(!strchr(unpack_value_starters, s->token)) {
+            set_error(s, "<format>", "Unexpected format character '%c'",
+                      s->token);
+            return -1;
+        }
+
+        if(!root) {
+            /* skipping */
+            value = NULL;
+        }
+        else {
+            value = json_array_get(root, i);
+            if(!value) {
+                set_error(s, "<validation>", "Array index %lu out of range",
+                          (unsigned long)i);
+                return -1;
+            }
+        }
+
+        if(unpack(s, value, ap))
+            return -1;
+
+        next_token(s);
+        i++;
+    }
+
+    if(strict == 0 && (s->flags & JSON_STRICT))
+        strict = 1;
+
+    if(root && strict == 1 && i != json_array_size(root)) {
+        long diff = (long)json_array_size(root) - (long)i;
+        set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int unpack(scanner_t *s, json_t *root, va_list *ap)
+{
+    switch(s->token)
+    {
+        case '{':
+            return unpack_object(s, root, ap);
+
+        case '[':
+            return unpack_array(s, root, ap);
+
+        case 's':
+            if(root && !json_is_string(root)) {
+                set_error(s, "<validation>", "Expected string, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                const char **target;
+
+                target = va_arg(*ap, const char **);
+                if(!target) {
+                    set_error(s, "<args>", "NULL string argument");
+                    return -1;
+                }
+
+                if(root)
+                    *target = json_string_value(root);
+            }
+            return 0;
+
+        case 'i':
+            if(root && !json_is_integer(root)) {
+                set_error(s, "<validation>", "Expected integer, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                int *target = va_arg(*ap, int*);
+                if(root)
+                    *target = (int)json_integer_value(root);
+            }
+
+            return 0;
+
+        case 'I':
+            if(root && !json_is_integer(root)) {
+                set_error(s, "<validation>", "Expected integer, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                json_int_t *target = va_arg(*ap, json_int_t*);
+                if(root)
+                    *target = json_integer_value(root);
+            }
+
+            return 0;
+
+        case 'b':
+            if(root && !json_is_boolean(root)) {
+                set_error(s, "<validation>", "Expected true or false, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                int *target = va_arg(*ap, int*);
+                if(root)
+                    *target = json_is_true(root);
+            }
+
+            return 0;
+
+        case 'f':
+            if(root && !json_is_real(root)) {
+                set_error(s, "<validation>", "Expected real, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                double *target = va_arg(*ap, double*);
+                if(root)
+                    *target = json_real_value(root);
+            }
+
+            return 0;
+
+        case 'F':
+            if(root && !json_is_number(root)) {
+                set_error(s, "<validation>", "Expected real or integer, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                double *target = va_arg(*ap, double*);
+                if(root)
+                    *target = json_number_value(root);
+            }
+
+            return 0;
+
+        case 'O':
+            if(root && !(s->flags & JSON_VALIDATE_ONLY))
+                json_incref(root);
+            /* Fall through */
+
+        case 'o':
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                json_t **target = va_arg(*ap, json_t**);
+                if(root)
+                    *target = root;
+            }
+
+            return 0;
+
+        case 'n':
+            /* Never assign, just validate */
+            if(root && !json_is_null(root)) {
+                set_error(s, "<validation>", "Expected null, got %s",
+                          type_name(root));
+                return -1;
+            }
+            return 0;
+
+        default:
+            set_error(s, "<format>", "Unexpected format character '%c'",
+                      s->token);
+            return -1;
+    }
+}
+
+json_t *json_vpack_ex(json_error_t *error, size_t flags,
+                      const char *fmt, va_list ap)
+{
+    scanner_t s;
+    va_list ap_copy;
+    json_t *value;
+
+    if(!fmt || !*fmt) {
+        jsonp_error_init(error, "<format>");
+        jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
+        return NULL;
+    }
+    jsonp_error_init(error, NULL);
+
+    scanner_init(&s, error, flags, fmt);
+    next_token(&s);
+
+    va_copy(ap_copy, ap);
+    value = pack(&s, &ap_copy);
+    va_end(ap_copy);
+
+    if(!value)
+        return NULL;
+
+    next_token(&s);
+    if(s.token) {
+        json_decref(value);
+        set_error(&s, "<format>", "Garbage after format string");
+        return NULL;
+    }
+
+    return value;
+}
+
+json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
+{
+    json_t *value;
+    va_list ap;
+
+    va_start(ap, fmt);
+    value = json_vpack_ex(error, flags, fmt, ap);
+    va_end(ap);
+
+    return value;
+}
+
+json_t *json_pack(const char *fmt, ...)
+{
+    json_t *value;
+    va_list ap;
+
+    va_start(ap, fmt);
+    value = json_vpack_ex(NULL, 0, fmt, ap);
+    va_end(ap);
+
+    return value;
+}
+
+int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
+                    const char *fmt, va_list ap)
+{
+    scanner_t s;
+    va_list ap_copy;
+
+    if(!root) {
+        jsonp_error_init(error, "<root>");
+        jsonp_error_set(error, -1, -1, 0, "NULL root value");
+        return -1;
+    }
+
+    if(!fmt || !*fmt) {
+        jsonp_error_init(error, "<format>");
+        jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
+        return -1;
+    }
+    jsonp_error_init(error, NULL);
+
+    scanner_init(&s, error, flags, fmt);
+    next_token(&s);
+
+    va_copy(ap_copy, ap);
+    if(unpack(&s, root, &ap_copy)) {
+        va_end(ap_copy);
+        return -1;
+    }
+    va_end(ap_copy);
+
+    next_token(&s);
+    if(s.token) {
+        set_error(&s, "<format>", "Garbage after format string");
+        return -1;
+    }
+
+    return 0;
+}
+
+int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
+{
+    int ret;
+    va_list ap;
+
+    va_start(ap, fmt);
+    ret = json_vunpack_ex(root, error, flags, fmt, ap);
+    va_end(ap);
+
+    return ret;
+}
+
+int json_unpack(json_t *root, const char *fmt, ...)
+{
+    int ret;
+    va_list ap;
+
+    va_start(ap, fmt);
+    ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
+    va_end(ap);
+
+    return ret;
+}
index 1a71a53..6d4edd6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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,18 +8,19 @@
 #define _GNU_SOURCE
 #include <stdlib.h>
 #include <string.h>
+#include "jansson_private.h"
 #include "strbuffer.h"
-#include "util.h"
 
 #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,16 +65,31 @@ 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)
     {
-        strbuff->size = max(strbuff->size * STRBUFFER_FACTOR,
-                            strbuff->length + size + 1);
+        size_t new_size;
+        char *new_value;
 
-        strbuff->value = realloc(strbuff->value, strbuff->size);
-        if(!strbuff->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);
+
+        new_value = jsonp_malloc(new_size);
+        if(!new_value)
+            return -1;
+
+        memcpy(new_value, strbuff->value, strbuff->length);
+
+        jsonp_free(strbuff->value);
+        strbuff->value = new_value;
+        strbuff->size = new_size;
     }
 
     memcpy(strbuff->value + strbuff->length, data, size);
index 4afefa9..23f8fff 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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 cf2e8e4..0359ee2 100644 (file)
--- a/src/utf.c
+++ b/src/utf.c
@@ -1,13 +1,14 @@
 /*
- * Copyright (c) 2009 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 "utf.h"
 
-int utf8_encode(int codepoint, char *buffer, int *size)
+int utf8_encode(int32_t codepoint, char *buffer, int *size)
 {
     if(codepoint < 0)
         return -1;
@@ -79,9 +80,10 @@ int utf8_check_first(char byte)
     }
 }
 
-int utf8_check_full(const char *buffer, int size)
+int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
 {
-    int i, value = 0;
+    int i;
+    int32_t value = 0;
     unsigned char u = (unsigned char)buffer[0];
 
     if(size == 2)
@@ -128,9 +130,38 @@ int utf8_check_full(const char *buffer, int size)
         return 0;
     }
 
+    if(codepoint)
+        *codepoint = value;
+
     return 1;
 }
 
+const char *utf8_iterate(const char *buffer, int32_t *codepoint)
+{
+    int count;
+    int32_t value;
+
+    if(!*buffer)
+        return buffer;
+
+    count = utf8_check_first(buffer[0]);
+    if(count <= 0)
+        return NULL;
+
+    if(count == 1)
+        value = (unsigned char)buffer[0];
+    else
+    {
+        if(!utf8_check_full(buffer, count, &value))
+            return NULL;
+    }
+
+    if(codepoint)
+        *codepoint = value;
+
+    return buffer + count;
+}
+
 int utf8_check_string(const char *string, int length)
 {
     int i;
@@ -148,7 +179,7 @@ int utf8_check_string(const char *string, int length)
             if(i + count > length)
                 return 0;
 
-            if(!utf8_check_full(&string[i], count))
+            if(!utf8_check_full(&string[i], count, NULL))
                 return 0;
 
             i += count - 1;
index 75d7b6e..2495cdd 100644 (file)
--- a/src/utf.h
+++ b/src/utf.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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,10 +8,31 @@
 #ifndef UTF_H
 #define UTF_H
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+
+#ifdef HAVE_INTTYPES_H
+/* inttypes.h includes stdint.h in a standard environment, so there's
+no need to include stdint.h separately. If inttypes.h doesn't define
+int32_t, it's defined in config.h. */
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+
+#else /* !HAVE_CONFIG_H */
+#ifdef _WIN32
+typedef int int32_t;
+#else /* !_WIN32 */
+/* Assume a standard environment */
+#include <inttypes.h>
+#endif /* _WIN32 */
+
+#endif /* HAVE_CONFIG_H */
+
 int utf8_encode(int codepoint, char *buffer, int *size);
 
 int utf8_check_first(char byte);
-int utf8_check_full(const char *buffer, int size);
+int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
+const char *utf8_iterate(const char *buffer, int32_t *codepoint);
 
 int utf8_check_string(const char *string, int length);
 
diff --git a/src/util.h b/src/util.h
deleted file mode 100644 (file)
index 66066c5..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- */
-
-#ifndef UTIL_H
-#define UTIL_H
-
-#define max(a, b)  ((a) > (b) ? (a) : (b))
-
-#endif
index 6497f85..ba9908e 100644 (file)
@@ -1,22 +1,31 @@
 /*
- * Copyright (c) 2009 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 <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"
-#include "util.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 inline void json_init(json_t *json, json_type type)
+static JSON_INLINE void json_init(json_t *json, json_type type)
 {
     json->type = type;
     json->refcount = 1;
@@ -25,45 +34,20 @@ static inline void json_init(json_t *json, json_type type)
 
 /*** object ***/
 
-static unsigned int hash_string(const void *key)
-{
-    const char *str = (const char *)key;
-    unsigned int hash = 5381;
-    unsigned int c;
-
-    while((c = (unsigned int)*str))
-    {
-        hash = ((hash << 5) + hash) + c;
-        str++;
-    }
-
-    return hash;
-}
-
-static int string_equal(const void *key1, const void *key2)
-{
-    return strcmp((const char *)key1, (const char *)key2) == 0;
-}
-
-static void value_decref(void *value)
-{
-    json_decref((json_t *)value);
-}
-
 json_t *json_object(void)
 {
-    json_object_t *object = malloc(sizeof(json_object_t));
+    json_object_t *object = jsonp_malloc(sizeof(json_object_t));
     if(!object)
         return NULL;
     json_init(&object->json, JSON_OBJECT);
 
-    if(hashtable_init(&object->hashtable, hash_string, string_equal,
-                      free, value_decref))
+    if(hashtable_init(&object->hashtable))
     {
-        free(object);
+        jsonp_free(object);
         return NULL;
     }
 
+    object->serial = 0;
     object->visited = 0;
 
     return &object->json;
@@ -72,15 +56,15 @@ json_t *json_object(void)
 static void json_delete_object(json_object_t *object)
 {
     hashtable_close(&object->hashtable);
-    free(object);
+    jsonp_free(object);
 }
 
-unsigned int json_object_size(const json_t *json)
+size_t json_object_size(const json_t *json)
 {
     json_object_t *object;
 
     if(!json_is_object(json))
-        return -1;
+        return 0;
 
     object = json_to_object(json);
     return object->hashtable.size;
@@ -101,17 +85,17 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
 {
     json_object_t *object;
 
-    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);
 
-    if(hashtable_set(&object->hashtable, strdup(key), value))
+    if(hashtable_set(&object->hashtable, key, object->serial++, value))
     {
         json_decref(value);
         return -1;
@@ -120,11 +104,6 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
     return 0;
 }
 
-int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
-{
-    return json_object_set_new_nocheck(json, key, json_incref(value));
-}
-
 int json_object_set_new(json_t *json, const char *key, json_t *value)
 {
     if(!key || !utf8_check_string(key, -1))
@@ -155,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;
+
+    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(other);
-    while(iter) {
-        const char *key;
-        json_t *value;
+    json_object_foreach(other, key, value) {
+        if(json_object_get(object, key))
+            json_object_set_nocheck(object, key, value);
+    }
 
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
+    return 0;
+}
 
-        if(json_object_set(object, key, value))
-            return -1;
+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;
 
-        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;
@@ -195,6 +200,17 @@ void *json_object_iter(json_t *json)
     return hashtable_iter(&object->hashtable);
 }
 
+void *json_object_iter_at(json_t *json, const char *key)
+{
+    json_object_t *object;
+
+    if(!key || !json_is_object(json))
+        return NULL;
+
+    object = json_to_object(json);
+    return hashtable_iter_at(&object->hashtable, key);
+}
+
 void *json_object_iter_next(json_t *json, void *iter)
 {
     json_object_t *object;
@@ -211,7 +227,7 @@ const char *json_object_iter_key(void *iter)
     if(!iter)
         return NULL;
 
-    return (const char *)hashtable_iter_key(iter);
+    return hashtable_iter_key(iter);
 }
 
 json_t *json_object_iter_value(void *iter)
@@ -222,12 +238,81 @@ json_t *json_object_iter_value(void *iter)
     return (json_t *)hashtable_iter_value(iter);
 }
 
+int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
+{
+    if(!json_is_object(json) || !iter || !value)
+        return -1;
+
+    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)
+{
+    const char *key;
+    json_t *value1, *value2;
+
+    if(json_object_size(object1) != json_object_size(object2))
+        return 0;
+
+    json_object_foreach(object1, key, value1) {
+        value2 = json_object_get(object2, key);
+
+        if(!json_equal(value1, value2))
+            return 0;
+    }
+
+    return 1;
+}
+
+static json_t *json_object_copy(json_t *object)
+{
+    json_t *result;
+
+    const char *key;
+    json_t *value;
+
+    result = json_object();
+    if(!result)
+        return NULL;
+
+    json_object_foreach(object, key, value)
+        json_object_set_nocheck(result, key, value);
+
+    return result;
+}
+
+static json_t *json_object_deep_copy(json_t *object)
+{
+    json_t *result;
+
+    const char *key;
+    json_t *value;
+
+    result = json_object();
+    if(!result)
+        return NULL;
+
+    json_object_foreach(object, key, value)
+        json_object_set_new_nocheck(result, key, json_deep_copy(value));
+
+    return result;
+}
+
 
 /*** array ***/
 
 json_t *json_array(void)
 {
-    json_array_t *array = malloc(sizeof(json_array_t));
+    json_array_t *array = jsonp_malloc(sizeof(json_array_t));
     if(!array)
         return NULL;
     json_init(&array->json, JSON_ARRAY);
@@ -235,9 +320,9 @@ json_t *json_array(void)
     array->entries = 0;
     array->size = 8;
 
-    array->table = malloc(array->size * sizeof(json_t *));
+    array->table = jsonp_malloc(array->size * sizeof(json_t *));
     if(!array->table) {
-        free(array);
+        jsonp_free(array);
         return NULL;
     }
 
@@ -248,16 +333,16 @@ json_t *json_array(void)
 
 static void json_delete_array(json_array_t *array)
 {
-    unsigned int i;
+    size_t i;
 
     for(i = 0; i < array->entries; i++)
         json_decref(array->table[i]);
 
-    free(array->table);
-    free(array);
+    jsonp_free(array->table);
+    jsonp_free(array);
 }
 
-unsigned int json_array_size(const json_t *json)
+size_t json_array_size(const json_t *json)
 {
     if(!json_is_array(json))
         return 0;
@@ -265,7 +350,7 @@ unsigned int json_array_size(const json_t *json)
     return json_to_array(json)->entries;
 }
 
-json_t *json_array_get(const json_t *json, unsigned int index)
+json_t *json_array_get(const json_t *json, size_t index)
 {
     json_array_t *array;
     if(!json_is_array(json))
@@ -278,7 +363,7 @@ json_t *json_array_get(const json_t *json, unsigned int index)
     return array->table[index];
 }
 
-int json_array_set_new(json_t *json, unsigned int index, json_t *value)
+int json_array_set_new(json_t *json, size_t index, json_t *value)
 {
     json_array_t *array;
 
@@ -304,24 +389,24 @@ int json_array_set_new(json_t *json, unsigned int index, json_t *value)
     return 0;
 }
 
-static void array_move(json_array_t *array, unsigned int dest,
-                       unsigned int src, unsigned int count)
+static void array_move(json_array_t *array, size_t dest,
+                       size_t src, size_t count)
 {
     memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *));
 }
 
-static void array_copy(json_t **dest, unsigned int dpos,
-                       json_t **src, unsigned int spos,
-                       unsigned int count)
+static void array_copy(json_t **dest, size_t dpos,
+                       json_t **src, size_t spos,
+                       size_t count)
 {
     memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *));
 }
 
 static json_t **json_array_grow(json_array_t *array,
-                                unsigned int amount,
+                                size_t amount,
                                 int copy)
 {
-    unsigned int new_size;
+    size_t new_size;
     json_t **old_table, **new_table;
 
     if(array->entries + amount <= array->size)
@@ -330,7 +415,7 @@ static json_t **json_array_grow(json_array_t *array,
     old_table = array->table;
 
     new_size = max(array->size + amount, array->size * 2);
-    new_table = malloc(new_size * sizeof(json_t *));
+    new_table = jsonp_malloc(new_size * sizeof(json_t *));
     if(!new_table)
         return NULL;
 
@@ -339,7 +424,7 @@ static json_t **json_array_grow(json_array_t *array,
 
     if(copy) {
         array_copy(array->table, 0, old_table, 0, array->entries);
-        free(old_table);
+        jsonp_free(old_table);
         return array->table;
     }
 
@@ -371,7 +456,7 @@ int json_array_append_new(json_t *json, json_t *value)
     return 0;
 }
 
-int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
+int json_array_insert_new(json_t *json, size_t index, json_t *value)
 {
     json_array_t *array;
     json_t **old_table;
@@ -400,7 +485,7 @@ int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
         array_copy(array->table, 0, old_table, 0, index);
         array_copy(array->table, index + 1, old_table, index,
                    array->entries - index);
-        free(old_table);
+        jsonp_free(old_table);
     }
     else
         array_move(array, index + 1, index, array->entries - index);
@@ -411,7 +496,7 @@ int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
     return 0;
 }
 
-int json_array_remove(json_t *json, unsigned int index)
+int json_array_remove(json_t *json, size_t index)
 {
     json_array_t *array;
 
@@ -433,7 +518,7 @@ int json_array_remove(json_t *json, unsigned int index)
 int json_array_clear(json_t *json)
 {
     json_array_t *array;
-    unsigned int i;
+    size_t i;
 
     if(!json_is_array(json))
         return -1;
@@ -449,7 +534,7 @@ int json_array_clear(json_t *json)
 int json_array_extend(json_t *json, json_t *other_json)
 {
     json_array_t *array, *other;
-    unsigned int i;
+    size_t i;
 
     if(!json_is_array(json) || !json_is_array(other_json))
         return -1;
@@ -468,6 +553,57 @@ int json_array_extend(json_t *json, json_t *other_json)
     return 0;
 }
 
+static int json_array_equal(json_t *array1, json_t *array2)
+{
+    size_t i, size;
+
+    size = json_array_size(array1);
+    if(size != json_array_size(array2))
+        return 0;
+
+    for(i = 0; i < size; i++)
+    {
+        json_t *value1, *value2;
+
+        value1 = json_array_get(array1, i);
+        value2 = json_array_get(array2, i);
+
+        if(!json_equal(value1, value2))
+            return 0;
+    }
+
+    return 1;
+}
+
+static json_t *json_array_copy(json_t *array)
+{
+    json_t *result;
+    size_t i;
+
+    result = json_array();
+    if(!result)
+        return NULL;
+
+    for(i = 0; i < json_array_size(array); i++)
+        json_array_append(result, json_array_get(array, i));
+
+    return result;
+}
+
+static json_t *json_array_deep_copy(json_t *array)
+{
+    json_t *result;
+    size_t i;
+
+    result = json_array();
+    if(!result)
+        return NULL;
+
+    for(i = 0; i < json_array_size(array); i++)
+        json_array_append_new(result, json_deep_copy(json_array_get(array, i)));
+
+    return result;
+}
 
 /*** string ***/
 
@@ -478,14 +614,14 @@ json_t *json_string_nocheck(const char *value)
     if(!value)
         return NULL;
 
-    string = malloc(sizeof(json_string_t));
+    string = jsonp_malloc(sizeof(json_string_t));
     if(!string)
         return NULL;
     json_init(&string->json, JSON_STRING);
 
-    string->value = strdup(value);
+    string->value = jsonp_strdup(value);
     if(!string->value) {
-        free(string);
+        jsonp_free(string);
         return NULL;
     }
 
@@ -508,37 +644,55 @@ const char *json_string_value(const json_t *json)
     return json_to_string(json)->value;
 }
 
-int json_string_set(const json_t *json, const char *value)
+int json_string_set_nocheck(json_t *json, const char *value)
 {
     char *dup;
     json_string_t *string;
 
-    if(!json_is_string(json) || !value || !utf8_check_string(value, -1))
+    if(!json_is_string(json) || !value)
         return -1;
 
-    dup = strdup(value);
+    dup = jsonp_strdup(value);
     if(!dup)
         return -1;
 
     string = json_to_string(json);
-    free(string->value);
+    jsonp_free(string->value);
     string->value = dup;
 
     return 0;
 }
 
+int json_string_set(json_t *json, const char *value)
+{
+    if(!value || !utf8_check_string(value, -1))
+        return -1;
+
+    return json_string_set_nocheck(json, value);
+}
+
 static void json_delete_string(json_string_t *string)
 {
-    free(string->value);
-    free(string);
+    jsonp_free(string->value);
+    jsonp_free(string);
+}
+
+static int json_string_equal(json_t *string1, json_t *string2)
+{
+    return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
+}
+
+static json_t *json_string_copy(json_t *string)
+{
+    return json_string_nocheck(json_string_value(string));
 }
 
 
 /*** integer ***/
 
-json_t *json_integer(int value)
+json_t *json_integer(json_int_t value)
 {
-    json_integer_t *integer = malloc(sizeof(json_integer_t));
+    json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t));
     if(!integer)
         return NULL;
     json_init(&integer->json, JSON_INTEGER);
@@ -547,7 +701,7 @@ json_t *json_integer(int value)
     return &integer->json;
 }
 
-int json_integer_value(const json_t *json)
+json_int_t json_integer_value(const json_t *json)
 {
     if(!json_is_integer(json))
         return 0;
@@ -555,7 +709,7 @@ int json_integer_value(const json_t *json)
     return json_to_integer(json)->value;
 }
 
-int json_integer_set(const json_t *json, int value)
+int json_integer_set(json_t *json, json_int_t value)
 {
     if(!json_is_integer(json))
         return -1;
@@ -567,7 +721,17 @@ int json_integer_set(const json_t *json, int value)
 
 static void json_delete_integer(json_integer_t *integer)
 {
-    free(integer);
+    jsonp_free(integer);
+}
+
+static int json_integer_equal(json_t *integer1, json_t *integer2)
+{
+    return json_integer_value(integer1) == json_integer_value(integer2);
+}
+
+static json_t *json_integer_copy(json_t *integer)
+{
+    return json_integer(json_integer_value(integer));
 }
 
 
@@ -575,7 +739,12 @@ static void json_delete_integer(json_integer_t *integer)
 
 json_t *json_real(double value)
 {
-    json_real_t *real = 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);
@@ -592,10 +761,10 @@ double json_real_value(const json_t *json)
     return json_to_real(json)->value;
 }
 
-int json_real_set(const json_t *json, double value)
+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;
 
@@ -604,7 +773,17 @@ int json_real_set(const json_t *json, double value)
 
 static void json_delete_real(json_real_t *real)
 {
-    free(real);
+    jsonp_free(real);
+}
+
+static int json_real_equal(json_t *real1, json_t *real2)
+{
+    return json_real_value(real1) == json_real_value(real2);
+}
+
+static json_t *json_real_copy(json_t *real)
+{
+    return json_real(json_real_value(real));
 }
 
 
@@ -613,7 +792,7 @@ static void json_delete_real(json_real_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
@@ -625,30 +804,21 @@ double json_number_value(const json_t *json)
 
 json_t *json_true(void)
 {
-    static json_t the_true = {
-        .type = JSON_TRUE,
-        .refcount = (unsigned int)1
-    };
+    static json_t the_true = {JSON_TRUE, (size_t)-1};
     return &the_true;
 }
 
 
 json_t *json_false(void)
 {
-    static json_t the_false = {
-        .type = JSON_FALSE,
-        .refcount = (unsigned int)1
-    };
+    static json_t the_false = {JSON_FALSE, (size_t)-1};
     return &the_false;
 }
 
 
 json_t *json_null(void)
 {
-    static json_t the_null = {
-        .type = JSON_NULL,
-        .refcount = (unsigned int)1
-    };
+    static json_t the_null = {JSON_NULL, (size_t)-1};
     return &the_null;
 }
 
@@ -674,3 +844,94 @@ void json_delete(json_t *json)
 
     /* json_delete is not called for true, false or null */
 }
+
+
+/*** equality ***/
+
+int json_equal(json_t *json1, json_t *json2)
+{
+    if(!json1 || !json2)
+        return 0;
+
+    if(json_typeof(json1) != json_typeof(json2))
+        return 0;
+
+    /* this covers true, false and null as they are singletons */
+    if(json1 == json2)
+        return 1;
+
+    if(json_is_object(json1))
+        return json_object_equal(json1, json2);
+
+    if(json_is_array(json1))
+        return json_array_equal(json1, json2);
+
+    if(json_is_string(json1))
+        return json_string_equal(json1, json2);
+
+    if(json_is_integer(json1))
+        return json_integer_equal(json1, json2);
+
+    if(json_is_real(json1))
+        return json_real_equal(json1, json2);
+
+    return 0;
+}
+
+
+/*** copying ***/
+
+json_t *json_copy(json_t *json)
+{
+    if(!json)
+        return NULL;
+
+    if(json_is_object(json))
+        return json_object_copy(json);
+
+    if(json_is_array(json))
+        return json_array_copy(json);
+
+    if(json_is_string(json))
+        return json_string_copy(json);
+
+    if(json_is_integer(json))
+        return json_integer_copy(json);
+
+    if(json_is_real(json))
+        return json_real_copy(json);
+
+    if(json_is_true(json) || json_is_false(json) || json_is_null(json))
+        return json;
+
+    return NULL;
+}
+
+json_t *json_deep_copy(json_t *json)
+{
+    if(!json)
+        return NULL;
+
+    if(json_is_object(json))
+        return json_object_deep_copy(json);
+
+    if(json_is_array(json))
+        return json_array_deep_copy(json);
+
+    /* for the rest of the types, deep copying doesn't differ from
+       shallow copying */
+
+    if(json_is_string(json))
+        return json_string_copy(json);
+
+    if(json_is_integer(json))
+        return json_integer_copy(json);
+
+    if(json_is_real(json))
+        return json_real_copy(json);
+
+    if(json_is_true(json) || json_is_false(json) || json_is_null(json))
+        return json;
+
+    return NULL;
+}
index ac70552..ae6c8f9 100644 (file)
@@ -1,9 +1,17 @@
-loadf_dumpf
-loads_dumps
-load_file_dump_file
-testlogs
-testprogs/test_array
-testprogs/test_load
-testprogs/test_number
-testprogs/test_object
-testprogs/test_simple
+logs
+bin/json_process
+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 ed11110..86d1614 100644 (file)
@@ -1,22 +1,10 @@
-DIST_SUBDIRS = testprogs testdata
-SUBDIRS = testprogs
+SUBDIRS = bin suites
+EXTRA_DIST = scripts run-suites
 
-check_PROGRAMS = loadf_dumpf loads_dumps load_file_dump_file
-
-AM_CPPFLAGS = -I$(top_srcdir)/src
-AM_CFLAGS = -Wall -Werror
-LDFLAGS = -static  # for speed and Valgrind
-LDADD = ../src/libjansson.la
-
-TESTS = test-api test-invalid test-valid
-
-EXTRA_DIST = \
-       test-api \
-       test-invalid \
-       test-valid \
-       run-test \
-       json-compare.py \
-       split-testfile.py
+TESTS = run-suites
+TESTS_ENVIRONMENT = \
+       top_srcdir=$(top_srcdir) \
+       top_builddir=$(top_builddir)
 
 clean-local:
-       rm -rf testlogs
+       rm -rf logs
diff --git a/test/bin/Makefile.am b/test/bin/Makefile.am
new file mode 100644 (file)
index 0000000..346db5d
--- /dev/null
@@ -0,0 +1,6 @@
+check_PROGRAMS = json_process
+
+AM_CPPFLAGS = -I$(top_srcdir)/src
+AM_CFLAGS = -Wall -Werror
+LDFLAGS = -static  # for speed and Valgrind
+LDADD = $(top_builddir)/src/libjansson.la
diff --git a/test/bin/json_process.c b/test/bin/json_process.c
new file mode 100644 (file)
index 0000000..40183c7
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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;
+    long result;
+
+    value = getenv(name);
+    if(!value)
+        return 0;
+
+    result = strtol(value, &end, 10);
+    if(*end != '\0')
+        return 0;
+
+    return (int)result;
+}
+
+/* Return a pointer to the first non-whitespace character of str.
+   Modifies str so that all trailing whitespace characters are
+   replaced by '\0'. */
+static const char *strip(char *str)
+{
+    size_t length;
+    char *result = str;
+    while(*result && l_isspace(*result))
+        result++;
+
+    length = strlen(result);
+    if(length == 0)
+        return result;
+
+    while(l_isspace(result[length - 1]))
+        result[--length] = '\0';
+
+    return result;
+}
+
+int main(int argc, char *argv[])
+{
+    int indent = 0;
+    size_t flags = 0;
+
+    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);
+        return 2;
+    }
+
+    if(indent > 0)
+        flags |= JSON_INDENT(indent);
+
+    if(getenv_int("JSON_COMPACT") > 0)
+        flags |= JSON_COMPACT;
+
+    if(getenv_int("JSON_ENSURE_ASCII"))
+        flags |= JSON_ENSURE_ASCII;
+
+    if(getenv_int("JSON_PRESERVE_ORDER"))
+        flags |= JSON_PRESERVE_ORDER;
+
+    if(getenv_int("JSON_SORT_KEYS"))
+        flags |= JSON_SORT_KEYS;
+
+    if(getenv_int("STRIP")) {
+        /* Load to memory, strip leading and trailing whitespace */
+        size_t size = 0, used = 0;
+        char *buffer = NULL;
+
+        while(1) {
+            int count;
+
+            size = (size == 0 ? 128 : size * 2);
+            buffer = realloc(buffer, size);
+            if(!buffer) {
+                fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
+                return 1;
+            }
+
+            count = fread(buffer + used, 1, size - used, stdin);
+            if(count < size - used) {
+                buffer[used + count] = '\0';
+                break;
+            }
+            used += count;
+        }
+
+        json = json_loads(strip(buffer), 0, &error);
+        free(buffer);
+    }
+    else
+        json = json_loadf(stdin, 0, &error);
+
+    if(!json) {
+        fprintf(stderr, "%d %d %d\n%s\n",
+                error.line, error.column, error.position,
+                error.text);
+        return 1;
+    }
+
+    json_dumpf(json, stdout, flags);
+    json_decref(json);
+
+    return 0;
+}
diff --git a/test/json-compare.py b/test/json-compare.py
deleted file mode 100755 (executable)
index f91530e..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
-#
-# Jansson is free software; you can redistribute it and/or modify
-# it under the terms of the MIT license. See LICENSE for details.
-
-import sys
-try:
-    import json
-except ImportError:
-    import simplejson as json
-
-def load(filename):
-    try:
-        jsonfile = open(filename)
-    except IOError, err:
-        print >>sys.stderr, "unable to load %s: %s" % \
-            (filename, err.strerror)
-        sys.exit(1)
-
-    try:
-        jsondata = json.load(jsonfile)
-    except ValueError, err:
-        print "%s is malformed: %s" % (filename, err)
-        sys.exit(1)
-    finally:
-        jsonfile.close()
-
-    return jsondata
-
-def main():
-    if len(sys.argv) != 3:
-        print >>sys.stderr, "usage: %s json1 json2" % sys.argv[0]
-        return 2
-
-    json1 = load(sys.argv[1])
-    json2 = load(sys.argv[2])
-    if json1 == json2:
-        return 0
-    else:
-        return 1
-
-if __name__ == '__main__':
-    sys.exit(main() or 0)
diff --git a/test/load_file_dump_file.c b/test/load_file_dump_file.c
deleted file mode 100644 (file)
index ac55afe..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- */
-
-#include <stdio.h>
-#include <jansson.h>
-
-int main(int argc, char *argv[])
-{
-    json_t *json;
-    json_error_t error;
-
-    if(argc != 3) {
-        fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
-        return 2;
-    }
-
-    json = json_load_file(argv[1], &error);
-    if(!json) {
-        fprintf(stderr, "%d\n%s\n", error.line, error.text);
-        return 1;
-    }
-
-    json_dump_file(json, argv[2], 0);
-    json_decref(json);
-
-    return 0;
-}
diff --git a/test/loadf_dumpf.c b/test/loadf_dumpf.c
deleted file mode 100644 (file)
index 75b889f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- */
-
-#include <stdio.h>
-#include <jansson.h>
-
-int main(int argc, char *argv[])
-{
-    json_t *json;
-    json_error_t error;
-
-    if(argc != 1) {
-        fprintf(stderr, "usage: %s\n", argv[0]);
-        return 2;
-    }
-
-    json = json_loadf(stdin, &error);
-    if(!json) {
-        fprintf(stderr, "%d\n%s\n", error.line, error.text);
-        return 1;
-    }
-
-    /* loadf_dumpf indents, others don't, so dumping with and without
-       indenting is tested */
-    json_dumpf(json, stdout, JSON_INDENT(4));
-    json_decref(json);
-
-    return 0;
-}
diff --git a/test/loads_dumps.c b/test/loads_dumps.c
deleted file mode 100644 (file)
index fefe1ba..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <jansson.h>
-
-#define BUFFER_SIZE (256 * 1024)
-
-int main(int argc, char *argv[])
-{
-    json_t *json;
-    json_error_t error;
-    int count;
-    char buffer[BUFFER_SIZE];
-    char *result;
-
-    if(argc != 1) {
-        fprintf(stderr, "usage: %s\n", argv[0]);
-        return 2;
-    }
-
-    count = fread(buffer, 1, BUFFER_SIZE, stdin);
-    if(count < 0 || count >= BUFFER_SIZE) {
-        fprintf(stderr, "unable to read input\n");
-        return 1;
-    }
-    buffer[count] = '\0';
-
-    json = json_loads(buffer, &error);
-    if(!json) {
-        fprintf(stderr, "%d\n%s\n", error.line, error.text);
-        return 1;
-    }
-
-    result = json_dumps(json, 0);
-    json_decref(json);
-
-    puts(result);
-    free(result);
-
-    return 0;
-}
diff --git a/test/run-suites b/test/run-suites
new file mode 100755 (executable)
index 0000000..4cbaa8b
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+while [ -n "$1" ]; do
+    suite=$1
+    if [ -x $top_srcdir/test/suites/$suite/run ]; then
+        SUITES="$SUITES $suite"
+    else
+        echo "No such suite: $suite"
+        exit 1
+    fi
+    shift
+done
+
+if [ -z "$SUITES" ]; then
+    suitedirs=$top_srcdir/test/suites/*
+    for suitedir in $suitedirs; do
+        if [ -d $suitedir ]; then
+            SUITES="$SUITES `basename $suitedir`"
+        fi
+    done
+fi
+
+[ -z "$STOP" ] && STOP=0
+
+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
+for suite in $SUITES; do
+    echo "Suite: $suite"
+    if $suites_srcdir/$suite/run $suite; then
+        passed=$(($passed+1))
+    else
+        failed=$(($failed+1))
+        [ $STOP -eq 1 ] && break
+    fi
+done
+
+if [ $failed -gt 0 ]; then
+    echo "$failed of $((passed+failed)) test suites failed"
+    exit 1
+else
+    echo "$passed test suites passed"
+    rm -rf $logdir
+fi
diff --git a/test/run-test b/test/run-test
deleted file mode 100644 (file)
index ffe653c..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
-#
-# Jansson is free software; you can redistribute it and/or modify
-# it under the terms of the MIT license. See LICENSE for details.
-
-VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q"
-
-run_testprog() {
-    local prog=$1
-    local prefix=$2
-    if [ -n "$VALGRIND" ]; then
-        local runner="$VALGRIND_CMDLINE "
-    fi
-
-    case "$prog" in
-        load_file_dump_file)
-            $runner./$prog \
-                $prefix.in \
-                $prefix.$prog.stdout \
-                2>$prefix.$prog.stderr
-            ;;
-        *)
-            $runner./$prog \
-                <$prefix.in \
-                >$prefix.$prog.stdout \
-                2>$prefix.$prog.stderr
-            ;;
-    esac
-
-    if [ -n "$VALGRIND" ]; then
-        # Check for Valgrind error output. The valgrind option
-        # --error-exitcode is not enough because Valgrind doesn't
-        # think unfreed allocs are errors.
-        if grep -E -q '^==[0-9]+== ' $prefix.$prog.stderr; then
-            echo "### $prefix ($prog) failed:" >&2
-            echo "valgrind detected an error" >&2
-            echo "for details, see test/$prefix.$prog.stderr" >&2
-            exit 1
-        fi
-    fi
-}
-
-for testfile in $TESTFILES; do
-    tmpdir="testlogs/`basename $testfile`"
-    rm -rf $tmpdir
-    mkdir -p $tmpdir
-    if echo "$testfile" | grep -q -E -e '-strip$'; then
-        opts="--strip"
-    fi
-    ${srcdir}/split-testfile.py $opts $testfile $tmpdir | while read name; do
-        run_test loadf_dumpf $tmpdir/$name
-        run_test loads_dumps $tmpdir/$name
-        run_test load_file_dump_file $tmpdir/$name
-        echo -n '.'
-    done || exit 1
-    echo
-done
diff --git a/test/scripts/run-tests.sh b/test/scripts/run-tests.sh
new file mode 100644 (file)
index 0000000..39ebfc4
--- /dev/null
@@ -0,0 +1,100 @@
+# 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
+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
+
+. $scriptdir/valgrind.sh
+
+rm -rf $suite_log
+mkdir -p $suite_log
+
+for test_path in $suite_srcdir/*; do
+    test_name=$(basename $test_path)
+    test_builddir=$suite_builddir/$test_name
+    test_log=$suite_log/$test_name
+
+    [ "$test_name" = "run" ] && continue
+    is_test || continue
+
+    rm -rf $test_log
+    mkdir -p $test_log
+    if [ $VERBOSE -eq 1 ]; then
+        printf '%s... ' "$test_name"
+    fi
+
+    run_test
+    case $? in
+        0)
+            # Success
+            if [ $VERBOSE -eq 1 ]; then
+                printf 'ok\n'
+            else
+                printf '.'
+            fi
+            rm -rf $test_log
+            ;;
+
+        77)
+            # Skip
+            if [ $VERBOSE -eq 1 ]; then
+                printf 'skipped\n'
+            else
+                printf 'S'
+            fi
+            rm -rf $test_log
+            ;;
+
+        *)
+            # Failure
+            if [ $VERBOSE -eq 1 ]; then
+                printf 'FAILED\n'
+            else
+                printf 'F'
+            fi
+
+            [ $STOP -eq 1 ] && break
+            ;;
+    esac
+done
+
+if [ $VERBOSE -eq 0 ]; then
+    printf '\n'
+fi
+
+if [ -n "$(ls -A $suite_log)" ]; then
+    for test_log in $suite_log/*; do
+        test_name=$(basename $test_log)
+        test_path=$suite_srcdir/$test_name
+        echo "================================================================="
+        echo "$suite_name/$test_name"
+        echo "================================================================="
+        show_error
+        echo
+    done
+    echo "================================================================="
+    exit 1
+else
+    rm -rf $suite_log
+fi
diff --git a/test/scripts/valgrind.sh b/test/scripts/valgrind.sh
new file mode 100644 (file)
index 0000000..5c1c794
--- /dev/null
@@ -0,0 +1,35 @@
+# 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.
+
+[ -z "$VALGRIND" ] && VALGRIND=0
+
+VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q"
+
+if [ $VALGRIND -eq 1 ]; then
+    test_runner="$VALGRIND_CMDLINE"
+    json_process="$VALGRIND_CMDLINE $json_process"
+else
+    test_runner=""
+fi
+
+valgrind_check() {
+    if [ $VALGRIND -eq 1 ]; then
+        # Check for Valgrind error output. The valgrind option
+        # --error-exitcode is not enough because Valgrind doesn't
+        # think unfreed allocs are errors.
+        if grep -E -q '^==[0-9]+== ' $1; then
+            touch $test_log/valgrind_error
+            return 1
+        fi
+    fi
+}
+
+valgrind_show_error() {
+    if [ $VALGRIND -eq 1 -a -f $test_log/valgrind_error ]; then
+        echo "valgrind detected an error"
+        return 0
+    fi
+    return 1
+}
diff --git a/test/split-testfile.py b/test/split-testfile.py
deleted file mode 100755 (executable)
index 97a489e..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
-#
-# Jansson is free software; you can redistribute it and/or modify
-# it under the terms of the MIT license. See LICENSE for details.
-
-import os
-import sys
-from optparse import OptionParser
-
-def strip_file(filename):
-    with open(filename) as fobj:
-        data = fobj.read()
-    with open(filename, 'w') as fobj:
-        fobj.write(data.strip())
-
-def open_files(outdir, i, name):
-    basename = '%02d_%s' % (i, name)
-    print basename
-    input_path = os.path.join(outdir, basename + '.in')
-    output_path = os.path.join(outdir, basename + '.out')
-    return open(input_path, 'w'), open(output_path, 'w')
-
-def main():
-    parser = OptionParser('usage: %prog [options] inputfile outputdir')
-    parser.add_option('--strip', help='strip whitespace from input',
-                      action='store_true', default=False)
-    options, args = parser.parse_args()
-
-    if len(args) != 2:
-        parser.print_help()
-        return 2
-
-    infile = os.path.normpath(args[0])
-    outdir = os.path.normpath(args[1])
-
-    if not os.path.exists(outdir):
-        print >>sys.stderr, 'output directory %r does not exist!' % outdir
-        return 1
-
-    n = 0
-    current = None
-    input, output = None, None
-
-    for line in open(infile):
-        if line.startswith('==== '):
-            n += 1
-            if input is not None and output is not None:
-                input.close()
-                output.close()
-                if options.strip:
-                    strip_file(input.name)
-            input, output = open_files(outdir, n, line[5:line.find(' ====\n')])
-            current = input
-        elif line == '====\n':
-            current = output
-        else:
-            current.write(line)
-
-    if input is not None and output is not None:
-        input.close()
-        output.close()
-
-    print >>sys.stderr, "%s: %d test cases" % (infile, n)
-
-if __name__ == '__main__':
-    sys.exit(main() or 0)
diff --git a/test/suites/Makefile.am b/test/suites/Makefile.am
new file mode 100644 (file)
index 0000000..a53eb07
--- /dev/null
@@ -0,0 +1,2 @@
+SUBDIRS = api
+EXTRA_DIST = invalid invalid-unicode valid
diff --git a/test/suites/api/Makefile.am b/test/suites/api/Makefile.am
new file mode 100644 (file)
index 0000000..9e60f48
--- /dev/null
@@ -0,0 +1,35 @@
+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 \
+       test_pack \
+       test_simple \
+       test_unpack
+
+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
+test_pack_SOURCES = test_pack.c util.h
+test_simple_SOURCES = test_simple.c util.h
+test_unpack_SOURCES = test_unpack.c util.h
+
+AM_CPPFLAGS = -I$(top_srcdir)/src
+AM_CFLAGS = -Wall -Werror
+LDFLAGS = -static  # for speed and Valgrind
+LDADD = $(top_builddir)/src/libjansson.la
diff --git a/test/suites/api/check-exports b/test/suites/api/check-exports
new file mode 100755 (executable)
index 0000000..1c2ba9a
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# 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 ' [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
+    exit 1
+fi
diff --git a/test/suites/api/run b/test/suites/api/run
new file mode 100755 (executable)
index 0000000..3327e15
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# 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.
+
+is_test() {
+    case "$test_name" in
+        *.c|check-exports)
+            return 0
+            ;;
+        *)
+            return 1
+            ;;
+    esac
+}
+
+run_test() {
+    if [ "$test_name" = "check-exports" ]; then
+        test_log=$test_log $test_path >$test_log/stdout 2>$test_log/stderr
+    else
+        $test_runner $suite_builddir/${test_name%.c} \
+            >$test_log/stdout \
+            2>$test_log/stderr \
+            || return 1
+        valgrind_check $test_log/stderr || return 1
+    fi
+}
+
+show_error() {
+    valgrind_show_error && return
+    cat $test_log/stderr
+}
+
+. $top_srcdir/test/scripts/run-tests.sh
similarity index 99%
rename from test/testprogs/test_array.c
rename to test/suites/api/test_array.c
index 45bfefd..4585db9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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;
 }
diff --git a/test/suites/api/test_copy.c b/test/suites/api/test_copy.c
new file mode 100644 (file)
index 0000000..5f22eb8
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * 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 <jansson.h>
+#include "util.h"
+
+static void test_copy_simple(void)
+{
+    json_t *value, *copy;
+
+    if(json_copy(NULL))
+        fail("copying NULL doesn't return NULL");
+
+    /* true */
+    value = json_true();
+    copy = json_copy(value);
+    if(value != copy)
+        fail("copying true failed");
+    json_decref(value);
+    json_decref(copy);
+
+    /* false */
+    value = json_false();
+    copy = json_copy(value);
+    if(value != copy)
+        fail("copying false failed");
+    json_decref(value);
+    json_decref(copy);
+
+    /* null */
+    value = json_null();
+    copy = json_copy(value);
+    if(value != copy)
+        fail("copying null failed");
+    json_decref(value);
+    json_decref(copy);
+
+    /* string */
+    value = json_string("foo");
+    if(!value)
+        fail("unable to create a string");
+    copy = json_copy(value);
+    if(!copy)
+        fail("unable to copy a string");
+    if(copy == value)
+        fail("copying a string doesn't copy");
+    if(!json_equal(copy, value))
+        fail("copying a string produces an inequal copy");
+    if(value->refcount != 1 || copy->refcount != 1)
+        fail("invalid refcounts");
+    json_decref(value);
+    json_decref(copy);
+
+    /* integer */
+    value = json_integer(543);
+    if(!value)
+        fail("unable to create an integer");
+    copy = json_copy(value);
+    if(!copy)
+        fail("unable to copy an integer");
+    if(copy == value)
+        fail("copying an integer doesn't copy");
+    if(!json_equal(copy, value))
+        fail("copying an integer produces an inequal copy");
+    if(value->refcount != 1 || copy->refcount != 1)
+        fail("invalid refcounts");
+    json_decref(value);
+    json_decref(copy);
+
+    /* real */
+    value = json_real(123e9);
+    if(!value)
+        fail("unable to create a real");
+    copy = json_copy(value);
+    if(!copy)
+        fail("unable to copy a real");
+    if(copy == value)
+        fail("copying a real doesn't copy");
+    if(!json_equal(copy, value))
+        fail("copying a real produces an inequal copy");
+    if(value->refcount != 1 || copy->refcount != 1)
+        fail("invalid refcounts");
+    json_decref(value);
+    json_decref(copy);
+}
+
+static void test_deep_copy_simple(void)
+{
+    json_t *value, *copy;
+
+    if(json_deep_copy(NULL))
+        fail("deep copying NULL doesn't return NULL");
+
+    /* true */
+    value = json_true();
+    copy = json_deep_copy(value);
+    if(value != copy)
+        fail("deep copying true failed");
+    json_decref(value);
+    json_decref(copy);
+
+    /* false */
+    value = json_false();
+    copy = json_deep_copy(value);
+    if(value != copy)
+        fail("deep copying false failed");
+    json_decref(value);
+    json_decref(copy);
+
+    /* null */
+    value = json_null();
+    copy = json_deep_copy(value);
+    if(value != copy)
+        fail("deep copying null failed");
+    json_decref(value);
+    json_decref(copy);
+
+    /* string */
+    value = json_string("foo");
+    if(!value)
+        fail("unable to create a string");
+    copy = json_deep_copy(value);
+    if(!copy)
+        fail("unable to deep copy a string");
+    if(copy == value)
+        fail("deep copying a string doesn't copy");
+    if(!json_equal(copy, value))
+        fail("deep copying a string produces an inequal copy");
+    if(value->refcount != 1 || copy->refcount != 1)
+        fail("invalid refcounts");
+    json_decref(value);
+    json_decref(copy);
+
+    /* integer */
+    value = json_integer(543);
+    if(!value)
+        fail("unable to create an integer");
+    copy = json_deep_copy(value);
+    if(!copy)
+        fail("unable to deep copy an integer");
+    if(copy == value)
+        fail("deep copying an integer doesn't copy");
+    if(!json_equal(copy, value))
+        fail("deep copying an integer produces an inequal copy");
+    if(value->refcount != 1 || copy->refcount != 1)
+        fail("invalid refcounts");
+    json_decref(value);
+    json_decref(copy);
+
+    /* real */
+    value = json_real(123e9);
+    if(!value)
+        fail("unable to create a real");
+    copy = json_deep_copy(value);
+    if(!copy)
+        fail("unable to deep copy a real");
+    if(copy == value)
+        fail("deep copying a real doesn't copy");
+    if(!json_equal(copy, value))
+        fail("deep copying a real produces an inequal copy");
+    if(value->refcount != 1 || copy->refcount != 1)
+        fail("invalid refcounts");
+    json_decref(value);
+    json_decref(copy);
+}
+
+static void test_copy_array(void)
+{
+    const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
+
+    json_t *array, *copy;
+    size_t i;
+
+    array = json_loads(json_array_text, 0, NULL);
+    if(!array)
+        fail("unable to parse an array");
+
+    copy = json_copy(array);
+    if(!copy)
+        fail("unable to copy an array");
+    if(copy == array)
+        fail("copying an array doesn't copy");
+    if(!json_equal(copy, array))
+        fail("copying an array produces an inequal copy");
+
+    for(i = 0; i < json_array_size(copy); i++)
+    {
+        if(json_array_get(array, i) != json_array_get(copy, i))
+            fail("copying an array modifies its elements");
+    }
+
+    json_decref(array);
+    json_decref(copy);
+}
+
+static void test_deep_copy_array(void)
+{
+    const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
+
+    json_t *array, *copy;
+    size_t i;
+
+    array = json_loads(json_array_text, 0, NULL);
+    if(!array)
+        fail("unable to parse an array");
+
+    copy = json_deep_copy(array);
+    if(!copy)
+        fail("unable to deep copy an array");
+    if(copy == array)
+        fail("deep copying an array doesn't copy");
+    if(!json_equal(copy, array))
+        fail("deep copying an array produces an inequal copy");
+
+    for(i = 0; i < json_array_size(copy); i++)
+    {
+        if(json_array_get(array, i) == json_array_get(copy, i))
+            fail("deep copying an array doesn't copy its elements");
+    }
+
+    json_decref(array);
+    json_decref(copy);
+}
+
+static void test_copy_object(void)
+{
+    const char *json_object_text =
+        "{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
+
+    json_t *object, *copy;
+    void *iter;
+
+    object = json_loads(json_object_text, 0, NULL);
+    if(!object)
+        fail("unable to parse an object");
+
+    copy = json_copy(object);
+    if(!copy)
+        fail("unable to copy an object");
+    if(copy == object)
+        fail("copying an object doesn't copy");
+    if(!json_equal(copy, object))
+        fail("copying an object produces an inequal copy");
+
+    iter = json_object_iter(object);
+    while(iter)
+    {
+        const char *key;
+        json_t *value1, *value2;
+
+        key = json_object_iter_key(iter);
+        value1 = json_object_iter_value(iter);
+        value2 = json_object_get(copy, key);
+
+        if(value1 != value2)
+            fail("deep copying an object modifies its items");
+
+        iter = json_object_iter_next(object, iter);
+    }
+
+    json_decref(object);
+    json_decref(copy);
+}
+
+static void test_deep_copy_object(void)
+{
+    const char *json_object_text =
+        "{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
+
+    json_t *object, *copy;
+    void *iter;
+
+    object = json_loads(json_object_text, 0, NULL);
+    if(!object)
+        fail("unable to parse an object");
+
+    copy = json_deep_copy(object);
+    if(!copy)
+        fail("unable to deep copy an object");
+    if(copy == object)
+        fail("deep copying an object doesn't copy");
+    if(!json_equal(copy, object))
+        fail("deep copying an object produces an inequal copy");
+
+    iter = json_object_iter(object);
+    while(iter)
+    {
+        const char *key;
+        json_t *value1, *value2;
+
+        key = json_object_iter_key(iter);
+        value1 = json_object_iter_value(iter);
+        value2 = json_object_get(copy, key);
+
+        if(value1 == value2)
+            fail("deep copying an object doesn't copy its items");
+
+        iter = json_object_iter_next(object, iter);
+    }
+
+    json_decref(object);
+    json_decref(copy);
+}
+
+static void run_tests()
+{
+    test_copy_simple();
+    test_deep_copy_simple();
+    test_copy_array();
+    test_deep_copy_array();
+    test_copy_object();
+    test_deep_copy_object();
+}
diff --git a/test/suites/api/test_dump.c b/test/suites/api/test_dump.c
new file mode 100644 (file)
index 0000000..8cd659c
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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 encode_twice()
+{
+    /* Encode an empty object/array, add an item, encode again */
+
+    json_t *json;
+    char *result;
+
+    json = json_object();
+    result = json_dumps(json, 0);
+    if(!result || strcmp(result, "{}"))
+      fail("json_dumps failed");
+    free(result);
+
+    json_object_set_new(json, "foo", json_integer(5));
+    result = json_dumps(json, 0);
+    if(!result || strcmp(result, "{\"foo\": 5}"))
+      fail("json_dumps failed");
+    free(result);
+
+    json_decref(json);
+
+    json = json_array();
+    result = json_dumps(json, 0);
+    if(!result || strcmp(result, "[]"))
+      fail("json_dumps failed");
+    free(result);
+
+    json_array_append_new(json, json_integer(5));
+    result = json_dumps(json, 0);
+    if(!result || strcmp(result, "[5]"))
+      fail("json_dumps failed");
+    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>}}}
+       array: [[[<circular reference to the $[0] array>]]]
+
+       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());
+    json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
+                    json_object_get(json, "a"));
+
+    if(json_dumps(json, 0))
+        fail("json_dumps encoded a circular reference!");
+
+    json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");
+
+    result = json_dumps(json, 0);
+    if(!result || strcmp(result, "{\"a\": {\"b\": {}}}"))
+        fail("json_dumps failed!");
+    free(result);
+
+    json_decref(json);
+
+    json = json_array();
+    json_array_append_new(json, json_array());
+    json_array_append_new(json_array_get(json, 0), json_array());
+    json_array_append(json_array_get(json_array_get(json, 0), 0),
+                      json_array_get(json, 0));
+
+    if(json_dumps(json, 0))
+        fail("json_dumps encoded a circular reference!");
+
+    json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);
+
+    result = json_dumps(json, 0);
+    if(!result || strcmp(result, "[[[]]]"))
+        fail("json_dumps failed!");
+    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);
+}
+
+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);
+}
diff --git a/test/suites/api/test_equal.c b/test/suites/api/test_equal.c
new file mode 100644 (file)
index 0000000..d7e08d2
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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 "util.h"
+
+static void test_equal_simple()
+{
+    json_t *value1, *value2;
+
+    if(json_equal(NULL, NULL))
+        fail("json_equal fails for two NULLs");
+
+    value1 = json_true();
+    if(json_equal(value1, NULL) || json_equal(NULL, value1))
+        fail("json_equal fails for NULL");
+
+    /* this covers true, false and null as they are singletons */
+    if(!json_equal(value1, value1))
+        fail("identical objects are not equal");
+    json_decref(value1);
+
+    /* integer */
+    value1 = json_integer(1);
+    value2 = json_integer(1);
+    if(!value1 || !value2)
+        fail("unable to create integers");
+    if(!json_equal(value1, value2))
+        fail("json_equal fails for two equal integers");
+    json_decref(value2);
+
+    value2 = json_integer(2);
+    if(!value2)
+        fail("unable to create an integer");
+    if(json_equal(value1, value2))
+        fail("json_equal fails for two inequal integers");
+
+    json_decref(value1);
+    json_decref(value2);
+
+    /* real */
+    value1 = json_real(1.2);
+    value2 = json_real(1.2);
+    if(!value1 || !value2)
+        fail("unable to create reals");
+    if(!json_equal(value1, value2))
+        fail("json_equal fails for two equal reals");
+    json_decref(value2);
+
+    value2 = json_real(3.141592);
+    if(!value2)
+        fail("unable to create an real");
+    if(json_equal(value1, value2))
+        fail("json_equal fails for two inequal reals");
+
+    json_decref(value1);
+    json_decref(value2);
+
+    /* string */
+    value1 = json_string("foo");
+    value2 = json_string("foo");
+    if(!value1 || !value2)
+        fail("unable to create strings");
+    if(!json_equal(value1, value2))
+        fail("json_equal fails for two equal strings");
+    json_decref(value2);
+
+    value2 = json_string("bar");
+    if(!value2)
+        fail("unable to create an string");
+    if(json_equal(value1, value2))
+        fail("json_equal fails for two inequal strings");
+
+    json_decref(value1);
+    json_decref(value2);
+}
+
+static void test_equal_array()
+{
+    json_t *array1, *array2;
+
+    array1 = json_array();
+    array2 = json_array();
+    if(!array1 || !array2)
+        fail("unable to create arrays");
+
+    if(!json_equal(array1, array2))
+        fail("json_equal fails for two empty arrays");
+
+    json_array_append_new(array1, json_integer(1));
+    json_array_append_new(array2, json_integer(1));
+    json_array_append_new(array1, json_string("foo"));
+    json_array_append_new(array2, json_string("foo"));
+    json_array_append_new(array1, json_integer(2));
+    json_array_append_new(array2, json_integer(2));
+    if(!json_equal(array1, array2))
+        fail("json_equal fails for two equal arrays");
+
+    json_array_remove(array2, 2);
+    if(json_equal(array1, array2))
+        fail("json_equal fails for two inequal arrays");
+
+    json_array_append_new(array2, json_integer(3));
+    if(json_equal(array1, array2))
+        fail("json_equal fails for two inequal arrays");
+
+    json_decref(array1);
+    json_decref(array2);
+}
+
+static void test_equal_object()
+{
+    json_t *object1, *object2;
+
+    object1 = json_object();
+    object2 = json_object();
+    if(!object1 || !object2)
+        fail("unable to create objects");
+
+    if(!json_equal(object1, object2))
+        fail("json_equal fails for two empty objects");
+
+    json_object_set_new(object1, "a", json_integer(1));
+    json_object_set_new(object2, "a", json_integer(1));
+    json_object_set_new(object1, "b", json_string("foo"));
+    json_object_set_new(object2, "b", json_string("foo"));
+    json_object_set_new(object1, "c", json_integer(2));
+    json_object_set_new(object2, "c", json_integer(2));
+    if(!json_equal(object1, object2))
+        fail("json_equal fails for two equal objects");
+
+    json_object_del(object2, "c");
+    if(json_equal(object1, object2))
+        fail("json_equal fails for two inequal objects");
+
+    json_object_set_new(object2, "c", json_integer(3));
+    if(json_equal(object1, object2))
+        fail("json_equal fails for two inequal objects");
+
+    json_object_del(object2, "c");
+    json_object_set_new(object2, "d", json_integer(2));
+    if(json_equal(object1, object2))
+        fail("json_equal fails for two inequal objects");
+
+    json_decref(object1);
+    json_decref(object2);
+}
+
+static void test_equal_complex()
+{
+    json_t *value1, *value2;
+
+    const char *complex_json =
+"{"
+"    \"integer\": 1, "
+"    \"real\": 3.141592, "
+"    \"string\": \"foobar\", "
+"    \"true\": true, "
+"    \"object\": {"
+"        \"array-in-object\": [1,true,\"foo\",{}],"
+"        \"object-in-object\": {\"foo\": \"bar\"}"
+"    },"
+"    \"array\": [\"foo\", false, null, 1.234]"
+"}";
+
+    value1 = json_loads(complex_json, 0, NULL);
+    value2 = json_loads(complex_json, 0, NULL);
+    if(!value1 || !value2)
+        fail("unable to parse JSON");
+    if(!json_equal(value1, value2))
+        fail("json_equal fails for two inequal strings");
+
+    json_decref(value1);
+    json_decref(value2);
+
+    /* TODO: There's no negative test case here */
+}
+
+static void run_tests()
+{
+    test_equal_simple();
+    test_equal_array();
+    test_equal_object();
+    test_equal_complex();
+}
diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c
new file mode 100644 (file)
index 0000000..30ba65c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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 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");
+
+    /* 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;
+
+    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");
+    }
+}
diff --git a/test/suites/api/test_memory_funcs.c b/test/suites/api/test_memory_funcs.c
new file mode 100644 (file)
index 0000000..4d6a81e
--- /dev/null
@@ -0,0 +1,82 @@
+#include <string.h>
+#include <jansson.h>
+
+#include "util.h"
+
+static int malloc_called = 0;
+static int free_called = 0;
+
+/* helper */
+static void create_and_free_complex_object()
+{
+    json_t *obj;
+
+    obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]",
+                    "foo", 42,
+                    "bar",
+                    "baz", 1,
+                    "qux", 0,
+                    "alice", "bar", "baz",
+                    "bob", 9, 8, 7);
+
+    json_decref(obj);
+}
+
+static void *my_malloc(size_t size)
+{
+    malloc_called += 1;
+    return malloc(size);
+}
+
+static void my_free(void *ptr)
+{
+    free_called += 1;
+    free(ptr);
+}
+
+static void test_simple()
+{
+    json_set_alloc_funcs(my_malloc, my_free);
+    create_and_free_complex_object();
+
+    if(malloc_called != 20 || free_called != 20)
+        fail("Custom allocation failed");
+}
+
+
+/*
+  Test the secure memory functions code given in the API reference
+  documentation, but by using plain memset instead of
+  guaranteed_memset().
+*/
+
+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 (char *)ptr + 8;
+}
+
+static void secure_free(void *ptr)
+{
+    size_t size;
+
+    ptr = (char *)ptr - 8;
+    size = *((size_t *)ptr);
+
+    /*guaranteed_*/memset(ptr, 0, size);
+    free(ptr);
+}
+
+static void test_secure_funcs(void)
+{
+    json_set_alloc_funcs(secure_malloc, secure_free);
+    create_and_free_complex_object();
+}
+
+static void run_tests()
+{
+    test_simple();
+    test_secure_funcs();
+}
similarity index 51%
rename from test/testprogs/test_number.c
rename to test/suites/api/test_number.c
index a634125..c256598 100644 (file)
@@ -1,14 +1,15 @@
 /*
- * Copyright (c) 2009 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
 }
similarity index 53%
rename from test/testprogs/test_object.c
rename to test/suites/api/test_object.c
index 57650e8..b317745 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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;
@@ -167,10 +209,136 @@ static void test_circular()
     json_decref(object1);
 }
 
+static void test_set_nocheck()
+{
+    json_t *object, *string;
+
+    object = json_object();
+    string = json_string("bar");
+
+    if(!object)
+        fail("unable to create object");
+    if(!string)
+        fail("unable to create string");
+
+    if(json_object_set_nocheck(object, "foo", string))
+        fail("json_object_set_nocheck failed");
+    if(json_object_get(object, "foo") != string)
+        fail("json_object_get after json_object_set_nocheck failed");
+
+    /* invalid UTF-8 in key */
+    if(json_object_set_nocheck(object, "a\xefz", string))
+        fail("json_object_set_nocheck failed for invalid UTF-8");
+    if(json_object_get(object, "a\xefz") != string)
+        fail("json_object_get after json_object_set_nocheck failed");
+
+    if(json_object_set_new_nocheck(object, "bax", json_integer(123)))
+        fail("json_object_set_new_nocheck failed");
+    if(json_integer_value(json_object_get(object, "bax")) != 123)
+        fail("json_object_get after json_object_set_new_nocheck failed");
+
+    /* invalid UTF-8 in key */
+    if(json_object_set_new_nocheck(object, "asdf\xfe", json_integer(321)))
+        fail("json_object_set_new_nocheck failed for invalid UTF-8");
+    if(json_integer_value(json_object_get(object, "asdf\xfe")) != 321)
+        fail("json_object_get after json_object_set_new_nocheck failed");
+
+    json_decref(string);
+    json_decref(object);
+}
+
+static void test_iterators()
+{
+    json_t *object, *foo, *bar, *baz;
+    void *iter;
+
+    if(json_object_iter(NULL))
+        fail("able to iterate over NULL");
+
+    if(json_object_iter_next(NULL, NULL))
+        fail("able to increment an iterator on a NULL object");
+
+    object = json_object();
+    foo = json_string("foo");
+    bar = json_string("bar");
+    baz = json_string("baz");
+    if(!object || !foo || !bar || !bar)
+        fail("unable to create values");
+
+    if(json_object_iter_next(object, NULL))
+        fail("able to increment a NULL iterator");
+
+    if(json_object_set(object, "a", foo) ||
+       json_object_set(object, "b", bar) ||
+       json_object_set(object, "c", baz))
+        fail("unable to populate object");
+
+    iter = json_object_iter(object);
+    if(!iter)
+        fail("unable to get iterator");
+    if(strcmp(json_object_iter_key(iter), "a"))
+        fail("iterating failed: wrong key");
+    if(json_object_iter_value(iter) != foo)
+        fail("iterating failed: wrong value");
+
+    iter = json_object_iter_next(object, iter);
+    if(!iter)
+        fail("unable to increment iterator");
+    if(strcmp(json_object_iter_key(iter), "b"))
+        fail("iterating failed: wrong key");
+    if(json_object_iter_value(iter) != bar)
+        fail("iterating failed: wrong value");
+
+    iter = json_object_iter_next(object, iter);
+    if(!iter)
+        fail("unable to increment iterator");
+    if(strcmp(json_object_iter_key(iter), "c"))
+        fail("iterating failed: wrong key");
+    if(json_object_iter_value(iter) != baz)
+        fail("iterating failed: wrong value");
+
+    if(json_object_iter_next(object, iter) != NULL)
+        fail("able to iterate over the end");
+
+    if(json_object_iter_at(object, "foo"))
+        fail("json_object_iter_at() succeeds for non-existent key");
+
+    iter = json_object_iter_at(object, "b");
+    if(!iter)
+        fail("json_object_iter_at() fails for an existing key");
+
+    if(strcmp(json_object_iter_key(iter), "b"))
+        fail("iterating failed: wrong key");
+    if(json_object_iter_value(iter) != bar)
+        fail("iterating failed: wrong value");
+
+    iter = json_object_iter_next(object, iter);
+    if(!iter)
+        fail("unable to increment iterator");
+    if(strcmp(json_object_iter_key(iter), "c"))
+        fail("iterating failed: wrong key");
+    if(json_object_iter_value(iter) != baz)
+        fail("iterating failed: wrong value");
+
+    if(json_object_iter_set(object, iter, bar))
+        fail("unable to set value at iterator");
+
+    if(strcmp(json_object_iter_key(iter), "c"))
+        fail("json_object_iter_key() fails after json_object_iter_set()");
+    if(json_object_iter_value(iter) != bar)
+        fail("json_object_iter_value() fails after json_object_iter_set()");
+    if(json_object_get(object, "c") != bar)
+        fail("json_object_get() fails after json_object_iter_set()");
+
+    json_decref(object);
+    json_decref(foo);
+    json_decref(bar);
+    json_decref(baz);
+}
+
 static void test_misc()
 {
     json_t *object, *string, *other_string, *value;
-    void *iter;
 
     object = json_object();
     string = json_string("test");
@@ -193,17 +361,6 @@ static void test_misc()
     if(!json_object_set(object, "a", NULL))
         fail("able to set NULL value");
 
-    iter = json_object_iter(object);
-    if(!iter)
-        fail("unable to get iterator");
-
-    if(strcmp(json_object_iter_key(iter), "a"))
-        fail("iterating failed: wrong key");
-    if(json_object_iter_value(iter) != string)
-        fail("iterating failed: wrong value");
-    if(json_object_iter_next(object, iter) != NULL)
-        fail("able to iterate over the end");
-
     /* invalid UTF-8 in key */
     if(!json_object_set(object, "a\xefz", string))
         fail("able to set invalid unicode key");
@@ -287,12 +444,68 @@ static void test_misc()
     json_decref(object);
 }
 
-int main()
+static void test_preserve_order()
+{
+    json_t *object;
+    char *result;
+
+    const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": 3, \"sit amet\": 5, \"helicopter\": 7}";
+
+    object = json_object();
+
+    json_object_set_new(object, "foobar", json_integer(1));
+    json_object_set_new(object, "bazquux", json_integer(2));
+    json_object_set_new(object, "lorem ipsum", json_integer(3));
+    json_object_set_new(object, "dolor", json_integer(4));
+    json_object_set_new(object, "sit amet", json_integer(5));
+
+    /* changing a value should preserve the order */
+    json_object_set_new(object, "bazquux", json_integer(6));
+
+    /* deletion shouldn't change the order of others */
+    json_object_del(object, "dolor");
+
+    /* add a new item just to make sure */
+    json_object_set_new(object, "helicopter", json_integer(7));
+
+    result = json_dumps(object, JSON_PRESERVE_ORDER);
+
+    if(strcmp(expected, result) != 0) {
+        fprintf(stderr, "%s != %s", expected, result);
+        fail("JSON_PRESERVE_ORDER doesn't work");
+    }
+
+    free(result);
+    json_decref(object);
+}
+
+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();
-
-    return 0;
+    test_set_nocheck();
+    test_iterators();
+    test_preserve_order();
+    test_foreach();
 }
diff --git a/test/suites/api/test_pack.c b/test/suites/api/test_pack.c
new file mode 100644 (file)
index 0000000..c7e7251
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+#include <jansson.h>
+#include <stdio.h>
+#include "util.h"
+
+static void run_tests()
+{
+    json_t *value;
+    int i;
+    json_error_t error;
+
+    /*
+     * Simple, valid json_pack cases
+     */
+
+    /* true */
+    value = json_pack("b", 1);
+    if(!json_is_true(value))
+        fail("json_pack boolean failed");
+    if(value->refcount != (size_t)-1)
+        fail("json_pack boolean refcount failed");
+    json_decref(value);
+
+    /* false */
+    value = json_pack("b", 0);
+    if(!json_is_false(value))
+        fail("json_pack boolean failed");
+    if(value->refcount != (size_t)-1)
+        fail("json_pack boolean refcount failed");
+    json_decref(value);
+
+    /* null */
+    value = json_pack("n");
+    if(!json_is_null(value))
+        fail("json_pack null failed");
+    if(value->refcount != (size_t)-1)
+        fail("json_pack null refcount failed");
+    json_decref(value);
+
+    /* integer */
+    value = json_pack("i", 1);
+    if(!json_is_integer(value) || json_integer_value(value) != 1)
+        fail("json_pack integer failed");
+    if(value->refcount != (size_t)1)
+        fail("json_pack integer refcount failed");
+    json_decref(value);
+
+    /* integer from json_int_t */
+    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 != (size_t)1)
+        fail("json_pack integer refcount failed");
+    json_decref(value);
+
+    /* real */
+    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 != (size_t)1)
+        fail("json_pack real refcount failed");
+    json_decref(value);
+
+    /* string */
+    value = json_pack("s", "test");
+    if(!json_is_string(value) || strcmp("test", json_string_value(value)))
+        fail("json_pack string failed");
+    if(value->refcount != (size_t)1)
+        fail("json_pack string refcount failed");
+    json_decref(value);
+
+    /* empty object */
+    value = json_pack("{}", 1.0);
+    if(!json_is_object(value) || json_object_size(value) != 0)
+        fail("json_pack empty object failed");
+    if(value->refcount != (size_t)1)
+        fail("json_pack empty object refcount failed");
+    json_decref(value);
+
+    /* empty list */
+    value = json_pack("[]", 1.0);
+    if(!json_is_array(value) || json_array_size(value) != 0)
+        fail("json_pack empty list failed");
+    if(value->refcount != (size_t)1)
+        fail("json_pack empty list failed");
+    json_decref(value);
+
+    /* non-incref'd object */
+    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 != (size_t)1)
+        fail("json_pack integer refcount failed");
+    json_decref(value);
+
+    /* incref'd object */
+    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 != (size_t)2)
+        fail("json_pack integer refcount failed");
+    json_decref(value);
+    json_decref(value);
+
+    /* simple object */
+    value = json_pack("{s:[]}", "foo");
+    if(!json_is_object(value) || json_object_size(value) != 1)
+        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 != (size_t)1)
+        fail("json_pack object refcount failed");
+    json_decref(value);
+
+    /* simple array */
+    value = json_pack("[i,i,i]", 0, 1, 2);
+    if(!json_is_array(value) || json_array_size(value) != 3)
+        fail("json_pack object failed");
+    for(i=0; i<3; i++)
+    {
+        if(!json_is_integer(json_array_get(value, i)) ||
+           json_integer_value(json_array_get(value, i)) != i)
+
+            fail("json_pack integer array failed");
+    }
+    json_decref(value);
+
+    /* Whitespace; regular string */
+    value = json_pack(" s ", "test");
+    if(!json_is_string(value) || strcmp("test", json_string_value(value)))
+        fail("json_pack string (with whitespace) failed");
+    json_decref(value);
+
+    /* Whitespace; empty array */
+    value = json_pack("[ ]");
+    if(!json_is_array(value) || json_array_size(value) != 0)
+        fail("json_pack empty array (with whitespace) failed");
+    json_decref(value);
+
+    /* Whitespace; array */
+    value = json_pack("[ i , i,  i ] ", 1, 2, 3);
+    if(!json_is_array(value) || json_array_size(value) != 3)
+        fail("json_pack array (with whitespace) failed");
+    json_decref(value);
+
+    /*
+     * Invalid cases
+     */
+
+    /* newline in format string */
+    if(json_pack_ex(&error, 0, "{\n\n1"))
+        fail("json_pack failed to catch invalid format '1'");
+    check_error("Expected format 's', got '1'", "<format>", 3, 1, 4);
+
+    /* mismatched open/close array/object */
+    if(json_pack_ex(&error, 0, "[}"))
+        fail("json_pack failed to catch mismatched '}'");
+    check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
+
+    if(json_pack_ex(&error, 0, "{]"))
+        fail("json_pack failed to catch mismatched ']'");
+    check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
+
+    /* missing close array */
+    if(json_pack_ex(&error, 0, "["))
+        fail("json_pack failed to catch missing ']'");
+    check_error("Unexpected end of format string", "<format>", 1, 2, 2);
+
+    /* missing close object */
+    if(json_pack_ex(&error, 0, "{"))
+        fail("json_pack failed to catch missing '}'");
+    check_error("Unexpected end of format string", "<format>", 1, 2, 2);
+
+    /* garbage after format string */
+    if(json_pack_ex(&error, 0, "[i]a", 42))
+        fail("json_pack failed to catch garbage after format string");
+    check_error("Garbage after format string", "<format>", 1, 4, 4);
+
+    if(json_pack_ex(&error, 0, "ia", 42))
+        fail("json_pack failed to catch garbage after format string");
+    check_error("Garbage after format string", "<format>", 1, 2, 2);
+
+    /* NULL string */
+    if(json_pack_ex(&error, 0, "s", NULL))
+        fail("json_pack failed to catch null argument string");
+    check_error("NULL string argument", "<args>", 1, 1, 1);
+
+    /* NULL format */
+    if(json_pack_ex(&error, 0, NULL))
+        fail("json_pack failed to catch NULL format string");
+    check_error("NULL or empty format string", "<format>", -1, -1, 0);
+
+    /* NULL key */
+    if(json_pack_ex(&error, 0, "{s:i}", NULL, 1))
+        fail("json_pack failed to catch NULL key");
+    check_error("NULL object key", "<args>", 1, 2, 2);
+
+    /* More complicated checks for row/columns */
+    if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
+        fail("json_pack failed to catch object as key");
+    check_error("Expected format 's', got '{'", "<format>", 1, 3, 3);
+
+    /* Complex object */
+    if(json_pack_ex(&error, 0, "{ s: {},  s:[ii{} }", "foo", "bar", 12, 13))
+        fail("json_pack failed to catch missing ]");
+    check_error("Unexpected format character '}'", "<format>", 1, 19, 19);
+
+    /* Complex array */
+    if(json_pack_ex(&error, 0, "[[[[[   [[[[[  [[[[ }]]]] ]]]] ]]]]]"))
+        fail("json_pack failed to catch extra }");
+    check_error("Unexpected format character '}'", "<format>", 1, 21, 21);
+
+    /* Invalid UTF-8 in object key */
+    if(json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42))
+        fail("json_pack failed to catch invalid UTF-8 in an object key");
+    check_error("Invalid UTF-8 in object key", "<args>", 1, 2, 2);
+
+    /* Invalid UTF-8 in a string */
+    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);
+}
similarity index 55%
rename from test/testprogs/test_simple.c
rename to test/suites/api/test_simple.c
index 9d7691e..4e2bb2f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 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");
@@ -73,6 +89,33 @@ int main()
     if(value)
         fail("json_string(<invalid utf-8>) failed");
 
+    value = json_string_nocheck("foo");
+    if(!value)
+        fail("json_string_nocheck failed");
+    if(strcmp(json_string_value(value), "foo"))
+        fail("invalid string value");
+
+    if(json_string_set_nocheck(value, "bar"))
+        fail("json_string_set_nocheck failed");
+    if(strcmp(json_string_value(value), "bar"))
+        fail("invalid string value");
+
+    json_decref(value);
+
+    /* invalid UTF-8 */
+    value = json_string_nocheck("qu\xff");
+    if(!value)
+        fail("json_string_nocheck failed");
+    if(strcmp(json_string_value(value), "qu\xff"))
+        fail("invalid string value");
+
+    if(json_string_set_nocheck(value, "\xfd\xfe\xff"))
+        fail("json_string_set_nocheck failed");
+    if(strcmp(json_string_value(value), "\xfd\xfe\xff"))
+        fail("invalid string value");
+
+    json_decref(value);
+
 
     value = json_integer(123);
     if(!value)
@@ -123,5 +166,34 @@ int main()
         fail("json_null failed");
     json_decref(value);
 
-    return 0;
+    /* Test reference counting on singletons (true, false, null) */
+    value = json_true();
+    if(value->refcount != (size_t)-1)
+      fail("refcounting true works incorrectly");
+    json_decref(value);
+    if(value->refcount != (size_t)-1)
+      fail("refcounting true works incorrectly");
+    json_incref(value);
+    if(value->refcount != (size_t)-1)
+      fail("refcounting true works incorrectly");
+
+    value = json_false();
+    if(value->refcount != (size_t)-1)
+      fail("refcounting false works incorrectly");
+    json_decref(value);
+    if(value->refcount != (size_t)-1)
+      fail("refcounting false works incorrectly");
+    json_incref(value);
+    if(value->refcount != (size_t)-1)
+      fail("refcounting false works incorrectly");
+
+    value = json_null();
+    if(value->refcount != (size_t)-1)
+      fail("refcounting null works incorrectly");
+    json_decref(value);
+    if(value->refcount != (size_t)-1)
+      fail("refcounting null works incorrectly");
+    json_incref(value);
+    if(value->refcount != (size_t)-1)
+      fail("refcounting null works incorrectly");
 }
diff --git a/test/suites/api/test_unpack.c b/test/suites/api/test_unpack.c
new file mode 100644 (file)
index 0000000..b6c7b11
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+#include <jansson.h>
+#include <stdio.h>
+#include "util.h"
+
+static void run_tests()
+{
+    json_t *j, *j2;
+    int i1, i2, i3;
+    json_int_t I1;
+    int rv;
+    double f;
+    char *s;
+
+    json_error_t error;
+
+    /*
+     * Simple, valid json_pack cases
+     */
+
+    /* true */
+    rv = json_unpack(json_true(), "b", &i1);
+    if(rv || !i1)
+        fail("json_unpack boolean failed");
+
+    /* false */
+    rv = json_unpack(json_false(), "b", &i1);
+    if(rv || i1)
+        fail("json_unpack boolean failed");
+
+    /* null */
+    if(json_unpack(json_null(), "n"))
+        fail("json_unpack null failed");
+
+    /* integer */
+    j = json_integer(42);
+    rv = json_unpack(j, "i", &i1);
+    if(rv || i1 != 42)
+        fail("json_unpack integer failed");
+    json_decref(j);
+
+    /* json_int_t */
+    j = json_integer(5555555);
+    rv = json_unpack(j, "I", &I1);
+    if(rv || I1 != 5555555)
+        fail("json_unpack json_int_t failed");
+    json_decref(j);
+
+    /* real */
+    j = json_real(1.7);
+    rv = json_unpack(j, "f", &f);
+    if(rv || f != 1.7)
+        fail("json_unpack real failed");
+    json_decref(j);
+
+    /* number */
+    j = json_integer(12345);
+    rv = json_unpack(j, "F", &f);
+    if(rv || f != 12345.0)
+        fail("json_unpack (real or) integer failed");
+    json_decref(j);
+
+    j = json_real(1.7);
+    rv = json_unpack(j, "F", &f);
+    if(rv || f != 1.7)
+        fail("json_unpack real (or integer) failed");
+    json_decref(j);
+
+    /* string */
+    j = json_string("foo");
+    rv = json_unpack(j, "s", &s);
+    if(rv || strcmp(s, "foo"))
+        fail("json_unpack string failed");
+    json_decref(j);
+
+    /* empty object */
+    j = json_object();
+    if(json_unpack(j, "{}"))
+        fail("json_unpack empty object failed");
+    json_decref(j);
+
+    /* empty list */
+    j = json_array();
+    if(json_unpack(j, "[]"))
+        fail("json_unpack empty list failed");
+    json_decref(j);
+
+    /* non-incref'd object */
+    j = json_object();
+    rv = json_unpack(j, "o", &j2);
+    if(j2 != j || j->refcount != 1)
+        fail("json_unpack object failed");
+    json_decref(j);
+
+    /* incref'd object */
+    j = json_object();
+    rv = json_unpack(j, "O", &j2);
+    if(j2 != j || j->refcount != 2)
+        fail("json_unpack object failed");
+    json_decref(j);
+    json_decref(j);
+
+    /* simple object */
+    j = json_pack("{s:i}", "foo", 42);
+    rv = json_unpack(j, "{s:i}", "foo", &i1);
+    if(rv || i1 != 42)
+        fail("json_unpack simple object failed");
+    json_decref(j);
+
+    /* simple array */
+    j = json_pack("[iii]", 1, 2, 3);
+    rv = json_unpack(j, "[i,i,i]", &i1, &i2, &i3);
+    if(rv || i1 != 1 || i2 != 2 || i3 != 3)
+        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
+     */
+
+    j = json_integer(42);
+    if(!json_unpack_ex(j, &error, 0, "z"))
+        fail("json_unpack succeeded with invalid format character");
+    check_error("Unexpected format character 'z'", "<format>", 1, 1, 1);
+
+    if(!json_unpack_ex(NULL, &error, 0, "[i]"))
+        fail("json_unpack succeeded with NULL root");
+    check_error("NULL root value", "<root>", -1, -1, 0);
+    json_decref(j);
+
+    /* mismatched open/close array/object */
+    j = json_pack("[]");
+    if(!json_unpack_ex(j, &error, 0, "[}"))
+        fail("json_unpack failed to catch mismatched ']'");
+    check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
+    json_decref(j);
+
+    j = json_pack("{}");
+    if(!json_unpack_ex(j, &error, 0, "{]"))
+        fail("json_unpack failed to catch mismatched '}'");
+    check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
+    json_decref(j);
+
+    /* missing close array */
+    j = json_pack("[]");
+    if(!json_unpack_ex(j, &error, 0, "["))
+        fail("json_unpack failed to catch missing ']'");
+    check_error("Unexpected end of format string", "<format>", 1, 2, 2);
+    json_decref(j);
+
+    /* missing close object */
+    j = json_pack("{}");
+    if(!json_unpack_ex(j, &error, 0, "{"))
+        fail("json_unpack failed to catch missing '}'");
+    check_error("Unexpected end of format string", "<format>", 1, 2, 2);
+    json_decref(j);
+
+    /* garbage after format string */
+    j = json_pack("[i]", 42);
+    if(!json_unpack_ex(j, &error, 0, "[i]a", &i1))
+        fail("json_unpack failed to catch garbage after format string");
+    check_error("Garbage after format string", "<format>", 1, 4, 4);
+    json_decref(j);
+
+    j = json_integer(12345);
+    if(!json_unpack_ex(j, &error, 0, "ia", &i1))
+        fail("json_unpack failed to catch garbage after format string");
+    check_error("Garbage after format string", "<format>", 1, 2, 2);
+    json_decref(j);
+
+    /* NULL format string */
+    j = json_pack("[]");
+    if(!json_unpack_ex(j, &error, 0, NULL))
+        fail("json_unpack failed to catch null format string");
+    check_error("NULL or empty format string", "<format>", -1, -1, 0);
+    json_decref(j);
+
+    /* NULL string pointer */
+    j = json_string("foobie");
+    if(!json_unpack_ex(j, &error, 0, "s", NULL))
+        fail("json_unpack failed to catch null string pointer");
+    check_error("NULL string argument", "<args>", 1, 1, 1);
+    json_decref(j);
+
+    /* invalid types */
+    j = json_integer(42);
+    j2 = json_string("foo");
+    if(!json_unpack_ex(j, &error, 0, "s"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected string, got integer", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j, &error, 0, "n"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected null, got integer", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j, &error, 0, "b"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected true or false, got integer", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j2, &error, 0, "i"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected integer, got string", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j2, &error, 0, "I"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected integer, got string", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j, &error, 0, "f"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected real, got integer", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j2, &error, 0, "F"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected real or integer, got string", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j, &error, 0, "[i]"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected array, got integer", "<validation>", 1, 1, 1);
+
+    if(!json_unpack_ex(j, &error, 0, "{si}", "foo"))
+        fail("json_unpack failed to catch invalid type");
+    check_error("Expected object, got integer", "<validation>", 1, 1, 1);
+
+    json_decref(j);
+    json_decref(j2);
+
+    /* Array index out of range */
+    j = json_pack("[i]", 1);
+    if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2))
+        fail("json_unpack failed to catch index out of array bounds");
+    check_error("Array index 1 out of range", "<validation>", 1, 3, 3);
+    json_decref(j);
+
+    /* NULL object key */
+    j = json_pack("{si}", "foo", 42);
+    if(!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1))
+        fail("json_unpack failed to catch null string pointer");
+    check_error("NULL object key", "<args>", 1, 2, 2);
+    json_decref(j);
+
+    /* Object key not found */
+    j = json_pack("{si}", "foo", 42);
+    if(!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1))
+        fail("json_unpack failed to catch null string pointer");
+    check_error("Object item not found: baz", "<validation>", 1, 3, 3);
+    json_decref(j);
+
+    /*
+     * Strict validation
+     */
+
+    j = json_pack("[iii]", 1, 2, 3);
+    rv = json_unpack(j, "[iii!]", &i1, &i2, &i3);
+    if(rv || i1 != 1 || i2 != 2 || i3 != 3)
+        fail("json_unpack array with strict validation failed");
+    json_decref(j);
+
+    j = json_pack("[iii]", 1, 2, 3);
+    if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2))
+        fail("json_unpack array with strict validation failed");
+    check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
+    json_decref(j);
+
+    /* Like above, but with JSON_STRICT instead of '!' format */
+    j = json_pack("[iii]", 1, 2, 3);
+    if(!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2))
+        fail("json_unpack array with strict validation failed");
+    check_error("1 array item(s) left unpacked", "<validation>", 1, 4, 4);
+    json_decref(j);
+
+    j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
+    rv = json_unpack(j, "{sssi!}", "foo", &s, "baz", &i1);
+    if(rv || strcmp(s, "bar") != 0 || i1 != 42)
+        fail("json_unpack object with strict validation failed");
+    json_decref(j);
+
+    /* 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");
+    check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
+    json_decref(j);
+
+    j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4);
+    if(json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY,
+                      "[i{sisn}[ii]]", "foo", "bar"))
+        fail("json_unpack complex value with strict validation failed");
+    json_decref(j);
+
+    /* ! and * must be last */
+    j = json_pack("[ii]", 1, 2);
+    if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2))
+        fail("json_unpack failed to catch ! in the middle of an array");
+    check_error("Expected ']' after '!', got 'i'", "<format>", 1, 4, 4);
+
+    if(!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2))
+        fail("json_unpack failed to catch * in the middle of an array");
+    check_error("Expected ']' after '*', got 'i'", "<format>", 1, 4, 4);
+    json_decref(j);
+
+    j = json_pack("{sssi}", "foo", "bar", "baz", 42);
+    if(!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1))
+        fail("json_unpack failed to catch ! in the middle of an object");
+    check_error("Expected '}' after '!', got 's'", "<format>", 1, 5, 5);
+
+    if(!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1))
+        fail("json_unpack failed to catch ! in the middle of an object");
+    check_error("Expected '}' after '*', got 's'", "<format>", 1, 5, 5);
+    json_decref(j);
+
+    /* Error in nested object */
+    j = json_pack("{s{snsn}}", "foo", "bar", "baz");
+    if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar"))
+        fail("json_unpack nested object with strict validation failed");
+    check_error("1 object item(s) left unpacked", "<validation>", 1, 7, 7);
+    json_decref(j);
+
+    /* Error in nested array */
+    j = json_pack("[[ii]]", 1, 2);
+    if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1))
+        fail("json_unpack nested array with strict validation failed");
+    check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
+    json_decref(j);
+
+    /* 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);
+}
diff --git a/test/suites/api/util.h b/test/suites/api/util.h
new file mode 100644 (file)
index 0000000..d8df972
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#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__)
+
+#define fail(msg)                                                \
+    do {                                                         \
+        failhdr;                                                 \
+        fprintf(stderr, "%s\n", msg);                            \
+        exit(1);                                                 \
+    } while(0)
+
+/* Assumes json_error_t error */
+#define check_error(text_, source_, line_, column_, position_)          \
+    do {                                                                \
+        if(strcmp(error.text, text_) != 0) {                            \
+            failhdr;                                                    \
+            fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \
+            exit(1);                                                    \
+        }                                                               \
+        if(strcmp(error.source, source_) != 0) {                        \
+            failhdr;                                                    \
+                                                                        \
+            fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \
+            exit(1);                                                    \
+        }                                                               \
+        if(error.line != line_) {                                       \
+            failhdr;                                                    \
+            fprintf(stderr, "line: %d != %d\n", error.line, line_);     \
+            exit(1);                                                    \
+        }                                                               \
+        if(error.column != column_) {                                   \
+            failhdr;                                                    \
+            fprintf(stderr, "column: %d != %d\n", error.column, column_); \
+            exit(1);                                                    \
+        }                                                               \
+        if(error.position != position_) {                               \
+            failhdr;                                                    \
+            fprintf(stderr, "position: %d != %d\n", error.position, position_); \
+            exit(1);                                                    \
+        }                                                               \
+    } while(0)
+
+
+static void run_tests();
+
+int main() {
+#ifdef HAVE_SETLOCALE
+    setlocale(LC_ALL, "");
+#endif
+    run_tests();
+    return 0;
+}
+
+#endif
diff --git a/test/suites/encoding-flags/array/input b/test/suites/encoding-flags/array/input
new file mode 100644 (file)
index 0000000..44e2ace
--- /dev/null
@@ -0,0 +1 @@
+[1, 2]
diff --git a/test/suites/encoding-flags/array/output b/test/suites/encoding-flags/array/output
new file mode 100644 (file)
index 0000000..fd8ef09
--- /dev/null
@@ -0,0 +1 @@
+[1, 2]
\ No newline at end of file
diff --git a/test/suites/encoding-flags/compact-array/env b/test/suites/encoding-flags/compact-array/env
new file mode 100644 (file)
index 0000000..4474aaf
--- /dev/null
@@ -0,0 +1,2 @@
+JSON_COMPACT=1
+export JSON_COMPACT
diff --git a/test/suites/encoding-flags/compact-array/input b/test/suites/encoding-flags/compact-array/input
new file mode 100644 (file)
index 0000000..44e2ace
--- /dev/null
@@ -0,0 +1 @@
+[1, 2]
diff --git a/test/suites/encoding-flags/compact-array/output b/test/suites/encoding-flags/compact-array/output
new file mode 100644 (file)
index 0000000..3169929
--- /dev/null
@@ -0,0 +1 @@
+[1,2]
\ No newline at end of file
diff --git a/test/suites/encoding-flags/compact-object/env b/test/suites/encoding-flags/compact-object/env
new file mode 100644 (file)
index 0000000..4474aaf
--- /dev/null
@@ -0,0 +1,2 @@
+JSON_COMPACT=1
+export JSON_COMPACT
diff --git a/test/suites/encoding-flags/compact-object/input b/test/suites/encoding-flags/compact-object/input
new file mode 100644 (file)
index 0000000..062e54f
--- /dev/null
@@ -0,0 +1 @@
+{"a": 1, "b": 2}
diff --git a/test/suites/encoding-flags/compact-object/output b/test/suites/encoding-flags/compact-object/output
new file mode 100644 (file)
index 0000000..73a5d70
--- /dev/null
@@ -0,0 +1 @@
+{"a":1,"b":2}
\ No newline at end of file
diff --git a/test/suites/encoding-flags/ensure-ascii/env b/test/suites/encoding-flags/ensure-ascii/env
new file mode 100644 (file)
index 0000000..1b7b3e3
--- /dev/null
@@ -0,0 +1,2 @@
+JSON_ENSURE_ASCII=1
+export JSON_ENSURE_ASCII
diff --git a/test/suites/encoding-flags/ensure-ascii/input b/test/suites/encoding-flags/ensure-ascii/input
new file mode 100644 (file)
index 0000000..69469ce
--- /dev/null
@@ -0,0 +1,8 @@
+[
+    "foo",
+    "å ä ö",
+    "foo åä",
+    "åä foo",
+    "å foo ä",
+    "clef g: 𝄞"
+]
diff --git a/test/suites/encoding-flags/ensure-ascii/output b/test/suites/encoding-flags/ensure-ascii/output
new file mode 100644 (file)
index 0000000..36f8eb5
--- /dev/null
@@ -0,0 +1 @@
+["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]
\ No newline at end of file
diff --git a/test/suites/encoding-flags/indent-array/env b/test/suites/encoding-flags/indent-array/env
new file mode 100644 (file)
index 0000000..d220f83
--- /dev/null
@@ -0,0 +1,2 @@
+JSON_INDENT=4
+export JSON_INDENT
diff --git a/test/suites/encoding-flags/indent-array/input b/test/suites/encoding-flags/indent-array/input
new file mode 100644 (file)
index 0000000..44e2ace
--- /dev/null
@@ -0,0 +1 @@
+[1, 2]
diff --git a/test/suites/encoding-flags/indent-array/output b/test/suites/encoding-flags/indent-array/output
new file mode 100644 (file)
index 0000000..c57d705
--- /dev/null
@@ -0,0 +1,4 @@
+[
+    1,
+    2
+]
\ No newline at end of file
diff --git a/test/suites/encoding-flags/indent-compact-array/env b/test/suites/encoding-flags/indent-compact-array/env
new file mode 100644 (file)
index 0000000..78fbfcc
--- /dev/null
@@ -0,0 +1,3 @@
+JSON_INDENT=4
+JSON_COMPACT=1
+export JSON_INDENT JSON_COMPACT
diff --git a/test/suites/encoding-flags/indent-compact-array/input b/test/suites/encoding-flags/indent-compact-array/input
new file mode 100644 (file)
index 0000000..44e2ace
--- /dev/null
@@ -0,0 +1 @@
+[1, 2]
diff --git a/test/suites/encoding-flags/indent-compact-array/output b/test/suites/encoding-flags/indent-compact-array/output
new file mode 100644 (file)
index 0000000..c57d705
--- /dev/null
@@ -0,0 +1,4 @@
+[
+    1,
+    2
+]
\ No newline at end of file
diff --git a/test/suites/encoding-flags/indent-compact-object/env b/test/suites/encoding-flags/indent-compact-object/env
new file mode 100644 (file)
index 0000000..78fbfcc
--- /dev/null
@@ -0,0 +1,3 @@
+JSON_INDENT=4
+JSON_COMPACT=1
+export JSON_INDENT JSON_COMPACT
diff --git a/test/suites/encoding-flags/indent-compact-object/input b/test/suites/encoding-flags/indent-compact-object/input
new file mode 100644 (file)
index 0000000..062e54f
--- /dev/null
@@ -0,0 +1 @@
+{"a": 1, "b": 2}
diff --git a/test/suites/encoding-flags/indent-compact-object/output b/test/suites/encoding-flags/indent-compact-object/output
new file mode 100644 (file)
index 0000000..9cc4294
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "a":1,
+    "b":2
+}
\ No newline at end of file
diff --git a/test/suites/encoding-flags/indent-object/env b/test/suites/encoding-flags/indent-object/env
new file mode 100644 (file)
index 0000000..d220f83
--- /dev/null
@@ -0,0 +1,2 @@
+JSON_INDENT=4
+export JSON_INDENT
diff --git a/test/suites/encoding-flags/indent-object/input b/test/suites/encoding-flags/indent-object/input
new file mode 100644 (file)
index 0000000..062e54f
--- /dev/null
@@ -0,0 +1 @@
+{"a": 1, "b": 2}
diff --git a/test/suites/encoding-flags/indent-object/output b/test/suites/encoding-flags/indent-object/output
new file mode 100644 (file)
index 0000000..0fbddba
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "a": 1,
+    "b": 2
+}
\ No newline at end of file
diff --git a/test/suites/encoding-flags/object/input b/test/suites/encoding-flags/object/input
new file mode 100644 (file)
index 0000000..062e54f
--- /dev/null
@@ -0,0 +1 @@
+{"a": 1, "b": 2}
diff --git a/test/suites/encoding-flags/object/output b/test/suites/encoding-flags/object/output
new file mode 100644 (file)
index 0000000..ecd219f
--- /dev/null
@@ -0,0 +1 @@
+{"a": 1, "b": 2}
\ No newline at end of file
diff --git a/test/suites/encoding-flags/preserve-order/env b/test/suites/encoding-flags/preserve-order/env
new file mode 100644 (file)
index 0000000..4d9d206
--- /dev/null
@@ -0,0 +1,2 @@
+JSON_PRESERVE_ORDER=1
+export JSON_PRESERVE_ORDER
diff --git a/test/suites/encoding-flags/preserve-order/input b/test/suites/encoding-flags/preserve-order/input
new file mode 100644 (file)
index 0000000..27bcf18
--- /dev/null
@@ -0,0 +1 @@
+{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
diff --git a/test/suites/encoding-flags/preserve-order/output b/test/suites/encoding-flags/preserve-order/output
new file mode 100644 (file)
index 0000000..7a443f6
--- /dev/null
@@ -0,0 +1 @@
+{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
\ No newline at end of file
diff --git a/test/suites/encoding-flags/run b/test/suites/encoding-flags/run
new file mode 100755 (executable)
index 0000000..c49d259
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# 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.
+
+is_test() {
+    test -d $test_path
+}
+
+run_test() {
+    (
+        if [ -f $test_path/env ]; then
+            . $test_path/env
+        fi
+        $json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
+    )
+    valgrind_check $test_log/stderr || return 1
+    cmp -s $test_path/output $test_log/stdout
+}
+
+show_error() {
+    valgrind_show_error && return
+
+    echo "EXPECTED OUTPUT:"
+    nl -bn $test_path/output
+    echo "ACTUAL OUTPUT:"
+    nl -bn $test_log/stdout
+}
+
+. $top_srcdir/test/scripts/run-tests.sh
diff --git a/test/suites/encoding-flags/sort-keys/env b/test/suites/encoding-flags/sort-keys/env
new file mode 100644 (file)
index 0000000..3ef24cb
--- /dev/null
@@ -0,0 +1,2 @@
+JSON_SORT_KEYS=1
+export JSON_SORT_KEYS
diff --git a/test/suites/encoding-flags/sort-keys/input b/test/suites/encoding-flags/sort-keys/input
new file mode 100644 (file)
index 0000000..66951d6
--- /dev/null
@@ -0,0 +1 @@
+{"foo": 1, "bar": 2, "baz": 3, "quux": 4}
diff --git a/test/suites/encoding-flags/sort-keys/output b/test/suites/encoding-flags/sort-keys/output
new file mode 100644 (file)
index 0000000..132d9df
--- /dev/null
@@ -0,0 +1 @@
+{"bar": 2, "baz": 3, "foo": 1, "quux": 4}
\ No newline at end of file
diff --git a/test/suites/invalid-unicode/encoded-surrogate-half/error b/test/suites/invalid-unicode/encoded-surrogate-half/error
new file mode 100644 (file)
index 0000000..762d2c4
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xed near '"'
diff --git a/test/suites/invalid-unicode/encoded-surrogate-half/input b/test/suites/invalid-unicode/encoded-surrogate-half/input
new file mode 100644 (file)
index 0000000..515dd93
--- /dev/null
@@ -0,0 +1 @@
+[" <-- encoded surrogate half"]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-after-backslash/error b/test/suites/invalid-unicode/invalid-utf-8-after-backslash/error
new file mode 100644 (file)
index 0000000..b16dc17
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+unable to decode byte 0xe5 near '"\'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-after-backslash/input b/test/suites/invalid-unicode/invalid-utf-8-after-backslash/input
new file mode 100644 (file)
index 0000000..57c8bee
--- /dev/null
@@ -0,0 +1 @@
+["\å"]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-array/error b/test/suites/invalid-unicode/invalid-utf-8-in-array/error
new file mode 100644 (file)
index 0000000..be15386
--- /dev/null
@@ -0,0 +1,2 @@
+1 1 1
+unable to decode byte 0xe5
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-array/input b/test/suites/invalid-unicode/invalid-utf-8-in-array/input
new file mode 100644 (file)
index 0000000..ebefcd6
--- /dev/null
@@ -0,0 +1 @@
+[å]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/error b/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/error
new file mode 100644 (file)
index 0000000..01b4476
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+unable to decode byte 0xe5 near '123'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/input b/test/suites/invalid-unicode/invalid-utf-8-in-bigger-int/input
new file mode 100644 (file)
index 0000000..e512f9a
--- /dev/null
@@ -0,0 +1 @@
+[123å]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-escape/error b/test/suites/invalid-unicode/invalid-utf-8-in-escape/error
new file mode 100644 (file)
index 0000000..c13583d
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+unable to decode byte 0xe5 near '"\u'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-escape/input b/test/suites/invalid-unicode/invalid-utf-8-in-escape/input
new file mode 100644 (file)
index 0000000..2b271b8
--- /dev/null
@@ -0,0 +1 @@
+["\uå"]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-exponent/error b/test/suites/invalid-unicode/invalid-utf-8-in-exponent/error
new file mode 100644 (file)
index 0000000..c7b20b7
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+unable to decode byte 0xe5 near '1e1'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-exponent/input b/test/suites/invalid-unicode/invalid-utf-8-in-exponent/input
new file mode 100644 (file)
index 0000000..d8e83c5
--- /dev/null
@@ -0,0 +1 @@
+[1e1å]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-identifier/error b/test/suites/invalid-unicode/invalid-utf-8-in-identifier/error
new file mode 100644 (file)
index 0000000..33dfc23
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xe5 near 'a'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-identifier/input b/test/suites/invalid-unicode/invalid-utf-8-in-identifier/input
new file mode 100644 (file)
index 0000000..ef03851
--- /dev/null
@@ -0,0 +1 @@
+[aå]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-int/error b/test/suites/invalid-unicode/invalid-utf-8-in-int/error
new file mode 100644 (file)
index 0000000..8f08970
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xe5 near '0'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-int/input b/test/suites/invalid-unicode/invalid-utf-8-in-int/input
new file mode 100644 (file)
index 0000000..371226e
--- /dev/null
@@ -0,0 +1 @@
+[0å]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/error b/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/error
new file mode 100644 (file)
index 0000000..b7660e3
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+unable to decode byte 0xe5 near '1e'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/input b/test/suites/invalid-unicode/invalid-utf-8-in-real-after-e/input
new file mode 100644 (file)
index 0000000..17fc29c
--- /dev/null
@@ -0,0 +1 @@
+[1eå]
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-string/error b/test/suites/invalid-unicode/invalid-utf-8-in-string/error
new file mode 100644 (file)
index 0000000..0b7039a
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xe5 near '"'
diff --git a/test/suites/invalid-unicode/invalid-utf-8-in-string/input b/test/suites/invalid-unicode/invalid-utf-8-in-string/input
new file mode 100644 (file)
index 0000000..00b79c0
--- /dev/null
@@ -0,0 +1 @@
+["å <-- invalid UTF-8"]
diff --git a/test/suites/invalid-unicode/lone-invalid-utf-8/error b/test/suites/invalid-unicode/lone-invalid-utf-8/error
new file mode 100644 (file)
index 0000000..8e9a511
--- /dev/null
@@ -0,0 +1,2 @@
+1 0 0
+unable to decode byte 0xe5
diff --git a/test/suites/invalid-unicode/lone-invalid-utf-8/input b/test/suites/invalid-unicode/lone-invalid-utf-8/input
new file mode 100644 (file)
index 0000000..eb80796
--- /dev/null
@@ -0,0 +1 @@
diff --git a/test/suites/invalid-unicode/lone-utf-8-continuation-byte/error b/test/suites/invalid-unicode/lone-utf-8-continuation-byte/error
new file mode 100644 (file)
index 0000000..86bbad3
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0x81 near '"'
diff --git a/test/suites/invalid-unicode/lone-utf-8-continuation-byte/input b/test/suites/invalid-unicode/lone-utf-8-continuation-byte/input
new file mode 100644 (file)
index 0000000..62a26b6
--- /dev/null
@@ -0,0 +1 @@
+["\81"]
diff --git a/test/suites/invalid-unicode/not-in-unicode-range/error b/test/suites/invalid-unicode/not-in-unicode-range/error
new file mode 100644 (file)
index 0000000..d07ccb3
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xf4 near '"'
diff --git a/test/suites/invalid-unicode/not-in-unicode-range/input b/test/suites/invalid-unicode/not-in-unicode-range/input
new file mode 100644 (file)
index 0000000..1216186
--- /dev/null
@@ -0,0 +1 @@
+[""]
diff --git a/test/suites/invalid-unicode/overlong-3-byte-encoding/error b/test/suites/invalid-unicode/overlong-3-byte-encoding/error
new file mode 100644 (file)
index 0000000..8a05aba
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xe0 near '"'
diff --git a/test/suites/invalid-unicode/overlong-3-byte-encoding/input b/test/suites/invalid-unicode/overlong-3-byte-encoding/input
new file mode 100644 (file)
index 0000000..0bf909f
--- /dev/null
@@ -0,0 +1 @@
+["à\80¢ <-- overlong encoding"]
diff --git a/test/suites/invalid-unicode/overlong-4-byte-encoding/error b/test/suites/invalid-unicode/overlong-4-byte-encoding/error
new file mode 100644 (file)
index 0000000..7e19c5f
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xf0 near '"'
diff --git a/test/suites/invalid-unicode/overlong-4-byte-encoding/input b/test/suites/invalid-unicode/overlong-4-byte-encoding/input
new file mode 100644 (file)
index 0000000..c6b6313
--- /dev/null
@@ -0,0 +1 @@
+["ð\80\80¢ <-- overlong encoding"]
diff --git a/test/suites/invalid-unicode/overlong-ascii-encoding/error b/test/suites/invalid-unicode/overlong-ascii-encoding/error
new file mode 100644 (file)
index 0000000..1d382ed
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xc1 near '"'
diff --git a/test/suites/invalid-unicode/overlong-ascii-encoding/input b/test/suites/invalid-unicode/overlong-ascii-encoding/input
new file mode 100644 (file)
index 0000000..ef6e10a
--- /dev/null
@@ -0,0 +1 @@
+["Á"]
diff --git a/test/suites/invalid-unicode/restricted-utf-8/error b/test/suites/invalid-unicode/restricted-utf-8/error
new file mode 100644 (file)
index 0000000..d018f5f
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xfd near '"'
diff --git a/test/suites/invalid-unicode/restricted-utf-8/input b/test/suites/invalid-unicode/restricted-utf-8/input
new file mode 100644 (file)
index 0000000..ba60170
--- /dev/null
@@ -0,0 +1 @@
+["ý"]
diff --git a/test/suites/invalid-unicode/run b/test/suites/invalid-unicode/run
new file mode 100755 (executable)
index 0000000..0565584
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# 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.
+
+is_test() {
+    test -d $test_path
+}
+
+run_test() {
+    $json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
+    valgrind_check $test_log/stderr || return 1
+    cmp -s $test_path/error $test_log/stderr
+}
+
+show_error() {
+    valgrind_show_error && return
+
+    echo "EXPECTED ERROR:"
+    nl -bn $test_path/error
+    echo "ACTUAL ERROR:"
+    nl -bn $test_log/stderr
+}
+
+. $top_srcdir/test/scripts/run-tests.sh
diff --git a/test/suites/invalid-unicode/truncated-utf-8/error b/test/suites/invalid-unicode/truncated-utf-8/error
new file mode 100644 (file)
index 0000000..8a05aba
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unable to decode byte 0xe0 near '"'
diff --git a/test/suites/invalid-unicode/truncated-utf-8/input b/test/suites/invalid-unicode/truncated-utf-8/input
new file mode 100644 (file)
index 0000000..bce9e18
--- /dev/null
@@ -0,0 +1 @@
+["àÿ <-- truncated UTF-8"]
diff --git a/test/suites/invalid/apostrophe/error b/test/suites/invalid/apostrophe/error
new file mode 100644 (file)
index 0000000..79bb2a0
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+invalid token near '''
diff --git a/test/suites/invalid/apostrophe/input b/test/suites/invalid/apostrophe/input
new file mode 100644 (file)
index 0000000..f2dd4d2
--- /dev/null
@@ -0,0 +1 @@
+['
diff --git a/test/suites/invalid/ascii-unicode-identifier/error b/test/suites/invalid/ascii-unicode-identifier/error
new file mode 100644 (file)
index 0000000..a4d8142
--- /dev/null
@@ -0,0 +1,2 @@
+1 1 1
+'[' or '{' expected near 'a'
diff --git a/test/suites/invalid/ascii-unicode-identifier/input b/test/suites/invalid/ascii-unicode-identifier/input
new file mode 100644 (file)
index 0000000..c2c0208
--- /dev/null
@@ -0,0 +1 @@
+aå
diff --git a/test/suites/invalid/brace-comma/error b/test/suites/invalid/brace-comma/error
new file mode 100644 (file)
index 0000000..ce04621
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+string or '}' expected near ','
diff --git a/test/suites/invalid/brace-comma/input b/test/suites/invalid/brace-comma/input
new file mode 100644 (file)
index 0000000..74a6628
--- /dev/null
@@ -0,0 +1 @@
+{,
diff --git a/test/suites/invalid/bracket-comma/error b/test/suites/invalid/bracket-comma/error
new file mode 100644 (file)
index 0000000..ce0a912
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unexpected token near ','
diff --git a/test/suites/invalid/bracket-comma/input b/test/suites/invalid/bracket-comma/input
new file mode 100644 (file)
index 0000000..5b911f1
--- /dev/null
@@ -0,0 +1 @@
+[,
diff --git a/test/suites/invalid/bracket-one-comma/error.normal b/test/suites/invalid/bracket-one-comma/error.normal
new file mode 100644 (file)
index 0000000..0248b11
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 4
+']' expected near end of file
diff --git a/test/suites/invalid/bracket-one-comma/error.strip b/test/suites/invalid/bracket-one-comma/error.strip
new file mode 100644 (file)
index 0000000..f89b38f
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+']' expected near end of file
diff --git a/test/suites/invalid/bracket-one-comma/input b/test/suites/invalid/bracket-one-comma/input
new file mode 100644 (file)
index 0000000..874691b
--- /dev/null
@@ -0,0 +1 @@
+[1,
diff --git a/test/suites/invalid/empty/error b/test/suites/invalid/empty/error
new file mode 100644 (file)
index 0000000..f45da6f
--- /dev/null
@@ -0,0 +1,2 @@
+1 0 0
+'[' or '{' expected near end of file
diff --git a/test/suites/invalid/empty/input b/test/suites/invalid/empty/input
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/suites/invalid/escaped-null-byte-in-string/error b/test/suites/invalid/escaped-null-byte-in-string/error
new file mode 100644 (file)
index 0000000..9795f18
--- /dev/null
@@ -0,0 +1,2 @@
+1 33 33
+\u0000 is not allowed
diff --git a/test/suites/invalid/escaped-null-byte-in-string/input b/test/suites/invalid/escaped-null-byte-in-string/input
new file mode 100644 (file)
index 0000000..22ae82b
--- /dev/null
@@ -0,0 +1 @@
+["\u0000 (null byte not allowed)"]
diff --git a/test/suites/invalid/extra-comma-in-array/error b/test/suites/invalid/extra-comma-in-array/error
new file mode 100644 (file)
index 0000000..cae86c2
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+unexpected token near ']'
diff --git a/test/suites/invalid/extra-comma-in-array/input b/test/suites/invalid/extra-comma-in-array/input
new file mode 100644 (file)
index 0000000..e8b1a17
--- /dev/null
@@ -0,0 +1 @@
+[1,]
diff --git a/test/suites/invalid/extra-comma-in-multiline-array/error b/test/suites/invalid/extra-comma-in-multiline-array/error
new file mode 100644 (file)
index 0000000..5baeea4
--- /dev/null
@@ -0,0 +1,2 @@
+6 1 17
+unexpected token near ']'
diff --git a/test/suites/invalid/extra-comma-in-multiline-array/input b/test/suites/invalid/extra-comma-in-multiline-array/input
new file mode 100644 (file)
index 0000000..bcb2a75
--- /dev/null
@@ -0,0 +1,6 @@
+[1,
+2,
+3,
+4,
+5,
+]
diff --git a/test/suites/invalid/garbage-after-newline/error b/test/suites/invalid/garbage-after-newline/error
new file mode 100644 (file)
index 0000000..5d2dec3
--- /dev/null
@@ -0,0 +1,2 @@
+2 3 11
+end of file expected near 'foo'
diff --git a/test/suites/invalid/garbage-after-newline/input b/test/suites/invalid/garbage-after-newline/input
new file mode 100644 (file)
index 0000000..3614ac7
--- /dev/null
@@ -0,0 +1,2 @@
+[1,2,3]
+foo
diff --git a/test/suites/invalid/garbage-at-the-end/error b/test/suites/invalid/garbage-at-the-end/error
new file mode 100644 (file)
index 0000000..cdd8175
--- /dev/null
@@ -0,0 +1,2 @@
+1 10 10
+end of file expected near 'foo'
diff --git a/test/suites/invalid/garbage-at-the-end/input b/test/suites/invalid/garbage-at-the-end/input
new file mode 100644 (file)
index 0000000..55aee53
--- /dev/null
@@ -0,0 +1 @@
+[1,2,3]foo
diff --git a/test/suites/invalid/integer-starting-with-zero/error b/test/suites/invalid/integer-starting-with-zero/error
new file mode 100644 (file)
index 0000000..64e0536
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+invalid token near '0'
diff --git a/test/suites/invalid/integer-starting-with-zero/input b/test/suites/invalid/integer-starting-with-zero/input
new file mode 100644 (file)
index 0000000..12f67e2
--- /dev/null
@@ -0,0 +1 @@
+[012]
diff --git a/test/suites/invalid/invalid-escape/error b/test/suites/invalid/invalid-escape/error
new file mode 100644 (file)
index 0000000..d9863f7
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+invalid escape near '"\a'
diff --git a/test/suites/invalid/invalid-escape/input b/test/suites/invalid/invalid-escape/input
new file mode 100644 (file)
index 0000000..64c7b70
--- /dev/null
@@ -0,0 +1 @@
+["\a <-- invalid escape"]
diff --git a/test/suites/invalid/invalid-identifier/error b/test/suites/invalid/invalid-identifier/error
new file mode 100644 (file)
index 0000000..496c6ab
--- /dev/null
@@ -0,0 +1,2 @@
+1 5 5
+invalid token near 'troo'
diff --git a/test/suites/invalid/invalid-identifier/input b/test/suites/invalid/invalid-identifier/input
new file mode 100644 (file)
index 0000000..3d2860d
--- /dev/null
@@ -0,0 +1 @@
+[troo
diff --git a/test/suites/invalid/invalid-negative-integer/error b/test/suites/invalid/invalid-negative-integer/error
new file mode 100644 (file)
index 0000000..f2526c5
--- /dev/null
@@ -0,0 +1,2 @@
+1 8 8
+']' expected near 'foo'
diff --git a/test/suites/invalid/invalid-negative-integer/input b/test/suites/invalid/invalid-negative-integer/input
new file mode 100644 (file)
index 0000000..6196980
--- /dev/null
@@ -0,0 +1 @@
+[-123foo]
diff --git a/test/suites/invalid/invalid-negative-real/error b/test/suites/invalid/invalid-negative-real/error
new file mode 100644 (file)
index 0000000..933158a
--- /dev/null
@@ -0,0 +1,2 @@
+1 12 12
+']' expected near 'foo'
diff --git a/test/suites/invalid/invalid-negative-real/input b/test/suites/invalid/invalid-negative-real/input
new file mode 100644 (file)
index 0000000..3c763d3
--- /dev/null
@@ -0,0 +1 @@
+[-123.123foo]
diff --git a/test/suites/invalid/invalid-second-surrogate/error b/test/suites/invalid/invalid-second-surrogate/error
new file mode 100644 (file)
index 0000000..e5a2359
--- /dev/null
@@ -0,0 +1,2 @@
+1 62 62
+invalid Unicode '\uD888\u3210'
diff --git a/test/suites/invalid/invalid-second-surrogate/input b/test/suites/invalid/invalid-second-surrogate/input
new file mode 100644 (file)
index 0000000..b21453f
--- /dev/null
@@ -0,0 +1 @@
+["\uD888\u3210 (first surrogate and invalid second surrogate)"]
diff --git a/test/suites/invalid/lone-open-brace/error.normal b/test/suites/invalid/lone-open-brace/error.normal
new file mode 100644 (file)
index 0000000..00dc765
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 2
+string or '}' expected near end of file
diff --git a/test/suites/invalid/lone-open-brace/error.strip b/test/suites/invalid/lone-open-brace/error.strip
new file mode 100644 (file)
index 0000000..bb1c047
--- /dev/null
@@ -0,0 +1,2 @@
+1 1 1
+string or '}' expected near end of file
diff --git a/test/suites/invalid/lone-open-brace/input b/test/suites/invalid/lone-open-brace/input
new file mode 100644 (file)
index 0000000..98232c6
--- /dev/null
@@ -0,0 +1 @@
+{
diff --git a/test/suites/invalid/lone-open-bracket/error.normal b/test/suites/invalid/lone-open-bracket/error.normal
new file mode 100644 (file)
index 0000000..f463928
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 2
+']' expected near end of file
diff --git a/test/suites/invalid/lone-open-bracket/error.strip b/test/suites/invalid/lone-open-bracket/error.strip
new file mode 100644 (file)
index 0000000..2bc07ea
--- /dev/null
@@ -0,0 +1,2 @@
+1 1 1
+']' expected near end of file
diff --git a/test/suites/invalid/lone-open-bracket/input b/test/suites/invalid/lone-open-bracket/input
new file mode 100644 (file)
index 0000000..558ed37
--- /dev/null
@@ -0,0 +1 @@
+[
diff --git a/test/suites/invalid/lone-second-surrogate/error b/test/suites/invalid/lone-second-surrogate/error
new file mode 100644 (file)
index 0000000..bc5f34e
--- /dev/null
@@ -0,0 +1,2 @@
+1 40 40
+invalid Unicode '\uDFAA'
diff --git a/test/suites/invalid/lone-second-surrogate/input b/test/suites/invalid/lone-second-surrogate/input
new file mode 100644 (file)
index 0000000..328e35c
--- /dev/null
@@ -0,0 +1 @@
+["\uDFAA (second surrogate on it's own)"]
diff --git a/test/suites/invalid/minus-sign-without-number/error b/test/suites/invalid/minus-sign-without-number/error
new file mode 100644 (file)
index 0000000..b3a78b9
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+invalid token near '-'
diff --git a/test/suites/invalid/minus-sign-without-number/input b/test/suites/invalid/minus-sign-without-number/input
new file mode 100644 (file)
index 0000000..0337883
--- /dev/null
@@ -0,0 +1 @@
+[-foo]
diff --git a/test/suites/invalid/negative-integer-starting-with-zero/error b/test/suites/invalid/negative-integer-starting-with-zero/error
new file mode 100644 (file)
index 0000000..36adc34
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+invalid token near '-0'
diff --git a/test/suites/invalid/negative-integer-starting-with-zero/input b/test/suites/invalid/negative-integer-starting-with-zero/input
new file mode 100644 (file)
index 0000000..6fbb7a2
--- /dev/null
@@ -0,0 +1 @@
+[-012]
diff --git a/test/suites/invalid/null-byte-in-string/error b/test/suites/invalid/null-byte-in-string/error
new file mode 100644 (file)
index 0000000..45f9bd8
--- /dev/null
@@ -0,0 +1,2 @@
+1 12 12
+control character 0x0 near '"null byte '
diff --git a/test/suites/invalid/null-byte-in-string/input b/test/suites/invalid/null-byte-in-string/input
new file mode 100644 (file)
index 0000000..268d1f1
Binary files /dev/null and b/test/suites/invalid/null-byte-in-string/input differ
diff --git a/test/suites/invalid/null-byte-in-string/nostrip b/test/suites/invalid/null-byte-in-string/nostrip
new file mode 100644 (file)
index 0000000..80f4bf7
--- /dev/null
@@ -0,0 +1,2 @@
+The embedded NULL byte breaks json_loads(), which is used instead of
+json_loadf() in the stripped tests.
diff --git a/test/suites/invalid/null-byte-outside-string/error b/test/suites/invalid/null-byte-outside-string/error
new file mode 100644 (file)
index 0000000..44d4def
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+invalid token near end of file
diff --git a/test/suites/invalid/null-byte-outside-string/input b/test/suites/invalid/null-byte-outside-string/input
new file mode 100644 (file)
index 0000000..aa550eb
Binary files /dev/null and b/test/suites/invalid/null-byte-outside-string/input differ
diff --git a/test/suites/invalid/null-byte-outside-string/nostrip b/test/suites/invalid/null-byte-outside-string/nostrip
new file mode 100644 (file)
index 0000000..80f4bf7
--- /dev/null
@@ -0,0 +1,2 @@
+The embedded NULL byte breaks json_loads(), which is used instead of
+json_loadf() in the stripped tests.
diff --git a/test/suites/invalid/null/error b/test/suites/invalid/null/error
new file mode 100644 (file)
index 0000000..1f5d464
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+'[' or '{' expected near 'null'
diff --git a/test/suites/invalid/null/input b/test/suites/invalid/null/input
new file mode 100644 (file)
index 0000000..19765bd
--- /dev/null
@@ -0,0 +1 @@
+null
diff --git a/test/suites/invalid/object-apostrophes/error b/test/suites/invalid/object-apostrophes/error
new file mode 100644 (file)
index 0000000..23fab01
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+string or '}' expected near '''
diff --git a/test/suites/invalid/object-apostrophes/input b/test/suites/invalid/object-apostrophes/input
new file mode 100644 (file)
index 0000000..52b2905
--- /dev/null
@@ -0,0 +1 @@
+{'a'
diff --git a/test/suites/invalid/object-garbage-at-end/error b/test/suites/invalid/object-garbage-at-end/error
new file mode 100644 (file)
index 0000000..06c4ec1
--- /dev/null
@@ -0,0 +1,2 @@
+1 12 12
+'}' expected near '123'
diff --git a/test/suites/invalid/object-garbage-at-end/input b/test/suites/invalid/object-garbage-at-end/input
new file mode 100644 (file)
index 0000000..62c19d7
--- /dev/null
@@ -0,0 +1 @@
+{"a":"a" 123}
diff --git a/test/suites/invalid/object-in-unterminated-array/error.normal b/test/suites/invalid/object-in-unterminated-array/error.normal
new file mode 100644 (file)
index 0000000..0248b11
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 4
+']' expected near end of file
diff --git a/test/suites/invalid/object-in-unterminated-array/error.strip b/test/suites/invalid/object-in-unterminated-array/error.strip
new file mode 100644 (file)
index 0000000..f89b38f
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+']' expected near end of file
diff --git a/test/suites/invalid/object-in-unterminated-array/input b/test/suites/invalid/object-in-unterminated-array/input
new file mode 100644 (file)
index 0000000..ca9ec37
--- /dev/null
@@ -0,0 +1 @@
+[{}
diff --git a/test/suites/invalid/object-no-colon/error.normal b/test/suites/invalid/object-no-colon/error.normal
new file mode 100644 (file)
index 0000000..78d84f7
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 5
+':' expected near end of file
diff --git a/test/suites/invalid/object-no-colon/error.strip b/test/suites/invalid/object-no-colon/error.strip
new file mode 100644 (file)
index 0000000..528e266
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+':' expected near end of file
diff --git a/test/suites/invalid/object-no-colon/input b/test/suites/invalid/object-no-colon/input
new file mode 100644 (file)
index 0000000..107e626
--- /dev/null
@@ -0,0 +1 @@
+{"a"
diff --git a/test/suites/invalid/object-no-value/error.normal b/test/suites/invalid/object-no-value/error.normal
new file mode 100644 (file)
index 0000000..47ad902
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 6
+unexpected token near end of file
diff --git a/test/suites/invalid/object-no-value/error.strip b/test/suites/invalid/object-no-value/error.strip
new file mode 100644 (file)
index 0000000..b36c5e2
--- /dev/null
@@ -0,0 +1,2 @@
+1 5 5
+unexpected token near end of file
diff --git a/test/suites/invalid/object-no-value/input b/test/suites/invalid/object-no-value/input
new file mode 100644 (file)
index 0000000..f68f262
--- /dev/null
@@ -0,0 +1 @@
+{"a":
diff --git a/test/suites/invalid/object-unterminated-value/error.normal b/test/suites/invalid/object-unterminated-value/error.normal
new file mode 100644 (file)
index 0000000..2ad76d4
--- /dev/null
@@ -0,0 +1,2 @@
+1 7 7
+unexpected newline near '"a'
diff --git a/test/suites/invalid/object-unterminated-value/error.strip b/test/suites/invalid/object-unterminated-value/error.strip
new file mode 100644 (file)
index 0000000..385afb5
--- /dev/null
@@ -0,0 +1,2 @@
+1 7 7
+premature end of input near '"a'
diff --git a/test/suites/invalid/object-unterminated-value/input b/test/suites/invalid/object-unterminated-value/input
new file mode 100644 (file)
index 0000000..b854d7e
--- /dev/null
@@ -0,0 +1 @@
+{"a":"a
diff --git a/test/suites/invalid/real-garbage-after-e/error b/test/suites/invalid/real-garbage-after-e/error
new file mode 100644 (file)
index 0000000..b40ffa9
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+invalid token near '1e'
diff --git a/test/suites/invalid/real-garbage-after-e/input b/test/suites/invalid/real-garbage-after-e/input
new file mode 100644 (file)
index 0000000..6a945ac
--- /dev/null
@@ -0,0 +1 @@
+[1ea]
diff --git a/test/suites/invalid/real-negative-overflow/error b/test/suites/invalid/real-negative-overflow/error
new file mode 100644 (file)
index 0000000..d7f8e41
--- /dev/null
@@ -0,0 +1,2 @@
+1 15 15
+real number overflow near '-123123e100000'
diff --git a/test/suites/invalid/real-negative-overflow/input b/test/suites/invalid/real-negative-overflow/input
new file mode 100644 (file)
index 0000000..b5bd21c
--- /dev/null
@@ -0,0 +1 @@
+[-123123e100000]
diff --git a/test/suites/invalid/real-positive-overflow/error b/test/suites/invalid/real-positive-overflow/error
new file mode 100644 (file)
index 0000000..55883c9
--- /dev/null
@@ -0,0 +1,2 @@
+1 14 14
+real number overflow near '123123e100000'
diff --git a/test/suites/invalid/real-positive-overflow/input b/test/suites/invalid/real-positive-overflow/input
new file mode 100644 (file)
index 0000000..524e53b
--- /dev/null
@@ -0,0 +1 @@
+[123123e100000]
diff --git a/test/suites/invalid/real-truncated-at-e/error b/test/suites/invalid/real-truncated-at-e/error
new file mode 100644 (file)
index 0000000..b40ffa9
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+invalid token near '1e'
diff --git a/test/suites/invalid/real-truncated-at-e/input b/test/suites/invalid/real-truncated-at-e/input
new file mode 100644 (file)
index 0000000..1d67b7b
--- /dev/null
@@ -0,0 +1 @@
+[1e]
diff --git a/test/suites/invalid/real-truncated-at-point/error b/test/suites/invalid/real-truncated-at-point/error
new file mode 100644 (file)
index 0000000..db972e8
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+invalid token near '1.'
diff --git a/test/suites/invalid/real-truncated-at-point/input b/test/suites/invalid/real-truncated-at-point/input
new file mode 100644 (file)
index 0000000..b652b3f
--- /dev/null
@@ -0,0 +1 @@
+[1.]
diff --git a/test/suites/invalid/run b/test/suites/invalid/run
new file mode 100755 (executable)
index 0000000..d15eba3
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# 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.
+
+is_test() {
+    test -d $test_path
+}
+
+do_run() {
+    variant=$1
+    s=".$1"
+
+    strip=0
+    if [ "$variant" = "strip" ]; then
+        # This test should not be stripped
+        [ -f $test_path/nostrip ] && return
+        strip=1
+    fi
+
+    STRIP=$strip $json_process \
+        <$test_path/input >$test_log/stdout$s 2>$test_log/stderr$s
+    valgrind_check $test_log/stderr$s || return 1
+
+    ref=error
+    [ -f $test_path/error$s ] && ref=error$s
+
+    if ! cmp -s $test_path/$ref $test_log/stderr$s; then
+        echo $variant > $test_log/variant
+        return 1
+    fi
+}
+
+run_test() {
+    do_run normal && do_run strip
+}
+
+show_error() {
+    valgrind_show_error && return
+
+    read variant < $test_log/variant
+    s=".$variant"
+
+    echo "VARIANT: $variant"
+
+    echo "EXPECTED ERROR:"
+    ref=error
+    [ -f $test_path/error$s ] && ref=error$s
+    nl -bn $test_path/$ref
+
+    echo "ACTUAL ERROR:"
+    nl -bn $test_log/stderr$s
+}
+
+. $top_srcdir/test/scripts/run-tests.sh
diff --git a/test/suites/invalid/tab-character-in-string/error b/test/suites/invalid/tab-character-in-string/error
new file mode 100644 (file)
index 0000000..9e2f76e
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+control character 0x9 near '"'
diff --git a/test/suites/invalid/tab-character-in-string/input b/test/suites/invalid/tab-character-in-string/input
new file mode 100644 (file)
index 0000000..3ebae09
--- /dev/null
@@ -0,0 +1 @@
+["      <-- tab character"]
diff --git a/test/suites/invalid/too-big-negative-integer/error b/test/suites/invalid/too-big-negative-integer/error
new file mode 100644 (file)
index 0000000..a0640b9
--- /dev/null
@@ -0,0 +1,2 @@
+1 32 32
+too big negative integer
diff --git a/test/suites/invalid/too-big-negative-integer/input b/test/suites/invalid/too-big-negative-integer/input
new file mode 100644 (file)
index 0000000..d6c26f1
--- /dev/null
@@ -0,0 +1 @@
+[-123123123123123123123123123123]
diff --git a/test/suites/invalid/too-big-positive-integer/error b/test/suites/invalid/too-big-positive-integer/error
new file mode 100644 (file)
index 0000000..3bdbefd
--- /dev/null
@@ -0,0 +1,2 @@
+1 31 31
+too big integer
diff --git a/test/suites/invalid/too-big-positive-integer/input b/test/suites/invalid/too-big-positive-integer/input
new file mode 100644 (file)
index 0000000..27c8553
--- /dev/null
@@ -0,0 +1 @@
+[123123123123123123123123123123]
diff --git a/test/suites/invalid/truncated-unicode-surrogate/error b/test/suites/invalid/truncated-unicode-surrogate/error
new file mode 100644 (file)
index 0000000..1b99f06
--- /dev/null
@@ -0,0 +1,2 @@
+1 46 46
+invalid Unicode '\uDADA'
diff --git a/test/suites/invalid/truncated-unicode-surrogate/input b/test/suites/invalid/truncated-unicode-surrogate/input
new file mode 100644 (file)
index 0000000..2b340f4
--- /dev/null
@@ -0,0 +1 @@
+["\uDADA (first surrogate without the second)"]
diff --git a/test/suites/invalid/unicode-identifier/error b/test/suites/invalid/unicode-identifier/error
new file mode 100644 (file)
index 0000000..178b0dd
--- /dev/null
@@ -0,0 +1,2 @@
+1 1 2
+'[' or '{' expected near 'å'
diff --git a/test/suites/invalid/unicode-identifier/input b/test/suites/invalid/unicode-identifier/input
new file mode 100644 (file)
index 0000000..aad321c
--- /dev/null
@@ -0,0 +1 @@
diff --git a/test/suites/invalid/unterminated-array-and-object/error.normal b/test/suites/invalid/unterminated-array-and-object/error.normal
new file mode 100644 (file)
index 0000000..5b19804
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 3
+string or '}' expected near end of file
diff --git a/test/suites/invalid/unterminated-array-and-object/error.strip b/test/suites/invalid/unterminated-array-and-object/error.strip
new file mode 100644 (file)
index 0000000..da2bb22
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+string or '}' expected near end of file
diff --git a/test/suites/invalid/unterminated-array-and-object/input b/test/suites/invalid/unterminated-array-and-object/input
new file mode 100644 (file)
index 0000000..cd9dc64
--- /dev/null
@@ -0,0 +1 @@
+[{
diff --git a/test/suites/invalid/unterminated-array/error.normal b/test/suites/invalid/unterminated-array/error.normal
new file mode 100644 (file)
index 0000000..8025ed1
--- /dev/null
@@ -0,0 +1,2 @@
+2 0 5
+']' expected near end of file
diff --git a/test/suites/invalid/unterminated-array/error.strip b/test/suites/invalid/unterminated-array/error.strip
new file mode 100644 (file)
index 0000000..495d0f7
--- /dev/null
@@ -0,0 +1,2 @@
+1 4 4
+']' expected near end of file
diff --git a/test/suites/invalid/unterminated-array/input b/test/suites/invalid/unterminated-array/input
new file mode 100644 (file)
index 0000000..727ee81
--- /dev/null
@@ -0,0 +1 @@
+["a"
diff --git a/test/suites/invalid/unterminated-empty-key/error.normal b/test/suites/invalid/unterminated-empty-key/error.normal
new file mode 100644 (file)
index 0000000..3d646ab
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+unexpected newline near '"'
diff --git a/test/suites/invalid/unterminated-empty-key/error.strip b/test/suites/invalid/unterminated-empty-key/error.strip
new file mode 100644 (file)
index 0000000..94f1947
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+premature end of input near '"'
diff --git a/test/suites/invalid/unterminated-empty-key/input b/test/suites/invalid/unterminated-empty-key/input
new file mode 100644 (file)
index 0000000..4117452
--- /dev/null
@@ -0,0 +1 @@
+{"
diff --git a/test/suites/invalid/unterminated-key/error.normal b/test/suites/invalid/unterminated-key/error.normal
new file mode 100644 (file)
index 0000000..5f09b77
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+unexpected newline near '"a'
diff --git a/test/suites/invalid/unterminated-key/error.strip b/test/suites/invalid/unterminated-key/error.strip
new file mode 100644 (file)
index 0000000..8b6bec4
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+premature end of input near '"a'
diff --git a/test/suites/invalid/unterminated-key/input b/test/suites/invalid/unterminated-key/input
new file mode 100644 (file)
index 0000000..705948c
--- /dev/null
@@ -0,0 +1 @@
+{"a
diff --git a/test/suites/invalid/unterminated-object-and-array/error b/test/suites/invalid/unterminated-object-and-array/error
new file mode 100644 (file)
index 0000000..ed97be7
--- /dev/null
@@ -0,0 +1,2 @@
+1 2 2
+string or '}' expected near '['
diff --git a/test/suites/invalid/unterminated-object-and-array/input b/test/suites/invalid/unterminated-object-and-array/input
new file mode 100644 (file)
index 0000000..da35a86
--- /dev/null
@@ -0,0 +1 @@
+{[
diff --git a/test/suites/invalid/unterminated-string/error.normal b/test/suites/invalid/unterminated-string/error.normal
new file mode 100644 (file)
index 0000000..5f09b77
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+unexpected newline near '"a'
diff --git a/test/suites/invalid/unterminated-string/error.strip b/test/suites/invalid/unterminated-string/error.strip
new file mode 100644 (file)
index 0000000..8b6bec4
--- /dev/null
@@ -0,0 +1,2 @@
+1 3 3
+premature end of input near '"a'
diff --git a/test/suites/invalid/unterminated-string/input b/test/suites/invalid/unterminated-string/input
new file mode 100644 (file)
index 0000000..38ab6b0
--- /dev/null
@@ -0,0 +1 @@
+["a
diff --git a/test/suites/valid/complex-array/input b/test/suites/valid/complex-array/input
new file mode 100644 (file)
index 0000000..1b9bbb9
--- /dev/null
@@ -0,0 +1,5 @@
+[1,2,3,4,
+"a", "b", "c",
+{"foo": "bar", "core": "dump"},
+true, false, true, true, null, false
+]
diff --git a/test/suites/valid/complex-array/output b/test/suites/valid/complex-array/output
new file mode 100644 (file)
index 0000000..7aefe56
--- /dev/null
@@ -0,0 +1 @@
+[1, 2, 3, 4, "a", "b", "c", {"core": "dump", "foo": "bar"}, true, false, true, true, null, false]
\ No newline at end of file
diff --git a/test/suites/valid/empty-array/input b/test/suites/valid/empty-array/input
new file mode 100644 (file)
index 0000000..fe51488
--- /dev/null
@@ -0,0 +1 @@
+[]
diff --git a/test/suites/valid/empty-array/output b/test/suites/valid/empty-array/output
new file mode 100644 (file)
index 0000000..0637a08
--- /dev/null
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/test/suites/valid/empty-object-in-array/input b/test/suites/valid/empty-object-in-array/input
new file mode 100644 (file)
index 0000000..93d5140
--- /dev/null
@@ -0,0 +1 @@
+[{}]
diff --git a/test/suites/valid/empty-object-in-array/output b/test/suites/valid/empty-object-in-array/output
new file mode 100644 (file)
index 0000000..ee1aac4
--- /dev/null
@@ -0,0 +1 @@
+[{}]
\ No newline at end of file
diff --git a/test/suites/valid/empty-object/input b/test/suites/valid/empty-object/input
new file mode 100644 (file)
index 0000000..0967ef4
--- /dev/null
@@ -0,0 +1 @@
+{}
diff --git a/test/suites/valid/empty-object/output b/test/suites/valid/empty-object/output
new file mode 100644 (file)
index 0000000..9e26dfe
--- /dev/null
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/test/suites/valid/empty-string/input b/test/suites/valid/empty-string/input
new file mode 100644 (file)
index 0000000..66a1e18
--- /dev/null
@@ -0,0 +1 @@
+[""]
diff --git a/test/suites/valid/empty-string/output b/test/suites/valid/empty-string/output
new file mode 100644 (file)
index 0000000..93b6be2
--- /dev/null
@@ -0,0 +1 @@
+[""]
\ No newline at end of file
diff --git a/test/suites/valid/escaped-utf-control-char/input b/test/suites/valid/escaped-utf-control-char/input
new file mode 100644 (file)
index 0000000..9a98545
--- /dev/null
@@ -0,0 +1 @@
+["\u0012 escaped control character"]
diff --git a/test/suites/valid/escaped-utf-control-char/output b/test/suites/valid/escaped-utf-control-char/output
new file mode 100644 (file)
index 0000000..07221b7
--- /dev/null
@@ -0,0 +1 @@
+["\u0012 escaped control character"]
\ No newline at end of file
diff --git a/test/suites/valid/false/input b/test/suites/valid/false/input
new file mode 100644 (file)
index 0000000..4343652
--- /dev/null
@@ -0,0 +1 @@
+[false]
diff --git a/test/suites/valid/false/output b/test/suites/valid/false/output
new file mode 100644 (file)
index 0000000..67b2f07
--- /dev/null
@@ -0,0 +1 @@
+[false]
\ No newline at end of file
diff --git a/test/suites/valid/negative-int/input b/test/suites/valid/negative-int/input
new file mode 100644 (file)
index 0000000..a96d5cd
--- /dev/null
@@ -0,0 +1 @@
+[-123]
diff --git a/test/suites/valid/negative-int/output b/test/suites/valid/negative-int/output
new file mode 100644 (file)
index 0000000..8e30f8b
--- /dev/null
@@ -0,0 +1 @@
+[-123]
\ No newline at end of file
diff --git a/test/suites/valid/negative-one/input b/test/suites/valid/negative-one/input
new file mode 100644 (file)
index 0000000..2363a1a
--- /dev/null
@@ -0,0 +1 @@
+[-1]
diff --git a/test/suites/valid/negative-one/output b/test/suites/valid/negative-one/output
new file mode 100644 (file)
index 0000000..99d21a2
--- /dev/null
@@ -0,0 +1 @@
+[-1]
\ No newline at end of file
diff --git a/test/suites/valid/negative-zero/input b/test/suites/valid/negative-zero/input
new file mode 100644 (file)
index 0000000..40fc49c
--- /dev/null
@@ -0,0 +1 @@
+[-0]
diff --git a/test/suites/valid/negative-zero/output b/test/suites/valid/negative-zero/output
new file mode 100644 (file)
index 0000000..6e7ea63
--- /dev/null
@@ -0,0 +1 @@
+[0]
\ No newline at end of file
diff --git a/test/suites/valid/null/input b/test/suites/valid/null/input
new file mode 100644 (file)
index 0000000..62864b3
--- /dev/null
@@ -0,0 +1 @@
+[null]
diff --git a/test/suites/valid/null/output b/test/suites/valid/null/output
new file mode 100644 (file)
index 0000000..500db4a
--- /dev/null
@@ -0,0 +1 @@
+[null]
\ No newline at end of file
diff --git a/test/suites/valid/one-byte-utf-8/input b/test/suites/valid/one-byte-utf-8/input
new file mode 100644 (file)
index 0000000..8bda468
--- /dev/null
@@ -0,0 +1 @@
+["\u002c one-byte UTF-8"]
diff --git a/test/suites/valid/one-byte-utf-8/output b/test/suites/valid/one-byte-utf-8/output
new file mode 100644 (file)
index 0000000..c33d250
--- /dev/null
@@ -0,0 +1 @@
+[", one-byte UTF-8"]
\ No newline at end of file
diff --git a/test/suites/valid/real-capital-e-negative-exponent/input b/test/suites/valid/real-capital-e-negative-exponent/input
new file mode 100644 (file)
index 0000000..1e9fa51
--- /dev/null
@@ -0,0 +1 @@
+[1E-2]
diff --git a/test/suites/valid/real-capital-e-negative-exponent/output b/test/suites/valid/real-capital-e-negative-exponent/output
new file mode 100644 (file)
index 0000000..75b9ef9
--- /dev/null
@@ -0,0 +1 @@
+[0.01]
\ No newline at end of file
diff --git a/test/suites/valid/real-capital-e-positive-exponent/input b/test/suites/valid/real-capital-e-positive-exponent/input
new file mode 100644 (file)
index 0000000..6a6ab93
--- /dev/null
@@ -0,0 +1 @@
+[1E+2]
diff --git a/test/suites/valid/real-capital-e-positive-exponent/output b/test/suites/valid/real-capital-e-positive-exponent/output
new file mode 100644 (file)
index 0000000..d8ff702
--- /dev/null
@@ -0,0 +1 @@
+[100.0]
\ No newline at end of file
diff --git a/test/suites/valid/real-capital-e/input b/test/suites/valid/real-capital-e/input
new file mode 100644 (file)
index 0000000..e703223
--- /dev/null
@@ -0,0 +1 @@
+[1E22]
diff --git a/test/suites/valid/real-capital-e/output b/test/suites/valid/real-capital-e/output
new file mode 100644 (file)
index 0000000..9a739f2
--- /dev/null
@@ -0,0 +1 @@
+[1e22]
\ No newline at end of file
diff --git a/test/suites/valid/real-exponent/input b/test/suites/valid/real-exponent/input
new file mode 100644 (file)
index 0000000..b2a69b9
--- /dev/null
@@ -0,0 +1 @@
+[123e45]
diff --git a/test/suites/valid/real-exponent/output b/test/suites/valid/real-exponent/output
new file mode 100644 (file)
index 0000000..5ffc719
--- /dev/null
@@ -0,0 +1 @@
+[1.2299999999999999e47]
\ No newline at end of file
diff --git a/test/suites/valid/real-fraction-exponent/input b/test/suites/valid/real-fraction-exponent/input
new file mode 100644 (file)
index 0000000..0c1660d
--- /dev/null
@@ -0,0 +1 @@
+[123.456e78]
diff --git a/test/suites/valid/real-fraction-exponent/output b/test/suites/valid/real-fraction-exponent/output
new file mode 100644 (file)
index 0000000..66a3c81
--- /dev/null
@@ -0,0 +1 @@
+[1.23456e80]
\ No newline at end of file
diff --git a/test/suites/valid/real-negative-exponent/input b/test/suites/valid/real-negative-exponent/input
new file mode 100644 (file)
index 0000000..daa4af9
--- /dev/null
@@ -0,0 +1 @@
+[1e-2]
diff --git a/test/suites/valid/real-negative-exponent/output b/test/suites/valid/real-negative-exponent/output
new file mode 100644 (file)
index 0000000..75b9ef9
--- /dev/null
@@ -0,0 +1 @@
+[0.01]
\ No newline at end of file
diff --git a/test/suites/valid/real-positive-exponent/input b/test/suites/valid/real-positive-exponent/input
new file mode 100644 (file)
index 0000000..f378077
--- /dev/null
@@ -0,0 +1 @@
+[1e+2]
diff --git a/test/suites/valid/real-positive-exponent/output b/test/suites/valid/real-positive-exponent/output
new file mode 100644 (file)
index 0000000..d8ff702
--- /dev/null
@@ -0,0 +1 @@
+[100.0]
\ No newline at end of file
diff --git a/test/suites/valid/real-underflow/input b/test/suites/valid/real-underflow/input
new file mode 100644 (file)
index 0000000..dc70996
--- /dev/null
@@ -0,0 +1 @@
+[123e-10000000]
diff --git a/test/suites/valid/real-underflow/output b/test/suites/valid/real-underflow/output
new file mode 100644 (file)
index 0000000..92df1df
--- /dev/null
@@ -0,0 +1 @@
+[0.0]
\ No newline at end of file
diff --git a/test/suites/valid/run b/test/suites/valid/run
new file mode 100755 (executable)
index 0000000..170760b
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# 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.
+
+JSON_SORT_KEYS=1
+export JSON_SORT_KEYS
+
+is_test() {
+    test -d $test_path
+}
+
+do_run() {
+    variant=$1
+    s=".$1"
+
+    strip=0
+    [ "$variant" = "strip" ] && strip=1
+
+    STRIP=$strip $json_process \
+        <$test_path/input >$test_log/stdout$s 2>$test_log/stderr$s
+    valgrind_check $test_log/stderr$s || return 1
+
+    ref=output
+    [ -f $test_path/output$s ] && ref=output$s
+
+    if ! cmp -s $test_path/$ref $test_log/stdout$s; then
+        echo $variant > $test_log/variant
+        return 1
+    fi
+}
+
+run_test() {
+    do_run normal && do_run strip
+}
+
+show_error() {
+    valgrind_show_error && return
+
+    read variant < $test_log/variant
+    s=".$variant"
+
+    echo "VARIANT: $variant"
+
+    echo "EXPECTED OUTPUT:"
+    ref=output
+    [ -f $test_path/output$s ] && ref=output$s
+    nl -bn $test_path/$ref
+
+    echo "ACTUAL OUTPUT:"
+    nl -bn $test_log/stdout$s
+}
+
+. $top_srcdir/test/scripts/run-tests.sh
diff --git a/test/suites/valid/short-string/input b/test/suites/valid/short-string/input
new file mode 100644 (file)
index 0000000..0c3426d
--- /dev/null
@@ -0,0 +1 @@
+["a"]
diff --git a/test/suites/valid/short-string/output b/test/suites/valid/short-string/output
new file mode 100644 (file)
index 0000000..eac5f7b
--- /dev/null
@@ -0,0 +1 @@
+["a"]
\ No newline at end of file
diff --git a/test/suites/valid/simple-ascii-string/input b/test/suites/valid/simple-ascii-string/input
new file mode 100644 (file)
index 0000000..929b215
--- /dev/null
@@ -0,0 +1 @@
+["abcdefghijklmnopqrstuvwxyz1234567890 "]
diff --git a/test/suites/valid/simple-ascii-string/output b/test/suites/valid/simple-ascii-string/output
new file mode 100644 (file)
index 0000000..90358ab
--- /dev/null
@@ -0,0 +1 @@
+["abcdefghijklmnopqrstuvwxyz1234567890 "]
\ No newline at end of file
diff --git a/test/suites/valid/simple-int-0/input b/test/suites/valid/simple-int-0/input
new file mode 100644 (file)
index 0000000..111bb86
--- /dev/null
@@ -0,0 +1 @@
+[0]
diff --git a/test/suites/valid/simple-int-0/output b/test/suites/valid/simple-int-0/output
new file mode 100644 (file)
index 0000000..6e7ea63
--- /dev/null
@@ -0,0 +1 @@
+[0]
\ No newline at end of file
diff --git a/test/suites/valid/simple-int-1/input b/test/suites/valid/simple-int-1/input
new file mode 100644 (file)
index 0000000..7660873
--- /dev/null
@@ -0,0 +1 @@
+[1]
diff --git a/test/suites/valid/simple-int-1/output b/test/suites/valid/simple-int-1/output
new file mode 100644 (file)
index 0000000..bace2a0
--- /dev/null
@@ -0,0 +1 @@
+[1]
\ No newline at end of file
diff --git a/test/suites/valid/simple-int-123/input b/test/suites/valid/simple-int-123/input
new file mode 100644 (file)
index 0000000..3214bfe
--- /dev/null
@@ -0,0 +1 @@
+[123]
diff --git a/test/suites/valid/simple-int-123/output b/test/suites/valid/simple-int-123/output
new file mode 100644 (file)
index 0000000..e47f69a
--- /dev/null
@@ -0,0 +1 @@
+[123]
\ No newline at end of file
diff --git a/test/suites/valid/simple-object/input b/test/suites/valid/simple-object/input
new file mode 100644 (file)
index 0000000..a34fb49
--- /dev/null
@@ -0,0 +1 @@
+{"a":[]}
diff --git a/test/suites/valid/simple-object/output b/test/suites/valid/simple-object/output
new file mode 100644 (file)
index 0000000..982abe8
--- /dev/null
@@ -0,0 +1 @@
+{"a": []}
\ No newline at end of file
diff --git a/test/suites/valid/simple-real/input b/test/suites/valid/simple-real/input
new file mode 100644 (file)
index 0000000..0fed7df
--- /dev/null
@@ -0,0 +1 @@
+[123.456789]
diff --git a/test/suites/valid/simple-real/output b/test/suites/valid/simple-real/output
new file mode 100644 (file)
index 0000000..b02878e
--- /dev/null
@@ -0,0 +1 @@
+[123.456789]
\ No newline at end of file
diff --git a/test/suites/valid/string-escapes/input b/test/suites/valid/string-escapes/input
new file mode 100644 (file)
index 0000000..d994564
--- /dev/null
@@ -0,0 +1 @@
+["\"\\\/\b\f\n\r\t"]
diff --git a/test/suites/valid/string-escapes/output b/test/suites/valid/string-escapes/output
new file mode 100644 (file)
index 0000000..ca5c1c6
--- /dev/null
@@ -0,0 +1 @@
+["\"\\/\b\f\n\r\t"]
\ No newline at end of file
diff --git a/test/suites/valid/three-byte-utf-8/input b/test/suites/valid/three-byte-utf-8/input
new file mode 100644 (file)
index 0000000..ccc0bfa
--- /dev/null
@@ -0,0 +1 @@
+["\u0821 three-byte UTF-8"]
diff --git a/test/suites/valid/three-byte-utf-8/output b/test/suites/valid/three-byte-utf-8/output
new file mode 100644 (file)
index 0000000..c44d124
--- /dev/null
@@ -0,0 +1 @@
+["ࠡ three-byte UTF-8"]
\ No newline at end of file
diff --git a/test/suites/valid/true/input b/test/suites/valid/true/input
new file mode 100644 (file)
index 0000000..29513c4
--- /dev/null
@@ -0,0 +1 @@
+[true]
diff --git a/test/suites/valid/true/output b/test/suites/valid/true/output
new file mode 100644 (file)
index 0000000..de601e3
--- /dev/null
@@ -0,0 +1 @@
+[true]
\ No newline at end of file
diff --git a/test/suites/valid/two-byte-utf-8/input b/test/suites/valid/two-byte-utf-8/input
new file mode 100644 (file)
index 0000000..05ae854
--- /dev/null
@@ -0,0 +1 @@
+["\u0123 two-byte UTF-8"]
diff --git a/test/suites/valid/two-byte-utf-8/output b/test/suites/valid/two-byte-utf-8/output
new file mode 100644 (file)
index 0000000..1f0988d
--- /dev/null
@@ -0,0 +1 @@
+["ģ two-byte UTF-8"]
\ No newline at end of file
diff --git a/test/suites/valid/utf-8-string/input b/test/suites/valid/utf-8-string/input
new file mode 100644 (file)
index 0000000..20dc64a
--- /dev/null
@@ -0,0 +1 @@
+["€þıœəßð some utf-8 ĸʒ×ŋµåäö𝄞"]
diff --git a/test/suites/valid/utf-8-string/output b/test/suites/valid/utf-8-string/output
new file mode 100644 (file)
index 0000000..5372865
--- /dev/null
@@ -0,0 +1 @@
+["€þıœəßð some utf-8 ĸʒ×ŋµåäö𝄞"]
\ No newline at end of file
diff --git a/test/suites/valid/utf-surrogate-four-byte-encoding/input b/test/suites/valid/utf-surrogate-four-byte-encoding/input
new file mode 100644 (file)
index 0000000..c598b41
--- /dev/null
@@ -0,0 +1 @@
+["\uD834\uDD1E surrogate, four-byte UTF-8"]
diff --git a/test/suites/valid/utf-surrogate-four-byte-encoding/output b/test/suites/valid/utf-surrogate-four-byte-encoding/output
new file mode 100644 (file)
index 0000000..fa806d2
--- /dev/null
@@ -0,0 +1 @@
+["𝄞 surrogate, four-byte UTF-8"]
\ No newline at end of file
diff --git a/test/test-api b/test/test-api
deleted file mode 100755 (executable)
index 04018c1..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
-#
-# Jansson is free software; you can redistribute it and/or modify
-# it under the terms of the MIT license. See LICENSE for details.
-
-VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q"
-LOGDIR="testlogs/api"
-N=`find testprogs -type f -executable | wc -l`
-
-echo "testprogs: $N tests"
-
-rm -rf $LOGDIR
-mkdir -p $LOGDIR
-
-if [ -n "$VALGRIND" ]; then
-    runner="$VALGRIND_CMDLINE "
-fi
-
-i=1
-failed=
-for prog in testprogs/*; do
-    [ -x $prog ] || continue
-    t=`basename $prog`
-    logbase="testlogs/api/`printf '%02d-%s' $i $t`"
-    if ! $runner./$prog >$logbase.stdout 2>$logbase.stderr; then
-        echo >&2
-        echo "### $prog failed:" >&2
-        cat $logbase.stderr
-        exit 1
-    fi
-    if [ -n "$VALGRIND" ]; then
-        # Check for Valgrind error output. The valgrind option
-        # --error-exitcode is not enough because Valgrind doesn't
-        # think unfreed allocs are errors.
-        if grep -E -q '^==[0-9]+== ' $logbase.stderr; then
-            echo "### $prog failed:" >&2
-            echo "valgrind detected an error" >&2
-            echo "for details, see test/$logbase.stderr" >&2
-            exit 1
-        fi
-    fi
-    echo -n '.'
-done
-echo
diff --git a/test/test-invalid b/test/test-invalid
deleted file mode 100755 (executable)
index b83c2e7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
-#
-# Jansson is free software; you can redistribute it and/or modify
-# it under the terms of the MIT license. See LICENSE for details.
-
-TESTFILES="${srcdir}/testdata/invalid ${srcdir}/testdata/invalid-strip ${srcdir}/testdata/invalid-unicode"
-
-run_test() {
-    local prog=$1
-    local prefix=$2
-
-    run_testprog $prog $prefix
-    if ! cmp $prefix.out $prefix.$prog.stderr >/dev/null; then
-        echo >&2
-        echo "### $prefix ($prog) failed:" >&2
-        cat $prefix.in >&2
-        echo "### expected output:" >&2
-        cat $prefix.out >&2
-        echo "### actual output:" >&2
-        cat $prefix.$prog.stderr >&2
-        exit 1
-    fi
-}
-
-. ${srcdir}/run-test
diff --git a/test/test-valid b/test/test-valid
deleted file mode 100755 (executable)
index 46d4edf..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
-#
-# Jansson is free software; you can redistribute it and/or modify
-# it under the terms of the MIT license. See LICENSE for details.
-
-TESTFILES="${srcdir}/testdata/valid ${srcdir}/testdata/valid-strip"
-
-run_test() {
-    local prog=$1
-    local prefix=$2
-
-    run_testprog $prog $prefix
-
-    if ! ${srcdir}/json-compare.py $prefix.in $prefix.$prog.stdout \
-        >$prefix.$prog.cmp-stdout
-    then
-        echo >&2
-        echo "### $prefix ($prog) failed:" >&2
-        cat $prefix.in >&2
-        if [ -f $prefix.$prog.stdout ]; then
-            echo "### output:" >&2
-            cat $prefix.$prog.stdout >&2
-        fi
-        if [ -s $prefix.$prog.stdout ]; then
-            echo "### compare output:" >&2
-            cat $prefix.$prog.cmp-stdout >&2
-        fi
-        exit 1
-    fi
-}
-
-. ${srcdir}/run-test
diff --git a/test/testdata/Makefile.am b/test/testdata/Makefile.am
deleted file mode 100644 (file)
index bb84cf5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-EXTRA_DIST = invalid invalid-strip invalid-unicode valid valid-strip
diff --git a/test/testdata/invalid b/test/testdata/invalid
deleted file mode 100644 (file)
index 7fede67..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-==== empty ====
-====
-1
-'[' or '{' expected near end of file
-==== null ====
-null
-====
-1
-'[' or '{' expected near 'null'
-==== lone-open-brace ====
-{
-====
-2
-string or '}' expected near end of file
-==== lone-open-bracket ====
-[
-====
-2
-']' expected near end of file
-==== bracket-comma ====
-[,
-====
-1
-unexpected token near ','
-==== bracket-one-comma ====
-[1,
-====
-2
-']' expected near end of file
-==== unterminated-string ====
-["a
-====
-1
-unexpected newline near '"a'
-==== unterminated-array ====
-["a"
-====
-2
-']' expected near end of file
-==== apostrophe ====
-['
-====
-1
-invalid token near '''
-==== brace-comma ====
-{,
-====
-1
-string or '}' expected near ','
-==== unterminated-empty-key ====
-{"
-====
-1
-unexpected newline near '"'
-==== unterminated-key ====
-{"a
-====
-1
-unexpected newline near '"a'
-==== object-no-colon ====
-{"a"
-====
-2
-':' expected near end of file
-==== object-apostrophes ====
-{'a'
-====
-1
-string or '}' expected near '''
-==== object-no-value ====
-{"a":
-====
-2
-unexpected token near end of file
-==== object-unterminated-value ====
-{"a":"a
-====
-1
-unexpected newline near '"a'
-==== object-garbage-at-end ====
-{"a":"a" 123}
-====
-1
-'}' expected near '123'
-==== unterminated-object-and-array ====
-{[
-====
-1
-string or '}' expected near '['
-==== unterminated-array-and-object ====
-[{
-====
-2
-string or '}' expected near end of file
-==== object-in-unterminated-array ====
-[{}
-====
-2
-']' expected near end of file
-==== extra-comma-in-array ====
-[1,]
-====
-1
-unexpected token near ']'
-==== extra-command-in-multiline-array ====
-[1,
-2,
-3,
-4,
-5,
-]
-====
-6
-unexpected token near ']'
-==== real-truncated-at-point ====
-[1.]
-====
-1
-invalid token near '1.'
-==== real-truncated-at-e ====
-[1e]
-====
-1
-invalid token near '1e'
-==== real-garbage-after-e ====
-[1ea]
-====
-1
-invalid token near '1e'
-==== real-positive-overflow ====
-[123123e100000]
-====
-1
-real number overflow near '123123e100000'
-==== real-negative-overflow ====
-[-123123e100000]
-====
-1
-real number overflow near '-123123e100000'
-==== real-underflow ====
-[123e-10000000]
-====
-1
-real number underflow near '123e-10000000'
-==== integer-starting-with-zero ====
-[012]
-====
-1
-invalid token near '0'
-==== negative-integer-starting-with-zero ====
-[-012]
-====
-1
-invalid token near '-0'
-==== too-big-positive-integer ====
-[123123123123123]
-====
-1
-too big integer near '123123123123123'
-==== too-big-negative-integer ====
-[-123123123123123]
-====
-1
-too big negative integer near '-123123123123123'
-==== invalid-identifier ====
-[troo
-====
-1
-invalid token near 'troo'
-==== minus-sign-without-number ====
-[-foo]
-====
-1
-invalid token near '-'
-==== invalid-negative-integerr ====
-[-123foo]
-====
-1
-']' expected near 'foo'
-==== invalid-negative-real ====
-[-123.123foo]
-====
-1
-']' expected near 'foo'
-==== invalid-escape ====
-["\a <-- invalid escape"]
-====
-1
-invalid escape near '"\'
-==== tab-character-in-string ====
-["      <-- tab character"]
-====
-1
-control character 0x9 near '"'
-==== null-byte-in-string ====
-["\u0000 (null byte not allowed)"]
-====
-1
-\u0000 is not allowed
-==== truncated-unicode-surrogate ====
-["\uDADA (first surrogate without the second)"]
-====
-1
-invalid Unicode '\uDADA'
-==== invalid-second-surrogate ====
-["\uD888\u3210 (first surrogate and invalid second surrogate)"]
-====
-1
-invalid Unicode '\uD888\u3210'
-==== lone-second-surrogate ====
-["\uDFAA (second surrogate on it's own)"]
-====
-1
-invalid Unicode '\uDFAA'
-==== unicode-identifier ====
-====
-1
-'[' or '{' expected near 'å'
-==== ascii-unicode-identifier ====
-aå
-====
-1
-'[' or '{' expected near 'a'
-==== garbage-at-the-end ====
-[1,2,3]foo
-====
-1
-end of file expected near 'foo'
-==== garbage-after-newline ====
-[1,2,3]
-foo
-====
-2
-end of file expected near 'foo'
diff --git a/test/testdata/invalid-strip b/test/testdata/invalid-strip
deleted file mode 100644 (file)
index 1efdc25..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-==== empty ====
-====
-1
-'[' or '{' expected near end of file
-==== null ====
-null
-====
-1
-'[' or '{' expected near 'null'
-==== lone-open-brace ====
-{
-====
-1
-string or '}' expected near end of file
-==== lone-open-bracket ====
-[
-====
-1
-']' expected near end of file
-==== bracket-comma ====
-[,
-====
-1
-unexpected token near ','
-==== bracket-one-comma ====
-[1,
-====
-1
-']' expected near end of file
-==== unterminated-string ====
-["a
-====
-1
-premature end of input near '"a'
-==== unterminated-array ====
-["a"
-====
-1
-']' expected near end of file
-==== apostrophe ====
-['
-====
-1
-invalid token near '''
-==== brace-comma ====
-{,
-====
-1
-string or '}' expected near ','
-==== unterminated-empty-key ====
-{"
-====
-1
-premature end of input near '"'
-==== unterminated-key ====
-{"a
-====
-1
-premature end of input near '"a'
-==== object-no-colon ====
-{"a"
-====
-1
-':' expected near end of file
-==== object-apostrophes ====
-{'a'
-====
-1
-string or '}' expected near '''
-==== object-no-value ====
-{"a":
-====
-1
-unexpected token near end of file
-==== object-unterminated-value ====
-{"a":"a
-====
-1
-premature end of input near '"a'
-==== object-garbage-at-end ====
-{"a":"a" 123}
-====
-1
-'}' expected near '123'
-==== unterminated-object-and-array ====
-{[
-====
-1
-string or '}' expected near '['
-==== unterminated-array-and-object ====
-[{
-====
-1
-string or '}' expected near end of file
-==== object-in-unterminated-array ====
-[{}
-====
-1
-']' expected near end of file
-==== extra-comma-in-array ====
-[1,]
-====
-1
-unexpected token near ']'
-==== extra-command-in-multiline-array ====
-[1,
-2,
-3,
-4,
-5,
-]
-====
-6
-unexpected token near ']'
-==== real-truncated-at-point ====
-[1.]
-====
-1
-invalid token near '1.'
-==== real-truncated-at-e ====
-[1e]
-====
-1
-invalid token near '1e'
-==== real-garbage-after-e ====
-[1ea]
-====
-1
-invalid token near '1e'
-==== real-positive-overflow ====
-[123123e100000]
-====
-1
-real number overflow near '123123e100000'
-==== real-negative-overflow ====
-[-123123e100000]
-====
-1
-real number overflow near '-123123e100000'
-==== real-underflow ====
-[123e-10000000]
-====
-1
-real number underflow near '123e-10000000'
-==== integer-starting-with-zero ====
-[012]
-====
-1
-invalid token near '0'
-==== negative-integer-starting-with-zero ====
-[-012]
-====
-1
-invalid token near '-0'
-==== too-big-positive-integer ====
-[123123123123123]
-====
-1
-too big integer near '123123123123123'
-==== too-big-negative-integer ====
-[-123123123123123]
-====
-1
-too big negative integer near '-123123123123123'
-==== invalid-identifier ====
-[troo
-====
-1
-invalid token near 'troo'
-==== minus-sign-without-number ====
-[-foo]
-====
-1
-invalid token near '-'
-==== invalid-negative-integerr ====
-[-123foo]
-====
-1
-']' expected near 'foo'
-==== invalid-negative-real ====
-[-123.123foo]
-====
-1
-']' expected near 'foo'
-==== invalid-escape ====
-["\a <-- invalid escape"]
-====
-1
-invalid escape near '"\'
-==== tab-character-in-string ====
-["      <-- tab character"]
-====
-1
-control character 0x9 near '"'
-==== null-byte-in-string ====
-["\u0000 (null byte not allowed)"]
-====
-1
-\u0000 is not allowed
-==== truncated-unicode-surrogate ====
-["\uDADA (first surrogate without the second)"]
-====
-1
-invalid Unicode '\uDADA'
-==== invalid-second-surrogate ====
-["\uD888\u3210 (first surrogate and invalid second surrogate)"]
-====
-1
-invalid Unicode '\uD888\u3210'
-==== lone-second-surrogate ====
-["\uDFAA (second surrogate on it's own)"]
-====
-1
-invalid Unicode '\uDFAA'
-==== unicode-identifier ====
-====
-1
-'[' or '{' expected near 'å'
-==== ascii-unicode-identifier ====
-aå
-====
-1
-'[' or '{' expected near 'a'
-==== garbage-at-the-end ====
-[1,2,3]foo
-====
-1
-end of file expected near 'foo'
-==== garbage-after-newline ====
-[1,2,3]
-foo
-====
-2
-end of file expected near 'foo'
diff --git a/test/testdata/invalid-unicode b/test/testdata/invalid-unicode
deleted file mode 100644 (file)
index 84db51f..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-==== lone-invalid-utf-8 ====
-====
--1
-unable to decode byte 0xe5 at position 0
-==== invalid-utf-8-in-string ====
-["å <-- invalid UTF-8"]
-====
--1
-unable to decode byte 0xe5 at position 2
-==== invalid-utf-8-in-array ====
-[å]
-====
--1
-unable to decode byte 0xe5 at position 1
-==== invalid-utf-8-in-identifier ====
-[aå]
-====
--1
-unable to decode byte 0xe5 at position 2
-==== invalid-utf-8-in-escape ====
-["\uå"]
-====
--1
-unable to decode byte 0xe5 at position 4
-==== invalid-utf-8-after-backslash ====
-["\å"]
-====
--1
-unable to decode byte 0xe5 at position 3
-==== invalid-utf-8-in-int ====
-[0å]
-====
--1
-unable to decode byte 0xe5 at position 2
-==== invalid-utf-8-in-bigger-int ====
-[123å]
-====
--1
-unable to decode byte 0xe5 at position 4
-==== invalid-utf-8-in-real-after-e ====
-[1eå]
-====
--1
-unable to decode byte 0xe5 at position 3
-==== invalid-utf-8-in-exponent ====
-[1e1å]
-====
--1
-unable to decode byte 0xe5 at position 4
-==== lone-utf-8-continuation-byte ====
-["\81"]
-====
--1
-unable to decode byte 0x81 at position 2
-==== overlong-ascii-encoding ====
-["Á"]
-====
--1
-unable to decode byte 0xc1 at position 2
-==== restricted-utf-8 ====
-["ý"]
-====
--1
-unable to decode byte 0xfd at position 2
-==== not-in-unicode-range ====
-[""]
-====
--1
-unable to decode byte 0xf4 at position 2
-==== overlong-3-byte-encoding ====
-["à\80¢ <-- overlong encoding"]
-====
--1
-unable to decode byte 0xe0 at position 2
-==== overlong-4-byte-encoding ====
-["ð\80\80¢ <-- overlong encoding"]
-====
--1
-unable to decode byte 0xf0 at position 2
-==== truncated-utf-8 ====
-["àÿ <-- truncated UTF-8"]
-====
--1
-unable to decode byte 0xe0 at position 2
-==== encoded-surrogate-half ====
-[" <-- encoded surrogate half"]
-====
--1
-unable to decode byte 0xed at position 2
diff --git a/test/testdata/valid b/test/testdata/valid
deleted file mode 100644 (file)
index 50cfc63..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-==== empty-string ====
-[""]
-==== short-string ====
-["a"]
-==== simple-ascii-string ====
-["abcdefghijklmnopqrstuvwxyz1234567890 "]
-==== utf-8-string ====
-["€þıœəßð some utf-8 ĸʒ×ŋµåäö𝄞"]
-==== string-escapes ====
-["\"\\\/\b\f\n\r\t"]
-==== one-byte-utf-8 ====
-["\u002c one-byte UTF-8"]
-==== two-byte-utf-8 ====
-["\u0123 two-byte UTF-8"]
-==== three-byte-utf-8 ====
-["\u0821 three-byte UTF-8"]
-==== utf-surrogate-four-byte-encoding ====
-["\uD834\uDD1E surrogate, four-byte UTF-8"]
-==== escaped-utf-control-char ====
-["\u0012 escaped control character"]
-==== simple-int-0 ====
-[0]
-==== simple-int-1 ====
-[1]
-==== simple-int-123 ====
-[123]
-==== negative-zero ====
-[-0]
-==== negative-one ====
-[-1]
-==== negative-int ====
-[-123]
-==== simple-real ====
-[123.456789]
-==== real-exponent ====
-[123e45]
-==== real-capital-e ====
-[1E22]
-==== real-positive-exponent ====
-[1e+2]
-==== real-negative-exponent ====
-[1e-2]
-==== real-capital-e-positive-exponent ====
-[1E+2]
-==== real-capital-e-negative-exponent ====
-[1E-2]
-==== real-fraction-exponent ====
-[123.456e78]
-==== true ====
-[true]
-==== false ====
-[false]
-==== null ====
-[null]
-==== empty-array ====
-[]
-==== empty-object-in-array ====
-[{}]
-==== complex-array ====
-[1,2,3,4,
-"a", "b", "c",
-{"foo": "bar", "core": "dump"},
-true, false, true, true, null, false
-]
-==== empty-object ====
-{}
-==== simple-object ====
-{"a":[]}
diff --git a/test/testdata/valid-strip b/test/testdata/valid-strip
deleted file mode 100644 (file)
index 50cfc63..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-==== empty-string ====
-[""]
-==== short-string ====
-["a"]
-==== simple-ascii-string ====
-["abcdefghijklmnopqrstuvwxyz1234567890 "]
-==== utf-8-string ====
-["€þıœəßð some utf-8 ĸʒ×ŋµåäö𝄞"]
-==== string-escapes ====
-["\"\\\/\b\f\n\r\t"]
-==== one-byte-utf-8 ====
-["\u002c one-byte UTF-8"]
-==== two-byte-utf-8 ====
-["\u0123 two-byte UTF-8"]
-==== three-byte-utf-8 ====
-["\u0821 three-byte UTF-8"]
-==== utf-surrogate-four-byte-encoding ====
-["\uD834\uDD1E surrogate, four-byte UTF-8"]
-==== escaped-utf-control-char ====
-["\u0012 escaped control character"]
-==== simple-int-0 ====
-[0]
-==== simple-int-1 ====
-[1]
-==== simple-int-123 ====
-[123]
-==== negative-zero ====
-[-0]
-==== negative-one ====
-[-1]
-==== negative-int ====
-[-123]
-==== simple-real ====
-[123.456789]
-==== real-exponent ====
-[123e45]
-==== real-capital-e ====
-[1E22]
-==== real-positive-exponent ====
-[1e+2]
-==== real-negative-exponent ====
-[1e-2]
-==== real-capital-e-positive-exponent ====
-[1E+2]
-==== real-capital-e-negative-exponent ====
-[1E-2]
-==== real-fraction-exponent ====
-[123.456e78]
-==== true ====
-[true]
-==== false ====
-[false]
-==== null ====
-[null]
-==== empty-array ====
-[]
-==== empty-object-in-array ====
-[{}]
-==== complex-array ====
-[1,2,3,4,
-"a", "b", "c",
-{"foo": "bar", "core": "dump"},
-true, false, true, true, null, false
-]
-==== empty-object ====
-{}
-==== simple-object ====
-{"a":[]}
diff --git a/test/testprogs/Makefile.am b/test/testprogs/Makefile.am
deleted file mode 100644 (file)
index 41807ed..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-check_PROGRAMS = test_array test_load test_simple test_number test_object
-
-test_array_SOURCES = test_array.c util.h
-test_load_SOURCES = test_load.c util.h
-test_simple_SOURCES = test_simple.c util.h
-test_number_SOURCES = test_number.c util.h
-test_object_SOURCES = test_object.c util.h
-
-AM_CPPFLAGS = -I$(top_srcdir)/src
-AM_CFLAGS = -Wall -Werror
-LDFLAGS = -static  # for speed and Valgrind
-LDADD = ../../src/libjansson.la
diff --git a/test/testprogs/test_load.c b/test/testprogs/test_load.c
deleted file mode 100644 (file)
index 4d8fa88..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- */
-
-#include <jansson.h>
-#include <string.h>
-#include "util.h"
-
-int main()
-{
-    json_t *json;
-    json_error_t error;
-
-    json = json_load_file("/path/to/nonexistent/file.json", &error);
-    if(error.line != -1)
-        fail("json_load_file returned an invalid line number");
-    if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
-        fail("json_load_file returned an invalid error message");
-
-    return 0;
-}
diff --git a/test/testprogs/util.h b/test/testprogs/util.h
deleted file mode 100644 (file)
index ec8e0e4..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- */
-
-#ifndef TESTPROGS_UTIL_H
-#define TESTPROGS_UTIL_H
-
-#include <stdlib.h>
-
-#define fail(msg)                                                \
-    do {                                                         \
-        fprintf(stderr, "%s:%s:%d: %s\n",                        \
-                __FILE__, __FUNCTION__, __LINE__, msg);          \
-        exit(1);                                                 \
-    } while(0)
-
-#endif
diff --git a/win32/jansson_config.h b/win32/jansson_config.h
new file mode 100644 (file)
index 0000000..fc8e5ea
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *
+ *
+ * This file specifies a part of the site-specific configuration for
+ * Jansson, namely those things that affect the public API in
+ * jansson.h.
+ *
+ * The configure script copies this file to jansson_config.h and
+ * replaces @var@ substitutions by values that fit your system. If you
+ * cannot run the configure script, you can do the value substitution
+ * by hand.
+ */
+
+#ifndef JANSSON_CONFIG_H
+#define JANSSON_CONFIG_H
+
+/* If your compiler supports the inline keyword in C, JSON_INLINE is
+   defined to `inline', otherwise empty. In C++, the inline is always
+   supported. */
+#ifdef __cplusplus
+#define JSON_INLINE inline
+#else
+#define JSON_INLINE __inline
+#endif
+
+/* If your compiler supports the `long long` type and the strtoll()
+   library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
+   otherwise to 0. */
+#define JSON_INTEGER_IS_LONG_LONG 1
+
+/* If locale.h and localeconv() are available, define to 1,
+   otherwise to 0. */
+#define JSON_HAVE_LOCALECONV 1
+
+#endif
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