145 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
67 changed files with 4141 additions and 229 deletions

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

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@
*.save
*.autosav
*.autosave
.DS_Store
# Tests
/tests/Makefile

33
AUTHORS
View File

@@ -1,25 +1,40 @@
Alan Coopersmith <alan.coopersmith@oracle.com>
Alan Urmancheev <108410815+alurm@users.noreply.github.com>
Alexander Dahl <post@lespocky.de>
Alexandru Ardelean <ardeleanalex@gmail.com>
An7ar35 <eadavison@protonmail.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>
BonsaY <bonsay@posteo.de>
Bruno Haible <bruno@clisp.org>
Cameron Armstrong <cameronscottarmstrong@gmail.com>
Careles <zhancheng1996@gmail.com>
changyong guo <guo1487@163.com>
chenguoping <chenguopingdota@163.com>
Chris Lamb <lamby@debian.org>
Christopher Head <chead@chead.ca>
Chris Wolfe <chriswwolfe@gmail.com>
C. Watford (christopher.watford@gmail.com)
Daniel Danzberger <daniel@dd-wrt.com>
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>
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>
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>
@@ -27,6 +42,8 @@ Ivan Romanov <drizt@land.ru>
Jaap Keuter <jaap.keuter@xs4all.nl>
Jakov Smolic <jakov.smolic@sartura.hr>
janczer <menshikov.ivn@gmail.com>
Ján Tomko <jtomko@redhat.com>
JC (Jonathan Chen) <jc@dijonkitchen.org>
Jehan <jehan@girinstud.io>
Jehiah Czebotar <jehiah@gmail.com>
Jonathan Wiens <j.wiens@teles.com>
@@ -34,15 +51,21 @@ Jose Bollo <jose.bollo@iot.bzh>
José Bollo <jose.bollo@iot.bzh>
Juuso Alasuutari <juuso.alasuutari@gmail.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>
Luca Mannella <luca.mannella@studenti.polito.it>
Marc <34656315+MarcT512@users.noreply.github.com>
Matthias Gatto <matthias.gatto@protonmail.com>
max <mpano91@gmail.com>
Micah Snyder <micasnyd@cisco.com>
Michael Clark <michael@metaparadigm.com>
Michele Locati <michele@locati.it>
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>
@@ -50,12 +73,22 @@ Po-Chuan Hsieh <sunpoet@sunpoet.net>
Ramiro Polla <ramiro.polla@gmail.com>
Rikard Falkeborn <rikard.falkeborn@gmail.com>
Robert Bielik <robert.bielik@dirac.com>
Robert Pang <robertpang@google.com>
Robert <roby_p97@yahoo.com>
Rolf Eike Beer <eb@emlix.com>
Rosen Penev <rosenp@gmail.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>
Yurii Rashkovskii <yrashk@gmail.com>

View File

@@ -3,24 +3,11 @@
# and many OpenWRT packages require, much newer CMake packages.
# We're stopping before 3.10 because that version starts requiring
# c++11, which isn't available on e.g HPUX.
cmake_minimum_required(VERSION 3.9)
# The project() command manages VERSION variables.
cmake_policy(SET CMP0048 NEW)
cmake_minimum_required(VERSION 3.9...3.12)
# JSON-C library is C only project.
# PROJECT_VERSION{,_MAJOR,_MINOR,_PATCH} set by project():
project(json-c LANGUAGES C VERSION 0.16.99)
# Targets may not link directly to themselves.
cmake_policy(SET CMP0038 NEW)
# MACOSX_RPATH is enabled by default.
# We set it explicitly to avoid the warning
cmake_policy(SET CMP0042 NEW)
# Only interpret if() arguments as variables or keywords when unquoted.
cmake_policy(SET CMP0054 NEW)
project(json-c LANGUAGES C VERSION 0.18.99)
# set default build type if not specified by user
if(NOT CMAKE_BUILD_TYPE)
@@ -29,13 +16,6 @@ endif()
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.
# New in version 3.12.
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
include(CTest)
# Set some packaging variables.
@@ -65,10 +45,6 @@ include(CMakePackageConfigHelpers)
option(BUILD_SHARED_LIBS "Default to building shared libraries" ON)
option(BUILD_STATIC_LIBS "Default to building static libraries" ON)
if (BUILD_SHARED_LIBS)
add_definitions(-D JSON_C_DLL)
endif()
# Generate a release merge and test it to verify the correctness of republishing the package.
ADD_CUSTOM_TARGET(distcheck
COMMAND make package_source
@@ -88,14 +64,70 @@ option(ENABLE_RDRAND "Enable RDRAND Hardware RNG Hash Seed."
option(ENABLE_THREADING "Enable partial threading support." 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) support." 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)
endif()
if (UNIX)
if (BUILD_SHARED_LIBS)
add_definitions(-D JSON_C_DLL)
endif()
if (UNIX OR AMIGA)
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
endif()
@@ -140,17 +172,30 @@ check_include_file(sys/random.h HAVE_SYS_RANDOM_H)
check_include_file(sys/stat.h HAVE_SYS_STAT_H)
check_include_file(xlocale.h HAVE_XLOCALE_H)
# Set json-c specific vars to stamp into json_config.h
# in a way that hopefully won't conflict with other
# projects that use json-c.
if (HAVE_INTTYPES_H)
# Set a json-c specific var to stamp into json_config.h
# in a way that hopefully won't conflict with other
# projects that use json-c.
set(JSON_C_HAVE_INTTYPES_H 1)
set(JSON_C_HAVE_INTTYPES_H 1)
endif()
if (HAVE_STDINT_H)
set(JSON_C_HAVE_STDINT_H 1)
endif()
check_symbol_exists(_isnan "float.h" HAVE_DECL__ISNAN)
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(isinf "math.h" HAVE_DECL_ISINF)
check_symbol_exists(isnan "math.h" HAVE_DECL_ISNAN)
@@ -158,22 +203,23 @@ if ((MSVC AND NOT (MSVC_VERSION LESS 1800)) OR MINGW OR CYGWIN OR UNIX)
endif()
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)
endif()
check_symbol_exists(vasprintf "stdio.h" HAVE_VASPRINTF)
check_symbol_exists(vsnprintf "stdio.h" HAVE_VSNPRINTF)
check_symbol_exists(vprintf "stdio.h" HAVE_VPRINTF)
check_symbol_exists(arc4random "stdlib.h" HAVE_ARC4RANDOM)
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)
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")
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "bsd")
endif()
endif()
endif()
@@ -187,13 +233,18 @@ endif()
if (HAVE_LOCALE_H)
check_symbol_exists(setlocale "locale.h" HAVE_SETLOCALE)
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()
# 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
# So, if it looks like we're compiling for a system like that just disable
# locale handling entirely.
exec_program(${CMAKE_C_COMPILER} ARGS -dumpmachine OUTPUT_VARIABLE CMAKE_GNU_C_MACHINE)
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)
@@ -289,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)
message(STATUS "Wrote ${PROJECT_BINARY_DIR}/json_config.h")
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
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")
if ("${DISABLE_WERROR}" STREQUAL "OFF")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
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} -Wcast-qual")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=deprecated-declarations")
@@ -305,7 +374,12 @@ if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL
endif()
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} /wd4100")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996")
@@ -316,7 +390,7 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4701")
endif()
if (NOT ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC"))
if (NOT ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC"))
check_c_source_compiles(
"
/* uClibc toolchains without threading barf when _REENTRANT is defined */
@@ -396,6 +470,7 @@ set(JSON_C_PUBLIC_HEADERS
set(JSON_C_HEADERS
${JSON_C_PUBLIC_HEADERS}
${PROJECT_SOURCE_DIR}/json_object_private.h
${PROJECT_SOURCE_DIR}/json_pointer_private.h
${PROJECT_SOURCE_DIR}/random_seed.h
${PROJECT_SOURCE_DIR}/strerror_override.h
${PROJECT_SOURCE_DIR}/math_compat.h
@@ -419,12 +494,26 @@ set(JSON_C_SOURCES
${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)
@@ -451,9 +540,10 @@ endif()
add_library(${PROJECT_NAME}
${JSON_C_SOURCES}
${JSON_C_HEADERS}
${JSON_C_RESOURCES}
)
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION 5.2.0
VERSION 5.4.0
SOVERSION 5)
list(APPEND CMAKE_TARGETS ${PROJECT_NAME})
# If json-c is used as subroject it set to target correct interface -I flags and allow
@@ -526,7 +616,6 @@ install(
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
if (UNIX OR MINGW OR CYGWIN)
SET(prefix ${CMAKE_INSTALL_PREFIX})
# exec_prefix is prefix by default and CMake does not have the
# concept.
@@ -554,7 +643,6 @@ if (UNIX OR MINGW OR CYGWIN)
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")
install(FILES ${PROJECT_BINARY_DIR}/json-c.pc DESTINATION "${INSTALL_PKGCONFIG_DIR}")
endif ()
install(FILES ${JSON_C_PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/json-c)
@@ -564,10 +652,9 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING AND
add_subdirectory(tests)
endif()
if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
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()

View File

@@ -1,14 +1,87 @@
0.17 (future release)
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
---------------------------------
@@ -19,7 +92,17 @@ Significant changes and bug fixes
* 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)
========================================

View File

@@ -1,6 +1,6 @@
\mainpage
`json-c`
json-c
========
1. [Overview and Build Status](#overview)
@@ -12,10 +12,12 @@
5. [Testing](#testing)
6. [Building with `vcpkg`](#buildvcpkg)
7. [Building for Android](#android)
7. [Linking to libjson-c](#linking)
8. [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
-----------------------------------
JSON-C implements a reference counting object model that allows you to easily
@@ -29,7 +31,8 @@ if you already have json-c installed and ready to use.
Home page for json-c: https://github.com/json-c/json-c/wiki
Getting Help <a name="gettinghelp"></a>
<a name="gettinghelp"></a>
Getting Help
------------
If you have questions about using json-c, please start a thread on
@@ -47,7 +50,8 @@ features or large changes of any kind, please first start a discussion
on the [forums](https://groups.google.com/forum/#!forum/json-c).
Building on Unix with `git`, `gcc` and `cmake` <a name="buildunix"></a>
<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)
@@ -60,7 +64,8 @@ Build Status
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)
### Prerequisites: <a name="installprereq"></a>
<a name="installprereq"></a>
### Prerequisites:
- `gcc`, `clang`, or another C compiler
@@ -80,7 +85,8 @@ sudo apt install doxygen # 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
@@ -116,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/),
@@ -188,7 +195,8 @@ cmake-configure can take a few options.
| 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.
@@ -224,7 +232,8 @@ JSONC_TEST_TRACE=1 make test
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:
@@ -237,7 +246,8 @@ 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.
Building for Android <a name="android">
<a name="android"></a>
Building for Android
----------------------
Building on Android is now particularly well supported, but there
@@ -261,7 +271,65 @@ cmake \
make install
```
Linking to `libjson-c` <a name="linking">
<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`,
@@ -298,7 +366,8 @@ cd build
cmake -DCMAKE_PREFIX_PATH=/path/to/json_c/install/lib64/cmake ..
```
Using json-c <a name="using">
<a name="using"></a>
Using json-c
------------
To use json-c you can either include json.h, or preferably, one of the

View File

@@ -16,6 +16,7 @@
changes (added/removed/updated funcs, etc...), and detect backwards compat
issues.
* 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.
* Mention removed features in ChangeLog
* Consider re-adding backwards compatible support, through symbol
@@ -40,9 +41,15 @@
## Release creation
Start creating the new release:
release=0.16
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}
rm -rf distcheck
mkdir distcheck
cd distcheck
# Note, the build directory *must* be entirely separate from
@@ -54,8 +61,7 @@ Start creating the new release:
Make any fixes/changes *before* branching.
cd json-c-${release}
git branch json-c-${release}
git checkout json-c-${release}
git checkout -b json-c-${release}
------------
@@ -135,12 +141,10 @@ 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 CMakeLists.txt
Update RELEASE_CHECKLIST.txt, set release=${release}+1
Update the set_target_properties() line in CmakeLists.txt to match the release branch.
Add a new empty section to the json-c.sym file, for ${release}+1
Update the set_target_properties() line in CmakeLists.txt to match the release branch.
git commit -a -m "Update the master branch to version ${release}.99"
git push
@@ -173,11 +177,12 @@ Update the gh-pages branch with new docs:
Update checksums on wiki page.
cd ..
openssl sha -sha256 json-c*gz
openssl sha1 -sha256 json-c*gz
openssl md5 json-c*gz
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)
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
if (HAVE_SYS_RESOURCE_H)
check_symbol_exists(getrusage "sys/resource.h" HAVE_GETRUSAGE)

View File

@@ -1,7 +1,9 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -38,6 +40,8 @@
static int formatted_output = JSON_C_TO_STRING_SPACED;
static int show_output = 1;
static int strict_mode = 0;
static int validate_utf8 = 0;
static int tokener_flags = 0;
static int color = 0;
static const char *fname = NULL;
@@ -74,11 +78,21 @@ static int parseit(int fd, int (*callback)(struct json_object *))
fprintf(stderr, "unable to allocate json_tokener: %s\n", strerror(errno));
return 1;
}
json_tokener_set_flags(tok, JSON_TOKENER_STRICT
if (strict_mode)
{
tokener_flags |= JSON_TOKENER_STRICT;
#ifdef JSON_TOKENER_ALLOW_TRAILING_CHARS
| JSON_TOKENER_ALLOW_TRAILING_CHARS
#endif
);
tokener_flags |= JSON_TOKENER_ALLOW_TRAILING_CHARS;
#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?
// json_object_from_fd isn't flexible enough, and mirroring
@@ -157,13 +171,15 @@ static void usage(const char *argv0, int exitval, const char *errmsg)
fp = stderr;
if (errmsg != NULL)
fprintf(fp, "ERROR: %s\n\n", errmsg);
fprintf(fp, "Usage: %s [-f|-F <arg>] [-n] [-s]\n", argv0);
fprintf(fp, "Usage: %s [-f|-F <arg>] [-n] [-s] [-u] [filename]\n", argv0);
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, " -c - color\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, " -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");
@@ -175,26 +191,28 @@ int main(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "fF:hnsc")) != -1)
while ((opt = getopt(argc, argv, "cfF:hnP:su")) != -1)
{
switch (opt)
{
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 'P': tokener_flags = atoi(optarg); break;
case 's': strict_mode = 1; break;
case 'c': color = JSON_C_TO_STRING_COLOR; break;
case 'u': validate_utf8 = 1; break;
case 'h': usage(argv[0], 0, NULL);
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();
if (parseit(fd, showobj) != 0)
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

@@ -125,6 +125,27 @@ int array_list_shrink(struct array_list *arr, size_t empty_slots)
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)
int array_list_put_idx(struct array_list *arr, size_t idx, void *data)
{

View File

@@ -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 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_add(struct array_list *al, void *data);

View File

@@ -137,6 +137,12 @@
/* Define to 1 if you have the `uselocale' function. */
#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. */
#cmakedefine HAVE_VASPRINTF

View File

@@ -1,2 +1,5 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#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@

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,11 +8,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
json_tokener *tok = json_tokener_new();
json_object *obj = json_tokener_parse_ex(tok, data1, size);
json_object_object_foreach(jobj, key, val) {
(void)json_object_get_type(val);
(void)json_object_get_string(val);
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(obj, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED);
(void)json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED);
json_object_put(obj);
json_tokener_free(tok);

View File

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

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

@@ -167,11 +167,23 @@ JSONC_0.15 {
} JSONC_0.14;
JSONC_0.16 {
# global:
# ...new symbols here...
# No new symbols in 0.16
} JSONC_0.15;
JSONC_0.17 {
# global:
# ...new symbols here...
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,6 +26,7 @@ extern "C" {
#include "json_c_version.h"
#include "json_object.h"
#include "json_object_iterator.h"
@JSON_H_JSON_PATCH@
@JSON_H_JSON_POINTER@
#include "json_tokener.h"
#include "json_util.h"

View File

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

View File

@@ -13,7 +13,15 @@
#include <inttypes.h>
#else
#ifdef JSON_C_HAVE_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 SCNd64 "I64d"
@@ -21,7 +29,7 @@
#endif
#if defined(_MSC_VER)
#if defined(_MSC_VER) && !defined(ssize_t)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif

View File

@@ -53,6 +53,7 @@
#endif
#endif
const char *json_number_chars = "0123456789.+-eE"; /* Unused, but part of public API, drop for 1.0 */
const char *json_hex_chars = "0123456789abcdefABCDEF";
static void json_object_generic_delete(struct json_object *jso);
@@ -720,6 +721,7 @@ int32_t json_object_get_int(const struct json_object *jso)
int64_t cint64 = 0;
double cdouble;
enum json_type o_type;
errno = 0;
if (!jso)
return 0;
@@ -755,17 +757,34 @@ int32_t json_object_get_int(const struct json_object *jso)
{
case json_type_int:
/* 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;
if (cint64 >= INT32_MAX)
}
if (cint64 > INT32_MAX)
{
errno = ERANGE;
return INT32_MAX;
}
return (int32_t)cint64;
case json_type_double:
cdouble = JC_DOUBLE_C(jso)->c_double;
if (cdouble <= INT32_MIN)
if (cdouble < INT32_MIN)
{
errno = ERANGE;
return INT32_MIN;
if (cdouble >= INT32_MAX)
}
if (cdouble > INT32_MAX)
{
errno = ERANGE;
return INT32_MAX;
}
if (isnan(cdouble))
{
errno = EINVAL;
return INT32_MIN;
}
return (int32_t)cdouble;
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
default: return 0;
@@ -800,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 cint;
errno = 0;
if (!jso)
return 0;
@@ -812,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_uint64:
if (jsoint->cint.c_uint64 >= INT64_MAX)
if (jsoint->cint.c_uint64 > INT64_MAX)
{
errno = ERANGE;
return INT64_MAX;
}
return (int64_t)jsoint->cint.c_uint64;
default: json_abort("invalid cint_type");
}
@@ -821,10 +844,21 @@ int64_t json_object_get_int64(const struct json_object *jso)
case json_type_double:
// INT64_MAX can't be exactly represented as a double
// 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;
if (JC_DOUBLE_C(jso)->c_double <= INT64_MIN)
}
if (JC_DOUBLE_C(jso)->c_double < (double)INT64_MIN)
{
errno = ERANGE;
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;
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
case json_type_string:
@@ -838,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 cuint;
errno = 0;
if (!jso)
return 0;
@@ -850,7 +885,10 @@ uint64_t json_object_get_uint64(const struct json_object *jso)
{
case json_object_int_type_int64:
if (jsoint->cint.c_int64 < 0)
{
errno = ERANGE;
return 0;
}
return (uint64_t)jsoint->cint.c_int64;
case json_object_int_type_uint64: return jsoint->cint.c_uint64;
default: json_abort("invalid cint_type");
@@ -859,10 +897,21 @@ uint64_t json_object_get_uint64(const struct json_object *jso)
case json_type_double:
// UINT64_MAX can't be exactly represented as a double
// 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;
}
if (JC_DOUBLE_C(jso)->c_double < 0)
{
errno = ERANGE;
return 0;
}
if (isnan(JC_DOUBLE_C(jso)->c_double))
{
errno = EINVAL;
return 0;
}
return (uint64_t)JC_DOUBLE_C(jso)->c_double;
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean;
case json_type_string:
@@ -1519,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);
}
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)
{
assert(json_object_get_type(jso) == json_type_array);

View File

@@ -172,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
* 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
* @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()
*/
JSON_EXPORT int json_object_put(struct json_object *obj);
@@ -191,6 +191,7 @@ JSON_EXPORT int json_object_put(struct json_object *obj);
json_type_object,
json_type_array,
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);
@@ -458,9 +459,9 @@ JSON_EXPORT struct json_object *json_object_object_get(const struct json_object
* associated with the given field name.
*
* 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);
/** Delete the given json_object field
@@ -560,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
* @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);
@@ -622,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,
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)
*
* *No* reference counts will be changed, and ownership of the returned
@@ -673,7 +693,7 @@ 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.
* 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
* 1 if it has a non zero length.
* 1 if it has a non zero length.
* If any other object type is passed 0 will be returned, even non-empty
* json_type_array and json_type_object objects.
*
@@ -719,9 +739,12 @@ JSON_EXPORT struct json_object *json_object_new_uint64(uint64_t i);
/** Get the int value of a json_object
*
* 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
* 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)
* double objects will return their integer conversion except for NaN values
* which return INT32_MIN and the errno is set to EINVAL.
* 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.
* If the value of too big or too small to fit into 32-bit, INT32_MAX or
@@ -763,8 +786,12 @@ JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val);
/** Get the int value of a json_object
*
* 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
* parsed as an int64. If no conversion exists then 0 is returned.
* double objects will return their int64 conversion except for NaN values
* 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
* whether or not conversion was successful (it does not clear the value for
@@ -778,8 +805,12 @@ JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj);
/** Get the uint value of a json_object
*
* 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
* parsed as an uint64. If no conversion exists then 0 is returned.
* double objects will return their uint64 conversion except for NaN values
* 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
* whether or not conversion was successful (it does not clear the value for
@@ -1023,7 +1054,7 @@ JSON_EXPORT struct json_object *json_object_new_null(void);
*
* @param obj1 the first 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);

View File

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

@@ -15,7 +15,9 @@
#include <stdlib.h>
#include <string.h>
#include "json_object_private.h"
#include "json_pointer.h"
#include "json_pointer_private.h"
#include "strdup_compat.h"
#include "vasprintf_compat.h"
@@ -41,10 +43,9 @@ 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, size_t *idx)
static int is_valid_index(const char *path, size_t *idx)
{
size_t i, len = strlen(path);
long int idx_val = -1;
/* 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
*/
@@ -53,7 +54,7 @@ static int is_valid_index(struct json_object *jo, const char *path, size_t *idx)
if (is_plain_digit(path[0]))
{
*idx = (path[0] - '0');
goto check_oob;
return 1;
}
errno = EINVAL;
return 0;
@@ -74,34 +75,27 @@ static int is_valid_index(struct json_object *jo, const char *path, size_t *idx)
}
}
idx_val = strtol(path, NULL, 10);
if (idx_val < 0)
{
errno = EINVAL;
return 0;
}
*idx = idx_val;
check_oob:
len = json_object_array_length(jo);
if (*idx >= len)
{
errno = ENOENT;
return 0;
}
// We know it's all digits, so the only error case here is overflow,
// but ULLONG_MAX will be longer than any array length so that's ok.
*idx = strtoull(path, NULL, 10);
return 1;
}
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))
{
size_t idx;
if (!is_valid_index(obj, path, &idx))
if (!is_valid_index(path, idx))
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 (value)
@@ -127,7 +121,8 @@ 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,
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))
{
@@ -135,16 +130,25 @@ static int json_pointer_set_single_path(struct json_object *parent, const char *
/* RFC (Chapter 4) states that '-' may be used to add new elements to an array */
if (path[0] == '-' && path[1] == '\0')
return json_object_array_add(parent, value);
if (!is_valid_index(parent, path, &idx))
if (!is_valid_index(path, &idx))
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(),
* and we should still be good here
*/
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
* (like string, int, bool).i.e. add a sub-object to it
@@ -153,9 +157,11 @@ static int json_pointer_set_single_path(struct json_object *parent, const char *
return -1;
}
static int json_pointer_get_recursive(struct json_object *obj, char *path,
struct json_object **value)
static int json_pointer_result_get_recursive(struct json_object *obj, char *path,
struct json_pointer_get_result *res)
{
struct json_object *parent_obj = obj;
size_t idx = 0;
char *endp;
int rc;
@@ -172,24 +178,47 @@ static int json_pointer_get_recursive(struct json_object *obj, char *path,
*endp = '\0';
/* 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;
if (endp)
{
/* Put the slash back, so that the sanity check passes on next recursion level */
*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 */
if (value)
*value = obj;
if (res) {
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;
}
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;
int rc;
@@ -202,8 +231,10 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje
if (path[0] == '\0')
{
if (res)
*res = obj;
res->parent = NULL;
res->obj = obj;
res->key_in_parent = NULL;
res->index_in_parent = UINT32_MAX;
return 0;
}
@@ -213,12 +244,30 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje
errno = ENOMEM;
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);
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, ...)
{
char *path_copy = NULL;
@@ -245,14 +294,16 @@ int json_pointer_getf(struct json_object *obj, struct json_object **res, const c
goto out;
}
rc = json_pointer_get_recursive(obj, path_copy, res);
rc = json_pointer_object_get_recursive(obj, path_copy, res);
out:
free(path_copy);
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;
char *path_copy = NULL;
@@ -282,7 +333,7 @@ int json_pointer_set(struct json_object **obj, const char *path, struct json_obj
if ((endp = strrchr(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 */
@@ -292,14 +343,28 @@ int json_pointer_set(struct json_object **obj, const char *path, struct json_obj
return -1;
}
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);
if (rc)
return rc;
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,
@@ -347,15 +412,64 @@ int json_pointer_setf(struct json_object **obj, struct json_object *value, const
}
*endp = '\0';
rc = json_pointer_get_recursive(*obj, path_copy, &set);
rc = json_pointer_object_get_recursive(*obj, path_copy, &set);
if (rc)
goto out;
set_single_path:
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:
free(path_copy);
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()'
* 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 path a (RFC6901) string notation for the sub-object to retrieve
* @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.
*
* 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())
*
* 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.
* (Just that single decrement that was mentioned above).
*
* Note that the 'path' string supports 'printf()' type arguments, so, whatever
* is added after the 'value' param will be treated as an argument for 'path'
* Example: json_pointer_set(obj, "/foo/%d/%s", value, 0, bak)
* This means, that you need to escape '%' with '%%' (just like in printf())
* @warning This function is vulnerable to an OOM.
* To prevent this, use the safer variant 'json_pointer_set_with_limit_index()'
* or the flexible 'json_pointer_set_with_array_cb()' with 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
@@ -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.
*
* 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())
*
* 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,
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
}
#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

@@ -46,7 +46,7 @@
#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 */
#define strncasecmp _strnicmp
#elif !HAVE_STRNCASECMP
@@ -104,7 +104,6 @@ static const char *json_tokener_errors[] = {
"success",
"continue",
"nesting too deep",
"out of memory",
"unexpected end of data",
"unexpected character",
"null expected",
@@ -117,7 +116,8 @@ static const char *json_tokener_errors[] = {
"invalid string sequence",
"expected comment",
"invalid utf-8 string",
"buffer size overflow"
"buffer size overflow",
"out of memory"
};
/* clang-format on */
@@ -145,8 +145,8 @@ enum json_tokener_error json_tokener_get_error(struct json_tokener *tok)
}
/* Stuff for decoding unicode sequences */
#define IS_HIGH_SURROGATE(uc) (((uc)&0xFC00) == 0xD800)
#define IS_LOW_SURROGATE(uc) (((uc)&0xFC00) == 0xDC00)
#define IS_HIGH_SURROGATE(uc) (((uc)&0xFFFFFC00) == 0xD800)
#define IS_LOW_SURROGATE(uc) (((uc)&0xFFFFFC00) == 0xDC00)
#define DECODE_SURROGATE_PAIR(hi, lo) ((((hi)&0x3FF) << 10) + ((lo)&0x3FF) + 0x10000)
static unsigned char utf8_replacement_char[3] = {0xEF, 0xBF, 0xBD};
@@ -154,6 +154,9 @@ struct json_tokener *json_tokener_new_ex(int depth)
{
struct json_tokener *tok;
if (depth < 1)
return NULL;
tok = (struct json_tokener *)calloc(1, sizeof(struct json_tokener));
if (!tok)
return NULL;
@@ -182,6 +185,8 @@ struct json_tokener *json_tokener_new(void)
void json_tokener_free(struct json_tokener *tok)
{
if (!tok)
return;
json_tokener_reset(tok);
if (tok->pb)
printbuf_free(tok->pb);
@@ -226,7 +231,10 @@ struct json_object *json_tokener_parse_verbose(const char *str, enum json_tokene
tok = json_tokener_new();
if (!tok)
{
*error = json_tokener_error_memory;
return NULL;
}
obj = json_tokener_parse_ex(tok, str, -1);
*error = tok->err;
if (tok->err != json_tokener_success
@@ -337,13 +345,32 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
#ifdef HAVE_USELOCALE
{
#ifdef HAVE_DUPLOCALE
locale_t duploc = duplocale(oldlocale);
newloc = newlocale(LC_NUMERIC_MASK, "C", duploc);
if (newloc == NULL)
if (duploc == NULL && errno == ENOMEM)
{
freelocale(duploc);
tok->err = json_tokener_error_memory;
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);
}
#elif defined(HAVE_SETLOCALE)
@@ -354,7 +381,10 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
{
oldlocale = strdup(tmplocale);
if (oldlocale == NULL)
{
tok->err = json_tokener_error_memory;
return NULL;
}
}
setlocale(LC_NUMERIC, "C");
}
@@ -661,6 +691,12 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
state = json_tokener_state_string_escape;
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))
{
printbuf_memappend_checked(tok->pb, case_start,
@@ -1214,6 +1250,12 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
state = json_tokener_state_string_escape;
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))
{
printbuf_memappend_checked(tok->pb, case_start,
@@ -1249,7 +1291,11 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char *
goto redo_char;
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);
obj_field_name = NULL;
saved_state = json_tokener_state_object_sep;

View File

@@ -28,7 +28,6 @@ enum json_tokener_error
json_tokener_success,
json_tokener_continue,
json_tokener_error_depth,
json_tokener_error_memory,
json_tokener_error_parse_eof,
json_tokener_error_parse_unexpected,
json_tokener_error_parse_null,
@@ -41,7 +40,8 @@ enum json_tokener_error
json_tokener_error_parse_string,
json_tokener_error_parse_comment,
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 */
};
/**
@@ -206,6 +206,7 @@ JSON_EXPORT struct json_tokener *json_tokener_new(void);
/**
* Allocate a new json_tokener with a custom max nesting depth.
* The depth must be at least 1.
* @see JSON_TOKENER_DEFAULT_DEPTH
*/
JSON_EXPORT struct json_tokener *json_tokener_new_ex(int depth);
@@ -230,7 +231,7 @@ JSON_EXPORT void json_tokener_reset(struct json_tokener *tok);
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`.
* @see json_tokener_parse()
* @see json_tokener_parse_ex()

View File

@@ -37,13 +37,15 @@
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef WIN32
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <io.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
#endif

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
#if defined(_MSC_VER) || defined(__MINGW32__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h> /* Get InterlockedCompareExchange */
#endif

View File

@@ -334,8 +334,9 @@ int lh_table_resize(struct lh_table *t, int new_size);
/**
* @deprecated Don't use this outside of linkhash.h:
*/
#if (defined(AIX_CC) || (defined(_MSC_VER) && (_MSC_VER <= 1800)) )
/* VS2010 can't handle inline funcs, so skip it there */
#if !defined (__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
/* 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
#else
#define _LH_INLINE inline

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

@@ -46,7 +46,7 @@ static void do_cpuid(int regs[], int h)
/* 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 do_cpuid __cpuid
@@ -129,7 +129,7 @@ static int get_rdrand_seed(void)
#endif
#if defined _MSC_VER
#if defined _MSC_VER && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))
#if _MSC_VER >= 1700
#define HAVE_RDRAND 1
@@ -229,6 +229,7 @@ static int get_dev_random_seed(int *seed)
if ((buf.st_mode & S_IFCHR) == 0)
return -1;
/* coverity[toctou] */
int fd = open(dev_random_file, O_RDONLY);
if (fd < 0)
{
@@ -253,7 +254,7 @@ static int get_dev_random_seed(int *seed)
/* get_cryptgenrandom_seed */
#ifdef WIN32
#ifdef _WIN32
#define HAVE_CRYPTGENRANDOM 1

View File

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

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.9...3.12) # see ../CMakeLists.txt for why
add_executable(test1Formatted test1.c parse_flags.c parse_flags.h)
target_compile_definitions(test1Formatted PRIVATE TEST_FORMATTED=1)
target_link_libraries(test1Formatted PRIVATE ${PROJECT_NAME})
@@ -39,6 +39,10 @@ set(ALL_TEST_NAMES
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})

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

@@ -189,6 +189,42 @@ void test_array_list_expand_internal(void)
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)
{
json_object *my_string, *my_int, *my_null, *my_object, *my_array;
@@ -253,6 +289,8 @@ int main(int argc, char **argv)
json_object_put(my_array);
test_array_insert_idx();
test_array_del_idx();
test_array_list_expand_internal();

View File

@@ -36,6 +36,7 @@ int main(int argc, char **argv)
\"array_with_zero\": [ 0 ],\n\
\"empty_object\": {},\n\
\"nonempty_object\": { \"a\": 123 },\n\
\"nan\": NaN,\n\
}";
/* Note: 2147483649 = INT_MAX + 2 */
/* Note: 9223372036854775809 = INT64_MAX + 2 */
@@ -62,6 +63,7 @@ int main(int argc, char **argv)
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.
printf("\n================================\n");
@@ -75,6 +77,7 @@ int main(int argc, char **argv)
checktype(new_obj, "int64_number");
checktype(new_obj, "negative_number");
checktype(new_obj, "a_null");
checktype(new_obj, "nan");
json_object_put(new_obj);

View File

@@ -12,6 +12,7 @@ Parsed input: {
"array_with_zero": [ 0 ],
"empty_object": {},
"nonempty_object": { "a": 123 },
"nan": NaN,
}
Result is not NULL
new_obj.string_of_digits json_object_get_type()=string
@@ -92,6 +93,12 @@ 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
@@ -104,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.negative_number : 0,0,0,1,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

@@ -4,9 +4,12 @@
#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"
@@ -25,6 +28,7 @@
#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)
{
@@ -41,10 +45,15 @@ int main(int argc, char **argv)
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 == 0);
CHECK_GET_INT(N_I64(INT64_MIN), INT32_MIN && errno == 0);
CHECK_GET_INT(N_STR(I64_MAX_S), INT32_MAX && errno == 0);
CHECK_GET_INT(N_STR(I64_MIN_S), 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);
@@ -53,11 +62,17 @@ int main(int argc, char **argv)
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");

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

@@ -269,6 +269,22 @@ static void test_example_set(void)
printf("%s\n", json_object_get_string(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)

View File

@@ -113,6 +113,9 @@ static void test_basic_parse(void)
single_basic_parse("\"\\udd27\"", 0);
// Test with a "short" high surrogate
single_basic_parse("[9,'\\uDAD", 0);
single_basic_parse("\"[9,'\\uDAD\"", 0);
// Test with a supplemental character that looks like a high surrogate
single_basic_parse("\"\\uD836\\uDE87\"", 0);
single_basic_parse("null", 0);
single_basic_parse("NaN", 0);
single_basic_parse("-NaN", 0); /* non-sensical, returns null */
@@ -297,6 +300,7 @@ struct incremental_step
{"d", -1, -1, json_tokener_continue, 0, 0},
{"1", -1, -1, json_tokener_continue, 0, 0},
{"e\"", -1, -1, json_tokener_success, 1, 0},
/* parse two char at every time */
{"\"\\u", -1, -1, json_tokener_continue, 0, 0},
{"d8", -1, -1, json_tokener_continue, 0, 0},
@@ -322,10 +326,20 @@ struct incremental_step
{"\"fff \\ud83d\\ude", -1, -1, json_tokener_continue, 0, 0},
{"00 bar\"", -1, -1, json_tokener_success, 1, 0},
/* Check a utf-8 char (a+umlaut) that has bytes that look negative when
char are signed (see also control char check below) */
{"\"\xc3\xa4\"", -1, -1, json_tokener_success, 1, 0},
{"\"\xc3\xa4\"", -1, -1, json_tokener_success, 1, JSON_TOKENER_STRICT},
/* Check that json_tokener_reset actually resets */
{"{ \"foo", -1, -1, json_tokener_continue, 1, 0},
{": \"bar\"}", -1, 0, json_tokener_error_parse_unexpected, 1, 0},
/* Check a supplemental code point that looks like a high surrogate */
{"\"\\uD836", -1, -1, json_tokener_continue, 0, 0},
{"\\uDE87", -1, -1, json_tokener_continue, 0, 0},
{"\"", -1, -1, json_tokener_success, 1, 0},
/* Check incremental parsing with trailing characters */
{"{ \"foo", -1, -1, json_tokener_continue, 0, 0},
{"\": {\"bar", -1, -1, json_tokener_continue, 0, 0},
@@ -394,8 +408,8 @@ struct incremental_step
{"Infinity", 9, 8, json_tokener_success, 1, 0},
{"infinity", 9, 8, json_tokener_success, 1, 0},
{"-infinity", 10, 9, json_tokener_success, 1, 0},
{"infinity", 9, 0, json_tokener_error_parse_unexpected, 1, JSON_TOKENER_STRICT},
{"-infinity", 10, 9, json_tokener_success, 1, 0},
{"-infinity", 10, 1, json_tokener_error_parse_unexpected, 1, JSON_TOKENER_STRICT},
{"inf", 3, 3, json_tokener_continue, 0, 0},
@@ -462,12 +476,15 @@ struct incremental_step
{"[18446744073709551616]", 23, 21, json_tokener_error_parse_number, 1, JSON_TOKENER_STRICT},
/* XXX this seems like a bug, should fail with _error_parse_number instead */
{"18446744073709551616", 21, 20, json_tokener_success, 1, 0},
{"18446744073709551616", 21, 20, json_tokener_error_parse_eof, 1, JSON_TOKENER_STRICT},
/* Exceeding integer limits as double parse OK */
{"[9223372036854775808.0]", 24, 23, json_tokener_success, 1, 0},
{"[-9223372036854775809.0]", 25, 24, json_tokener_success, 1, 0},
{"[-9223372036854775809.0]", 25, 24, json_tokener_success, 1, JSON_TOKENER_STRICT},
{"[18446744073709551615.0]", 25, 24, json_tokener_success, 1, 0},
{"[18446744073709551616.0]", 25, 24, json_tokener_success, 1, 0},
{"[18446744073709551616.0]", 25, 24, json_tokener_success, 1, JSON_TOKENER_STRICT},
/* offset=1 because "n" is the start of "null". hmm... */
@@ -524,6 +541,7 @@ struct incremental_step
{"\"\\a\"", -1, 2, json_tokener_error_parse_string, 1, 0},
/* Check '\'' in strict model */
{"\'foo\'", -1, 5, json_tokener_success, 1, 0},
{"\'foo\'", -1, 0, json_tokener_error_parse_unexpected, 1, JSON_TOKENER_STRICT},
/* Parse array/object */
@@ -535,7 +553,7 @@ struct incremental_step
{"{\"a\":}", -1, 5, json_tokener_error_parse_unexpected, 1, 0},
{"{\"a\":1,\"a\":2}", -1, -1, json_tokener_success, 1, 0},
{"\"a\":1}", -1, 3, json_tokener_success, 1, 0},
{"{\"a\":1", -1, -1, json_tokener_continue, 1, 0},
{"{\"a\":1", -1, -1, json_tokener_continue, 1, 0}, //}
{"[,]", -1, 1, json_tokener_error_parse_unexpected, 1, 0},
{"[,1]", -1, 1, json_tokener_error_parse_unexpected, 1, 0},
@@ -544,9 +562,10 @@ struct incremental_step
* in what we accept (up to a point).
*/
{"[1,2,3,]", -1, -1, json_tokener_success, 0, 0},
{"[1,2,,3,]", -1, 5, json_tokener_error_parse_unexpected, 0, 0},
{"[1,2,3,]", -1, 7, json_tokener_error_parse_unexpected, 1, JSON_TOKENER_STRICT},
{"[1,2,,3,]", -1, 5, json_tokener_error_parse_unexpected, 0, 0},
{"[1,2,,3,]", -1, 5, json_tokener_error_parse_unexpected, 0, JSON_TOKENER_STRICT},
{"{\"a\":1,}", -1, 7, json_tokener_error_parse_unexpected, 1, JSON_TOKENER_STRICT},
// utf-8 test
@@ -595,6 +614,80 @@ struct incremental_step
{"\x7b\x22\x31\x81\x22\x3a\x31\x7d", -1, 3, json_tokener_error_parse_utf8_string, 1,
JSON_TOKENER_VALIDATE_UTF8},
// Note, current asciiz APIs can't parse \x00, skip it
{ "\"0\x01\x02\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
"\x10\x11\x12\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\"",
-1, -1, json_tokener_success, 1, 0 },
{ "{\"0\x01\x02\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
"\x10\x11\x12\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\":1}",
-1, -1, json_tokener_success, 1, 0 },
// Test control chars again, this time in strict mode, which should fail
{ "\"\x01\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x02\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x03\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x04\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x05\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x06\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x07\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x08\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x09\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x0a\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x0b\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x0c\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x0d\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x0e\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x0f\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x10\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x11\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x12\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x13\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x14\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x15\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x16\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x17\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x18\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x19\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x1a\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x1b\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x1c\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x1d\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x1e\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "\"\x1f\"", -1, 1, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x01\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x02\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x03\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x04\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x05\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x06\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x07\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x08\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x09\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x0a\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x0b\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x0c\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x0d\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x0e\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x0f\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x10\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x11\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x12\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x13\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x14\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x15\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x16\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x17\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x18\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x19\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x1a\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x1b\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x1c\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x1d\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x1e\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{ "{\"\x1f\":1}", -1, 2, json_tokener_error_parse_string, 1, JSON_TOKENER_STRICT },
{NULL, -1, -1, json_tokener_success, 0, 0},
};
@@ -618,7 +711,7 @@ static void test_incremental_parse(void)
printf("json_tokener_parse(%s) ... ", string_to_parse);
new_obj = json_tokener_parse(string_to_parse);
if (new_obj == NULL)
puts("got error as expected");
printf("%s", "got error as expected\n");
/* test incremental parsing in various forms */
tok = json_tokener_new();

View File

@@ -13,6 +13,8 @@ new_obj.to_string("\ud840\u4e16")="<22>世"
new_obj.to_string("\ud840")="<22>"
new_obj.to_string("\udd27")="<22>"
new_obj.to_string([9,'\uDAD)=null
new_obj.to_string("[9,'\uDAD")=null
new_obj.to_string("\uD836\uDE87")="𝪇"
new_obj.to_string(null)=null
new_obj.to_string(NaN)=NaN
new_obj.to_string(-NaN)=null
@@ -134,8 +136,13 @@ json_tokener_parse_ex(tok, "fff \ud834\udd, 15) ... OK: got correct error: cont
json_tokener_parse_ex(tok, 1e bar" , 7) ... OK: got object of type [string]: "fff 𝄞 bar"
json_tokener_parse_ex(tok, "fff \ud83d\ude, 15) ... OK: got correct error: continue
json_tokener_parse_ex(tok, 00 bar" , 7) ... OK: got object of type [string]: "fff 😀 bar"
json_tokener_parse_ex(tok, "ä" , 4) ... OK: got object of type [string]: "ä"
json_tokener_parse_ex(tok, "ä" , 4) ... OK: got object of type [string]: "ä"
json_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue
json_tokener_parse_ex(tok, : "bar"} , 8) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, "\uD836 , 7) ... OK: got correct error: continue
json_tokener_parse_ex(tok, \uDE87 , 6) ... OK: got correct error: continue
json_tokener_parse_ex(tok, " , 1) ... OK: got object of type [string]: "𝪇"
json_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue
json_tokener_parse_ex(tok, ": {"bar , 8) ... OK: got correct error: continue
json_tokener_parse_ex(tok, ":13}}XXXX , 10) ... OK: got object of type [object]: { "foo": { "bar": 13 } }
@@ -177,8 +184,8 @@ json_tokener_parse_ex(tok, null , 4) ... OK: got correct error: continu
json_tokener_parse_ex(tok, null , 5) ... OK: got object of type [null]: null
json_tokener_parse_ex(tok, Infinity , 9) ... OK: got object of type [double]: Infinity
json_tokener_parse_ex(tok, infinity , 9) ... OK: got object of type [double]: Infinity
json_tokener_parse_ex(tok, -infinity , 10) ... OK: got object of type [double]: -Infinity
json_tokener_parse_ex(tok, infinity , 9) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, -infinity , 10) ... OK: got object of type [double]: -Infinity
json_tokener_parse_ex(tok, -infinity , 10) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, inf , 3) ... OK: got correct error: continue
json_tokener_parse_ex(tok, inity , 6) ... OK: got object of type [double]: Infinity
@@ -218,11 +225,14 @@ json_tokener_parse_ex(tok, [-9223372036854775809], 23) ... OK: got correct erro
json_tokener_parse_ex(tok, [18446744073709551615], 23) ... OK: got object of type [array]: [ 18446744073709551615 ]
json_tokener_parse_ex(tok, [18446744073709551616], 23) ... OK: got object of type [array]: [ 18446744073709551615 ]
json_tokener_parse_ex(tok, [18446744073709551616], 23) ... OK: got correct error: number expected
json_tokener_parse_ex(tok, 18446744073709551616, 21) ... OK: got object of type [int]: 18446744073709551615
json_tokener_parse_ex(tok, 18446744073709551616, 21) ... OK: got correct error: unexpected end of data
json_tokener_parse_ex(tok, [9223372036854775808.0], 24) ... OK: got object of type [array]: [ 9223372036854775808.0 ]
json_tokener_parse_ex(tok, [-9223372036854775809.0], 25) ... OK: got object of type [array]: [ -9223372036854775809.0 ]
json_tokener_parse_ex(tok, [-9223372036854775809.0], 25) ... OK: got object of type [array]: [ -9223372036854775809.0 ]
json_tokener_parse_ex(tok, [18446744073709551615.0], 25) ... OK: got object of type [array]: [ 18446744073709551615.0 ]
json_tokener_parse_ex(tok, [18446744073709551616.0], 25) ... OK: got object of type [array]: [ 18446744073709551616.0 ]
json_tokener_parse_ex(tok, [18446744073709551616.0], 25) ... OK: got object of type [array]: [ 18446744073709551616.0 ]
json_tokener_parse_ex(tok, noodle , 7) ... OK: got correct error: null expected
json_tokener_parse_ex(tok, naodle , 7) ... OK: got correct error: null expected
json_tokener_parse_ex(tok, track , 6) ... OK: got correct error: boolean expected
@@ -250,6 +260,7 @@ json_tokener_parse_ex(tok, "\t" , 4) ... OK: got object of type [string
json_tokener_parse_ex(tok, "\/" , 4) ... OK: got object of type [string]: "\/"
json_tokener_parse_ex(tok, "/" , 3) ... OK: got object of type [string]: "\/"
json_tokener_parse_ex(tok, "\a" , 4) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, 'foo' , 5) ... OK: got object of type [string]: "foo"
json_tokener_parse_ex(tok, 'foo' , 5) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, [1,2,3] , 7) ... OK: got object of type [array]: [ 1, 2, 3 ]
json_tokener_parse_ex(tok, [1,2,3} , 7) ... OK: got correct error: array value separator ',' expected
@@ -263,8 +274,9 @@ json_tokener_parse_ex(tok, {"a":1 , 6) ... OK: got correct error: continu
json_tokener_parse_ex(tok, [,] , 3) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, [,1] , 4) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, [1,2,3,] , 8) ... OK: got object of type [array]: [ 1, 2, 3 ]
json_tokener_parse_ex(tok, [1,2,,3,] , 9) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, [1,2,3,] , 8) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, [1,2,,3,] , 9) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, [1,2,,3,] , 9) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, {"a":1,} , 8) ... OK: got correct error: unexpected character
json_tokener_parse_ex(tok, "123asc$%&" , 11) ... OK: got object of type [string]: "123asc$%&"
json_tokener_parse_ex(tok, "123asc$%&" , 11) ... OK: got object of type [string]: "123asc$%&"
@@ -288,5 +300,73 @@ json_tokener_parse_ex(tok, "\ud855
json_tokener_parse_ex(tok, "\ud0031<33>" , 10) ... OK: got correct error: invalid utf-8 string
json_tokener_parse_ex(tok, 11<31>11 , 5) ... OK: got correct error: invalid utf-8 string
json_tokener_parse_ex(tok, {"1<>":1} , 8) ... OK: got correct error: invalid utf-8 string
End Incremental Tests OK=198 ERROR=0
json_tokener_parse_ex(tok, "0
", 36) ... OK: got object of type [string]: "0\u0001\u0002\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"
json_tokener_parse_ex(tok, {"0
":1}, 40) ... OK: got object of type [object]: { "0\u0001\u0002\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f": 1 }
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, " " , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "
" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, " " , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, " " , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "
" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, "" , 3) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {" ":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"
":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {" ":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {" ":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"
":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence
json_tokener_parse_ex(tok, {"":1} , 7) ... OK: got correct error: invalid string sequence

View File

@@ -0,0 +1,249 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "json.h"
static const char *input_json_str = "{ "
"'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 "
"}";
static void test_example_set_with_limit_index(void)
{
struct json_object *jo2, *jo1 = json_tokener_parse(input_json_str);
size_t limit_index = 10;
// testing if json_pointer_set_with_limit_index() works as json_pointer_set()
assert(jo1 != NULL);
printf("PASSED - SET_WITH_LIMIT - LOADED TEST JSON\n");
printf("%s\n", json_object_get_string(jo1));
assert(0 == json_pointer_set_with_limit_index(&jo1, "/foo/1", json_object_new_string("cod"), limit_index));
assert(0 == strcmp("cod", json_object_get_string(json_object_array_get_idx(
json_object_object_get(jo1, "foo"), 1))));
printf("PASSED - SET_WITH_LIMIT - 'cod' in /foo/1\n");
assert(0 != json_pointer_set_with_limit_index(&jo1, "/fud/gaw", (jo2 = json_tokener_parse("[1,2,3]")), limit_index));
assert(errno == ENOENT);
printf("PASSED - SET_WITH_LIMIT - non-existing /fud/gaw\n");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/fud", json_object_new_object(), limit_index));
printf("PASSED - SET_WITH_LIMIT - /fud == {}\n");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/fud/gaw", jo2, limit_index)); /* re-using jo2 from above */
printf("PASSED - SET_WITH_LIMIT - /fug/gaw == [1,2,3]\n");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/fud/gaw/0", json_object_new_int(0), limit_index));
assert(0 == json_pointer_setf(&jo1, json_object_new_int(0), "%s%s/%d", "/fud", "/gaw", 0));
printf("PASSED - SET_WITH_LIMIT - /fug/gaw == [0,2,3]\n");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/fud/gaw/-", json_object_new_int(4), limit_index));
printf("PASSED - SET_WITH_LIMIT - /fug/gaw == [0,2,3,4]\n");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/", json_object_new_int(9), limit_index));
printf("PASSED - SET_WITH_LIMIT - / == 9\n");
jo2 = json_tokener_parse(
"{ 'foo': [ 'bar', 'cod' ], '': 9, 'a/b': 1, 'c%d': 2, 'e^f': 3, 'g|h': 4, 'i\\\\j': "
"5, 'k\\\"l': 6, ' ': 7, 'm~n': 8, 'fud': { 'gaw': [ 0, 2, 3, 4 ] } }");
assert(json_object_equal(jo2, jo1));
printf("PASSED - SET_WITH_LIMIT - Final JSON is: %s\n", json_object_get_string(jo1));
json_object_put(jo2);
assert(0 == json_pointer_set_with_limit_index(&jo1, "", json_object_new_int(10), limit_index));
assert(10 == json_object_get_int(jo1));
printf("%s\n", json_object_get_string(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_with_limit_index(&jo1, "/7", json_object_new_int(7), limit_index));
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);
// testing with limit_index
jo1 = json_tokener_parse("{'foo': ['bar', 'baz']}");
jo2 = json_tokener_parse("{'foo': ['bar', 'cod']}");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/foo/1", json_object_new_string("cod"), limit_index));
assert(json_object_equal(jo1, jo2));
printf("PASSED - SET_LIMIT - Set value within limit (/foo/1 with limit 10)\n");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/bar", json_object_new_string("new_field"), limit_index));
printf("PASSED - SET_LIMIT - Set value on an object (limit is ignored)\n");
assert(0 == json_pointer_set_with_limit_index(&jo1, "/foo/20", json_object_new_string("big_index"), (size_t)-1));
printf("PASSED - SET_LIMIT - Set value with limit_index = -1 (no limit)\n");
json_object_put(jo1);
json_object_put(jo2);
}
static void test_wrong_inputs_set_with_limit_index(void)
{
struct json_object *jo2, *jo1 = json_tokener_parse(input_json_str);
size_t limit_index = 10;
// testing if json_pointer_set_with_limit_index() works as json_pointer_set()
assert(jo1 != NULL);
printf("PASSED - SET_WITH_LIMIT - LOADED TEST JSON\n");
printf("%s\n", json_object_get_string(jo1));
assert(0 != json_pointer_set_with_limit_index(NULL, NULL, NULL, limit_index));
assert(0 != json_pointer_set_with_limit_index(&jo1, NULL, NULL, limit_index));
printf("PASSED - SET_WITH_LIMIT - failed with NULL params for input json & path\n");
assert(0 != json_pointer_set_with_limit_index(&jo1, "foo/bar", (jo2 = json_object_new_string("cod")), limit_index));
printf("PASSED - SET_WITH_LIMIT - failed 'cod' with path 'foo/bar'\n");
json_object_put(jo2);
// assert(0 !=
// json_pointer_setf(&jo1, (jo2 = json_object_new_string("cod")), "%s", "foo/bar"));
// printf("PASSED - SET_WITH_LIMIT - failed 'cod' with path 'foo/bar'\n");
// json_object_put(jo2);
assert(0 != json_pointer_set_with_limit_index(&jo1, "0", (jo2 = json_object_new_string("cod")), limit_index));
printf("PASSED - SET_WITH_LIMIT - failed with invalid array index'\n");
json_object_put(jo2);
jo2 = json_object_new_string("whatever");
assert(0 != json_pointer_set_with_limit_index(&jo1, "/fud/gaw", jo2, limit_index));
assert(0 == json_pointer_set_with_limit_index(&jo1, "/fud", json_object_new_object(), limit_index));
assert(0 == json_pointer_set_with_limit_index(&jo1, "/fud/gaw", jo2, limit_index)); /* re-using jo2 from above */
// ownership of jo2 transferred into jo1
jo2 = json_object_new_int(0);
assert(0 != json_pointer_set_with_limit_index(&jo1, "/fud/gaw/0", jo2, limit_index));
json_object_put(jo2);
jo2 = json_object_new_int(0);
assert(0 != json_pointer_set_with_limit_index(&jo1, "/fud/gaw/", jo2, limit_index));
json_object_put(jo2);
printf("PASSED - SET_WITH_LIMIT - failed to set index to non-array\n");
// assert(0 == json_pointer_setf(&jo1, json_object_new_string("cod"), "%s", "\0"));
json_object_put(jo1);
// testing with limit_index
jo1 = json_tokener_parse("{'foo': ['bar', 'baz']}");
errno = 0;
jo2 = json_object_new_string("out_of_bounds");
assert(0 != json_pointer_set_with_limit_index(&jo1, "/foo/20", jo2, limit_index));
assert(errno == EINVAL);
printf("PASSED - SET_LIMIT - Failed to set index 20 with limit 10\n");
// The value object was not consumed, so we must put it.
json_object_put(jo2);
// corner case: setting an index that equals the limit (should be ok, as it's idx > max_idx)
errno = 0;
assert(0 == json_pointer_set_with_limit_index(&jo1, "/foo/10", json_object_new_string("at_the_limit"), limit_index));
printf("PASSED - SET_LIMIT - Succeeded to set index 10 with limit 10\n");
json_object_put(jo1);
}
// callback for testing json_pointer_set_with_cb()
static int test_cb_print_msg(json_object *parent, const char *key, size_t idx,
json_object *value, void *priv)
{
printf("PASSED - SET_WITH_CB - This callback is called\n");
json_object_put(value);
return 0;
}
// callback for testing json_pointer_set_with_cb() with rejection logic
static int test_cb_reject_logic(json_object *parent, const char *key, size_t idx,
json_object *value, void *priv)
{
// Reject any operation if the key is "reject"
if (key && strcmp(key, "reject") == 0)
{
printf("PASSED - SET_WITH_CB - Callback correctly identified key 'reject' to reject\n");
// Note: caller of json_pointer_set must free value
return -1;
}
json_object_put(value);
return 0;
}
static void test_set_with_cb(void)
{
struct json_object *jo2, *jo1 = json_tokener_parse(input_json_str);
size_t limit_index = 5;
assert(jo1 != NULL);
printf("PASSED - SET_WITH_CB - LOADED TEST JSON\n");
printf("%s\n", json_object_get_string(jo1));
assert(0 == json_pointer_set_with_cb(&jo1, "/foo/1", json_object_new_string("cod"), test_cb_print_msg, 1, NULL));
printf("PASSED - SET_WITH_CB - callback test_cb_print_msg for /foo/1\n");
assert(0 == json_pointer_set_with_cb(&jo1, "/foo/4", json_object_new_string("in"), json_object_array_put_with_idx_limit_cb, 0, &limit_index));
printf("PASSED - SET_WITH_CB - callback json_object_array_put_with_idx_limit_cb for /foo/4 with limit_index 5\n");
assert(0 == json_pointer_set_with_cb(&jo1, "/foo/5", json_object_new_string("border"), json_object_array_put_with_idx_limit_cb, 0, &limit_index));
printf("PASSED - SET_WITH_CB - failed with callback json_object_array_put_with_idx_limit_cb for /foo/5 with limit_index 5\n");
assert(0 != json_pointer_set_with_cb(&jo1, "/foo/10", (jo2 = json_object_new_string("out")), json_object_array_put_with_idx_limit_cb, 0, &limit_index));
json_object_put(jo2);
assert(errno == EINVAL);
printf("PASSED - SET_WITH_CB - failed with callback json_object_array_put_with_idx_limit_cb for /foo/10 with limit_index 5\n");
assert(0 != json_pointer_set_with_cb(&jo1, "/foo/2", (jo2 = json_object_new_string("null_priv")), json_object_array_put_with_idx_limit_cb, 0, NULL));
json_object_put(jo2);
assert(errno == EFAULT);
printf("PASSED - SET_WITH_CB - failed with callback json_object_array_put_with_idx_limit_cb with NULL priv\n");
json_object_put(jo1);
jo1 = json_tokener_parse("{'foo': 'bar'}");
assert(jo1 != NULL);
assert(0 == json_pointer_set_with_cb(&jo1, "/foo", json_object_new_string("cod"), test_cb_print_msg, 1, NULL));
printf("PASSED - SET_WITH_CB - cb_handles_obj=1: callback was triggered for object operation\n");
assert(0 == json_pointer_set_with_cb(&jo1, "/new_key", json_object_new_string("new_value"), test_cb_print_msg, 0, NULL));
printf("PASSED - SET_WITH_CB - cb_handles_obj=0: callback was NOT triggered for object operation, default logic was used\n");
json_object_put(jo1);
// testing rejection logic callback
jo1 = json_tokener_parse("{'data': {} }");
assert(jo1 != NULL);
assert(0 == json_pointer_set_with_cb(&jo1, "/data/accept", json_object_new_string("accepted_value"), test_cb_reject_logic, 1, NULL));
printf("PASSED - SET_WITH_CB - Rejection callback approved an allowed key\n");
assert(0 != json_pointer_set_with_cb(&jo1, "/data/reject", (jo2 = json_object_new_string("rejected_value")), test_cb_reject_logic, 1, NULL));
json_object_put(jo2);
printf("PASSED - SET_WITH_CB - Rejection callback rejected a forbidden key\n");
json_object_put(jo1);
}
int main(int argc, char **argv)
{
test_example_set_with_limit_index();
test_wrong_inputs_set_with_limit_index();
test_set_with_cb();
return 0;
}

View File

@@ -0,0 +1,36 @@
PASSED - SET_WITH_LIMIT - LOADED TEST JSON
{ "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 }
PASSED - SET_WITH_LIMIT - 'cod' in /foo/1
PASSED - SET_WITH_LIMIT - non-existing /fud/gaw
PASSED - SET_WITH_LIMIT - /fud == {}
PASSED - SET_WITH_LIMIT - /fug/gaw == [1,2,3]
PASSED - SET_WITH_LIMIT - /fug/gaw == [0,2,3]
PASSED - SET_WITH_LIMIT - /fug/gaw == [0,2,3,4]
PASSED - SET_WITH_LIMIT - / == 9
PASSED - SET_WITH_LIMIT - Final JSON is: { "foo": [ "bar", "cod" ], "": 9, "a\/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, "i\\j": 5, "k\"l": 6, " ": 7, "m~n": 8, "fud": { "gaw": [ 0, 2, 3, 4 ] } }
10
PASSED - SET_LIMIT - Set value within limit (/foo/1 with limit 10)
PASSED - SET_LIMIT - Set value on an object (limit is ignored)
PASSED - SET_LIMIT - Set value with limit_index = -1 (no limit)
PASSED - SET_WITH_LIMIT - LOADED TEST JSON
{ "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 }
PASSED - SET_WITH_LIMIT - failed with NULL params for input json & path
PASSED - SET_WITH_LIMIT - failed 'cod' with path 'foo/bar'
PASSED - SET_WITH_LIMIT - failed with invalid array index'
PASSED - SET_WITH_LIMIT - failed to set index to non-array
PASSED - SET_LIMIT - Failed to set index 20 with limit 10
PASSED - SET_LIMIT - Succeeded to set index 10 with limit 10
PASSED - SET_WITH_CB - LOADED TEST JSON
{ "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 }
PASSED - SET_WITH_CB - This callback is called
PASSED - SET_WITH_CB - callback test_cb_print_msg for /foo/1
PASSED - SET_WITH_CB - callback json_object_array_put_with_idx_limit_cb for /foo/4 with limit_index 5
PASSED - SET_WITH_CB - failed with callback json_object_array_put_with_idx_limit_cb for /foo/5 with limit_index 5
PASSED - SET_WITH_CB - failed with callback json_object_array_put_with_idx_limit_cb for /foo/10 with limit_index 5
PASSED - SET_WITH_CB - failed with callback json_object_array_put_with_idx_limit_cb with NULL priv
PASSED - SET_WITH_CB - This callback is called
PASSED - SET_WITH_CB - cb_handles_obj=1: callback was triggered for object operation
PASSED - SET_WITH_CB - cb_handles_obj=0: callback was NOT triggered for object operation, default logic was used
PASSED - SET_WITH_CB - Rejection callback approved an allowed key
PASSED - SET_WITH_CB - Callback correctly identified key 'reject' to reject
PASSED - SET_WITH_CB - Rejection callback rejected a forbidden key

View File

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

View File

@@ -2,11 +2,11 @@
#undef NDEBUG
#endif
#include "strerror_override.h"
#ifdef WIN32
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <io.h>
#include <windows.h>
#endif /* defined(WIN32) */
#endif /* defined(_WIN32) */
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
@@ -27,7 +27,9 @@
static void test_read_valid_with_fd(const char *testdir);
static void test_read_valid_nested_with_fd(const char *testdir);
static void test_read_nonexistant(void);
#ifndef _WIN32
static void test_read_closed(void);
#endif
static void test_write_to_file(void);
static void stat_and_cat(const char *file);
@@ -92,7 +94,12 @@ static void test_write_to_file(void)
static void stat_and_cat(const char *file)
{
struct stat sb;
int d = open(file, O_RDONLY);
int flags = O_RDONLY;
#ifdef O_BINARY
// This fixes Windows which otherwise opens this in text mode and returns different counts
flags |= O_BINARY;
#endif
int d = open(file, flags);
if (d < 0)
{
printf("FAIL: unable to open %s: %s\n", file, strerror(errno));
@@ -159,7 +166,10 @@ int main(int argc, char **argv)
test_read_valid_with_fd(testdir);
test_read_valid_nested_with_fd(testdir);
test_read_nonexistant();
#ifndef _WIN32
// Disabled because the Windows CRT causes a crash during this test that cannot be disabled/stopped/worked around
test_read_closed();
#endif
test_write_to_file();
test_read_fd_equal(testdir);
return EXIT_SUCCESS;
@@ -169,8 +179,12 @@ static void test_read_valid_with_fd(const char *testdir)
{
char filename[PATH_MAX];
(void)snprintf(filename, sizeof(filename), "%s/valid.json", testdir);
int d = open(filename, O_RDONLY);
int flags = O_RDONLY;
#ifdef O_BINARY
// This fixes Windows which otherwise opens this in text mode and returns different counts
flags |= O_BINARY;
#endif
int d = open(filename, flags);
if (d < 0)
{
fprintf(stderr, "FAIL: unable to open %s: %s\n", filename, strerror(errno));
@@ -194,8 +208,12 @@ static void test_read_valid_nested_with_fd(const char *testdir)
{
char filename[PATH_MAX];
(void)snprintf(filename, sizeof(filename), "%s/valid_nested.json", testdir);
int d = open(filename, O_RDONLY);
int flags = O_RDONLY;
#ifdef O_BINARY
// This fixes Windows which otherwise opens this in text mode and returns different counts
flags |= O_BINARY;
#endif
int d = open(filename, flags);
if (d < 0)
{
fprintf(stderr, "FAIL: unable to open %s: %s\n", filename, strerror(errno));
@@ -250,7 +268,7 @@ static void test_read_nonexistant(void)
json_util_get_last_err());
}
}
#ifndef _WIN32
static void test_read_closed(void)
{
// Test reading from a closed fd
@@ -258,6 +276,7 @@ static void test_read_closed(void)
if (d < 0)
{
puts("FAIL: unable to open");
return;
}
// Copy over to a fixed fd number so test output is consistent.
int fixed_d = 10;
@@ -281,6 +300,7 @@ static void test_read_closed(void)
"expecting NULL, EBADF, got:NULL, %s\n",
json_util_get_last_err());
}
#endif
static void test_read_fd_equal(const char *testdir)
{
@@ -288,8 +308,12 @@ static void test_read_fd_equal(const char *testdir)
(void)snprintf(filename, sizeof(filename), "%s/valid_nested.json", testdir);
json_object *jso = json_object_from_file(filename);
int d = open(filename, O_RDONLY);
int flags = O_RDONLY;
#ifdef O_BINARY
// This fixes Windows which otherwise opens this in text mode and returns different counts
flags |= O_BINARY;
#endif
int d = open(filename, flags);
if (d < 0)
{
fprintf(stderr, "FAIL: unable to open %s: %s\n", filename, strerror(errno));

View File

@@ -8,9 +8,9 @@
#include "snprintf_compat.h"
#ifndef WIN32
#ifndef _WIN32
#include <stdarg.h>
#endif /* !defined(WIN32) */
#endif /* !defined(_WIN32) */
#include <stdint.h>
#include <stdlib.h>
@@ -18,10 +18,10 @@
/* CAW: compliant version of vasprintf */
static int vasprintf(char **buf, const char *fmt, va_list ap)
{
#ifndef WIN32
#ifndef _WIN32
static char _T_emptybuffer = '\0';
va_list ap2;
#endif /* !defined(WIN32) */
#endif /* !defined(_WIN32) */
int chars;
char *b;
@@ -30,16 +30,16 @@ static int vasprintf(char **buf, const char *fmt, va_list ap)
return -1;
}
#ifdef WIN32
#ifdef _WIN32
chars = _vscprintf(fmt, ap);
#else /* !defined(WIN32) */
#else /* !defined(_WIN32) */
/* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite
* our buffer like on some 64bit sun systems... but hey, it's time to move on
*/
va_copy(ap2, ap);
chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap2);
va_end(ap2);
#endif /* defined(WIN32) */
#endif /* defined(_WIN32) */
if (chars < 0 || (size_t)chars + 1 > SIZE_MAX / sizeof(char))
{
return -1;