339 Commits

Author SHA1 Message Date
Eric Hawicz
52ddfb35f1 Fix memory leak in test_safe_json_pointer_set, the caller must free the passed value when json_pointer_set failed. Also, reformat. 2026-02-19 19:05:10 -05:00
Eric Hawicz
8c987e0b73 Issue #919: Fix build on non-x86 windows. Patch thanks to @dotanbrk 2026-02-19 18:48:05 -05:00
Eric Hawicz
d7f14cb330 Merge pull request #917 from thely314/fix/oom-too-large-index
Fix: OOM vulnerability cause by is_valid_index
2026-02-14 17:23:49 -05:00
lone
c293d9b61c general callback, safer API & related tests
- Changed json_pointer_set_with_array_cb to json_pointer_set_with_cb, related cb updated
- Added tests(test_safe_json_pointer_set.*) for new-exported funcs, also updated cmake & meson

Signed-off-by: lone <lonechan314@qq.com>
2026-02-09 14:01:38 +08:00
Eric Hawicz
8c01a7c32f Merge pull request #903 from neheb/1
Some checks failed
meson / Linux-GCC (13) (push) Has been cancelled
meson / Linux-GCC (9) (push) Has been cancelled
meson / Linux-Clang (22) (push) Has been cancelled
meson / Linux-Clang (9) (push) Has been cancelled
meson / VisualStudio (x64) (push) Has been cancelled
meson / VisualStudio (x86) (push) Has been cancelled
meson / VisualStudio-clang-cl (push) Has been cancelled
meson / MSYS2 (CLANG64) (push) Has been cancelled
meson / MSYS2 (UCRT64) (push) Has been cancelled
meson / Cygwin (push) Has been cancelled
meson / MacOS (push) Has been cancelled
meson / DragonflyBSD (push) Has been cancelled
meson / FreeBSD (push) Has been cancelled
meson / NetBSD (push) Has been cancelled
meson / OmniOS (push) Has been cancelled
meson / OpenBSD (push) Has been cancelled
meson / Alpine (aarch64) (push) Has been cancelled
meson / Alpine (armhf) (push) Has been cancelled
meson / Alpine (armv7) (push) Has been cancelled
meson / Alpine (ppc64le) (push) Has been cancelled
meson / Alpine (riscv64) (push) Has been cancelled
meson / Alpine (s390x) (push) Has been cancelled
meson / Alpine (x86) (push) Has been cancelled
meson / Alpine (x86_64) (push) Has been cancelled
meson: use override_dependency
2026-02-08 17:08:04 -05:00
Rosen Penev
d61b41c8bb github CI: add
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2026-02-05 13:46:00 -08:00
Rosen Penev
1d4885ad09 fix compilation with clang-cl
Something in the configure stage goes wrong where it believe strncasecmp
is present but the header defining it is not. Work around this.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2026-02-05 13:46:00 -08:00
Rosen Penev
92a4911e88 meson: use static library for tests
Needed as tests use private APIs that are not exported.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2026-02-05 13:46:00 -08:00
Rosen Penev
798d8f7235 meson: add license information
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2026-02-05 13:21:54 -08:00
Rosen Penev
a0c6bb32dd meson: simplify __thread check and fix MinGW
> was missing.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2026-02-05 13:21:54 -08:00
Rosen Penev
af2671a212 meson: use override_dependency
Allows wraps to use the dependency_names syntax instead of the possibly
changing variable syntax.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2026-02-05 13:21:54 -08:00
Eric Hawicz
46defb4349 Merge pull request #915 from jaisonZheng/fix-macos-locale-memory-leak
Fix memory leak on macOS by auto-enabling NEWLOCALE_NEEDS_FREELOCALE
2026-01-31 13:22:57 -05:00
lone
bda8299eb1 fix: adapt to the early C std
Signed-off-by: lone <lonechan314@qq.com>
2026-01-27 14:43:56 +08:00
lone
1c3b98ddb5 amend: removed marco related logic
Signed-off-by: lone <lonechan314@qq.com>
2026-01-26 17:54:53 +08:00
lone
22e470ac5c Fix: try to make json_pointer_set_with_array_cb a public api
make json_pointer_set_with_array_cb a public api, along with a function with wrapped callback

Signed-off-by: lone <lonechan314@qq.com>
2026-01-26 17:33:00 +08:00
lone
3d86402028 Fix: OOM vulnerability cause by is_valid_index
An OOM vulnerability exists in the json_pointer_set function (and related functions).
See issue #916 for more details.

To fix that, added a sanity check in the is_valid_index function to limit the maximum value of a parsed array index.
Provided a configurable macro for modification.

Signed-off-by: lone <lonechan314@qq.com>
2026-01-25 23:54:27 +08:00
JaisonZheng
653a12c7c2 Fix memory leak on macOS by auto-enabling NEWLOCALE_NEEDS_FREELOCALE
macOS's newlocale() does not follow POSIX specification - it doesn't
reuse the passed locale_t, causing ~1,472 bytes leak per json_tokener_parse() call.

This is the same issue as #668 (FreeBSD). The existing workaround works,
but macOS was not auto-detected.

Tested with AddressSanitizer: leak drops from 1,472,000 bytes to ~120 bytes
(system init only).
2026-01-21 17:28:20 +08:00
Eric Hawicz
d05ea4851f Update ChangeLog, abi-check.sh, AUTHORS and issues_closed list for an upcoming 0.19 release. 2026-01-19 17:46:57 -05:00
Eric Hawicz
8948568031 Merge pull request #913 from mlocati-forks/dll-versioninfo
Add VERSIONINFO to libjson-c.dll
2026-01-18 15:25:29 -05:00
Michele Locati
b01b5b701b Add VERSIONINFO to libjson-c.dll 2026-01-18 20:23:50 +01:00
Eric Hawicz
26fcd8d569 Merge pull request #910 from bhaible/support-msvc-with-autoconf
Support MSVC in packages that use GNU Autoconf.
2026-01-09 23:18:40 -05:00
Bruno Haible
9774826e79 Support MSVC in packages that use GNU Autoconf.
Fixes https://github.com/json-c/json-c/issues/909

When ssize_t is defined as a macro, typically through a package's config.h file,
the installed json*.h files must not attempt to define ssize_t via typedef.
2026-01-09 23:51:43 +01:00
Eric Hawicz
14c8e2e5ee Merge pull request #907 from travier/master-cmake4
tests/CMakeLists: Allow build with CMake 4
2025-12-29 23:42:24 -05:00
Timothée Ravier
e4b9dfba49 tests/CMakeLists: Allow build with CMake 4
See: https://github.com/json-c/json-c/issues/889
2025-12-23 11:52:43 +01:00
Eric Hawicz
a1249bfda0 Merge pull request #905 from glance-/fix-typo
Fix typo
2025-12-01 06:37:11 -05:00
Anton Lundin
dc44b05961 Fix typo
That include line wasn't valid code, so that resulted in:
Checking for size of "size_t" : -1

And later on:
json-c/json_patch.c:28:2: error: #error Unable to determine size of size_t
2025-12-01 11:37:59 +01:00
Eric Hawicz
20974e5f88 Merge pull request #904 from Lakshmi-Surekha/aix-getopt-dont-include
Include getopt.h header file if the platform is not AIX.
2025-11-29 11:40:00 -05:00
Lakshmi-Surekha
a7b34a299b apps: use CMake check for getopt.h
Add CMake detection for getopt.h and update json_parse.c to include it
only when HAVE_GETOPT_H is defined. Fixes AIX build failure.
2025-11-29 01:32:10 -06:00
Eric Hawicz
243ed1a51c Merge pull request #900 from neheb/1
meson cleanups
2025-11-04 18:32:47 -05:00
Rosen Penev
f565798e2c meson: remove useless if
get_supported_arguments already handles this.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2025-11-04 10:45:20 -08:00
Rosen Penev
8c012da8a6 meson: add header to size_t check
Might be needed under MSVC.
2025-11-04 10:45:20 -08:00
Rosen Penev
bd7355470e meson: replace target_machine with host_machine
The latter is the canonical way to express this.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2025-11-04 10:45:20 -08:00
Rosen Penev
40fe4d2b04 meson: use loops for checks
All of these are effectively the same. No point in duplicating it
everywhere.

Remove prefix from has_function. Apparently having the prefix in there
changes behavior.

Also fix an error on MinGW where SSIZE_T_MAX is undefined.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2025-11-04 10:44:42 -08:00
Eric Hawicz
2c412d4593 Merge pull request #899 from Seagate/meson_support
Meson support
2025-10-27 16:00:37 -04:00
Tyler Erickson
0883c24130 Merge branch 'json-c:master' into meson_support 2025-10-27 12:13:23 -06:00
Eric Hawicz
fbdd4bed24 Merge pull request #898 from Seagate/fix_cmake_clangcl
bug: Fixing Cmake build when using clang-cl
2025-10-25 09:46:22 -04:00
Eric Hawicz
33f4cb9353 Merge pull request #895 from nim65s/patch-1
CMake: sync minimum version
2025-10-25 09:45:50 -04:00
Tyler Erickson
a5bc36de26 Removing werror option as meson has a built-in option for this
Signed-off-by: Tyler Erickson <tyler.erickson@seagate.com>
2025-10-24 09:48:56 -06:00
Tyler Erickson
3723b182db bug: Fixing runtime issue with test_util_file.c in Windows
Windows was failing tests due to how the Windows API works with some of the calls used.
When opening and reading a file O_BINARY is needed otherwise it fails for size checks. This is due to how Windows handles newlines and counts between text and binary mode file reads.

Also fixed is the test for /dev/null.
In Windows, this fails, but crashes due to a missing return statement when it cannot open this file.
I also tried telling windows to open the special file NUL, however that leads to a CRT crash later in the test that cannot be stopped and will always fail. Rather than fail a test that Windows will always fail, it has been disabled in that specific case.

Signed-off-by: Tyler Erickson <tyler.erickson@seagate.com>
2025-10-20 12:10:57 -06:00
Tyler Erickson
e3a33ae8ae make: Adding support for building json-c with meson
Adding meson build files for json-c that work similarly to the cmake build files.
Where it made sense, I reused existing cmake .h.in files or generated entirely from meson.
All tests were done with GCC and Clang in ubuntu 24.04, Windows using MSVC 2022 and Clang-cl from llvm's repo using version 21.1.3

Signed-off-by: Tyler Erickson <tyler.erickson@seagate.com>
2025-10-20 12:08:01 -06:00
Tyler Erickson
461d9f90c4 bug: Fixing Cmake build when using clang-cl
Clang-cl will fail to build and produce warnings about redefining existing symbols, mostly for float.h and math.h compatibility.

To resolve this, this moves the clang-cl detection earlier in the CMakeLists.txt so that CLANG_CL can be checked properly where there is an existing MSVC workaround for these symbols.
This resolves the build using the latest clang-cl from the LLVM clang repo as well as clang-cl that can be installed with MSVC 2022.

Signed-off-by: Tyler Erickson <tyler.erickson@seagate.com>
2025-10-20 11:39:04 -06:00
Eric Hawicz
2372e9518e Merge pull request #893 from sffc/supplemental-code-point-bug
Fix bug involving supplemental code points that look like high surrogates
2025-08-07 09:46:47 -04:00
Guilhem Saurel
8af05ab785 CMake: sync minimum version 2025-08-06 22:10:41 +02:00
Shane F. Carr
7974657c56 Fix code and update tests 2025-07-30 17:40:56 -07:00
Shane F. Carr
dbefb48948 Add tests with current behavior 2025-07-30 17:39:47 -07:00
Eric Hawicz
bf92456789 Make json_parse a bit more useful by adding -u (validate UTF8) and -P (specify arbitrary tokener parse flags), and read from stdin if no filename is provided. 2025-07-08 21:04:00 -04:00
Eric Hawicz
7cee5237dc Issue #867 - also disallow control characters in keys 2025-04-03 21:16:29 -04:00
Eric Hawicz
7bca694b47 Merge pull request #884 from careles/mingw_win64-WIN32_LEAN_AND_MEAN-redefined
fix macro WIN32_LEAN_AND_MEAN redefined
2025-01-09 00:12:09 -05:00
Careles
51bcdd0000 fix macro WIN32_LEAN_AND_MEAN redefined
json-c as add_subdirectory and project cmakelists.txt has add_definitions(-DWIN32_LEAN_AND_MEAN)
2025-01-06 10:56:53 +08:00
Eric Hawicz
a304464807 Merge pull request #872 from GitMensch/patch-2
fix linkhash breaking -std=c89
2024-12-29 13:34:38 -05:00
Eric Hawicz
7b73916815 Merge pull request #847 from sacredbanana/amiga
Add support for Commodore Amiga
2024-12-29 13:26:11 -05:00
Eric Hawicz
ee83daa409 Merge pull request #882 from simonresch/handle-nan-to-int-conversion
Explicitly handle NaN values when converting to integer
2024-12-26 19:07:07 -05:00
Cameron Armstrong (Nightfox)
ca1a6b0cc9 Add support for MorphOS 2024-12-24 10:09:50 +08:00
Cameron Armstrong (Nightfox)
daff6eb746 Remove linking to libamiga and libc 2024-12-24 10:09:50 +08:00
Cameron Armstrong (Nightfox)
254b5abef8 Do not use duplocale if building for libnix because it isnt supported yet 2024-12-24 10:09:50 +08:00
Cameron Armstrong (Nightfox)
d31d635af1 Clean up CMakeLists.txt and remove Amiga specific locale.h guard 2024-12-24 10:09:50 +08:00
Cameron Armstrong (Nightfox)
743ebf53e5 Fix Readme 2024-12-24 10:09:50 +08:00
Cameron Armstrong
f928e7c0fa Get working ror libnix and clib2 2024-12-24 10:09:50 +08:00
Cameron Armstrong
4feebc1cd6 Add support for Commodore Amiga 2024-12-24 10:09:50 +08:00
Eric Hawicz
961c31f8ed Merge pull request #879 from janotomko/null
Handle NULL gracefully in json_tokener_free
2024-12-07 13:33:05 -05:00
Simon Resch
0c3588330a Set error codes for truncating int conversions
json_object_get_int/int64/uint64() now sets errno to ERANGE when the source value
can't be represented in the target type.
2024-11-27 07:17:20 +01:00
Simon Resch
8c13801f2c Explicitly handle NaN values when converting to int
Json objects of type double with the value NaN could cause undefined
behavior when casting double to int in `json_object_get_int`.
2024-11-27 07:17:20 +01:00
Eric Hawicz
c63d4a4194 Merge pull request #878 from 0xfadead/readme-fix-underline
Fix underline bug in README
2024-11-23 19:39:50 -05:00
Eric Hawicz
77bb5c54fc Merge pull request #883 from simonresch/add-oss-fuzz-tests
Add fuzz tests for json_object/point/array apis
2024-11-23 19:39:13 -05:00
Simon Resch
839fbe0d63 Add fuzz tests for json_object/point/array apis
Extends the coverage of fuzz tests executed in OSS-Fuzz.
2024-11-22 07:43:32 +01:00
Eric Hawicz
ff8ed0f094 Issue #881: don't allow json_tokener_new_ex() with a depth < 1 2024-11-17 22:11:24 -05:00
Eric Hawicz
565f181f65 Fix issue #875: cast to unsigned char so bytes above 0x7f aren't interpreted as negative, which was causing the strict-mode control characters check to incorrectly trigger. 2024-11-08 22:20:40 -05:00
Ján Tomko
828c12b226 Handle NULL gracefully in json_tokener_free
Similarly to glibc's free, make json_tokener_free(NULL)
a no-op, to simplify cleanup paths.

Signed-off-by: Ján Tomko <jtomko@redhat.com>
2024-11-06 15:23:00 +01:00
Eric Hawicz
474ee12435 Fix the apps/json_parse "-s" (strict) option so it actually does something, and default to non-strict. 2024-11-03 19:44:21 -05:00
Awildidiot
d6b973e047 Fix underline bug in README 2024-11-03 20:03:49 +01:00
Eric Hawicz
07148f33a1 Merge pull request #873 from esiaero/master
Correct year in changelog for 0.18 release
2024-10-10 20:55:18 -04:00
Evan Si
2ec0b87b58 Correct year in changelog for 0.18 release 2024-10-10 23:47:55 +00:00
Simon Sobisch
6aab3da807 fix linkhash breaking -std=c89
making the definition of _LH_INLINE depending on __STDC_VERSION__, not on a specific compiler
2024-10-07 08:16:42 +00:00
Eric Hawicz
c793374f16 Update openssl command to work for newer versions of openssl. 2024-09-15 13:16:48 -04:00
Eric Hawicz
e9bd26d568 Update the master branch to version 0.18.99 2024-09-15 13:06:06 -04:00
Eric Haszlakiewicz
7ebdc4ddf8 Update abi-check.sh to compare 0.16 and 0.18 2024-09-15 16:10:57 +00:00
Eric Hawicz
87900c0a2e Fix the expected output for test_parse 2024-09-15 11:59:05 -04:00
Eric Hawicz
e5ca4d3b3d Update ChangeLog, AUTHORS and issues_closed_for_0.18.md for an upcoming 0.18 release. 2024-09-15 11:54:50 -04:00
Eric Hawicz
e3db88d114 Merge pull request #871 from json-c/disallow-control-chars
Issue #867: disallow control characters in strict mode.
2024-09-15 11:22:58 -04:00
Eric Hawicz
6bfab90c87 Issue #867: disallow control characters in strict mode. 2024-09-15 10:37:45 -04:00
Eric Hawicz
ecb7a4c9a3 Merge pull request #864 from pavelzw/patch-1
Build pkg-config for msvc as well
2024-08-29 21:08:52 -04:00
Pavel Zwerschke
906d1581de Update CMakeLists.txt 2024-08-29 10:55:02 +02:00
Pavel Zwerschke
9b8d170d1f Build pkg-config for msvc as well 2024-06-18 00:11:13 +02:00
Eric Hawicz
6e481aa061 Merge pull request #861 from bhaible/handle-another-oom-condition
Handle yet another out-of-memory condition.
2024-05-05 09:49:26 -04:00
Eric Haszlakiewicz
781675758f Although CMAKE_C_COMPILER_FRONTEND_VARIANT is defined, it's not always set to some value. Fix builds for cases when it's blank. 2024-04-28 17:15:42 +00:00
Bruno Haible
833233faa8 Handle yet another out-of-memory condition.
duplocale() can return NULL, with errno set to ENOMEM.
In this case, bail out and set the current error code to
json_tokener_error_memory.
2024-04-22 01:54:10 +02:00
Eric Hawicz
31a22fb2da Issue #857: fix a few places where json_tokener should have been returning json_tokener_error_memory but wasn't. 2024-04-21 10:37:16 -04:00
Eric Hawicz
0051f2dbe0 Merge pull request #858 from alurm/master
json_tokener.h: json_tokener_parse_verbose: fix typo
2024-04-16 11:23:09 -04:00
Alan Urmancheev
0741ce3918 json_tokener.h: json_tokener_parse_verbose: fix typo 2024-04-16 14:56:00 +00:00
Eric Hawicz
e3464a2ab4 Issue #829: attempt to detect clang-cl.exe and pass MSVC-compatile command line arguments. 2024-03-29 22:40:45 -04:00
Eric Hawicz
61016412fb Issue #850: fill in & adjust the "@returns" docs for several functions. 2024-03-29 18:46:05 -04:00
Eric Hawicz
f2eac07129 doxygen can't handle html on heading, split that to separate lines
https://github.com/doxygen/doxygen/issues/10466
2024-03-29 18:45:22 -04:00
Eric Hawicz
e93ae70417 Fix issue #854: Set error=json_tokener_error_memory in json_tokener_parser_verbose() when allocating the tokener fails. 2024-03-29 18:09:12 -04:00
Eric Hawicz
0bc2dd5e60 Merge pull request #852 from fedefrancescon/docs/micro-typo-fix
docs: micro typo in json_object_put doc
2024-03-02 17:31:24 -05:00
Federico Francescon
9c44c7ed52 docs: micro typo in json_object_get doc 2024-03-02 22:41:10 +01:00
Eric Hawicz
401249cbdf Merge pull request #831 from neheb/1
rename WIN32 to _WIN32
2024-02-08 08:11:57 -05:00
Rosen Penev
66f7869219 rename WIN32 to _WIN32
The latter is the proper macro defined by Windows headers.

Fixes compilation under at least clang-cl which mandates function
declarations.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-02-07 18:06:55 -08:00
Eric Hawicz
dabed80523 Merge pull request #849 from rouault/random_seed_toctou
random_seed.c: add a Coverity Scan suppression
2024-01-08 12:45:39 -05:00
Even Rouault
f6c8eba84e random_seed.c: add a Coverity Scan suppression
Coverity Scan warns about the use of open() after stat() being a
potential TOCTOU (Time of check time of use) issue. But here there is no
such issue.
2024-01-07 20:55:38 +01:00
Eric Hawicz
76f819e51d Merge pull request #846 from evo-i/CMP0153
Use execute_process instead of exec_program
2023-12-17 07:23:07 -05:00
evo
eef983b1df Update CMakeLists.txt
Replace with `execute_process` available on v3.0+
2023-12-17 11:19:20 +03:00
evo
c0cf4bc23b Update CMakeLists.txt
Now saved to variable.
2023-12-17 00:32:21 +03:00
evo
e85c8ada54 Fix ECHO_OUTPUT_VARIABLE 2023-12-17 00:23:51 +03:00
evo
2c84476c77 [CMP0153](https://cmake.org/cmake/help/latest/policy/CMP0153.html) 2023-12-12 16:28:41 +03:00
Eric Haszlakiewicz
407ddb4350 Issue #842 - fix one particular sign conversion warning.
There are many others that show up if we were to add -Wsign-conversions,
but this is the only one using a literal constant.
2023-11-30 13:59:13 +00:00
Eric Hawicz
33337523f5 Merge pull request #839 from ffontaine/master
json_pointer.c: initialize idx
2023-11-08 18:11:13 -05:00
Fabrice Fontaine
7ff7eab603 json_pointer.c: initialize idx
Fix the following build failure with gcc 5:

/home/thomas/autobuild/instance-2/output-1/build/json-c-0.17/json_pointer.c: In function 'json_pointer_result_get_recursive':
/home/thomas/autobuild/instance-2/output-1/build/json-c-0.17/json_pointer.c:193:25: error: 'idx' may be used uninitialized in this function [-Werror=maybe-uninitialized]
    res->index_in_parent = idx;
                         ^

Fixes:
 - http://autobuild.buildroot.org/results/523b35a979d59121fe4e18c38171792b06233940/

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2023-11-08 22:42:48 +01:00
Eric Hawicz
88bce2b38e Merge pull request #837 from HansolChoe/fix-tokener-parse-ex-fuzzer
Add type checking for parsed objects in tokener_parse_ex_fuzzer
2023-10-19 07:49:32 -04:00
HansolChoe
a907f7dd1b tokener_parse_ex_fuzzer: add type checking for parsed object 2023-10-19 16:55:15 +09:00
Eric Hawicz
ad8b8afa7d Take 2 fixing the placement of json_tokener_error_memory in the enum. (json_tokener_error_size is an actual error, *not* a measure of the size of the enum!) 2023-09-22 22:26:21 -04:00
Eric Hawicz
4186537557 Merge pull request #827 from robert-pang/master
Resolve build issue in tokener_parse_ex_fuzzer.c
2023-09-11 00:13:45 +02:00
Eric Hawicz
9ff6b68baf Merge pull request #826 from emlix/cmake-policies
CMake: remove needless policy setting
2023-09-11 00:10:55 +02:00
Robert Pang
1a7defaa69 Fix variable and API names in tokener_parse_ex_fuzzer.cc to resolve build issue. 2023-09-05 13:58:51 -07:00
Rolf Eike Beer
c4a615a052 CMake: request all policies up to CMake 3.12 being set to NEW 2023-09-05 14:31:24 +02:00
Rolf Eike Beer
159822dd8a CMake: request consistent minimum version 2023-09-05 14:31:14 +02:00
Rolf Eike Beer
990acb4e19 CMake: remove needless policy setting
These policies were all introduced before CMake 3.9, so they will automatically
be initialized to the new behavior when requesting a minimum version of 3.9.
2023-09-05 12:14:46 +02:00
Eric Haszlakiewicz
502522a93d Update the master branch to version 0.17.99 2023-08-12 19:08:59 +00:00
Eric Hawicz
12ea386989 Add an abi-check.sh script that shows how to run the abi-compliance-checker, update release creation steps slightly. 2023-08-12 14:56:51 -04:00
Eric Haszlakiewicz
6dd8618170 Also fix messages returned from json_tokener_error_desc() (broke due to the ordering change of enum json_tokener_error) 2023-08-12 18:52:32 +00:00
Eric Hawicz
3dad6941da Move the json_tokener_error_memory entry to the end of enum json_tokener_error to restore binary compatibility with 0.16 2023-08-12 13:54:50 -04:00
Eric Hawicz
f7e9d8e216 Update the AUTHORS file and add issues_closed_for_0.17.md for the upcoming 0.17 release 2023-08-12 13:53:27 -04:00
Eric Hawicz
85ed501a3d Update the ChangeLog for 0.17 based on notable commits since 0.16 2023-08-08 17:31:38 -04:00
Eric Hawicz
077661f3d1 Fix issue #823: add back json_number_chars, but only because it's part of the public API. 2023-08-06 15:36:19 -04:00
Eric Hawicz
87127d31c6 Merge pull request #679 from GitMensch/patch-1
Let json-c be used with obsolete compilers
2023-08-05 22:32:37 -04:00
Eric Hawicz
1ee12100b6 PR #679: add workaround for old compilers w/o stdint.h (i.e. VS2008 and earlier) 2023-08-05 22:11:30 -04:00
Eric Hawicz
0bcfb6bc84 Merge pull request #822 from SSharshunov/master
Added option to disable app build
2023-08-04 11:47:23 -04:00
Sergey Sharshunov
bef40a342e Added option to disable app build 2023-08-04 19:46:50 +05:00
Eric Hawicz
2316dbae85 Merge pull request #702 from commodo/json_patch
json_patch: add first implementation only with patch application
2023-07-31 22:19:30 -04:00
Eric Hawicz
612ba56f06 Don't export json_pointer_get_internal, move json_object_array_insert_idx and json_patch_apply to the JSONC_0.17 section in json-c.sym 2023-07-31 22:18:04 -04:00
Eric Hawicz
469bc0e4bb Work around a somewhat misleading warning about "a function declaration without a prototype is deprecated in all versions of C" in test1.c 2023-07-31 22:18:04 -04:00
Eric Hawicz
ce3184243a Fix json_patch_apply handling of removing the whole document (i.e. "path":"").
Enable all disabled tests, add a few more including some with null documents.
2023-07-31 22:18:04 -04:00
Eric Hawicz
9dbf2880cc Adjust the behavior of the args passed to json_patch_apply to make it easier to do in place modifications, and add a struct json_patch_error to report more details on failures. 2023-07-31 22:18:04 -04:00
Eric Hawicz
a14a3a680c Fix an uninitialized memory access in json_pointer.
Add comments describing when the fields of the internal struct json_pointer_get_result are valid.
2023-07-31 22:18:03 -04:00
Eric Hawicz
efc530594b Create a json_pointer_private.h and move a few things there, fix test warnings, note array_list_insert_idx is private. 2023-07-31 22:18:03 -04:00
Alexandru Ardelean
3b8363fcdc tests: test_json_patch: add test suite for JSON patch
Essentially, this change adds the test cases from this repo:
   https://github.com/json-patch/json-patch-tests

Specifically:
   https://github.com/json-patch/json-patch-tests/blob/master/spec_tests.json
   https://github.com/json-patch/json-patch-tests/blob/master/tests.json

The files were taken at the date of this commit, at git hash
  ea3af85790cb72893d0676597814b7532019c24e

Some tests may not have an 'expected' or 'error' field. Those are ignored.
One test was disabled manually via "disabled_in_json_c", because it tries
an impossible test, i.e. to add 2 "op" fields in the same patch entry,
something which is impossible in a JSON object.

For the 'error' cases, right now we only test that an error happens.
Later, we can extend this to check the error codes.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:18:03 -04:00
Alexandru Ardelean
538b046884 json_patch: add first implementation only with patch application
Initially I wanted to also do a function that generates the JSON patch from
two JSON documents, but even just applying the JSON patch was a bit of
work, especially when needing to satisfy all the test-cases.

This change defines all the operation in the RFC6902. The addition isn't
too big (for the json_patch_apply() function), as part of the heavy lifting
is also done by JSON pointer logic.

All the ops were tested with the test-cases defined at:
  https://github.com/json-patch/json-patch-tests

RFC6902: https://tools.ietf.org/html/rfc6902

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:18:01 -04:00
Alexandru Ardelean
e4d9fbd52a json_pointer: split json_pointer_set_with_array_cb()
JSON patch is a bit more clear on how some array operations should be
handled. Unfortunately, handling them on a case-by-case is a bit tricky
because it's difficult to satisfy properly an 'add' operating with a 'move'
operation and the basic json_pointer_set().

With json_pointer_set{f}() we use json_object_array_put_idx() to insert a
value at a certain index.

With JSON patch:
* for the 'add' operation, we need to insert a value at a given index,
  which means shifting existing values by one to the right
  - also, we cannot allow values to be inserted/added outside the bounds of
    the array
* a 'move' operation, is described as a 'remove' and then an 'add';
  for arrays this complicates things, because when we want to a move a
  value within the array, we have to remove it first (during which the size
  of the array is reduced by one); when the size of the array is reduced by
  one, we can't add it to the last position in the array (before the
  remove)

The only sane method to handle this (after a few considerations) is to
provide a callback to the function that does the final put/insert into
the array. That way, we can do some final checks where these are needed to
handle each corner-case.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:17:30 -04:00
Alexandru Ardelean
1c38dea651 json_pointer: move array out-of-bounds check outside of is_valid_index()
The out-of-bounds check is useful when trying to index/obtain a value from
an array.
However, when we set a value to a specific JSON pointer, we can allow
values that are outside the length of the current array.
The RFC6901 doc isn't clear on that aspect, and doing so is a bit more
in-line with how json_object_array_{put,insert}_idx() functions behave.

This changes the behavior of json_pointer_set{f}() because now a value can
be set anywhere in the array.

Also, added a test-case for this behavior change.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:17:30 -04:00
Alexandru Ardelean
5a46a3b76d json_pointer: introduce json_pointer_get_internal() for internal usage
For JSON patch, we require that we get access to the parent of a JSON
object as well in order to do some operations via the API.

For example, given the object:
{
  "foo": "bar",
  "array", [ 1, 2, 3]
}

Using JSON pointer with the path
 * '/foo' will return 'bar' of type string
 * '/array/0' will return '1', of type integer

The problem is, that if we do 'json_object_put()' on any of the objects
above, this will not detach them from the parent, because there is no
information back to the parent.

One way to fix this, is to introduce links back to the parent, and have
these links be made by 'json_object_array_{put,insert}_idx()' and
'json_object_object_add{_ex}()'[1].

[1] For json_object_object_add_ex() we would need to de-constify the second
parameter, as we need to change it's internal state when being added to a
parent object. It may break some applications, but who knows.

But, since this information is needed mostly for JSON patch, another way to
address this, is to also retrieve the parent of an object via JSON pointer
and use json_object_object_del() and json_object_array_del_idx() on the
object's parent.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:17:30 -04:00
Alexandru Ardelean
43d3118935 json_pointer: convert index to size_t type
The index cannot be negative when parsing in is_valid_index(), because we
don't allow the '-' character in a string before we get to the strtol()
function.

So, might as well remove the negative check (for idx) in is_valid_index()
and convert it to size_t. That may allow for higher values for the index
(which can be insane, but some people may want to try it).

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:17:30 -04:00
Alexandru Ardelean
d5c5b2caec tests: test1: add test cases for json_object_array_insert_idx()
This change adds a few test cases to test the behavior of the new
json_object_array_insert_idx() function, to make sure it behaves according
to specification in doc-string.

This test uses assert() vs the old method of comparing outputs.
This will cause the test to fail because the outputs won't match, since the
assert() will kick in.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:17:30 -04:00
Alexandru Ardelean
a86d7a8f5a json_object: introduce json_object_array_insert_idx() API function
The behavior of the json_object_array_put_idx() is that, if a user wants to
insert an element inside a JSON array, the element will be replaced.

For some cases, a user would want to insert an element into the JSON array
and shift the elements to the right.

For indexes that are outside the length of the current array this behaves
like json_object_array_put_idx().
If a user wants to enforce that the JSON array is not expanded, then the
json_object_array_length() function can be used to guard against that.

The main driver for this change is JSON patch, where the 'add' operation in
an array means inserting a value at a certain index and shifting everything
by one.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:17:30 -04:00
Alexandru Ardelean
5568916eb1 json_pointer: fix comments about printf() variants of set/get()
These were wrong. Some details about the json_pointer_setf() &
json_pointer_getf() were added in the json_pointer_set() &
json_pointer_get() doc-strings.

This change removes them.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2023-07-31 22:17:30 -04:00
Eric Hawicz
71d845e819 Issue #668: add the option to specify "cmake -DUSELOCALE_NEEDS_FREELOCALE=1" to work around a bug in older versions of FreeBSD (<12.4). 2023-07-30 13:38:15 -04:00
Eric Hawicz
c8b0a90e95 Issue #808: turn off -Wshorten-64-to-32 for clang builds (aka iOS builds) to workaround "implicit conversion loses integer precision" warnings. 2023-07-30 11:38:01 -04:00
Eric Hawicz
9b36c72945 Merge pull request #772 from cosmo-ray/color
add JSON_C_TO_STRING_COLOR option
2023-07-13 18:04:48 -04:00
Matthias Gatto
9803032b9d add JSON_C_TO_STRING_COLOR option
This option enable color in json_object_to_json_string_ext.
I've try to made something similar to jq output,
but I've color true/false and null in purple,
as it's what is common color scheme used in programing language in emacs.

also add a '-c' option into json_parser to test it.

note: that I could have done a color() function similar to
what is done with indent(), but as the code is pretty small
I've keep it as it. so if you want  me to use a subfunction
tell me and I'll do it.

Signed-off-by: Matthias Gatto <matthias.gatto@protonmail.com>
2023-07-10 20:45:20 +02:00
Eric Hawicz
bdfdb5fe10 Skip apps when we're included in someone else's build. Inspired by ssrlive in PR #813. 2023-07-06 20:56:49 -04:00
Eric Hawicz
11311ed2a3 Fix the -f option to apps/json_parse, add a -F <arg> option to specify arbitrary flags to pass to json_object_to_json_string_ext(). 2023-07-04 12:00:31 -04:00
Eric Hawicz
e9d3ab209a Merge pull request #759 from c3h2-ctf/truncation
json_tokener_parse_ex: handle out of memory errors
2023-07-04 11:45:57 -04:00
Eric Haszlakiewicz
4d529f92dc Issue #688: Only exclude generated doc/* files, keep the CMakeLists.txt, etc... so the nodoc tarball can still be built. 2023-07-01 18:02:00 +00:00
Eric Haszlakiewicz
d0f32a5a43 Stop linking a copy of _json_c_strerror() (strerror_override.c) into the tests.
That hasn't been needed since since commit 6068d3f, which changed that code to
 check an env var instead ("_JSON_C_STRERROR_ENABLE").
Fixes issue #812, about dup symbols in static builds with clang.
2023-03-28 23:08:39 +00:00
Eric Hawicz
efd536af48 Merge pull request #810 from dijonkitchen/patch-1
docs: update to Internet Standard reference
2023-03-10 14:54:44 -05:00
JC (Jonathan Chen)
4d5507f5dc docs: update to Internet Standard reference 2023-03-10 11:10:29 -05:00
Eric Haszlakiewicz
d1716fe431 Bump up the minimum cmake version to 3.9.
This gets us up to a version that supports features we're already using
(i.e. add_compile_options), but stops short of a cmake that requires
c++11, which some OSes still don't support.
Closes issue #774
2023-02-23 01:01:14 +00:00
Eric Haszlakiewicz
1741bcd3ea Issue #570: note brief instructions for building on Android. 2023-01-05 00:25:09 +00:00
Eric Hawicz
343f24f920 Merge pull request #804 from yrashk/cmp-0042
Problem: cmake 3.25.1 warns about CMP0042 not being set
2022-12-27 09:32:33 -05:00
Eric Hawicz
6ec5e5842d Merge pull request #803 from yrashk/patch-2
Problem: confusing error message in snprintf_compat.h
2022-12-27 09:26:30 -05:00
Eric Hawicz
b6ba9429d0 Merge pull request #802 from yrashk/patch-1
Problem: modern CMake warns about version 2.8
2022-12-27 09:25:58 -05:00
Yurii Rashkovskii
3cc0c47221 Problem: cmake 3.25.1 warns about CMP0042 not being set
Solution: set it explictly to OLD behavior
2022-12-23 15:05:46 -08:00
Yurii Rashkovskii
7b971b52aa Problem: confusing error message in snprintf_compat.h
Solution: fix it to reflect what's happening properly
2022-12-23 14:35:36 -08:00
Yurii Rashkovskii
debe8b70b7 Problem: modern CMake warns about version 2.8
The warning is as follows:

```
Compatibility with CMake < 2.8.12 will be removed from a future version of CMake.
```

Solution: set it at 2.8.12
2022-12-23 14:33:31 -08:00
Eric Hawicz
79c147203e Merge pull request #796 from fedefrancescon/test-add-int-get
Added Test for get int functions
2022-11-16 22:23:11 -05:00
Federico Francescon
1a2fdc49e7 Fixed test_int_get expected output 2022-11-16 01:33:02 +01:00
Federico Francescon
b1fb1508ee Added basic tests for json_object_get_int, json_object_get_int64, json_object_get_uint64 2022-11-16 00:49:18 +01:00
Eric Haszlakiewicz
bc35549f90 Update expected output for test_parse, missed in the previous commit. 2022-10-30 19:42:42 +00:00
Eric Haszlakiewicz
d6f46ae104 Explicitly check for integer overflow/underflow when parsing integers with JSON_TOKENER_STRICT. 2022-10-30 19:39:30 +00:00
Eric Haszlakiewicz
c50bf9df9c Apply same EINVAL handling to json_parse_uint64() as was done for json_parse_int64(). Document that overflow/underflow for these functions is not an error, but sets errno=ERANGE. 2022-10-30 03:25:32 +00:00
Eric Haszlakiewicz
57bef5edc4 Issue #792 - set errno=EINVAL if parsing the string in json_parse_int64 fails, to match the docs for json_object_get_int. 2022-10-26 02:19:38 +00:00
Eric Hawicz
777dd06be8 Merge pull request #790 from Sarcares/patch-1
Small update to README file
2022-09-13 22:15:48 -04:00
Luca Mannella
84248a7884 Small update to README file
it could be necessary to execute make install
2022-09-12 17:29:47 +02:00
Eric Hawicz
81f0807b63 Merge pull request #784 from rouault/get_time_seed_coverity_scan_silence
get_time_seed(): silence warning emitted by Coverity Scan static analyzer
2022-08-16 18:10:06 -04:00
Even Rouault
57ea393004 get_time_seed(): silence warning emitted by Coverity Scan static analyzer
It warns about the return of time() being truncated to 32 bit, which is
not an issue here.
(this warning was emitted because of the https://github.com/OSGeo/gdal
project embedding a copy of libjson-c and running Coverity Scan
analysis)
2022-08-16 11:11:58 +02:00
Eric Hawicz
9417f4e726 Merge pull request #783 from kraj/master
Fix build with clang-15+
2022-08-14 08:19:19 -04:00
Khem Raj
257b29c991 json_pointer.c: Move idx_val declaration to top of function
This helps compiling with MS compiler, error seems to be
due to defining a variable within the body of the function
its allowed in c99 but not in c89. This should fix build with
MSVC 16.0.40219.1 compiler from Visual Studio 14 2015

Signed-off-by: Khem Raj <raj.khem@gmail.com>
2022-08-14 00:46:28 -07:00
Khem Raj
d1deed499f json_inttypes.h: Define ssize_t on windows platforms
Signed-off-by: Khem Raj <raj.khem@gmail.com>
2022-08-13 21:45:11 -07:00
Khem Raj
6eca65617a Fix build with clang-15+
Fixes
json_util.c:63:35: error: a function declaration without a prototype is deprecated in all versions of C [-We
rror,-Wstrict-prototypes]
const char *json_util_get_last_err()
                                  ^
                                   void

Signed-off-by: Khem Raj <raj.khem@gmail.com>
2022-08-13 20:47:20 -07:00
Eric Hawicz
ac4dfa44cb Merge pull request #782 from DimitriPapadopoulos/codespell
Fix typos found by codespell
2022-08-13 09:43:50 -04:00
Dimitri Papadopoulos
9462c0a7b5 Fix typos found by codespell
Do not fix typos from past commits found in release notes.
2022-08-13 15:47:40 +03:00
Eric Haszlakiewicz
bdd5e03d6e Apply some of the fixes from PR #740, although by using size_t instead of castings. 2022-07-31 19:28:48 +00:00
Eric Hawicz
4b0c6de760 Merge pull request #757 from c3h2-ctf/big
json_object_from_fd_ex: fail if file is too large
2022-07-31 14:27:32 -04:00
Eric Haszlakiewicz
253a5fa99d Issue #705: disable locale handling when building for a uClibc system because its duplocale() function (intentionally) crashes. 2022-07-30 20:27:35 +00:00
Eric Haszlakiewicz
9749b0cb66 When serializing with JSON_C_TO_STRING_PRETTY set, keep the opening and closing curly or square braces on same line for empty objects or arrays. Issue #778. 2022-07-30 19:27:14 +00:00
Eric Haszlakiewicz
2e9b7456a5 Update Travis links to point at travis-ci.com instead of travis-ci.org 2022-07-26 23:47:58 +00:00
Eric Haszlakiewicz
5282e73600 Entirely drop mode bits from open(O_RDONLY) to avoid warnings on certain platforms. Fixes issue #779. 2022-07-26 23:43:30 +00:00
Eric Haszlakiewicz
8b35a78d29 Trim a few travis builds, update others to more recent toolchains. 2022-07-24 20:37:14 +00:00
Eric Haszlakiewicz
22773b1e51 Fix the expected output for test_set_serializer. 2022-07-24 19:00:27 +00:00
Eric Haszlakiewicz
16208fc01a Add test to check for the memory leak mentioned in issue #781 2022-07-24 18:59:26 +00:00
Eric Hawicz
2a2d861bc0 Merge pull request #781 from dddaniel/master
Fix memory leak with emtpy strings in json_object_set_string
2022-07-24 14:15:42 -04:00
Daniel Danzberger
213bb5caa1 Fix memory leak with emtpy strings in json_object_set_string
When a json string object is updated with a bigger string, a new
malloc'ed buffer is used to store the new string and it's size is made
negative to indicate that an external buffer is in use.

When that same json string object get's updated again with an empty
stirng (size = 0), the new external malloc'ed buffer is still used.
But the fact that the new size value is not negative removes the
indicator that the externally malloc'ed buffer is used.

This becomes a problem when the object get's updated again with any
other string, because a new buffer will be malloced and linked to the
object while to old one won't be free'd.

This causes a memory leak when updating a json string with
json_object_set_stirng() which has previously been updated
with an empty string.

Example:
--
obj = json_object_new_string("data");
json_object_set_string(obj, "more data");
json_object_set_string(obj, "");
json_object_set_string(obj, "other data"); /* leaks */
--

This commit fixes the issue by free'ing the external buffer when an
empty string is set and use the internal one again.

Signed-off-by: Daniel Danzberger <daniel@dd-wrt.com>
2022-07-24 19:03:49 +02:00
Eric Haszlakiewicz
d28ac67dde Fix issue #775 - use a loop instead of list(TRANSFORM ...) to support cmake < 3.12. 2022-06-27 02:15:24 +00:00
Eric Hawicz
5d98f7825a Merge pull request #776 from stoeckmann/typo
Fix typo
2022-06-21 21:39:00 -04:00
Tobias Stoeckmann
f63573460c Fix typo 2022-06-21 14:39:01 +02:00
Eric Haszlakiewicz
6ee0a35a20 Fix #771/#768 by marking usage() as "noreturn" instead of using "FALLTHRU" in the case statement where it'd called. 2022-06-13 01:02:53 +00:00
Eric Hawicz
11546bfd07 Merge pull request #769 from An7ar35/issue-768
Issue #768
2022-06-03 23:14:16 -04:00
An7ar35
5d00384e0d missing 'FALLTHRU' added to sort out error messages when using "-Werror=implicit-fallthrough" flag 2022-06-01 12:36:33 +02:00
Eric Haszlakiewicz
49c3721a5a Unset NDEBUG so assert() is enabled in all tests. One more fix there when building with -DCMAKE_BUILD_TYPE=release. 2022-05-30 15:39:54 +00:00
Eric Haszlakiewicz
2f0942bdd7 Specify dependent libraries, including -lbsd, in a more consistent way so linking against a static json-c works better. Related issue #766
Use target_link_libraries, plus fill in Libs.private in json-c.pc so pkg-config --static --libs works appropriately.
Also, only link against libbsd when arc4random is actually found there.
2022-05-30 15:30:11 +00:00
Eric Haszlakiewicz
6ba1adf8ef Include the tests and apps directories at the _end_, since order in the
cmake rules matters, and fix compile warnings now that we're building
those sources with all the regular flags.
2022-05-30 14:33:16 +00:00
Eric Haszlakiewicz
86b46cac07 Add --disable-static and --disable-dynamic options to the cmake-configure script. 2022-05-30 14:31:08 +00:00
Eric Haszlakiewicz
f2fc1ca00a Note the DSIABLE_JSON_POINTER and DISABLE_EXTRA_LIBS build options on the README 2022-04-14 12:57:43 +00:00
Eric Haszlakiewicz
acccefd770 Add a "Getting Help" section to the README. 2022-04-14 12:55:32 +00:00
Eric Haszlakiewicz
de5a64888a Update the master branch to version 16.99 2022-04-14 12:29:39 +00:00
Eric Haszlakiewicz
7e3eba72db Clarify how to set the uploaded release tarballs to be publically readable. 2022-04-14 12:17:59 +00:00
Eric Haszlakiewicz
7d303478a4 Update the ChangeLog with recent changes, in preparation for a 0.16 release.
Add a list of issues closed for this release.
2022-04-14 01:05:38 +00:00
Eric Haszlakiewicz
66dcdf549e Add new authors since the 0.15 release, tweak instructions for how to update that list. 2022-04-14 01:04:53 +00:00
Eric Hawicz
4902f9cb87 Merge pull request #765 from jvoisin/patch-1
Improve a bit the coverage of the fuzzer
2022-04-13 14:28:51 -04:00
Julien Voisin
558d48a6f3 Improve a bit the coverage of the fuzzer 2022-04-13 15:42:46 +02:00
Eric Haszlakiewicz
46b58ad9e7 Fix issue #764: adjust test_util_file to match new output (changed in 6e53347) 2022-04-08 22:58:24 +00:00
Eric Hawicz
0b937f6173 Merge pull request #758 from c3h2-ctf/context
Preserve context if out of memory
2022-04-04 18:01:39 -04:00
Eric Hawicz
65274cf3b5 Update issue templates 2022-04-04 17:10:31 -04:00
Tobias Stoeckmann
0c0d901443 Preserve context if out of memory
If memory allocation fails in json_c_set_serialization_double_format or
json_object_copy_serializer_data then return with an error value and
preserve previous values without overriding them with NULL.
2022-03-31 18:05:48 +02:00
Eric Haszlakiewicz
e244146dfd Remove single quote from #error line. Fixes issue #761 2022-03-25 01:12:07 +00:00
Eric Hawicz
a1c092bd7b Merge pull request #760 from BonsaiYi/error
Code style: removed unneeded double-quotes
2022-03-23 00:14:36 -04:00
BonsaY
716978f20d Code style removed unneeded double-quotes
this way, it complies with the other #error usages
2022-03-21 21:10:45 +01:00
Tobias Stoeckmann
9e6acc9a4e json_tokener_parse_ex: handle out of memory errors
Do not silently truncate values or skip entries if out of memory errors
occur.

Proof of Concept:

- Create poc.c, a program which creates an eight megabyte large json
  object with key "A" and a lot of "B"s as value, one of them is
  UTF-formatted:

```c
 #include <err.h>
 #include <stdio.h>
 #include <string.h>

 #include "json.h"

 #define STR_LEN (8 * 1024 * 1024)
 #define STR_PREFIX "{ \"A\": \""
 #define STR_SUFFIX "\\u0042\" }"

int main(void) {
  char *str;
  struct json_tokener *tok;
  struct json_object *obj;

  if ((tok = json_tokener_new()) == NULL)
    errx(1, "json_tokener_new");

  if ((str = malloc(STR_LEN)) == NULL)
    err(1, "malloc");
  memset(str, 'B', STR_LEN);
  memcpy(str, STR_PREFIX, sizeof(STR_PREFIX) - 1);
  memcpy(str + STR_LEN - sizeof(STR_SUFFIX), STR_SUFFIX, sizeof(STR_SUFFIX));

  obj = json_tokener_parse(str);
  free(str);

  printf("%p\n", obj);
  if (obj != NULL) {
    printf("%.*s\n", 50, json_object_to_json_string(obj));
    json_object_put(obj);
  }

  json_tokener_free(tok);
  return 0;
}
```
- Compile and run poc, assuming you have enough free heap space:
```
gcc $(pkg-config --cflags --libs) -o poc poc.c
./poc
0x559421e15de0
{ "A": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
```
- Reduce available heap and run again, which leads to truncation:
```
ulimit -d 10000
./poc
0x555a5b453de0
{ "A": "B" }
```
- Compile json-c with this change and run with reduced heap again:
```
ulimit -d 10000
./poc
(nil)
```

The output is limited to 70 characters, i.e. json-c parses the 8 MB
string correctly but the poc does not print all of them to the screen.

The truncation occurs because the parser tries to add all chars up
to the UTF-8 formatted 'B' at once. Since memory is limited to 10 MB
there is not enough for this operation. The parser does not fail but
continues normally.

Another possibility is to create a json file close to 2 GB and run a
program on a system with limited amount of RAM, i.e. around 3 GB. But
ulimit restrictions are much easier for proof of concepts.

Treat memory errors correctly and abort operations.
2022-03-20 17:42:47 +01:00
Tobias Stoeckmann
5accae04bb json_object_from_fd_ex: fail if file is too large
If the input file is too large to fit into a printbuf then return an
error value instead of silently truncating the parsed content.

This introduces errno handling into printbuf to distinguish between an
input file being too large and running out of memory.
2022-03-20 13:17:37 +01:00
Eric Hawicz
79459b2de2 Merge pull request #755 from c3h2-ctf/error
Fix error messages
2022-03-19 10:05:48 -04:00
Eric Hawicz
2326e34390 Merge pull request #754 from c3h2-ctf/doc
Minor improvements to documentation
2022-03-19 10:04:58 -04:00
Tobias Stoeckmann
6e533471a8 Fix error messages
Error messages did not reflect actual function names.
2022-03-19 10:59:43 +01:00
Tobias Stoeckmann
a7d85bfba9 Fix typos in comments
Spotted during code reviews.
2022-03-19 10:39:56 +01:00
Tobias Stoeckmann
cbc603b587 Adjusted URLs
Most of these sites support HTTPS (some forward to HTTPS when accessing
the HTTP versions). Use HTTPS directly if supported.

Some URLs led to 404 error pages. Adjusted the links to point to
new locations.

I did not adjust the Microsoft HTML Help Workshop link because it seems
that this software is not available anymore. Instead of removing the
link entirely I kept it there in case it helps someone to find the
software on archived websites.
2022-03-19 10:34:55 +01:00
Eric Hawicz
f0d93cee14 Merge pull request #748 from c3h2-ctf/printbuf
sprintbuf(): test for all vsnprintf error values
2022-03-07 00:06:57 -05:00
Eric Hawicz
9b832c8710 Merge pull request #752 from c3h2-ctf/typos
Fix typos
2022-03-06 23:33:42 -05:00
Tobias Stoeckmann
543a8eb5f9 Fix typos
Mostly found with codespell and during code review.
2022-03-06 16:07:36 +01:00
Tobias Stoeckmann
94b2a0cb29 sprintbuf(): test for all vsnprintf error values
The POSIX specification states that vsnprintf returns "a negative value"
in case of error, but the code checks explicitly only for -1.
2022-03-06 16:05:32 +01:00
Eric Hawicz
a4389f4f30 Merge pull request #749 from c3h2-ctf/sprintbuf
sprintbuf(): handle printbuf_memappend errors
2022-03-03 21:32:09 -05:00
Eric Hawicz
4493db76d6 Merge pull request #750 from c3h2-ctf/clearmem
printbuf_memset(): set gaps to zero
2022-03-03 21:29:27 -05:00
Eric Hawicz
1491d92038 Merge pull request #751 from c3h2-ctf/arguments
printbuf: do not allow invalid arguments
2022-03-03 21:26:22 -05:00
Tobias Stoeckmann
d07da04c14 sprintbuf(): handle printbuf_memappend errors
If errors occur in printbuf_memappend, then these errors should be
propagated through sprintbuf to indicate the error to the user.

Proof of Concept:
```
 #include <err.h>
 #include <limits.h>
 #include <stdio.h>

 #include "json.h"

 int
 main(void) {
  struct printbuf *pb;
  if ((pb = printbuf_new()) == NULL)
   err(1, "printbuf_new");
  if (printbuf_memset(pb, INT_MAX - 9, 'a', 1) < 0)
   errx(1, "printbuf_memset");
  printf("length: %d\n", printbuf_length(pb));
  printf("sprintbuf: %d\n", sprintbuf(pb, "string too long"));
  printf("length: %d\n", printbuf_length(pb));
  printbuf_free(pb);
  return 0;
 }
```

You can see that sprintbuf does not return an error but length is still
the same, i.e. the string "string too long" has not been appended.

I would like to add this as a unit test but it really depends on the
operating system if printbuf_memset() would fail if not enough memory is
available or not.
2022-03-03 21:24:27 +01:00
Tobias Stoeckmann
63c602ff80 printbuf_memset(): set gaps to zero
It is possible to have a printbuf with "gaps", i.e. areas within the
print buffer which have not been initialized by using printbuf_memset.

Always clear memory in such cases.

Example:
```
struct printbuf *pb = printbuf_new();
printbuf_memset(pb, 10, 'a', 2);
```
In this case pb->buf[0] is '\0' but pb->buf[1] up to pb->buf[9] are
not set. The length would be 12 due to successful printbuf_memset.
2022-03-03 21:18:53 +01:00
Tobias Stoeckmann
4355242477 printbuf: do not allow invalid arguments
If invalid arguments are passed to printbuf functions return -1 to
protect printbuf internals.
2022-03-03 21:15:19 +01:00
Eric Hawicz
bd56cc8bf7 Merge pull request #745 from c3h2-ctf/vasprintf
vasprintf(): avoid out of memory accesses
2022-02-27 08:34:21 -05:00
Eric Hawicz
723ac8cbb5 Merge pull request #746 from rouault/typo_fixes
Fix typos in code comments and ChangeLog
2022-02-26 23:45:15 -05:00
Even Rouault
3bb54f97e7 Fix typos in code comments and ChangeLog 2022-02-25 00:14:47 +01:00
Tobias Stoeckmann
5c72257220 vasprintf(): avoid out of memory accesses
Systems without vasprintf fall back to implementation in header file
vasprintf_compat.h. This version could run into heap overflow issues
with very long arguments or formats provoking a lot of output.

The vsnprintf function returns a negative value if more than INT_MAX
characters would be written since its int return type could not
handle this (and %n couldn't handle it either).

Before testing for a possible error value the additional char for
\0 is already added. A -1 error code would not be detected.

Increment only after implicitly casting to an unsigned value to avoid
signed integer overflow if INT_MAX has been returned.

Use va_copy to duplicate the original ap argument for multiple uses
on non-WIN32 systems. At least with glibc the test suite would fail
because the arguments are not reset after leaving the vsnprintf call.

Removed support for apparently very old glibc versions which do not
comply with vsnprintf standard descriptions. It breaks support for
modern ones which are not forced to return -1 in case of error. The
standard specifies merely "a negative value".

How to reproduce:

- Use a system without vasprintf
- Alternatively remove -D_GNU_SOURCE from CMakeLists.txt
- Compile and run:

  #include "json.h"
  int main(void) {
    struct printbuf *pb = printbuf_new();
    sprintbuf(pb, "prefix %2147483647s", "*");
    printbuf_free(pb);
    return 0;
  }
2022-02-24 23:06:57 +01:00
Eric Hawicz
66cce0a1d7 Merge pull request #739 from rouault/avoid_unsigned_integer_overflow
json_escape_str(): avoid harmless unsigned integer overflow
2022-02-18 20:21:18 -05:00
Eric Hawicz
b11f79c52c Merge pull request #741 from rouault/json_type_to_name_formatter
json_type_to_name(): use correct printf() formatter
2022-02-18 20:08:31 -05:00
Eric Hawicz
78246db72f Merge pull request #742 from rouault/json_object_copy_serializer_data_add_assertion
json_object_copy_serializer_data(): add assertion
2022-02-18 20:08:02 -05:00
Eric Hawicz
a1ea216878 Merge pull request #744 from Kizuna-Meraki/close-random
Close file on error path.
2022-02-18 20:07:33 -05:00
Kizuna-Meraki
d783ad76c7 Close file on error path.
The file was only be closed when there was no error and
was being left open when there was an error. By moving
the close(fd) statement out of the if-clause, the file
can be close regardless if there is an error or not.
After the file is closed, it can be checked for errors.
2022-02-17 21:27:01 +01:00
Even Rouault
f2c0df404b json_type_to_name(): use correct printf() formatter
Was detected by Coverity Scan when analyzing GDAL's code base which has
a copy of json-c
2022-01-31 00:27:41 +01:00
Even Rouault
82d9433813 json_object_copy_serializer_data(): add assertion
This makes Coverity Scan happier since it believes that the initial
check ``if (!src->_userdata && !src->_user_delete)`` could mean that
src->_user_data may be nullptr.
2022-01-16 20:50:58 +01:00
Even Rouault
296db618e9 json_escape_str(): avoid harmless unsigned integer overflow
Current behaviour is perfectly valid, since wrap-over upon overflow is
well defined behaviour for unsigned types, but it is nevertheless nice to be
able to build with -fsanitize=undefined,unsigned-integer-overflow

There is no significant effect on the generated assembly as can be seen
on the diff of objdump -d output on a optimized build (the compiler
just decided to switch the order of a comparison):

@@ -135,8 +135,8 @@
  1d0:	0f 84 70 ff ff ff    	je     146 <json_escape_str+0x146>
  1d6:	4c 3b 24 24          	cmp    (%rsp),%r12
  1da:	0f 85 2d ff ff ff    	jne    10d <json_escape_str+0x10d>
- 1e0:	49 39 f4             	cmp    %rsi,%r12
- 1e3:	0f 87 b7 00 00 00    	ja     2a0 <json_escape_str+0x2a0>
+ 1e0:	4c 39 e6             	cmp    %r12,%rsi
+ 1e3:	0f 82 b7 00 00 00    	jb     2a0 <json_escape_str+0x2a0>
  1e9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax
  1ee:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  1f5:	00 00
2022-01-12 23:44:39 +01:00
Eric Hawicz
3b4f1e92ad Merge pull request #737 from tniessen/patch-1
Fix typo in README
2022-01-11 21:25:38 -05:00
Tobias Nießen
928fa96db7 Fix typo in README 2022-01-10 00:12:25 +01:00
Eric Haszlakiewicz
a5c2e11460 Cause the cmake include dirs to also have ${CMAKE_INSTALL_INCLUDEDIR}/json-c, so downstream packages that use cmake to link against json-c can choose whether to include headers as just e.g. #include <json_object.h>, if they care to do so.
Update the README to better explain this, and make a few other tweaks.
2021-12-22 02:52:37 +00:00
Eric Haszlakiewicz
2d2382d709 Add linkhash accessor functions (lh_table_head(), lh_entry_next(), etc...) to pave the way for making the lh_table and lh_entry structure opaque in the future.
Update the docs to mark all members of those structures deprecated, and
 suggest what to use instead.
2021-11-30 03:27:55 +00:00
Eric Haszlakiewicz
320548caf6 Drop the REFCOUNT_DEBUG code, it hasn't been used in ages. 2021-11-30 03:08:02 +00:00
Eric Hawicz
9a90b8477b Merge pull request #734 from json-c/newer-appveyor
Newer appveyor config for VS2022 etc...
Update the appveyor config to specify "image" instead of just "os", and build for VS2017, VS2019 and VS2022.
2021-11-29 13:40:41 -05:00
Eric Haszlakiewicz
1cb56e705f Put the most recent image first in appveyor builds. 2021-11-29 18:21:05 +00:00
Eric Haszlakiewicz
1763edaba3 Skip most "Release" builds. s/b_config/CONFIGURATION/ to fix artifact creation. 2021-11-29 18:18:48 +00:00
Eric Haszlakiewicz
a5fed59756 There doesn't seem to be a way to extend the appveyor build matrix for just one image, so instead list a whole bunch of excluded builds. 2021-11-27 02:55:29 +00:00
Eric Haszlakiewicz
0910e18bbd Use the newer appveyor images to build for VS2017 and VS2019. Trim the number of builds we do. 2021-11-27 02:29:26 +00:00
Eric Haszlakiewicz
f1fb9abf14 Note the need to also set CTEST_OUTPUT_ON_FAILURE to get test output 2021-11-26 22:49:42 +00:00
Eric Hawicz
42aa6f7257 Merge pull request #732 from DiracResearch/fix/static_include_dirs
Fix/static include dirs
2021-11-11 23:52:43 -05:00
Robert Bielik
286b4fdd27 Fix uninitialized value error for clang-8 msan 2021-11-11 10:23:05 +01:00
Robert Bielik
a66a6cc51b Fix for clang ub sanitizer 2021-11-11 09:51:18 +01:00
Robert Bielik
21f767f63f Add target include dirs for static library as well 2021-11-11 09:37:53 +01:00
Eric Hawicz
9b0fb2b33e Merge pull request #727 from jobol/propo2
Really use prefix JSON_C_OBJECT_ADD_
2021-10-22 19:06:11 -04:00
José Bollo
8bf3b45a29 Really use prefix JSON_C_OBJECT_ADD_*
This change introduces JSON_C_OBJECT_ADD_CONSTANT_KEY
as a replacement of JSON_C_OBJECT_KEY_IS_CONSTANT.

The description of json_object_object_add_ex tells to
look at the flags JSON_C_OBJECT_ADD_* but it is not
for JSON_C_OBJECT_KEY_IS_CONSTANT.

From the point of vue of a developper using json-c,
the function json_object_object_add_ex is mainly used,
not the hash facility, it seems more natural to provide
a regular naming of prefix JSON_C_OBJECT_ADD_CONSTANT_KEY.
2021-10-19 11:18:17 +02:00
Eric Hawicz
05c5d15075 Merge pull request #729 from DeX77/patch-1
* don't assume includedir
2021-10-17 14:23:28 -04:00
DeX77
d6d4b71d7d * don't assume includedir
This change syncs the public header include install location with what gets written into pkgconfig file.
2021-10-15 11:12:39 +02:00
Eric Hawicz
382f9462c2 Merge pull request #726 from leongross/fix/test-cmake3.1
fix cmake version for tests
2021-10-09 09:17:25 -04:00
Leon Gross
8be2ffdcd7 fix cmake version for tests 2021-10-06 15:29:33 +02:00
Eric Hawicz
4fc44f32df Merge pull request #722 from imaami/fix-json_tokener_new_ex-use-after-free
Fix use-after-free in json_tokener_new_ex()
2021-09-04 23:16:13 -04:00
Juuso Alasuutari
9361d8d3a8 Fix use-after-free in json_tokener_new_ex()
The failure path taken in the event of printbuf_new() returning NULL
calls free() on tok->stack after already having freed tok. Swap the
order of the two calls to fix an obvious memory access violation.

Fixes: bcb6d7d347 ("Handle allocation failure in json_tokener_new_ex")
Signed-off-by: Juuso Alasuutari <juuso.alasuutari@gmail.com>
2021-09-04 20:14:30 +03:00
Eric Hawicz
dc1ef7d566 Merge pull request #718 from Pawday/master
CMake create uninstall target if unix generator is used
2021-07-31 22:46:01 -04:00
Pawday
e91e4cc9fb Setted cmake "uninstall" target to exist in unix like operating systems only 2021-07-26 18:52:29 +03:00
Eric Haszlakiewicz
f61f1a7a91 Add workaround for Visual Studio not knowing about "inline". 2021-07-25 20:31:59 +00:00
Eric Haszlakiewicz
db6a6cc2b2 Switch the Travis build to use osx_image: xcode12.5, in an attempt to avoid timeouts with Homebrew. 2021-07-25 20:03:55 +00:00
Eric Haszlakiewicz
6a0df2609e Merge some old work to include (some of) PR #464 into the current master branch. 2021-07-25 19:07:06 +00:00
Eric Haszlakiewicz
8c727e5ce1 Only define an "uninstall" target if it's not already defined (e.g. by projects that include json-c) 2021-07-25 15:11:11 +00:00
Eric Hawicz
be9671e48a Merge pull request #714 from Hex052/clang-format_AfterCaseLabel
Add AfterCaseLabel to .clang-format
2021-07-05 18:18:06 -04:00
Hex052
9dde931a1c Add AfterCaseLabel to .clang-format
This is to fix the behavior that might've changed between older versions of clang-format, I'm not sure.
Version 10 tries to put the bracket on the same line as case without this.
2021-07-04 18:28:21 -08:00
Eric Haszlakiewicz
75bf657cc2 If inttypes.h is present, use it, even on Windows. 2021-06-13 21:12:22 +00:00
Eric Haszlakiewicz
9ca50cf2f8 Issue #709: adjust some include guards to be a bit more json-c specific. 2021-06-02 23:53:23 +00:00
Eric Hawicz
0b7e78c309 Merge pull request #706 from davidjmccann/master
Check __STDC_VERSION__ is defined before checking its value
2021-05-15 08:41:24 -04:00
David McCann
00098efc96 Merge branch 'json-c:master' into master 2021-05-13 06:34:10 +01:00
David McCann
9b53c92ea3 Check __STDC_VERSION__ is defined before checking its value
Prevent an undef warning regarding __STDC_VERSION__ by checking whether it is defined before checking its value.
2021-05-13 06:31:18 +01:00
Eric Hawicz
cd7109f767 Merge pull request #696 from ssrlive/master
To avoid target exe file export JSON functions.
2021-05-01 15:21:31 -04:00
Eric Hawicz
78e390a261 Merge pull request #701 from commodo/configurable-opts
[RFC] json_pointer: allow the feature to be disabled
2021-04-17 17:26:49 -04:00
Alexandru Ardelean
8abeebc9b2 json_pointer: allow the feature to be disabled
Some users may not want to included it in their build/system. So allow a
cmake symbol to disable it.

A user can do 'cmake -DDISABLE_JSON_POINTER=ON <json_c_root_dir>' and
disable the json_pointer functionality. That saves about 17 KB (on an
x86_64) machine. This may be useful on smaller embedded systems; even
though the saving would be fewer kilobytes.

One thing that also needs to change a bit, is that the 'json.h' be
autogenerated via cmake, in order to conditionally include that
"json_pointer.h" file.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2021-04-16 11:49:38 +03:00
Alexandru Ardelean
1f8b64f62c tests: CMakeLists.txt: move test names to variable
The intent is to be able to disable some features that get built into the
library. When we do that, we also need to disable some tests.

It's easier when adjusting a variable that contains the list of test names,
versus modifying the list in the foreach() statement.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
2021-04-16 09:32:17 +03:00
Eric Hawicz
94909840be Merge pull request #700 from Philosoph228/werror-fix
Fix unused variable for Win32 build in random_seed.c
2021-04-14 22:38:36 -04:00
Philosoph228
9c0565100a random_seed: fix unused variable for win32 build 2021-04-13 11:47:01 +05:00
ssrlive
ba181548bc To avoid target exe file export JSON functions. 2021-03-02 14:27:40 +08:00
Eric Haszlakiewicz
041cef434a Add a DISABLE_EXTRA_LIBS option to skip using libbsd, per @neheb's request on issue #692/commit 0f61f692. 2021-02-15 20:19:56 +00:00
Eric Haszlakiewicz
f787810890 If arc4random is used, don't bother compiling in the other fallback methods since they'll never be used. Fixes PR#695 about unreachable code too. 2021-02-13 03:23:58 +00:00
Eric Hawicz
7c859c54e4 Merge pull request #694 from ihsinme/patch-1
fix invalid unsigned arithmetic.
2021-02-06 17:25:24 -05:00
ihsinme
c456963110 Update json_object.c 2021-02-05 18:58:20 +03:00
Eric Haszlakiewicz
0f61f6921b Iesue #692: use arc4random() if it's available (in libc on BSD systems, and libbsd on Linux). 2021-01-13 01:57:25 +00:00
Eric Haszlakiewicz
69d650528d Keep the doc directory in the nodoc release tarball, just exclude its contents. 2021-01-13 01:30:16 +00:00
Eric Hawicz
4754f47434 Merge pull request #674 from ploxiln/random_seed_err_continue
random_seed: on error, continue to next method
2021-01-12 20:22:54 -05:00
Eric Hawicz
1fcb9e476b Merge pull request #689 from neheb/patch-1
fix compilation with clang
2020-12-18 21:30:14 -05:00
Rosen Penev
987d3b2c86 fix compilation with clang
Fixes the following warning:

json_pointer.c:230:7: warning: implicit declaration of function
    'vasprintf' is invalid in C99 [-Wimplicit-function-declaration]
            rc = vasprintf(&path_copy, path_fmt, args);
2020-12-17 19:59:37 -08:00
Pierce Lopez
0fd3b7d316 random_seed: on error, continue to next method
instead of exiting the process
2020-10-07 01:22:30 -04:00
Eric Hawicz
df27756491 Merge pull request #667 from stoeckmann/regression
Fixed test1 regression.
2020-09-11 20:56:51 -04:00
Tobias Stoeckmann
7af593c140 Fixed test1 regression.
SIZEOF_SIZE_T might be only defined in config.h.

Include config.h for these systems to pass tests which are only
supposed to be run on 32 bit systems.

Fixes issue #666.
2020-09-11 21:09:40 +02:00
Eric Hawicz
785a94b7a2 Merge pull request #665 from stoeckmann/tokener
Handle more allocation failures in json_tokener* functions
2020-08-24 09:51:41 -04:00
Eric Hawicz
eb08a92218 Merge pull request #660 from stoeckmann/arraylist
Validate size arguments in arraylist functions.
2020-08-24 09:51:18 -04:00
Tobias Stoeckmann
369e8477d2 Validate size arguments in arraylist functions.
The array_list_new2 function, which is externally reachable through
json_object_new_array_ext, does not check if specified initial size
actually fits into memory on 32 bit architectures.

It also allows negative values, which could lead to an overflow on these
architectures as well. I have added test cases for these situations.

While at it, also protect array_list_shrink against too large
empty_slots argument. No test added because it takes a huge length
value, therefore a lot of items within the array, to overflow the
calculation. In theory this affects 64 bit sytems as well, but since the
arraylist API is not supposed to be used by external applications
according to its header file, the call is protected due to int
limitation of json_object_array_shrink.
2020-08-24 12:13:50 +02:00
Eric Hawicz
b4e72c2655 Merge pull request #664 from stoeckmann/string
Limit strings at INT_MAX length
2020-08-23 16:43:31 -04:00
Eric Hawicz
f941931804 Merge pull request #663 from stoeckmann/strerror
Properly format errnos in _json_c_strerror
2020-08-23 16:38:21 -04:00
Eric Hawicz
1bc7a6b223 Merge pull request #662 from stoeckmann/random
Prevent signed overflow in get_time_seed
2020-08-23 16:34:40 -04:00
Tobias Stoeckmann
df62119b7f Prevent signed overflow in get_time_seed
Casting time(2) return value to int and multiplying the result with
such a constant will definitely lead to a signed overflow by this day.

Since signed overflows are undefined behaviour in C, avoid this.

Casting to unsigned is more than enough since the upper bits of a
64 bit time_t value will be removed with the int conversion anyway.
2020-08-22 13:25:21 +02:00
Tobias Stoeckmann
bcb6d7d347 Handle allocation failure in json_tokener_new_ex
The allocation of printbuf_new might fail. Return NULL to indicate tis
error to the caller. Otherwise later usage of the returned tokener would
lead to null pointer dereference.
2020-08-22 13:18:10 +02:00
Tobias Stoeckmann
e50154f615 Cap string length at INT_MAX.
Several issues occur if a string is longer than INT_MAX:

- The function json_object_get_string_len returns the length of a string
  as int. If the string is longer than INT_MAX, the result would be
  negative.
- That in turn would lead to possible out of boundary access when
  comparing these strings with memcmp and the returned length as done in
  json_object_equal.
- If json_escape_str is called with such strings, out of boundary
  accesses can occur due to internal int handling (also fixed).
- The string cannot be printed out due to printbuffer limits at
  INT_MAX (which is still true after this commit).

Such huge strings can only be inserted through API calls at this point
because input files are capped at INT_MAX anyway.

Due to huge amount of RAM needed to reproduce these issues I have not
added test cases.
2020-08-22 13:16:36 +02:00
Tobias Stoeckmann
583911a66c Aligned comment in _json_object_new_string
The comment only aligns correctly if tab size is 4. Replaced
spaces with tabs to stay in sync with style of other lines.
2020-08-22 13:07:45 +02:00
Tobias Stoeckmann
4298431150 Properly format errnos in _json_c_strerror
The function _json_c_strerror does not properly format unknown errnos.
The int to ascii loop ignores the leading digit if the number can be
divided by 10 and if an errno has been formatted, shorter errnos would
not properly terminate the newly created string, showing the ending
numbers of the previous output.

A test case has been added to show these effects.

Since this function has been introduced for tests, the effect of this on
real life code is basically non-existing. First an environment variable
has to be set to activate this strerror code and second an unknown errno
would have to be encountered.
2020-08-22 11:35:50 +02:00
Eric Haszlakiewicz
2b439ea598 Fix json_object_get_boolean() doc for the object and array cases (always returns 0), and add those cases to the test_cast test.
See also issue #658.
2020-08-17 14:57:21 +00:00
Eric Hawicz
cf30cba4ac Merge pull request #657 from stoeckmann/getrandom
Use GRND_NONBLOCK with getrandom.
2020-08-15 15:01:41 -04:00
Tobias Stoeckmann
f052e42f56 Use GRND_NONBLOCK with getrandom.
The json-c library is used in cryptsetup for LUKS2 header information.
Since cryptsetup can be called very early during boot, the developers
avoid getrandom() calls in their own code base for now. [1]

Introducing a blocking getrandom() call in json-c therefore introduces
this issue for cryptsetup as well. Even though cryptsetup issues do not
have to be json-c issues, here is my proposal:

Let's use a non-blocking call, falling back to other sources if the call
would block. Since getrandom() accesses urandom, it must mean that we
are in an early boot phase -- otherwise the call would not block
according to its manual page.

As stated in manual page of random(4), accessing /dev/urandom won't
block but return weak random numbers, therefore this fallback would work
for json-c.

While at it, fixed the debug message.

[1] https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/47
    which references to https://lwn.net/Articles/800509/
2020-08-15 15:52:17 +02:00
Eric Hawicz
104b408ee8 Merge pull request #656 from pogaram/fix-warnings
Fixed warnings
2020-08-14 16:57:32 -04:00
Aram Poghosyan
0ffb384409 Fixed warnings 2020-08-14 11:45:33 +04:00
Eric Hawicz
98b7ee93fd Merge pull request #655 from MarcT512/issue654
json_parse: Fix read past end of buffer
2020-08-07 10:18:30 -04:00
Marc
4e9e44e525 Fix read past end of buffer
Resolves https://github.com/json-c/json-c/issues/654
2020-08-07 10:49:45 +01:00
Eric Haszlakiewicz
8c7849e6e3 Eliminate use of ctype.h and replace isdigit() and tolower() with non-locale-sensitive approaches. 2020-08-02 04:06:44 +00:00
Eric Haszlakiewicz
f3d8006d34 Neither vertical tab nor formfeed are considered whitespace per the JSON spec, remove them from is_ws_char(). 2020-08-02 03:59:56 +00:00
Eric Haszlakiewicz
8b43ff0c22 Merge the is_ws_char() and is_hex_char() changes to json_tokener from branch 'ramiropolla/for_upstream' (PR #464) 2020-08-02 02:55:45 +00:00
Eric Haszlakiewicz
bfec9c8685 Take a hint from PR #464 and use json_object_new_string_len() to avoid a needless extra strlen() call.
Also, create a _json_object_get_string_len() for internal use when we know
for sure the object we're dealing with is a json_type_string.
2020-08-02 02:23:01 +00:00
Eric Hawicz
56a89f902f Merge pull request #653 from lamby/966657-json-c-please-make-the-build-reproducible
Make the documentation build reproducibly
2020-08-01 22:06:38 -04:00
Chris Lamb
46eea84554 Make the documentation build reproducibly
Whilst working on the Reproducible Builds effort [0] I noticed that
json-c could not be built reproducibly.

This is because it used the full, absolute path name as an (sanitised)
input to a filename, resulting in some binary package containing, for
example:

  /usr/share/doc/libjson-c-dev/html/md__build_1st_json-c-0_815_issues_closed_for_0_813.html
                                        ^^^^^^^^^^^^^^^^^^^^^^
or

  /usr/share/doc/libjson-c-dev/html/md__build_2_json-c-0_815_2nd_issues_closed_for_0_813.html
                                        ^^^^^^^^^^^^^^^^^^^^^^^^

These differing values are based on the path in which json-c is built. This was
originally filed in Debian as #966657 [1].

 [0] https://reproducible-builds.org/
 [1] https://bugs.debian.org/966657
2020-08-01 11:26:55 +01:00
Eric Hawicz
88cce7b9c5 Merge pull request #651 from alanc/getrandom
Getrandom
2020-07-31 20:30:11 -04:00
Alan Coopersmith
6cf4847796 Use getrandom() if available in json_c_get_random_seed
Lower overhead than opening & reading from /dev/urandom, and works
in chroots and other situtations where /dev/urandom is not available.
Falls back to existing methods when kernel doesn't support the syscall.
2020-07-31 08:28:07 -07:00
Eric Haszlakiewicz
002411293d Issue #649: Drop the generated doc/Doxyfile when creating a release. 2020-07-28 03:52:22 +00:00
Eric Hawicz
66f8ca3c03 Merge pull request #650 from sartura/readme-update
README: fix spelling errors
2020-07-27 10:31:05 -04:00
Jakov Smolic
55bf2d365d README: fix spelling errors
Signed-off-by: Jakov Smolic <jakov.smolic@sartura.hr>
2020-07-27 15:05:55 +02:00
Eric Haszlakiewicz
47189b5ff1 Include updating the json-c-current-releaes gh-pages symlink as part of the release process. 2020-07-26 15:51:07 +00:00
Eric Haszlakiewicz
de02d09c32 Update the master branch to version 0.15.99 2020-07-26 15:26:53 +00:00
Eric Haszlakiewicz
870965e1ea Update AUTHORS, add issues_closed_for_0.15.md, tweak the release checklist slightly. 2020-07-24 03:17:13 +00:00
Ramiro Polla
38a112380b json_object: cleanup of *set_string* functions
This commit also has the side-effect that errno is set on failed calls
to json_object_set_string(_len).
2018-12-21 00:30:26 +01:00
Ramiro Polla
906188e1cf json_object: speed up creation of objects
Instead of using calloc(), call malloc() and initialize the relevant
fields individually.

speedup for 32-bit: ~15%
speedup for 64-bit: ~ 5%
2018-12-21 00:30:26 +01:00
Ramiro Polla
c9a0ac5886 json_tokener: optimize parsing of integer values
speedup for 32-bit: ~8%
speedup for 64-bit: ~9%
2018-12-21 00:30:21 +01:00
Ramiro Polla
d98fc501fb json_tokener: optimize check for number characters
speedup for 32-bit: ~5%
speedup for 64-bit: ~3%
2018-12-21 00:24:17 +01:00
Ramiro Polla
45c601bfa4 json_tokener: optimize check for hex characters
speedup for 32-bit: ~1%
speedup for 64-bit: ~1%
2018-12-21 00:24:17 +01:00
Ramiro Polla
158c248d5c json_tokener: optimize check for whitespace characters
speedup for 32-bit: ~15%
speedup for 64-bit: ~ 2%
2018-12-21 00:24:17 +01:00
Ramiro Polla
ab3e40b37c json_object_deep_copy: fix deep copy of strings containing '\0' 2018-12-20 22:26:06 +01:00
Ramiro Polla
1f46d2f40f json_object_private: remove _delete field
This field is set based on o_type when the object is created and it is
not changed during the lifetime of the object. Therefore we can check
o_type to choose the proper delete function in json_object_put(), and
save sizeof(void *) bytes in struct json_object_private.
2018-12-20 22:26:06 +01:00
119 changed files with 6068 additions and 916 deletions

View File

@@ -23,6 +23,8 @@ AllowShortFunctionsOnASingleLine: Empty
BreakBeforeBraces: Custom BreakBeforeBraces: Custom
# Control of individual brace wrapping cases. # Control of individual brace wrapping cases.
BraceWrapping: BraceWrapping:
# Wrap brackets inside of a case
AfterCaseLabel: true
# Wrap class definition. # Wrap class definition.
AfterClass: true AfterClass: true
# Wrap control statements # Wrap control statements

View File

@@ -1,5 +1,5 @@
# EditorConfig # EditorConfig
# http://EditorConfig.org # https://EditorConfig.org
# top-most EditorConfig file # top-most EditorConfig file
root = true root = true

View File

@@ -8,7 +8,7 @@ assignees: ''
--- ---
Note: for general questions and comments, please use the forums at: Note: for general questions and comments, please use the forums at:
https://groups.google.com/forum/#!forum/json-c https://groups.google.com/g/json-c
**Describe the bug** **Describe the bug**
A clear and concise description of what the bug is, and any information about where you're running into the bug that you feel might be relevant. A clear and concise description of what the bug is, and any information about where you're running into the bug that you feel might be relevant.

228
.github/workflows/meson.yml vendored Normal file
View File

@@ -0,0 +1,228 @@
name: meson
on: [push, pull_request]
concurrency:
group: ${{github.workflow}}-${{github.head_ref}}
cancel-in-progress: true
jobs:
Linux-GCC:
runs-on: ubuntu-22.04
strategy:
matrix:
cc: ['9', '13']
steps:
- uses: actions/checkout@v4
- uses: egor-tensin/setup-gcc@v1
with:
version: ${{matrix.cc}}
- name: Install packages
run: |
python3 -m pip install meson ninja
- name: Compile and Test
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
Linux-Clang:
runs-on: ubuntu-22.04
strategy:
matrix:
cc: ['9', '22']
steps:
- uses: actions/checkout@v4
- uses: egor-tensin/setup-clang@v1
- name: Install packages
run: |
python3 -m pip install meson ninja
- name: Compile and Test
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
VisualStudio:
runs-on: windows-latest
strategy:
matrix:
platform: ['x64', 'x86']
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
python -m pip install meson ninja
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{matrix.platform}}
- name: Compile and Test
env:
CFLAGS: '/analyze'
run: |
meson setup "${{github.workspace}}/build" --wrap-mode=forcefallback -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
VisualStudio-clang-cl:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
python -m pip install meson ninja
- uses: ilammy/msvc-dev-cmd@v1
- name: Compile and Test
env:
CC: clang-cl
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
MSYS2:
runs-on: windows-latest
strategy:
matrix:
platform: ['UCRT64', 'CLANG64']
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.platform}}
pacboy: >-
cc:p
cmake:p
meson:p
ninja:p
pkgconf:p
- name: Compile and Test
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
Cygwin:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: 'MSYS'
install: >-
gcc
meson
ninja
- name: Compile and Test
run: |
meson setup build -Dwarning_level=3 -Dcpp_std=gnu++20 -Dbuildtype=debug
meson compile -C build --verbose
meson test -C build --verbose
MacOS:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
brew install meson
- name: Compile and Test
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
DragonflyBSD:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: vmactions/dragonflybsd-vm@v1
with:
prepare: |
pkg install -y ninja meson pkgconf
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
FreeBSD:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: vmactions/freebsd-vm@v1
with:
prepare: |
pkg install -y ninja meson pkgconf
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
NetBSD:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: vmactions/netbsd-vm@v1
with:
prepare: |
/usr/sbin/pkg_add meson pkgconf
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
OmniOS:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: vmactions/omnios-vm@v1
with:
prepare: |
pkg install gcc14 meson-313 ninja pkg-config python-313
run: |
/usr/lib/python3.13/bin/meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
/usr/lib/python3.13/bin/meson compile -C "${{github.workspace}}/build" --verbose
/usr/lib/python3.13/bin/meson test -C "${{github.workspace}}/build" --verbose
OpenBSD:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: vmactions/openbsd-vm@v1
with:
prepare: |
pkg_add ninja meson pkgconf
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose
Alpine:
runs-on: ubuntu-latest
strategy:
matrix:
platform: ['x86_64', 'x86', 'armhf', 'armv7', 'aarch64', 'ppc64le', 'riscv64', 's390x']
defaults:
run:
shell: alpine.sh {0}
steps:
- uses: actions/checkout@v4
- uses: jirutka/setup-alpine@v1
with:
branch: edge
arch: ${{matrix.platform}}
packages: >
build-base pkgconf meson samurai
- name: Compile and Test
run: |
meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dbuildtype=debug
meson compile -C "${{github.workspace}}/build" --verbose
meson test -C "${{github.workspace}}/build" --verbose

3
.gitignore vendored
View File

@@ -10,6 +10,7 @@
*.save *.save
*.autosav *.autosav
*.autosave *.autosave
.DS_Store
# Tests # Tests
/tests/Makefile /tests/Makefile
@@ -27,6 +28,7 @@
/tests/test_double_serializer /tests/test_double_serializer
/tests/test_float /tests/test_float
/tests/test_int_add /tests/test_int_add
/tests/test_int_get
/tests/test_json_pointer /tests/test_json_pointer
/tests/test_locale /tests/test_locale
/tests/test_null /tests/test_null
@@ -70,6 +72,7 @@
# It's not good practice to build directly in the source tree # It's not good practice to build directly in the source tree
# but ignore cmake auto-generated files anyway: # but ignore cmake auto-generated files anyway:
/json_config.h /json_config.h
/json.h
/config.h /config.h
/json-c.pc /json-c.pc
/Makefile /Makefile

View File

@@ -1,8 +1,8 @@
language: cpp language: cpp
matrix: matrix:
include: include:
# gcc
# xenial # ubuntu xenial 16.04
# gcc 5 is the default on xenial # gcc 5 is the default on xenial
- os: linux - os: linux
dist: xenial dist: xenial
@@ -15,35 +15,37 @@ matrix:
- doxygen - doxygen
- cmake - cmake
env: CHECK="true" env: CHECK="true"
# bionic
- os: linux
dist: bionic
compiler: gcc
env: MATRIX_EVAL="CC=gcc-6 && CXX=g++-6"
# gcc 7 is the default on bionic # ubuntu bionic 18.04
# gcc 7 is the default on bionic
- os: linux - os: linux
dist: bionic dist: bionic
compiler: gcc compiler: gcc
addons:
apt:
packages:
- valgrind
- cppcheck
- doxygen
- cmake
env: CHECK="true" env: CHECK="true"
# ubuntu focal fossa 20.04
# gcc 9 is the default on bionic
- os: linux - os: linux
dist: bionic dist: focal
compiler: gcc compiler: gcc
env: MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" addons:
apt:
packages:
- valgrind
- cppcheck
- doxygen
- cmake
env: CHECK="true"
# clang # clang
# xenial # xenial
- os: linux
dist: xenial
compiler: clang
addons:
apt:
sources:
- llvm-toolchain-xenial-5.0
packages:
- clang-5.0
- cmake
env: MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0"
- os: linux - os: linux
dist: xenial dist: xenial
compiler: clang compiler: clang
@@ -56,9 +58,9 @@ matrix:
- cmake - cmake
env: MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" env: MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0"
# clang-7 is the default on xenial and bionic # clang-7 is the default on focal, xenial and bionic
- os: linux - os: linux
dist: xenial dist: focal
compiler: clang compiler: clang
addons: addons:
apt: apt:
@@ -69,17 +71,9 @@ matrix:
- cmake - cmake
env: CHECK="true" env: CHECK="true"
# bionic
- os: linux
dist: bionic
compiler: clang
env: CHECK="true"
# osx # osx
- os: osx - os: osx
osx_image: xcode9.4 osx_image: xcode13.4
env: XCODE="true"
- os: osx
osx_image: xcode10.1
env: XCODE="true" CHECK="true" env: XCODE="true" CHECK="true"
# run coveralls # run coveralls

64
AUTHORS
View File

@@ -1,30 +1,94 @@
Alan Coopersmith <alan.coopersmith@oracle.com>
Alan Urmancheev <108410815+alurm@users.noreply.github.com>
Alexander Dahl <post@lespocky.de> Alexander Dahl <post@lespocky.de>
Alexandru Ardelean <ardeleanalex@gmail.com>
An7ar35 <eadavison@protonmail.com>
andy5995 <andy400-dev@yahoo.com> andy5995 <andy400-dev@yahoo.com>
Anton Lundin <glance@ac2.se>
Aram Poghosyan <Aram.Poghosyan@teamviewer.com>
Awildidiot <69577313+0xfadead@users.noreply.github.com>
Björn Esser <besser82@fedoraproject.org> Björn Esser <besser82@fedoraproject.org>
BonsaY <bonsay@posteo.de>
Bruno Haible <bruno@clisp.org>
Cameron Armstrong <cameronscottarmstrong@gmail.com>
Careles <zhancheng1996@gmail.com>
changyong guo <guo1487@163.com> changyong guo <guo1487@163.com>
chenguoping <chenguopingdota@163.com> chenguoping <chenguopingdota@163.com>
Chris Lamb <lamby@debian.org>
Christopher Head <chead@chead.ca> Christopher Head <chead@chead.ca>
Chris Wolfe <chriswwolfe@gmail.com> Chris Wolfe <chriswwolfe@gmail.com>
C. Watford (christopher.watford@gmail.com) C. Watford (christopher.watford@gmail.com)
Daniel Danzberger <daniel@dd-wrt.com>
Darjan Krijan <darjan_krijan@gmx.de> Darjan Krijan <darjan_krijan@gmx.de>
David McCann <mccannd@uk.ibm.com>
DeX77 <dex@dragonslave.de>
Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com>
dota17 <chenguopingdota@163.com>
Eric Haszlakiewicz <erh+git@nimenees.com>
Eric Hawicz <erh+git@nimenees.com> Eric Hawicz <erh+git@nimenees.com>
Evan Si <evsi@amazon.com>
Even Rouault <even.rouault@spatialys.com>
evo <149657734+evo-i@users.noreply.github.com>
Fabrice Fontaine <fontaine.fabrice@gmail.com>
Federico Francescon <federico.francescon@higeco.com>
Gianluigi Tiesi <sherpya@netfarm.it>
grdowns <grdowns@microsoft.com> grdowns <grdowns@microsoft.com>
Guilhem Saurel <guilhem.saurel@laas.fr>
HansolChoe <hansol614@gmail.com>
Hex052 <elijahiff@gmail.com>
hofnarr <hofnarr@hofnarr.fi>
ihsinme <61293369+ihsinme@users.noreply.github.com>
Ivan Romanov <drizt@land.ru> Ivan Romanov <drizt@land.ru>
Jaap Keuter <jaap.keuter@xs4all.nl> Jaap Keuter <jaap.keuter@xs4all.nl>
Jakov Smolic <jakov.smolic@sartura.hr>
janczer <menshikov.ivn@gmail.com> janczer <menshikov.ivn@gmail.com>
Ján Tomko <jtomko@redhat.com>
JC (Jonathan Chen) <jc@dijonkitchen.org>
Jehan <jehan@girinstud.io> Jehan <jehan@girinstud.io>
Jehiah Czebotar <jehiah@gmail.com> Jehiah Czebotar <jehiah@gmail.com>
Jonathan Wiens <j.wiens@teles.com> Jonathan Wiens <j.wiens@teles.com>
Jose Bollo <jose.bollo@iot.bzh> Jose Bollo <jose.bollo@iot.bzh>
José Bollo <jose.bollo@iot.bzh>
Juuso Alasuutari <juuso.alasuutari@gmail.com>
Keith Holman <keith.holman@windriver.com> Keith Holman <keith.holman@windriver.com>
Khem Raj <raj.khem@gmail.com>
Kizuna-Meraki <z9@kizunameraki.de>
Lakshmi-Surekha <Lakshmi.Kovvuri@ibm.com>
Leon Gross <leon.gross@rub.de>
Liang, Gao <liang.gao@intel.com> Liang, Gao <liang.gao@intel.com>
Luca Mannella <luca.mannella@studenti.polito.it>
Marc <34656315+MarcT512@users.noreply.github.com>
Matthias Gatto <matthias.gatto@protonmail.com>
max <mpano91@gmail.com> max <mpano91@gmail.com>
Micah Snyder <micasnyd@cisco.com>
Michael Clark <michael@metaparadigm.com> Michael Clark <michael@metaparadigm.com>
Michele Locati <michele@locati.it>
myd7349 <myd7349@gmail.com> myd7349 <myd7349@gmail.com>
Pascal Cuoq <cuoq@trust-in-soft.com>
Pavel Zwerschke <pavelzw@gmail.com>
Pawday <pawday@mail.ru>
Philosoph228 <philosoph228@gmail.com>
Pierce Lopez <pierce.lopez@gmail.com> Pierce Lopez <pierce.lopez@gmail.com>
Po-Chuan Hsieh <sunpoet@sunpoet.net> Po-Chuan Hsieh <sunpoet@sunpoet.net>
Ramiro Polla <ramiro.polla@gmail.com> Ramiro Polla <ramiro.polla@gmail.com>
Rikard Falkeborn <rikard.falkeborn@gmail.com> Rikard Falkeborn <rikard.falkeborn@gmail.com>
Robert Bielik <robert.bielik@dirac.com>
Robert Pang <robertpang@google.com>
Robert <roby_p97@yahoo.com> Robert <roby_p97@yahoo.com>
Rolf Eike Beer <eb@emlix.com>
Rosen Penev <rosenp@gmail.com>
Rubasri Kalidas <rubasri.kalidas@intel.com> Rubasri Kalidas <rubasri.kalidas@intel.com>
Sergey Sharshunov <s.sharshunov@gmail.com>
Shane F. Carr <shane@unicode.org>
Simon McVittie <smcv@collabora.com>
Simon Resch <simon.resch@code-intelligence.de>
Simon Sobisch <simonsobisch@web.de>
ssrlive <30760636+ssrlive@users.noreply.github.com>
Timothée Ravier <tim@siosm.fr>
Tobias Nießen <tniessen@users.noreply.github.com>
Tobias Stoeckmann <tobias@stoeckmann.org>
Tudor Brindus <me@tbrindus.ca>
Tyler Erickson <tyler.erickson@seagate.com>
Tyler Erickson <vonericsen@gmail.com>
Unmanned Player <36690541+unmanned-player@users.noreply.github.com> Unmanned Player <36690541+unmanned-player@users.noreply.github.com>
Yurii Rashkovskii <yrashk@gmail.com>

View File

@@ -1,31 +1,13 @@
# Many projects still are stuck using CMake 2.8 is several places so it's good to provide backward support too. This is # CMake 3.9 was released in 2017/07
# specially true in old embedded systems (OpenWRT and friends) where CMake isn't necessarily upgraded. # As of 2023, many versions of Linux, NetBSD and FreeBSD provide,
cmake_minimum_required(VERSION 2.8) # and many OpenWRT packages require, much newer CMake packages.
# We're stopping before 3.10 because that version starts requiring
if(POLICY CMP0048) # c++11, which isn't available on e.g HPUX.
cmake_policy(SET CMP0048 NEW) cmake_minimum_required(VERSION 3.9...3.12)
endif()
# JSON-C library is C only project. # JSON-C library is C only project.
if (CMAKE_VERSION VERSION_LESS 3.0) # PROJECT_VERSION{,_MAJOR,_MINOR,_PATCH} set by project():
project(json-c) project(json-c LANGUAGES C VERSION 0.18.99)
set(PROJECT_VERSION_MAJOR "0")
set(PROJECT_VERSION_MINOR "14")
set(PROJECT_VERSION_PATCH "99")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
else()
project(json-c LANGUAGES C VERSION 0.14.99)
endif()
# If we've got 3.0 then it's good, let's provide support. Otherwise, leave it be.
if(POLICY CMP0038)
# Policy CMP0038 introduced was in CMake 3.0
cmake_policy(SET CMP0038 NEW)
endif()
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
# set default build type if not specified by user # set default build type if not specified by user
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
@@ -34,24 +16,8 @@ endif()
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2")
# Include file check macros honor CMAKE_REQUIRED_LIBRARIES
# i.e. the check_include_file() calls will include -lm when checking.
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
include(CTest) include(CTest)
if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING AND
(NOT MSVC OR NOT (MSVC_VERSION LESS 1800)) # Tests need at least VS2013
)
add_subdirectory(tests)
endif()
if (NOT MSVC) # cmd line apps don't built on Windows currently.
add_subdirectory(apps)
endif()
# Set some packaging variables. # Set some packaging variables.
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
@@ -97,13 +63,71 @@ option(DISABLE_WERROR "Avoid treating compiler warnings as fatal
option(ENABLE_RDRAND "Enable RDRAND Hardware RNG Hash Seed." OFF) option(ENABLE_RDRAND "Enable RDRAND Hardware RNG Hash Seed." OFF)
option(ENABLE_THREADING "Enable partial threading support." OFF) option(ENABLE_THREADING "Enable partial threading support." OFF)
option(OVERRIDE_GET_RANDOM_SEED "Override json_c_get_random_seed() with custom code." OFF) option(OVERRIDE_GET_RANDOM_SEED "Override json_c_get_random_seed() with custom code." OFF)
option(DISABLE_EXTRA_LIBS "Avoid linking against extra libraries, such as libbsd." OFF)
option(DISABLE_JSON_POINTER "Disable JSON pointer (RFC6901) and JSON patch support." OFF)
option(DISABLE_JSON_PATCH "Disable JSON patch (RFC6902) support." OFF)
option(NEWLOCALE_NEEDS_FREELOCALE "Work around newlocale bugs in old FreeBSD and macOS by calling freelocale" OFF)
option(BUILD_APPS "Default to building apps" ON)
# macOS (Darwin) has the same newlocale() bug as older FreeBSD versions:
# newlocale() does not free the locale object passed to it, causing memory leaks.
# See: https://github.com/json-c/json-c/issues/668
if(APPLE AND NOT NEWLOCALE_NEEDS_FREELOCALE)
message(STATUS "macOS detected: enabling NEWLOCALE_NEEDS_FREELOCALE to prevent memory leaks")
set(NEWLOCALE_NEEDS_FREELOCALE ON CACHE BOOL "macOS needs freelocale workaround" FORCE)
endif()
if (UNIX OR MINGW OR CYGWIN) if (AMIGA)
set(DISABLE_THREAD_LOCAL_STORAGE ON)
set(ENABLE_THREADING OFF)
set(BUILD_SHARED_LIBS OFF)
set(BUILD_APPS OFF)
set(DISABLE_STATIC_FPIC ON)
if ($ENV{CROSS_PFX} STREQUAL "m68k-amigaos")
set(AMIGA_M68K ON)
set(BUILD_TESTING OFF)
if (${M68K_CRT} STREQUAL "newlib")
set(NEWLIB ON)
elseif(${M68K_CRT} STREQUAL "clib2")
set(CLIB2 ON)
elseif(${M68K_CRT} STREQUAL "ixemul")
set(IXEMUL ON)
elseif(${M68K_CRT} STREQUAL "nix20")
set(NIX20 ON)
elseif(${M68K_CRT} STREQUAL "nix13")
set(NIX13 ON)
else()
set(NEWLIB ON)
endif()
message(STATUS "Building for Motorola 68k AmigaOS using CRT: ${M68K_CRT}")
elseif ($ENV{CROSS_PFX} STREQUAL "ppc-amigaos")
set(AMIGA_PPC ON)
message(STATUS "Building for PowerPC AmigaOS")
elseif($ENV{CROSS_PFX} STREQUAL "ppc-morphos")
set(MORPHOS ON)
if (NOIXEMUL)
message(STATUS "Building for PowerPC MorphOS without ixemul")
set(DISABLE_WERROR ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -noixemul")
else()
message(STATUS "Building for PowerPC MorphOS with ixemul")
endif()
else()
message(FATAL_ERROR "Unsupported AmigaOS target")
endif()
else()
message(STATUS "Building for ${CMAKE_SYSTEM_NAME}")
endif()
if (UNIX OR MINGW OR CYGWIN OR AMIGA)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
endif() endif()
if (UNIX) if (BUILD_SHARED_LIBS)
add_definitions(-D JSON_C_DLL)
endif()
if (UNIX OR AMIGA)
list(APPEND CMAKE_REQUIRED_LIBRARIES m) list(APPEND CMAKE_REQUIRED_LIBRARIES m)
endif() endif()
@@ -144,17 +168,34 @@ check_include_file(stdint.h HAVE_STDINT_H)
check_include_file(stdlib.h HAVE_STDLIB_H) check_include_file(stdlib.h HAVE_STDLIB_H)
check_include_file(sys/cdefs.h HAVE_SYS_CDEFS_H) check_include_file(sys/cdefs.h HAVE_SYS_CDEFS_H)
check_include_file(sys/param.h HAVE_SYS_PARAM_H) check_include_file(sys/param.h HAVE_SYS_PARAM_H)
check_include_file(sys/random.h HAVE_SYS_RANDOM_H)
check_include_file(sys/stat.h HAVE_SYS_STAT_H) check_include_file(sys/stat.h HAVE_SYS_STAT_H)
check_include_file(xlocale.h HAVE_XLOCALE_H) check_include_file(xlocale.h HAVE_XLOCALE_H)
if (HAVE_INTTYPES_H AND NOT MSVC) # Set json-c specific vars to stamp into json_config.h
set(JSON_C_HAVE_INTTYPES_H 1) # in a way that hopefully won't conflict with other
# projects that use json-c.
if (HAVE_INTTYPES_H)
set(JSON_C_HAVE_INTTYPES_H 1)
endif()
if (HAVE_STDINT_H)
set(JSON_C_HAVE_STDINT_H 1)
endif() endif()
check_symbol_exists(_isnan "float.h" HAVE_DECL__ISNAN) check_symbol_exists(_isnan "float.h" HAVE_DECL__ISNAN)
check_symbol_exists(_finite "float.h" HAVE_DECL__FINITE) check_symbol_exists(_finite "float.h" HAVE_DECL__FINITE)
if ((MSVC AND NOT (MSVC_VERSION LESS 1800)) OR MINGW OR CYGWIN OR UNIX) if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
# Detect clang-cl.exe, it is Clang but with MSVC compatible command line arguments
execute_process (COMMAND ${CMAKE_C_COMPILER} -? ERROR_QUIET OUTPUT_QUIET RESULT_VARIABLE _clang_result)
if (_clang_result EQUAL 0)
set(CLANG_CL TRUE)
else()
set(CLANG_CL FALSE)
endif()
endif()
if ((MSVC AND NOT (MSVC_VERSION LESS 1800)) OR MINGW OR CYGWIN OR UNIX OR AMIGA OR CLANG_CL)
check_symbol_exists(INFINITY "math.h" HAVE_DECL_INFINITY) check_symbol_exists(INFINITY "math.h" HAVE_DECL_INFINITY)
check_symbol_exists(isinf "math.h" HAVE_DECL_ISINF) check_symbol_exists(isinf "math.h" HAVE_DECL_ISINF)
check_symbol_exists(isnan "math.h" HAVE_DECL_ISNAN) check_symbol_exists(isnan "math.h" HAVE_DECL_ISNAN)
@@ -162,12 +203,26 @@ if ((MSVC AND NOT (MSVC_VERSION LESS 1800)) OR MINGW OR CYGWIN OR UNIX)
endif() endif()
check_symbol_exists(_doprnt "stdio.h" HAVE_DOPRNT) check_symbol_exists(_doprnt "stdio.h" HAVE_DOPRNT)
if (UNIX OR MINGW OR CYGWIN) if (UNIX OR MINGW OR CYGWIN OR AMIGA)
check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF) check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF)
endif() endif()
check_symbol_exists(vasprintf "stdio.h" HAVE_VASPRINTF) check_symbol_exists(vasprintf "stdio.h" HAVE_VASPRINTF)
check_symbol_exists(vsnprintf "stdio.h" HAVE_VSNPRINTF) check_symbol_exists(vsnprintf "stdio.h" HAVE_VSNPRINTF)
check_symbol_exists(vprintf "stdio.h" HAVE_VPRINTF) check_symbol_exists(vprintf "stdio.h" HAVE_VPRINTF)
if (NOT AMIGA_M68K)
check_symbol_exists(arc4random "stdlib.h" HAVE_ARC4RANDOM)
endif()
if (NOT HAVE_ARC4RANDOM AND DISABLE_EXTRA_LIBS STREQUAL "OFF")
check_include_file(bsd/stdlib.h HAVE_BSD_STDLIB_H)
if (HAVE_BSD_STDLIB_H)
list(APPEND CMAKE_REQUIRED_LIBRARIES "bsd")
unset(HAVE_ARC4RANDOM CACHE)
check_symbol_exists(arc4random "bsd/stdlib.h" HAVE_ARC4RANDOM)
if (NOT HAVE_ARC4RANDOM)
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "bsd")
endif()
endif()
endif()
if (HAVE_FCNTL_H) if (HAVE_FCNTL_H)
check_symbol_exists(open "fcntl.h" HAVE_OPEN) check_symbol_exists(open "fcntl.h" HAVE_OPEN)
@@ -178,7 +233,24 @@ endif()
if (HAVE_LOCALE_H) if (HAVE_LOCALE_H)
check_symbol_exists(setlocale "locale.h" HAVE_SETLOCALE) check_symbol_exists(setlocale "locale.h" HAVE_SETLOCALE)
check_symbol_exists(uselocale "locale.h" HAVE_USELOCALE) check_symbol_exists(uselocale "locale.h" HAVE_USELOCALE)
if (NOT NIX20 AND NOT NIX13)
# libnix does not fully support this yet
check_symbol_exists(duplocale "locale.h" HAVE_DUPLOCALE)
endif()
endif() endif()
# uClibc *intentionally* crashes in duplocale(), at least as of:
# https://github.com/ffainelli/uClibc/blob/266bdc1/libc/misc/locale/locale.c#L1322
# So, if it looks like we're compiling for a system like that just disable
# locale handling entirely.
execute_process (COMMAND ${CMAKE_C_COMPILER} -dumpmachine ERROR_QUIET OUTPUT_VARIABLE CMAKE_GNU_C_MACHINE)
if (CMAKE_GNU_C_MACHINE MATCHES "uclibc")
message(STATUS "Detected uClibc compiler, disabling locale handling")
set(HAVE_SETLOCALE 0)
set(HAVE_USELOCALE 0)
endif()
if (HAVE_STRINGS_H) if (HAVE_STRINGS_H)
check_symbol_exists(strcasecmp "strings.h" HAVE_STRCASECMP) check_symbol_exists(strcasecmp "strings.h" HAVE_STRCASECMP)
check_symbol_exists(strncasecmp "strings.h" HAVE_STRNCASECMP) check_symbol_exists(strncasecmp "strings.h" HAVE_STRNCASECMP)
@@ -190,6 +262,9 @@ endif()
if (HAVE_SYSLOG_H) if (HAVE_SYSLOG_H)
check_symbol_exists(vsyslog "syslog.h" HAVE_VSYSLOG) check_symbol_exists(vsyslog "syslog.h" HAVE_VSYSLOG)
endif() endif()
if (HAVE_SYS_RANDOM_H)
check_symbol_exists(getrandom "sys/random.h" HAVE_GETRANDOM)
endif()
if (HAVE_SYS_RESOURCE_H) if (HAVE_SYS_RESOURCE_H)
check_symbol_exists(getrusage "sys/resource.h" HAVE_GETRUSAGE) check_symbol_exists(getrusage "sys/resource.h" HAVE_GETRUSAGE)
endif() endif()
@@ -265,11 +340,29 @@ message(STATUS "Wrote ${PROJECT_BINARY_DIR}/config.h")
configure_file(${PROJECT_SOURCE_DIR}/cmake/json_config.h.in ${PROJECT_BINARY_DIR}/json_config.h) configure_file(${PROJECT_SOURCE_DIR}/cmake/json_config.h.in ${PROJECT_BINARY_DIR}/json_config.h)
message(STATUS "Wrote ${PROJECT_BINARY_DIR}/json_config.h") message(STATUS "Wrote ${PROJECT_BINARY_DIR}/json_config.h")
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") if (NOT DEFINED CMAKE_C_COMPILER_FRONTEND_VARIANT OR "${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "") # only available in cmake 3.14+
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "MSVC")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "GNU")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
if (CLANG_CL)
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "MSVC")
else()
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "GNU")
message(STATUS, "clang result is NOT 0")
endif()
endif()
endif()
if ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
if ("${DISABLE_WERROR}" STREQUAL "OFF") if ("${DISABLE_WERROR}" STREQUAL "OFF")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
endif() endif()
if (AMIGA_M68K)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fbaserel")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=deprecated-declarations") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=deprecated-declarations")
@@ -281,7 +374,12 @@ if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
endif() endif()
add_definitions(-D_GNU_SOURCE) add_definitions(-D_GNU_SOURCE)
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
if ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "Clang")
# Remove this for 1.0 when we can bump the ABI and actually fix these warnings.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shorten-64-to-32")
endif()
elseif ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /DEBUG") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /DEBUG")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4100") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4100")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996")
@@ -292,7 +390,7 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4701") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4701")
endif() endif()
if (NOT ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")) if (NOT ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC"))
check_c_source_compiles( check_c_source_compiles(
" "
/* uClibc toolchains without threading barf when _REENTRANT is defined */ /* uClibc toolchains without threading barf when _REENTRANT is defined */
@@ -354,14 +452,13 @@ set(JSON_C_PUBLIC_HEADERS
# Note: config.h is _not_ included here # Note: config.h is _not_ included here
${PROJECT_BINARY_DIR}/json_config.h ${PROJECT_BINARY_DIR}/json_config.h
${PROJECT_SOURCE_DIR}/json.h ${PROJECT_BINARY_DIR}/json.h
${PROJECT_SOURCE_DIR}/arraylist.h ${PROJECT_SOURCE_DIR}/arraylist.h
${PROJECT_SOURCE_DIR}/debug.h ${PROJECT_SOURCE_DIR}/debug.h
${PROJECT_SOURCE_DIR}/json_c_version.h ${PROJECT_SOURCE_DIR}/json_c_version.h
${PROJECT_SOURCE_DIR}/json_inttypes.h ${PROJECT_SOURCE_DIR}/json_inttypes.h
${PROJECT_SOURCE_DIR}/json_object.h ${PROJECT_SOURCE_DIR}/json_object.h
${PROJECT_SOURCE_DIR}/json_object_iterator.h ${PROJECT_SOURCE_DIR}/json_object_iterator.h
${PROJECT_SOURCE_DIR}/json_pointer.h
${PROJECT_SOURCE_DIR}/json_tokener.h ${PROJECT_SOURCE_DIR}/json_tokener.h
${PROJECT_SOURCE_DIR}/json_types.h ${PROJECT_SOURCE_DIR}/json_types.h
${PROJECT_SOURCE_DIR}/json_util.h ${PROJECT_SOURCE_DIR}/json_util.h
@@ -373,9 +470,9 @@ set(JSON_C_PUBLIC_HEADERS
set(JSON_C_HEADERS set(JSON_C_HEADERS
${JSON_C_PUBLIC_HEADERS} ${JSON_C_PUBLIC_HEADERS}
${PROJECT_SOURCE_DIR}/json_object_private.h ${PROJECT_SOURCE_DIR}/json_object_private.h
${PROJECT_SOURCE_DIR}/json_pointer_private.h
${PROJECT_SOURCE_DIR}/random_seed.h ${PROJECT_SOURCE_DIR}/random_seed.h
${PROJECT_SOURCE_DIR}/strerror_override.h ${PROJECT_SOURCE_DIR}/strerror_override.h
${PROJECT_SOURCE_DIR}/strerror_override_private.h
${PROJECT_SOURCE_DIR}/math_compat.h ${PROJECT_SOURCE_DIR}/math_compat.h
${PROJECT_SOURCE_DIR}/snprintf_compat.h ${PROJECT_SOURCE_DIR}/snprintf_compat.h
${PROJECT_SOURCE_DIR}/strdup_compat.h ${PROJECT_SOURCE_DIR}/strdup_compat.h
@@ -388,7 +485,6 @@ set(JSON_C_SOURCES
${PROJECT_SOURCE_DIR}/json_c_version.c ${PROJECT_SOURCE_DIR}/json_c_version.c
${PROJECT_SOURCE_DIR}/json_object.c ${PROJECT_SOURCE_DIR}/json_object.c
${PROJECT_SOURCE_DIR}/json_object_iterator.c ${PROJECT_SOURCE_DIR}/json_object_iterator.c
${PROJECT_SOURCE_DIR}/json_pointer.c
${PROJECT_SOURCE_DIR}/json_tokener.c ${PROJECT_SOURCE_DIR}/json_tokener.c
${PROJECT_SOURCE_DIR}/json_util.c ${PROJECT_SOURCE_DIR}/json_util.c
${PROJECT_SOURCE_DIR}/json_visit.c ${PROJECT_SOURCE_DIR}/json_visit.c
@@ -398,16 +494,45 @@ set(JSON_C_SOURCES
${PROJECT_SOURCE_DIR}/strerror_override.c ${PROJECT_SOURCE_DIR}/strerror_override.c
) )
set(JSON_C_RESOURCES)
if (BUILD_SHARED_LIBS AND WIN32)
enable_language(RC)
set(JSON_C_RESOURCES ${JSON_C_RESOURCES} ${PROJECT_SOURCE_DIR}/libjson.rc)
endif()
if (NOT DISABLE_JSON_POINTER)
set(JSON_C_PUBLIC_HEADERS ${JSON_C_PUBLIC_HEADERS} ${PROJECT_SOURCE_DIR}/json_pointer.h)
set(JSON_C_SOURCES ${JSON_C_SOURCES} ${PROJECT_SOURCE_DIR}/json_pointer.c)
set(JSON_H_JSON_POINTER "#include \"json_pointer.h\"")
if (NOT DISABLE_JSON_PATCH)
set(JSON_C_PUBLIC_HEADERS ${JSON_C_PUBLIC_HEADERS} ${PROJECT_SOURCE_DIR}/json_patch.h)
set(JSON_C_SOURCES ${JSON_C_SOURCES} ${PROJECT_SOURCE_DIR}/json_patch.c)
set(JSON_H_JSON_PATCH "#include \"json_patch.h\"")
endif()
else()
set(JSON_H_JSON_POINTER "")
set(JSON_H_JSON_PATCH "")
endif()
configure_file(json.h.cmakein ${PROJECT_BINARY_DIR}/json.h @ONLY)
include_directories(${PROJECT_SOURCE_DIR}) include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_BINARY_DIR})
add_subdirectory(doc) add_subdirectory(doc)
# uninstall # "uninstall" custom target for make generators in unix like operating systems
add_custom_target(uninstall # and if that target is not present
COMMAND cat ${PROJECT_BINARY_DIR}/install_manifest.txt | xargs rm if (CMAKE_GENERATOR STREQUAL "Unix Makefiles")
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} if(NOT TARGET uninstall)
) add_custom_target(uninstall
COMMAND cat ${PROJECT_BINARY_DIR}/install_manifest.txt | xargs rm
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
endif()
# XXX for a normal full distribution we'll need to figure out # XXX for a normal full distribution we'll need to figure out
# XXX how to build both shared and static libraries. # XXX how to build both shared and static libraries.
@@ -415,9 +540,10 @@ add_custom_target(uninstall
add_library(${PROJECT_NAME} add_library(${PROJECT_NAME}
${JSON_C_SOURCES} ${JSON_C_SOURCES}
${JSON_C_HEADERS} ${JSON_C_HEADERS}
${JSON_C_RESOURCES}
) )
set_target_properties(${PROJECT_NAME} PROPERTIES set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION 5.0.0 VERSION 5.4.0
SOVERSION 5) SOVERSION 5)
list(APPEND CMAKE_TARGETS ${PROJECT_NAME}) list(APPEND CMAKE_TARGETS ${PROJECT_NAME})
# If json-c is used as subroject it set to target correct interface -I flags and allow # If json-c is used as subroject it set to target correct interface -I flags and allow
@@ -428,6 +554,8 @@ target_include_directories(${PROJECT_NAME}
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}> $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
) )
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_REQUIRED_LIBRARIES})
# Allow to build static and shared libraries at the same time # Allow to build static and shared libraries at the same time
if (BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS) if (BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS)
set(STATIC_LIB ${PROJECT_NAME}-static) set(STATIC_LIB ${PROJECT_NAME}-static)
@@ -435,6 +563,13 @@ if (BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS)
${JSON_C_SOURCES} ${JSON_C_SOURCES}
${JSON_C_HEADERS} ${JSON_C_HEADERS}
) )
target_include_directories(${PROJECT_NAME}-static
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
)
target_link_libraries(${PROJECT_NAME}-static PUBLIC ${CMAKE_REQUIRED_LIBRARIES})
# rename the static library # rename the static library
if (NOT MSVC) if (NOT MSVC)
@@ -461,7 +596,7 @@ install(TARGETS ${CMAKE_TARGETS}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/json-c
) )
install(EXPORT ${PROJECT_NAME}-targets install(EXPORT ${PROJECT_NAME}-targets
@@ -481,7 +616,6 @@ install(
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
) )
if (UNIX OR MINGW OR CYGWIN)
SET(prefix ${CMAKE_INSTALL_PREFIX}) SET(prefix ${CMAKE_INSTALL_PREFIX})
# exec_prefix is prefix by default and CMake does not have the # exec_prefix is prefix by default and CMake does not have the
# concept. # concept.
@@ -489,9 +623,38 @@ if (UNIX OR MINGW OR CYGWIN)
SET(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) SET(libdir ${CMAKE_INSTALL_FULL_LIBDIR})
SET(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) SET(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
SET(VERSION ${PROJECT_VERSION}) SET(VERSION ${PROJECT_VERSION})
# Linking against the static json-c requires
# dependent packages to include additional libs:
SET(LIBS_LIST ${CMAKE_REQUIRED_LIBRARIES})
# Note: We would need cmake >= 3.12 in order to use list(TRANSFORM ...)
function(list_transform_prepend var prefix)
set(temp "")
foreach(f ${${var}})
list(APPEND temp "${prefix}${f}")
endforeach()
set(${var} "${temp}" PARENT_SCOPE)
endfunction()
list_transform_prepend(LIBS_LIST "-l")
string(REPLACE ";" " " LIBS "${LIBS_LIST}")
configure_file(json-c.pc.in json-c.pc @ONLY) configure_file(json-c.pc.in json-c.pc @ONLY)
set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files") set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
install(FILES ${PROJECT_BINARY_DIR}/json-c.pc DESTINATION "${INSTALL_PKGCONFIG_DIR}") install(FILES ${PROJECT_BINARY_DIR}/json-c.pc DESTINATION "${INSTALL_PKGCONFIG_DIR}")
endif ()
install(FILES ${JSON_C_PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/json-c) install(FILES ${JSON_C_PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/json-c)
if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING AND
(NOT MSVC OR NOT (MSVC_VERSION LESS 1800)) # Tests need at least VS2013
)
add_subdirectory(tests)
endif()
if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_APPS)
# skip apps when we're included in someone else's build
if (NOT MSVC) # cmd line apps don't built on Windows currently.
add_subdirectory(apps)
endif()
endif()

171
ChangeLog
View File

@@ -1,6 +1,165 @@
Next Release 0.15 0.19 (up to commit 8948568, 2026-01-18)
===================== ========================================
Deprecated and removed features:
--------------------------------
* none
New features
------------
* Add support for Commodore Amiga and MorphOS
* Allow builds with CMake 4 - sync minimum version across all CMakeLists.txt files
* Update openssl command to work for newer versions of openssl.
* Add support for building json-c with meson
* Support MSVC in packages that use GNU Autoconf.
* Add VERSIONINFO to libjson-c.dll
* Make json_tokener_free(NULL) a no-op, to simplify cleanup paths.
* Explicitly handle NaN values when converting to int
* Set errno=RANGE in json_object_get_int/int64/uint64() when the source value can't be represented in the target type.
* Make json_parse a bit more useful by adding -u (validate UTF8) and -P (specify arbitrary tokener parse flags), and read from stdin if no filename is provided.
* Fix the apps/json_parse "-s" (strict) option so it actually does something, and default to non-strict.
Significant changes and bug fixes
---------------------------------
* Issue #867 - also disallow control characters in keys in JSON_TOKENER_STRICT mode
* Issue #875: cast to unsigned char so bytes above 0x7f aren't interpreted as negative, which was causing the strict-mode control characters check to incorrectly trigger.
* Issue #881: don't allow json_tokener_new_ex() with a depth < 1
* Fix linkhash breaking -std=c89
* Fixing Cmake build when using clang-cl, avoids errors about redefining existing symbols
* Fix AIX build failure - Add CMake detection for getopt.h
* Fix bug involving supplemental code points that look like high surrogates
* Fix runtime issue with test_util_file.c in Windows (add O_BINARY)
* Fix macro WIN32_LEAN_AND_MEAN redefined
0.18 (up to commit 6bfab90, 2024-09-15)
========================================
Deprecated and removed features:
--------------------------------
* Clean up pre-3.9 CMake support in CMakeLists.txt
New features
------------
* Build pkg-config for msvc as well
Significant changes and bug fixes
---------------------------------
* Critical fix for binary compatibility with 0.16: Move the
json_tokener_error_memory entry to the end of enum json_tokener_error.
* Issue #829: attempt to detect clang-cl.exe and pass MSVC-compatile command
line arguments.
* PR #831 - rename WIN32 to _WIN32
* PR #839 - Fix gcc 5 "may be used uninitialized" failure in json_pointer.c
* PR #849 - random_seed.c: add a Coverity Scan suppression
* Issue #854: Set error=json_tokener_error_memory in
json_tokener_parser_verbose() when allocating the tokener fails.
* Issue #857: fix a few places where json_tokener should have been returning
json_tokener_error_memory but wasn't.
* Handle yet another out-of-memory condition in json_tokener, duplocate can
return NULL.
* Various fixes in the fuzzers
* A few minor doc fixes
***
0.17 (up to commit 077661f, 2023-08-08)
========================================
Known Bugs
----------
* DO NOT USE THE 0.17 RELEASE: json_tokener_error_memory in misplaced in the
json_tokener_error enum, leading to binary compatibility issues.
Deprecated and removed features:
--------------------------------
* None
New features
------------
* json_patch: add first implementation only with patch application
* Add --disable-static and --disable-dynamic options to the cmake-configure script.
* Add -DBUILD_APPS=NO option to disable app build
* Minimum cmake version is now 3.9
Significant changes and bug fixes
---------------------------------
* When serializing with JSON_C_TO_STRING_PRETTY set, keep the opening and
closing curly or square braces on same line for empty objects or arrays.
* Disable locale handling when targeting a uClibc system due to problems
with its duplocale() function.
* When parsing with JSON_TOKENER_STRICT set, integer overflow/underflow
now result in a json_tokener_error_parse_number. Without that flag
values are capped at INT64_MIN/UINT64_MAX.
* Fix memory leak with emtpy strings in json_object_set_string
* json_object_from_fd_ex: fail if file is too large (>=INT_MAX bytes)
* Add back json_number_chars, but only because it's part of the public API.
* Entirely drop mode bits from open(O_RDONLY) to avoid warnings on certain
platforms.
* Specify dependent libraries, including -lbsd, in a more consistent way so
linking against a static json-c works better
* Fix a variety of build problems and add & improve tests
* Update RFC reference to https://www.rfc-editor.org/rfc/rfc8259
***
0.16 (up to commit 66dcdf5, 2022-04-13)
========================================
Deprecated and removed features:
--------------------------------
* JSON_C_OBJECT_KEY_IS_CONSTANT is deprecated in favor of
JSON_C_OBJECT_ADD_CONSTANT_KEY
* Direct access to lh_table and lh_entry structure members is deprecated.
Use access functions instead, lh_table_head(), lh_entry_next(), etc...
* Drop REFCOUNT_DEBUG code.
New features
------------
* The 0.16 release introduces no new features
Build changes
-------------
* Add a DISABLE_EXTRA_LIBS option to skip using libbsd
* Add a DISABLE_JSON_POINTER option to skip compiling in json_pointer support.
Significant changes and bug fixes
---------------------------------
* Cap string length at INT_MAX to avoid various issues with very long strings.
* json_object_deep_copy: fix deep copy of strings containing '\0'
* Fix read past end of buffer in the "json_parse" command
* Avoid out of memory accesses in the locally provided vasprintf() function
(for those platforms that use it)
* Handle allocation failure in json_tokener_new_ex
* Fix use-after-free in json_tokener_new_ex() in the event of printbuf_new() returning NULL
* printbuf_memset(): set gaps to zero - areas within the print buffer which
have not been initialized by using printbuf_memset
* printbuf: return -1 on invalid arguments (len < 0 or total buffer > INT_MAX)
* sprintbuf(): propagate printbuf_memappend errors back to the caller
Optimizations
--------------
* Speed up parsing by replacing ctype functions with simplified, faster
non-locale-sensitive ones in json_tokener and json_object_to_json_string.
* Neither vertical tab nor formfeed are considered whitespace per the JSON spec
* json_object: speed up creation of objects, calloc() -> malloc() + set fields
* Avoid needless extra strlen() call in json_c_shallow_copy_default() and
json_object_equal() when the object is known to be a json_type_string.
Other changes
-------------
* Validate size arguments in arraylist functions.
* Use getrandom() if available; with GRND_NONBLOCK to allow use of json-c
very early during boot, such as part of cryptsetup.
* Use arc4random() if it's available.
* random_seed: on error, continue to next method instead of exiting the process
* Close file when unable to read from /dev/urandom in get_dev_random_seed()
***
0.15 (up to commit 870965e, 2020/07/26)
========================================
Deprecated and removed features: Deprecated and removed features:
-------------------------------- --------------------------------
@@ -59,7 +218,7 @@ Other changes
* #589 - Detect broken RDRAND during initialization; also, fix segfault * #589 - Detect broken RDRAND during initialization; also, fix segfault
in the CPUID check. in the CPUID check.
* #592 - Fix integer overflows to prevert out of bounds write on large input. * #592 - Fix integer overflows to prevert out of bounds write on large input.
* Protect against division by zero in linkhash, when creaed with zero size. * Protect against division by zero in linkhash, when created with zero size.
* #602 - Fix json_parse_uint64() internal error checking, leaving the retval * #602 - Fix json_parse_uint64() internal error checking, leaving the retval
untouched in more failure cases. untouched in more failure cases.
* #614 - Prevent truncation when custom double formatters insert extra \0's * #614 - Prevent truncation when custom double formatters insert extra \0's
@@ -185,7 +344,7 @@ Behavior changes:
* Use size_t for array length and size. Platforms where sizeof(size_t) != sizeof(int) may not be backwards compatible * Use size_t for array length and size. Platforms where sizeof(size_t) != sizeof(int) may not be backwards compatible
See commits 45c56b, 92e9a5 and others. See commits 45c56b, 92e9a5 and others.
* Check for failue when allocating memory, returning NULL and errno=ENOMEM. * Check for failure when allocating memory, returning NULL and errno=ENOMEM.
See commit 2149a04. See commit 2149a04.
* Change json_object_object_add() return type from void to int, and will return -1 on failures, instead of exiting. (Note: this is not an ABI change) * Change json_object_object_add() return type from void to int, and will return -1 on failures, instead of exiting. (Note: this is not an ABI change)
@@ -376,7 +535,7 @@ List of new functions added:
* Add an alternative iterator implementation, see json_object_iterator.h * Add an alternative iterator implementation, see json_object_iterator.h
* Make json_object_iter public to enable external use of the * Make json_object_iter public to enable external use of the
json_object_object_foreachC macro. json_object_object_foreachC macro.
* Add a printbuf_memset() function to provide an effecient way to set and * Add a printbuf_memset() function to provide an efficient way to set and
append things like whitespace indentation. append things like whitespace indentation.
* Adjust json_object_is_type and json_object_get_type so they return * Adjust json_object_is_type and json_object_get_type so they return
json_type_null for NULL objects and handle NULL passed to json_type_null for NULL objects and handle NULL passed to
@@ -462,7 +621,7 @@ List of new functions added:
0.7 0.7
=== ===
* Add escaping of backslash to json output * Add escaping of backslash to json output
* Add escaping of foward slash on tokenizing and output * Add escaping of forward slash on tokenizing and output
* Changes to internal tokenizer from using recursion to * Changes to internal tokenizer from using recursion to
using a depth state structure to allow incremental parsing using a depth state structure to allow incremental parsing

View File

@@ -26,12 +26,12 @@
</ul> </ul>
<h3>Documentation</h3> <h3>Documentation</h3>
<P>Doxygen generated documentation exists <a href="http://json-c.github.io/json-c/">here</a>.</P> <P>Doxygen generated documentation exists <a href="https://json-c.github.io/json-c/">here</a>.</P>
<h3><a href="https://github.com/json-c/json-c">GIT Reposository</a></h3> <h3><a href="https://github.com/json-c/json-c">GIT Reposository</a></h3>
<p><strong><code>git clone https://github.com/json-c/json-c.git</code></strong></p> <p><strong><code>git clone https://github.com/json-c/json-c.git</code></strong></p>
<h3><a href="http://groups.google.com/group/json-c">Mailing List</a></h3> <h3><a href="https://groups.google.com/group/json-c">Mailing List</a></h3>
<pi>Send email to <strong><code>json-c <i>&lt;at&gt;</i> googlegroups <i>&lt;dot&gt;</i> com</code></strong></p> <pi>Send email to <strong><code>json-c <i>&lt;at&gt;</i> googlegroups <i>&lt;dot&gt;</i> com</code></strong></p>
<h3><a href="COPYING">License</a></h3> <h3><a href="COPYING">License</a></h3>

208
README.md
View File

@@ -1,43 +1,75 @@
\mainpage \mainpage
`json-c` json-c
======== ========
1. [Overview and Build Status](#overview) 1. [Overview and Build Status](#overview)
2. [Building on Unix](#buildunix) 2. [Getting Help](#gettinghelp)
3. [Building on Unix](#buildunix)
* [Prerequisites](#installprereq) * [Prerequisites](#installprereq)
* [Build commands](#buildcmds) * [Build commands](#buildcmds)
3. [CMake options](#CMake) 4. [CMake options](#CMake)
4. [Testing](#testing) 5. [Testing](#testing)
5. [Building with `vcpkg`](#buildvcpkg) 6. [Building with `vcpkg`](#buildvcpkg)
6. [Linking to libjson-c](#linking) 7. [Building for Android](#android)
7. [Using json-c](#using) 8. [Building for Commodore Amiga or MorphOS](#amiga)
9. [Linking to libjson-c](#linking)
10. [Using json-c](#using)
JSON-C - A JSON implementation in C <a name="overview"></a> <a name="overview"></a>
JSON-C - A JSON implementation in C
----------------------------------- -----------------------------------
Build Status
* [AppVeyor Build](https://ci.appveyor.com/project/hawicz/json-c) ![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/json-c/json-c?branch=master&svg=true)
* [Travis Build](https://travis-ci.org/json-c/json-c) ![Travis Build Status](https://travis-ci.org/json-c/json-c.svg?branch=master)
Test Status
* [Coveralls](https://coveralls.io/github/json-c/json-c?branch=master) [![Coverage Status](https://coveralls.io/repos/github/json-c/json-c/badge.svg?branch=master)](https://coveralls.io/github/json-c/json-c?branch=master)
JSON-C implements a reference counting object model that allows you to easily JSON-C implements a reference counting object model that allows you to easily
construct JSON objects in C, output them as JSON formatted strings and parse construct JSON objects in C, output them as JSON formatted strings and parse
JSON formatted strings back into the C representation of JSON objects. JSON formatted strings back into the C representation of JSON objects.
It aims to conform to [RFC 7159](https://tools.ietf.org/html/rfc7159). It aims to conform to [RFC 8259](https://www.rfc-editor.org/rfc/rfc8259).
Building on Unix with `git`, `gcc` and `cmake` <a name="buildunix"></a> Skip down to [Using json-c](#using)
-------------------------------------------------- or check out the [API docs](https://json-c.github.io/json-c/),
if you already have json-c installed and ready to use.
Home page for json-c: https://github.com/json-c/json-c/wiki Home page for json-c: https://github.com/json-c/json-c/wiki
### Prerequisites: <a name="installprereq"></a> <a name="gettinghelp"></a>
Getting Help
------------
If you have questions about using json-c, please start a thread on
our forums at: https://groups.google.com/forum/#!forum/json-c
If you believe you've discovered a bug, report it at
(https://github.com/json-c/json-c/issues). Please be sure to include
the version of json-c you're using, the OS you're running on, and any
other relevant details. Fully reproducible test cases and/or patches
to fix problems are greatly appreciated.
Fixes for bugs, or small new features can be directly submitted as a
[pull request](https://github.com/json-c/json-c/pulls). For major new
features or large changes of any kind, please first start a discussion
on the [forums](https://groups.google.com/forum/#!forum/json-c).
<a name="buildunix"></a>
Building on Unix with `git`, `gcc` and `cmake`
--------------------------------------------------
If you already have json-c installed, see [Linking to `libjson-c`](#linking)
for how to build and link your program against it.
Build Status
* [AppVeyor Build](https://ci.appveyor.com/project/hawicz/json-c) ![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/json-c/json-c?branch=master&svg=true)
* [Travis Build](https://app.travis-ci.com/github/json-c/json-c) ![Travis Build Status](https://api.travis-ci.com/json-c/json-c.svg?branch=master)
Test Status
* [Coveralls](https://coveralls.io/github/json-c/json-c?branch=master) [![Coverage Status](https://coveralls.io/repos/github/json-c/json-c/badge.svg?branch=master)](https://coveralls.io/github/json-c/json-c?branch=master)
<a name="installprereq"></a>
### Prerequisites:
- `gcc`, `clang`, or another C compiler - `gcc`, `clang`, or another C compiler
- cmake>=2.8, >=3.16 recommended - `cmake>=2.8`, `>=3.16` recommended, `cmake=>3.1` for tests
To generate docs you'll also need: To generate docs you'll also need:
- `doxygen>=1.8.13` - `doxygen>=1.8.13`
@@ -53,7 +85,8 @@ sudo apt install doxygen # optional
sudo apt install valgrind # optional sudo apt install valgrind # optional
``` ```
### Build instructions: <a name="buildcmds"></a> <a name="buildcmds"></a>
### Build instructions:
`json-c` GitHub repo: https://github.com/json-c/json-c `json-c` GitHub repo: https://github.com/json-c/json-c
@@ -74,13 +107,13 @@ Then:
$ make $ make
$ make test $ make test
$ make USE_VALGRIND=0 test # optionally skip using valgrind $ make USE_VALGRIND=0 test # optionally skip using valgrind
$ make install $ sudo make install # it could be necessary to execute make install
``` ```
### Generating documentation with Doxygen: ### Generating documentation with Doxygen:
The libray documentation can be generated directly from the source codes using Doxygen tool: The library documentation can be generated directly from the source code using Doxygen tool:
```sh ```sh
# in build directory # in build directory
@@ -89,7 +122,8 @@ google-chrome doc/html/index.html
``` ```
CMake Options <a name="CMake"></a> <a name="CMake"></a>
CMake Options
-------------------- --------------------
The json-c library is built with [CMake](https://cmake.org/cmake-tutorial/), The json-c library is built with [CMake](https://cmake.org/cmake-tutorial/),
@@ -105,6 +139,8 @@ DISABLE_STATIC_FPIC | Bool | The default builds position independent
DISABLE_BSYMBOLIC | Bool | Disable use of -Bsymbolic-functions. DISABLE_BSYMBOLIC | Bool | Disable use of -Bsymbolic-functions.
DISABLE_THREAD_LOCAL_STORAGE | Bool | Disable use of Thread-Local Storage (HAVE___THREAD). DISABLE_THREAD_LOCAL_STORAGE | Bool | Disable use of Thread-Local Storage (HAVE___THREAD).
DISABLE_WERROR | Bool | Disable use of -Werror. DISABLE_WERROR | Bool | Disable use of -Werror.
DISABLE_EXTRA_LIBS | Bool | Disable use of extra libraries, libbsd
DISABLE_JSON_POINTER | Bool | Omit json_pointer support from the build.
ENABLE_RDRAND | Bool | Enable RDRAND Hardware RNG Hash Seed. ENABLE_RDRAND | Bool | Enable RDRAND Hardware RNG Hash Seed.
ENABLE_THREADING | Bool | Enable partial threading support. ENABLE_THREADING | Bool | Enable partial threading support.
OVERRIDE_GET_RANDOM_SEED | String | A block of code to use instead of the default implementation of json_c_get_random_seed(), e.g. on embedded platforms where not even the fallback to time() works. Must be a single line. OVERRIDE_GET_RANDOM_SEED | String | A block of code to use instead of the default implementation of json_c_get_random_seed(), e.g. on embedded platforms where not even the fallback to time() works. Must be a single line.
@@ -159,7 +195,8 @@ cmake-configure can take a few options.
| disable-werror | Avoid treating compiler warnings as fatal errors | | disable-werror | Avoid treating compiler warnings as fatal errors |
Testing: <a name="testing"></a> <a name="testing"></a>
Testing:
---------- ----------
By default, if valgrind is available running tests uses it. By default, if valgrind is available running tests uses it.
@@ -186,7 +223,7 @@ If a test fails, check `Testing/Temporary/LastTest.log`,
`tests/testSubDir/${testname}/${testname}.vg.out`, and other similar files. `tests/testSubDir/${testname}/${testname}.vg.out`, and other similar files.
If there is insufficient output try: If there is insufficient output try:
```sh ```sh
VERBOSE=1 make test VERBOSE=1 CTEST_OUTPUT_ON_FAILURE=1 make test
``` ```
or or
```sh ```sh
@@ -195,7 +232,8 @@ JSONC_TEST_TRACE=1 make test
and check the log files again. and check the log files again.
Building on Unix and Windows with `vcpkg` <a name="buildvcpkg"></a> <a name="buildvcpkg"></a>
Building on Unix and Windows with `vcpkg`
-------------------------------------------------- --------------------------------------------------
You can download and install JSON-C using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: You can download and install JSON-C using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
@@ -208,8 +246,90 @@ You can download and install JSON-C using the [vcpkg](https://github.com/Microso
The JSON-C port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. The JSON-C port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
<a name="android"></a>
Building for Android
----------------------
Linking to `libjson-c` <a name="linking"> Building on Android is now particularly well supported, but there
have been some reports of success using
https://developer.android.com/ndk/guides/cmake
```
mkdir json-c-build
cd json-c-build/
export NDK_HOME=~/Library/Android/sdk/ndk/22.1.7171670/
cmake \
--toolchain=$NDK_HOME/build/cmake/android.toolchain.cmake \
-DANDROID_STL=none \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-29 \
-DANDROID_LD=lld \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_INSTALL_PREFIX=<install prefix> \
-DENABLE_THREADING=true \
..
make install
```
<a name="amiga"></a>
Building for Commodore Amiga or MorphOS
----------------------
Building for Commodore Amiga is supported for both Motorola 68k (AmigaOS 3) and PowerPC (AmigaOS 4) architectures. MorphOS on compatible PowerPC hardware is also supported. You can set up a cross compiler locally, however it is much easier to use the already preconfigured Amiga development environment wtthin a Docker container.
Install Docker on your machine if you don't already have it. You can download Docker Desktop for Windows/macOS/Linux [here](https://www.docker.com/products/docker-desktop/).
### To build for Motorola 68k Amiga:
```
mkdir build
docker run --rm \
-v ${PWD}:/work \
-e USER=$( id -u ) -e GROUP=$( id -g ) \
-it sacredbanana/amiga-compiler:m68k-amigaos bash
cd build
cmake -DM68K_CRT=newlib ..
make
```
libjson-c.a will get created in the build directory.
You can change newlib to nix20, nix13, ixemul or clib2 if you would like to build the library suited for libnix or clib2 instead. Newlib is default.
### To build for PowerPC Amiga:
```
mkdir build
docker run --rm \
-v ${PWD}:/work \
-e USER=$( id -u ) -e GROUP=$( id -g ) \
-it sacredbanana/amiga-compiler:ppc-amigaos bash
cd build
cmake ..
make
```
libjson-c.a will get created in the build directory.
### To build for PowerPC MorphOS:
```
mkdir build
docker run --rm \
-v ${PWD}:/work \
-e USER=$( id -u ) -e GROUP=$( id -g ) \
-it sacredbanana/amiga-compiler:ppc-morphos bash
cd build
cmake -DNOIXEMUL=1 ..
make
```
If you are making an application that absolutely requires ixemul, then remove the `-DNOIXEMUL=1`.
libjson-c.a will get created in the build directory.
<a name="linking"></a>
Linking to `libjson-c`
---------------------- ----------------------
If your system has `pkgconfig`, If your system has `pkgconfig`,
@@ -220,19 +340,37 @@ CFLAGS += $(shell pkg-config --cflags json-c)
LDFLAGS += $(shell pkg-config --libs json-c) LDFLAGS += $(shell pkg-config --libs json-c)
``` ```
Without `pkgconfig`, you would do something like this: Without `pkgconfig`, you might do something like this:
```make ```make
JSON_C_DIR=/path/to/json_c/install JSON_C_DIR=/path/to/json_c/install
CFLAGS += -I$(JSON_C_DIR)/include/json-c CFLAGS += -I$(JSON_C_DIR)/include/json-c
# Or to use lines like: #include <json-c/json_object.h>
#CFLAGS += -I$(JSON_C_DIR)/include
LDFLAGS+= -L$(JSON_C_DIR)/lib -ljson-c LDFLAGS+= -L$(JSON_C_DIR)/lib -ljson-c
``` ```
If your project uses cmake:
Using json-c <a name="using"> * Add to your CMakeLists.txt file:
```cmake
find_package(json-c CONFIG)
target_link_libraries(${PROJECT_NAME} PRIVATE json-c::json-c)
```
* Then you might run in your project:
```sh
cd build
cmake -DCMAKE_PREFIX_PATH=/path/to/json_c/install/lib64/cmake ..
```
<a name="using"></a>
Using json-c
------------ ------------
To use json-c you can either include json.h, or preferrably, one of the To use json-c you can either include json.h, or preferably, one of the
following more specific header files: following more specific header files:
* json_object.h - Core types and methods. * json_object.h - Core types and methods.
@@ -241,9 +379,9 @@ following more specific header files:
objects from a json-c object tree. objects from a json-c object tree.
* json_object_iterator.h - Methods for iterating over single json_object instances. (See also `json_object_object_foreach()` in json_object.h) * json_object_iterator.h - Methods for iterating over single json_object instances. (See also `json_object_object_foreach()` in json_object.h)
* json_visit.h - Methods for walking a tree of json-c objects. * json_visit.h - Methods for walking a tree of json-c objects.
* json_util.h - Miscelleanous utility functions. * json_util.h - Miscellaneous utility functions.
For a full list of headers see [files.html](http://json-c.github.io/json-c/json-c-current-release/doc/html/files.html) For a full list of headers see [files.html](https://json-c.github.io/json-c/json-c-current-release/doc/html/files.html)
The primary type in json-c is json_object. It describes a reference counted The primary type in json-c is json_object. It describes a reference counted
tree of json objects which are created by either parsing text with a tree of json objects which are created by either parsing text with a
@@ -251,7 +389,7 @@ json_tokener (i.e. `json_tokener_parse_ex()`), or by creating
(with `json_object_new_object()`, `json_object_new_int()`, etc...) and adding (with `json_object_new_object()`, `json_object_new_int()`, etc...) and adding
(with `json_object_object_add()`, `json_object_array_add()`, etc...) them (with `json_object_object_add()`, `json_object_array_add()`, etc...) them
individually. individually.
Typically, every object in the tree will have one reference, from it's parent. Typically, every object in the tree will have one reference, from its parent.
When you are done with the tree of objects, you call json_object_put() on just When you are done with the tree of objects, you call json_object_put() on just
the root object to free it, which recurses down through any child objects the root object to free it, which recurses down through any child objects
calling json_object_put() on each one of those in turn. calling json_object_put() on each one of those in turn.
@@ -266,7 +404,7 @@ the parent being freed or it being removed from its parent
When parsing text, the json_tokener object is independent from the json_object When parsing text, the json_tokener object is independent from the json_object
that it returns. It can be allocated (`json_tokener_new()`) that it returns. It can be allocated (`json_tokener_new()`)
used ones or multiple times (`json_tokener_parse_ex()`, and used one or multiple times (`json_tokener_parse_ex()`, and
freed (`json_tokener_free()`) while the json_object objects live on. freed (`json_tokener_free()`) while the json_object objects live on.
A json_object tree can be serialized back into a string with A json_object tree can be serialized back into a string with

View File

@@ -16,11 +16,19 @@
changes (added/removed/updated funcs, etc...), and detect backwards compat changes (added/removed/updated funcs, etc...), and detect backwards compat
issues. issues.
* https://github.com/lvc/abi-compliance-checker * https://github.com/lvc/abi-compliance-checker
* See also `abi-check.sh`
* If the new release is not backwards compatible, then this is a MAJOR release. * If the new release is not backwards compatible, then this is a MAJOR release.
* Mention removed features in ChangeLog * Mention removed features in ChangeLog
* Consider re-adding backwards compatible support, through symbol * Consider re-adding backwards compatible support, through symbol
aliases and appropriate entries in json-c.sym aliases and appropriate entries in json-c.sym
* Be sure any new symbols are listed in json-c.sym as part of
the _new_ release version.
* Update the AUTHORS file * Update the AUTHORS file
PREV=$(git tag | tail -1)
( git log -r ${PREV}..HEAD | grep Author: | sed -e's/Author: //' ; cat AUTHORS ) | sort -u > A1
mv A1 AUTHORS
* Exclude mentioning changes that have already been included in a point * Exclude mentioning changes that have already been included in a point
release of the previous release branch. release of the previous release branch.
@@ -33,29 +41,33 @@
## Release creation ## Release creation
Start creating the new release: Start creating the new release:
release=0.15
PREV=$(git tag | tail -1)
PREV=${PREV#json-c-}
PREV=${PREV%-*}
release=0.$((${PREV#*.} + 1))
cd ~
git clone https://github.com/json-c/json-c json-c-${release} git clone https://github.com/json-c/json-c json-c-${release}
rm -rf distcheck
mkdir distcheck mkdir distcheck
cd distcheck cd distcheck
# Note, the build directory *must* be entirely separate from # Note, the build directory *must* be entirely separate from
# the source tree for distcheck to work properly. # the source tree for distcheck to work properly.
cmake ../json-c-${release} cmake -DCMAKE_BUILD_TYPE=Release ../json-c-${release}
make distcheck make distcheck
cd .. cd ..
Make any fixes/changes *before* branching. Make any fixes/changes *before* branching.
cd json-c-${release} cd json-c-${release}
git branch json-c-${release} git checkout -b json-c-${release}
git checkout json-c-${release}
------------ ------------
Using ${release}: Using ${release}:
Update the version in json_c_version.h Update the version in json_c_version.h
Update the version in CMakeLists.txt (VERSION in the project(...) line) Update the version in CMakeLists.txt (VERSION in the project(...) line)
Update the version in config.h.win32 (several places)
Update the set_target_properties() line in CmakeLists.txt to set the shared Update the set_target_properties() line in CmakeLists.txt to set the shared
library version. Generally, unless we're doing a major release, change: library version. Generally, unless we're doing a major release, change:
@@ -74,9 +86,11 @@ If we're doing a major release (SONAME bump), also bump the version
Generate the doxygen documentation: Generate the doxygen documentation:
doxygen (cd ../distcheck && make doc)
cp -r -p ../distcheck/doc/{html,Doxyfile} doc/.
rm doc/Doxyfile # Remove generated file w/ hardcoded paths
git add -f doc git add -f doc
git commit doc git commit doc -m "Generate docs for the ${release} release"
------------ ------------
@@ -86,7 +100,11 @@ Create the release tarballs:
echo .git > excludes echo .git > excludes
tar -czf json-c-${release}.tar.gz -X excludes json-c-${release} tar -czf json-c-${release}.tar.gz -X excludes json-c-${release}
echo doc >> excludes echo 'doc/*.cmake' >> excludes
echo 'doc/CMakeFiles' >> excludes
echo 'doc/Makefile' >> excludes
echo 'doc/Doxyfile' >> excludes
echo 'doc/html' >> excludes
tar -czf json-c-${release}-nodoc.tar.gz -X excludes json-c-${release} tar -czf json-c-${release}-nodoc.tar.gz -X excludes json-c-${release}
------------ ------------
@@ -104,8 +122,9 @@ Tag the branch:
Go to Amazon S3 service at: Go to Amazon S3 service at:
https://console.aws.amazon.com/s3/ https://console.aws.amazon.com/s3/
Upload the two tarballs in the json-c_releases folder. Upload the two tarballs in the json-c_releases/releases folder.
When uploading, use "Standard" storage class, and make the uploaded files publicly accessible. * Expand "Permissions", pick "Grant public-read access"
* Expand "Properties", ensure "Standard" storage class is picked.
Logout of Amazon S3, and verify that the files are visible. Logout of Amazon S3, and verify that the files are visible.
https://s3.amazonaws.com/json-c_releases/releases/index.html https://s3.amazonaws.com/json-c_releases/releases/index.html
@@ -121,13 +140,12 @@ Add new section to ChangeLog for ${release}+1
Use ${release}.99 to indicate a version "newer" than anything on the branch: Use ${release}.99 to indicate a version "newer" than anything on the branch:
Update the version in json_c_version.h Update the version in json_c_version.h
Update the version in CMakeLists.txt Update the version in CMakeLists.txt
Update the version in config.h.win32
Update RELEASE_CHECKLIST.txt, set release=${release}+1
Update the set_target_properties() line in CmakeLists.txt to match the release branch. Update the set_target_properties() line in CmakeLists.txt to match the release branch.
git commit -a -m "Update the master branch to version 0.${release}.99" Add a new empty section to the json-c.sym file, for ${release}+1
git commit -a -m "Update the master branch to version ${release}.99"
git push git push
------------ ------------
@@ -143,6 +161,8 @@ Update the gh-pages branch with new docs:
mkdir json-c-${release} mkdir json-c-${release}
cp -R ../json-c-${release}/doc json-c-${release}/. cp -R ../json-c-${release}/doc json-c-${release}/.
git add json-c-${release} git add json-c-${release}
rm json-c-current-release
ln -s json-c-${release} json-c-current-release
git commit -a -m "Add the ${release} docs." git commit -a -m "Add the ${release} docs."
vi index.html vi index.html
@@ -157,11 +177,12 @@ Update the gh-pages branch with new docs:
Update checksums on wiki page. Update checksums on wiki page.
cd .. cd ..
openssl sha -sha256 json-c*gz openssl sha1 -sha256 json-c*gz
openssl md5 json-c*gz openssl md5 json-c*gz
Copy and paste this output into the wiki page at: Copy and paste this output into the wiki page at:
https://github.com/json-c/json-c/wiki * https://github.com/json-c/json-c/wiki
* https://github.com/json-c/json-c/wiki/Old-Releases
------------ ------------

50
abi-check.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/sh
set -e
# The 0.17 release is broken
prev=0.18
release=0.19
# ... clone json-c, abi-compliance-checker, abi-dumper
if [ "$1" != "--skip-build" ] ; then
mkdir build
cd build
CFLAGS=-Og cmake -DCMAKE_INSTALL_PREFIX=~/json-c-installs/json-c-${release} ..
make && make test && make install
fi
# Assume the old version has already been built
cd ~/abi-compliance-checker
mkxml()
{
ver="$1"
if [ ! -e ../json-c-installs/json-c-${ver}/lib64 ] ; then
ln -s lib ../json-c-installs/json-c-${ver}/lib64
fi
cat <<EOF > json-c-${ver}.xml
<foo>
<version>
${ver}
</version>
<headers>
../json-c-installs/json-c-${ver}/include/json-c
</headers>
<libs>
../json-c-installs/json-c-${ver}/lib64/libjson-c.so
</libs>
</foo>
EOF
}
mkxml ${release}
mkxml ${prev}
perl abi-compliance-checker.pl -lib json-c -dump json-c-${prev}.xml -dump-path ./ABI-${prev}.dump
perl abi-compliance-checker.pl -lib json-c -dump json-c-${release}.xml -dump-path ./ABI-${release}.dump
perl abi-compliance-checker.pl -l json-c -old ABI-${prev}.dump -new ABI-${release}.dump
echo "look in compat_reports/json-c/..."

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8) # see ../CMakeLists.txt for why 2.8 cmake_minimum_required(VERSION 3.9...3.12) # see ../CMakeLists.txt for why
if(POLICY CMP0075) if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW) cmake_policy(SET CMP0075 NEW)
@@ -94,6 +94,9 @@ endif() # end "standalone mode" block
# --------------------------------- # ---------------------------------
# Check whether getopt.h exists
check_include_file(getopt.h HAVE_GETOPT_H)
check_include_file(sys/resource.h HAVE_SYS_RESOURCE_H) # for getrusage check_include_file(sys/resource.h HAVE_SYS_RESOURCE_H) # for getrusage
if (HAVE_SYS_RESOURCE_H) if (HAVE_SYS_RESOURCE_H)
check_symbol_exists(getrusage "sys/resource.h" HAVE_GETRUSAGE) check_symbol_exists(getrusage "sys/resource.h" HAVE_GETRUSAGE)

View File

@@ -1,7 +1,9 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -22,16 +24,32 @@
#include <sys/time.h> #include <sys/time.h>
#endif #endif
static int formatted_output = 0; #ifndef JSON_NORETURN
#if defined(_MSC_VER)
#define JSON_NORETURN __declspec(noreturn)
#elif defined(__OS400__)
#define JSON_NORETURN
#else
/* 'cold' attribute is for optimization, telling the computer this code
* path is unlikely.
*/
#define JSON_NORETURN __attribute__((noreturn, cold))
#endif
#endif
static int formatted_output = JSON_C_TO_STRING_SPACED;
static int show_output = 1; static int show_output = 1;
static int strict_mode = 0; static int strict_mode = 0;
static int validate_utf8 = 0;
static int tokener_flags = 0;
static int color = 0;
static const char *fname = NULL; static const char *fname = NULL;
#ifndef HAVE_JSON_TOKENER_GET_PARSE_END #ifndef HAVE_JSON_TOKENER_GET_PARSE_END
#define json_tokener_get_parse_end(tok) ((tok)->char_offset) #define json_tokener_get_parse_end(tok) ((tok)->char_offset)
#endif #endif
static void usage(const char *argv0, int exitval, const char *errmsg); JSON_NORETURN static void usage(const char *argv0, int exitval, const char *errmsg);
static void showmem(void); static void showmem(void);
static int parseit(int fd, int (*callback)(struct json_object *)); static int parseit(int fd, int (*callback)(struct json_object *));
static int showobj(struct json_object *new_obj); static int showobj(struct json_object *new_obj);
@@ -42,7 +60,7 @@ static void showmem(void)
struct rusage rusage; struct rusage rusage;
memset(&rusage, 0, sizeof(rusage)); memset(&rusage, 0, sizeof(rusage));
getrusage(RUSAGE_SELF, &rusage); getrusage(RUSAGE_SELF, &rusage);
printf("maxrss: %ld KB\n", rusage.ru_maxrss); fprintf(stderr, "maxrss: %ld KB\n", rusage.ru_maxrss);
#endif #endif
} }
@@ -50,7 +68,7 @@ static int parseit(int fd, int (*callback)(struct json_object *))
{ {
struct json_object *obj; struct json_object *obj;
char buf[32768]; char buf[32768];
int ret; ssize_t ret;
int depth = JSON_TOKENER_DEFAULT_DEPTH; int depth = JSON_TOKENER_DEFAULT_DEPTH;
json_tokener *tok; json_tokener *tok;
@@ -60,11 +78,21 @@ static int parseit(int fd, int (*callback)(struct json_object *))
fprintf(stderr, "unable to allocate json_tokener: %s\n", strerror(errno)); fprintf(stderr, "unable to allocate json_tokener: %s\n", strerror(errno));
return 1; return 1;
} }
json_tokener_set_flags(tok, JSON_TOKENER_STRICT if (strict_mode)
{
tokener_flags |= JSON_TOKENER_STRICT;
#ifdef JSON_TOKENER_ALLOW_TRAILING_CHARS #ifdef JSON_TOKENER_ALLOW_TRAILING_CHARS
| JSON_TOKENER_ALLOW_TRAILING_CHARS tokener_flags |= JSON_TOKENER_ALLOW_TRAILING_CHARS;
#endif #endif
); }
if (validate_utf8)
{
tokener_flags |= JSON_TOKENER_VALIDATE_UTF8;
}
if (tokener_flags)
{
json_tokener_set_flags(tok, tokener_flags);
}
// XXX push this into some kind of json_tokener_parse_fd API? // XXX push this into some kind of json_tokener_parse_fd API?
// json_object_from_fd isn't flexible enough, and mirroring // json_object_from_fd isn't flexible enough, and mirroring
@@ -73,19 +101,21 @@ static int parseit(int fd, int (*callback)(struct json_object *))
size_t total_read = 0; size_t total_read = 0;
while ((ret = read(fd, buf, sizeof(buf))) > 0) while ((ret = read(fd, buf, sizeof(buf))) > 0)
{ {
total_read += ret; size_t retu = (size_t)ret; // We know it's positive
int start_pos = 0; total_read += retu;
while (start_pos != ret) size_t start_pos = 0;
while (start_pos != retu)
{ {
obj = json_tokener_parse_ex(tok, &buf[start_pos], ret - start_pos); obj = json_tokener_parse_ex(tok, &buf[start_pos], retu - start_pos);
enum json_tokener_error jerr = json_tokener_get_error(tok); enum json_tokener_error jerr = json_tokener_get_error(tok);
int parse_end = json_tokener_get_parse_end(tok); size_t parse_end = json_tokener_get_parse_end(tok);
if (obj == NULL && jerr != json_tokener_continue) if (obj == NULL && jerr != json_tokener_continue)
{ {
char *aterr = &buf[start_pos + parse_end]; const char *aterr = (start_pos + parse_end < (int)sizeof(buf)) ?
&buf[start_pos + parse_end] : "";
fflush(stdout); fflush(stdout);
int fail_offset = total_read - ret + start_pos + parse_end; size_t fail_offset = total_read - retu + start_pos + parse_end;
fprintf(stderr, "Failed at offset %d: %s %c\n", fail_offset, fprintf(stderr, "Failed at offset %lu: %s %c\n", (unsigned long)fail_offset,
json_tokener_error_desc(jerr), aterr[0]); json_tokener_error_desc(jerr), aterr[0]);
json_tokener_free(tok); json_tokener_free(tok);
return 1; return 1;
@@ -101,7 +131,7 @@ static int parseit(int fd, int (*callback)(struct json_object *))
} }
} }
start_pos += json_tokener_get_parse_end(tok); start_pos += json_tokener_get_parse_end(tok);
assert(start_pos <= ret); assert(start_pos <= retu);
} }
} }
if (ret < 0) if (ret < 0)
@@ -121,15 +151,12 @@ static int showobj(struct json_object *new_obj)
return 1; return 1;
} }
printf("Successfully parsed object from %s\n", fname); fprintf(stderr, "Successfully parsed object from %s\n", fname);
if (show_output) if (show_output)
{ {
const char *output; const char *output;
if (formatted_output) output = json_object_to_json_string_ext(new_obj, formatted_output | color);
output = json_object_to_json_string(new_obj);
else
output = json_object_to_json_string_ext(new_obj, JSON_C_TO_STRING_PRETTY);
printf("%s\n", output); printf("%s\n", output);
} }
@@ -144,11 +171,16 @@ static void usage(const char *argv0, int exitval, const char *errmsg)
fp = stderr; fp = stderr;
if (errmsg != NULL) if (errmsg != NULL)
fprintf(fp, "ERROR: %s\n\n", errmsg); fprintf(fp, "ERROR: %s\n\n", errmsg);
fprintf(fp, "Usage: %s [-f] [-n] [-s]\n", argv0); fprintf(fp, "Usage: %s [-f|-F <arg>] [-n] [-s] [-u] [filename]\n", argv0);
fprintf(fp, " -f - Format the output with JSON_C_TO_STRING_PRETTY\n"); fprintf(fp, " -f - Format the output to stdout with JSON_C_TO_STRING_PRETTY (default is JSON_C_TO_STRING_SPACED)\n");
fprintf(fp, " -F - Format the output to stdout with <arg>, e.g. 0 for JSON_C_TO_STRING_PLAIN\n");
fprintf(fp, " -n - No output\n"); fprintf(fp, " -n - No output\n");
fprintf(fp, " -s - Parse in strict mode, flags:\n"); fprintf(fp, " -c - Set JSON_C_TO_STRING_COLOR to colorize the output\n");
fprintf(fp, " -P - Initialize tokener flags to the given value\n");
fprintf(fp, " -s - Parse in strict mode, add flags:\n");
fprintf(fp, " JSON_TOKENER_STRICT|JSON_TOKENER_ALLOW_TRAILING_CHARS\n"); fprintf(fp, " JSON_TOKENER_STRICT|JSON_TOKENER_ALLOW_TRAILING_CHARS\n");
fprintf(fp, " -u - Add the JSON_TOKENER_VALIDATE_UTF8 flag when parsing\n");
fprintf(fp, " Diagnostic information will be emitted to stderr\n");
fprintf(fp, "\nWARNING WARNING WARNING\n"); fprintf(fp, "\nWARNING WARNING WARNING\n");
fprintf(fp, "This is a prototype, it may change or be removed at any time!\n"); fprintf(fp, "This is a prototype, it may change or be removed at any time!\n");
@@ -157,27 +189,30 @@ static void usage(const char *argv0, int exitval, const char *errmsg)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
json_object *new_obj;
int opt; int opt;
while ((opt = getopt(argc, argv, "fhns")) != -1) while ((opt = getopt(argc, argv, "cfF:hnP:su")) != -1)
{ {
switch (opt) switch (opt)
{ {
case 'f': formatted_output = 1; break; case 'c': color = JSON_C_TO_STRING_COLOR; break;
case 'f': formatted_output = JSON_C_TO_STRING_PRETTY; break;
case 'F': formatted_output = atoi(optarg); break;
case 'n': show_output = 0; break; case 'n': show_output = 0; break;
case 'P': tokener_flags = atoi(optarg); break;
case 's': strict_mode = 1; break; case 's': strict_mode = 1; break;
case 'u': validate_utf8 = 1; break;
case 'h': usage(argv[0], 0, NULL); case 'h': usage(argv[0], 0, NULL);
default: /* '?' */ usage(argv[0], EXIT_FAILURE, "Unknown arguments"); default: /* '?' */ usage(argv[0], EXIT_FAILURE, "Unknown arguments");
} }
} }
if (optind >= argc) int fd = STDIN_FILENO;
fname = "stdin";
if (argc > optind && strcmp(argv[optind], "-") != 0)
{ {
usage(argv[0], EXIT_FAILURE, "Expected argument after options"); fname = argv[optind];
fd = open(fname, O_RDONLY, 0);
} }
fname = argv[optind];
int fd = open(argv[optind], O_RDONLY, 0);
showmem(); showmem();
if (parseit(fd, showobj) != 0) if (parseit(fd, showobj) != 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

27
apps/meson.build Normal file
View File

@@ -0,0 +1,27 @@
appconf_data = configuration_data()
# Check for json_tokener_get_parse_end symbol
appconf_data.set('HAVE_JSON_TOKENER_GET_PARSE_END',
cc.has_function('json_tokener_get_parse_end', prefix: '#include <json_tokener.h>') ? 1 : 0
)
# Check for getrusage if sys/resource.h is available
appconf_data.set('HAVE_SYS_RESOURCE_H', cc.has_header('sys/resource.h') ? 1 : 0, description: 'Define to 1 if you have the <sys/resource.h> header file.')
if appconf_data.get('HAVE_SYS_RESOURCE_H') == 1
appconf_data.set('HAVE_GETRUSAGE',
cc.has_function('getrusage', prefix: '#include <sys/resource.h>') ? 1 : 0, description: 'Define if you have the `getrusage` function. ')
else
appconf_data.set('HAVE_GETRUSAGE', 0, description: 'Define if you have the `getrusage` function. ')
endif
# Generate apps_config.h
configure_file(
output: 'apps_config.h',
configuration: appconf_data
)
# Build json_parse executable
executable('json_parse', 'json_parse.c',
dependencies: [jsonc_dep],
install: false
)

View File

@@ -1,37 +1,125 @@
version: '{branch}.{build}' version: '{branch}.{build}'
os: Windows Server 2012 R2
image:
# b_toolset: v143
- Visual Studio 2022
# VS2015 also used for earlier VS builds
# aka os: Windows Server 2012 R2
- Visual Studio 2015
# aka os: Windows Server 2016
# b_toolset: v141
- Visual Studio 2017
# aka os: Windows Server 2019
# b_toolset: v142
- Visual Studio 2019
platform: x64 platform: x64
# There should be a better way to set-up a build matrix.
environment: environment:
matrix: matrix:
- b_toolset: Windows7.1SDK - b_toolset: Windows7.1SDK
b_config: Debug
- b_toolset: Windows7.1SDK
b_config: Release
- b_toolset: v120 - b_toolset: v120
b_config: Debug
- b_toolset: v120
b_config: Release
- b_toolset: v140 - b_toolset: v140
b_config: Debug
- b_toolset: v140 - b_toolset: v141
b_config: Release
- b_toolset: v142
- b_toolset: v143
configuration:
- Debug
- Release
build_script: build_script:
- cmake -T %b_toolset% -DCMAKE_BUILD_TYPE=%b_config% -DCMAKE_INSTALL_PREFIX=t_install . - cmake -T %b_toolset% -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_INSTALL_PREFIX=t_install .
- cmake --build . --target install - cmake --build . --target install
matrix:
exclude:
# Skip release builds for all except the newest image
- image: Visual Studio 2015
configuration: Release
# In the "old" image, new toolsets aren't available:
- image: Visual Studio 2015
b_toolset: v141
- image: Visual Studio 2015
b_toolset: v142
- image: Visual Studio 2015
b_toolset: v143
# ----
- image: Visual Studio 2017
configuration: Release
# In the "new" images, exclude all toolsets except the relevant
# one for that image:
- image: Visual Studio 2017
b_toolset: Windows7.1SDK
- image: Visual Studio 2017
b_toolset: v120
- image: Visual Studio 2017
b_toolset: v140
- image: Visual Studio 2017
b_toolset: v142
- image: Visual Studio 2017
b_toolset: v143
# ----
- image: Visual Studio 2019
configuration: Release
- image: Visual Studio 2019
b_toolset: Windows7.1SDK
- image: Visual Studio 2019
b_toolset: v120
- image: Visual Studio 2019
b_toolset: v140
- image: Visual Studio 2019
b_toolset: v141
- image: Visual Studio 2019
b_toolset: v143
# ----
- image: Visual Studio 2022
b_toolset: Windows7.1SDK
- image: Visual Studio 2022
b_toolset: v120
- image: Visual Studio 2022
b_toolset: v140
- image: Visual Studio 2022
b_toolset: v141
- image: Visual Studio 2022
b_toolset: v142
after_build: after_build:
- cd t_install - cd t_install
- 7z a ../json-c.win32.%b_toolset%.%b_config%.zip * - 7z a ../json-c.win32.%b_toolset%.%CONFIGURATION%.zip *
artifacts: artifacts:
- path: json-c.win32.%b_toolset%.%b_config%.zip - path: json-c.win32.%b_toolset%.%CONFIGURATION%.zip
name: json-c.win32.%b_toolset%.%b_config%.zip name: json-c.win32.%b_toolset%.%CONFIGURATION%.zip

View File

@@ -45,6 +45,8 @@ struct array_list *array_list_new2(array_list_free_fn *free_fn, int initial_size
{ {
struct array_list *arr; struct array_list *arr;
if (initial_size < 0 || (size_t)initial_size >= SIZE_T_MAX / sizeof(void *))
return NULL;
arr = (struct array_list *)malloc(sizeof(struct array_list)); arr = (struct array_list *)malloc(sizeof(struct array_list));
if (!arr) if (!arr)
return NULL; return NULL;
@@ -106,6 +108,8 @@ int array_list_shrink(struct array_list *arr, size_t empty_slots)
void *t; void *t;
size_t new_size; size_t new_size;
if (empty_slots >= SIZE_T_MAX / sizeof(void *) - arr->length)
return -1;
new_size = arr->length + empty_slots; new_size = arr->length + empty_slots;
if (new_size == arr->size) if (new_size == arr->size)
return 0; return 0;
@@ -121,6 +125,27 @@ int array_list_shrink(struct array_list *arr, size_t empty_slots)
return 0; return 0;
} }
int array_list_insert_idx(struct array_list *arr, size_t idx, void *data)
{
size_t move_amount;
if (idx >= arr->length)
return array_list_put_idx(arr, idx, data);
/* we're at full size, what size_t can support */
if (arr->length == SIZE_T_MAX)
return -1;
if (array_list_expand_internal(arr, arr->length + 1))
return -1;
move_amount = (arr->length - idx) * sizeof(void *);
memmove(arr->array + idx + 1, arr->array + idx, move_amount);
arr->array[idx] = data;
arr->length++;
return 0;
}
//static inline int _array_list_put_idx(struct array_list *arr, size_t idx, void *data) //static inline int _array_list_put_idx(struct array_list *arr, size_t idx, void *data)
int array_list_put_idx(struct array_list *arr, size_t idx, void *data) int array_list_put_idx(struct array_list *arr, size_t idx, void *data)
{ {

View File

@@ -15,8 +15,8 @@
* Although this is exposed by the json_object_get_array() method, * Although this is exposed by the json_object_get_array() method,
* it is not recommended for direct use. * it is not recommended for direct use.
*/ */
#ifndef _arraylist_h_ #ifndef _json_c_arraylist_h_
#define _arraylist_h_ #define _json_c_arraylist_h_
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -62,6 +62,8 @@ extern void array_list_free(struct array_list *al);
extern void *array_list_get_idx(struct array_list *al, size_t i); extern void *array_list_get_idx(struct array_list *al, size_t i);
extern int array_list_insert_idx(struct array_list *al, size_t i, void *data);
extern int array_list_put_idx(struct array_list *al, size_t i, void *data); extern int array_list_put_idx(struct array_list *al, size_t i, void *data);
extern int array_list_add(struct array_list *al, void *data); extern int array_list_add(struct array_list *al, void *data);

View File

@@ -70,7 +70,7 @@ Issues
Using heaptrack, and analyzing the histogram, only shows ~2.6MB Using heaptrack, and analyzing the histogram, only shows ~2.6MB
``` ```
heaptrack ./json_parse -n canada.json heaptrack ./json_parse -n canada.json
heaptrack --analyze heaptrack*gz -H histgram.out heaptrack --analyze heaptrack*gz -H histogram.out
awk ' { s=$1; count=$2; ru=(int((s+ 15) / 16)) * 16; wasted = ((ru-s)*count); print s, count, ru-s, wasted; total=total+wasted} END { print "Total: ", total }' histogram.out awk ' { s=$1; count=$2; ru=(int((s+ 15) / 16)) * 16; wasted = ((ru-s)*count); print s, count, ru-s, wasted; total=total+wasted} END { print "Total: ", total }' histogram.out
``` ```

View File

@@ -30,6 +30,7 @@ $0 [<configure_options>] [-- [<cmake options>]]
--enable-static build static libraries [default=yes] --enable-static build static libraries [default=yes]
--disable-Bsymbolic Avoid linking with -Bsymbolic-function --disable-Bsymbolic Avoid linking with -Bsymbolic-function
--disable-werror Avoid treating compiler warnings as fatal errors --disable-werror Avoid treating compiler warnings as fatal errors
--disable-extra-libs Avoid linking against extra libraries, such as libbsd
EOF EOF
exit exit
@@ -64,15 +65,24 @@ while [ $# -gt 0 ] ; do
--enable-shared) --enable-shared)
FLAGS+=(-DBUILD_SHARED_LIBS=ON) FLAGS+=(-DBUILD_SHARED_LIBS=ON)
;; ;;
--disable-shared)
FLAGS+=(-DBUILD_SHARED_LIBS=OFF)
;;
--enable-static) --enable-static)
FLAGS+=(-DBUILD_STATIC_LIBS=ON) FLAGS+=(-DBUILD_STATIC_LIBS=ON)
;; ;;
--disable-static)
FLAGS+=(-DBUILD_STATIC_LIBS=OFF)
;;
--disable-Bsymbolic) --disable-Bsymbolic)
FLAGS+=(-DDISABLE_BSYMBOLIC=ON) FLAGS+=(-DDISABLE_BSYMBOLIC=ON)
;; ;;
--disable-werror) --disable-werror)
FLAGS+=(-DDISABLE_WERROR=ON) FLAGS+=(-DDISABLE_WERROR=ON)
;; ;;
--disable-extra-libs)
FLAGS+=(-DDISABLE_EXTRA_LIBS=ON)
;;
--) --)
shift shift
break break

View File

@@ -56,6 +56,9 @@
/* Define to 1 if you have the <sys/param.h> header file. */ /* Define to 1 if you have the <sys/param.h> header file. */
#cmakedefine HAVE_SYS_PARAM_H @HAVE_SYS_PARAM_H@ #cmakedefine HAVE_SYS_PARAM_H @HAVE_SYS_PARAM_H@
/* Define to 1 if you have the <sys/random.h> header file. */
#cmakedefine HAVE_SYS_RANDOM_H
/* Define to 1 if you have the <sys/resource.h> header file. */ /* Define to 1 if you have the <sys/resource.h> header file. */
#cmakedefine HAVE_SYS_RESOURCE_H #cmakedefine HAVE_SYS_RESOURCE_H
@@ -71,6 +74,12 @@
/* Define to 1 if you have the <xlocale.h> header file. */ /* Define to 1 if you have the <xlocale.h> header file. */
#cmakedefine HAVE_XLOCALE_H #cmakedefine HAVE_XLOCALE_H
/* Define to 1 if you have the <bsd/stdlib.h> header file. */
#cmakedefine HAVE_BSD_STDLIB_H
/* Define to 1 if you have `arc4random' */
#cmakedefine HAVE_ARC4RANDOM
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
#cmakedefine HAVE_DOPRNT #cmakedefine HAVE_DOPRNT
@@ -128,6 +137,12 @@
/* Define to 1 if you have the `uselocale' function. */ /* Define to 1 if you have the `uselocale' function. */
#cmakedefine HAVE_USELOCALE #cmakedefine HAVE_USELOCALE
/* Define to 1 if you have the `duplocale' function. */
#cmakedefine HAVE_DUPLOCALE
/* Define to 1 if newlocale() needs freelocale() called on it's `base` argument */
#cmakedefine NEWLOCALE_NEEDS_FREELOCALE
/* Define to 1 if you have the `vasprintf' function. */ /* Define to 1 if you have the `vasprintf' function. */
#cmakedefine HAVE_VASPRINTF #cmakedefine HAVE_VASPRINTF
@@ -140,6 +155,9 @@
/* Define to 1 if you have the `vsyslog' function. */ /* Define to 1 if you have the `vsyslog' function. */
#cmakedefine HAVE_VSYSLOG @HAVE_VSYSLOG@ #cmakedefine HAVE_VSYSLOG @HAVE_VSYSLOG@
/* Define if you have the `getrandom' function. */
#cmakedefine HAVE_GETRANDOM
/* Define if you have the `getrusage' function. */ /* Define if you have the `getrusage' function. */
#cmakedefine HAVE_GETRUSAGE #cmakedefine HAVE_GETRUSAGE

View File

@@ -1,2 +1,5 @@
/* Define to 1 if you have the <inttypes.h> header file. */ /* Define to 1 if you have the <inttypes.h> header file. */
#cmakedefine JSON_C_HAVE_INTTYPES_H @JSON_C_HAVE_INTTYPES_H@ #cmakedefine JSON_C_HAVE_INTTYPES_H @JSON_C_HAVE_INTTYPES_H@
/* Define to 1 if you have the <stdint.h> header file. */
#cmakedefine JSON_C_HAVE_STDINT_H @JSON_C_HAVE_STDINT_H@

View File

@@ -14,8 +14,8 @@
* @file * @file
* @brief Do not use, json-c internal, may be changed or removed at any time. * @brief Do not use, json-c internal, may be changed or removed at any time.
*/ */
#ifndef _DEBUG_H_ #ifndef _JSON_C_DEBUG_H_
#define _DEBUG_H_ #define _JSON_C_DEBUG_H_
#include <stdlib.h> #include <stdlib.h>
@@ -24,7 +24,7 @@ extern "C" {
#endif #endif
#ifndef JSON_EXPORT #ifndef JSON_EXPORT
#if defined(_MSC_VER) #if defined(_MSC_VER) && defined(JSON_C_DLL)
#define JSON_EXPORT __declspec(dllexport) #define JSON_EXPORT __declspec(dllexport)
#else #else
#define JSON_EXPORT extern #define JSON_EXPORT extern

View File

@@ -20,7 +20,7 @@
# This tag specifies the encoding used for all characters in the config file # This tag specifies the encoding used for all characters in the config file
# that follow. The default is UTF-8 which is also the encoding used for all text # that follow. The default is UTF-8 which is also the encoding used for all text
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # built into libc) for the transcoding. See https://www.gnu.org/software/libiconv
# for the list of possible encodings. # for the list of possible encodings.
# The default value is: UTF-8. # The default value is: UTF-8.
@@ -152,7 +152,7 @@ FULL_PATH_NAMES = YES
# will be relative from the directory where doxygen is started. # will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES. # This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH = STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which # path mentioned in the documentation of a class, which tells the reader which
@@ -285,7 +285,7 @@ EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable # according to the Markdown format, which allows for more readable
# documentation. See http://daringfireball.net/projects/markdown/ for details. # documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can # The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues. # case of backward compatibilities issues.
@@ -318,7 +318,7 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # https://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead # will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present. # of private inheritance when no explicit protection keyword is present.
# The default value is: NO. # The default value is: NO.
@@ -427,7 +427,7 @@ EXTRACT_PACKAGE = NO
# included in the documentation. # included in the documentation.
# The default value is: NO. # The default value is: NO.
EXTRACT_STATIC = NO EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO # locally in source files will be included in the documentation. If set to NO
@@ -677,7 +677,7 @@ LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing # The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib # the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool # extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using # For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references. # search path. See also \cite for info how to create references.
@@ -758,7 +758,7 @@ INPUT = @CMAKE_SOURCE_DIR@ @CMAKE_BINARY_DIR@
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: http://www.gnu.org/software/libiconv) for the list of # documentation (see: https://www.gnu.org/software/libiconv) for the list of
# possible encodings. # possible encodings.
# The default value is: UTF-8. # The default value is: UTF-8.
@@ -961,7 +961,7 @@ SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will # If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in # point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system # source browser. The htags tool is part of GNU's global source tagging system
# (see http://www.gnu.org/software/global/global.html). You will need version # (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher. # 4.8.6 or higher.
# #
# To use it do the following: # To use it do the following:
@@ -989,7 +989,7 @@ USE_HTAGS = NO
VERBATIM_HEADERS = NO VERBATIM_HEADERS = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the # If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # clang parser (see: https://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template # cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type # rich C++ code for which doxygen's built-in parser lacks the necessary type
# information. # information.
@@ -1103,7 +1103,7 @@ HTML_STYLESHEET =
# cascading style sheets that are included after the standard style sheets # cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects. # created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the # This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefor more robust against future updates. # standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet files to the output directory. # Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra stylesheet files is of importance (e.g. the last # Note: The order of the extra stylesheet files is of importance (e.g. the last
# stylesheet in the list overrules the setting of the previous ones in the # stylesheet in the list overrules the setting of the previous ones in the
@@ -1125,7 +1125,7 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the stylesheet and background images according to # will adjust the colors in the stylesheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see # this color. Hue is specified as an angle on a colorwheel, see
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value # https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again. # purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220. # Minimum value: 0, maximum value: 359, default value: 220.
@@ -1183,12 +1183,13 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be # If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development # generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: http://developer.apple.com/tools/xcode/), introduced with # environment (see: https://developer.apple.com/tools/xcode/), introduced with
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in # Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in # that directory and running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # startup. See
# https://developer.apple.com/library/archive/featuredarticles/DoxygenXcode/
# for more information. # for more information.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1304,7 +1305,7 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace # Project output. For more information please see Qt Help Project / Namespace
# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project. # The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1312,8 +1313,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual # Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # Folders (see:
# folders). # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc. # The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1321,23 +1322,23 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom # filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # Filters (see:
# filters). # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom # custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # Filters (see:
# filters). # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS = QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see: # project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS =
@@ -1442,7 +1443,7 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering # https://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using prerendered bitmaps. Use this if you do not have LaTeX # instead of using prerendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When # installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path # enabled you may also need to install MathJax separately and configure the path
@@ -1454,7 +1455,7 @@ USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for # When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see: # the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/latest/output.html) for more details. # https://docs.mathjax.org/en/latest/output/) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best # Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG. # compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS. # The default value is: HTML-CSS.
@@ -1469,11 +1470,11 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing # Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of # MathJax. However, it is strongly recommended to install a local copy of
# MathJax from http://www.mathjax.org before deployment. # MathJax from https://www.mathjax.org before deployment.
# The default value is: http://cdn.mathjax.org/mathjax/latest. # The default value is: https://cdn.mathjax.org/mathjax/latest.
# This tag requires that the tag USE_MATHJAX is set to YES. # This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example # extension names that should be enabled during MathJax rendering. For example
@@ -1484,7 +1485,7 @@ MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site # of code that will be used on startup of the MathJax code. See the MathJax site
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # (see: https://docs.mathjax.org/en/latest/output/) for more details. For an
# example see the documentation. # example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES. # This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1531,7 +1532,7 @@ SERVER_BASED_SEARCH = NO
# #
# Doxygen ships with an example indexer ( doxyindexer) and search engine # Doxygen ships with an example indexer ( doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library # (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/). # Xapian (see: https://xapian.org/).
# #
# See the section "External Indexing and Searching" for details. # See the section "External Indexing and Searching" for details.
# The default value is: NO. # The default value is: NO.
@@ -1544,7 +1545,7 @@ EXTERNAL_SEARCH = NO
# #
# Doxygen ships with an example indexer ( doxyindexer) and search engine # Doxygen ships with an example indexer ( doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library # (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/). See the section "External Indexing and # Xapian (see: https://xapian.org/). See the section "External Indexing and
# Searching" for details. # Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES. # This tag requires that the tag SEARCHENGINE is set to YES.
@@ -1646,8 +1647,8 @@ EXTRA_PACKAGES =
# Note: Only use a user-defined header if you know what you are doing! The # Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title, # following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber, # $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, # $projectbrief, $projectlogo. Doxygen will replace $title with the empty string,
# for the replacement values of the other commands the user is refered to # for the replacement values of the other commands the user is referred to
# HTML_HEADER. # HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1717,7 +1718,7 @@ LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See # bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. # https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain. # The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1984,7 +1985,9 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator. # recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = THIS_FUNCTION_IS_DEPRECATED(f)=f PREDEFINED = \
_LH_INLINE=inline \
JSON_C_CONST_FUNCTION(func)=func
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The # tag can be used to specify a list of macro names that should be expanded. The
@@ -2071,7 +2074,7 @@ CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc # You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see: # command. Doxygen will then run the mscgen tool (see:
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the # https://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where # documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the # the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path. # default search path.
@@ -2093,7 +2096,7 @@ HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see: # available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is # Bell Labs. The other options in this section have no effect if this option is
# set to NO # set to NO
# The default value is: YES. # The default value is: YES.

View File

@@ -5,7 +5,7 @@
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # https://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,

27
fuzz/json_array_fuzzer.cc Normal file
View File

@@ -0,0 +1,27 @@
#include <fuzzer/FuzzedDataProvider.h>
#include "json.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fdp(data, size);
json_object *my_array = json_object_new_array();
for (int i = 0; i < 3; ++i) {
json_object *jso = json_tokener_parse(fdp.ConsumeRandomLengthString(10).c_str());
if (jso == NULL) {
continue;
}
json_object_array_add(my_array, jso);
}
json_object_array_insert_idx(my_array, fdp.ConsumeIntegralInRange<size_t>(0, 10),
json_object_new_int(fdp.ConsumeIntegral<int>()));
json_object_array_get_idx(my_array, fdp.ConsumeIntegralInRange<size_t>(0, 10));
json_object_array_put_idx(my_array, fdp.ConsumeIntegralInRange<size_t>(0, 10),
json_object_new_int(fdp.ConsumeIntegral<int>()));
json_object_array_del_idx(my_array, fdp.ConsumeIntegralInRange<size_t>(0, 10),
fdp.ConsumeIntegralInRange<size_t>(0, 10));
json_object_array_shrink(my_array, fdp.ConsumeIntegralInRange<size_t>(0, 10));
json_object_array_sort(my_array, [](const void *a, const void *b) { return 0; });
json_object_array_length(my_array);
json_object_put(my_array);
return 0;
}

View File

@@ -0,0 +1,21 @@
"{"
"}"
","
"["
"]"
","
":"
"e"
"e+"
"e-"
"E"
"E+"
"E-"
"\""
"null"
"1"
"1.234"
"3e4"
"NaN"
"Infinity"
"-Infinity"

View File

@@ -0,0 +1,44 @@
#include <fuzzer/FuzzedDataProvider.h>
#include "json.h"
#include "json_visit.h"
// Function to test json_c_visit
static int emit_object(json_object *jso, int flags, json_object *parent_jso, const char *jso_key,
size_t *jso_index, void *userarg) {
return JSON_C_VISIT_RETURN_CONTINUE;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fdp(data, size);
json_object *jso = json_tokener_parse(fdp.ConsumeRandomLengthString(20).c_str());
json_object_get_boolean(jso);
json_object_get_double(jso);
json_object_get_int(jso);
json_object_get_int64(jso);
json_object_get_uint64(jso);
json_object_get_string(jso);
json_object_get_string_len(jso);
json_object_get_object(jso);
json_object_get_array(jso);
json_object_get_type(jso);
json_c_visit(jso, 0, emit_object, NULL);
json_object_set_int(jso, fdp.ConsumeIntegral<int>());
json_object_set_int64(jso, fdp.ConsumeIntegral<int64_t>());
json_object_set_uint64(jso, fdp.ConsumeIntegral<uint64_t>());
json_object_set_double(jso, fdp.ConsumeFloatingPoint<double>());
json_object_set_string(jso, fdp.ConsumeRandomLengthString(10).c_str());
json_object_set_boolean(jso, fdp.ConsumeBool());
std::string str = fdp.ConsumeRandomLengthString(10);
json_object_set_string_len(jso, str.c_str(), str.size());
json_object *dst = NULL;
json_object_deep_copy(jso, &dst, json_c_shallow_copy_default);
json_object_put(dst);
json_object_put(jso);
return 0;
}

View File

@@ -0,0 +1,21 @@
"{"
"}"
","
"["
"]"
","
":"
"e"
"e+"
"e-"
"E"
"E+"
"E-"
"\""
"null"
"1"
"1.234"
"3e4"
"NaN"
"Infinity"
"-Infinity"

View File

@@ -0,0 +1,53 @@
#include <fuzzer/FuzzedDataProvider.h>
#include "json.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fdp(data, size);
struct json_tokener *tokener =
json_tokener_new_ex(fdp.ConsumeIntegralInRange<int>(1, JSON_TOKENER_DEFAULT_DEPTH));
int flags = 0;
if (fdp.ConsumeBool()) {
flags |= JSON_TOKENER_VALIDATE_UTF8;
}
if (fdp.ConsumeBool()) {
flags |= JSON_TOKENER_ALLOW_TRAILING_CHARS;
}
if (fdp.ConsumeBool()) {
flags |= JSON_TOKENER_STRICT;
}
json_tokener_set_flags(tokener, flags);
std::string path = fdp.ConsumeRandomLengthString(5);
std::string sub_json_str = fdp.ConsumeRandomLengthString(10);
bool use_format_string = fdp.ConsumeBool();
std::string json_str = fdp.ConsumeRemainingBytesAsString();
struct json_object *jo1 = json_tokener_parse_ex(tokener, json_str.c_str(), json_str.size());
struct json_object *sub_json = json_tokener_parse(sub_json_str.c_str());
if (sub_json == NULL) {
sub_json = json_object_new_object();
}
struct json_object *jo2 = NULL;
if (use_format_string) {
json_pointer_getf(jo1, &jo2, "%s", path.c_str());
if (json_pointer_setf(&jo1, sub_json, "%s", path.c_str()))
{
json_object_put(sub_json);
}
} else {
json_pointer_get(jo1, path.c_str(), &jo2);
if (json_pointer_set(&jo1, path.c_str(), sub_json))
{
json_object_put(sub_json);
}
}
// Clean up the main JSON object
json_object_put(jo1);
json_tokener_free(tokener);
return 0;
}

View File

@@ -0,0 +1,21 @@
"{"
"}"
","
"["
"]"
","
":"
"e"
"e+"
"e-"
"E"
"E+"
"E-"
"\""
"null"
"1"
"1.234"
"3e4"
"NaN"
"Infinity"
"-Infinity"

View File

@@ -8,6 +8,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
json_tokener *tok = json_tokener_new(); json_tokener *tok = json_tokener_new();
json_object *obj = json_tokener_parse_ex(tok, data1, size); json_object *obj = json_tokener_parse_ex(tok, data1, size);
if (json_object_is_type(obj, json_type_object)) {
json_object_object_foreach(obj, key, val) {
(void)json_object_get_type(val);
(void)json_object_get_string(val);
}
}
(void)json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED);
json_object_put(obj); json_object_put(obj);
json_tokener_free(tok); json_tokener_free(tok);
return 0; return 0;

View File

@@ -16,3 +16,6 @@
"1" "1"
"1.234" "1.234"
"3e4" "3e4"
"NaN"
"Infinity"
"-Infinity"

85
issues_closed_for_0.15.md Normal file
View File

@@ -0,0 +1,85 @@
This list was created with:
```
curl "https://api.github.com/search/issues?q=repo%3Ajson-c%2Fjson-c+closed%3A>2020-04-18+created%3A<2020-07-23&sort=created&order=asc&per_page=100&page=1" > issues1.out
jq -r '.items[] | "[" + .title + "](" + .url + ")" | tostring' issues?.out > issues.md
sed -e's,^\[ *\(.*\)\](https://api.github.com/.*/\([0-9].*\)),* [Issue #\2](https://github.com/json-c/json-c/issues/\2) - \1,' -i issues.md
#... manual editing ...
```
----
Issues and Pull Requests closed for the 0.15 release
(since commit 31ab57ca, the 0.14 branch point, 2020-04-19)
* [Issue #428](https://github.com/json-c/json-c/issues/428) - Added new_null() function
* [Issue #429](https://github.com/json-c/json-c/issues/429) - Conflict of interest between JSON_C_TO_STRING_SPACED and JSON_C_TO_STRING_PRETTY
* [Issue #451](https://github.com/json-c/json-c/issues/451) - Add option to disable HAVE___THREAD
* [Issue #471](https://github.com/json-c/json-c/issues/471) - create folders with mode 0755 when building
* [Issue #476](https://github.com/json-c/json-c/issues/476) - Add new function named json_object_new_string_noalloc
* [Issue #484](https://github.com/json-c/json-c/issues/484) - Add support for uint64
* [Issue #487](https://github.com/json-c/json-c/issues/487) - Any plans to make new release? (0.14)
* [Issue #493](https://github.com/json-c/json-c/issues/493) - Kdopen rename library
* [Issue #507](https://github.com/json-c/json-c/issues/507) - Double value -1.0 converts to integer in json_object_to_json_string()
* [Issue #508](https://github.com/json-c/json-c/issues/508) - Recommend enabling the `-fPIC` compiler flag by default
* [Issue #517](https://github.com/json-c/json-c/issues/517) - Lja mods
* [Issue #534](https://github.com/json-c/json-c/issues/534) - Both json-c and json-glib have json_object_get_type()
* [Issue #584](https://github.com/json-c/json-c/issues/584) - CMake: SOVERSION and the major library VERSION need to be in lockstep.
* [Issue #585](https://github.com/json-c/json-c/issues/585) - CMake: Do not install config.h, as it is not a public header file.
* [Issue #586](https://github.com/json-c/json-c/issues/586) - 10796 Segmentation fault
* [Issue #588](https://github.com/json-c/json-c/issues/588) - Broken RDRAND causes infinite looping
* [Issue #589](https://github.com/json-c/json-c/issues/589) - Detect broken RDRAND during initialization
* [Issue #590](https://github.com/json-c/json-c/issues/590) - Fix segmentation fault in CPUID check
* [Issue #591](https://github.com/json-c/json-c/issues/591) - Update README.md
* [Issue #592](https://github.com/json-c/json-c/issues/592) - Prevent out of boundary write on malicious input
* [Issue #593](https://github.com/json-c/json-c/issues/593) - Building both static and shared libraries
* [Issue #594](https://github.com/json-c/json-c/issues/594) - Some subsequent call of lh_get_hash not working
* [Issue #595](https://github.com/json-c/json-c/issues/595) - Support to build both static and shared libraries
* [Issue #596](https://github.com/json-c/json-c/issues/596) - QA Notice: Package triggers severe warnings
* [Issue #597](https://github.com/json-c/json-c/issues/597) - json_parse demo: fix and use usage() function
* [Issue #598](https://github.com/json-c/json-c/issues/598) - Turning off shared libs causes target duplication or build error
* [Issue #599](https://github.com/json-c/json-c/issues/599) - cannot add more than 11 objects. Is this a known issue?
* [Issue #600](https://github.com/json-c/json-c/issues/600) - Library name conflicts on Windows are back again
* [Issue #601](https://github.com/json-c/json-c/issues/601) - json_tokener_parse() in master sets errno=1 "Operation not permitted"
* [Issue #602](https://github.com/json-c/json-c/issues/602) - fix json_parse_uint64() internal error checking with errno
* [Issue #603](https://github.com/json-c/json-c/issues/603) - Backport of fixes from master branch.
* [Issue #604](https://github.com/json-c/json-c/issues/604) - commit f2e991a3419ee4078e8915e840b1a0d9003b349e breaks cross-compilation with mingw
* [Issue #605](https://github.com/json-c/json-c/issues/605) - Update to 0.15 release
* [Issue #606](https://github.com/json-c/json-c/issues/606) - Improved support for IBM operating systems
* [Issue #607](https://github.com/json-c/json-c/issues/607) - json-c-0.13.x: Fix CVE-2020-12762 - json-c through 0.14 has an integer overflow and out-of-bounds write ...
* [Issue #608](https://github.com/json-c/json-c/issues/608) - json-c-0.14: Fix CVE-2020-12762 - json-c through 0.14 has an integer overflow and out-of-bounds write ...
* [Issue #609](https://github.com/json-c/json-c/issues/609) - use unsigned types for sizes in lh_table and entries
* [Issue #610](https://github.com/json-c/json-c/issues/610) - let's not call lh_table_resize with INT_MAX
* [Issue #611](https://github.com/json-c/json-c/issues/611) - json-c-0.12.x: Fix CVE-2020-12762 - json-c through 0.14 has an integer overflow and out-of-bounds write ...
* [Issue #613](https://github.com/json-c/json-c/issues/613) - json-c-0.10: Fix CVE-2020-12762 - json-c through 0.14 has an integer overflow and out-of-bounds write ...
* [Issue #614](https://github.com/json-c/json-c/issues/614) - Prevent truncation on custom double formatters.
* [Issue #615](https://github.com/json-c/json-c/issues/615) - New release with security fix
* [Issue #616](https://github.com/json-c/json-c/issues/616) - Parsing fails if UTF-16 low surrogate pair is not in same chunk is the high pair
* [Issue #617](https://github.com/json-c/json-c/issues/617) - Add an option to disable the use of thread-local storage.
* [Issue #618](https://github.com/json-c/json-c/issues/618) - test_deep_copy: Fix assertion value.
* [Issue #619](https://github.com/json-c/json-c/issues/619) - CMake: Fix out-of-tree build for Doxygen documentation.
* [Issue #621](https://github.com/json-c/json-c/issues/621) - json-c and jansson libraries have symbol conflicts
* [Issue #622](https://github.com/json-c/json-c/issues/622) - doc: Move Doxyfile into doc subdir.
* [Issue #623](https://github.com/json-c/json-c/issues/623) - json_tokener_parse : Segmentation fault
* [Issue #626](https://github.com/json-c/json-c/issues/626) - Fixes for cmake 2.8.12 + link issue on AIX 6.1/cc 11.01
* [Issue #627](https://github.com/json-c/json-c/issues/627) - Compat fixes
* [Issue #628](https://github.com/json-c/json-c/issues/628) - get_cryptgenrandom_seed: compat with old windows + fallback
* [Issue #629](https://github.com/json-c/json-c/issues/629) - [0.12] Remove the Visual Studio project file
* [Issue #630](https://github.com/json-c/json-c/issues/630) - Linking with Windows MINGW not working
* [Issue #632](https://github.com/json-c/json-c/issues/632) - Json object split
* [Issue #633](https://github.com/json-c/json-c/issues/633) - fix issue 616: support the surrogate pair in split file.
* [Issue #634](https://github.com/json-c/json-c/issues/634) - Issue #508: `-fPIC` to link libjson-c.a with libs
* [Issue #635](https://github.com/json-c/json-c/issues/635) - expression has no effect warning in json_tokener.c
* [Issue #636](https://github.com/json-c/json-c/issues/636) - json_object_get_string free str memory
* [Issue #637](https://github.com/json-c/json-c/issues/637) - json_object_put() has 'double free or corruption (out) '
* [Issue #638](https://github.com/json-c/json-c/issues/638) - json-c/json_object.c:50:2: error: #error Unable to determine size of ssize_t
* [Issue #639](https://github.com/json-c/json-c/issues/639) - build: Add a symbol version to all exported symbols
* [Issue #640](https://github.com/json-c/json-c/issues/640) - Fix build issues with SSIZE_MAX on 64bit Linux
* [Issue #641](https://github.com/json-c/json-c/issues/641) - Formal verification of your test suite
* [Issue #642](https://github.com/json-c/json-c/issues/642) - Please provide more precise informations about when to call json_object_put
* [Issue #643](https://github.com/json-c/json-c/issues/643) - not able to compare with string
* [Issue #644](https://github.com/json-c/json-c/issues/644) - Why src->_userdata not checked before calling strdup?
* [Issue #645](https://github.com/json-c/json-c/issues/645) - Misuse of tolower() in json_tokener.c
* [Issue #646](https://github.com/json-c/json-c/issues/646) - Cast to unsigned char instead of int when calling tolower (Fixes #645)

107
issues_closed_for_0.16.md Normal file
View File

@@ -0,0 +1,107 @@
This list was created with:
```
PREV=2020-07-23
NOW=2022-04-13
curl "https://api.github.com/search/issues?q=repo%3Ajson-c%2Fjson-c+closed%3A>${PREV}+created%3A<${NOW}&sort=created&order=asc&per_page=100&page=1" > issues1.out
jq -r '.items[] | "[" + .title + "](" + .url + ")" | tostring' issues?.out > issues.md
sed -e's,^\[ *\(.*\)\](https://api.github.com/.*/\([0-9].*\)),* [Issue #\2](https://github.com/json-c/json-c/issues/\2) - \1,' -i issues.md
cat issues.md >> issues_closed_for_0.16.md
```
* [Issue #464](https://github.com/json-c/json-c/issues/464) - Speed up parsing and object creation
* [Issue #540](https://github.com/json-c/json-c/issues/540) - request: json_init_library
* [Issue #631](https://github.com/json-c/json-c/issues/631) - New 0.14 release requests
* [Issue #647](https://github.com/json-c/json-c/issues/647) - "cmake -DCMAKE_BUILD_TYPE=Release" fails with error: 'cint64' may be used uninitialized
* [Issue #648](https://github.com/json-c/json-c/issues/648) - Fix "may be used uninitialized" Release build failure
* [Issue #649](https://github.com/json-c/json-c/issues/649) - json-c tag 0.15 tarball contains a file doc/Doxyfile and generated doxygen files in doc/html
* [Issue #650](https://github.com/json-c/json-c/issues/650) - README: fix spelling errors
* [Issue #651](https://github.com/json-c/json-c/issues/651) - Getrandom
* [Issue #652](https://github.com/json-c/json-c/issues/652) - Waste memory
* [Issue #653](https://github.com/json-c/json-c/issues/653) - Make the documentation build reproducibly
* [Issue #654](https://github.com/json-c/json-c/issues/654) - A stack-buffer-overflow in json_parse.c:89:44
* [Issue #655](https://github.com/json-c/json-c/issues/655) - json_parse: Fix read past end of buffer
* [Issue #656](https://github.com/json-c/json-c/issues/656) - Fixed warnings
* [Issue #657](https://github.com/json-c/json-c/issues/657) - Use GRND_NONBLOCK with getrandom.
* [Issue #658](https://github.com/json-c/json-c/issues/658) - json_object_get_boolean() returns wrong result for objects and arrays
* [Issue #659](https://github.com/json-c/json-c/issues/659) - fix json_object_get_boolean() to behave like documented
* [Issue #660](https://github.com/json-c/json-c/issues/660) - Validate size arguments in arraylist functions.
* [Issue #661](https://github.com/json-c/json-c/issues/661) - Cleanup of some code parts
* [Issue #662](https://github.com/json-c/json-c/issues/662) - Prevent signed overflow in get_time_seed
* [Issue #663](https://github.com/json-c/json-c/issues/663) - Properly format errnos in _json_c_strerror
* [Issue #664](https://github.com/json-c/json-c/issues/664) - Limit strings at INT_MAX length
* [Issue #665](https://github.com/json-c/json-c/issues/665) - Handle more allocation failures in json_tokener* functions
* [Issue #666](https://github.com/json-c/json-c/issues/666) - test1 json_object_new_array_ext test is failing
* [Issue #667](https://github.com/json-c/json-c/issues/667) - Fixed test1 regression.
* [Issue #670](https://github.com/json-c/json-c/issues/670) - Created Stone-Paper-Scissor Game by C language
* [Issue #672](https://github.com/json-c/json-c/issues/672) - Calling exit() after failure to generate random seed
* [Issue #673](https://github.com/json-c/json-c/issues/673) - switchcasemenuproject
* [Issue #674](https://github.com/json-c/json-c/issues/674) - random_seed: on error, continue to next method
* [Issue #682](https://github.com/json-c/json-c/issues/682) - libjson-c-dev vs libjson-c3
* [Issue #683](https://github.com/json-c/json-c/issues/683) - [Question] Is it possible to clear a ptr of json_object?
* [Issue #684](https://github.com/json-c/json-c/issues/684) - json_tokener_parse_verbose failed with core dump
* [Issue #685](https://github.com/json-c/json-c/issues/685) - json_tokener_parse memory leak?
* [Issue #689](https://github.com/json-c/json-c/issues/689) - fix compilation with clang
* [Issue #690](https://github.com/json-c/json-c/issues/690) - "1," produces an object with int 1; "1" produces a null object
* [Issue #691](https://github.com/json-c/json-c/issues/691) - failed tests
* [Issue #692](https://github.com/json-c/json-c/issues/692) - patch to add arc4random
* [Issue #693](https://github.com/json-c/json-c/issues/693) - Optional parameter for packing as array
* [Issue #694](https://github.com/json-c/json-c/issues/694) - fix invalid unsigned arithmetic.
* [Issue #695](https://github.com/json-c/json-c/issues/695) - /tmp/json-c/random_seed.c:327:6: error
* [Issue #696](https://github.com/json-c/json-c/issues/696) - To avoid target exe file export JSON functions.
* [Issue #697](https://github.com/json-c/json-c/issues/697) - json_object_get_string() return value truncated when assigning it to a pointer type in Win32 App
* [Issue #698](https://github.com/json-c/json-c/issues/698) - Feature request: set allocator
* [Issue #699](https://github.com/json-c/json-c/issues/699) - Linking to libjson-c Issue
* [Issue #700](https://github.com/json-c/json-c/issues/700) - Fix unused variable for Win32 build in random_seed.c
* [Issue #701](https://github.com/json-c/json-c/issues/701) - [RFC] json_pointer: allow the feature to be disabled
* [Issue #703](https://github.com/json-c/json-c/issues/703) - Fix vasprintf fallback
* [Issue #706](https://github.com/json-c/json-c/issues/706) - Check __STDC_VERSION__ is defined before checking its value
* [Issue #707](https://github.com/json-c/json-c/issues/707) - How to build json-c-0.15 for arm arch
* [Issue #708](https://github.com/json-c/json-c/issues/708) - direct access to elements
* [Issue #709](https://github.com/json-c/json-c/issues/709) - Include guards not namespaced / build errors for debug.h with openNDS
* [Issue #710](https://github.com/json-c/json-c/issues/710) - 'file system sandbox blocked mmap()' error on iOS
* [Issue #711](https://github.com/json-c/json-c/issues/711) - creating a json object
* [Issue #712](https://github.com/json-c/json-c/issues/712) - building json-c using cmake for ESP32
* [Issue #713](https://github.com/json-c/json-c/issues/713) - When value converted to char* can not compare it with another value
* [Issue #714](https://github.com/json-c/json-c/issues/714) - Add AfterCaseLabel to .clang-format
* [Issue #716](https://github.com/json-c/json-c/issues/716) - Fixed cmake command
* [Issue #717](https://github.com/json-c/json-c/issues/717) - Cmake is able delete all files by "clean" target
* [Issue #718](https://github.com/json-c/json-c/issues/718) - CMake create uninstall target if unix generator is used
* [Issue #719](https://github.com/json-c/json-c/issues/719) - Parsing multiple JSON strings
* [Issue #722](https://github.com/json-c/json-c/issues/722) - Fix use-after-free in json_tokener_new_ex()
* [Issue #723](https://github.com/json-c/json-c/issues/723) - if set __stdcall (/Gz)
* [Issue #724](https://github.com/json-c/json-c/issues/724) - #723
* [Issue #725](https://github.com/json-c/json-c/issues/725) - json_object_from_file() execution segment error
* [Issue #726](https://github.com/json-c/json-c/issues/726) - fix cmake version for tests
* [Issue #727](https://github.com/json-c/json-c/issues/727) - Really use prefix JSON_C_OBJECT_ADD_
* [Issue #728](https://github.com/json-c/json-c/issues/728) - DRAFT PROPOSAL - Add option JSON_C_OBJECT_ADD_IF_NOT_NULL
* [Issue #729](https://github.com/json-c/json-c/issues/729) - * don't assume includedir
* [Issue #731](https://github.com/json-c/json-c/issues/731) - Json-c Error
* [Issue #732](https://github.com/json-c/json-c/issues/732) - Fix/static include dirs
* [Issue #734](https://github.com/json-c/json-c/issues/734) - Newer appveyor config for VS2022 etc...
* [Issue #735](https://github.com/json-c/json-c/issues/735) - Add policy_max to minimum required cmake version
* [Issue #736](https://github.com/json-c/json-c/issues/736) - json_object.c:308: json_object_put: Assertion `jso->_ref_count > 0' failed
* [Issue #737](https://github.com/json-c/json-c/issues/737) - Fix typo in README
* [Issue #738](https://github.com/json-c/json-c/issues/738) - General question - Is there an SLA for handling newly detected security issues?
* [Issue #739](https://github.com/json-c/json-c/issues/739) - json_escape_str(): avoid harmless unsigned integer overflow
* [Issue #741](https://github.com/json-c/json-c/issues/741) - json_type_to_name(): use correct printf() formatter
* [Issue #742](https://github.com/json-c/json-c/issues/742) - json_object_copy_serializer_data(): add assertion
* [Issue #743](https://github.com/json-c/json-c/issues/743) - Cmd adb root
* [Issue #744](https://github.com/json-c/json-c/issues/744) - Close file on error path.
* [Issue #745](https://github.com/json-c/json-c/issues/745) - vasprintf(): avoid out of memory accesses
* [Issue #746](https://github.com/json-c/json-c/issues/746) - Fix typos in code comments and ChangeLog
* [Issue #747](https://github.com/json-c/json-c/issues/747) - json_object_put: Assertion `jso->_ref_count > 0' failed
* [Issue #748](https://github.com/json-c/json-c/issues/748) - sprintbuf(): test for all vsnprintf error values
* [Issue #749](https://github.com/json-c/json-c/issues/749) - sprintbuf(): handle printbuf_memappend errors
* [Issue #750](https://github.com/json-c/json-c/issues/750) - printbuf_memset(): set gaps to zero
* [Issue #751](https://github.com/json-c/json-c/issues/751) - printbuf: do not allow invalid arguments
* [Issue #752](https://github.com/json-c/json-c/issues/752) - Fix typos
* [Issue #753](https://github.com/json-c/json-c/issues/753) - CTest failed in MSVC build
* [Issue #754](https://github.com/json-c/json-c/issues/754) - Minor improvements to documentation
* [Issue #755](https://github.com/json-c/json-c/issues/755) - Fix error messages
* [Issue #758](https://github.com/json-c/json-c/issues/758) - Preserve context if out of memory
* [Issue #760](https://github.com/json-c/json-c/issues/760) - Code style: removed unneeded double-quotes
* [Issue #761](https://github.com/json-c/json-c/issues/761) - Last commit merged to master breaks compilation
* [Issue #762](https://github.com/json-c/json-c/issues/762) - how to merge two jsons by json-c
* [Issue #763](https://github.com/json-c/json-c/issues/763) - Question: sort_fn arguments
* [Issue #764](https://github.com/json-c/json-c/issues/764) - Make test fail on test case test_util_file

88
issues_closed_for_0.17.md Normal file
View File

@@ -0,0 +1,88 @@
This list was created with:
```
PREV=2022-04-13
NOW=2023-08-12
curl "https://api.github.com/search/issues?q=repo%3Ajson-c%2Fjson-c+closed%3A>${PREV}+created%3A<${NOW}&sort=created&order=asc&per_page=100&page=1" > issues1.out
jq -r '.items[] | "[" + .title + "](" + .url + ")" | tostring' issues?.out > issues.md
sed -e's,^\[ *\(.*\)\](https://api.github.com/.*/\([0-9].*\)),* [Issue #\2](https://github.com/json-c/json-c/issues/\2) - \1,' -i issues.md
cat issues.md >> issues_closed_for_0.17.md
```
* [Issue #191](https://github.com/json-c/json-c/issues/191) - Override int64 to only display uint64 strings
* [Issue #537](https://github.com/json-c/json-c/issues/537) - Replace '\0' only when parsing key, not change data in value.
* [Issue #570](https://github.com/json-c/json-c/issues/570) - Figure out what needs to be done with Android.configure.mk
* [Issue #587](https://github.com/json-c/json-c/issues/587) - Store the hashValue to avoid repeating the hash calculation during the hash resize.
* [Issue #612](https://github.com/json-c/json-c/issues/612) - json-c-0.11: Fix CVE-2020-12762 - json-c through 0.14 has an integer overflow and out-of-bounds write ...
* [Issue #620](https://github.com/json-c/json-c/issues/620) - Introduce json_object_new_string_{ext,noalloc}().
* [Issue #624](https://github.com/json-c/json-c/issues/624) - json-c-0.14: Detect broken RDRAND during initialization.
* [Issue #625](https://github.com/json-c/json-c/issues/625) - json-c-0.13.x: Detect broken RDRAND during initialization.
* [Issue #668](https://github.com/json-c/json-c/issues/668) - Memory usage regression due to newlocal() on older FreeBSD releases
* [Issue #676](https://github.com/json-c/json-c/issues/676) - dereferencing type-punned pointer might break strict-aliasing rules [-Werror=strict-aliasing]
* [Issue #677](https://github.com/json-c/json-c/issues/677) - Naming conflict when using both json-c and jansson
* [Issue #679](https://github.com/json-c/json-c/issues/679) - Let json-c be used with obsolete compilers
* [Issue #681](https://github.com/json-c/json-c/issues/681) - json_tokener_parse_ex: `null` (4 bytes) only parses as valid JSON when passed with null terminator (5 bytes). Documentation issue?
* [Issue #686](https://github.com/json-c/json-c/issues/686) - Remove dependency on libM::getrandom
* [Issue #687](https://github.com/json-c/json-c/issues/687) - Does not build on Apple Silicon M1
* [Issue #688](https://github.com/json-c/json-c/issues/688) - json-c-0.15-nodoc.tar.gz build fails
* [Issue #702](https://github.com/json-c/json-c/issues/702) - json_patch: add first implementation only with patch application
* [Issue #704](https://github.com/json-c/json-c/issues/704) - add json_object_array_insert_idx() + test-cases + fix json_pointer doc-strings
* [Issue #705](https://github.com/json-c/json-c/issues/705) - segmentation fault on json-c parsing methods in cross compiled target
* [Issue #721](https://github.com/json-c/json-c/issues/721) - cmake test fails with building json-c with icc
* [Issue #730](https://github.com/json-c/json-c/issues/730) - Need a comparison with other JSON libraries in C
* [Issue #733](https://github.com/json-c/json-c/issues/733) - Official release? 1.0?
* [Issue #756](https://github.com/json-c/json-c/issues/756) - Question: Is there any way to build this with Gnu Make?
* [Issue #757](https://github.com/json-c/json-c/issues/757) - json_object_from_fd_ex: fail if file is too large
* [Issue #759](https://github.com/json-c/json-c/issues/759) - json_tokener_parse_ex: handle out of memory errors
* [Issue #766](https://github.com/json-c/json-c/issues/766) - Some people have trouble with undefined references to arc4random
* [Issue #767](https://github.com/json-c/json-c/issues/767) - How to create a character array using json-c
* [Issue #768](https://github.com/json-c/json-c/issues/768) - commits from May 30, 2022 killed my docker build process
* [Issue #769](https://github.com/json-c/json-c/issues/769) - Issue #768
* [Issue #770](https://github.com/json-c/json-c/issues/770) - json_parse.c:170:13: error: this statement may fall through
* [Issue #771](https://github.com/json-c/json-c/issues/771) - fix fallthough warning
* [Issue #772](https://github.com/json-c/json-c/issues/772) - add JSON_C_TO_STRING_COLOR option
* [Issue #773](https://github.com/json-c/json-c/issues/773) - problem with u_int64_t
* [Issue #774](https://github.com/json-c/json-c/issues/774) - The function add_compile_options was added to CMake version 2.8.12 and later but your minimum is 2.8 which will not work
* [Issue #775](https://github.com/json-c/json-c/issues/775) - list(TRANSFORM ...) is not available prior to CMake 3.12.
* [Issue #776](https://github.com/json-c/json-c/issues/776) - Fix typo
* [Issue #777](https://github.com/json-c/json-c/issues/777) - Don't try to change locale when libc only supports the C locale
* [Issue #778](https://github.com/json-c/json-c/issues/778) - Do not insert newlines when converting empty arrays to json string and JSON_C_TO_STRING_PRETTY is used
* [Issue #779](https://github.com/json-c/json-c/issues/779) - Fix compiling for Android
* [Issue #780](https://github.com/json-c/json-c/issues/780) - Memory Leak when setting empty strings when c_string.pdata is used
* [Issue #781](https://github.com/json-c/json-c/issues/781) - Fix memory leak with emtpy strings in json_object_set_string
* [Issue #782](https://github.com/json-c/json-c/issues/782) - Fix typos found by codespell
* [Issue #783](https://github.com/json-c/json-c/issues/783) - Fix build with clang-15+
* [Issue #784](https://github.com/json-c/json-c/issues/784) - get_time_seed(): silence warning emitted by Coverity Scan static analyzer
* [Issue #786](https://github.com/json-c/json-c/issues/786) - ghpages update was not published for json-c-0.16
* [Issue #787](https://github.com/json-c/json-c/issues/787) - -static linker flag result in building failed
* [Issue #788](https://github.com/json-c/json-c/issues/788) - Clear sensitive information.
* [Issue #789](https://github.com/json-c/json-c/issues/789) - Unnecessary struct declaration and unsafe function usage
* [Issue #790](https://github.com/json-c/json-c/issues/790) - Small update to README file
* [Issue #791](https://github.com/json-c/json-c/issues/791) - json_object_object_foreach not ISO-C compliant
* [Issue #792](https://github.com/json-c/json-c/issues/792) - ` json_object_get_int` does not set `EINVAL` on invalid string
* [Issue #794](https://github.com/json-c/json-c/issues/794) - replaced
* [Issue #796](https://github.com/json-c/json-c/issues/796) - Added Test for get int functions
* [Issue #797](https://github.com/json-c/json-c/issues/797) - make uninstall
* [Issue #798](https://github.com/json-c/json-c/issues/798) - API to deal with enums is missing
* [Issue #799](https://github.com/json-c/json-c/issues/799) - json_object_put: Assertion `jso->_ref_count > 0' failed.
* [Issue #800](https://github.com/json-c/json-c/issues/800) - String converted to scientific notation
* [Issue #801](https://github.com/json-c/json-c/issues/801) - #error You do not have strncasecmp on your system.
* [Issue #802](https://github.com/json-c/json-c/issues/802) - Problem: modern CMake warns about version 2.8
* [Issue #803](https://github.com/json-c/json-c/issues/803) - Problem: confusing error message in snprintf_compat.h
* [Issue #804](https://github.com/json-c/json-c/issues/804) - Problem: cmake 3.25.1 warns about CMP0042 not being set
* [Issue #806](https://github.com/json-c/json-c/issues/806) - The problem is libjson-c.dylib incompatible with OS version
* [Issue #807](https://github.com/json-c/json-c/issues/807) - json simple parse syntax
* [Issue #808](https://github.com/json-c/json-c/issues/808) - iOS Build using cmake fails due to 64 to 32bits conversion precision loss
* [Issue #809](https://github.com/json-c/json-c/issues/809) - Feature request json_object_new_uint()
* [Issue #810](https://github.com/json-c/json-c/issues/810) - docs: update to Internet Standard reference
* [Issue #811](https://github.com/json-c/json-c/issues/811) - dependence on execution character set
* [Issue #812](https://github.com/json-c/json-c/issues/812) - Duplicate symbol when compiling with clang-cl
* [Issue #813](https://github.com/json-c/json-c/issues/813) - Build apps only in project itself.
* [Issue #814](https://github.com/json-c/json-c/issues/814) - Code execution order
* [Issue #816](https://github.com/json-c/json-c/issues/816) - Hi I need to generate libjson-c.so.3 and libjson-c.so.3.0.1, please help with steps
* [Issue #818](https://github.com/json-c/json-c/issues/818) - error: a function declaration without a prototype is deprecated in all versions of C
* [Issue #819](https://github.com/json-c/json-c/issues/819) - build with intel 2023 fails on vasprintf
* [Issue #820](https://github.com/json-c/json-c/issues/820) - ISO C forbids in
* [Issue #821](https://github.com/json-c/json-c/issues/821) - Any release planing for 0.17?
* [Issue #822](https://github.com/json-c/json-c/issues/822) - Added option to disable app build
* [Issue #823](https://github.com/json-c/json-c/issues/823) - Symbol not found during linking stage of libjson-c.so

52
issues_closed_for_0.18.md Normal file
View File

@@ -0,0 +1,52 @@
This list was created with:
```
PREV=2023-08-12
NOW=2024-09-15
curl "https://api.github.com/search/issues?q=repo%3Ajson-c%2Fjson-c+closed%3A>${PREV}+created%3A<${NOW}&sort=created&order=asc&per_page=100&page=1" > issues1.out
jq -r '.items[] | "[" + .title + "](" + .url + ")" | tostring' issues?.out > issues.md
sed -e's,^\[ *\(.*\)\](https://api.github.com/.*/\([0-9].*\)),* [Issue #\2](https://github.com/json-c/json-c/issues/\2) - \1,' -i issues.md
cat issues.md >> issues_closed_for_0.18.md
```
* [Issue #826](https://github.com/json-c/json-c/issues/826) - CMake: remove needless policy setting
* [Issue #827](https://github.com/json-c/json-c/issues/827) - Resolve build issue in tokener_parse_ex_fuzzer.c
* [Issue #828](https://github.com/json-c/json-c/issues/828) - Put arc4random check under DISABLE_EXTRA_LIBS=OFF in CMakeLists.txt.
* [Issue #829](https://github.com/json-c/json-c/issues/829) - Cannot build with clang-cl
* [Issue #830](https://github.com/json-c/json-c/issues/830) - I am looking whether this CVE - CVE-2021-32292 is impacted by json-c version v0.13.1or not ?
* [Issue #831](https://github.com/json-c/json-c/issues/831) - rename WIN32 to _WIN32
* [Issue #833](https://github.com/json-c/json-c/issues/833) - memory leak issue in 0.13
* [Issue #834](https://github.com/json-c/json-c/issues/834) - fix error des of 'error_memory'
* [Issue #835](https://github.com/json-c/json-c/issues/835) - fix error des of 'error_memory'
* [Issue #836](https://github.com/json-c/json-c/issues/836) - Null pointer dereference in tokener_parse_ex_fuzzer.cc
* [Issue #837](https://github.com/json-c/json-c/issues/837) - Add type checking for parsed objects in tokener_parse_ex_fuzzer
* [Issue #838](https://github.com/json-c/json-c/issues/838) - Cannot link properly using cmake
* [Issue #839](https://github.com/json-c/json-c/issues/839) - json_pointer.c: initialize idx
* [Issue #840](https://github.com/json-c/json-c/issues/840) - Memory leak when enable HAVE_SETLOCALE and HAVE_USELOCALE
* [Issue #841](https://github.com/json-c/json-c/issues/841) - Random crash in json_tokener_parse
* [Issue #842](https://github.com/json-c/json-c/issues/842) - Assignment bug in json_pointer.c
* [Issue #843](https://github.com/json-c/json-c/issues/843) - _WIN32 should be used, not WIN32 in source code ifdefs.
* [Issue #844](https://github.com/json-c/json-c/issues/844) - Doxygen: mis-wording in `json_object_put`
* [Issue #845](https://github.com/json-c/json-c/issues/845) - [CMP0153](https://cmake.org/cmake/help/latest/policy/CMP0153.html)
* [Issue #846](https://github.com/json-c/json-c/issues/846) - CMP0153
* [Issue #848](https://github.com/json-c/json-c/issues/848) - json_object_from_file caused calloc SIGSEGV
* [Issue #849](https://github.com/json-c/json-c/issues/849) - random_seed.c: add a Coverity Scan suppression
* [Issue #850](https://github.com/json-c/json-c/issues/850) - Missing return description
* [Issue #851](https://github.com/json-c/json-c/issues/851) - Allow NULL in json_object_new_string() and json_object_new_string_len()
* [Issue #852](https://github.com/json-c/json-c/issues/852) - docs: micro typo in json_object_put doc
* [Issue #853](https://github.com/json-c/json-c/issues/853) - json_tokener_parse_verbose does not set the error indicator when it fails to allocate the tokener
* [Issue #854](https://github.com/json-c/json-c/issues/854) - json_tokener_parse_verbose sets the error indicator to a wrong value when there is a memory allocation failure
* [Issue #856](https://github.com/json-c/json-c/issues/856) - Using libjson-c.so or compiling from source seg faults on json_tokener_parse_ex in alpine docker container
* [Issue #857](https://github.com/json-c/json-c/issues/857) - json_tokener_parse_verbose sets the error indicator to a wrong value when there is a memory allocation failure
* [Issue #858](https://github.com/json-c/json-c/issues/858) - json_tokener.h: json_tokener_parse_verbose: fix typo
* [Issue #859](https://github.com/json-c/json-c/issues/859) - Develop
* [Issue #860](https://github.com/json-c/json-c/issues/860) - Develop
* [Issue #861](https://github.com/json-c/json-c/issues/861) - Handle yet another out-of-memory condition.
* [Issue #862](https://github.com/json-c/json-c/issues/862) - Missing `json-c-targets.cmake` during build cmake build step
* [Issue #863](https://github.com/json-c/json-c/issues/863) - build ios simulator error
* [Issue #864](https://github.com/json-c/json-c/issues/864) - Build pkg-config for msvc as well
* [Issue #865](https://github.com/json-c/json-c/issues/865) - Assertion usage
* [Issue #866](https://github.com/json-c/json-c/issues/866) - Added github actions
* [Issue #868](https://github.com/json-c/json-c/issues/868) - I meet compile error in harmony
* [Issue #869](https://github.com/json-c/json-c/issues/869) - At a high level how are jsons parses? Are they terminated by the new line?
* [Issue #870](https://github.com/json-c/json-c/issues/870) - Non-Compliant features should be optional (and disabled)

91
issues_closed_for_0.19.md Normal file
View File

@@ -0,0 +1,91 @@
This list was created with:
```
PREV=2024-09-15
NOW=2026-01-19
curl "https://api.github.com/search/issues?q=repo%3Ajson-c%2Fjson-c+closed%3A>${PREV}+created%3A<${NOW}&sort=created&order=asc&per_page=100&page=1" > issues1.out
jq -r '.items[] | "[" + .title + "](" + .url + ")" | tostring' issues?.out > issues.md
sed -e's,^\[ *\(.*\)\](https://api.github.com/.*/\([0-9].*\)),* [Issue #\2](https://github.com/json-c/json-c/issues/\2) - \1,' -i issues.md
cat issues.md >> issues_closed_for_0.19.md
```
* [Issue #826](https://github.com/json-c/json-c/issues/826) - CMake: remove needless policy setting
* [Issue #827](https://github.com/json-c/json-c/issues/827) - Resolve build issue in tokener_parse_ex_fuzzer.c
* [Issue #828](https://github.com/json-c/json-c/issues/828) - Put arc4random check under DISABLE_EXTRA_LIBS=OFF in CMakeLists.txt.
* [Issue #829](https://github.com/json-c/json-c/issues/829) - Cannot build with clang-cl
* [Issue #830](https://github.com/json-c/json-c/issues/830) - I am looking whether this CVE - CVE-2021-32292 is impacted by json-c version v0.13.1or not ?
* [Issue #831](https://github.com/json-c/json-c/issues/831) - rename WIN32 to _WIN32
* [Issue #833](https://github.com/json-c/json-c/issues/833) - memory leak issue in 0.13
* [Issue #834](https://github.com/json-c/json-c/issues/834) - fix error des of 'error_memory'
* [Issue #835](https://github.com/json-c/json-c/issues/835) - fix error des of 'error_memory'
* [Issue #836](https://github.com/json-c/json-c/issues/836) - Null pointer dereference in tokener_parse_ex_fuzzer.cc
* [Issue #837](https://github.com/json-c/json-c/issues/837) - Add type checking for parsed objects in tokener_parse_ex_fuzzer
* [Issue #838](https://github.com/json-c/json-c/issues/838) - Cannot link properly using cmake
* [Issue #839](https://github.com/json-c/json-c/issues/839) - json_pointer.c: initialize idx
* [Issue #840](https://github.com/json-c/json-c/issues/840) - Memory leak when enable HAVE_SETLOCALE and HAVE_USELOCALE
* [Issue #841](https://github.com/json-c/json-c/issues/841) - Random crash in json_tokener_parse
* [Issue #842](https://github.com/json-c/json-c/issues/842) - Assignment bug in json_pointer.c
* [Issue #843](https://github.com/json-c/json-c/issues/843) - _WIN32 should be used, not WIN32 in source code ifdefs.
* [Issue #844](https://github.com/json-c/json-c/issues/844) - Doxygen: mis-wording in `json_object_put`
* [Issue #845](https://github.com/json-c/json-c/issues/845) - [CMP0153](https://cmake.org/cmake/help/latest/policy/CMP0153.html)
* [Issue #846](https://github.com/json-c/json-c/issues/846) - CMP0153
* [Issue #848](https://github.com/json-c/json-c/issues/848) - json_object_from_file caused calloc SIGSEGV
* [Issue #849](https://github.com/json-c/json-c/issues/849) - random_seed.c: add a Coverity Scan suppression
* [Issue #850](https://github.com/json-c/json-c/issues/850) - Missing return description
* [Issue #851](https://github.com/json-c/json-c/issues/851) - Allow NULL in json_object_new_string() and json_object_new_string_len()
* [Issue #852](https://github.com/json-c/json-c/issues/852) - docs: micro typo in json_object_put doc
* [Issue #853](https://github.com/json-c/json-c/issues/853) - json_tokener_parse_verbose does not set the error indicator when it fails to allocate the tokener
* [Issue #854](https://github.com/json-c/json-c/issues/854) - json_tokener_parse_verbose sets the error indicator to a wrong value when there is a memory allocation failure
* [Issue #856](https://github.com/json-c/json-c/issues/856) - Using libjson-c.so or compiling from source seg faults on json_tokener_parse_ex in alpine docker container
* [Issue #857](https://github.com/json-c/json-c/issues/857) - json_tokener_parse_verbose sets the error indicator to a wrong value when there is a memory allocation failure
* [Issue #858](https://github.com/json-c/json-c/issues/858) - json_tokener.h: json_tokener_parse_verbose: fix typo
* [Issue #859](https://github.com/json-c/json-c/issues/859) - Develop
* [Issue #860](https://github.com/json-c/json-c/issues/860) - Develop
* [Issue #861](https://github.com/json-c/json-c/issues/861) - Handle yet another out-of-memory condition.
* [Issue #862](https://github.com/json-c/json-c/issues/862) - Missing `json-c-targets.cmake` during build cmake build step
* [Issue #863](https://github.com/json-c/json-c/issues/863) - build ios simulator error
* [Issue #864](https://github.com/json-c/json-c/issues/864) - Build pkg-config for msvc as well
* [Issue #865](https://github.com/json-c/json-c/issues/865) - Assertion usage
* [Issue #866](https://github.com/json-c/json-c/issues/866) - Added github actions
* [Issue #868](https://github.com/json-c/json-c/issues/868) - I meet compile error in harmony
* [Issue #869](https://github.com/json-c/json-c/issues/869) - At a high level how are jsons parses? Are they terminated by the new line?
* [Issue #870](https://github.com/json-c/json-c/issues/870) - Non-Compliant features should be optional (and disabled)
* [Issue #825](https://github.com/json-c/json-c/issues/825) - Add support for WASI
* [Issue #847](https://github.com/json-c/json-c/issues/847) - Add support for Commodore Amiga
* [Issue #867](https://github.com/json-c/json-c/issues/867) - Parsing of control characters
* [Issue #872](https://github.com/json-c/json-c/issues/872) - fix linkhash breaking -std=c89
* [Issue #873](https://github.com/json-c/json-c/issues/873) - Correct year in changelog for 0.18 release
* [Issue #874](https://github.com/json-c/json-c/issues/874) - comparison with infinity
* [Issue #875](https://github.com/json-c/json-c/issues/875) - Unicode characters seem to be rejected with JSON_TOKENER_STRICT
* [Issue #876](https://github.com/json-c/json-c/issues/876) - json_object_from_fd(_ex) doesn't work with a socket descriptor
* [Issue #877](https://github.com/json-c/json-c/issues/877) - Fix underline bug in README
* [Issue #878](https://github.com/json-c/json-c/issues/878) - Fix underline bug in README
* [Issue #879](https://github.com/json-c/json-c/issues/879) - Handle NULL gracefully in json_tokener_free
* [Issue #880](https://github.com/json-c/json-c/issues/880) - Undefined behavior when coercing NaN value to int
* [Issue #881](https://github.com/json-c/json-c/issues/881) - Tokener with depth=0 causes segmentation fault
* [Issue #882](https://github.com/json-c/json-c/issues/882) - Explicitly handle NaN values when converting to integer
* [Issue #883](https://github.com/json-c/json-c/issues/883) - Add fuzz tests for json_object/point/array apis
* [Issue #884](https://github.com/json-c/json-c/issues/884) - fix macro WIN32_LEAN_AND_MEAN redefined
* [Issue #886](https://github.com/json-c/json-c/issues/886) - Linking to json-c using cmake's find_package error
* [Issue #887](https://github.com/json-c/json-c/issues/887) - Can json-c accept invalid JSON strings?
* [Issue #888](https://github.com/json-c/json-c/issues/888) - Allow build with cmake 4.0.0
* [Issue #891](https://github.com/json-c/json-c/issues/891) - Cmake cant build only static library
* [Issue #892](https://github.com/json-c/json-c/issues/892) - JSON_TOKENER_STRICT rejects U+00BA : MASCULINE ORDINAL INDICATOR
* [Issue #893](https://github.com/json-c/json-c/issues/893) - Fix bug involving supplemental code points that look like high surrogates
* [Issue #894](https://github.com/json-c/json-c/issues/894) - json_object_to_json_string_* trims string to 32767 characters
* [Issue #895](https://github.com/json-c/json-c/issues/895) - CMake: sync minimum version
* [Issue #897](https://github.com/json-c/json-c/issues/897) - error: calloc sizes specified with sizeof in the earlier argument and not in the later argument
* [Issue #898](https://github.com/json-c/json-c/issues/898) - bug: Fixing Cmake build when using clang-cl
* [Issue #899](https://github.com/json-c/json-c/issues/899) - Meson support
* [Issue #900](https://github.com/json-c/json-c/issues/900) - meson cleanups
* [Issue #901](https://github.com/json-c/json-c/issues/901) - miscellanious gitignore updates
* [Issue #904](https://github.com/json-c/json-c/issues/904) - Include getopt.h header file if the platform is not AIX.
* [Issue #905](https://github.com/json-c/json-c/issues/905) - Fix typo
* [Issue #906](https://github.com/json-c/json-c/issues/906) - json-c: fix calloc argument order to avoid GCC14 -Wcalloc-transposed-args in arraylist.c and json_object.c
* [Issue #907](https://github.com/json-c/json-c/issues/907) - tests/CMakeLists: Allow build with CMake 4
* [Issue #908](https://github.com/json-c/json-c/issues/908) - [json-c-0.18] tests/CMakeLists: Allow build with CMake 4
* [Issue #909](https://github.com/json-c/json-c/issues/909) - compilation error with MSVC in packages that use GNU Autoconf
* [Issue #910](https://github.com/json-c/json-c/issues/910) - Support MSVC in packages that use GNU Autoconf.
* [Issue #911](https://github.com/json-c/json-c/issues/911) - Building libjson-c DLL for Windows under Cygwin: why it's named cygjson-c?
* [Issue #912](https://github.com/json-c/json-c/issues/912) - libjson-c.dll is missing VERSIONINFO
* [Issue #913](https://github.com/json-c/json-c/issues/913) - Add VERSIONINFO to libjson-c.dll

View File

@@ -41,6 +41,8 @@ JSONC_PRIVATE {
printbuf_new; printbuf_new;
printbuf_reset; printbuf_reset;
sprintbuf; sprintbuf;
# Used by tests:
_json_c_strerror;
}; };
JSONC_0.14 { JSONC_0.14 {
@@ -163,3 +165,25 @@ JSONC_0.15 {
json_object_array_shrink; json_object_array_shrink;
json_object_new_array_ext; json_object_new_array_ext;
} JSONC_0.14; } JSONC_0.14;
JSONC_0.16 {
# No new symbols in 0.16
} JSONC_0.15;
JSONC_0.17 {
global:
json_object_array_insert_idx;
json_patch_apply;
# array_list_insert_idx is intentionally not exported
} JSONC_0.16;
JSONC_0.18 {
# global:
} JSONC_0.17;
JSONC_0.19 {
global:
json_pointer_set_with_limit_index;
json_pointer_set_with_cb;
json_object_array_put_with_idx_limit_cb;
} JSONC_0.18;

View File

@@ -26,7 +26,8 @@ extern "C" {
#include "json_c_version.h" #include "json_c_version.h"
#include "json_object.h" #include "json_object.h"
#include "json_object_iterator.h" #include "json_object_iterator.h"
#include "json_pointer.h" @JSON_H_JSON_PATCH@
@JSON_H_JSON_POINTER@
#include "json_tokener.h" #include "json_tokener.h"
#include "json_util.h" #include "json_util.h"
#include "linkhash.h" #include "linkhash.h"

View File

@@ -17,14 +17,14 @@ extern "C" {
#endif #endif
#define JSON_C_MAJOR_VERSION 0 #define JSON_C_MAJOR_VERSION 0
#define JSON_C_MINOR_VERSION 14 #define JSON_C_MINOR_VERSION 18
#define JSON_C_MICRO_VERSION 99 #define JSON_C_MICRO_VERSION 99
#define JSON_C_VERSION_NUM \ #define JSON_C_VERSION_NUM \
((JSON_C_MAJOR_VERSION << 16) | (JSON_C_MINOR_VERSION << 8) | JSON_C_MICRO_VERSION) ((JSON_C_MAJOR_VERSION << 16) | (JSON_C_MINOR_VERSION << 8) | JSON_C_MICRO_VERSION)
#define JSON_C_VERSION "0.14.99" #define JSON_C_VERSION "0.18.99"
#ifndef JSON_EXPORT #ifndef JSON_EXPORT
#if defined(_MSC_VER) #if defined(_MSC_VER) && defined(JSON_C_DLL)
#define JSON_EXPORT __declspec(dllexport) #define JSON_EXPORT __declspec(dllexport)
#else #else
#define JSON_EXPORT extern #define JSON_EXPORT extern

View File

@@ -13,7 +13,15 @@
#include <inttypes.h> #include <inttypes.h>
#else #else
#ifdef JSON_C_HAVE_STDINT_H
#include <stdint.h> #include <stdint.h>
#else
/* Really only valid for old MS compilers, VS2008 and earlier: */
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#endif
#define PRId64 "I64d" #define PRId64 "I64d"
#define SCNd64 "I64d" #define SCNd64 "I64d"
@@ -21,4 +29,9 @@
#endif #endif
#if defined(_MSC_VER) && !defined(ssize_t)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
#endif #endif

View File

@@ -13,7 +13,6 @@
#include "strerror_override.h" #include "strerror_override.h"
#include <assert.h> #include <assert.h>
#include <ctype.h>
#ifdef HAVE_LIMITS_H #ifdef HAVE_LIMITS_H
#include <limits.h> #include <limits.h>
#endif #endif
@@ -35,8 +34,11 @@
#include "snprintf_compat.h" #include "snprintf_compat.h"
#include "strdup_compat.h" #include "strdup_compat.h"
/* Avoid ctype.h and locale overhead */
#define is_plain_digit(c) ((c) >= '0' && (c) <= '9')
#if SIZEOF_LONG_LONG != SIZEOF_INT64_T #if SIZEOF_LONG_LONG != SIZEOF_INT64_T
#error "The long long type isn't 64-bits" #error The long long type is not 64-bits
#endif #endif
#ifndef SSIZE_T_MAX #ifndef SSIZE_T_MAX
@@ -51,9 +53,7 @@
#endif #endif
#endif #endif
// Don't define this. It's not thread-safe. const char *json_number_chars = "0123456789.+-eE"; /* Unused, but part of public API, drop for 1.0 */
/* #define REFCOUNT_DEBUG 1 */
const char *json_hex_chars = "0123456789abcdefABCDEF"; const char *json_hex_chars = "0123456789abcdefABCDEF";
static void json_object_generic_delete(struct json_object *jso); static void json_object_generic_delete(struct json_object *jso);
@@ -65,6 +65,12 @@ static void json_object_generic_delete(struct json_object *jso);
#define inline #define inline
#endif #endif
/* define colors */
#define ANSI_COLOR_RESET "\033[0m"
#define ANSI_COLOR_FG_GREEN "\033[0;32m"
#define ANSI_COLOR_FG_BLUE "\033[0;34m"
#define ANSI_COLOR_FG_MAGENTA "\033[0;35m"
/* /*
* Helper functions to more safely cast to a particular type of json_object * Helper functions to more safely cast to a particular type of json_object
*/ */
@@ -159,41 +165,6 @@ static json_object_to_json_string_fn _json_object_userdata_to_json_string;
* */ * */
JSON_NORETURN static void json_abort(const char *message); JSON_NORETURN static void json_abort(const char *message);
/* ref count debugging */
#ifdef REFCOUNT_DEBUG
static struct lh_table *json_object_table;
static void json_object_init(void) __attribute__((constructor));
static void json_object_init(void)
{
MC_DEBUG("json_object_init: creating object table\n");
json_object_table = lh_kptr_table_new(128, NULL);
}
static void json_object_fini(void) __attribute__((destructor));
static void json_object_fini(void)
{
struct lh_entry *ent;
if (MC_GET_DEBUG())
{
if (json_object_table->count)
{
MC_DEBUG("json_object_fini: %d referenced objects at exit\n",
json_object_table->count);
lh_foreach(json_object_table, ent)
{
struct json_object *obj = (struct json_object *)lh_entry_v(ent);
MC_DEBUG("\t%s:%p\n", json_type_to_name(obj->o_type), obj);
}
}
}
MC_DEBUG("json_object_fini: freeing object table\n");
lh_table_free(json_object_table);
}
#endif /* REFCOUNT_DEBUG */
/* helper for accessing the optimized string data component in json_object /* helper for accessing the optimized string data component in json_object
*/ */
static inline char *get_string_component_mutable(struct json_object *jso) static inline char *get_string_component_mutable(struct json_object *jso)
@@ -214,10 +185,11 @@ static inline const char *get_string_component(const struct json_object *jso)
static int json_escape_str(struct printbuf *pb, const char *str, size_t len, int flags) static int json_escape_str(struct printbuf *pb, const char *str, size_t len, int flags)
{ {
int pos = 0, start_offset = 0; size_t pos = 0, start_offset = 0;
unsigned char c; unsigned char c;
while (len--) while (len)
{ {
--len;
c = str[pos]; c = str[pos];
switch (c) switch (c)
{ {
@@ -235,7 +207,7 @@ static int json_escape_str(struct printbuf *pb, const char *str, size_t len, int
break; break;
} }
if (pos - start_offset > 0) if (pos > start_offset)
printbuf_memappend(pb, str + start_offset, pos - start_offset); printbuf_memappend(pb, str + start_offset, pos - start_offset);
if (c == '\b') if (c == '\b')
@@ -261,7 +233,7 @@ static int json_escape_str(struct printbuf *pb, const char *str, size_t len, int
if (c < ' ') if (c < ' ')
{ {
char sbuf[7]; char sbuf[7];
if (pos - start_offset > 0) if (pos > start_offset)
printbuf_memappend(pb, str + start_offset, printbuf_memappend(pb, str + start_offset,
pos - start_offset); pos - start_offset);
snprintf(sbuf, sizeof(sbuf), "\\u00%c%c", json_hex_chars[c >> 4], snprintf(sbuf, sizeof(sbuf), "\\u00%c%c", json_hex_chars[c >> 4],
@@ -273,7 +245,7 @@ static int json_escape_str(struct printbuf *pb, const char *str, size_t len, int
pos++; pos++;
} }
} }
if (pos - start_offset > 0) if (pos > start_offset)
printbuf_memappend(pb, str + start_offset, pos - start_offset); printbuf_memappend(pb, str + start_offset, pos - start_offset);
return 0; return 0;
} }
@@ -337,10 +309,6 @@ int json_object_put(struct json_object *jso)
static void json_object_generic_delete(struct json_object *jso) static void json_object_generic_delete(struct json_object *jso)
{ {
#ifdef REFCOUNT_DEBUG
MC_DEBUG("json_object_delete_%s: %p\n", json_type_to_name(jso->o_type), jso);
lh_table_delete(json_object_table, jso);
#endif /* REFCOUNT_DEBUG */
printbuf_free(jso->_pb); printbuf_free(jso->_pb);
free(jso); free(jso);
} }
@@ -362,10 +330,6 @@ static inline struct json_object *json_object_new(enum json_type o_type, size_t
jso->_userdata = NULL; jso->_userdata = NULL;
//jso->... // Type-specific fields must be set by caller //jso->... // Type-specific fields must be set by caller
#ifdef REFCOUNT_DEBUG
lh_table_insert(json_object_table, jso, jso);
MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso);
#endif /* REFCOUNT_DEBUG */
return jso; return jso;
} }
@@ -503,35 +467,45 @@ static int json_object_object_to_json_string(struct json_object *jso, struct pri
struct json_object_iter iter; struct json_object_iter iter;
printbuf_strappend(pb, "{" /*}*/); printbuf_strappend(pb, "{" /*}*/);
if (flags & JSON_C_TO_STRING_PRETTY)
printbuf_strappend(pb, "\n");
json_object_object_foreachC(jso, iter) json_object_object_foreachC(jso, iter)
{ {
if (had_children) if (had_children)
{ {
printbuf_strappend(pb, ","); printbuf_strappend(pb, ",");
if (flags & JSON_C_TO_STRING_PRETTY)
printbuf_strappend(pb, "\n");
} }
if (flags & JSON_C_TO_STRING_PRETTY)
printbuf_strappend(pb, "\n");
had_children = 1; had_children = 1;
if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY))
printbuf_strappend(pb, " "); printbuf_strappend(pb, " ");
indent(pb, level + 1, flags); indent(pb, level + 1, flags);
if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_FG_BLUE);
printbuf_strappend(pb, "\""); printbuf_strappend(pb, "\"");
json_escape_str(pb, iter.key, strlen(iter.key), flags); json_escape_str(pb, iter.key, strlen(iter.key), flags);
printbuf_strappend(pb, "\"");
if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_RESET);
if (flags & JSON_C_TO_STRING_SPACED) if (flags & JSON_C_TO_STRING_SPACED)
printbuf_strappend(pb, "\": "); printbuf_strappend(pb, ": ");
else else
printbuf_strappend(pb, "\":"); printbuf_strappend(pb, ":");
if (iter.val == NULL)
if (iter.val == NULL) {
if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_FG_MAGENTA);
printbuf_strappend(pb, "null"); printbuf_strappend(pb, "null");
else if (iter.val->_to_json_string(iter.val, pb, level + 1, flags) < 0) if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_RESET);
} else if (iter.val->_to_json_string(iter.val, pb, level + 1, flags) < 0)
return -1; return -1;
} }
if (flags & JSON_C_TO_STRING_PRETTY) if ((flags & JSON_C_TO_STRING_PRETTY) && had_children)
{ {
if (had_children) printbuf_strappend(pb, "\n");
printbuf_strappend(pb, "\n");
indent(pb, level, flags); indent(pb, level, flags);
} }
if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY))
@@ -542,7 +516,7 @@ static int json_object_object_to_json_string(struct json_object *jso, struct pri
static void json_object_lh_entry_free(struct lh_entry *ent) static void json_object_lh_entry_free(struct lh_entry *ent)
{ {
if (!ent->k_is_constant) if (!lh_entry_k_is_constant(ent))
free(lh_entry_k(ent)); free(lh_entry_k(ent));
json_object_put((struct json_object *)lh_entry_v(ent)); json_object_put((struct json_object *)lh_entry_v(ent));
} }
@@ -605,7 +579,7 @@ int json_object_object_add_ex(struct json_object *jso, const char *const key,
if (!existing_entry) if (!existing_entry)
{ {
const void *const k = const void *const k =
(opts & JSON_C_OBJECT_KEY_IS_CONSTANT) ? (const void *)key : strdup(key); (opts & JSON_C_OBJECT_ADD_CONSTANT_KEY) ? (const void *)key : strdup(key);
if (k == NULL) if (k == NULL)
return -1; return -1;
return lh_table_insert_w_hash(JC_OBJECT(jso)->c_object, k, val, hash, opts); return lh_table_insert_w_hash(JC_OBJECT(jso)->c_object, k, val, hash, opts);
@@ -613,7 +587,7 @@ int json_object_object_add_ex(struct json_object *jso, const char *const key,
existing_value = (json_object *)lh_entry_v(existing_entry); existing_value = (json_object *)lh_entry_v(existing_entry);
if (existing_value) if (existing_value)
json_object_put(existing_value); json_object_put(existing_value);
existing_entry->v = val; lh_entry_set_val(existing_entry, val);
return 0; return 0;
} }
@@ -672,9 +646,18 @@ void json_object_object_del(struct json_object *jso, const char *key)
static int json_object_boolean_to_json_string(struct json_object *jso, struct printbuf *pb, static int json_object_boolean_to_json_string(struct json_object *jso, struct printbuf *pb,
int level, int flags) int level, int flags)
{ {
int ret;
if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_FG_MAGENTA);
if (JC_BOOL(jso)->c_boolean) if (JC_BOOL(jso)->c_boolean)
return printbuf_strappend(pb, "true"); ret = printbuf_strappend(pb, "true");
return printbuf_strappend(pb, "false"); else
ret = printbuf_strappend(pb, "false");
if (ret > -1 && flags & JSON_C_TO_STRING_COLOR)
return printbuf_strappend(pb, ANSI_COLOR_RESET);
return ret;
} }
struct json_object *json_object_new_boolean(json_bool b) struct json_object *json_object_new_boolean(json_bool b)
@@ -735,9 +718,10 @@ struct json_object *json_object_new_int(int32_t i)
int32_t json_object_get_int(const struct json_object *jso) int32_t json_object_get_int(const struct json_object *jso)
{ {
int64_t cint64=0; int64_t cint64 = 0;
double cdouble; double cdouble;
enum json_type o_type; enum json_type o_type;
errno = 0;
if (!jso) if (!jso)
return 0; return 0;
@@ -773,17 +757,34 @@ int32_t json_object_get_int(const struct json_object *jso)
{ {
case json_type_int: case json_type_int:
/* Make sure we return the correct values for out of range numbers. */ /* Make sure we return the correct values for out of range numbers. */
if (cint64 <= INT32_MIN) if (cint64 < INT32_MIN)
{
errno = ERANGE;
return INT32_MIN; return INT32_MIN;
if (cint64 >= INT32_MAX) }
if (cint64 > INT32_MAX)
{
errno = ERANGE;
return INT32_MAX; return INT32_MAX;
}
return (int32_t)cint64; return (int32_t)cint64;
case json_type_double: case json_type_double:
cdouble = JC_DOUBLE_C(jso)->c_double; cdouble = JC_DOUBLE_C(jso)->c_double;
if (cdouble <= INT32_MIN) if (cdouble < INT32_MIN)
{
errno = ERANGE;
return INT32_MIN; return INT32_MIN;
if (cdouble >= INT32_MAX) }
if (cdouble > INT32_MAX)
{
errno = ERANGE;
return INT32_MAX; return INT32_MAX;
}
if (isnan(cdouble))
{
errno = EINVAL;
return INT32_MIN;
}
return (int32_t)cdouble; return (int32_t)cdouble;
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
default: return 0; default: return 0;
@@ -818,6 +819,7 @@ struct json_object *json_object_new_uint64(uint64_t i)
int64_t json_object_get_int64(const struct json_object *jso) int64_t json_object_get_int64(const struct json_object *jso)
{ {
int64_t cint; int64_t cint;
errno = 0;
if (!jso) if (!jso)
return 0; return 0;
@@ -830,8 +832,11 @@ int64_t json_object_get_int64(const struct json_object *jso)
{ {
case json_object_int_type_int64: return jsoint->cint.c_int64; case json_object_int_type_int64: return jsoint->cint.c_int64;
case json_object_int_type_uint64: case json_object_int_type_uint64:
if (jsoint->cint.c_uint64 >= INT64_MAX) if (jsoint->cint.c_uint64 > INT64_MAX)
{
errno = ERANGE;
return INT64_MAX; return INT64_MAX;
}
return (int64_t)jsoint->cint.c_uint64; return (int64_t)jsoint->cint.c_uint64;
default: json_abort("invalid cint_type"); default: json_abort("invalid cint_type");
} }
@@ -839,10 +844,21 @@ int64_t json_object_get_int64(const struct json_object *jso)
case json_type_double: case json_type_double:
// INT64_MAX can't be exactly represented as a double // INT64_MAX can't be exactly represented as a double
// so cast to tell the compiler it's ok to round up. // so cast to tell the compiler it's ok to round up.
if (JC_DOUBLE_C(jso)->c_double >= (double)INT64_MAX) if (JC_DOUBLE_C(jso)->c_double > (double)INT64_MAX)
{
errno = ERANGE;
return INT64_MAX; return INT64_MAX;
if (JC_DOUBLE_C(jso)->c_double <= INT64_MIN) }
if (JC_DOUBLE_C(jso)->c_double < (double)INT64_MIN)
{
errno = ERANGE;
return INT64_MIN; return INT64_MIN;
}
if (isnan(JC_DOUBLE_C(jso)->c_double))
{
errno = EINVAL;
return INT64_MIN;
}
return (int64_t)JC_DOUBLE_C(jso)->c_double; return (int64_t)JC_DOUBLE_C(jso)->c_double;
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
case json_type_string: case json_type_string:
@@ -856,6 +872,7 @@ int64_t json_object_get_int64(const struct json_object *jso)
uint64_t json_object_get_uint64(const struct json_object *jso) uint64_t json_object_get_uint64(const struct json_object *jso)
{ {
uint64_t cuint; uint64_t cuint;
errno = 0;
if (!jso) if (!jso)
return 0; return 0;
@@ -868,7 +885,10 @@ uint64_t json_object_get_uint64(const struct json_object *jso)
{ {
case json_object_int_type_int64: case json_object_int_type_int64:
if (jsoint->cint.c_int64 < 0) if (jsoint->cint.c_int64 < 0)
{
errno = ERANGE;
return 0; return 0;
}
return (uint64_t)jsoint->cint.c_int64; return (uint64_t)jsoint->cint.c_int64;
case json_object_int_type_uint64: return jsoint->cint.c_uint64; case json_object_int_type_uint64: return jsoint->cint.c_uint64;
default: json_abort("invalid cint_type"); default: json_abort("invalid cint_type");
@@ -877,10 +897,21 @@ uint64_t json_object_get_uint64(const struct json_object *jso)
case json_type_double: case json_type_double:
// UINT64_MAX can't be exactly represented as a double // UINT64_MAX can't be exactly represented as a double
// so cast to tell the compiler it's ok to round up. // so cast to tell the compiler it's ok to round up.
if (JC_DOUBLE_C(jso)->c_double >= (double)UINT64_MAX) if (JC_DOUBLE_C(jso)->c_double > (double)UINT64_MAX)
{
errno = ERANGE;
return UINT64_MAX; return UINT64_MAX;
}
if (JC_DOUBLE_C(jso)->c_double < 0) if (JC_DOUBLE_C(jso)->c_double < 0)
{
errno = ERANGE;
return 0; return 0;
}
if (isnan(JC_DOUBLE_C(jso)->c_double))
{
errno = EINVAL;
return 0;
}
return (uint64_t)JC_DOUBLE_C(jso)->c_double; return (uint64_t)JC_DOUBLE_C(jso)->c_double;
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
case json_type_string: case json_type_string:
@@ -976,7 +1007,21 @@ int json_c_set_serialization_double_format(const char *double_format, int global
#endif #endif
if (global_serialization_float_format) if (global_serialization_float_format)
free(global_serialization_float_format); free(global_serialization_float_format);
global_serialization_float_format = double_format ? strdup(double_format) : NULL; if (double_format)
{
char *p = strdup(double_format);
if (p == NULL)
{
_json_c_set_last_err("json_c_set_serialization_double_format: "
"out of memory\n");
return -1;
}
global_serialization_float_format = p;
}
else
{
global_serialization_float_format = NULL;
}
} }
else if (global_or_thread == JSON_C_OPTION_THREAD) else if (global_or_thread == JSON_C_OPTION_THREAD)
{ {
@@ -986,16 +1031,31 @@ int json_c_set_serialization_double_format(const char *double_format, int global
free(tls_serialization_float_format); free(tls_serialization_float_format);
tls_serialization_float_format = NULL; tls_serialization_float_format = NULL;
} }
tls_serialization_float_format = double_format ? strdup(double_format) : NULL; if (double_format)
{
char *p = strdup(double_format);
if (p == NULL)
{
_json_c_set_last_err("json_c_set_serialization_double_format: "
"out of memory\n");
return -1;
}
tls_serialization_float_format = p;
}
else
{
tls_serialization_float_format = NULL;
}
#else #else
_json_c_set_last_err("json_c_set_option: not compiled with __thread support\n"); _json_c_set_last_err("json_c_set_serialization_double_format: not compiled "
"with __thread support\n");
return -1; return -1;
#endif #endif
} }
else else
{ {
_json_c_set_last_err("json_c_set_option: invalid global_or_thread value: %d\n", _json_c_set_last_err("json_c_set_serialization_double_format: invalid "
global_or_thread); "global_or_thread value: %d\n", global_or_thread);
return -1; return -1;
} }
return 0; return 0;
@@ -1056,8 +1116,7 @@ static int json_object_double_to_json_string_format(struct json_object *jso, str
format_drops_decimals = 1; format_drops_decimals = 1;
looks_numeric = /* Looks like *some* kind of number */ looks_numeric = /* Looks like *some* kind of number */
isdigit((unsigned char)buf[0]) || is_plain_digit(buf[0]) || (size > 1 && buf[0] == '-' && is_plain_digit(buf[1]));
(size > 1 && buf[0] == '-' && isdigit((unsigned char)buf[1]));
if (size < (int)sizeof(buf) - 2 && looks_numeric && !p && /* Has no decimal point */ if (size < (int)sizeof(buf) - 2 && looks_numeric && !p && /* Has no decimal point */
strchr(buf, 'e') == NULL && /* Not scientific notation */ strchr(buf, 'e') == NULL && /* Not scientific notation */
@@ -1235,9 +1294,13 @@ static int json_object_string_to_json_string(struct json_object *jso, struct pri
int level, int flags) int level, int flags)
{ {
ssize_t len = JC_STRING(jso)->len; ssize_t len = JC_STRING(jso)->len;
if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_FG_GREEN);
printbuf_strappend(pb, "\""); printbuf_strappend(pb, "\"");
json_escape_str(pb, get_string_component(jso), len < 0 ? -(ssize_t)len : len, flags); json_escape_str(pb, get_string_component(jso), len < 0 ? -(ssize_t)len : len, flags);
printbuf_strappend(pb, "\""); printbuf_strappend(pb, "\"");
if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_RESET);
return 0; return 0;
} }
@@ -1254,17 +1317,17 @@ static struct json_object *_json_object_new_string(const char *s, const size_t l
struct json_object_string *jso; struct json_object_string *jso;
/* /*
* Structures Actual memory layout * Structures Actual memory layout
* ------------------- -------------------- * ------------------- --------------------
* [json_object_string [json_object_string * [json_object_string [json_object_string
* [json_object] [json_object] * [json_object] [json_object]
* ...other fields... ...other fields... * ...other fields... ...other fields...
* c_string] len * c_string] len
* bytes * bytes
* of * of
* string * string
* data * data
* \0] * \0]
*/ */
if (len > (SSIZE_T_MAX - (sizeof(*jso) - sizeof(jso->c_string)) - 1)) if (len > (SSIZE_T_MAX - (sizeof(*jso) - sizeof(jso->c_string)) - 1))
return NULL; return NULL;
@@ -1281,7 +1344,8 @@ static struct json_object *_json_object_new_string(const char *s, const size_t l
return NULL; return NULL;
jso->len = len; jso->len = len;
memcpy(jso->c_string.idata, s, len); memcpy(jso->c_string.idata, s, len);
jso->c_string.idata[len] = '\0'; // Cast below needed for Clang UB sanitizer
((char *)jso->c_string.idata)[len] = '\0';
return &jso->base; return &jso->base;
} }
@@ -1305,18 +1369,20 @@ const char *json_object_get_string(struct json_object *jso)
default: return json_object_to_json_string(jso); default: return json_object_to_json_string(jso);
} }
} }
int json_object_get_string_len(const struct json_object *jso)
static inline ssize_t _json_object_get_string_len(const struct json_object_string *jso)
{ {
ssize_t len; ssize_t len;
len = jso->len;
return (len < 0) ? -(ssize_t)len : len;
}
int json_object_get_string_len(const struct json_object *jso)
{
if (!jso) if (!jso)
return 0; return 0;
switch (jso->o_type) switch (jso->o_type)
{ {
case json_type_string: case json_type_string: return _json_object_get_string_len(JC_STRING_C(jso));
{
len = JC_STRING_C(jso)->len;
return (len < 0) ? -(ssize_t)len : len;
}
default: return 0; default: return 0;
} }
} }
@@ -1329,16 +1395,24 @@ static int _json_object_set_string_len(json_object *jso, const char *s, size_t l
if (jso == NULL || jso->o_type != json_type_string) if (jso == NULL || jso->o_type != json_type_string)
return 0; return 0;
if (len >= SSIZE_T_MAX - 1) if (len >= INT_MAX - 1)
// jso->len is a signed ssize_t, so it can't hold the // jso->len is a signed ssize_t, so it can't hold the
// full size_t range. // full size_t range. json_object_get_string_len returns
// length as int, cap length at INT_MAX.
return 0; return 0;
dstbuf = get_string_component_mutable(jso);
curlen = JC_STRING(jso)->len; curlen = JC_STRING(jso)->len;
if (curlen < 0) if (curlen < 0) {
curlen = -curlen; if (len == 0) {
free(JC_STRING(jso)->c_string.pdata);
JC_STRING(jso)->len = curlen = 0;
} else {
curlen = -curlen;
}
}
newlen = len; newlen = len;
dstbuf = get_string_component_mutable(jso);
if ((ssize_t)len > curlen) if ((ssize_t)len > curlen)
{ {
@@ -1385,31 +1459,34 @@ static int json_object_array_to_json_string(struct json_object *jso, struct prin
size_t ii; size_t ii;
printbuf_strappend(pb, "["); printbuf_strappend(pb, "[");
if (flags & JSON_C_TO_STRING_PRETTY)
printbuf_strappend(pb, "\n");
for (ii = 0; ii < json_object_array_length(jso); ii++) for (ii = 0; ii < json_object_array_length(jso); ii++)
{ {
struct json_object *val; struct json_object *val;
if (had_children) if (had_children)
{ {
printbuf_strappend(pb, ","); printbuf_strappend(pb, ",");
if (flags & JSON_C_TO_STRING_PRETTY)
printbuf_strappend(pb, "\n");
} }
if (flags & JSON_C_TO_STRING_PRETTY)
printbuf_strappend(pb, "\n");
had_children = 1; had_children = 1;
if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY))
printbuf_strappend(pb, " "); printbuf_strappend(pb, " ");
indent(pb, level + 1, flags); indent(pb, level + 1, flags);
val = json_object_array_get_idx(jso, ii); val = json_object_array_get_idx(jso, ii);
if (val == NULL) if (val == NULL) {
if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_FG_MAGENTA);
printbuf_strappend(pb, "null"); printbuf_strappend(pb, "null");
else if (val->_to_json_string(val, pb, level + 1, flags) < 0) if (flags & JSON_C_TO_STRING_COLOR)
printbuf_strappend(pb, ANSI_COLOR_RESET);
} else if (val->_to_json_string(val, pb, level + 1, flags) < 0)
return -1; return -1;
} }
if (flags & JSON_C_TO_STRING_PRETTY) if ((flags & JSON_C_TO_STRING_PRETTY) && had_children)
{ {
if (had_children) printbuf_strappend(pb, "\n");
printbuf_strappend(pb, "\n");
indent(pb, level, flags); indent(pb, level, flags);
} }
@@ -1491,6 +1568,12 @@ int json_object_array_add(struct json_object *jso, struct json_object *val)
return array_list_add(JC_ARRAY(jso)->c_array, val); return array_list_add(JC_ARRAY(jso)->c_array, val);
} }
int json_object_array_insert_idx(struct json_object *jso, size_t idx, struct json_object *val)
{
assert(json_object_get_type(jso) == json_type_array);
return array_list_insert_idx(JC_ARRAY(jso)->c_array, idx, val);
}
int json_object_array_put_idx(struct json_object *jso, size_t idx, struct json_object *val) int json_object_array_put_idx(struct json_object *jso, size_t idx, struct json_object *val)
{ {
assert(json_object_get_type(jso) == json_type_array); assert(json_object_get_type(jso) == json_type_array);
@@ -1605,9 +1688,10 @@ int json_object_equal(struct json_object *jso1, struct json_object *jso2)
case json_type_string: case json_type_string:
{ {
return (json_object_get_string_len(jso1) == json_object_get_string_len(jso2) && return (_json_object_get_string_len(JC_STRING(jso1)) ==
_json_object_get_string_len(JC_STRING(jso2)) &&
memcmp(get_string_component(jso1), get_string_component(jso2), memcmp(get_string_component(jso1), get_string_component(jso2),
json_object_get_string_len(jso1)) == 0); _json_object_get_string_len(JC_STRING(jso1))) == 0);
} }
case json_type_object: return json_object_all_values_equal(jso1, jso2); case json_type_object: return json_object_all_values_equal(jso1, jso2);
@@ -1628,14 +1712,22 @@ static int json_object_copy_serializer_data(struct json_object *src, struct json
if (dst->_to_json_string == json_object_userdata_to_json_string || if (dst->_to_json_string == json_object_userdata_to_json_string ||
dst->_to_json_string == _json_object_userdata_to_json_string) dst->_to_json_string == _json_object_userdata_to_json_string)
{ {
dst->_userdata = strdup(src->_userdata); char *p;
assert(src->_userdata);
p = strdup(src->_userdata);
if (p == NULL)
{
_json_c_set_last_err("json_object_copy_serializer_data: out of memory\n");
return -1;
}
dst->_userdata = p;
} }
// else if ... other supported serializers ... // else if ... other supported serializers ...
else else
{ {
_json_c_set_last_err( _json_c_set_last_err(
"json_object_deep_copy: unable to copy unknown serializer data: %p\n", "json_object_copy_serializer_data: unable to copy unknown serializer data: "
(void *)dst->_to_json_string); "%p\n", (void *)dst->_to_json_string);
return -1; return -1;
} }
dst->_user_delete = src->_user_delete; dst->_user_delete = src->_user_delete;
@@ -1672,7 +1764,10 @@ int json_c_shallow_copy_default(json_object *src, json_object *parent, const cha
} }
break; break;
case json_type_string: *dst = json_object_new_string(get_string_component(src)); break; case json_type_string:
*dst = json_object_new_string_len(get_string_component(src),
_json_object_get_string_len(JC_STRING(src)));
break;
case json_type_object: *dst = json_object_new_object(); break; case json_type_object: *dst = json_object_new_object(); break;
@@ -1724,8 +1819,8 @@ static int json_object_deep_copy_recursive(struct json_object *src, struct json_
/* This handles the `json_type_null` case */ /* This handles the `json_type_null` case */
if (!iter.val) if (!iter.val)
jso = NULL; jso = NULL;
else if (json_object_deep_copy_recursive(iter.val, src, iter.key, -1, &jso, else if (json_object_deep_copy_recursive(iter.val, src, iter.key, UINT_MAX,
shallow_copy) < 0) &jso, shallow_copy) < 0)
{ {
json_object_put(jso); json_object_put(jso);
return -1; return -1;
@@ -1789,7 +1884,7 @@ int json_object_deep_copy(struct json_object *src, struct json_object **dst,
if (shallow_copy == NULL) if (shallow_copy == NULL)
shallow_copy = json_c_shallow_copy_default; shallow_copy = json_c_shallow_copy_default;
rc = json_object_deep_copy_recursive(src, NULL, NULL, -1, dst, shallow_copy); rc = json_object_deep_copy_recursive(src, NULL, NULL, UINT_MAX, dst, shallow_copy);
if (rc < 0) if (rc < 0)
{ {
json_object_put(*dst); json_object_put(*dst);

View File

@@ -52,7 +52,7 @@ extern "C" {
* json_object_to_file_ext() functions which causes * json_object_to_file_ext() functions which causes
* the output to be formatted. * the output to be formatted.
* *
* See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ * See the "Two Space Tab" option at https://jsonformatter.curiousconcept.com/
* for an example of the format. * for an example of the format.
*/ */
#define JSON_C_TO_STRING_PRETTY (1 << 1) #define JSON_C_TO_STRING_PRETTY (1 << 1)
@@ -74,6 +74,15 @@ extern "C" {
*/ */
#define JSON_C_TO_STRING_NOSLASHESCAPE (1 << 4) #define JSON_C_TO_STRING_NOSLASHESCAPE (1 << 4)
/**
* A flag for the json_object_to_json_string_ext() and
* json_object_to_file_ext() functions which causes
* the output to be formatted.
*
* Use color for printing json.
*/
#define JSON_C_TO_STRING_COLOR (1 << 5)
/** /**
* A flag for the json_object_object_add_ex function which * A flag for the json_object_object_add_ex function which
* causes the value to be added without a check if it already exists. * causes the value to be added without a check if it already exists.
@@ -100,9 +109,17 @@ extern "C" {
* key is given as a real constant value in the function * key is given as a real constant value in the function
* call, e.g. as in * call, e.g. as in
* json_object_object_add_ex(obj, "ip", json, * json_object_object_add_ex(obj, "ip", json,
* JSON_C_OBJECT_KEY_IS_CONSTANT); * JSON_C_OBJECT_ADD_CONSTANT_KEY);
*/ */
#define JSON_C_OBJECT_KEY_IS_CONSTANT (1 << 2) #define JSON_C_OBJECT_ADD_CONSTANT_KEY (1 << 2)
/**
* This flag is an alias to JSON_C_OBJECT_ADD_CONSTANT_KEY.
* Historically, this flag was used first and the new name
* JSON_C_OBJECT_ADD_CONSTANT_KEY was introduced for version
* 0.16.00 in order to have regular naming.
* Use of this flag is now legacy.
*/
#define JSON_C_OBJECT_KEY_IS_CONSTANT JSON_C_OBJECT_ADD_CONSTANT_KEY
/** /**
* Set the global value of an option, which will apply to all * Set the global value of an option, which will apply to all
@@ -131,7 +148,7 @@ extern "C" {
* beyond the lifetime of the parent object. * beyond the lifetime of the parent object.
* - Detaching an object field or array index from its parent object * - Detaching an object field or array index from its parent object
* (using `json_object_object_del()` or `json_object_array_del_idx()`) * (using `json_object_object_del()` or `json_object_array_del_idx()`)
* - Sharing a json_object with multiple (not necesarily parallel) threads * - Sharing a json_object with multiple (not necessarily parallel) threads
* of execution that all expect to free it (with `json_object_put()`) when * of execution that all expect to free it (with `json_object_put()`) when
* they're done. * they're done.
* *
@@ -155,10 +172,10 @@ JSON_EXPORT struct json_object *json_object_get(struct json_object *obj);
* is a member of (unless you know you've called `json_object_get(obj)` to * is a member of (unless you know you've called `json_object_get(obj)` to
* explicitly increment the refcount). * explicitly increment the refcount).
* *
* NULL may be passed, which which case this is a no-op. * NULL may be passed, in which case this is a no-op.
* *
* @param obj the json_object instance * @param obj the json_object instance
* @returns 1 if the object was freed. * @returns 1 if the object was freed, 0 if only the refcount was decremented
* @see json_object_get() * @see json_object_get()
*/ */
JSON_EXPORT int json_object_put(struct json_object *obj); JSON_EXPORT int json_object_put(struct json_object *obj);
@@ -174,6 +191,7 @@ JSON_EXPORT int json_object_put(struct json_object *obj);
json_type_object, json_type_object,
json_type_array, json_type_array,
json_type_string json_type_string
* @returns 1 if the object is of the specified type, 0 otherwise
*/ */
JSON_EXPORT int json_object_is_type(const struct json_object *obj, enum json_type type); JSON_EXPORT int json_object_is_type(const struct json_object *obj, enum json_type type);
@@ -441,9 +459,9 @@ JSON_EXPORT struct json_object *json_object_object_get(const struct json_object
* associated with the given field name. * associated with the given field name.
* *
* It is safe to pass a NULL value. * It is safe to pass a NULL value.
* @returns whether or not the key exists * @returns 1 if the key exists, 0 otherwise
*/ */
JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key, JSON_EXPORT int json_object_object_get_ex(const struct json_object *obj, const char *key,
struct json_object **value); struct json_object **value);
/** Delete the given json_object field /** Delete the given json_object field
@@ -470,19 +488,19 @@ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key
* @param val the local name for the json_object* object variable defined in * @param val the local name for the json_object* object variable defined in
* the body * the body
*/ */
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L #if defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
#define json_object_object_foreach(obj, key, val) \ #define json_object_object_foreach(obj, key, val) \
char *key = NULL; \ char *key = NULL; \
struct json_object *val __attribute__((__unused__)) = NULL; \ struct json_object *val __attribute__((__unused__)) = NULL; \
for (struct lh_entry *entry##key = json_object_get_object(obj)->head, \ for (struct lh_entry *entry##key = lh_table_head(json_object_get_object(obj)), \
*entry_next##key = NULL; \ *entry_next##key = NULL; \
({ \ ({ \
if (entry##key) \ if (entry##key) \
{ \ { \
key = (char *)lh_entry_k(entry##key); \ key = (char *)lh_entry_k(entry##key); \
val = (struct json_object *)lh_entry_v(entry##key); \ val = (struct json_object *)lh_entry_v(entry##key); \
entry_next##key = entry##key->next; \ entry_next##key = lh_entry_next(entry##key); \
}; \ }; \
entry##key; \ entry##key; \
}); \ }); \
@@ -495,25 +513,25 @@ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key
struct json_object *val = NULL; \ struct json_object *val = NULL; \
struct lh_entry *entry##key; \ struct lh_entry *entry##key; \
struct lh_entry *entry_next##key = NULL; \ struct lh_entry *entry_next##key = NULL; \
for (entry##key = json_object_get_object(obj)->head; \ for (entry##key = lh_table_head(json_object_get_object(obj)); \
(entry##key ? (key = (char *)lh_entry_k(entry##key), \ (entry##key ? (key = (char *)lh_entry_k(entry##key), \
val = (struct json_object *)lh_entry_v(entry##key), \ val = (struct json_object *)lh_entry_v(entry##key), \
entry_next##key = entry##key->next, entry##key) \ entry_next##key = lh_entry_next(entry##key), entry##key) \
: 0); \ : 0); \
entry##key = entry_next##key) entry##key = entry_next##key)
#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ #endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) */
/** Iterate through all keys and values of an object (ANSI C Safe) /** Iterate through all keys and values of an object (ANSI C Safe)
* @param obj the json_object instance * @param obj the json_object instance
* @param iter the object iterator, use type json_object_iter * @param iter the object iterator, use type json_object_iter
*/ */
#define json_object_object_foreachC(obj, iter) \ #define json_object_object_foreachC(obj, iter) \
for (iter.entry = json_object_get_object(obj)->head; \ for (iter.entry = lh_table_head(json_object_get_object(obj)); \
(iter.entry ? (iter.key = (char *)lh_entry_k(iter.entry), \ (iter.entry ? (iter.key = (char *)lh_entry_k(iter.entry), \
iter.val = (struct json_object *)lh_entry_v(iter.entry), iter.entry) \ iter.val = (struct json_object *)lh_entry_v(iter.entry), iter.entry) \
: 0); \ : 0); \
iter.entry = iter.entry->next) iter.entry = lh_entry_next(iter.entry))
/* Array type methods */ /* Array type methods */
@@ -543,7 +561,7 @@ JSON_EXPORT struct array_list *json_object_get_array(const struct json_object *o
/** Get the length of a json_object of type json_type_array /** Get the length of a json_object of type json_type_array
* @param obj the json_object instance * @param obj the json_object instance
* @returns an int * @returns the length of the array
*/ */
JSON_EXPORT size_t json_object_array_length(const struct json_object *obj); JSON_EXPORT size_t json_object_array_length(const struct json_object *obj);
@@ -605,6 +623,25 @@ JSON_EXPORT int json_object_array_add(struct json_object *obj, struct json_objec
JSON_EXPORT int json_object_array_put_idx(struct json_object *obj, size_t idx, JSON_EXPORT int json_object_array_put_idx(struct json_object *obj, size_t idx,
struct json_object *val); struct json_object *val);
/** Insert an element at a specified index in an array (a json_object of type json_type_array)
*
* The reference count will *not* be incremented. This is to make adding
* fields to objects in code more compact. If you want to retain a reference
* to an added object you must wrap the passed object with json_object_get
*
* The array size will be automatically be expanded to the size of the
* index if the index is larger than the current size.
* If the index is within the existing array limits, then the element will be
* inserted and all elements will be shifted. This is the only difference between
* this function and json_object_array_put_idx().
*
* @param obj the json_object instance
* @param idx the index to insert the element at
* @param val the json_object to be added
*/
JSON_EXPORT int json_object_array_insert_idx(struct json_object *obj, size_t idx,
struct json_object *val);
/** Get the element at specified index of array `obj` (which must be a json_object of type json_type_array) /** Get the element at specified index of array `obj` (which must be a json_object of type json_type_array)
* *
* *No* reference counts will be changed, and ownership of the returned * *No* reference counts will be changed, and ownership of the returned
@@ -656,8 +693,9 @@ JSON_EXPORT struct json_object *json_object_new_boolean(json_bool b);
* The type is coerced to a json_bool if the passed object is not a json_bool. * The type is coerced to a json_bool if the passed object is not a json_bool.
* integer and double objects will return 0 if there value is zero * integer and double objects will return 0 if there value is zero
* or 1 otherwise. If the passed object is a string it will return * or 1 otherwise. If the passed object is a string it will return
* 1 if it has a non zero length. If any other object type is passed * 1 if it has a non zero length.
* 1 will be returned if the object is not NULL. * If any other object type is passed 0 will be returned, even non-empty
* json_type_array and json_type_object objects.
* *
* @param obj the json_object instance * @param obj the json_object instance
* @returns a json_bool * @returns a json_bool
@@ -701,9 +739,12 @@ JSON_EXPORT struct json_object *json_object_new_uint64(uint64_t i);
/** Get the int value of a json_object /** Get the int value of a json_object
* *
* The type is coerced to a int if the passed object is not a int. * The type is coerced to a int if the passed object is not a int.
* double objects will return their integer conversion. Strings will be * double objects will return their integer conversion except for NaN values
* parsed as an integer. If no conversion exists then 0 is returned * which return INT32_MIN and the errno is set to EINVAL.
* and errno is set to EINVAL. null is equivalent to 0 (no error values set) * Strings will be parsed as an integer. If no conversion exists then 0 is
* returned and errno is set to EINVAL. null is equivalent to 0 (no error values
* set).
* Sets errno to ERANGE if the value exceeds the range of int.
* *
* Note that integers are stored internally as 64-bit values. * Note that integers are stored internally as 64-bit values.
* If the value of too big or too small to fit into 32-bit, INT32_MAX or * If the value of too big or too small to fit into 32-bit, INT32_MAX or
@@ -738,15 +779,19 @@ JSON_EXPORT int json_object_set_int(struct json_object *obj, int new_value);
* *
* @param obj the json_object instance * @param obj the json_object instance
* @param val the value to add * @param val the value to add
* @returns 1 if the increment succeded, 0 otherwise * @returns 1 if the increment succeeded, 0 otherwise
*/ */
JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val); JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val);
/** Get the int value of a json_object /** Get the int value of a json_object
* *
* The type is coerced to a int64 if the passed object is not a int64. * The type is coerced to a int64 if the passed object is not a int64.
* double objects will return their int64 conversion. Strings will be * double objects will return their int64 conversion except for NaN values
* parsed as an int64. If no conversion exists then 0 is returned. * which return INT64_MIN and the errno is set to EINVAL.
* Strings will be parsed as an int64. If no conversion exists then 0 is
* returned and errno is set to EINVAL. null is equivalent to 0 (no error values
* set).
* Sets errno to ERANGE if the value exceeds the range of int64.
* *
* NOTE: Set errno to 0 directly before a call to this function to determine * NOTE: Set errno to 0 directly before a call to this function to determine
* whether or not conversion was successful (it does not clear the value for * whether or not conversion was successful (it does not clear the value for
@@ -760,8 +805,12 @@ JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj);
/** Get the uint value of a json_object /** Get the uint value of a json_object
* *
* The type is coerced to a uint64 if the passed object is not a uint64. * The type is coerced to a uint64 if the passed object is not a uint64.
* double objects will return their uint64 conversion. Strings will be * double objects will return their uint64 conversion except for NaN values
* parsed as an uint64. If no conversion exists then 0 is returned. * which return 0 and the errno is set to EINVAL.
* Strings will be parsed as an uint64. If no conversion exists then 0 is
* returned and errno is set to EINVAL. null is equivalent to 0 (no error values
* set).
* Sets errno to ERANGE if the value exceeds the range of uint64.
* *
* NOTE: Set errno to 0 directly before a call to this function to determine * NOTE: Set errno to 0 directly before a call to this function to determine
* whether or not conversion was successful (it does not clear the value for * whether or not conversion was successful (it does not clear the value for
@@ -1005,7 +1054,7 @@ JSON_EXPORT struct json_object *json_object_new_null(void);
* *
* @param obj1 the first json_object instance * @param obj1 the first json_object instance
* @param obj2 the second json_object instance * @param obj2 the second json_object instance
* @returns whether both objects are equal or not * @returns 1 if both objects are equal, 0 otherwise
*/ */
JSON_EXPORT int json_object_equal(struct json_object *obj1, struct json_object *obj2); JSON_EXPORT int json_object_equal(struct json_object *obj1, struct json_object *obj2);
@@ -1055,7 +1104,7 @@ JSON_EXPORT json_c_shallow_copy_fn json_c_shallow_copy_default;
* when custom serializers are in use. See also * when custom serializers are in use. See also
* json_object set_serializer. * json_object set_serializer.
* *
* @returns 0 if the copy went well, -1 if an error occured during copy * @returns 0 if the copy went well, -1 if an error occurred during copy
* or if the destination pointer is non-NULL * or if the destination pointer is non-NULL
*/ */

View File

@@ -71,7 +71,7 @@ struct json_object_iterator json_object_iter_begin(struct json_object *obj)
/// @note For a pair-less Object, head is NULL, which matches our /// @note For a pair-less Object, head is NULL, which matches our
/// definition of the "end" iterator /// definition of the "end" iterator
iter.opaque_ = pTable->head; iter.opaque_ = lh_table_head(pTable);
return iter; return iter;
} }
@@ -98,7 +98,7 @@ void json_object_iter_next(struct json_object_iterator *iter)
JASSERT(NULL != iter); JASSERT(NULL != iter);
JASSERT(kObjectEndIterValue != iter->opaque_); JASSERT(kObjectEndIterValue != iter->opaque_);
iter->opaque_ = ((const struct lh_entry *)iter->opaque_)->next; iter->opaque_ = lh_entry_next(((const struct lh_entry *)iter->opaque_));
} }
/** /**

View File

@@ -28,7 +28,7 @@ struct json_object;
#include <unistd.h> #include <unistd.h>
#endif /* HAVE_UNISTD_H */ #endif /* HAVE_UNISTD_H */
#ifdef _MSC_VER #if defined(_MSC_VER) && !defined(ssize_t)
#include <BaseTsd.h> #include <BaseTsd.h>
typedef SSIZE_T ssize_t; typedef SSIZE_T ssize_t;
#endif #endif

334
json_patch.c Normal file
View File

@@ -0,0 +1,334 @@
/*
* Copyright (c) 2021 Alexandru Ardelean.
* Copyright (c) 2023 Eric Hawicz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See COPYING for details.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "json_patch.h"
#include "json_object_private.h"
#include "json_pointer_private.h"
#include <limits.h>
#ifndef SIZE_T_MAX
#if SIZEOF_SIZE_T == SIZEOF_INT
#define SIZE_T_MAX UINT_MAX
#elif SIZEOF_SIZE_T == SIZEOF_LONG
#define SIZE_T_MAX ULONG_MAX
#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
#define SIZE_T_MAX ULLONG_MAX
#else
#error Unable to determine size of size_t
#endif
#endif
#define _set_err(_errval, _errmsg) do { \
patch_error->errno_code = (_errval); \
patch_error->errmsg = (_errmsg); \
errno = 0; /* To avoid confusion */ \
} while (0)
#define _set_err_from_ptrget(_errval, _fieldname) do { \
patch_error->errno_code = (_errval); \
patch_error->errmsg = (_errval) == ENOENT ? \
"Did not find element referenced by " _fieldname " field" : \
"Invalid " _fieldname " field"; \
errno = 0; /* To avoid confusion */ \
} while(0)
/**
* JavaScript Object Notation (JSON) Patch
* RFC 6902 - https://tools.ietf.org/html/rfc6902
*/
static int json_patch_apply_test(struct json_object **res,
struct json_object *patch_elem,
const char *path, struct json_patch_error *patch_error)
{
struct json_object *value1, *value2;
if (!json_object_object_get_ex(patch_elem, "value", &value1)) {
_set_err(EINVAL, "Patch object does not contain a 'value' field");
return -1;
}
if (json_pointer_get(*res, path, &value2))
{
_set_err_from_ptrget(errno, "path");
return -1;
}
if (!json_object_equal(value1, value2)) {
_set_err(ENOENT, "Value of element referenced by 'path' field did not match 'value' field");
return -1;
}
return 0;
}
static int __json_patch_apply_remove(struct json_pointer_get_result *jpres)
{
if (json_object_is_type(jpres->parent, json_type_array)) {
return json_object_array_del_idx(jpres->parent, jpres->index_in_parent, 1);
} else if (jpres->parent && jpres->key_in_parent) {
json_object_object_del(jpres->parent, jpres->key_in_parent);
return 0;
} else {
// We're removing the root object
(void)json_object_put(jpres->obj);
jpres->obj = NULL;
return 0;
}
}
static int json_patch_apply_remove(struct json_object **res, const char *path, struct json_patch_error *patch_error)
{
struct json_pointer_get_result jpres;
int rc;
if (json_pointer_get_internal(*res, path, &jpres))
{
_set_err_from_ptrget(errno, "path");
return -1;
}
rc = __json_patch_apply_remove(&jpres);
if (rc < 0)
_set_err(EINVAL, "Unable to remove path referenced by 'path' field");
// This means we removed and freed the root object, i.e. *res
if (jpres.parent == NULL)
*res = NULL;
return rc;
}
// callback for json_pointer_set_with_cb()
// key is ignored because this callback is only for arrays
static int json_object_array_insert_idx_cb(struct json_object *parent, const char *key, size_t idx,
struct json_object *value, void *priv)
{
int rc;
int *add = priv;
if (idx > json_object_array_length(parent))
{
// Note: will propagate back out through json_pointer_set_with_cb()
errno = EINVAL;
return -1;
}
if (*add)
rc = json_object_array_insert_idx(parent, idx, value);
else
rc = json_object_array_put_idx(parent, idx, value);
if (rc < 0)
errno = EINVAL;
return rc;
}
static int json_patch_apply_add_replace(struct json_object **res,
struct json_object *patch_elem,
const char *path, int add, struct json_patch_error *patch_error)
{
struct json_object *value;
int rc;
if (!json_object_object_get_ex(patch_elem, "value", &value)) {
_set_err(EINVAL, "Patch object does not contain a 'value' field");
return -1;
}
/* if this is a replace op, then we need to make sure it exists before replacing */
if (!add && json_pointer_get(*res, path, NULL)) {
_set_err_from_ptrget(errno, "path");
return -1;
}
rc = json_pointer_set_with_cb(res, path, json_object_get(value),
json_object_array_insert_idx_cb, 0, &add);
if (rc)
{
_set_err(errno, "Failed to set value at path referenced by 'path' field");
json_object_put(value);
}
return rc;
}
// callback for json_pointer_set_with_cb()
// key is ignored because this callback is only for arrays
static int json_object_array_move_cb(struct json_object *parent, const char *key, size_t idx,
struct json_object *value, void *priv)
{
int rc;
struct json_pointer_get_result *from = priv;
size_t len = json_object_array_length(parent);
/**
* If it's the same array parent, it means that we removed
* and element from it, so the length is temporarily reduced
* by 1, which means that if we try to move an element to
* the last position, we need to check the current length + 1
*/
if (parent == from->parent)
len++;
if (idx > len)
{
// Note: will propagate back out through json_pointer_set_with_cb()
errno = EINVAL;
return -1;
}
rc = json_object_array_insert_idx(parent, idx, value);
if (rc < 0)
errno = EINVAL;
return rc;
}
static int json_patch_apply_move_copy(struct json_object **res,
struct json_object *patch_elem,
const char *path, int move, struct json_patch_error *patch_error)
{
json_pointer_set_cb array_set_cb;
struct json_pointer_get_result from;
struct json_object *jfrom;
const char *from_s;
size_t from_s_len;
int rc;
if (!json_object_object_get_ex(patch_elem, "from", &jfrom)) {
_set_err(EINVAL, "Patch does not contain a 'from' field");
return -1;
}
from_s = json_object_get_string(jfrom);
from_s_len = strlen(from_s);
if (strncmp(from_s, path, from_s_len) == 0) {
/**
* If lengths match, it's a noop, if they don't,
* then we're trying to move a parent under a child
* which is not allowed as per RFC 6902 section 4.4
* The "from" location MUST NOT be a proper prefix of the "path"
* location; i.e., a location cannot be moved into one of its children.
*/
if (from_s_len == strlen(path))
return 0;
_set_err(EINVAL, "Invalid attempt to move parent under a child");
return -1;
}
rc = json_pointer_get_internal(*res, from_s, &from);
if (rc)
{
_set_err_from_ptrget(errno, "from");
return rc;
}
// Note: it's impossible for json_pointer to find the root obj, due
// to the path check above, so from.parent is guaranteed non-NULL
json_object_get(from.obj);
if (!move) {
array_set_cb = json_object_array_insert_idx_cb;
} else {
rc = __json_patch_apply_remove(&from);
if (rc < 0) {
json_object_put(from.obj);
return rc;
}
array_set_cb = json_object_array_move_cb;
}
rc = json_pointer_set_with_cb(res, path, from.obj, array_set_cb, 0, &from);
if (rc)
{
_set_err(errno, "Failed to set value at path referenced by 'path' field");
json_object_put(from.obj);
}
return rc;
}
int json_patch_apply(struct json_object *copy_from, struct json_object *patch,
struct json_object **base, struct json_patch_error *patch_error)
{
size_t ii;
int rc = 0;
struct json_patch_error placeholder;
if (!patch_error)
patch_error = &placeholder;
patch_error->patch_failure_idx = SIZE_T_MAX;
patch_error->errno_code = 0;
if (base == NULL||
(*base == NULL && copy_from == NULL) ||
(*base != NULL && copy_from != NULL))
{
_set_err(EFAULT, "Exactly one of *base or copy_from must be non-NULL");
return -1;
}
if (!json_object_is_type(patch, json_type_array)) {
_set_err(EFAULT, "Patch object is not of type json_type_array");
return -1;
}
if (copy_from != NULL)
{
if (json_object_deep_copy(copy_from, base, NULL) < 0)
{
_set_err(ENOMEM, "Unable to copy copy_from using json_object_deep_copy()");
return -1;
}
}
/* Go through all operations ; apply them on res */
for (ii = 0; ii < json_object_array_length(patch); ii++) {
struct json_object *jop, *jpath;
struct json_object *patch_elem = json_object_array_get_idx(patch, ii);
const char *op, *path;
patch_error->patch_failure_idx = ii;
if (!json_object_object_get_ex(patch_elem, "op", &jop)) {
_set_err(EINVAL, "Patch object does not contain 'op' field");
return -1;
}
op = json_object_get_string(jop);
if (!json_object_object_get_ex(patch_elem, "path", &jpath)) {
_set_err(EINVAL, "Patch object does not contain 'path' field");
return -1;
}
path = json_object_get_string(jpath); // Note: empty string is ok!
if (!strcmp(op, "test"))
rc = json_patch_apply_test(base, patch_elem, path, patch_error);
else if (!strcmp(op, "remove"))
rc = json_patch_apply_remove(base, path, patch_error);
else if (!strcmp(op, "add"))
rc = json_patch_apply_add_replace(base, patch_elem, path, 1, patch_error);
else if (!strcmp(op, "replace"))
rc = json_patch_apply_add_replace(base, patch_elem, path, 0, patch_error);
else if (!strcmp(op, "move"))
rc = json_patch_apply_move_copy(base, patch_elem, path, 1, patch_error);
else if (!strcmp(op, "copy"))
rc = json_patch_apply_move_copy(base, patch_elem, path, 0, patch_error);
else {
_set_err(EINVAL, "Patch object has invalid 'op' field");
return -1;
}
if (rc < 0)
break;
}
return rc;
}

80
json_patch.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2021 Alexadru Ardelean.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See COPYING for details.
*
*/
/**
* @file
* @brief JSON Patch (RFC 6902) implementation for manipulating JSON objects
*/
#ifndef _json_patch_h_
#define _json_patch_h_
#include "json_pointer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Details of an error that occurred during json_patch_apply()
*/
struct json_patch_error {
/**
* An errno value indicating what kind of error occurred.
* Possible values include:
* - ENOENT - A path referenced in the operation does not exist.
* - EINVAL - An invalid operation or with invalid path was attempted
* - ENOMEM - Unable to allocate memory
* - EFAULT - Invalid arguments were passed to json_patch_apply()
* (i.e. a C API error, vs. a data error like EINVAL)
*/
int errno_code;
/**
* The index into the patch array of the operation that failed,
* or SIZE_T_MAX for overall errors.
*/
size_t patch_failure_idx;
/**
* A human readable error message.
* Allocated from static storage, does not need to be freed.
*/
const char *errmsg;
};
/**
* Apply the JSON patch to the base object.
* The patch object must be formatted as per RFC 6902, i.e.
* a json_type_array containing patch operations.
* If the patch is not correctly formatted, an error will
* be returned.
*
* The json_object at *base will be modified in place.
* Exactly one of *base or copy_from must be non-NULL.
* If *base is NULL, a new copy of copy_from will allocated and populated
* using json_object_deep_copy(). In this case json_object_put() _must_ be
* used to free *base even if the overall patching operation fails.
*
* If anything fails during patching a negative value will be returned,
* and patch_error (if non-NULL) will be populated with error details.
*
* @param base a pointer to the JSON object which to patch
* @param patch the JSON object that describes the patch to be applied
* @param copy_from a JSON object to copy to *base
* @param patch_error optional, details about errors
*
* @return negative if an error (or not found), or 0 if patch completely applied
*/
JSON_EXPORT int json_patch_apply(struct json_object *copy_from, struct json_object *patch,
struct json_object **base, struct json_patch_error *patch_error);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -10,16 +10,20 @@
#include "strerror_override.h" #include "strerror_override.h"
#include <ctype.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "json_object_private.h"
#include "json_pointer.h" #include "json_pointer.h"
#include "json_pointer_private.h"
#include "strdup_compat.h" #include "strdup_compat.h"
#include "vasprintf_compat.h" #include "vasprintf_compat.h"
/* Avoid ctype.h and locale overhead */
#define is_plain_digit(c) ((c) >= '0' && (c) <= '9')
/** /**
* JavaScript Object Notation (JSON) Pointer * JavaScript Object Notation (JSON) Pointer
* RFC 6901 - https://tools.ietf.org/html/rfc6901 * RFC 6901 - https://tools.ietf.org/html/rfc6901
@@ -27,8 +31,8 @@
static void string_replace_all_occurrences_with_char(char *s, const char *occur, char repl_char) static void string_replace_all_occurrences_with_char(char *s, const char *occur, char repl_char)
{ {
int slen = strlen(s); size_t slen = strlen(s);
int skip = strlen(occur) - 1; /* length of the occurrence, minus the char we're replacing */ size_t skip = strlen(occur) - 1; /* length of the occurrence, minus the char we're replacing */
char *p = s; char *p = s;
while ((p = strstr(p, occur))) while ((p = strstr(p, occur)))
{ {
@@ -39,18 +43,18 @@ static void string_replace_all_occurrences_with_char(char *s, const char *occur,
} }
} }
static int is_valid_index(struct json_object *jo, const char *path, int32_t *idx) static int is_valid_index(const char *path, size_t *idx)
{ {
int i, len = strlen(path); size_t i, len = strlen(path);
/* this code-path optimizes a bit, for when we reference the 0-9 index range /* this code-path optimizes a bit, for when we reference the 0-9 index range
* in a JSON array and because leading zeros not allowed * in a JSON array and because leading zeros not allowed
*/ */
if (len == 1) if (len == 1)
{ {
if (isdigit((unsigned char)path[0])) if (is_plain_digit(path[0]))
{ {
*idx = (path[0] - '0'); *idx = (path[0] - '0');
goto check_oob; return 1;
} }
errno = EINVAL; errno = EINVAL;
return 0; return 0;
@@ -64,39 +68,34 @@ static int is_valid_index(struct json_object *jo, const char *path, int32_t *idx
/* RFC states base-10 decimals */ /* RFC states base-10 decimals */
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
if (!isdigit((unsigned char)path[i])) if (!is_plain_digit(path[i]))
{ {
errno = EINVAL; errno = EINVAL;
return 0; return 0;
} }
} }
*idx = strtol(path, NULL, 10); // We know it's all digits, so the only error case here is overflow,
if (*idx < 0) // but ULLONG_MAX will be longer than any array length so that's ok.
{ *idx = strtoull(path, NULL, 10);
errno = EINVAL;
return 0;
}
check_oob:
len = json_object_array_length(jo);
if (*idx >= len)
{
errno = ENOENT;
return 0;
}
return 1; return 1;
} }
static int json_pointer_get_single_path(struct json_object *obj, char *path, static int json_pointer_get_single_path(struct json_object *obj, char *path,
struct json_object **value) struct json_object **value, size_t *idx)
{ {
if (json_object_is_type(obj, json_type_array)) if (json_object_is_type(obj, json_type_array))
{ {
int32_t idx; if (!is_valid_index(path, idx))
if (!is_valid_index(obj, path, &idx))
return -1; return -1;
obj = json_object_array_get_idx(obj, idx); if (*idx >= json_object_array_length(obj))
{
errno = ENOENT;
return -1;
}
obj = json_object_array_get_idx(obj, *idx);
if (obj) if (obj)
{ {
if (value) if (value)
@@ -122,24 +121,34 @@ static int json_pointer_get_single_path(struct json_object *obj, char *path,
} }
static int json_pointer_set_single_path(struct json_object *parent, const char *path, static int json_pointer_set_single_path(struct json_object *parent, const char *path,
struct json_object *value) struct json_object *value,
json_pointer_set_cb set_cb, int cb_handles_obj, void *priv)
{ {
if (json_object_is_type(parent, json_type_array)) if (json_object_is_type(parent, json_type_array))
{ {
int32_t idx; size_t idx;
/* RFC (Chapter 4) states that '-' may be used to add new elements to an array */ /* RFC (Chapter 4) states that '-' may be used to add new elements to an array */
if (path[0] == '-' && path[1] == '\0') if (path[0] == '-' && path[1] == '\0')
return json_object_array_add(parent, value); return json_object_array_add(parent, value);
if (!is_valid_index(parent, path, &idx)) if (!is_valid_index(path, &idx))
return -1; return -1;
return json_object_array_put_idx(parent, idx, value); return set_cb(parent, NULL, idx, value, priv);
} }
/* path replacements should have been done in json_pointer_get_single_path(), /* path replacements should have been done in json_pointer_get_single_path(),
* and we should still be good here * and we should still be good here
*/ */
if (json_object_is_type(parent, json_type_object)) if (json_object_is_type(parent, json_type_object))
return json_object_object_add(parent, path, value); {
if (cb_handles_obj)
{
return set_cb(parent, path, (size_t)-1, value, priv);
}
else
{
return json_object_object_add(parent, path, value);
}
}
/* Getting here means that we tried to "dereference" a primitive JSON type /* Getting here means that we tried to "dereference" a primitive JSON type
* (like string, int, bool).i.e. add a sub-object to it * (like string, int, bool).i.e. add a sub-object to it
@@ -148,9 +157,11 @@ static int json_pointer_set_single_path(struct json_object *parent, const char *
return -1; return -1;
} }
static int json_pointer_get_recursive(struct json_object *obj, char *path, static int json_pointer_result_get_recursive(struct json_object *obj, char *path,
struct json_object **value) struct json_pointer_get_result *res)
{ {
struct json_object *parent_obj = obj;
size_t idx = 0;
char *endp; char *endp;
int rc; int rc;
@@ -167,24 +178,47 @@ static int json_pointer_get_recursive(struct json_object *obj, char *path,
*endp = '\0'; *endp = '\0';
/* If we err-ed here, return here */ /* If we err-ed here, return here */
if ((rc = json_pointer_get_single_path(obj, path, &obj))) if ((rc = json_pointer_get_single_path(obj, path, &obj, &idx)))
return rc; return rc;
if (endp) if (endp)
{ {
/* Put the slash back, so that the sanity check passes on next recursion level */ /* Put the slash back, so that the sanity check passes on next recursion level */
*endp = '/'; *endp = '/';
return json_pointer_get_recursive(obj, endp, value); return json_pointer_result_get_recursive(obj, endp, res);
} }
/* We should be at the end of the recursion here */ /* We should be at the end of the recursion here */
if (value) if (res) {
*value = obj; res->parent = parent_obj;
res->obj = obj;
if (json_object_is_type(res->parent, json_type_array))
res->index_in_parent = idx;
else
res->key_in_parent = path;
}
return 0; return 0;
} }
int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res) static int json_pointer_object_get_recursive(struct json_object *obj, char *path,
struct json_object **value)
{
struct json_pointer_get_result res;
int rc;
rc = json_pointer_result_get_recursive(obj, path, &res);
if (rc)
return rc;
if (value)
*value = res.obj;
return 0;
}
int json_pointer_get_internal(struct json_object *obj, const char *path,
struct json_pointer_get_result *res)
{ {
char *path_copy = NULL; char *path_copy = NULL;
int rc; int rc;
@@ -197,8 +231,10 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje
if (path[0] == '\0') if (path[0] == '\0')
{ {
if (res) res->parent = NULL;
*res = obj; res->obj = obj;
res->key_in_parent = NULL;
res->index_in_parent = UINT32_MAX;
return 0; return 0;
} }
@@ -208,12 +244,30 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje
errno = ENOMEM; errno = ENOMEM;
return -1; return -1;
} }
rc = json_pointer_get_recursive(obj, path_copy, res); rc = json_pointer_result_get_recursive(obj, path_copy, res);
/* re-map the path string to the const-path string */
if (rc == 0 && json_object_is_type(res->parent, json_type_object) && res->key_in_parent)
res->key_in_parent = path + (res->key_in_parent - path_copy);
free(path_copy); free(path_copy);
return rc; return rc;
} }
int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res)
{
struct json_pointer_get_result jpres;
int rc;
rc = json_pointer_get_internal(obj, path, &jpres);
if (rc)
return rc;
if (res)
*res = jpres.obj;
return 0;
}
int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...) int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...)
{ {
char *path_copy = NULL; char *path_copy = NULL;
@@ -240,14 +294,16 @@ int json_pointer_getf(struct json_object *obj, struct json_object **res, const c
goto out; goto out;
} }
rc = json_pointer_get_recursive(obj, path_copy, res); rc = json_pointer_object_get_recursive(obj, path_copy, res);
out: out:
free(path_copy); free(path_copy);
return rc; return rc;
} }
int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value) int json_pointer_set_with_cb(struct json_object **obj, const char *path,
struct json_object *value,
json_pointer_set_cb set_cb, int cb_handles_obj, void *priv)
{ {
const char *endp; const char *endp;
char *path_copy = NULL; char *path_copy = NULL;
@@ -277,7 +333,7 @@ int json_pointer_set(struct json_object **obj, const char *path, struct json_obj
if ((endp = strrchr(path, '/')) == path) if ((endp = strrchr(path, '/')) == path)
{ {
path++; path++;
return json_pointer_set_single_path(*obj, path, value); return json_pointer_set_single_path(*obj, path, value, set_cb, cb_handles_obj, priv);
} }
/* pass a working copy to the recursive call */ /* pass a working copy to the recursive call */
@@ -287,14 +343,28 @@ int json_pointer_set(struct json_object **obj, const char *path, struct json_obj
return -1; return -1;
} }
path_copy[endp - path] = '\0'; path_copy[endp - path] = '\0';
rc = json_pointer_get_recursive(*obj, path_copy, &set); rc = json_pointer_object_get_recursive(*obj, path_copy, &set);
free(path_copy); free(path_copy);
if (rc) if (rc)
return rc; return rc;
endp++; endp++;
return json_pointer_set_single_path(set, endp, value); return json_pointer_set_single_path(set, endp, value, set_cb, cb_handles_obj, priv);
}
static int default_put_cb(struct json_object *parent, const char *key, size_t idx,
struct json_object *value, void *priv)
{
if (key == NULL)
return json_object_array_put_idx(parent, idx, value);
else
return json_object_object_add(parent, key, value);
}
int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value)
{
return json_pointer_set_with_cb(obj, path, value, default_put_cb, 1, NULL);
} }
int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt,
@@ -342,15 +412,64 @@ int json_pointer_setf(struct json_object **obj, struct json_object *value, const
} }
*endp = '\0'; *endp = '\0';
rc = json_pointer_get_recursive(*obj, path_copy, &set); rc = json_pointer_object_get_recursive(*obj, path_copy, &set);
if (rc) if (rc)
goto out; goto out;
set_single_path: set_single_path:
endp++; endp++;
rc = json_pointer_set_single_path(set, endp, value); rc = json_pointer_set_single_path(set, endp, value, default_put_cb, 1, NULL);
out: out:
free(path_copy); free(path_copy);
return rc; return rc;
} }
int json_pointer_set_with_limit_index(struct json_object **obj, const char *path,
struct json_object *value, size_t limit_index)
{
// -1 means no limits
if (limit_index == (size_t)-1)
{
return json_pointer_set_with_cb(obj, path, value, default_put_cb, 1, NULL);
}
return json_pointer_set_with_cb(obj, path, value,
json_object_array_put_with_idx_limit_cb, 0, &limit_index);
}
/* safe callback for array index limit */
int json_object_array_put_with_idx_limit_cb(struct json_object *jso, const char *key, size_t idx,
struct json_object *jso_new, void *priv)
{
size_t max_idx;
if (key == NULL)
{
// array operation
// use priv as a size_t pointer to pass in the maximum allowed index.
// The priv is required context for this callback and must not be NULL.
if (!priv)
{
errno = EFAULT;
return -1;
}
max_idx = *(size_t*)priv;
// Check against a maximum to prevent excessive memory allocations.
// An extremely large index, even if it doesn't overflow size_t,
// will cause a huge memory allocation request via realloc,
// leading to an OOM.
if (idx > max_idx)
{
errno = EINVAL;
return -1;
}
return json_object_array_put_idx(jso, idx, jso_new);
}
else
{
// object operation
return json_object_object_add(jso, key, jso_new);
}
}

View File

@@ -32,11 +32,6 @@ extern "C" {
* Internally, this is equivalent to doing a series of 'json_object_object_get()' * Internally, this is equivalent to doing a series of 'json_object_object_get()'
* and 'json_object_array_get_idx()' along the given 'path'. * and 'json_object_array_get_idx()' along the given 'path'.
* *
* Note that the 'path' string supports 'printf()' type arguments, so, whatever
* is added after the 'res' param will be treated as an argument for 'path'
* Example: json_pointer_get(obj, "/foo/%d/%s", &res, 0, bar)
* This means, that you need to escape '%' with '%%' (just like in printf())
*
* @param obj the json_object instance/tree from where to retrieve sub-objects * @param obj the json_object instance/tree from where to retrieve sub-objects
* @param path a (RFC6901) string notation for the sub-object to retrieve * @param path a (RFC6901) string notation for the sub-object to retrieve
* @param res a pointer that stores a reference to the json_object * @param res a pointer that stores a reference to the json_object
@@ -50,7 +45,9 @@ JSON_EXPORT int json_pointer_get(struct json_object *obj, const char *path,
/** /**
* This is a variant of 'json_pointer_get()' that supports printf() style arguments. * This is a variant of 'json_pointer_get()' that supports printf() style arguments.
* *
* Example: json_pointer_getf(obj, res, "/foo/%d/%s", 0, bak) * Variable arguments go after the 'path_fmt' parameter.
*
* Example: json_pointer_getf(obj, res, "/foo/%d/%s", 0, "bar")
* This also means that you need to escape '%' with '%%' (just like in printf()) * This also means that you need to escape '%' with '%%' (just like in printf())
* *
* Please take into consideration all recommended 'printf()' format security * Please take into consideration all recommended 'printf()' format security
@@ -84,10 +81,9 @@ JSON_EXPORT int json_pointer_getf(struct json_object *obj, struct json_object **
* That also implies that 'json_pointer_set()' does not do any refcount incrementing. * That also implies that 'json_pointer_set()' does not do any refcount incrementing.
* (Just that single decrement that was mentioned above). * (Just that single decrement that was mentioned above).
* *
* Note that the 'path' string supports 'printf()' type arguments, so, whatever * @warning This function is vulnerable to an OOM.
* is added after the 'value' param will be treated as an argument for 'path' * To prevent this, use the safer variant 'json_pointer_set_with_limit_index()'
* Example: json_pointer_set(obj, "/foo/%d/%s", value, 0, bak) * or the flexible 'json_pointer_set_with_array_cb()' with a custom callback.
* This means, that you need to escape '%' with '%%' (just like in printf())
* *
* @param obj the json_object instance/tree to which to add a sub-object * @param obj the json_object instance/tree to which to add a sub-object
* @param path a (RFC6901) string notation for the sub-object to set in the tree * @param path a (RFC6901) string notation for the sub-object to set in the tree
@@ -101,7 +97,9 @@ JSON_EXPORT int json_pointer_set(struct json_object **obj, const char *path,
/** /**
* This is a variant of 'json_pointer_set()' that supports printf() style arguments. * This is a variant of 'json_pointer_set()' that supports printf() style arguments.
* *
* Example: json_pointer_setf(obj, value, "/foo/%d/%s", 0, bak) * Variable arguments go after the 'path_fmt' parameter.
*
* Example: json_pointer_setf(obj, value, "/foo/%d/%s", 0, "bar")
* This also means that you need to escape '%' with '%%' (just like in printf()) * This also means that you need to escape '%' with '%%' (just like in printf())
* *
* Please take into consideration all recommended 'printf()' format security * Please take into consideration all recommended 'printf()' format security
@@ -116,6 +114,73 @@ JSON_EXPORT int json_pointer_set(struct json_object **obj, const char *path,
JSON_EXPORT int json_pointer_setf(struct json_object **obj, struct json_object *value, JSON_EXPORT int json_pointer_setf(struct json_object **obj, struct json_object *value,
const char *path_fmt, ...); const char *path_fmt, ...);
/**
* A convenient and safe variant of 'json_pointer_set()' that prevents excessive memory allocations
* by enforcing a limit on array indices.
*
* @param obj the json_object instance/tree to which to add a sub-object
* @param path a (RFC6901) string notation for the sub-object to set in the tree
* @param value object to set at path
* @param limit_index The maximum allowed value for an array index. If a path
* contains an index larger than this, the function will fail
* with errno set to EINVAL. A value of -1 can be used to specify
* no limit, reverting to the original behavior
*
* @return negative if an error (or not found), or 0 if succeeded
*/
JSON_EXPORT int json_pointer_set_with_limit_index(struct json_object **obj, const char *path,
struct json_object *value, size_t limit_index);
/**
* Callback function type.
*
* When setting an array element, 'key' will be NULL and 'idx' will be the
* target index.
* When setting an object field, 'key' will be the target key and 'idx' will
* be -1.
*/
typedef int(*json_pointer_set_cb)(json_object *parent, const char *key, size_t idx,
json_object *value, void *priv);
/**
* Variant of 'json_pointer_set()' that allows specifying a custom callback
*
* @param obj the json_object instance/tree to which to add a sub-object
* @param path a (RFC6901) string notation for the sub-object to set in the tree
* @param value object to set at path
* @param set_cb A custom callback function to handle setting the element
* @param cb_handles_obj If 0, the callback is only invoked for array modifications.
* If 1, the callback is invoked for both array and object
* modifications.
* @param priv A private pointer passed through to the set_cb callback,
* for user-defined context
*
* @return negative if an error (or not found), or 0 if succeeded
*/
JSON_EXPORT int json_pointer_set_with_cb(struct json_object **obj, const char *path,
struct json_object *value,
json_pointer_set_cb set_cb, int cb_handles_obj, void *priv);
/**
* A safer callback for 'json_pointer_set_with_cb()' that enforces a
* maximum array index.
*
* This function can be used as the 'set_cb' argument to prevent OOM.
* It expects the 'priv' argument to be a valid pointer to a 'size_t' variable
* that holds the maximum allowed index.
*
* @param jso the parent json_object array.
* @param key the object field where the element is to be placed, should be NULL here.
* @param idx the index where the element is to be placed.
* @param jso_new the new json_object to place at the index.
* @param priv A pointer to a 'size_t' variable specifying the maximum index.
* This pointer must not be NULL.
*
* @return 0 on success, or a negative value if idx exceeds the limit or 'priv' is NULL.
*/
JSON_EXPORT int json_object_array_put_with_idx_limit_cb(struct json_object *jso, const char *key, size_t idx,
struct json_object *jso_new, void *priv);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

50
json_pointer_private.h Normal file
View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 Eric Hawicz
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See COPYING for details.
*/
/**
* @file
* @brief Do not use, json-c internal, may be changed or removed at any time.
*/
#ifndef _json_pointer_private_h_
#define _json_pointer_private_h_
#ifdef __cplusplus
extern "C" {
#endif
struct json_pointer_get_result {
struct json_object *parent;
struct json_object *obj;
// The key of the found object; only valid when parent is json_type_object
// Caution: re-uses tail end of the `path` argument to json_pointer_get_internal
const char *key_in_parent;
// the index of the found object; only valid when parent is json_type_array
uint32_t index_in_parent;
};
int json_pointer_get_internal(struct json_object *obj, const char *path,
struct json_pointer_get_result *res);
// replaced by json_pointer_set_cb
// typedef int(*json_pointer_array_set_cb)(json_object *parent, size_t idx,
// json_object *value, void *priv);
typedef int(*json_pointer_set_cb)(json_object *parent, const char *key, size_t idx,
json_object *value, void *priv);
// replaced by json_pointer_set_with_cb
// int json_pointer_set_with_array_cb(struct json_object **obj, const char *path,
// struct json_object *value,
// json_pointer_array_set_cb array_set_cb, void *priv);
int json_pointer_set_with_cb(struct json_object **obj, const char *path,
struct json_object *value,
json_pointer_set_cb set_cb, int cb_handles_obj, void *priv);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -10,14 +10,14 @@
* *
* Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved.
* The copyrights to the contents of this file are licensed under the MIT License * The copyrights to the contents of this file are licensed under the MIT License
* (http://www.opensource.org/licenses/mit-license.php) * (https://www.opensource.org/licenses/mit-license.php)
*/ */
#include "config.h" #include "config.h"
#include "math_compat.h" #include "math_compat.h"
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <stddef.h> #include <stddef.h>
@@ -46,13 +46,41 @@
#define jt_hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x)&7) + 9) #define jt_hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x)&7) + 9)
#if !HAVE_STRNCASECMP && defined(_MSC_VER) #if !HAVE_STRNCASECMP && defined(_WIN32)
/* MSC has the version as _strnicmp */ /* MSC has the version as _strnicmp */
#define strncasecmp _strnicmp #define strncasecmp _strnicmp
#elif !HAVE_STRNCASECMP #elif !HAVE_STRNCASECMP
#error You do not have strncasecmp on your system. #error You do not have strncasecmp on your system.
#endif /* HAVE_STRNCASECMP */ #endif /* HAVE_STRNCASECMP */
#if defined(_MSC_VER) && (_MSC_VER <= 1800)
/* VS2013 doesn't know about "inline" */
#define inline __inline
#elif defined(AIX_CC)
#define inline
#endif
/* The following helper functions are used to speed up parsing. They
* are faster than their ctype counterparts because they assume that
* the input is in ASCII and that the locale is set to "C". The
* compiler will also inline these functions, providing an additional
* speedup by saving on function calls.
*/
static inline int is_ws_char(char c)
{
return c == ' '
|| c == '\t'
|| c == '\n'
|| c == '\r';
}
static inline int is_hex_char(char c)
{
return (c >= '0' && c <= '9')
|| (c >= 'A' && c <= 'F')
|| (c >= 'a' && c <= 'f');
}
/* Use C99 NAN by default; if not available, nan("") should work too. */ /* Use C99 NAN by default; if not available, nan("") should work too. */
#ifndef NAN #ifndef NAN
#define NAN nan("") #define NAN nan("")
@@ -61,7 +89,8 @@
static const char json_null_str[] = "null"; static const char json_null_str[] = "null";
static const int json_null_str_len = sizeof(json_null_str) - 1; static const int json_null_str_len = sizeof(json_null_str) - 1;
static const char json_inf_str[] = "Infinity"; static const char json_inf_str[] = "Infinity";
static const char json_inf_str_lower[] = "infinity"; /* Swapped case "Infinity" to avoid need to call tolower() on input chars: */
static const char json_inf_str_invert[] = "iNFINITY";
static const unsigned int json_inf_str_len = sizeof(json_inf_str) - 1; static const unsigned int json_inf_str_len = sizeof(json_inf_str) - 1;
static const char json_nan_str[] = "NaN"; static const char json_nan_str[] = "NaN";
static const int json_nan_str_len = sizeof(json_nan_str) - 1; static const int json_nan_str_len = sizeof(json_nan_str) - 1;
@@ -87,7 +116,8 @@ static const char *json_tokener_errors[] = {
"invalid string sequence", "invalid string sequence",
"expected comment", "expected comment",
"invalid utf-8 string", "invalid utf-8 string",
"buffer size overflow" "buffer size overflow",
"out of memory"
}; };
/* clang-format on */ /* clang-format on */
@@ -115,8 +145,8 @@ enum json_tokener_error json_tokener_get_error(struct json_tokener *tok)
} }
/* Stuff for decoding unicode sequences */ /* Stuff for decoding unicode sequences */
#define IS_HIGH_SURROGATE(uc) (((uc)&0xFC00) == 0xD800) #define IS_HIGH_SURROGATE(uc) (((uc)&0xFFFFFC00) == 0xD800)
#define IS_LOW_SURROGATE(uc) (((uc)&0xFC00) == 0xDC00) #define IS_LOW_SURROGATE(uc) (((uc)&0xFFFFFC00) == 0xDC00)
#define DECODE_SURROGATE_PAIR(hi, lo) ((((hi)&0x3FF) << 10) + ((lo)&0x3FF) + 0x10000) #define DECODE_SURROGATE_PAIR(hi, lo) ((((hi)&0x3FF) << 10) + ((lo)&0x3FF) + 0x10000)
static unsigned char utf8_replacement_char[3] = {0xEF, 0xBF, 0xBD}; static unsigned char utf8_replacement_char[3] = {0xEF, 0xBF, 0xBD};
@@ -124,6 +154,9 @@ struct json_tokener *json_tokener_new_ex(int depth)
{ {
struct json_tokener *tok; struct json_tokener *tok;
if (depth < 1)
return NULL;
tok = (struct json_tokener *)calloc(1, sizeof(struct json_tokener)); tok = (struct json_tokener *)calloc(1, sizeof(struct json_tokener));
if (!tok) if (!tok)
return NULL; return NULL;
@@ -134,6 +167,12 @@ struct json_tokener *json_tokener_new_ex(int depth)
return NULL; return NULL;
} }
tok->pb = printbuf_new(); tok->pb = printbuf_new();
if (!tok->pb)
{
free(tok->stack);
free(tok);
return NULL;
}
tok->max_depth = depth; tok->max_depth = depth;
json_tokener_reset(tok); json_tokener_reset(tok);
return tok; return tok;
@@ -146,6 +185,8 @@ struct json_tokener *json_tokener_new(void)
void json_tokener_free(struct json_tokener *tok) void json_tokener_free(struct json_tokener *tok)
{ {
if (!tok)
return;
json_tokener_reset(tok); json_tokener_reset(tok);
if (tok->pb) if (tok->pb)
printbuf_free(tok->pb); printbuf_free(tok->pb);
@@ -190,7 +231,10 @@ struct json_object *json_tokener_parse_verbose(const char *str, enum json_tokene
tok = json_tokener_new(); tok = json_tokener_new();
if (!tok) if (!tok)
{
*error = json_tokener_error_memory;
return NULL; return NULL;
}
obj = json_tokener_parse_ex(tok, str, -1); obj = json_tokener_parse_ex(tok, str, -1);
*error = tok->err; *error = tok->err;
if (tok->err != json_tokener_success if (tok->err != json_tokener_success
@@ -250,11 +294,24 @@ struct json_object *json_tokener_parse_verbose(const char *str, enum json_tokene
/* ADVANCE_CHAR() macro: /* ADVANCE_CHAR() macro:
* Increments str & tok->char_offset. * Increments str & tok->char_offset.
* For convenience of existing conditionals, returns the old value of c (0 on eof) * For convenience of existing conditionals, returns the old value of c (0 on eof).
* Implicit inputs: c var * Implicit inputs: c var
*/ */
#define ADVANCE_CHAR(str, tok) (++(str), ((tok)->char_offset)++, c) #define ADVANCE_CHAR(str, tok) (++(str), ((tok)->char_offset)++, c)
/* printbuf_memappend_checked(p, s, l) macro:
* Add string s of length l to printbuffer p.
* If operation fails abort parse operation with memory error.
*/
#define printbuf_memappend_checked(p, s, l) \
do { \
if (printbuf_memappend((p), (s), (l)) < 0) \
{ \
tok->err = json_tokener_error_memory; \
goto out; \
} \
} while (0)
/* End optimization macro defs */ /* End optimization macro defs */
struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *str, int len) struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *str, int len)
@@ -288,13 +345,32 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
#ifdef HAVE_USELOCALE #ifdef HAVE_USELOCALE
{ {
#ifdef HAVE_DUPLOCALE
locale_t duploc = duplocale(oldlocale); locale_t duploc = duplocale(oldlocale);
newloc = newlocale(LC_NUMERIC_MASK, "C", duploc); if (duploc == NULL && errno == ENOMEM)
if (newloc == NULL)
{ {
freelocale(duploc); tok->err = json_tokener_error_memory;
return NULL; return NULL;
} }
newloc = newlocale(LC_NUMERIC_MASK, "C", duploc);
#else
newloc = newlocale(LC_NUMERIC_MASK, "C", oldlocale);
#endif
if (newloc == NULL)
{
tok->err = json_tokener_error_memory;
#ifdef HAVE_DUPLOCALE
freelocale(duploc);
#endif
return NULL;
}
#ifdef NEWLOCALE_NEEDS_FREELOCALE
#ifdef HAVE_DUPLOCALE
// Older versions of FreeBSD (<12.4) don't free the locale
// passed to newlocale(), so do it here
freelocale(duploc);
#endif
#endif
uselocale(newloc); uselocale(newloc);
} }
#elif defined(HAVE_SETLOCALE) #elif defined(HAVE_SETLOCALE)
@@ -302,7 +378,14 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
char *tmplocale; char *tmplocale;
tmplocale = setlocale(LC_NUMERIC, NULL); tmplocale = setlocale(LC_NUMERIC, NULL);
if (tmplocale) if (tmplocale)
{
oldlocale = strdup(tmplocale); oldlocale = strdup(tmplocale);
if (oldlocale == NULL)
{
tok->err = json_tokener_error_memory;
return NULL;
}
}
setlocale(LC_NUMERIC, "C"); setlocale(LC_NUMERIC, "C");
} }
#endif #endif
@@ -316,7 +399,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
case json_tokener_state_eatws: case json_tokener_state_eatws:
/* Advance until we change state */ /* Advance until we change state */
while (isspace((unsigned char)c)) while (is_ws_char(c))
{ {
if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok)))
goto out; goto out;
@@ -324,7 +407,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
if (c == '/' && !(tok->flags & JSON_TOKENER_STRICT)) if (c == '/' && !(tok->flags & JSON_TOKENER_STRICT))
{ {
printbuf_reset(tok->pb); printbuf_reset(tok->pb);
printbuf_memappend_fast(tok->pb, &c, 1); printbuf_memappend_checked(tok->pb, &c, 1);
state = json_tokener_state_comment_start; state = json_tokener_state_comment_start;
} }
else else
@@ -342,14 +425,20 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
saved_state = json_tokener_state_object_field_start; saved_state = json_tokener_state_object_field_start;
current = json_object_new_object(); current = json_object_new_object();
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
break; break;
case '[': case '[':
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
saved_state = json_tokener_state_array; saved_state = json_tokener_state_array;
current = json_object_new_array(); current = json_object_new_array();
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
break; break;
case 'I': case 'I':
case 'i': case 'i':
@@ -421,17 +510,15 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
* complicated with likely little performance benefit. * complicated with likely little performance benefit.
*/ */
int is_negative = 0; int is_negative = 0;
const char *_json_inf_str = json_inf_str;
if (!(tok->flags & JSON_TOKENER_STRICT))
_json_inf_str = json_inf_str_lower;
/* Note: tok->st_pos must be 0 when state is set to json_tokener_state_inf */ /* Note: tok->st_pos must be 0 when state is set to json_tokener_state_inf */
while (tok->st_pos < (int)json_inf_str_len) while (tok->st_pos < (int)json_inf_str_len)
{ {
char inf_char = *str; char inf_char = *str;
if (!(tok->flags & JSON_TOKENER_STRICT)) if (inf_char != json_inf_str[tok->st_pos] &&
inf_char = tolower((unsigned char)*str); ((tok->flags & JSON_TOKENER_STRICT) ||
if (inf_char != _json_inf_str[tok->st_pos]) inf_char != json_inf_str_invert[tok->st_pos])
)
{ {
tok->err = json_tokener_error_parse_unexpected; tok->err = json_tokener_error_parse_unexpected;
goto out; goto out;
@@ -454,7 +541,10 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
} }
current = json_object_new_double(is_negative ? -INFINITY : INFINITY); current = json_object_new_double(is_negative ? -INFINITY : INFINITY);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
goto redo_char; goto redo_char;
@@ -464,7 +554,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
int size; int size;
int size_nan; int size_nan;
printbuf_memappend_fast(tok->pb, &c, 1); printbuf_memappend_checked(tok->pb, &c, 1);
size = json_min(tok->st_pos + 1, json_null_str_len); size = json_min(tok->st_pos + 1, json_null_str_len);
size_nan = json_min(tok->st_pos + 1, json_nan_str_len); size_nan = json_min(tok->st_pos + 1, json_nan_str_len);
if ((!(tok->flags & JSON_TOKENER_STRICT) && if ((!(tok->flags & JSON_TOKENER_STRICT) &&
@@ -487,7 +577,10 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
current = json_object_new_double(NAN); current = json_object_new_double(NAN);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
goto redo_char; goto redo_char;
@@ -516,7 +609,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
tok->err = json_tokener_error_parse_comment; tok->err = json_tokener_error_parse_comment;
goto out; goto out;
} }
printbuf_memappend_fast(tok->pb, &c, 1); printbuf_memappend_checked(tok->pb, &c, 1);
break; break;
case json_tokener_state_comment: case json_tokener_state_comment:
@@ -527,12 +620,12 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok))
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
goto out; goto out;
} }
} }
printbuf_memappend_fast(tok->pb, case_start, 1 + str - case_start); printbuf_memappend_checked(tok->pb, case_start, 1 + str - case_start);
state = json_tokener_state_comment_end; state = json_tokener_state_comment_end;
} }
break; break;
@@ -545,19 +638,19 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok))
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
goto out; goto out;
} }
} }
printbuf_memappend_fast(tok->pb, case_start, str - case_start); printbuf_memappend_checked(tok->pb, case_start, str - case_start);
MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf);
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
} }
break; break;
case json_tokener_state_comment_end: case json_tokener_state_comment_end:
printbuf_memappend_fast(tok->pb, &c, 1); printbuf_memappend_checked(tok->pb, &c, 1);
if (c == '/') if (c == '/')
{ {
MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf);
@@ -577,28 +670,37 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
if (c == tok->quote_char) if (c == tok->quote_char)
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
current = current =
json_object_new_string_len(tok->pb->buf, tok->pb->bpos); json_object_new_string_len(tok->pb->buf, tok->pb->bpos);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
break; break;
} }
else if (c == '\\') else if (c == '\\')
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
saved_state = json_tokener_state_string; saved_state = json_tokener_state_string;
state = json_tokener_state_string_escape; state = json_tokener_state_string_escape;
break; break;
} }
else if ((tok->flags & JSON_TOKENER_STRICT) && (unsigned char)c <= 0x1f)
{
// Disallow control characters in strict mode
tok->err = json_tokener_error_parse_string;
goto out;
}
if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok))
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
goto out; goto out;
} }
} }
@@ -611,7 +713,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
case '"': case '"':
case '\\': case '\\':
case '/': case '/':
printbuf_memappend_fast(tok->pb, &c, 1); printbuf_memappend_checked(tok->pb, &c, 1);
state = saved_state; state = saved_state;
break; break;
case 'b': case 'b':
@@ -620,15 +722,15 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
case 't': case 't':
case 'f': case 'f':
if (c == 'b') if (c == 'b')
printbuf_memappend_fast(tok->pb, "\b", 1); printbuf_memappend_checked(tok->pb, "\b", 1);
else if (c == 'n') else if (c == 'n')
printbuf_memappend_fast(tok->pb, "\n", 1); printbuf_memappend_checked(tok->pb, "\n", 1);
else if (c == 'r') else if (c == 'r')
printbuf_memappend_fast(tok->pb, "\r", 1); printbuf_memappend_checked(tok->pb, "\r", 1);
else if (c == 't') else if (c == 't')
printbuf_memappend_fast(tok->pb, "\t", 1); printbuf_memappend_checked(tok->pb, "\t", 1);
else if (c == 'f') else if (c == 'f')
printbuf_memappend_fast(tok->pb, "\f", 1); printbuf_memappend_checked(tok->pb, "\f", 1);
state = saved_state; state = saved_state;
break; break;
case 'u': case 'u':
@@ -647,7 +749,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
/* Handle a 4-byte \uNNNN sequence, or two sequences if a surrogate pair */ /* Handle a 4-byte \uNNNN sequence, or two sequences if a surrogate pair */
while (1) while (1)
{ {
if (!c || !strchr(json_hex_chars, c)) if (!c || !is_hex_char(c))
{ {
tok->err = json_tokener_error_parse_string; tok->err = json_tokener_error_parse_string;
goto out; goto out;
@@ -688,8 +790,8 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
/* High surrogate was not followed by a low surrogate /* High surrogate was not followed by a low surrogate
* Replace the high and process the rest normally * Replace the high and process the rest normally
*/ */
printbuf_memappend_fast(tok->pb, printbuf_memappend_checked(tok->pb,
(char *)utf8_replacement_char, 3); (char *)utf8_replacement_char, 3);
} }
tok->high_surrogate = 0; tok->high_surrogate = 0;
} }
@@ -698,14 +800,14 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
unsigned char unescaped_utf[1]; unsigned char unescaped_utf[1];
unescaped_utf[0] = tok->ucs_char; unescaped_utf[0] = tok->ucs_char;
printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 1); printbuf_memappend_checked(tok->pb, (char *)unescaped_utf, 1);
} }
else if (tok->ucs_char < 0x800) else if (tok->ucs_char < 0x800)
{ {
unsigned char unescaped_utf[2]; unsigned char unescaped_utf[2];
unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6);
unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f);
printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 2); printbuf_memappend_checked(tok->pb, (char *)unescaped_utf, 2);
} }
else if (IS_HIGH_SURROGATE(tok->ucs_char)) else if (IS_HIGH_SURROGATE(tok->ucs_char))
{ {
@@ -714,7 +816,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
* we can't simply peek ahead here, because the * we can't simply peek ahead here, because the
* characters we need might not be passed to us * characters we need might not be passed to us
* until a subsequent call to json_tokener_parse. * until a subsequent call to json_tokener_parse.
* Instead, transition throug a couple of states. * Instead, transition through a couple of states.
* (now): * (now):
* _escape_unicode => _unicode_need_escape * _escape_unicode => _unicode_need_escape
* (see a '\\' char): * (see a '\\' char):
@@ -731,7 +833,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
else if (IS_LOW_SURROGATE(tok->ucs_char)) else if (IS_LOW_SURROGATE(tok->ucs_char))
{ {
/* Got a low surrogate not preceded by a high */ /* Got a low surrogate not preceded by a high */
printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); printbuf_memappend_checked(tok->pb, (char *)utf8_replacement_char, 3);
} }
else if (tok->ucs_char < 0x10000) else if (tok->ucs_char < 0x10000)
{ {
@@ -739,7 +841,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12);
unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f);
unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f);
printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 3); printbuf_memappend_checked(tok->pb, (char *)unescaped_utf, 3);
} }
else if (tok->ucs_char < 0x110000) else if (tok->ucs_char < 0x110000)
{ {
@@ -748,12 +850,12 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f);
unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f);
unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f);
printbuf_memappend_fast(tok->pb, (char *)unescaped_utf, 4); printbuf_memappend_checked(tok->pb, (char *)unescaped_utf, 4);
} }
else else
{ {
/* Don't know what we got--insert the replacement char */ /* Don't know what we got--insert the replacement char */
printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); printbuf_memappend_checked(tok->pb, (char *)utf8_replacement_char, 3);
} }
state = saved_state; // i.e. _state_string or _state_object_field state = saved_state; // i.e. _state_string or _state_object_field
} }
@@ -768,7 +870,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
* it. Put a replacement char in for the high surrogate * it. Put a replacement char in for the high surrogate
* and pop back up to _state_string or _state_object_field. * and pop back up to _state_string or _state_object_field.
*/ */
printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); printbuf_memappend_checked(tok->pb, (char *)utf8_replacement_char, 3);
tok->high_surrogate = 0; tok->high_surrogate = 0;
tok->ucs_char = 0; tok->ucs_char = 0;
tok->st_pos = 0; tok->st_pos = 0;
@@ -787,7 +889,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
* Put a replacement char in for the high surrogate * Put a replacement char in for the high surrogate
* and handle the escape sequence normally. * and handle the escape sequence normally.
*/ */
printbuf_memappend_fast(tok->pb, (char *)utf8_replacement_char, 3); printbuf_memappend_checked(tok->pb, (char *)utf8_replacement_char, 3);
tok->high_surrogate = 0; tok->high_surrogate = 0;
tok->ucs_char = 0; tok->ucs_char = 0;
tok->st_pos = 0; tok->st_pos = 0;
@@ -802,7 +904,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
case json_tokener_state_boolean: case json_tokener_state_boolean:
{ {
int size1, size2; int size1, size2;
printbuf_memappend_fast(tok->pb, &c, 1); printbuf_memappend_checked(tok->pb, &c, 1);
size1 = json_min(tok->st_pos + 1, json_true_str_len); size1 = json_min(tok->st_pos + 1, json_true_str_len);
size2 = json_min(tok->st_pos + 1, json_false_str_len); size2 = json_min(tok->st_pos + 1, json_false_str_len);
if ((!(tok->flags & JSON_TOKENER_STRICT) && if ((!(tok->flags & JSON_TOKENER_STRICT) &&
@@ -813,7 +915,10 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
current = json_object_new_boolean(1); current = json_object_new_boolean(1);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
goto redo_char; goto redo_char;
@@ -827,7 +932,10 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
current = json_object_new_boolean(0); current = json_object_new_boolean(0);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
saved_state = json_tokener_state_finish; saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
goto redo_char; goto redo_char;
@@ -907,7 +1015,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok))
{ {
printbuf_memappend_fast(tok->pb, case_start, case_len); printbuf_memappend_checked(tok->pb, case_start, case_len);
goto out; goto out;
} }
} }
@@ -916,17 +1024,17 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
it might have been intended to be, and return a potentially it might have been intended to be, and return a potentially
more understandable error right away. more understandable error right away.
However, if we're at the top-level, use the number as-is However, if we're at the top-level, use the number as-is
because c can be part of a new object to parse on the because c can be part of a new object to parse on the
next call to json_tokener_parse(). next call to json_tokener_parse().
*/ */
if (tok->depth > 0 && c != ',' && c != ']' && c != '}' && c != '/' && if (tok->depth > 0 && c != ',' && c != ']' && c != '}' && c != '/' &&
c != 'I' && c != 'i' && !isspace((unsigned char)c)) c != 'I' && c != 'i' && !is_ws_char(c))
{ {
tok->err = json_tokener_error_parse_number; tok->err = json_tokener_error_parse_number;
goto out; goto out;
} }
if (case_len > 0) if (case_len > 0)
printbuf_memappend_fast(tok->pb, case_start, case_len); printbuf_memappend_checked(tok->pb, case_start, case_len);
// Check for -Infinity // Check for -Infinity
if (tok->pb->buf[0] == '-' && case_len <= 1 && (c == 'i' || c == 'I')) if (tok->pb->buf[0] == '-' && case_len <= 1 && (c == 'i' || c == 'I'))
@@ -959,13 +1067,26 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
if (!tok->is_double && tok->pb->buf[0] == '-' && if (!tok->is_double && tok->pb->buf[0] == '-' &&
json_parse_int64(tok->pb->buf, &num64) == 0) json_parse_int64(tok->pb->buf, &num64) == 0)
{ {
if (errno == ERANGE && (tok->flags & JSON_TOKENER_STRICT))
{
tok->err = json_tokener_error_parse_number;
goto out;
}
current = json_object_new_int64(num64); current = json_object_new_int64(num64);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
} }
else if (!tok->is_double && tok->pb->buf[0] != '-' && else if (!tok->is_double && tok->pb->buf[0] != '-' &&
json_parse_uint64(tok->pb->buf, &numuint64) == 0) json_parse_uint64(tok->pb->buf, &numuint64) == 0)
{ {
if (errno == ERANGE && (tok->flags & JSON_TOKENER_STRICT))
{
tok->err = json_tokener_error_parse_number;
goto out;
}
if (numuint64 && tok->pb->buf[0] == '0' && if (numuint64 && tok->pb->buf[0] == '0' &&
(tok->flags & JSON_TOKENER_STRICT)) (tok->flags & JSON_TOKENER_STRICT))
{ {
@@ -977,13 +1098,19 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
num64 = (uint64_t)numuint64; num64 = (uint64_t)numuint64;
current = json_object_new_int64(num64); current = json_object_new_int64(num64);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
} }
else else
{ {
current = json_object_new_uint64(numuint64); current = json_object_new_uint64(numuint64);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
} }
} }
else if (tok->is_double && else if (tok->is_double &&
@@ -992,7 +1119,10 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
current = json_object_new_double_s(numd, tok->pb->buf); current = json_object_new_double_s(numd, tok->pb->buf);
if (current == NULL) if (current == NULL)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
} }
else else
{ {
@@ -1037,7 +1167,10 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
case json_tokener_state_array_add: case json_tokener_state_array_add:
if (json_object_array_add(current, obj) != 0) if (json_object_array_add(current, obj) != 0)
{
tok->err = json_tokener_error_memory;
goto out; goto out;
}
saved_state = json_tokener_state_array_sep; saved_state = json_tokener_state_array_sep;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
goto redo_char; goto redo_char;
@@ -1097,25 +1230,36 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{ {
if (c == tok->quote_char) if (c == tok->quote_char)
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
obj_field_name = strdup(tok->pb->buf); obj_field_name = strdup(tok->pb->buf);
if (obj_field_name == NULL)
{
tok->err = json_tokener_error_memory;
goto out;
}
saved_state = json_tokener_state_object_field_end; saved_state = json_tokener_state_object_field_end;
state = json_tokener_state_eatws; state = json_tokener_state_eatws;
break; break;
} }
else if (c == '\\') else if (c == '\\')
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
saved_state = json_tokener_state_object_field; saved_state = json_tokener_state_object_field;
state = json_tokener_state_string_escape; state = json_tokener_state_string_escape;
break; break;
} }
else if ((tok->flags & JSON_TOKENER_STRICT) && (unsigned char)c <= 0x1f)
{
// Disallow control characters in strict mode
tok->err = json_tokener_error_parse_string;
goto out;
}
if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok))
{ {
printbuf_memappend_fast(tok->pb, case_start, printbuf_memappend_checked(tok->pb, case_start,
str - case_start); str - case_start);
goto out; goto out;
} }
} }
@@ -1147,7 +1291,11 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
goto redo_char; goto redo_char;
case json_tokener_state_object_value_add: case json_tokener_state_object_value_add:
json_object_object_add(current, obj_field_name, obj); if (json_object_object_add(current, obj_field_name, obj) != 0)
{
tok->err = json_tokener_error_memory;
goto out;
}
free(obj_field_name); free(obj_field_name);
obj_field_name = NULL; obj_field_name = NULL;
saved_state = json_tokener_state_object_sep; saved_state = json_tokener_state_object_sep;

View File

@@ -40,7 +40,8 @@ enum json_tokener_error
json_tokener_error_parse_string, json_tokener_error_parse_string,
json_tokener_error_parse_comment, json_tokener_error_parse_comment,
json_tokener_error_parse_utf8_string, json_tokener_error_parse_utf8_string,
json_tokener_error_size json_tokener_error_size, /* A string longer than INT32_MAX was passed as input */
json_tokener_error_memory /* Failed to allocate memory */
}; };
/** /**
@@ -205,6 +206,7 @@ JSON_EXPORT struct json_tokener *json_tokener_new(void);
/** /**
* Allocate a new json_tokener with a custom max nesting depth. * Allocate a new json_tokener with a custom max nesting depth.
* The depth must be at least 1.
* @see JSON_TOKENER_DEFAULT_DEPTH * @see JSON_TOKENER_DEFAULT_DEPTH
*/ */
JSON_EXPORT struct json_tokener *json_tokener_new_ex(int depth); JSON_EXPORT struct json_tokener *json_tokener_new_ex(int depth);
@@ -229,7 +231,7 @@ JSON_EXPORT void json_tokener_reset(struct json_tokener *tok);
JSON_EXPORT struct json_object *json_tokener_parse(const char *str); JSON_EXPORT struct json_object *json_tokener_parse(const char *str);
/** /**
* Parser a json_object out of the string `str`, but if it fails * Parse a json_object out of the string `str`, but if it fails
* return the error in `*error`. * return the error in `*error`.
* @see json_tokener_parse() * @see json_tokener_parse()
* @see json_tokener_parse_ex() * @see json_tokener_parse_ex()

View File

@@ -18,7 +18,7 @@ extern "C" {
#endif #endif
#ifndef JSON_EXPORT #ifndef JSON_EXPORT
#if defined(_MSC_VER) #if defined(_MSC_VER) && defined(JSON_C_DLL)
#define JSON_EXPORT __declspec(dllexport) #define JSON_EXPORT __declspec(dllexport)
#else #else
#define JSON_EXPORT extern #define JSON_EXPORT extern

View File

@@ -14,7 +14,6 @@
#include "strerror_override.h" #include "strerror_override.h"
#include <ctype.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
@@ -38,13 +37,15 @@
#include <unistd.h> #include <unistd.h>
#endif /* HAVE_UNISTD_H */ #endif /* HAVE_UNISTD_H */
#ifdef WIN32 #ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif
#include <io.h> #include <io.h>
#include <windows.h> #include <windows.h>
#endif /* defined(WIN32) */ #endif /* defined(_WIN32) */
#if !defined(HAVE_OPEN) && defined(WIN32) #if !defined(HAVE_OPEN) && defined(_WIN32)
#define open _open #define open _open
#endif #endif
@@ -61,7 +62,7 @@ static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const
static char _last_err[256] = ""; static char _last_err[256] = "";
const char *json_util_get_last_err() const char *json_util_get_last_err(void)
{ {
if (_last_err[0] == '\0') if (_last_err[0] == '\0')
return NULL; return NULL;
@@ -86,13 +87,13 @@ struct json_object *json_object_from_fd_ex(int fd, int in_depth)
struct printbuf *pb; struct printbuf *pb;
struct json_object *obj; struct json_object *obj;
char buf[JSON_FILE_BUF_SIZE]; char buf[JSON_FILE_BUF_SIZE];
int ret; ssize_t ret;
int depth = JSON_TOKENER_DEFAULT_DEPTH; int depth = JSON_TOKENER_DEFAULT_DEPTH;
json_tokener *tok; json_tokener *tok;
if (!(pb = printbuf_new())) if (!(pb = printbuf_new()))
{ {
_json_c_set_last_err("json_object_from_file: printbuf_new failed\n"); _json_c_set_last_err("json_object_from_fd_ex: printbuf_new failed\n");
return NULL; return NULL;
} }
@@ -102,19 +103,29 @@ struct json_object *json_object_from_fd_ex(int fd, int in_depth)
if (!tok) if (!tok)
{ {
_json_c_set_last_err( _json_c_set_last_err(
"json_object_from_fd: unable to allocate json_tokener(depth=%d): %s\n", depth, "json_object_from_fd_ex: unable to allocate json_tokener(depth=%d): %s\n",
strerror(errno)); depth, strerror(errno));
printbuf_free(pb); printbuf_free(pb);
return NULL; return NULL;
} }
while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) while ((ret = read(fd, buf, sizeof(buf))) > 0)
{ {
printbuf_memappend(pb, buf, ret); if (printbuf_memappend(pb, buf, ret) < 0)
{
#if JSON_FILE_BUF_SIZE > INT_MAX
#error "Can't append more than INT_MAX bytes at a time"
#endif
_json_c_set_last_err(
"json_object_from_fd_ex: failed to printbuf_memappend after reading %d+%d bytes: %s", printbuf_length(pb), (int)ret, strerror(errno));
json_tokener_free(tok);
printbuf_free(pb);
return NULL;
}
} }
if (ret < 0) if (ret < 0)
{ {
_json_c_set_last_err("json_object_from_fd: error reading fd %d: %s\n", fd, _json_c_set_last_err("json_object_from_fd_ex: error reading fd %d: %s\n", fd,
strerror(errno)); strerror(errno));
json_tokener_free(tok); json_tokener_free(tok);
printbuf_free(pb); printbuf_free(pb);
@@ -138,8 +149,8 @@ struct json_object *json_object_from_file(const char *filename)
if ((fd = open(filename, O_RDONLY)) < 0) if ((fd = open(filename, O_RDONLY)) < 0)
{ {
_json_c_set_last_err("json_object_from_file: error opening file %s: %s\n", filename, _json_c_set_last_err("json_object_from_file: error opening file %s: %s\n",
strerror(errno)); filename, strerror(errno));
return NULL; return NULL;
} }
obj = json_object_from_fd(fd); obj = json_object_from_fd(fd);
@@ -156,14 +167,14 @@ int json_object_to_file_ext(const char *filename, struct json_object *obj, int f
if (!obj) if (!obj)
{ {
_json_c_set_last_err("json_object_to_file: object is null\n"); _json_c_set_last_err("json_object_to_file_ext: object is null\n");
return -1; return -1;
} }
if ((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) if ((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0)
{ {
_json_c_set_last_err("json_object_to_file: error opening file %s: %s\n", filename, _json_c_set_last_err("json_object_to_file_ext: error opening file %s: %s\n",
strerror(errno)); filename, strerror(errno));
return -1; return -1;
} }
ret = _json_object_to_fd(fd, obj, flags, filename); ret = _json_object_to_fd(fd, obj, flags, filename);
@@ -185,9 +196,9 @@ int json_object_to_fd(int fd, struct json_object *obj, int flags)
} }
static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename) static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename)
{ {
int ret; ssize_t ret;
const char *json_str; const char *json_str;
unsigned int wpos, wsize; size_t wpos, wsize;
filename = filename ? filename : "(fd)"; filename = filename ? filename : "(fd)";
@@ -196,20 +207,19 @@ static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const
return -1; return -1;
} }
/* CAW: probably unnecessary, but the most 64bit safe */ wsize = strlen(json_str);
wsize = (unsigned int)(strlen(json_str) & UINT_MAX);
wpos = 0; wpos = 0;
while (wpos < wsize) while (wpos < wsize)
{ {
if ((ret = write(fd, json_str + wpos, wsize - wpos)) < 0) if ((ret = write(fd, json_str + wpos, wsize - wpos)) < 0)
{ {
_json_c_set_last_err("json_object_to_file: error writing file %s: %s\n", _json_c_set_last_err("json_object_to_fd: error writing file %s: %s\n",
filename, strerror(errno)); filename, strerror(errno));
return -1; return -1;
} }
/* because of the above check for ret < 0, we can safely cast and add */ /* because of the above check for ret < 0, we can safely cast and add */
wpos += (unsigned int)ret; wpos += (size_t)ret;
} }
return 0; return 0;
@@ -239,7 +249,12 @@ int json_parse_int64(const char *buf, int64_t *retval)
val = strtoll(buf, &end, 10); val = strtoll(buf, &end, 10);
if (end != buf) if (end != buf)
*retval = val; *retval = val;
return ((val == 0 && errno != 0) || (end == buf)) ? 1 : 0; if ((val == 0 && errno != 0) || (end == buf))
{
errno = EINVAL;
return 1;
}
return 0;
} }
int json_parse_uint64(const char *buf, uint64_t *retval) int json_parse_uint64(const char *buf, uint64_t *retval)
@@ -256,7 +271,12 @@ int json_parse_uint64(const char *buf, uint64_t *retval)
val = strtoull(buf, &end, 10); val = strtoull(buf, &end, 10);
if (end != buf) if (end != buf)
*retval = val; *retval = val;
return ((val == 0 && errno != 0) || (end == buf)) ? 1 : 0; if ((val == 0 && errno != 0) || (end == buf))
{
errno = EINVAL;
return 1;
}
return 0;
} }
#ifndef HAVE_REALLOC #ifndef HAVE_REALLOC
@@ -289,8 +309,8 @@ const char *json_type_to_name(enum json_type o_type)
int o_type_int = (int)o_type; int o_type_int = (int)o_type;
if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name))
{ {
_json_c_set_last_err("json_type_to_name: type %d is out of range [0,%d]\n", o_type, _json_c_set_last_err("json_type_to_name: type %d is out of range [0,%u]\n", o_type,
NELEM(json_type_name)); (unsigned)NELEM(json_type_name));
return NULL; return NULL;
} }
return json_type_name[o_type]; return json_type_name[o_type];

View File

@@ -100,8 +100,17 @@ JSON_EXPORT int json_object_to_fd(int fd, struct json_object *obj, int flags);
*/ */
JSON_EXPORT const char *json_util_get_last_err(void); JSON_EXPORT const char *json_util_get_last_err(void);
/* these parsing helpers return zero on success */ /**
* A parsing helper for integer values. Returns 0 on success,
* with the parsed value assigned to *retval. Overflow/underflow
* are NOT considered errors, but errno will be set to ERANGE,
* just like the strtol/strtoll functions do.
*/
JSON_EXPORT int json_parse_int64(const char *buf, int64_t *retval); JSON_EXPORT int json_parse_int64(const char *buf, int64_t *retval);
/**
* A parsing help for integer values, providing one extra bit of
* magnitude beyond json_parse_int64().
*/
JSON_EXPORT int json_parse_uint64(const char *buf, uint64_t *retval); JSON_EXPORT int json_parse_uint64(const char *buf, uint64_t *retval);
/** /**
* @deprecated * @deprecated

37
libjson.rc Normal file
View File

@@ -0,0 +1,37 @@
#include <winver.h>
#include "json_c_version.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION JSON_C_MAJOR_VERSION,JSON_C_MINOR_VERSION,JSON_C_MICRO_VERSION,0
PRODUCTVERSION JSON_C_MAJOR_VERSION,JSON_C_MINOR_VERSION,JSON_C_MICRO_VERSION,0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_DOS_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "04090000" /* Lang = US English, Charset = ASCII */
BEGIN
VALUE "Comments", "A JSON implementation in C\0"
VALUE "CompanyName", "Eric Haszlakiewicz\0"
VALUE "FileDescription", "JSON-C\0"
VALUE "FileVersion", JSON_C_VERSION "\0"
VALUE "InternalName", "libjson-c.dll\0"
VALUE "LegalCopyright", "MIT License, 2009-2012\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "libjson-c.dll\0"
VALUE "ProductName", "JSON-C\0"
VALUE "ProductVersion", JSON_C_VERSION "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0 /* US English, ASCII */
END
END

View File

@@ -25,7 +25,9 @@
#endif #endif
#if defined(_MSC_VER) || defined(__MINGW32__) #if defined(_MSC_VER) || defined(__MINGW32__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h> /* Get InterlockedCompareExchange */ #include <windows.h> /* Get InterlockedCompareExchange */
#endif #endif
@@ -65,9 +67,9 @@ int lh_ptr_equal(const void *k1, const void *k2)
/* /*
* hashlittle from lookup3.c, by Bob Jenkins, May 2006, Public Domain. * hashlittle from lookup3.c, by Bob Jenkins, May 2006, Public Domain.
* http://burtleburtle.net/bob/c/lookup3.c * https://burtleburtle.net/bob/c/lookup3.c
* minor modifications to make functions static so no symbols are exported * minor modifications to make functions static so no symbols are exported
* minor mofifications to compile with -Werror * minor modifications to compile with -Werror
*/ */
/* /*
@@ -81,7 +83,7 @@ if SELF_TEST is defined. You can use this free for any purpose. It's in
the public domain. It has no warranty. the public domain. It has no warranty.
You probably want to use hashlittle(). hashlittle() and hashbig() You probably want to use hashlittle(). hashlittle() and hashbig()
hash byte arrays. hashlittle() is is faster than hashbig() on hash byte arrays. hashlittle() is faster than hashbig() on
little-endian machines. Intel and AMD are little-endian machines. little-endian machines. Intel and AMD are little-endian machines.
On second thought, you probably want hashlittle2(), which is identical to On second thought, you probably want hashlittle2(), which is identical to
hashlittle() except it returns two 32-bit hashes for the price of one. hashlittle() except it returns two 32-bit hashes for the price of one.
@@ -156,7 +158,7 @@ satisfy this are
14 9 3 7 17 3 14 9 3 7 17 3
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
for "differ" defined as + with a one-bit base and a two-bit delta. I for "differ" defined as + with a one-bit base and a two-bit delta. I
used http://burtleburtle.net/bob/hash/avalanche.html to choose used https://burtleburtle.net/bob/hash/avalanche.html to choose
the operations, constants, and arrangements of the variables. the operations, constants, and arrangements of the variables.
This does not achieve avalanche. There are input bits of (a,b,c) This does not achieve avalanche. There are input bits of (a,b,c)
@@ -285,9 +287,9 @@ static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
* rest of the string. Every machine with memory protection I've seen * rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will * does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash * still catch it and complain. The masking trick does make the hash
* noticably faster for short strings (like English words). * noticeably faster for short strings (like English words).
* AddressSanitizer is similarly picky about overrunning * AddressSanitizer is similarly picky about overrunning
* the buffer. (http://clang.llvm.org/docs/AddressSanitizer.html * the buffer. (https://clang.llvm.org/docs/AddressSanitizer.html)
*/ */
#ifdef VALGRIND #ifdef VALGRIND
#define PRECISE_MEMORY_ACCESS 1 #define PRECISE_MEMORY_ACCESS 1
@@ -439,8 +441,8 @@ static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
} }
/* clang-format on */ /* clang-format on */
/* a simple hash function similiar to what perl does for strings. /* a simple hash function similar to what perl does for strings.
* for good results, the string should not be excessivly large. * for good results, the string should not be excessively large.
*/ */
static unsigned long lh_perllike_str_hash(const void *k) static unsigned long lh_perllike_str_hash(const void *k)
{ {
@@ -465,7 +467,7 @@ static unsigned long lh_char_hash(const void *k)
if (random_seed == -1) if (random_seed == -1)
{ {
RANDOM_SEED_TYPE seed; RANDOM_SEED_TYPE seed;
/* we can't use -1 as it is the unitialized sentinel */ /* we can't use -1 as it is the uninitialized sentinel */
while ((seed = json_c_get_random_seed()) == -1) {} while ((seed = json_c_get_random_seed()) == -1) {}
#if SIZEOF_INT == 8 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 #if SIZEOF_INT == 8 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
#define USE_SYNC_COMPARE_AND_SWAP 1 #define USE_SYNC_COMPARE_AND_SWAP 1
@@ -481,12 +483,12 @@ static unsigned long lh_char_hash(const void *k)
#elif defined _MSC_VER || defined __MINGW32__ #elif defined _MSC_VER || defined __MINGW32__
InterlockedCompareExchange(&random_seed, seed, -1); InterlockedCompareExchange(&random_seed, seed, -1);
#else #else
//#warning "racy random seed initializtion if used by multiple threads" //#warning "racy random seed initialization if used by multiple threads"
random_seed = seed; /* potentially racy */ random_seed = seed; /* potentially racy */
#endif #endif
} }
return hashlittle((const char *)k, strlen((const char *)k), random_seed); return hashlittle((const char *)k, strlen((const char *)k), (uint32_t)random_seed);
} }
int lh_char_equal(const void *k1, const void *k2) int lh_char_equal(const void *k1, const void *k2)
@@ -546,7 +548,7 @@ int lh_table_resize(struct lh_table *t, int new_size)
unsigned long h = lh_get_hash(new_t, ent->k); unsigned long h = lh_get_hash(new_t, ent->k);
unsigned int opts = 0; unsigned int opts = 0;
if (ent->k_is_constant) if (ent->k_is_constant)
opts = JSON_C_OBJECT_KEY_IS_CONSTANT; opts = JSON_C_OBJECT_ADD_CONSTANT_KEY;
if (lh_table_insert_w_hash(new_t, ent->k, ent->v, h, opts) != 0) if (lh_table_insert_w_hash(new_t, ent->k, ent->v, h, opts) != 0)
{ {
lh_table_free(new_t); lh_table_free(new_t);
@@ -599,7 +601,7 @@ int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, con
} }
t->table[n].k = k; t->table[n].k = k;
t->table[n].k_is_constant = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT); t->table[n].k_is_constant = (opts & JSON_C_OBJECT_ADD_CONSTANT_KEY);
t->table[n].v = v; t->table[n].v = v;
t->count++; t->count++;

View File

@@ -16,8 +16,8 @@
* this is exposed by the json_object_get_object() function and within the * this is exposed by the json_object_get_object() function and within the
* json_object_iter type, it is not recommended for direct use. * json_object_iter type, it is not recommended for direct use.
*/ */
#ifndef _linkhash_h_ #ifndef _json_c_linkhash_h_
#define _linkhash_h_ #define _json_c_linkhash_h_
#include "json_object.h" #include "json_object.h"
@@ -80,64 +80,84 @@ typedef unsigned long(lh_hash_fn)(const void *k);
typedef int(lh_equal_fn)(const void *k1, const void *k2); typedef int(lh_equal_fn)(const void *k1, const void *k2);
/** /**
* An entry in the hash table * An entry in the hash table. Outside of linkhash.c, treat this as opaque.
*/ */
struct lh_entry struct lh_entry
{ {
/** /**
* The key. Use lh_entry_k() instead of accessing this directly. * The key.
* @deprecated Use lh_entry_k() instead of accessing this directly.
*/ */
const void *k; const void *k;
/** /**
* A flag for users of linkhash to know whether or not they * A flag for users of linkhash to know whether or not they
* need to free k. * need to free k.
* @deprecated use lh_entry_k_is_constant() instead.
*/ */
int k_is_constant; int k_is_constant;
/** /**
* The value. Use lh_entry_v() instead of accessing this directly. * The value.
* @deprecated Use lh_entry_v() instead of accessing this directly.
*/ */
const void *v; const void *v;
/** /**
* The next entry * The next entry.
* @deprecated Use lh_entry_next() instead of accessing this directly.
*/ */
struct lh_entry *next; struct lh_entry *next;
/** /**
* The previous entry. * The previous entry.
* @deprecated Use lh_entry_prev() instead of accessing this directly.
*/ */
struct lh_entry *prev; struct lh_entry *prev;
}; };
/** /**
* The hash table structure. * The hash table structure. Outside of linkhash.c, treat this as opaque.
*/ */
struct lh_table struct lh_table
{ {
/** /**
* Size of our hash. * Size of our hash.
* @deprecated do not use outside of linkhash.c
*/ */
int size; int size;
/** /**
* Numbers of entries. * Numbers of entries.
* @deprecated Use lh_table_length() instead.
*/ */
int count; int count;
/** /**
* The first entry. * The first entry.
* @deprecated Use lh_table_head() instead.
*/ */
struct lh_entry *head; struct lh_entry *head;
/** /**
* The last entry. * The last entry.
* @deprecated Do not use, may be removed in a future release.
*/ */
struct lh_entry *tail; struct lh_entry *tail;
/**
* Internal storage of the actual table of entries.
* @deprecated do not use outside of linkhash.c
*/
struct lh_entry *table; struct lh_entry *table;
/** /**
* A pointer onto the function responsible for freeing an entry. * A pointer to the function responsible for freeing an entry.
* @deprecated do not use outside of linkhash.c
*/ */
lh_entry_free_fn *free_fn; lh_entry_free_fn *free_fn;
/**
* @deprecated do not use outside of linkhash.c
*/
lh_hash_fn *hash_fn; lh_hash_fn *hash_fn;
/**
* @deprecated do not use outside of linkhash.c
*/
lh_equal_fn *equal_fn; lh_equal_fn *equal_fn;
}; };
typedef struct lh_table lh_table; typedef struct lh_table lh_table;
@@ -145,7 +165,7 @@ typedef struct lh_table lh_table;
/** /**
* Convenience list iterator. * Convenience list iterator.
*/ */
#define lh_foreach(table, entry) for (entry = table->head; entry; entry = entry->next) #define lh_foreach(table, entry) for (entry = lh_table_head(table); entry; entry = lh_entry_next(entry))
/** /**
* lh_foreach_safe allows calling of deletion routine while iterating. * lh_foreach_safe allows calling of deletion routine while iterating.
@@ -155,7 +175,7 @@ typedef struct lh_table lh_table;
* @param tmp a struct lh_entry * variable to hold a temporary pointer to the next element * @param tmp a struct lh_entry * variable to hold a temporary pointer to the next element
*/ */
#define lh_foreach_safe(table, entry, tmp) \ #define lh_foreach_safe(table, entry, tmp) \
for (entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) for (entry = lh_table_head(table); entry && ((tmp = lh_entry_next(entry)) || 1); entry = tmp)
/** /**
* Create a new linkhash table. * Create a new linkhash table.
@@ -231,7 +251,7 @@ extern int lh_table_insert(struct lh_table *t, const void *k, const void *v);
* @param k a pointer to the key to insert. * @param k a pointer to the key to insert.
* @param v a pointer to the value to insert. * @param v a pointer to the value to insert.
* @param h hash value of the key to insert * @param h hash value of the key to insert
* @param opts if set to JSON_C_OBJECT_KEY_IS_CONSTANT, sets lh_entry.k_is_constant * @param opts if set to JSON_C_OBJECT_ADD_CONSTANT_KEY, sets lh_entry.k_is_constant
* so t's free function knows to avoid freeing the key. * so t's free function knows to avoid freeing the key.
*/ */
extern int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, extern int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v,
@@ -295,6 +315,9 @@ extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e);
*/ */
extern int lh_table_delete(struct lh_table *t, const void *k); extern int lh_table_delete(struct lh_table *t, const void *k);
/**
* Return the number of entries in the table.
*/
extern int lh_table_length(struct lh_table *t); extern int lh_table_length(struct lh_table *t);
/** /**
@@ -311,17 +334,27 @@ int lh_table_resize(struct lh_table *t, int new_size);
/** /**
* @deprecated Don't use this outside of linkhash.h: * @deprecated Don't use this outside of linkhash.h:
*/ */
#if (defined(AIX_CC) || (defined(_MSC_VER) && (_MSC_VER <= 1800)) ) #if !defined (__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
/* VS2010 can't handle inline funcs, so skip it there */ /* C89 compilers like VS2010 can't handle inline funcs, so skip it there,
note: this also applies to -std=c89 in GCC! */
#define _LH_INLINE #define _LH_INLINE
#else #else
#define _LH_INLINE inline #define _LH_INLINE inline
#endif #endif
/**
* Return the first entry in the lh_table.
* @see lh_entry_next()
*/
static _LH_INLINE struct lh_entry *lh_table_head(const lh_table *t)
{
return t->head;
}
/** /**
* Calculate the hash of a key for a given table. * Calculate the hash of a key for a given table.
* *
* This is an exension to support functions that need to calculate * This is an extension to support functions that need to calculate
* the hash several times and allows them to do it just once and then pass * the hash several times and allows them to do it just once and then pass
* in the hash to all utility functions. Depending on use case, this can be a * in the hash to all utility functions. Depending on use case, this can be a
* considerable performance improvement. * considerable performance improvement.
@@ -334,7 +367,6 @@ static _LH_INLINE unsigned long lh_get_hash(const struct lh_table *t, const void
return t->hash_fn(k); return t->hash_fn(k);
} }
#undef _LH_INLINE
/** /**
* @deprecated Don't use this outside of linkhash.h: * @deprecated Don't use this outside of linkhash.h:
@@ -350,9 +382,22 @@ static _LH_INLINE unsigned long lh_get_hash(const struct lh_table *t, const void
* *
* lh_entry.k is const to indicate and help ensure that linkhash itself doesn't modify * lh_entry.k is const to indicate and help ensure that linkhash itself doesn't modify
* it, but callers are allowed to do what they want with it. * it, but callers are allowed to do what they want with it.
* See also lh_entry.k_is_constant * @see lh_entry_k_is_constant()
*/ */
#define lh_entry_k(entry) _LH_UNCONST((entry)->k) static _LH_INLINE void *lh_entry_k(const struct lh_entry *e)
{
return _LH_UNCONST(e->k);
}
/**
* Returns 1 if the key for the given entry is constant, and thus
* does not need to be freed when the lh_entry is freed.
* @see lh_table_insert_w_hash()
*/
static _LH_INLINE int lh_entry_k_is_constant(const struct lh_entry *e)
{
return e->k_is_constant;
}
/** /**
* Return a non-const version of lh_entry.v. * Return a non-const version of lh_entry.v.
@@ -360,7 +405,41 @@ static _LH_INLINE unsigned long lh_get_hash(const struct lh_table *t, const void
* v is const to indicate and help ensure that linkhash itself doesn't modify * v is const to indicate and help ensure that linkhash itself doesn't modify
* it, but callers are allowed to do what they want with it. * it, but callers are allowed to do what they want with it.
*/ */
#define lh_entry_v(entry) _LH_UNCONST((entry)->v) static _LH_INLINE void *lh_entry_v(const struct lh_entry *e)
{
return _LH_UNCONST(e->v);
}
/**
* Change the value for an entry. The caller is responsible for freeing
* the previous value.
*/
static _LH_INLINE void lh_entry_set_val(struct lh_entry *e, void *newval)
{
e->v = newval;
}
/**
* Return the next element, or NULL if there is no next element.
* @see lh_table_head()
* @see lh_entry_prev()
*/
static _LH_INLINE struct lh_entry *lh_entry_next(const struct lh_entry *e)
{
return e->next;
}
/**
* Return the previous element, or NULL if there is no previous element.
* @see lh_table_head()
* @see lh_entry_next()
*/
static _LH_INLINE struct lh_entry *lh_entry_prev(const struct lh_entry *e)
{
return e->prev;
}
#undef _LH_INLINE
#ifdef __cplusplus #ifdef __cplusplus
} }

284
meson.build Normal file
View File

@@ -0,0 +1,284 @@
project('json-c', 'c', version: '0.18.99',
meson_version: '>=0.54.0',
license: 'MIT',
default_options: ['buildtype=release', 'warning_level=2'])
cc = meson.get_compiler('c')
# Configuration header generation
conf_data = configuration_data()
jconf_data = configuration_data()
conf_data.set('VERSION', meson.project_version())
has_std_lib = cc.has_header('stdlib.h')
has_std_arg = cc.has_header('stdarg.h')
has_string = cc.has_header('string.h')
has_float = cc.has_header('float.h')
if has_std_lib and has_std_arg and has_string and has_float
conf_data.set('STDC_HEADERS', 1, description : 'Define to 1 if you have the ANSI C header files.')
endif
bsd_dep = dependency('libbsd', required: false)
headers = {
'bsd/stdlib.h': bsd_dep,
'dlfcn.h': [],
'endian.h': [],
'fcntl.h': [],
'inttypes.h': [],
'limits.h': [],
'locale.h': [],
'memory.h': [],
'stdarg.h': [],
'stdint.h': [],
'stdlib.h': [],
'string.h': [],
'strings.h': [],
'syslog.h': [],
'sys/cdefs.h': [],
'sys/param.h': [],
'sys/random.h': [],
'sys/resource.h': [],
'sys/stat.h': [],
'sys/types.h': [],
'unistd.h': [],
'xlocale.h': [],
}
foreach h, d : headers
if cc.has_header(h, dependencies: d)
conf_data.set('HAVE_@0@'.format(h.underscorify().to_upper()), 1, description : 'Define to 1 if you have the <@0@> header file'.format(h))
endif
endforeach
if cc.has_header('inttypes.h')
conf_data.set('JSON_C_HAVE_INTTYPES_H', 1, description : 'Define to 1 if you have the <inttypes.h> header file.')
jconf_data.set('JSON_C_HAVE_INTTYPES_H', 1, description : 'Define to 1 if you have the <inttypes.h> header file.')
endif
if cc.has_header('stdint.h')
conf_data.set('JSON_C_HAVE_STDINT_H', 1, description : 'Define to 1 if you have the <stdint.h> header file.')
jconf_data.set('JSON_C_HAVE_STDINT_H', 1, description : 'Define to 1 if you have the <stdint.h> header file.')
endif
funcs = [
'open',
'realloc',
'setlocale',
'strdup',
'strerror',
'uselocale',
'duplocale',
'vsyslog',
'getrandom',
'getrusage',
'strtoll',
'strtoull',
'arc4random',
'vasprintf',
]
if conf_data.has('HAVE_STRINGS_H')
funcs += ['strcasecmp', 'strncasecmp']
endif
foreach f : funcs
if cc.has_function(f)
conf_data.set('HAVE_@0@'.format(f.to_upper()), 1, description : 'Define to 1 if you have the `@0@` function.'.format(f))
endif
endforeach
foreach f : ['snprintf', 'vsnprintf', 'vprintf']
if cc.has_header_symbol('stdio.h', f)
conf_data.set('HAVE_@0@'.format(f.to_upper()), 1, description : 'Define to 1 if you have the `@0@` function.'.format(f))
endif
endforeach
if not conf_data.has('HAVE_VPRINTF') and cc.has_function('_doprnt')
conf_data.set('HAVE_DOPRNT', 1, description : 'Define to 1 if you have _doprnt but not vprintf.')
endif
if conf_data.has('HAVE_STRTOLL')
conf_data.set('json_c_strtoll', 'strtoll')
elif cc.has_function('_strtoi64', prefix : '#include <stdlib.h>')
conf_data.set('json_c_strtoll', '_strtoi64')
endif
if conf_data.has('HAVE_STRTOULL')
conf_data.set('json_c_strtoull', 'strtoull')
elif cc.has_function('_strtoui64', prefix : '#include <stdlib.h>')
conf_data.set('json_c_strtoull', '_strtoui64')
endif
decls = {
'INFINITY': 'math.h',
'isinf': 'math.h',
'isnan': 'math.h',
'NAN': 'math.h',
'_finite': 'float.h',
'_isnan': 'float.h',
}
foreach d, h : decls
if cc.has_header_symbol(h, d)
conf_data.set('HAVE_DECL_@0@'.format(d.to_upper()), 1, description : 'Define to 1 if you have the declaration of `@0@`'.format(d))
endif
endforeach
check_thread = cc.compiles('__thread int x;', name: 'Check for __thread support')
if check_thread
conf_data.set('HAVE___THREAD', 1)
conf_data.set('SPEC___THREAD', '__thread')
elif cc.get_argument_syntax() == 'msvc'
conf_data.set('SPEC___THREAD', '__declspec(thread)')
endif
gnu_warning_section_support = cc.compiles('''
extern void json_object_get();
__asm__(".section .gnu.json_object_get\n\t.ascii \"Please link against libjson-c instead of libjson\"\n\t.text");
int main(int c, char *v) { return 0; }
''', name: 'Check for GNU warning section support')
if gnu_warning_section_support
conf_data.set('HAS_GNU_WARNING_LONG', 1, description : 'Define to 1 if the compiler supports .gnu.warning sections.')
endif
atomic_builtin_support = cc.compiles('''
int main() {
int x = 0;
int i = __sync_add_and_fetch(&x, 1);
return x;
}
''',
name: 'Check for atomic builtins')
if atomic_builtin_support
conf_data.set('HAVE_ATOMIC_BUILTINS', 1, description : 'Define to 1 if the compiler supports atomic builtins.')
endif
if get_option('enable_rdrand')
conf_data.set('ENABLE_RDRAND', 1)
endif
if get_option('override_get_random_seed')
conf_data.set('OVERRIDE_GET_RANDOM_SEED', get_option('override_get_random_seed'))
endif
if get_option('enable_threading')
conf_data.set('ENABLE_THREADING', 1)
endif
if get_option('newlocale_needs_freelocale')
conf_data.set('NEWLOCALE_NEEDS_FREELOCALE', 1)
endif
conf_data.set('SIZEOF_INT', cc.sizeof('int'))
conf_data.set('SIZEOF_INT64_T', cc.sizeof('int64_t', prefix : '#include <stdint.h>'))
conf_data.set('SIZEOF_LONG', cc.sizeof('long'))
conf_data.set('SIZEOF_LONG_LONG', cc.sizeof('long long'))
conf_data.set('SIZEOF_SIZE_T', cc.sizeof('size_t', prefix : '#include <stddef.h>'))
if cc.get_argument_syntax() == 'msvc'
conf_data.set('SIZEOF_SSIZE_T', cc.sizeof('SSIZE_T', prefix : '#include <BaseTsd.h>\n#include <stddef.h>'))
else
conf_data.set('SIZEOF_SSIZE_T', cc.sizeof('ssize_t', prefix : '#include <sys/types.h>'))
endif
conf_data.set('PACKAGE_VERSION', meson.project_version())
conf_data.set('PROJECT_NAME', meson.project_name())
configure_header = configure_file(
output: 'config.h',
configuration: conf_data
)
json_configure_header = configure_file(
output: 'json_config.h',
configuration: jconf_data
)
jhconf_data = configuration_data()
jhconf_data.set('JSON_H_JSON_PATCH',
get_option('disable_json_patch') ? '' : '#include "json_patch.h"'
)
jhconf_data.set('JSON_H_JSON_POINTER',
get_option('disable_json_pointer') ? '' : '#include "json_pointer.h"'
)
json_header = configure_file(
input: 'json.h.cmakein',
output: 'json.h',
configuration: jhconf_data
)
# Platform-specific flags
add_project_arguments('-D_GNU_SOURCE', language: 'c')
if host_machine.system() == 'windows'
add_project_arguments('-DWIN32', language: 'c')
endif
# Compiler flags
message('target is ' + host_machine.system())
if host_machine.system() == 'windows'
# Cover any compiler on Windows attempting to use MSVC's standard library
add_project_arguments('-D_CRT_NONSTDC_NO_DEPRECATE', '-D_CRT_SECURE_NO_WARNINGS', language: 'c')
endif
add_project_arguments(cc.get_supported_arguments('-Wno-unused-parameter'), language : 'c')
sym = cc.get_supported_link_arguments(
'-Wl,--version-script,@0@/json-c.sym'.format(
meson.current_source_dir(),
),
)
# Source files
sources = files(
'arraylist.c', 'debug.c', 'json_c_version.c', 'json_object.c',
'json_object_iterator.c', 'json_tokener.c', 'json_util.c',
'json_visit.c', 'linkhash.c', 'printbuf.c', 'random_seed.c',
'strerror_override.c'
)
if not get_option('disable_json_pointer')
sources += files('json_pointer.c')
if not get_option('disable_json_patch')
sources += files('json_patch.c')
endif
endif
# Include directories
inc = include_directories('.')
# Build library
libjson = library('json-c',
sources,
include_directories: inc,
dependencies: bsd_dep,
install: true,
link_args: sym,
version: '5.4.0',
soversion: '5',
)
jsonc_dep = declare_dependency(link_with: libjson, include_directories: inc)
meson.override_dependency('json-c', jsonc_dep)
# Install headers
install_headers(
'arraylist.h', 'debug.h', 'json_c_version.h', 'json_inttypes.h',
'json_object.h', 'json_object_iterator.h', 'json_tokener.h',
'json_types.h', 'json_util.h', 'json_visit.h', 'linkhash.h',
'printbuf.h', json_configure_header, json_header
)
# Optional apps
if get_option('build_apps') and host_machine.system() != 'windows'
subdir('apps')
endif
# Optional tests
if get_option('buildtype') == 'debug'
subdir('tests')
endif

11
meson_options.txt Normal file
View File

@@ -0,0 +1,11 @@
option('disable_bsymbolic', type: 'boolean', value: false, description: 'Avoid linking with -Bsymbolic-function')
option('disable_thread_local_storage', type: 'boolean', value: false, description: 'Disable Thread-Local Storage')
option('enable_rdrand', type: 'boolean', value: false, description: 'Enable RDRAND Hardware RNG')
option('enable_threading', type: 'boolean', value: false, description: 'Enable partial threading support')
option('override_get_random_seed', type: 'boolean', value: false, description: 'Override json_c_get_random_seed()')
option('disable_extra_libs', type: 'boolean', value: false, description: 'Avoid linking extra libraries like libbsd')
option('disable_json_pointer', type: 'boolean', value: false, description: 'Disable JSON pointer support')
option('disable_json_patch', type: 'boolean', value: false, description: 'Disable JSON patch support')
option('newlocale_needs_freelocale', type: 'boolean', value: false, description: 'FreeBSD workaround for newlocale')
option('build_apps', type: 'boolean', value: true, description: 'Build command-line apps')

View File

@@ -10,11 +10,12 @@
* *
* Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved.
* The copyrights to the contents of this file are licensed under the MIT License * The copyrights to the contents of this file are licensed under the MIT License
* (http://www.opensource.org/licenses/mit-license.php) * (https://www.opensource.org/licenses/mit-license.php)
*/ */
#include "config.h" #include "config.h"
#include <errno.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -56,6 +57,8 @@ struct printbuf *printbuf_new(void)
* *
* If the current size is large enough, nothing is changed. * If the current size is large enough, nothing is changed.
* *
* If extension failed, errno is set to indicate the error.
*
* Note: this does not check the available space! The caller * Note: this does not check the available space! The caller
* is responsible for performing those calculations. * is responsible for performing those calculations.
*/ */
@@ -68,7 +71,10 @@ static int printbuf_extend(struct printbuf *p, int min_size)
return 0; return 0;
/* Prevent signed integer overflows with large buffers. */ /* Prevent signed integer overflows with large buffers. */
if (min_size > INT_MAX - 8) if (min_size > INT_MAX - 8)
{
errno = EFBIG;
return -1; return -1;
}
if (p->size > INT_MAX / 2) if (p->size > INT_MAX / 2)
new_size = min_size + 8; new_size = min_size + 8;
else { else {
@@ -77,7 +83,7 @@ static int printbuf_extend(struct printbuf *p, int min_size)
new_size = min_size + 8; new_size = min_size + 8;
} }
#ifdef PRINTBUF_DEBUG #ifdef PRINTBUF_DEBUG
MC_DEBUG("printbuf_memappend: realloc " MC_DEBUG("printbuf_extend: realloc "
"bpos=%d min_size=%d old_size=%d new_size=%d\n", "bpos=%d min_size=%d old_size=%d new_size=%d\n",
p->bpos, min_size, p->size, new_size); p->bpos, min_size, p->size, new_size);
#endif /* PRINTBUF_DEBUG */ #endif /* PRINTBUF_DEBUG */
@@ -91,8 +97,11 @@ static int printbuf_extend(struct printbuf *p, int min_size)
int printbuf_memappend(struct printbuf *p, const char *buf, int size) int printbuf_memappend(struct printbuf *p, const char *buf, int size)
{ {
/* Prevent signed integer overflows with large buffers. */ /* Prevent signed integer overflows with large buffers. */
if (size > INT_MAX - p->bpos - 1) if (size < 0 || size > INT_MAX - p->bpos - 1)
{
errno = EFBIG;
return -1; return -1;
}
if (p->size <= p->bpos + size + 1) if (p->size <= p->bpos + size + 1)
{ {
if (printbuf_extend(p, p->bpos + size + 1) < 0) if (printbuf_extend(p, p->bpos + size + 1) < 0)
@@ -111,8 +120,11 @@ int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len)
if (offset == -1) if (offset == -1)
offset = pb->bpos; offset = pb->bpos;
/* Prevent signed integer overflows with large buffers. */ /* Prevent signed integer overflows with large buffers. */
if (len > INT_MAX - offset) if (len < 0 || offset < -1 || len > INT_MAX - offset)
{
errno = EFBIG;
return -1; return -1;
}
size_needed = offset + len; size_needed = offset + len;
if (pb->size < size_needed) if (pb->size < size_needed)
{ {
@@ -120,6 +132,8 @@ int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len)
return -1; return -1;
} }
if (pb->bpos < offset)
memset(pb->buf + pb->bpos, '\0', offset - pb->bpos);
memset(pb->buf + offset, charvalue, len); memset(pb->buf + offset, charvalue, len);
if (pb->bpos < size_needed) if (pb->bpos < size_needed)
pb->bpos = size_needed; pb->bpos = size_needed;
@@ -134,16 +148,16 @@ int sprintbuf(struct printbuf *p, const char *msg, ...)
int size; int size;
char buf[128]; char buf[128];
/* user stack buffer first */ /* use stack buffer first */
va_start(ap, msg); va_start(ap, msg);
size = vsnprintf(buf, 128, msg, ap); size = vsnprintf(buf, 128, msg, ap);
va_end(ap); va_end(ap);
/* if string is greater than stack buffer, then use dynamic string /* if string is greater than stack buffer, then use dynamic string
* with vasprintf. Note: some implementation of vsnprintf return -1 * with vasprintf. Note: some implementations of vsnprintf return -1
* if output is truncated whereas some return the number of bytes that * if output is truncated whereas some return the number of bytes that
* would have been written - this code handles both cases. * would have been written - this code handles both cases.
*/ */
if (size == -1 || size > 127) if (size < 0 || size > 127)
{ {
va_start(ap, msg); va_start(ap, msg);
if ((size = vasprintf(&t, msg, ap)) < 0) if ((size = vasprintf(&t, msg, ap)) < 0)
@@ -152,15 +166,14 @@ int sprintbuf(struct printbuf *p, const char *msg, ...)
return -1; return -1;
} }
va_end(ap); va_end(ap);
printbuf_memappend(p, t, size); size = printbuf_memappend(p, t, size);
free(t); free(t);
return size;
} }
else else
{ {
printbuf_memappend(p, buf, size); size = printbuf_memappend(p, buf, size);
return size;
} }
return size;
} }
void printbuf_reset(struct printbuf *p) void printbuf_reset(struct printbuf *p)

View File

@@ -10,21 +10,21 @@
* *
* Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved.
* The copyrights to the contents of this file are licensed under the MIT License * The copyrights to the contents of this file are licensed under the MIT License
* (http://www.opensource.org/licenses/mit-license.php) * (https://www.opensource.org/licenses/mit-license.php)
*/ */
/** /**
* @file * @file
* @brief Internal string buffer handing. Unless you're writing a * @brief Internal string buffer handling. Unless you're writing a
* json_object_to_json_string_fn implementation for use with * json_object_to_json_string_fn implementation for use with
* json_object_set_serializer() direct use of this is not * json_object_set_serializer() direct use of this is not
* recommended. * recommended.
*/ */
#ifndef _printbuf_h_ #ifndef _json_c_printbuf_h_
#define _printbuf_h_ #define _json_c_printbuf_h_
#ifndef JSON_EXPORT #ifndef JSON_EXPORT
#if defined(_MSC_VER) #if defined(_MSC_VER) && defined(JSON_C_DLL)
#define JSON_EXPORT __declspec(dllexport) #define JSON_EXPORT __declspec(dllexport)
#else #else
#define JSON_EXPORT extern #define JSON_EXPORT extern

View File

@@ -13,9 +13,23 @@
#include "config.h" #include "config.h"
#include "strerror_override.h" #include "strerror_override.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_BSD_STDLIB_H
#include <bsd/stdlib.h>
#endif
#define DEBUG_SEED(s) #define DEBUG_SEED(s)
#if defined(__APPLE__) || defined(__unix__) || defined(__linux__)
#define HAVE_DEV_RANDOM 1
#endif
#ifdef HAVE_ARC4RANDOM
#undef HAVE_GETRANDOM
#undef HAVE_DEV_RANDOM
#undef HAVE_CRYPTGENRANDOM
#endif
#if defined ENABLE_RDRAND #if defined ENABLE_RDRAND
/* cpuid */ /* cpuid */
@@ -32,7 +46,7 @@ static void do_cpuid(int regs[], int h)
/* clang-format on */ /* clang-format on */
} }
#elif defined _MSC_VER #elif defined _MSC_VER && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))
#define HAS_X86_CPUID 1 #define HAS_X86_CPUID 1
#define do_cpuid __cpuid #define do_cpuid __cpuid
@@ -115,7 +129,7 @@ static int get_rdrand_seed(void)
#endif #endif
#if defined _MSC_VER #if defined _MSC_VER && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))
#if _MSC_VER >= 1700 #if _MSC_VER >= 1700
#define HAVE_RDRAND 1 #define HAVE_RDRAND 1
@@ -155,9 +169,45 @@ retry:
#endif /* defined ENABLE_RDRAND */ #endif /* defined ENABLE_RDRAND */
/* has_dev_urandom */ #ifdef HAVE_GETRANDOM
#if defined(__APPLE__) || defined(__unix__) || defined(__linux__) #include <stdlib.h>
#ifdef HAVE_SYS_RANDOM_H
#include <sys/random.h>
#endif
static int get_getrandom_seed(int *seed)
{
DEBUG_SEED("get_getrandom_seed");
ssize_t ret;
do
{
ret = getrandom(seed, sizeof(*seed), GRND_NONBLOCK);
} while ((ret == -1) && (errno == EINTR));
if (ret == -1)
{
if (errno == ENOSYS) /* syscall not available in kernel */
return -1;
if (errno == EAGAIN) /* entropy not yet initialized */
return -1;
fprintf(stderr, "error from getrandom(): %s", strerror(errno));
return -1;
}
if (ret != sizeof(*seed))
return -1;
return 0;
}
#endif /* defined HAVE_GETRANDOM */
/* get_dev_random_seed */
#ifdef HAVE_DEV_RANDOM
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
@@ -167,50 +217,44 @@ retry:
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#define HAVE_DEV_RANDOM 1
static const char *dev_random_file = "/dev/urandom"; static const char *dev_random_file = "/dev/urandom";
static int has_dev_urandom(void) static int get_dev_random_seed(int *seed)
{
struct stat buf;
if (stat(dev_random_file, &buf))
{
return 0;
}
return ((buf.st_mode & S_IFCHR) != 0);
}
/* get_dev_random_seed */
static int get_dev_random_seed(void)
{ {
DEBUG_SEED("get_dev_random_seed"); DEBUG_SEED("get_dev_random_seed");
struct stat buf;
if (stat(dev_random_file, &buf))
return -1;
if ((buf.st_mode & S_IFCHR) == 0)
return -1;
/* coverity[toctou] */
int fd = open(dev_random_file, O_RDONLY); int fd = open(dev_random_file, O_RDONLY);
if (fd < 0) if (fd < 0)
{ {
fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno));
exit(1); return -1;
} }
int r; ssize_t nread = read(fd, seed, sizeof(*seed));
ssize_t nread = read(fd, &r, sizeof(r));
if (nread != sizeof(r))
{
fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
exit(1);
}
close(fd); close(fd);
return r;
if (nread != sizeof(*seed))
{
fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
return -1;
}
return 0;
} }
#endif #endif
/* get_cryptgenrandom_seed */ /* get_cryptgenrandom_seed */
#ifdef WIN32 #ifdef _WIN32
#define HAVE_CRYPTGENRANDOM 1 #define HAVE_CRYPTGENRANDOM 1
@@ -226,13 +270,10 @@ static int get_dev_random_seed(void)
#pragma comment(lib, "advapi32.lib") #pragma comment(lib, "advapi32.lib")
#endif #endif
static int get_time_seed(void); static int get_cryptgenrandom_seed(int *seed)
static int get_cryptgenrandom_seed(void)
{ {
HCRYPTPROV hProvider = 0; HCRYPTPROV hProvider = 0;
DWORD dwFlags = CRYPT_VERIFYCONTEXT; DWORD dwFlags = CRYPT_VERIFYCONTEXT;
int r;
DEBUG_SEED("get_cryptgenrandom_seed"); DEBUG_SEED("get_cryptgenrandom_seed");
@@ -243,34 +284,37 @@ static int get_cryptgenrandom_seed(void)
if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags)) if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags))
{ {
fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError()); fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError());
r = get_time_seed(); return -1;
} }
else else
{ {
BOOL ret = CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r); BOOL ret = CryptGenRandom(hProvider, sizeof(*seed), (BYTE *)seed);
CryptReleaseContext(hProvider, 0); CryptReleaseContext(hProvider, 0);
if (!ret) if (!ret)
{ {
fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError()); fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError());
r = get_time_seed(); return -1;
} }
} }
return r; return 0;
} }
#endif #endif
/* get_time_seed */ /* get_time_seed */
#ifndef HAVE_ARC4RANDOM
#include <time.h> #include <time.h>
static int get_time_seed(void) static int get_time_seed(void)
{ {
DEBUG_SEED("get_time_seed"); DEBUG_SEED("get_time_seed");
return (int)time(NULL) * 433494437; /* coverity[store_truncates_time_t] */
return (unsigned)time(NULL) * 433494437;
} }
#endif
/* json_c_get_random_seed */ /* json_c_get_random_seed */
@@ -283,12 +327,31 @@ int json_c_get_random_seed(void)
if (has_rdrand()) if (has_rdrand())
return get_rdrand_seed(); return get_rdrand_seed();
#endif #endif
#ifdef HAVE_ARC4RANDOM
/* arc4random never fails, so use it if it's available */
return arc4random();
#else
#ifdef HAVE_GETRANDOM
{
int seed = 0;
if (get_getrandom_seed(&seed) == 0)
return seed;
}
#endif
#if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM #if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM
if (has_dev_urandom()) {
return get_dev_random_seed(); int seed = 0;
if (get_dev_random_seed(&seed) == 0)
return seed;
}
#endif #endif
#if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM #if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM
return get_cryptgenrandom_seed(); {
int seed = 0;
if (get_cryptgenrandom_seed(&seed) == 0)
return seed;
}
#endif #endif
return get_time_seed(); return get_time_seed();
#endif /* !HAVE_ARC4RANDOM */
} }

View File

@@ -35,7 +35,7 @@ static int json_c_snprintf(char *str, size_t size, const char *format, ...)
#define snprintf json_c_snprintf #define snprintf json_c_snprintf
#elif !defined(HAVE_SNPRINTF) /* !HAVE_SNPRINTF */ #elif !defined(HAVE_SNPRINTF) /* !HAVE_SNPRINTF */
#error Need vsnprintf! #error snprintf is required but was not found
#endif /* !HAVE_SNPRINTF && defined(WIN32) */ #endif /* !HAVE_SNPRINTF */
#endif /* __snprintf_compat_h */ #endif /* __snprintf_compat_h */

View File

@@ -94,7 +94,7 @@ char *_json_c_strerror(int errno_in)
} }
// It's not one of the known errno values, return the numeric value. // It's not one of the known errno values, return the numeric value.
for (ii = 0; errno_in > 10; errno_in /= 10, ii++) for (ii = 0; errno_in >= 10; errno_in /= 10, ii++)
{ {
digbuf[ii] = "0123456789"[(errno_in % 10)]; digbuf[ii] = "0123456789"[(errno_in % 10)];
} }
@@ -105,5 +105,6 @@ char *_json_c_strerror(int errno_in)
{ {
errno_buf[start_idx] = digbuf[ii]; errno_buf[start_idx] = digbuf[ii];
} }
errno_buf[start_idx] = '\0';
return errno_buf; return errno_buf;
} }

View File

@@ -1,14 +0,0 @@
#ifndef __json_strerror_override_private_h__
#define __json_strerror_override_private_h__
/**
* @file
* @brief Do not use, json-c internal, may be changed or removed at any time.
*/
#include "json_types.h"
/* Used by tests to get consistent output */
JSON_EXPORT int _json_c_strerror_enable;
#endif

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9...3.12) # see ../CMakeLists.txt for why
add_executable(test1Formatted test1.c parse_flags.c parse_flags.h) add_executable(test1Formatted test1.c parse_flags.c parse_flags.h)
target_compile_definitions(test1Formatted PRIVATE TEST_FORMATTED=1) target_compile_definitions(test1Formatted PRIVATE TEST_FORMATTED=1)
target_link_libraries(test1Formatted PRIVATE ${PROJECT_NAME}) target_link_libraries(test1Formatted PRIVATE ${PROJECT_NAME})
@@ -12,35 +12,42 @@ target_link_libraries(test2Formatted PRIVATE ${PROJECT_NAME})
include_directories(PUBLIC ${CMAKE_SOURCE_DIR}) include_directories(PUBLIC ${CMAKE_SOURCE_DIR})
foreach(TESTNAME set(ALL_TEST_NAMES
test1 test1
test2 test2
test4 test4
testReplaceExisting testReplaceExisting
test_cast test_cast
test_charcase test_charcase
test_compare test_compare
test_deep_copy test_deep_copy
test_double_serializer test_double_serializer
test_float test_float
test_int_add test_int_add
test_json_pointer test_int_get
test_locale test_locale
test_null test_null
test_parse test_parse
test_parse_int64 test_parse_int64
test_printbuf test_printbuf
test_set_serializer test_set_serializer
test_set_value test_set_value
test_util_file test_strerror
test_visit test_util_file
test_object_iterator) test_visit
test_object_iterator)
if (NOT DISABLE_JSON_POINTER)
set(ALL_TEST_NAMES ${ALL_TEST_NAMES} test_json_pointer)
set(ALL_TEST_NAMES ${ALL_TEST_NAMES} test_safe_json_pointer_set)
if (NOT DISABLE_JSON_PATCH)
set(ALL_TEST_NAMES ${ALL_TEST_NAMES} test_json_patch)
endif()
endif()
foreach(TESTNAME ${ALL_TEST_NAMES})
add_executable(${TESTNAME} ${TESTNAME}.c) add_executable(${TESTNAME} ${TESTNAME}.c)
if(${TESTNAME} STREQUAL test_util_file)
# For output consistency, we need _json_c_strerror() in some tests:
target_sources(${TESTNAME} PRIVATE ../strerror_override.c)
endif()
add_test(NAME ${TESTNAME} COMMAND ${PROJECT_SOURCE_DIR}/tests/${TESTNAME}.test) add_test(NAME ${TESTNAME} COMMAND ${PROJECT_SOURCE_DIR}/tests/${TESTNAME}.test)
# XXX using the non-target_ versions of these doesn't work :( # XXX using the non-target_ versions of these doesn't work :(

View File

@@ -0,0 +1,233 @@
[
{
"comment": "4.1. add with missing object",
"doc": { "q": { "bar": 2 } },
"patch": [ {"op": "add", "path": "/a/b", "value": 1} ],
"error":
"path /a does not exist -- missing objects are not created recursively"
},
{
"comment": "A.1. Adding an Object Member",
"doc": {
"foo": "bar"
},
"patch": [
{ "op": "add", "path": "/baz", "value": "qux" }
],
"expected": {
"baz": "qux",
"foo": "bar"
}
},
{
"comment": "A.2. Adding an Array Element",
"doc": {
"foo": [ "bar", "baz" ]
},
"patch": [
{ "op": "add", "path": "/foo/1", "value": "qux" }
],
"expected": {
"foo": [ "bar", "qux", "baz" ]
}
},
{
"comment": "A.3. Removing an Object Member",
"doc": {
"baz": "qux",
"foo": "bar"
},
"patch": [
{ "op": "remove", "path": "/baz" }
],
"expected": {
"foo": "bar"
}
},
{
"comment": "A.4. Removing an Array Element",
"doc": {
"foo": [ "bar", "qux", "baz" ]
},
"patch": [
{ "op": "remove", "path": "/foo/1" }
],
"expected": {
"foo": [ "bar", "baz" ]
}
},
{
"comment": "A.5. Replacing a Value",
"doc": {
"baz": "qux",
"foo": "bar"
},
"patch": [
{ "op": "replace", "path": "/baz", "value": "boo" }
],
"expected": {
"baz": "boo",
"foo": "bar"
}
},
{
"comment": "A.6. Moving a Value",
"doc": {
"foo": {
"bar": "baz",
"waldo": "fred"
},
"qux": {
"corge": "grault"
}
},
"patch": [
{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
],
"expected": {
"foo": {
"bar": "baz"
},
"qux": {
"corge": "grault",
"thud": "fred"
}
}
},
{
"comment": "A.7. Moving an Array Element",
"doc": {
"foo": [ "all", "grass", "cows", "eat" ]
},
"patch": [
{ "op": "move", "from": "/foo/1", "path": "/foo/3" }
],
"expected": {
"foo": [ "all", "cows", "eat", "grass" ]
}
},
{
"comment": "A.8. Testing a Value: Success",
"doc": {
"baz": "qux",
"foo": [ "a", 2, "c" ]
},
"patch": [
{ "op": "test", "path": "/baz", "value": "qux" },
{ "op": "test", "path": "/foo/1", "value": 2 }
],
"expected": {
"baz": "qux",
"foo": [ "a", 2, "c" ]
}
},
{
"comment": "A.9. Testing a Value: Error",
"doc": {
"baz": "qux"
},
"patch": [
{ "op": "test", "path": "/baz", "value": "bar" }
],
"error": "string not equivalent"
},
{
"comment": "A.10. Adding a nested Member Object",
"doc": {
"foo": "bar"
},
"patch": [
{ "op": "add", "path": "/child", "value": { "grandchild": { } } }
],
"expected": {
"foo": "bar",
"child": {
"grandchild": {
}
}
}
},
{
"comment": "A.11. Ignoring Unrecognized Elements",
"doc": {
"foo":"bar"
},
"patch": [
{ "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }
],
"expected": {
"foo":"bar",
"baz":"qux"
}
},
{
"comment": "A.12. Adding to a Non-existent Target",
"doc": {
"foo": "bar"
},
"patch": [
{ "op": "add", "path": "/baz/bat", "value": "qux" }
],
"error": "add to a non-existent target"
},
{
"comment": "A.13 Invalid JSON Patch Document",
"doc": {
"foo": "bar"
},
"patch": [
{ "op": "add", "path": "/baz", "value": "qux", "op": "remove" }
],
"error_wont_happen_in_jsonc": "operation has two 'op' members",
"error": "Did not find element referenced by path field"
},
{
"comment": "A.14. ~ Escape Ordering",
"doc": {
"/": 9,
"~1": 10
},
"patch": [{"op": "test", "path": "/~01", "value": 10}],
"expected": {
"/": 9,
"~1": 10
}
},
{
"comment": "A.15. Comparing Strings and Numbers",
"doc": {
"/": 9,
"~1": 10
},
"patch": [{"op": "test", "path": "/~01", "value": "10"}],
"error": "number is not equal to string"
},
{
"comment": "A.16. Adding an Array Value",
"doc": {
"foo": ["bar"]
},
"patch": [{ "op": "add", "path": "/foo/-", "value": ["abc", "def"] }],
"expected": {
"foo": ["bar", ["abc", "def"]]
}
}
]

540
tests/json_patch_tests.json Normal file
View File

@@ -0,0 +1,540 @@
[
{ "comment": "empty list, empty docs",
"doc": {},
"patch": [],
"expected": {} },
{ "comment": "empty patch list",
"doc": {"foo": 1},
"patch": [],
"expected": {"foo": 1} },
{ "comment": "rearrangements OK?",
"doc": {"foo": 1, "bar": 2},
"patch": [],
"expected": {"bar":2, "foo": 1} },
{ "comment": "rearrangements OK? How about one level down ... array",
"doc": [{"foo": 1, "bar": 2}],
"patch": [],
"expected": [{"bar":2, "foo": 1}] },
{ "comment": "rearrangements OK? How about one level down...",
"doc": {"foo":{"foo": 1, "bar": 2}},
"patch": [],
"expected": {"foo":{"bar":2, "foo": 1}} },
{ "comment": "add replaces any existing field",
"doc": {"foo": null},
"patch": [{"op": "add", "path": "/foo", "value":1}],
"expected": {"foo": 1} },
{ "comment": "toplevel array",
"doc": [],
"patch": [{"op": "add", "path": "/0", "value": "foo"}],
"expected": ["foo"] },
{ "comment": "toplevel array, no change",
"doc": ["foo"],
"patch": [],
"expected": ["foo"] },
{ "comment": "toplevel object, numeric string",
"doc": {},
"patch": [{"op": "add", "path": "/foo", "value": "1"}],
"expected": {"foo":"1"} },
{ "comment": "toplevel object, integer",
"doc": {},
"patch": [{"op": "add", "path": "/foo", "value": 1}],
"expected": {"foo":1} },
{ "comment": "Toplevel scalar values OK?",
"doc": "foo",
"patch": [{"op": "replace", "path": "", "value": "bar"}],
"expected": "bar"
},
{ "comment": "replace object document with array document?",
"doc": {},
"patch": [{"op": "add", "path": "", "value": []}],
"expected": [] },
{ "comment": "replace array document with object document?",
"doc": [],
"patch": [{"op": "add", "path": "", "value": {}}],
"expected": {} },
{ "comment": "append to root array document?",
"doc": [],
"patch": [{"op": "add", "path": "/-", "value": "hi"}],
"expected": ["hi"] },
{ "comment": "Add, / target",
"doc": {},
"patch": [ {"op": "add", "path": "/", "value":1 } ],
"expected": {"":1} },
{ "comment": "Add, /foo/ deep target (trailing slash)",
"doc": {"foo": {}},
"patch": [ {"op": "add", "path": "/foo/", "value":1 } ],
"expected": {"foo":{"": 1}} },
{ "comment": "Add composite value at top level",
"doc": {"foo": 1},
"patch": [{"op": "add", "path": "/bar", "value": [1, 2]}],
"expected": {"foo": 1, "bar": [1, 2]} },
{ "comment": "Add into composite value",
"doc": {"foo": 1, "baz": [{"qux": "hello"}]},
"patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}],
"expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} },
{ "doc": {"bar": [1, 2]},
"patch": [{"op": "add", "path": "/bar/8", "value": "5"}],
"error": "Out of bounds (upper)" },
{ "doc": {"bar": [1, 2]},
"patch": [{"op": "add", "path": "/bar/-1", "value": "5"}],
"error": "Out of bounds (lower)" },
{ "doc": {"foo": 1},
"patch": [{"op": "add", "path": "/bar", "value": true}],
"expected": {"foo": 1, "bar": true} },
{ "doc": {"foo": 1},
"patch": [{"op": "add", "path": "/bar", "value": false}],
"expected": {"foo": 1, "bar": false} },
{ "doc": {"foo": 1},
"patch": [{"op": "add", "path": "/bar", "value": null}],
"expected": {"foo": 1, "bar": null} },
{ "comment": "0 can be an array index or object element name",
"doc": {"foo": 1},
"patch": [{"op": "add", "path": "/0", "value": "bar"}],
"expected": {"foo": 1, "0": "bar" } },
{ "doc": ["foo"],
"patch": [{"op": "add", "path": "/1", "value": "bar"}],
"expected": ["foo", "bar"] },
{ "doc": ["foo", "sil"],
"patch": [{"op": "add", "path": "/1", "value": "bar"}],
"expected": ["foo", "bar", "sil"] },
{ "doc": ["foo", "sil"],
"patch": [{"op": "add", "path": "/0", "value": "bar"}],
"expected": ["bar", "foo", "sil"] },
{ "comment": "push item to array via last index + 1",
"doc": ["foo", "sil"],
"patch": [{"op":"add", "path": "/2", "value": "bar"}],
"expected": ["foo", "sil", "bar"] },
{ "comment": "add item to array at index > length should fail",
"doc": ["foo", "sil"],
"patch": [{"op":"add", "path": "/3", "value": "bar"}],
"error": "index is greater than number of items in array" },
{ "comment": "test against implementation-specific numeric parsing",
"doc": {"1e0": "foo"},
"patch": [{"op": "test", "path": "/1e0", "value": "foo"}],
"expected": {"1e0": "foo"} },
{ "comment": "test with bad number should fail",
"doc": ["foo", "bar"],
"patch": [{"op": "test", "path": "/1e0", "value": "bar"}],
"error": "test op shouldn't get array element 1" },
{ "doc": ["foo", "sil"],
"patch": [{"op": "add", "path": "/bar", "value": 42}],
"error": "Object operation on array target" },
{ "doc": ["foo", "sil"],
"patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}],
"expected": ["foo", ["bar", "baz"], "sil"],
"comment": "value in array add not flattened" },
{ "doc": {"foo": 1, "bar": [1, 2, 3, 4]},
"patch": [{"op": "remove", "path": "/bar"}],
"expected": {"foo": 1} },
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
"patch": [{"op": "remove", "path": "/baz/0/qux"}],
"expected": {"foo": 1, "baz": [{}]} },
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
"patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}],
"expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} },
{ "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]},
"patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}],
"expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} },
{ "doc": ["foo"],
"patch": [{"op": "replace", "path": "/0", "value": "bar"}],
"expected": ["bar"] },
{ "doc": [""],
"patch": [{"op": "replace", "path": "/0", "value": 0}],
"expected": [0] },
{ "doc": [""],
"patch": [{"op": "replace", "path": "/0", "value": true}],
"expected": [true] },
{ "doc": [""],
"patch": [{"op": "replace", "path": "/0", "value": false}],
"expected": [false] },
{ "doc": [""],
"patch": [{"op": "replace", "path": "/0", "value": null}],
"expected": [null] },
{ "doc": ["foo", "sil"],
"patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}],
"expected": ["foo", ["bar", "baz"]],
"comment": "value in array replace not flattened" },
{ "comment": "replace whole document",
"doc": {"foo": "bar"},
"patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}],
"expected": {"baz": "qux"} },
{ "comment": "add whole document, null",
"doc": {},
"Note1": "We can't pass null in to json_patch_apply, so start with _something_ and remove it",
"patch": [
{"op": "remove", "path": ""},
{"op": "add", "path": "", "value": {"baz": "qux"}}
],
"expected": {"baz": "qux"} },
{ "comment": "replace whole document, null",
"doc": {},
"Note1": "We can't pass null in to json_patch_apply, so start with _something_ and remove it",
"patch": [
{"op": "remove", "path": ""},
{"op": "replace", "path": "", "value": {"baz": "qux"}}
],
"error": "The spec says the target location must exist, so replacing a null document fails"
},
{ "comment": "remove whole document",
"doc": {"foo": "bar"},
"patch": [{"op": "remove", "path": ""}],
"expected": null },
{ "comment": "remove whole document",
"doc": {"foo": "bar"},
"patch": [{"op": "remove", "path": ""}],
"expected": null },
{ "comment": "remove whole document, array",
"doc": ["foo", "bar"],
"patch": [{"op": "remove", "path": ""}],
"expected": null },
{ "comment": "remove whole document, string",
"doc": "foo",
"patch": [{"op": "remove", "path": ""}],
"expected": null },
{ "comment": "remove whole document, null",
"doc": {},
"Note1": "We can't pass null in to json_patch_apply, so start with _something_ and remove it",
"patch": [
{"op": "remove", "path": ""},
{"op": "remove", "path": ""},
],
"error": "The spec says the target location must exist, so removing a null document fails"
},
{ "comment": "test replace with missing parent key should fail",
"doc": {"bar": "baz"},
"patch": [{"op": "replace", "path": "/foo/bar", "value": false}],
"error": "replace op should fail with missing parent key" },
{ "comment": "spurious patch properties",
"doc": {"foo": 1},
"patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}],
"expected": {"foo": 1} },
{ "doc": {"foo": null},
"patch": [{"op": "test", "path": "/foo", "value": null}],
"expected": {"foo": null},
"comment": "null value should be valid obj property" },
{ "doc": {"foo": null},
"patch": [{"op": "replace", "path": "/foo", "value": "truthy"}],
"expected": {"foo": "truthy"},
"comment": "null value should be valid obj property to be replaced with something truthy" },
{ "doc": {"foo": null},
"patch": [{"op": "move", "from": "/foo", "path": "/bar"}],
"expected": {"bar": null},
"comment": "null value should be valid obj property to be moved" },
{ "doc": {"foo": null},
"patch": [{"op": "copy", "from": "/foo", "path": "/bar"}],
"expected": {"foo": null, "bar": null},
"comment": "null value should be valid obj property to be copied" },
{ "doc": {"foo": null},
"patch": [{"op": "remove", "path": "/foo"}],
"expected": {},
"comment": "null value should be valid obj property to be removed" },
{ "doc": {"foo": "bar"},
"patch": [{"op": "replace", "path": "/foo", "value": null}],
"expected": {"foo": null},
"comment": "null value should still be valid obj property replace other value" },
{ "doc": {"foo": {"foo": 1, "bar": 2}},
"patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}],
"expected": {"foo": {"foo": 1, "bar": 2}},
"comment": "test should pass despite rearrangement" },
{ "doc": {"foo": [{"foo": 1, "bar": 2}]},
"patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}],
"expected": {"foo": [{"foo": 1, "bar": 2}]},
"comment": "test should pass despite (nested) rearrangement" },
{ "doc": {"foo": {"bar": [1, 2, 5, 4]}},
"patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}],
"expected": {"foo": {"bar": [1, 2, 5, 4]}},
"comment": "test should pass - no error" },
{ "doc": {"foo": {"bar": [1, 2, 5, 4]}},
"patch": [{"op": "test", "path": "/foo", "value": [1, 2]}],
"error": "test op should fail" },
{ "comment": "Test the whole document",
"doc": { "foo": 1 },
"patch": [{"op": "test", "path": "", "value": {"foo": 1}}],
"expected": { "foo": 1 } },
{ "comment": "Test the whole document, no match",
"doc": { "foo": 1 },
"patch": [{"op": "test", "path": "", "value": {"foo": 2}}],
"expected": { "foo": 1 },
"error": "Tested value does not match original doc" },
{ "comment": "Empty-string element",
"doc": { "": 1 },
"patch": [{"op": "test", "path": "/", "value": 1}],
"expected": { "": 1 } },
{ "doc": {
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
},
"patch": [{"op": "test", "path": "/foo", "value": ["bar", "baz"]},
{"op": "test", "path": "/foo/0", "value": "bar"},
{"op": "test", "path": "/", "value": 0},
{"op": "test", "path": "/a~1b", "value": 1},
{"op": "test", "path": "/c%d", "value": 2},
{"op": "test", "path": "/e^f", "value": 3},
{"op": "test", "path": "/g|h", "value": 4},
{"op": "test", "path": "/i\\j", "value": 5},
{"op": "test", "path": "/k\"l", "value": 6},
{"op": "test", "path": "/ ", "value": 7},
{"op": "test", "path": "/m~0n", "value": 8}],
"expected": {
"": 0,
" ": 7,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"foo": [
"bar",
"baz"
],
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
"m~n": 8
}
},
{ "comment": "Move to same location has no effect",
"doc": {"foo": 1},
"patch": [{"op": "move", "from": "/foo", "path": "/foo"}],
"expected": {"foo": 1} },
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
"patch": [{"op": "move", "from": "/foo", "path": "/bar"}],
"expected": {"baz": [{"qux": "hello"}], "bar": 1} },
{ "doc": {"baz": [{"qux": "hello"}], "bar": 1},
"patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}],
"expected": {"baz": [{}, "hello"], "bar": 1} },
{ "doc": {"baz": [{"qux": "hello"}], "bar": 1},
"patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}],
"expected": {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}} },
{ "comment": "replacing the root of the document is possible with add",
"doc": {"foo": "bar"},
"patch": [{"op": "add", "path": "", "value": {"baz": "qux"}}],
"expected": {"baz":"qux"}},
{ "comment": "Adding to \"/-\" adds to the end of the array",
"doc": [ 1, 2 ],
"patch": [ { "op": "add", "path": "/-", "value": { "foo": [ "bar", "baz" ] } } ],
"expected": [ 1, 2, { "foo": [ "bar", "baz" ] } ]},
{ "comment": "Adding to \"/-\" adds to the end of the array, even n levels down",
"doc": [ 1, 2, [ 3, [ 4, 5 ] ] ],
"patch": [ { "op": "add", "path": "/2/1/-", "value": { "foo": [ "bar", "baz" ] } } ],
"expected": [ 1, 2, [ 3, [ 4, 5, { "foo": [ "bar", "baz" ] } ] ] ]},
{ "comment": "test remove with bad number should fail",
"doc": {"foo": 1, "baz": [{"qux": "hello"}]},
"patch": [{"op": "remove", "path": "/baz/1e0/qux"}],
"error": "remove op shouldn't remove from array with bad number" },
{ "comment": "test remove on array",
"doc": [1, 2, 3, 4],
"patch": [{"op": "remove", "path": "/0"}],
"expected": [2, 3, 4] },
{ "comment": "test repeated removes",
"doc": [1, 2, 3, 4],
"patch": [{ "op": "remove", "path": "/1" },
{ "op": "remove", "path": "/2" }],
"expected": [1, 3] },
{ "comment": "test remove with bad index should fail",
"doc": [1, 2, 3, 4],
"patch": [{"op": "remove", "path": "/1e0"}],
"error": "remove op shouldn't remove from array with bad number" },
{ "comment": "test replace with bad number should fail",
"doc": [""],
"patch": [{"op": "replace", "path": "/1e0", "value": false}],
"error": "replace op shouldn't replace in array with bad number" },
{ "comment": "test copy with bad number should fail",
"doc": {"baz": [1,2,3], "bar": 1},
"patch": [{"op": "copy", "from": "/baz/1e0", "path": "/boo"}],
"error": "copy op shouldn't work with bad number" },
{ "comment": "test move with bad number should fail",
"doc": {"foo": 1, "baz": [1,2,3,4]},
"patch": [{"op": "move", "from": "/baz/1e0", "path": "/foo"}],
"error": "move op shouldn't work with bad number" },
{ "comment": "test add with bad number should fail",
"doc": ["foo", "sil"],
"patch": [{"op": "add", "path": "/1e0", "value": "bar"}],
"error": "add op shouldn't add to array with bad number" },
{ "comment": "missing 'path' parameter",
"doc": {},
"patch": [ { "op": "add", "value": "bar" } ],
"error": "missing 'path' parameter" },
{ "comment": "'path' parameter with null value",
"doc": {},
"patch": [ { "op": "add", "path": null, "value": "bar" } ],
"error": "null is not valid value for 'path'" },
{ "comment": "invalid JSON Pointer token",
"doc": {},
"patch": [ { "op": "add", "path": "foo", "value": "bar" } ],
"error": "JSON Pointer should start with a slash" },
{ "comment": "missing 'value' parameter to add",
"doc": [ 1 ],
"patch": [ { "op": "add", "path": "/-" } ],
"error": "missing 'value' parameter" },
{ "comment": "missing 'value' parameter to replace",
"doc": [ 1 ],
"patch": [ { "op": "replace", "path": "/0" } ],
"error": "missing 'value' parameter" },
{ "comment": "missing 'value' parameter to test",
"doc": [ null ],
"patch": [ { "op": "test", "path": "/0" } ],
"error": "missing 'value' parameter" },
{ "comment": "missing value parameter to test - where undef is falsy",
"doc": [ false ],
"patch": [ { "op": "test", "path": "/0" } ],
"error": "missing 'value' parameter" },
{ "comment": "missing from parameter to copy",
"doc": [ 1 ],
"patch": [ { "op": "copy", "path": "/-" } ],
"error": "missing 'from' parameter" },
{ "comment": "missing from location to copy",
"doc": { "foo": 1 },
"patch": [ { "op": "copy", "from": "/bar", "path": "/foo" } ],
"error": "missing 'from' location" },
{ "comment": "missing from parameter to move",
"doc": { "foo": 1 },
"patch": [ { "op": "move", "path": "" } ],
"error": "missing 'from' parameter" },
{ "comment": "missing from location to move",
"doc": { "foo": 1 },
"patch": [ { "op": "move", "from": "/bar", "path": "/foo" } ],
"error": "missing 'from' location" },
{ "comment": "duplicate ops, json-c parses this as op:move",
"doc": { "foo": "bar" },
"patch": [ { "op": "add", "path": "/baz", "value": "qux",
"op": "move", "from":"/foo" } ],
"error_wont_happen_in_jsonc": "patch has two 'op' members",
"expected": { "baz": "bar" }
},
{ "comment": "unrecognized op should fail",
"doc": {"foo": 1},
"patch": [{"op": "spam", "path": "/foo", "value": 1}],
"error": "Unrecognized op 'spam'" },
{ "comment": "test with bad array number that has leading zeros",
"doc": ["foo", "bar"],
"patch": [{"op": "test", "path": "/00", "value": "foo"}],
"error": "test op should reject the array value, it has leading zeros" },
{ "comment": "test with bad array number that has leading zeros",
"doc": ["foo", "bar"],
"patch": [{"op": "test", "path": "/01", "value": "bar"}],
"error": "test op should reject the array value, it has leading zeros" },
{ "comment": "Removing nonexistent field",
"doc": {"foo" : "bar"},
"patch": [{"op": "remove", "path": "/baz"}],
"error": "removing a nonexistent field should fail" },
{ "comment": "Removing deep nonexistent path",
"doc": {"foo" : "bar"},
"patch": [{"op": "remove", "path": "/missing1/missing2"}],
"error": "removing a nonexistent field should fail" },
{ "comment": "Removing nonexistent index",
"doc": ["foo", "bar"],
"patch": [{"op": "remove", "path": "/2"}],
"error": "removing a nonexistent index should fail" },
{ "comment": "Patch with different capitalisation than doc",
"doc": {"foo":"bar"},
"patch": [{"op": "add", "path": "/FOO", "value": "BAR"}],
"expected": {"foo": "bar", "FOO": "BAR"}
}
]

76
tests/meson.build Normal file
View File

@@ -0,0 +1,76 @@
#This hack is needed for Windows tests
test_lib = static_library(
'jsonctest',
objects: libjson.extract_all_objects(
recursive: false,
),
install: false,
)
test_deps = declare_dependency(
include_directories: '..',
link_with: test_lib,
)
# List of test sources and expected output files
test_cases = [
['test1', 'test1.expected'],
['test2', 'test2.expected'],
['test4', 'test4.expected'],
['testReplaceExisting', 'testReplaceExisting.expected'],
['test_cast', 'test_cast.expected'],
['test_charcase', 'test_charcase.expected'],
['test_compare', 'test_compare.expected'],
['test_deep_copy', 'test_deep_copy.expected'],
['test_double_serializer', 'test_double_serializer.expected'],
['test_float', 'test_float.expected'],
['test_int_add', 'test_int_add.expected'],
['test_int_get', 'test_int_get.expected'],
['test_locale', 'test_locale.expected'],
['test_null', 'test_null.expected'],
['test_parse', 'test_parse.expected'],
['test_parse_int64', 'test_parse_int64.expected'],
['test_printbuf', 'test_printbuf.expected'],
['test_set_serializer', 'test_set_serializer.expected'],
['test_set_value', 'test_set_value.expected'],
['test_strerror', 'test_strerror.expected'],
['test_util_file', 'test_util_file.expected'],
['test_visit', 'test_visit.expected'],
['test_object_iterator', 'test_object_iterator.expected'],
['test_json_pointer', 'test_json_pointer.expected'],
['test_safe_json_pointer_set', 'test_safe_json_pointer_set.expected'],
['test_json_patch', 'test_json_patch.expected'],
]
# Copy expected files and test data
expected_files = []
foreach t : test_cases
expected_files += t[1]
endforeach
foreach f : expected_files + ['valid.json', 'valid_nested.json', 'json_patch_spec_tests.json', 'json_patch_tests.json']
configure_file(input: f, output: f, copy: true)
endforeach
# Build and register tests
special_args = {
'test_json_patch': ['.'],
'test_util_file': ['.'],
}
testdir = meson.current_build_dir()
message('Test data directory: ' + testdir)
foreach t : test_cases
name = t[0]
expected = t[1]
exe = executable(name, name + '.c',
dependencies: test_deps
)
test(name, exe,
args: special_args.get(name, []),
env: ['EXPECTED_FILE=' + meson.current_build_dir() / expected],
workdir: testdir
)
endforeach

View File

@@ -1,3 +1,6 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>

View File

@@ -1,9 +1,15 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h> #include <assert.h>
#include <limits.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "config.h"
#include "json.h" #include "json.h"
#include "parse_flags.h" #include "parse_flags.h"
@@ -55,7 +61,7 @@ static const char *to_json_string(json_object *obj, int flags)
#endif #endif
json_object *make_array(void); json_object *make_array(void);
json_object *make_array() json_object *make_array(void)
{ {
json_object *my_array; json_object *my_array;
@@ -71,7 +77,7 @@ json_object *make_array()
} }
void test_array_del_idx(void); void test_array_del_idx(void);
void test_array_del_idx() void test_array_del_idx(void)
{ {
int rc; int rc;
size_t ii; size_t ii;
@@ -137,7 +143,7 @@ void test_array_del_idx()
} }
void test_array_list_expand_internal(void); void test_array_list_expand_internal(void);
void test_array_list_expand_internal() void test_array_list_expand_internal(void)
{ {
int rc; int rc;
size_t ii; size_t ii;
@@ -183,6 +189,42 @@ void test_array_list_expand_internal()
json_object_put(my_array); json_object_put(my_array);
} }
void test_array_insert_idx(void);
void test_array_insert_idx(void)
{
json_object *my_array;
struct json_object *jo1;
my_array = json_object_new_array();
json_object_array_add(my_array, json_object_new_int(1));
json_object_array_add(my_array, json_object_new_int(2));
json_object_array_add(my_array, json_object_new_int(5));
json_object_array_insert_idx(my_array, 2, json_object_new_int(4));
jo1 = json_tokener_parse("[1, 2, 4, 5]");
assert(1 == json_object_equal(my_array, jo1));
json_object_put(jo1);
json_object_array_insert_idx(my_array, 2, json_object_new_int(3));
jo1 = json_tokener_parse("[1, 2, 3, 4, 5]");
assert(1 == json_object_equal(my_array, jo1));
json_object_put(jo1);
json_object_array_insert_idx(my_array, 5, json_object_new_int(6));
jo1 = json_tokener_parse("[1, 2, 3, 4, 5, 6]");
assert(1 == json_object_equal(my_array, jo1));
json_object_put(jo1);
json_object_array_insert_idx(my_array, 7, json_object_new_int(8));
jo1 = json_tokener_parse("[1, 2, 3, 4, 5, 6, null, 8]");
assert(1 == json_object_equal(my_array, jo1));
json_object_put(jo1);
json_object_put(my_array);
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
json_object *my_string, *my_int, *my_null, *my_object, *my_array; json_object *my_string, *my_int, *my_null, *my_object, *my_array;
@@ -247,6 +289,8 @@ int main(int argc, char **argv)
json_object_put(my_array); json_object_put(my_array);
test_array_insert_idx();
test_array_del_idx(); test_array_del_idx();
test_array_list_expand_internal(); test_array_list_expand_internal();
@@ -305,8 +349,34 @@ int main(int argc, char **argv)
{ {
printf("\t%s: %s\n", key, json_object_to_json_string(val)); printf("\t%s: %s\n", key, json_object_to_json_string(val));
} }
json_object *empty_array = json_object_new_array();
json_object *empty_obj = json_object_new_object();
json_object_object_add(my_object, "empty_array", empty_array);
json_object_object_add(my_object, "empty_obj", empty_obj);
printf("my_object.to_string()=%s\n", json_object_to_json_string(my_object)); printf("my_object.to_string()=%s\n", json_object_to_json_string(my_object));
json_object_put(my_array);
my_array = json_object_new_array_ext(INT_MIN + 1);
if (my_array != NULL)
{
printf("ERROR: able to allocate an array of negative size!\n");
fflush(stdout);
json_object_put(my_array);
my_array = NULL;
}
#if SIZEOF_SIZE_T == SIZEOF_INT
my_array = json_object_new_array_ext(INT_MAX / 2 + 2);
if (my_array != NULL)
{
printf("ERROR: able to allocate an array of insufficient size!\n");
fflush(stdout);
json_object_put(my_array);
my_array = NULL;
}
#endif
json_object_put(my_string); json_object_put(my_string);
json_object_put(my_int); json_object_put(my_int);
json_object_put(my_null); json_object_put(my_null);

View File

@@ -75,4 +75,4 @@ my_object=
foo: "bar" foo: "bar"
bool0: false bool0: false
bool1: true bool1: true
my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true, "empty_array": [ ], "empty_obj": { } }

View File

@@ -75,4 +75,4 @@ my_object=
foo: "bar" foo: "bar"
bool0: false bool0: false
bool1: true bool1: true
my_object.to_string()={"abc":12,"foo":"bar","bool0":false,"bool1":true} my_object.to_string()={"abc":12,"foo":"bar","bool0":false,"bool1":true,"empty_array":[],"empty_obj":{}}

View File

@@ -97,5 +97,7 @@ my_object.to_string()={
"abc":12, "abc":12,
"foo":"bar", "foo":"bar",
"bool0":false, "bool0":false,
"bool1":true "bool1":true,
"empty_array":[],
"empty_obj":{}
} }

View File

@@ -75,4 +75,4 @@ my_object=
foo: "bar" foo: "bar"
bool0: false bool0: false
bool1: true bool1: true
my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true, "empty_array": [ ], "empty_obj": { } }

View File

@@ -97,5 +97,7 @@ my_object.to_string()={
"abc": 12, "abc": 12,
"foo": "bar", "foo": "bar",
"bool0": false, "bool0": false,
"bool1": true "bool1": true,
"empty_array": [],
"empty_obj": {}
} }

View File

@@ -97,5 +97,7 @@ my_object.to_string()={
"abc": 12, "abc": 12,
"foo": "bar", "foo": "bar",
"bool0": false, "bool0": false,
"bool1": true "bool1": true,
"empty_array": [],
"empty_obj": {}
} }

View File

@@ -1,3 +1,6 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>

View File

@@ -2,6 +2,9 @@
* gcc -o utf8 utf8.c -I/home/y/include -L./.libs -ljson * gcc -o utf8 utf8.c -I/home/y/include -L./.libs -ljson
*/ */
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "config.h" #include "config.h"
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
@@ -28,7 +31,7 @@ void print_hex(const char *s)
} }
static void test_lot_of_adds(void); static void test_lot_of_adds(void);
static void test_lot_of_adds() static void test_lot_of_adds(void)
{ {
int ii; int ii;
char key[50]; char key[50];

View File

@@ -1,3 +1,6 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>

View File

@@ -3,6 +3,9 @@
* Also checks the json_object_get_type and json_object_is_type functions. * Also checks the json_object_get_type and json_object_is_type functions.
*/ */
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -28,6 +31,12 @@ int main(int argc, char **argv)
\"int64_number\": 2147483649,\n\ \"int64_number\": 2147483649,\n\
\"negative_number\": -321321321,\n\ \"negative_number\": -321321321,\n\
\"a_null\": null,\n\ \"a_null\": null,\n\
\"empty_array\": [],\n\
\"nonempty_array\": [ 123 ],\n\
\"array_with_zero\": [ 0 ],\n\
\"empty_object\": {},\n\
\"nonempty_object\": { \"a\": 123 },\n\
\"nan\": NaN,\n\
}"; }";
/* Note: 2147483649 = INT_MAX + 2 */ /* Note: 2147483649 = INT_MAX + 2 */
/* Note: 9223372036854775809 = INT64_MAX + 2 */ /* Note: 9223372036854775809 = INT64_MAX + 2 */
@@ -49,6 +58,12 @@ int main(int argc, char **argv)
getit(new_obj, "int64_number"); getit(new_obj, "int64_number");
getit(new_obj, "negative_number"); getit(new_obj, "negative_number");
getit(new_obj, "a_null"); getit(new_obj, "a_null");
getit(new_obj, "empty_array");
getit(new_obj, "nonempty_array");
getit(new_obj, "array_with_zero");
getit(new_obj, "empty_object");
getit(new_obj, "nonempty_object");
getit(new_obj, "nan");
// Now check the behaviour of the json_object_is_type() function. // Now check the behaviour of the json_object_is_type() function.
printf("\n================================\n"); printf("\n================================\n");
@@ -62,6 +77,7 @@ int main(int argc, char **argv)
checktype(new_obj, "int64_number"); checktype(new_obj, "int64_number");
checktype(new_obj, "negative_number"); checktype(new_obj, "negative_number");
checktype(new_obj, "a_null"); checktype(new_obj, "a_null");
checktype(new_obj, "nan");
json_object_put(new_obj); json_object_put(new_obj);
@@ -84,7 +100,7 @@ static void getit(struct json_object *new_obj, const char *field)
printf("new_obj.%s json_object_get_double()=%f\n", field, json_object_get_double(o)); printf("new_obj.%s json_object_get_double()=%f\n", field, json_object_get_double(o));
} }
static void checktype_header() static void checktype_header(void)
{ {
printf("json_object_is_type: %s,%s,%s,%s,%s,%s,%s\n", json_type_to_name(json_type_null), printf("json_object_is_type: %s,%s,%s,%s,%s,%s,%s\n", json_type_to_name(json_type_null),
json_type_to_name(json_type_boolean), json_type_to_name(json_type_double), json_type_to_name(json_type_boolean), json_type_to_name(json_type_double),

View File

@@ -7,6 +7,12 @@ Parsed input: {
"int64_number": 2147483649, "int64_number": 2147483649,
"negative_number": -321321321, "negative_number": -321321321,
"a_null": null, "a_null": null,
"empty_array": [],
"nonempty_array": [ 123 ],
"array_with_zero": [ 0 ],
"empty_object": {},
"nonempty_object": { "a": 123 },
"nan": NaN,
} }
Result is not NULL Result is not NULL
new_obj.string_of_digits json_object_get_type()=string new_obj.string_of_digits json_object_get_type()=string
@@ -57,6 +63,42 @@ new_obj.a_null json_object_get_int64()=0
new_obj.a_null json_object_get_uint64()=0 new_obj.a_null json_object_get_uint64()=0
new_obj.a_null json_object_get_boolean()=0 new_obj.a_null json_object_get_boolean()=0
new_obj.a_null json_object_get_double()=0.000000 new_obj.a_null json_object_get_double()=0.000000
new_obj.empty_array json_object_get_type()=array
new_obj.empty_array json_object_get_int()=0
new_obj.empty_array json_object_get_int64()=0
new_obj.empty_array json_object_get_uint64()=0
new_obj.empty_array json_object_get_boolean()=0
new_obj.empty_array json_object_get_double()=0.000000
new_obj.nonempty_array json_object_get_type()=array
new_obj.nonempty_array json_object_get_int()=0
new_obj.nonempty_array json_object_get_int64()=0
new_obj.nonempty_array json_object_get_uint64()=0
new_obj.nonempty_array json_object_get_boolean()=0
new_obj.nonempty_array json_object_get_double()=0.000000
new_obj.array_with_zero json_object_get_type()=array
new_obj.array_with_zero json_object_get_int()=0
new_obj.array_with_zero json_object_get_int64()=0
new_obj.array_with_zero json_object_get_uint64()=0
new_obj.array_with_zero json_object_get_boolean()=0
new_obj.array_with_zero json_object_get_double()=0.000000
new_obj.empty_object json_object_get_type()=object
new_obj.empty_object json_object_get_int()=0
new_obj.empty_object json_object_get_int64()=0
new_obj.empty_object json_object_get_uint64()=0
new_obj.empty_object json_object_get_boolean()=0
new_obj.empty_object json_object_get_double()=0.000000
new_obj.nonempty_object json_object_get_type()=object
new_obj.nonempty_object json_object_get_int()=0
new_obj.nonempty_object json_object_get_int64()=0
new_obj.nonempty_object json_object_get_uint64()=0
new_obj.nonempty_object json_object_get_boolean()=0
new_obj.nonempty_object json_object_get_double()=0.000000
new_obj.nan json_object_get_type()=double
new_obj.nan json_object_get_int()=-2147483648
new_obj.nan json_object_get_int64()=-9223372036854775808
new_obj.nan json_object_get_uint64()=0
new_obj.nan json_object_get_boolean()=1
new_obj.nan json_object_get_double()=nan
================================ ================================
json_object_is_type: null,boolean,double,int,object,array,string json_object_is_type: null,boolean,double,int,object,array,string
@@ -69,3 +111,4 @@ new_obj.boolean_false : 0,1,0,0,0,0,0
new_obj.int64_number : 0,0,0,1,0,0,0 new_obj.int64_number : 0,0,0,1,0,0,0
new_obj.negative_number : 0,0,0,1,0,0,0 new_obj.negative_number : 0,0,0,1,0,0,0
new_obj.a_null : 1,0,0,0,0,0,0 new_obj.a_null : 1,0,0,0,0,0,0
new_obj.nan : 0,0,1,0,0,0,0

View File

@@ -1,3 +1,6 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h> #include <assert.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
@@ -19,7 +22,7 @@ int main(int argc, char **argv)
} }
/* make sure only lowercase forms are parsed in strict mode */ /* make sure only lowercase forms are parsed in strict mode */
static void test_case_parse() static void test_case_parse(void)
{ {
struct json_tokener *tok; struct json_tokener *tok;
json_object *new_obj; json_object *new_obj;

View File

@@ -2,6 +2,9 @@
* Tests if json_object_equal behaves correct. * Tests if json_object_equal behaves correct.
*/ */
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -9,7 +12,7 @@
#include "json_inttypes.h" #include "json_inttypes.h"
#include "json_object.h" #include "json_object.h"
int main() int main(int argc, char **argv)
{ {
/* integer tests */ /* integer tests */
struct json_object *int1 = json_object_new_int(0); struct json_object *int1 = json_object_new_int(0);

View File

@@ -22,7 +22,7 @@ static const char *json_str1 =
" \"number\": 16446744073709551615," " \"number\": 16446744073709551615,"
" \"title\": \"S\"," " \"title\": \"S\","
" \"null_obj\": null, " " \"null_obj\": null, "
" \"exixt\": false," " \"exist\": false,"
" \"quantity\":20," " \"quantity\":20,"
" \"univalent\":19.8," " \"univalent\":19.8,"
" \"GlossList\": {" " \"GlossList\": {"
@@ -136,7 +136,7 @@ int main(int argc, char **argv)
assert(0 == json_object_deep_copy(src2, &dst2, NULL)); assert(0 == json_object_deep_copy(src2, &dst2, NULL));
assert(0 == json_object_deep_copy(src3, &dst3, NULL)); assert(0 == json_object_deep_copy(src3, &dst3, NULL));
printf("PASSED - all json_object_deep_copy() returned succesful\n"); printf("PASSED - all json_object_deep_copy() returned successful\n");
assert(-1 == json_object_deep_copy(src1, &dst1, NULL)); assert(-1 == json_object_deep_copy(src1, &dst1, NULL));
assert(errno == EINVAL); assert(errno == EINVAL);
@@ -151,7 +151,7 @@ int main(int argc, char **argv)
assert(1 == json_object_equal(src2, dst2)); assert(1 == json_object_equal(src2, dst2));
assert(1 == json_object_equal(src3, dst3)); assert(1 == json_object_equal(src3, dst3));
printf("PASSED - all json_object_equal() tests returned succesful\n"); printf("PASSED - all json_object_equal() tests returned successful\n");
assert(0 == strcmp(json_object_to_json_string_ext(src1, JSON_C_TO_STRING_PRETTY), assert(0 == strcmp(json_object_to_json_string_ext(src1, JSON_C_TO_STRING_PRETTY),
json_object_to_json_string_ext(dst1, JSON_C_TO_STRING_PRETTY))); json_object_to_json_string_ext(dst1, JSON_C_TO_STRING_PRETTY)));

View File

@@ -1,7 +1,7 @@
PASSED - loaded input data PASSED - loaded input data
PASSED - all json_object_deep_copy() returned succesful PASSED - all json_object_deep_copy() returned successful
PASSED - all json_object_deep_copy() returned EINVAL for non-null pointer PASSED - all json_object_deep_copy() returned EINVAL for non-null pointer
PASSED - all json_object_equal() tests returned succesful PASSED - all json_object_equal() tests returned successful
PASSED - comparison of string output PASSED - comparison of string output
PASSED - trying to overrwrite an object that has refcount > 1 PASSED - trying to overrwrite an object that has refcount > 1
Printing JSON objects for visual inspection Printing JSON objects for visual inspection
@@ -14,7 +14,7 @@ Printing JSON objects for visual inspection
"number":16446744073709551615, "number":16446744073709551615,
"title":"S", "title":"S",
"null_obj":null, "null_obj":null,
"exixt":false, "exist":false,
"quantity":20, "quantity":20,
"univalent":19.8, "univalent":19.8,
"GlossList":{ "GlossList":{

View File

@@ -2,6 +2,9 @@
* Tests if the format string for double serialization is handled correctly * Tests if the format string for double serialization is handled correctly
*/ */
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
@@ -11,7 +14,7 @@
/* Avoid compiler warnings about diving by constant zero */ /* Avoid compiler warnings about diving by constant zero */
double zero_dot_zero = 0.0; double zero_dot_zero = 0.0;
int main() int main(int argc, char **argv)
{ {
struct json_object *obj = json_object_new_double(0.5); struct json_object *obj = json_object_new_double(0.5);
char udata[] = "test"; char udata[] = "test";

View File

@@ -1,5 +1,8 @@
/* Copyright (C) 2016 by Rainer Gerhards /* Copyright (C) 2016 by Rainer Gerhards
* Released under ASL 2.0 */ * Released under ASL 2.0 */
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "config.h" #include "config.h"
#include "json_object.h" #include "json_object.h"
#include "json_tokener.h" #include "json_tokener.h"

View File

@@ -1,3 +1,6 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>

80
tests/test_int_get.c Normal file
View File

@@ -0,0 +1,80 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <math.h>
#include "json.h"
#define I32_MAX_S "2147483647"
#define I32_MIN_S "-2147483648"
#define I64_MAX_S "9223372036854775807"
#define I64_OVER "9223372036854775808"
#define I64_MIN_S "-9223372036854775808"
#define I64_UNDER "-9223372036854775809"
#define U64_MAX_S "18446744073709551615"
#define U64_OUT_S "18446744073709551616"
#define CHECK_GET(GET_F, J, EXPECTED) { struct json_object *jtmp = J; errno = 0; assert(GET_F(jtmp) == EXPECTED); json_object_put(jtmp); }
#define CHECK_GET_INT(J, EXPECTED) CHECK_GET(json_object_get_int, J, EXPECTED)
#define CHECK_GET_INT64(J, EXPECTED) CHECK_GET(json_object_get_int64, J, EXPECTED)
#define CHECK_GET_UINT64(J, EXPECTED) CHECK_GET(json_object_get_uint64, J, EXPECTED)
#define CHECK_BASE(J, EXPECTED) CHECK_GET_INT(J, EXPECTED); CHECK_GET_INT64(J, EXPECTED); CHECK_GET_UINT64(J, EXPECTED)
#define N_INT json_object_new_int
#define N_I64 json_object_new_int64
#define N_U64 json_object_new_uint64
#define N_STR json_object_new_string
#define N_DBL json_object_new_double
int main(int argc, char **argv)
{
CHECK_BASE(N_INT(5), 5);
CHECK_BASE(N_INT(0), 0);
CHECK_BASE(N_STR("0"), 0);
CHECK_BASE(N_STR("00000"), 0);
CHECK_BASE(N_STR("000004568789"), 4568789);
CHECK_BASE(N_STR("0xFF"), 0 && errno == 0); // Hex-string values being parsed as 0 is the intended behavior
CHECK_BASE(N_STR("333this_seems_a_valid_string"), 333);
CHECK_BASE(N_STR("this_is_not_a_number"), 0 && errno == EINVAL);
CHECK_BASE(N_STR("B0"), 0 && errno == EINVAL);
printf("BASE CHECK PASSED\n");
CHECK_GET_INT(N_I64(INT32_MAX), INT32_MAX && errno == 0);
CHECK_GET_INT(N_I64(INT32_MIN), INT32_MIN && errno == 0);
CHECK_GET_INT(N_I64(INT64_MAX), INT32_MAX && errno == ERANGE);
CHECK_GET_INT(N_I64(INT64_MIN), INT32_MIN && errno == ERANGE);
CHECK_GET_INT(N_STR(I32_MAX_S), INT32_MAX && errno == 0);
CHECK_GET_INT(N_STR(I32_MIN_S), INT32_MIN && errno == 0);
CHECK_GET_INT(N_STR(I64_MAX_S), INT32_MAX && errno == ERANGE);
CHECK_GET_INT(N_STR(I64_MIN_S), INT32_MIN && errno == ERANGE);
CHECK_GET_INT(N_DBL(INFINITY), INT32_MAX && errno == ERANGE);
CHECK_GET_INT(N_DBL(-INFINITY), INT32_MIN && errno == ERANGE);
CHECK_GET_INT(N_DBL(NAN), INT32_MIN && errno == EINVAL);
printf("INT GET PASSED\n");
CHECK_GET_INT64(N_I64(INT64_MAX), INT64_MAX && errno == 0);
CHECK_GET_INT64(N_I64(INT64_MIN), INT64_MIN && errno == 0);
CHECK_GET_INT64(N_STR(I64_MAX_S), INT64_MAX && errno == 0);
CHECK_GET_INT64(N_STR(I64_MIN_S), INT64_MIN && errno == 0);
CHECK_GET_INT64(N_STR(I64_OVER), INT64_MAX && errno == ERANGE);
CHECK_GET_INT64(N_STR(I64_UNDER), INT64_MIN && errno == ERANGE);
CHECK_GET_INT64(N_DBL(INFINITY), INT64_MAX && errno == ERANGE);
CHECK_GET_INT64(N_DBL(-INFINITY), INT64_MIN && errno == ERANGE);
CHECK_GET_INT64(N_DBL(NAN), INT64_MIN && errno == EINVAL);
printf("INT64 GET PASSED\n");
CHECK_GET_UINT64(N_U64(UINT64_MAX), UINT64_MAX && errno == 0);
CHECK_GET_UINT64(N_U64(-1), UINT64_MAX && errno == 0);
CHECK_GET_UINT64(N_STR(U64_OUT_S), UINT64_MAX && errno == ERANGE);
CHECK_GET_UINT64(N_DBL(INFINITY), UINT64_MAX && errno == ERANGE);
CHECK_GET_UINT64(N_DBL(-INFINITY), 0 && errno == ERANGE);
CHECK_GET_UINT64(N_DBL(NAN), 0 && errno == EINVAL);
printf("UINT64 GET PASSED\n");
printf("PASSED\n");
return 0;
}

View File

@@ -0,0 +1,5 @@
BASE CHECK PASSED
INT GET PASSED
INT64 GET PASSED
UINT64 GET PASSED
PASSED

1
tests/test_int_get.test Symbolic link
View File

@@ -0,0 +1 @@
test_basic.test

127
tests/test_json_patch.c Normal file
View File

@@ -0,0 +1,127 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "strerror_override.h"
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "json.h"
#include "snprintf_compat.h"
#ifndef PATH_MAX
#define PATH_MAX 256
#endif
void test_json_patch_op(struct json_object *jo)
{
const char *comment = json_object_get_string(json_object_object_get(jo, "comment"));
struct json_object *doc = json_object_object_get(jo, "doc");
struct json_object *patch = json_object_object_get(jo, "patch");
struct json_object *expected = NULL;
json_bool have_expected = json_object_object_get_ex(jo, "expected", &expected);
struct json_object *error = json_object_object_get(jo, "error");
const char *error_s = json_object_get_string(error);
struct json_object *res = NULL;
int ret;
printf("Testing '%s', doc '%s' patch '%s' : ",
comment ? comment : error_s,
json_object_get_string(doc),
json_object_get_string(patch));
if (!error && !have_expected) {
printf("BAD TEST - no expected or error conditions in test: %s\n", json_object_to_json_string(jo));
assert(0);
}
fflush(stdout);
struct json_patch_error jperr;
if (error) {
assert(-1 == json_patch_apply(doc, patch, &res, &jperr));
assert(jperr.errno_code != 0);
printf("OK\n");
printf(" => json_patch_apply failed as expected: %s at patch idx %zu: %s\n",
strerror(jperr.errno_code), jperr.patch_failure_idx, jperr.errmsg);
json_object_put(res);
} else {
ret = json_patch_apply(doc, patch, &res, &jperr);
if (ret) {
fprintf(stderr, "json_patch_apply() returned '%d'\n", ret);
fprintf(stderr, "Expected: %s\n", json_object_get_string(expected));
fprintf(stderr, "Got: %s\n", res ? json_object_get_string(res) : "(null)");
fprintf(stderr, "json_patch_apply failed: %s at patch idx %zu: %s\n",
strerror(jperr.errno_code), jperr.patch_failure_idx, jperr.errmsg);
fflush(stderr);
assert(0);
}
// Note: res might be NULL if the whole document was removed
assert(jperr.errno_code == 0);
ret = json_object_equal(expected, res);
if (ret == 0) {
fprintf(stderr, "json_object_equal() returned '%d'\n", ret);
fprintf(stderr, "Expected: %s\n", json_object_get_string(expected));
fprintf(stderr, "Got: %s\n", json_object_get_string(res));
fflush(stderr);
assert(0);
}
json_object_put(res);
res = NULL;
printf("OK\n");
}
}
void test_json_patch_using_file(const char *testdir, const char *filename)
{
char full_filename[PATH_MAX];
(void)snprintf(full_filename, sizeof(full_filename), "%s/%s", testdir, filename);
size_t ii;
printf("Testing using file %s\n", filename);
json_object *jo = json_object_from_file(full_filename);
if (!jo) {
fprintf(stderr, "FAIL: unable to open %s: %s\n", full_filename, strerror(errno));
exit(EXIT_FAILURE);
}
for (ii = 0; ii < json_object_array_length(jo); ii++) {
struct json_object *jo1 = json_object_array_get_idx(jo, ii);
test_json_patch_op(jo1);
}
json_object_put(jo);
}
int main(int argc, char **argv)
{
const char *testdir;
if (argc < 2)
{
fprintf(stderr,
"Usage: %s <testdir>\n"
" <testdir> is the location of input files\n",
argv[0]);
return EXIT_FAILURE;
}
testdir = argv[1];
// Test json_c_version.c
if (strncmp(json_c_version(), JSON_C_VERSION, sizeof(JSON_C_VERSION)))
{
printf("FAIL: Output from json_c_version(): %s does not match %s",
json_c_version(), JSON_C_VERSION);
return EXIT_FAILURE;
}
if (json_c_version_num() != JSON_C_VERSION_NUM)
{
printf("FAIL: Output from json_c_version_num(): %d does not match %d",
json_c_version_num(), JSON_C_VERSION_NUM);
return EXIT_FAILURE;
}
test_json_patch_using_file(testdir, "json_patch_spec_tests.json");
test_json_patch_using_file(testdir, "json_patch_tests.json");
return 0;
}

View File

@@ -0,0 +1,158 @@
Testing using file json_patch_spec_tests.json
Testing '4.1. add with missing object', doc '{ "q": { "bar": 2 } }' patch '[ { "op": "add", "path": "\/a\/b", "value": 1 } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'A.1. Adding an Object Member', doc '{ "foo": "bar" }' patch '[ { "op": "add", "path": "\/baz", "value": "qux" } ]' : OK
Testing 'A.2. Adding an Array Element', doc '{ "foo": [ "bar", "baz" ] }' patch '[ { "op": "add", "path": "\/foo\/1", "value": "qux" } ]' : OK
Testing 'A.3. Removing an Object Member', doc '{ "baz": "qux", "foo": "bar" }' patch '[ { "op": "remove", "path": "\/baz" } ]' : OK
Testing 'A.4. Removing an Array Element', doc '{ "foo": [ "bar", "qux", "baz" ] }' patch '[ { "op": "remove", "path": "\/foo\/1" } ]' : OK
Testing 'A.5. Replacing a Value', doc '{ "baz": "qux", "foo": "bar" }' patch '[ { "op": "replace", "path": "\/baz", "value": "boo" } ]' : OK
Testing 'A.6. Moving a Value', doc '{ "foo": { "bar": "baz", "waldo": "fred" }, "qux": { "corge": "grault" } }' patch '[ { "op": "move", "from": "\/foo\/waldo", "path": "\/qux\/thud" } ]' : OK
Testing 'A.7. Moving an Array Element', doc '{ "foo": [ "all", "grass", "cows", "eat" ] }' patch '[ { "op": "move", "from": "\/foo\/1", "path": "\/foo\/3" } ]' : OK
Testing 'A.8. Testing a Value: Success', doc '{ "baz": "qux", "foo": [ "a", 2, "c" ] }' patch '[ { "op": "test", "path": "\/baz", "value": "qux" }, { "op": "test", "path": "\/foo\/1", "value": 2 } ]' : OK
Testing 'A.9. Testing a Value: Error', doc '{ "baz": "qux" }' patch '[ { "op": "test", "path": "\/baz", "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Value of element referenced by 'path' field did not match 'value' field
Testing 'A.10. Adding a nested Member Object', doc '{ "foo": "bar" }' patch '[ { "op": "add", "path": "\/child", "value": { "grandchild": { } } } ]' : OK
Testing 'A.11. Ignoring Unrecognized Elements', doc '{ "foo": "bar" }' patch '[ { "op": "add", "path": "\/baz", "value": "qux", "xyz": 123 } ]' : OK
Testing 'A.12. Adding to a Non-existent Target', doc '{ "foo": "bar" }' patch '[ { "op": "add", "path": "\/baz\/bat", "value": "qux" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'A.13 Invalid JSON Patch Document', doc '{ "foo": "bar" }' patch '[ { "op": "remove", "path": "\/baz", "value": "qux" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Did not find element referenced by path field
Testing 'A.14. ~ Escape Ordering', doc '{ "\/": 9, "~1": 10 }' patch '[ { "op": "test", "path": "\/~01", "value": 10 } ]' : OK
Testing 'A.15. Comparing Strings and Numbers', doc '{ "\/": 9, "~1": 10 }' patch '[ { "op": "test", "path": "\/~01", "value": "10" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Value of element referenced by 'path' field did not match 'value' field
Testing 'A.16. Adding an Array Value', doc '{ "foo": [ "bar" ] }' patch '[ { "op": "add", "path": "\/foo\/-", "value": [ "abc", "def" ] } ]' : OK
Testing using file json_patch_tests.json
Testing 'empty list, empty docs', doc '{ }' patch '[ ]' : OK
Testing 'empty patch list', doc '{ "foo": 1 }' patch '[ ]' : OK
Testing 'rearrangements OK?', doc '{ "foo": 1, "bar": 2 }' patch '[ ]' : OK
Testing 'rearrangements OK? How about one level down ... array', doc '[ { "foo": 1, "bar": 2 } ]' patch '[ ]' : OK
Testing 'rearrangements OK? How about one level down...', doc '{ "foo": { "foo": 1, "bar": 2 } }' patch '[ ]' : OK
Testing 'add replaces any existing field', doc '{ "foo": null }' patch '[ { "op": "add", "path": "\/foo", "value": 1 } ]' : OK
Testing 'toplevel array', doc '[ ]' patch '[ { "op": "add", "path": "\/0", "value": "foo" } ]' : OK
Testing 'toplevel array, no change', doc '[ "foo" ]' patch '[ ]' : OK
Testing 'toplevel object, numeric string', doc '{ }' patch '[ { "op": "add", "path": "\/foo", "value": "1" } ]' : OK
Testing 'toplevel object, integer', doc '{ }' patch '[ { "op": "add", "path": "\/foo", "value": 1 } ]' : OK
Testing 'Toplevel scalar values OK?', doc 'foo' patch '[ { "op": "replace", "path": "", "value": "bar" } ]' : OK
Testing 'replace object document with array document?', doc '{ }' patch '[ { "op": "add", "path": "", "value": [ ] } ]' : OK
Testing 'replace array document with object document?', doc '[ ]' patch '[ { "op": "add", "path": "", "value": { } } ]' : OK
Testing 'append to root array document?', doc '[ ]' patch '[ { "op": "add", "path": "\/-", "value": "hi" } ]' : OK
Testing 'Add, / target', doc '{ }' patch '[ { "op": "add", "path": "\/", "value": 1 } ]' : OK
Testing 'Add, /foo/ deep target (trailing slash)', doc '{ "foo": { } }' patch '[ { "op": "add", "path": "\/foo\/", "value": 1 } ]' : OK
Testing 'Add composite value at top level', doc '{ "foo": 1 }' patch '[ { "op": "add", "path": "\/bar", "value": [ 1, 2 ] } ]' : OK
Testing 'Add into composite value', doc '{ "foo": 1, "baz": [ { "qux": "hello" } ] }' patch '[ { "op": "add", "path": "\/baz\/0\/foo", "value": "world" } ]' : OK
Testing 'Out of bounds (upper)', doc '{ "bar": [ 1, 2 ] }' patch '[ { "op": "add", "path": "\/bar\/8", "value": "5" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'Out of bounds (lower)', doc '{ "bar": [ 1, 2 ] }' patch '[ { "op": "add", "path": "\/bar\/-1", "value": "5" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Failed to set value at path referenced by 'path' field
Testing '(null)', doc '{ "foo": 1 }' patch '[ { "op": "add", "path": "\/bar", "value": true } ]' : OK
Testing '(null)', doc '{ "foo": 1 }' patch '[ { "op": "add", "path": "\/bar", "value": false } ]' : OK
Testing '(null)', doc '{ "foo": 1 }' patch '[ { "op": "add", "path": "\/bar", "value": null } ]' : OK
Testing '0 can be an array index or object element name', doc '{ "foo": 1 }' patch '[ { "op": "add", "path": "\/0", "value": "bar" } ]' : OK
Testing '(null)', doc '[ "foo" ]' patch '[ { "op": "add", "path": "\/1", "value": "bar" } ]' : OK
Testing '(null)', doc '[ "foo", "sil" ]' patch '[ { "op": "add", "path": "\/1", "value": "bar" } ]' : OK
Testing '(null)', doc '[ "foo", "sil" ]' patch '[ { "op": "add", "path": "\/0", "value": "bar" } ]' : OK
Testing 'push item to array via last index + 1', doc '[ "foo", "sil" ]' patch '[ { "op": "add", "path": "\/2", "value": "bar" } ]' : OK
Testing 'add item to array at index > length should fail', doc '[ "foo", "sil" ]' patch '[ { "op": "add", "path": "\/3", "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'test against implementation-specific numeric parsing', doc '{ "1e0": "foo" }' patch '[ { "op": "test", "path": "\/1e0", "value": "foo" } ]' : OK
Testing 'test with bad number should fail', doc '[ "foo", "bar" ]' patch '[ { "op": "test", "path": "\/1e0", "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid path field
Testing 'Object operation on array target', doc '[ "foo", "sil" ]' patch '[ { "op": "add", "path": "\/bar", "value": 42 } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'value in array add not flattened', doc '[ "foo", "sil" ]' patch '[ { "op": "add", "path": "\/1", "value": [ "bar", "baz" ] } ]' : OK
Testing '(null)', doc '{ "foo": 1, "bar": [ 1, 2, 3, 4 ] }' patch '[ { "op": "remove", "path": "\/bar" } ]' : OK
Testing '(null)', doc '{ "foo": 1, "baz": [ { "qux": "hello" } ] }' patch '[ { "op": "remove", "path": "\/baz\/0\/qux" } ]' : OK
Testing '(null)', doc '{ "foo": 1, "baz": [ { "qux": "hello" } ] }' patch '[ { "op": "replace", "path": "\/foo", "value": [ 1, 2, 3, 4 ] } ]' : OK
Testing '(null)', doc '{ "foo": [ 1, 2, 3, 4 ], "baz": [ { "qux": "hello" } ] }' patch '[ { "op": "replace", "path": "\/baz\/0\/qux", "value": "world" } ]' : OK
Testing '(null)', doc '[ "foo" ]' patch '[ { "op": "replace", "path": "\/0", "value": "bar" } ]' : OK
Testing '(null)', doc '[ "" ]' patch '[ { "op": "replace", "path": "\/0", "value": 0 } ]' : OK
Testing '(null)', doc '[ "" ]' patch '[ { "op": "replace", "path": "\/0", "value": true } ]' : OK
Testing '(null)', doc '[ "" ]' patch '[ { "op": "replace", "path": "\/0", "value": false } ]' : OK
Testing '(null)', doc '[ "" ]' patch '[ { "op": "replace", "path": "\/0", "value": null } ]' : OK
Testing 'value in array replace not flattened', doc '[ "foo", "sil" ]' patch '[ { "op": "replace", "path": "\/1", "value": [ "bar", "baz" ] } ]' : OK
Testing 'replace whole document', doc '{ "foo": "bar" }' patch '[ { "op": "replace", "path": "", "value": { "baz": "qux" } } ]' : OK
Testing 'add whole document, null', doc '{ }' patch '[ { "op": "remove", "path": "" }, { "op": "add", "path": "", "value": { "baz": "qux" } } ]' : OK
Testing 'replace whole document, null', doc '{ }' patch '[ { "op": "remove", "path": "" }, { "op": "replace", "path": "", "value": { "baz": "qux" } } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 1: Invalid path field
Testing 'remove whole document', doc '{ "foo": "bar" }' patch '[ { "op": "remove", "path": "" } ]' : OK
Testing 'remove whole document', doc '{ "foo": "bar" }' patch '[ { "op": "remove", "path": "" } ]' : OK
Testing 'remove whole document, array', doc '[ "foo", "bar" ]' patch '[ { "op": "remove", "path": "" } ]' : OK
Testing 'remove whole document, string', doc 'foo' patch '[ { "op": "remove", "path": "" } ]' : OK
Testing 'remove whole document, null', doc '{ }' patch '[ { "op": "remove", "path": "" }, { "op": "remove", "path": "" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 1: Invalid path field
Testing 'test replace with missing parent key should fail', doc '{ "bar": "baz" }' patch '[ { "op": "replace", "path": "\/foo\/bar", "value": false } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Did not find element referenced by path field
Testing 'spurious patch properties', doc '{ "foo": 1 }' patch '[ { "op": "test", "path": "\/foo", "value": 1, "spurious": 1 } ]' : OK
Testing 'null value should be valid obj property', doc '{ "foo": null }' patch '[ { "op": "test", "path": "\/foo", "value": null } ]' : OK
Testing 'null value should be valid obj property to be replaced with something truthy', doc '{ "foo": null }' patch '[ { "op": "replace", "path": "\/foo", "value": "truthy" } ]' : OK
Testing 'null value should be valid obj property to be moved', doc '{ "foo": null }' patch '[ { "op": "move", "from": "\/foo", "path": "\/bar" } ]' : OK
Testing 'null value should be valid obj property to be copied', doc '{ "foo": null }' patch '[ { "op": "copy", "from": "\/foo", "path": "\/bar" } ]' : OK
Testing 'null value should be valid obj property to be removed', doc '{ "foo": null }' patch '[ { "op": "remove", "path": "\/foo" } ]' : OK
Testing 'null value should still be valid obj property replace other value', doc '{ "foo": "bar" }' patch '[ { "op": "replace", "path": "\/foo", "value": null } ]' : OK
Testing 'test should pass despite rearrangement', doc '{ "foo": { "foo": 1, "bar": 2 } }' patch '[ { "op": "test", "path": "\/foo", "value": { "bar": 2, "foo": 1 } } ]' : OK
Testing 'test should pass despite (nested) rearrangement', doc '{ "foo": [ { "foo": 1, "bar": 2 } ] }' patch '[ { "op": "test", "path": "\/foo", "value": [ { "bar": 2, "foo": 1 } ] } ]' : OK
Testing 'test should pass - no error', doc '{ "foo": { "bar": [ 1, 2, 5, 4 ] } }' patch '[ { "op": "test", "path": "\/foo", "value": { "bar": [ 1, 2, 5, 4 ] } } ]' : OK
Testing 'test op should fail', doc '{ "foo": { "bar": [ 1, 2, 5, 4 ] } }' patch '[ { "op": "test", "path": "\/foo", "value": [ 1, 2 ] } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Value of element referenced by 'path' field did not match 'value' field
Testing 'Test the whole document', doc '{ "foo": 1 }' patch '[ { "op": "test", "path": "", "value": { "foo": 1 } } ]' : OK
Testing 'Test the whole document, no match', doc '{ "foo": 1 }' patch '[ { "op": "test", "path": "", "value": { "foo": 2 } } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Value of element referenced by 'path' field did not match 'value' field
Testing 'Empty-string element', doc '{ "": 1 }' patch '[ { "op": "test", "path": "\/", "value": 1 } ]' : OK
Testing '(null)', doc '{ "foo": [ "bar", "baz" ], "": 0, "a\/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, "i\\j": 5, "k\"l": 6, " ": 7, "m~n": 8 }' patch '[ { "op": "test", "path": "\/foo", "value": [ "bar", "baz" ] }, { "op": "test", "path": "\/foo\/0", "value": "bar" }, { "op": "test", "path": "\/", "value": 0 }, { "op": "test", "path": "\/a~1b", "value": 1 }, { "op": "test", "path": "\/c%d", "value": 2 }, { "op": "test", "path": "\/e^f", "value": 3 }, { "op": "test", "path": "\/g|h", "value": 4 }, { "op": "test", "path": "\/i\\j", "value": 5 }, { "op": "test", "path": "\/k\"l", "value": 6 }, { "op": "test", "path": "\/ ", "value": 7 }, { "op": "test", "path": "\/m~0n", "value": 8 } ]' : OK
Testing 'Move to same location has no effect', doc '{ "foo": 1 }' patch '[ { "op": "move", "from": "\/foo", "path": "\/foo" } ]' : OK
Testing '(null)', doc '{ "foo": 1, "baz": [ { "qux": "hello" } ] }' patch '[ { "op": "move", "from": "\/foo", "path": "\/bar" } ]' : OK
Testing '(null)', doc '{ "baz": [ { "qux": "hello" } ], "bar": 1 }' patch '[ { "op": "move", "from": "\/baz\/0\/qux", "path": "\/baz\/1" } ]' : OK
Testing '(null)', doc '{ "baz": [ { "qux": "hello" } ], "bar": 1 }' patch '[ { "op": "copy", "from": "\/baz\/0", "path": "\/boo" } ]' : OK
Testing 'replacing the root of the document is possible with add', doc '{ "foo": "bar" }' patch '[ { "op": "add", "path": "", "value": { "baz": "qux" } } ]' : OK
Testing 'Adding to "/-" adds to the end of the array', doc '[ 1, 2 ]' patch '[ { "op": "add", "path": "\/-", "value": { "foo": [ "bar", "baz" ] } } ]' : OK
Testing 'Adding to "/-" adds to the end of the array, even n levels down', doc '[ 1, 2, [ 3, [ 4, 5 ] ] ]' patch '[ { "op": "add", "path": "\/2\/1\/-", "value": { "foo": [ "bar", "baz" ] } } ]' : OK
Testing 'test remove with bad number should fail', doc '{ "foo": 1, "baz": [ { "qux": "hello" } ] }' patch '[ { "op": "remove", "path": "\/baz\/1e0\/qux" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid path field
Testing 'test remove on array', doc '[ 1, 2, 3, 4 ]' patch '[ { "op": "remove", "path": "\/0" } ]' : OK
Testing 'test repeated removes', doc '[ 1, 2, 3, 4 ]' patch '[ { "op": "remove", "path": "\/1" }, { "op": "remove", "path": "\/2" } ]' : OK
Testing 'test remove with bad index should fail', doc '[ 1, 2, 3, 4 ]' patch '[ { "op": "remove", "path": "\/1e0" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid path field
Testing 'test replace with bad number should fail', doc '[ "" ]' patch '[ { "op": "replace", "path": "\/1e0", "value": false } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid path field
Testing 'test copy with bad number should fail', doc '{ "baz": [ 1, 2, 3 ], "bar": 1 }' patch '[ { "op": "copy", "from": "\/baz\/1e0", "path": "\/boo" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid from field
Testing 'test move with bad number should fail', doc '{ "foo": 1, "baz": [ 1, 2, 3, 4 ] }' patch '[ { "op": "move", "from": "\/baz\/1e0", "path": "\/foo" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid from field
Testing 'test add with bad number should fail', doc '[ "foo", "sil" ]' patch '[ { "op": "add", "path": "\/1e0", "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'missing 'path' parameter', doc '{ }' patch '[ { "op": "add", "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch object does not contain 'path' field
Testing ''path' parameter with null value', doc '{ }' patch '[ { "op": "add", "path": null, "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'invalid JSON Pointer token', doc '{ }' patch '[ { "op": "add", "path": "foo", "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Failed to set value at path referenced by 'path' field
Testing 'missing 'value' parameter to add', doc '[ 1 ]' patch '[ { "op": "add", "path": "\/-" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch object does not contain a 'value' field
Testing 'missing 'value' parameter to replace', doc '[ 1 ]' patch '[ { "op": "replace", "path": "\/0" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch object does not contain a 'value' field
Testing 'missing 'value' parameter to test', doc '[ null ]' patch '[ { "op": "test", "path": "\/0" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch object does not contain a 'value' field
Testing 'missing value parameter to test - where undef is falsy', doc '[ false ]' patch '[ { "op": "test", "path": "\/0" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch object does not contain a 'value' field
Testing 'missing from parameter to copy', doc '[ 1 ]' patch '[ { "op": "copy", "path": "\/-" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch does not contain a 'from' field
Testing 'missing from location to copy', doc '{ "foo": 1 }' patch '[ { "op": "copy", "from": "\/bar", "path": "\/foo" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Did not find element referenced by from field
Testing 'missing from parameter to move', doc '{ "foo": 1 }' patch '[ { "op": "move", "path": "" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch does not contain a 'from' field
Testing 'missing from location to move', doc '{ "foo": 1 }' patch '[ { "op": "move", "from": "\/bar", "path": "\/foo" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Did not find element referenced by from field
Testing 'duplicate ops, json-c parses this as op:move', doc '{ "foo": "bar" }' patch '[ { "op": "move", "path": "\/baz", "value": "qux", "from": "\/foo" } ]' : OK
Testing 'unrecognized op should fail', doc '{ "foo": 1 }' patch '[ { "op": "spam", "path": "\/foo", "value": 1 } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Patch object has invalid 'op' field
Testing 'test with bad array number that has leading zeros', doc '[ "foo", "bar" ]' patch '[ { "op": "test", "path": "\/00", "value": "foo" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid path field
Testing 'test with bad array number that has leading zeros', doc '[ "foo", "bar" ]' patch '[ { "op": "test", "path": "\/01", "value": "bar" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=EINVAL at patch idx 0: Invalid path field
Testing 'Removing nonexistent field', doc '{ "foo": "bar" }' patch '[ { "op": "remove", "path": "\/baz" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Did not find element referenced by path field
Testing 'Removing deep nonexistent path', doc '{ "foo": "bar" }' patch '[ { "op": "remove", "path": "\/missing1\/missing2" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Did not find element referenced by path field
Testing 'Removing nonexistent index', doc '[ "foo", "bar" ]' patch '[ { "op": "remove", "path": "\/2" } ]' : OK
=> json_patch_apply failed as expected: ERRNO=ENOENT at patch idx 0: Did not find element referenced by path field
Testing 'Patch with different capitalisation than doc', doc '{ "foo": "bar" }' patch '[ { "op": "add", "path": "\/FOO", "value": "BAR" } ]' : OK

17
tests/test_json_patch.test Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
export _JSON_C_STRERROR_ENABLE=1
# Common definitions
if test -z "$srcdir"; then
srcdir="${0%/*}"
test "$srcdir" = "$0" && srcdir=.
test -z "$srcdir" && srcdir=.
fi
. "$srcdir/test-defs.sh"
filename=$(basename "$0")
filename="${filename%.*}"
run_output_test $filename "$srcdir"
exit $?

View File

@@ -124,7 +124,7 @@ static void test_example_get(void)
json_object_put(jo1); json_object_put(jo1);
} }
/* I'm not too happy with the RFC example to test the recusion of the json_pointer_get() function */ /* I'm not too happy with the RFC example to test the recursion of the json_pointer_get() function */
static void test_recursion_get(void) static void test_recursion_get(void)
{ {
struct json_object *jo2, *jo1 = json_tokener_parse(rec_input_json_str); struct json_object *jo2, *jo1 = json_tokener_parse(rec_input_json_str);
@@ -269,6 +269,22 @@ static void test_example_set(void)
printf("%s\n", json_object_get_string(jo1)); printf("%s\n", json_object_get_string(jo1));
json_object_put(jo1); json_object_put(jo1);
jo1 = json_tokener_parse("[0, 1, 2, 3]");
jo2 = json_tokener_parse("[0, 1, 2, 3, null, null, null, 7]");
assert(0 == json_pointer_set(&jo1, "/7", json_object_new_int(7)));
assert(1 == json_object_equal(jo1, jo2));
json_object_put(jo1);
jo1 = json_tokener_parse("[0, 1, 2, 3]");
assert(0 == json_pointer_setf(&jo1, json_object_new_int(7), "/%u", 7));
assert(1 == json_object_equal(jo1, jo2));
json_object_put(jo1);
json_object_put(jo2);
} }
static void test_wrong_inputs_set(void) static void test_wrong_inputs_set(void)

View File

@@ -1,3 +1,6 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h> #include <assert.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
@@ -21,8 +24,6 @@ int main(int argc, char **argv)
json_object *new_obj; json_object *new_obj;
#ifdef HAVE_SETLOCALE #ifdef HAVE_SETLOCALE
setlocale(LC_NUMERIC, "de_DE"); setlocale(LC_NUMERIC, "de_DE");
#else
printf("No locale\n");
#endif #endif
char buf1[10], buf2[10]; char buf1[10], buf2[10];

Some files were not shown because too many files have changed in this diff Show More