json_pointer_get_single_path() returned ENOENT when the array element
at an in-range index was NULL, which is how json-c stores a JSON null.
A null object member was already found, but on [0, null, 2] "/1" failed
with "not found", although RFC 6901 says it references the null value.
The array index is bounds-checked just before, so a NULL element now
resolves like a null member: json_pointer_get() returns 0 and sets
*res to NULL. This also fixes json_patch_apply(), which failed every
"test", "replace" and "remove" on a null array element, and every
"move" and "copy" from one, with "Did not find element referenced by
path field".
Added a json_pointer_get() test, and json_patch_tests.json cases that
mirror the existing null object member ones.
json_pointer_get_internal() decodes the last reference token in a
private copy of the path, then pointed key_in_parent at the same offset
in the caller's path, where the token is still escaped. For a path like
"/a~1b" that gave "a~1b" instead of "a/b", so json_patch's remove,
and the remove step of move, called json_object_object_del() with a key
that doesn't exist. Nothing was removed and the patch still reported
success; a move left the source member in place.
Point key_in_parent at the key as it is stored in the parent object
instead, which is always the decoded name.
Added remove and move cases with escaped member names to
json_patch_tests.json and refreshed test_json_patch.expected.
Discard the previous document's high surrogate when resetting the tokener.
Otherwise the next Unicode escape can add a replacement character or
combine with a surrogate from the abandoned document.
Extend the existing incremental parse tests for incomplete input and a
fatal error, using independent next-document string expectations.
Fixes#984.
Reported-by: GeekQAQ (GitHub)
Assisted-by: OpenAI Codex (AI)
Reviewed-by: GPT-6 Luna (AI)
json_patch_apply_move_copy() checks that "from" is not a proper prefix
of "path" per RFC 6902 section 4.4 (a location cannot be moved into one
of its own children), but it did so with a plain strncmp() over the raw
pointer strings, without checking that the shared run of characters
actually ends on a JSON Pointer reference-token boundary in "path".
As a result, moving or copying between two sibling object keys where
one name is a textual prefix of the other, e.g. "/aa" to "/aab", was
rejected with "Invalid attempt to move parent under a child" even
though "/aa" is not an ancestor pointer of "/aab" at all, just an
unrelated key that happens to share a few leading characters.
The fix requires that the character in "path" right after the shared
"from" prefix be either the end of the string (equal paths, a no-op)
or a '/' (a genuine nested reference token), before treating it as an
attempt to move a location under its own descendant.
Added two cases to json_patch_tests.json covering the move and copy
operations between such sibling keys, and refreshed the corresponding
golden test_json_patch.expected output.
Some test_json_patch test cases have neither a comment field and
nor a error field, so the expression
"comment ? comment : error_s" evaluates to NULL.
The printf statement
printf("Testing '%s', ...", comment ? comment : error_s, ...);
passes NULL as a %s argument. GNU libc handles this gracefully and prints (null).
On AIX, passing NULL for %s prints an empty string, causing the test output
to differ from the expected file.
The fix adds a simple macro:
define STR_OR_NULL(s) ((s) ? (s) : "(null)")
that retruns "(null)" when s is NULL.
still waiting for more data, and also after read() failures.
Finalize the tokener at EOF with the terminating NUL so valid scalar
values can complete, while rejecting genuinely truncated input.
Also propagate read errors to the caller and add CLI regression tests.
lseek() expects (fd, offset, whence). The test passed (fd, SEEK_SET, 0),
which does not conform to the function signature. It should be corrected
to (fd, 0, SEEK_SET).
Strict mode rejected leading zeros but nothing else about the shape of a
number, so several malformed numbers parsed and were then written back out
verbatim, producing JSON that other parsers reject:
$ echo '[2.e3]' | ./json_parse -s -
[ 2.e3 ]
RFC 8259 requires a mandatory integer part and at least one digit in both the
fraction and the exponent:
number = [ minus ] int [ frac ] [ exp ]
int = zero / ( digit1-9 *DIGIT )
frac = decimal-point 1*DIGIT
exp = e [ minus / plus ] 1*DIGIT
so "1.", "-2.", "2.e3", "2.e+3", "0.e1", "-.123", "1e" and "1e+" are all
invalid. Walk the accumulated text against that grammar instead. The leading
zero rule from the previous check is part of the same walk rather than a
separate test, and its cases keep their coverage.
Only JSON_TOKENER_STRICT is affected; the default tokener stays as lenient as
it was. Measured against JSONTestSuite (318 files): strict mode went from 44
to 37 files accepted that the suite says must be rejected, default mode stayed
at 71, and nothing that must be accepted regressed in either mode.
When the appended data points into the printbuf itself, growing the buffer may invalidate the source pointer before it is copied. Preserve the source offset across realloc and use memmove for internal overlap.
Tests will now be built at `meson test`-time, so there's no cost if the
user isn't going to run them.
This is better than guarding on build type, as distros may want to test production
builds before shipping.
Signed-off-by: Sam James <sam@gentoo.org>
The iterative rewrite in 17328a67 made json_object_put() return 0 for freed scalars, strings and empty containers, contradicting the documented contract in json_object.h. Distinguish "freed" from "still referenced" in _json_object_put_maybe_free() and add regression tests.
This patch fixes the build error with clang-21
tests/test_deep_nesting.c:19:34: error: a function declaration
without a prototype is deprecated in all versions of C
[-Werror,-Wstrict-prototypes]
19 | static char *generate_json_string()
by fixing generate_json_string() definition.
It also removes prototypes for static functions that are not needed.
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>