From 60a43a4ff69c9fe8ce9b1daf809c8c97666c6145 Mon Sep 17 00:00:00 2001 From: Landon Curt Noll Date: Sun, 27 Sep 2026 16:44:39 -0700 Subject: [PATCH 1/2] release 2.6.1 2026-09-27 Fixed a few comment typos, and string typos. Improve a few NULL tests. Improved `jparse(3)` man page to discuss when `filename` is `NULL`, and `stream` is `!= NULL`. Added test for when JSON stream is `NULL`, and `filename` uses stream. This update should have nil effect on use of this library and the API remains identical to "2.6.0 2026-09-25". Changed `JPARSE_REPO_VERSION` to "2.6.1 2026-09-27" Changed `JPARSE_TOOL_VERSION` to "2.1.1 2026-09-27" Changed `JPARSE_LIBRARY_VERSION` to "2.5.1 2026-09-27" Changed `JPARSE_UTILS_VERSION` to "2.2.1 2026-06-27" --- README.md | 4 ++-- jparse.l | 37 ++++++++++++++++++++++--------------- jparse.ref.c | 37 ++++++++++++++++++++++--------------- jparse.tab.ref.c | 2 +- jsemtblgen.c | 14 ++++++++------ man/man3/jparse.3 | 14 ++++++++++++-- test_jparse/json_api_test.c | 37 +++++++++++++++++++++++++++++++++++++ version.h | 8 ++++---- 8 files changed, 108 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index dd356ef..f66c1ea 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ _README.md last updated: 24 June 2025_ `jparse` is a JSON parser (as a stand-alone tool and a library) written in C with the help of `flex(1)` and `bison(1)`. This library, and all the tools, some -of which use the library and others which do not, were were co-developed in +of which use the library and others which do not, were co-developed in 2022-2024 by: *@xexyl* (**Cody Boone Ferguson**, [https://xexyl.net](https://xexyl.net), @@ -139,7 +139,7 @@ make all **IMPORTANT NOTE**: the Makefiles allow one to override certain things by having a file `Makefile.local` in the same directory. This can cause compilation errors -so if you do have such a file you should use so with caution. +so if you do have such a file you should do so with caution. diff --git a/jparse.l b/jparse.l index f76b5d9..bf8aeed 100644 --- a/jparse.l +++ b/jparse.l @@ -631,7 +631,7 @@ parse_json(char const *ptr, size_t len, char const *filename, bool *is_valid) not_reached(); } else { /* - * assume the JSON block is is valid; set *is_valid to true so that the + * assume the JSON block is valid; set *is_valid to true so that the * caller does not need to worry about it (well they should in case this * is removed but we do it explicitly for them anyway). */ @@ -843,7 +843,7 @@ parse_json_str(char const *ptr, size_t len, bool *is_valid) not_reached(); } else { /* - * assume the JSON block is is valid; set *is_valid to true so that the + * assume the JSON block is valid; set *is_valid to true so that the * caller does not need to worry about it (well they should in case this * is removed but we do it explicitly for them anyway). */ @@ -884,8 +884,8 @@ parse_json_str(char const *ptr, size_t len, bool *is_valid) * * given: * stream - open file stream containing JSON data - * filename - name of file or NULL for stdin - * is_valid - non-NULL printer to boolean to set depending on json validity + * filename - name of file, "-" for stdin, or NULL if no filename should be reported + * is_valid - non-NULL pointer to boolean to set depending on json validity * * return: * pointer to a JSON parse tree @@ -897,8 +897,11 @@ parse_json_str(char const *ptr, size_t len, bool *is_valid) * YY_BUFFER_STATE is part of the scanner and not the parser and that's required * for the parse_json() function. * - * NOTE: if filename is "-" and stream is NULL, then the stream is set to stdin, - * as if the filename was NULL. + * NOTE: if filename is NULL, it is reported as "-" only when stream is also + * NULL. Otherwise, the supplied stream is used and the filename is just + * omitted from diagnostics. + * + * NOTE: if filename is "-" and stream is NULL, then the stream is set to stdin. * * NOTE: this function only warns on error, except for NULL is_valid, in which * case it is an error; warning on errors is so that an entire report of @@ -923,7 +926,7 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) not_reached(); } else { /* - * assume the JSON stream is is valid JSON; set *is_valid to true so + * assume the JSON stream is valid JSON; set *is_valid to true so * that the caller does not need to worry about it (well they should in * case this is removed but we do it explicitly for them anyway). */ @@ -931,9 +934,13 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) } if (filename == NULL) { - json_dbg(JSON_DBG_HIGH, __func__, "filename is NULL, forcing it to be \"-\" for stdin"); - filename = "-"; - stream = stdin; + if (stream == NULL) { + json_dbg(JSON_DBG_HIGH, __func__, "filename and stream are NULL, forcing stdin"); + filename = "-"; + stream = stdin; + } else { + json_dbg(JSON_DBG_HIGH, __func__, "filename is NULL, using supplied stream without a filename"); + } } else if (!strcmp(filename, "-") && stream == NULL) { stream = stdin; } @@ -988,7 +995,7 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) /* * warn about read error */ - werr(48, __func__, "could not read read stream"); + werr(48, __func__, "could not read stream"); /* * we need to clearerr() or fclose(), depending on the stream. */ @@ -1071,7 +1078,7 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) stream = NULL; /* - * return the JSON parse tree tree + * return the JSON parse tree */ return tree; } @@ -1227,7 +1234,7 @@ is_read(char const *path) * * given: * filename - filename of file to parse - * is_valid - non-NULL printer to boolean to set depending on json validity + * is_valid - non-NULL pointer to boolean to set depending on json validity * * return: * pointer to a JSON parse tree @@ -1258,7 +1265,7 @@ parse_json_file(char const *filename, bool *is_valid) not_reached(); } else { /* - * assume the JSON file is is valid JSON; set *is_valid to true so + * assume the JSON file is valid JSON; set *is_valid to true so * that the caller does not need to worry about it (well they should in * case this is removed but we do it explicitly for them anyway). */ @@ -1395,7 +1402,7 @@ parse_json_file(char const *filename, bool *is_valid) tree = parse_json_stream(stream, filename, is_valid); /* - * return the JSON parse tree tree + * return the JSON parse tree */ return tree; } diff --git a/jparse.ref.c b/jparse.ref.c index febacab..79a4556 100644 --- a/jparse.ref.c +++ b/jparse.ref.c @@ -2931,7 +2931,7 @@ parse_json(char const *ptr, size_t len, char const *filename, bool *is_valid) not_reached(); } else { /* - * assume the JSON block is is valid; set *is_valid to true so that the + * assume the JSON block is valid; set *is_valid to true so that the * caller does not need to worry about it (well they should in case this * is removed but we do it explicitly for them anyway). */ @@ -3143,7 +3143,7 @@ parse_json_str(char const *ptr, size_t len, bool *is_valid) not_reached(); } else { /* - * assume the JSON block is is valid; set *is_valid to true so that the + * assume the JSON block is valid; set *is_valid to true so that the * caller does not need to worry about it (well they should in case this * is removed but we do it explicitly for them anyway). */ @@ -3184,8 +3184,8 @@ parse_json_str(char const *ptr, size_t len, bool *is_valid) * * given: * stream - open file stream containing JSON data - * filename - name of file or NULL for stdin - * is_valid - non-NULL printer to boolean to set depending on json validity + * filename - name of file, "-" for stdin, or NULL if no filename should be reported + * is_valid - non-NULL pointer to boolean to set depending on json validity * * return: * pointer to a JSON parse tree @@ -3197,8 +3197,11 @@ parse_json_str(char const *ptr, size_t len, bool *is_valid) * YY_BUFFER_STATE is part of the scanner and not the parser and that's required * for the parse_json() function. * - * NOTE: if filename is "-" and stream is NULL, then the stream is set to stdin, - * as if the filename was NULL. + * NOTE: if filename is NULL, it is reported as "-" only when stream is also + * NULL. Otherwise, the supplied stream is used and the filename is just + * omitted from diagnostics. + * + * NOTE: if filename is "-" and stream is NULL, then the stream is set to stdin. * * NOTE: this function only warns on error, except for NULL is_valid, in which * case it is an error; warning on errors is so that an entire report of @@ -3223,7 +3226,7 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) not_reached(); } else { /* - * assume the JSON stream is is valid JSON; set *is_valid to true so + * assume the JSON stream is valid JSON; set *is_valid to true so * that the caller does not need to worry about it (well they should in * case this is removed but we do it explicitly for them anyway). */ @@ -3231,9 +3234,13 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) } if (filename == NULL) { - json_dbg(JSON_DBG_HIGH, __func__, "filename is NULL, forcing it to be \"-\" for stdin"); - filename = "-"; - stream = stdin; + if (stream == NULL) { + json_dbg(JSON_DBG_HIGH, __func__, "filename and stream are NULL, forcing stdin"); + filename = "-"; + stream = stdin; + } else { + json_dbg(JSON_DBG_HIGH, __func__, "filename is NULL, using supplied stream without a filename"); + } } else if (!strcmp(filename, "-") && stream == NULL) { stream = stdin; } @@ -3288,7 +3295,7 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) /* * warn about read error */ - werr(48, __func__, "could not read read stream"); + werr(48, __func__, "could not read stream"); /* * we need to clearerr() or fclose(), depending on the stream. */ @@ -3371,7 +3378,7 @@ parse_json_stream(FILE *stream, char const *filename, bool *is_valid) stream = NULL; /* - * return the JSON parse tree tree + * return the JSON parse tree */ return tree; } @@ -3527,7 +3534,7 @@ is_read(char const *path) * * given: * filename - filename of file to parse - * is_valid - non-NULL printer to boolean to set depending on json validity + * is_valid - non-NULL pointer to boolean to set depending on json validity * * return: * pointer to a JSON parse tree @@ -3558,7 +3565,7 @@ parse_json_file(char const *filename, bool *is_valid) not_reached(); } else { /* - * assume the JSON file is is valid JSON; set *is_valid to true so + * assume the JSON file is valid JSON; set *is_valid to true so * that the caller does not need to worry about it (well they should in * case this is removed but we do it explicitly for them anyway). */ @@ -3695,7 +3702,7 @@ parse_json_file(char const *filename, bool *is_valid) tree = parse_json_stream(stream, filename, is_valid); /* - * return the JSON parse tree tree + * return the JSON parse tree */ return tree; } diff --git a/jparse.tab.ref.c b/jparse.tab.ref.c index 3c52525..d49ac67 100644 --- a/jparse.tab.ref.c +++ b/jparse.tab.ref.c @@ -2508,7 +2508,7 @@ yyerror(JPARSE_LTYPE *yyltype, struct json **node, yyscan_t scanner, char const * firewall */ if (scanner == NULL) { - err(61, __func__, "NULL scanner"); + err(62, __func__, "NULL scanner"); not_reached(); } /* diff --git a/jsemtblgen.c b/jsemtblgen.c index a25b0c1..3810ffa 100644 --- a/jsemtblgen.c +++ b/jsemtblgen.c @@ -410,8 +410,8 @@ main(int argc, char **argv) not_reached(); } for (c=0; c < len; ++c) { - if (islower(tbl_name[c])) { - cap_tbl_name[c] = (char)toupper(tbl_name[c]); + if (islower((unsigned char)tbl_name[c])) { + cap_tbl_name[c] = (char)toupper((unsigned char)tbl_name[c]); } } dbg(DBG_MED, "output_mode: %s", h_mode ? ".h include file" : ".c src file"); @@ -893,7 +893,8 @@ alloc_c_funct_name(char const *prefix, char const *str) * case: prefix begins with a digit * case: prefix begins with an underscore */ - if (prefix_is_reserved == true || prefix[0] == '\0' || isdigit(prefix[0]) || prefix[0] == '_') { + if (prefix_is_reserved == true || prefix[0] == '\0' || + isdigit((unsigned char)prefix[0]) || prefix[0] == '_') { /* add x due to the cases mentioned above */ *p++ = 'x'; } @@ -902,7 +903,7 @@ alloc_c_funct_name(char const *prefix, char const *str) * process prefix */ while (prefix[0] != '\0') { - if (!isalnum(prefix[0])) { + if (!isalnum((unsigned char)prefix[0])) { /* convert non-C name character to underscore */ *p++ = '_'; } else { @@ -923,7 +924,8 @@ alloc_c_funct_name(char const *prefix, char const *str) * case: str begins with a digit * case: str begins with an underscore */ - if (str_is_reserved == true || str[0] == '\0' || isdigit(str[0]) || str[0] == '_') { + if (str_is_reserved == true || str[0] == '\0' || + isdigit((unsigned char)str[0]) || str[0] == '_') { /* add x due to the cases mentioned above */ *p++ = 'x'; } @@ -932,7 +934,7 @@ alloc_c_funct_name(char const *prefix, char const *str) * process each str character in C, converting non-alphanumeric characters to underscore. */ while (str[0] != '\0') { - if (!isalnum(str[0])) { + if (!isalnum((unsigned char)str[0])) { /* convert non-C name character to underscore */ *p++ = '_'; } else { diff --git a/man/man3/jparse.3 b/man/man3/jparse.3 index ff93a82..c9a4491 100644 --- a/man/man3/jparse.3 +++ b/man/man3/jparse.3 @@ -103,9 +103,9 @@ described later. .PP If .I filename -is not NULL and is an empty string, then it will set the filename to +is .BR \*(lq-\*(rq , -indicating that the input comes from stdin. +it indicates that the input comes from stdin. If it is a NULL pointer, the filename in the parser extra data will be set to NULL, and messages will not show a filename. .PP The variable @@ -150,6 +150,16 @@ and does not accept a number of bytes to scan. The .I filename is for messages and is not used to determine what file to open as the stream should already be open. +If +.I filename +is NULL and +.I stream +is non-NULL, the supplied stream is still parsed; only the diagnostic filename is omitted. +If both +.I filename +and +.I stream +are NULL, stdin is used. If you wish to do that you should use the .BR parse_json_file () function instead, which takes a filename and the diff --git a/test_jparse/json_api_test.c b/test_jparse/json_api_test.c index 02dcedd..715451a 100644 --- a/test_jparse/json_api_test.c +++ b/test_jparse/json_api_test.c @@ -101,6 +101,42 @@ test_parse_json_len_limit(void) } +static int +test_parse_json_stream_null_filename_uses_stream(void) +{ + struct json *tree = NULL; + FILE *stream = NULL; + bool is_valid = false; + + if (freopen("/dev/null", "r", stdin) == NULL) { + fprintf(stderr, "freopen(/dev/null) failed\n"); + return 1; + } + + stream = tmpfile(); + if (stream == NULL) { + fprintf(stderr, "tmpfile() failed\n"); + return 1; + } + if (fputs("{\"k\":1}", stream) == EOF) { + fprintf(stderr, "writing temporary JSON stream failed\n"); + fclose(stream); + return 1; + } + rewind(stream); + + tree = parse_json_stream(stream, NULL, &is_valid); + if (tree == NULL || is_valid != true || tree->type != JTYPE_OBJECT) { + fprintf(stderr, "parse_json_stream() ignored the supplied stream when filename is NULL\n"); + free_tree(tree); + return 1; + } + + free_tree(tree); + return 0; +} + + int main(void) { @@ -110,5 +146,6 @@ main(void) ret |= test_bool_slice(); ret |= test_null_slice(); ret |= test_parse_json_len_limit(); + ret |= test_parse_json_stream_null_filename_uses_stream(); return ret; } diff --git a/version.h b/version.h index 30cd0bc..94dfd94 100644 --- a/version.h +++ b/version.h @@ -55,22 +55,22 @@ * * NOTE: this should match the latest Release string in CHANGES.md */ -#define JPARSE_REPO_VERSION "2.6.0 2026-09-25" /* format: major.minor YYYY-MM-DD */ +#define JPARSE_REPO_VERSION "2.6.1 2026-09-27" /* format: major.minor YYYY-MM-DD */ /* * official jparse version */ -#define JPARSE_TOOL_VERSION "2.1.0 2026-09-25" /* format: major.minor YYYY-MM-DD */ +#define JPARSE_TOOL_VERSION "2.1.1 2026-09-27" /* format: major.minor YYYY-MM-DD */ /* * official JSON parser version */ -#define JPARSE_LIBRARY_VERSION "2.5.0 2026-09-25" /* library version format: major.minor YYYY-MM-DD */ +#define JPARSE_LIBRARY_VERSION "2.5.1 2026-09-27" /* library version format: major.minor YYYY-MM-DD */ /* * official utility functions (util.c) version */ -#define JPARSE_UTILS_VERSION "2.2.0 2026-06-25" /* format: major.minor YYYY-MM-DD */ +#define JPARSE_UTILS_VERSION "2.2.1 2026-06-27" /* format: major.minor YYYY-MM-DD */ #endif /* INCLUDE_JPARSE_VERSION_H */ From a901e8de6e6ec45e022eeb68b08605916911366d Mon Sep 17 00:00:00 2001 From: Landon Curt Noll Date: Sun, 27 Sep 2026 17:00:34 -0700 Subject: [PATCH 2/2] update CHANGES.md as per release 2.6.1 2026-09-27 --- CHANGES.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 63e3942..be4451e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,24 @@ # Significant changes in the JSON parser repo -## Release 2.6.0 2026-09-25 +## Release 2.6.1 2026-09-27 -Sequenced exit codes. +Fixed a few comment typos, and string typos. -Updated `JPARSE_UTF8_VERSION` to `"2.1.5 2026-09-27"` for recent changes. +Improve a few NULL tests. + +Improved `jparse(3)` man page to discuss when `filename` +is `NULL`, and `stream` is `!= NULL`. + +Added test for when JSON stream is `NULL`, and `filename` uses stream. + +This update should have nil effect on use of this library +and the API remains identical to "2.6.0 2026-09-25". + +Changed `JPARSE_REPO_VERSION` to "2.6.1 2026-09-27" +Changed `JPARSE_TOOL_VERSION` to "2.1.1 2026-09-27" +Changed `JPARSE_LIBRARY_VERSION` to "2.5.1 2026-09-27" +Changed `JPARSE_UTILS_VERSION` to "2.2.1 2026-06-27" ## Release 2.6.0 2026-09-25 @@ -64,6 +77,10 @@ Updated `JPARSE_TOOL_VERSION` to `"2.1.0 2026-09-25"`. Updated `JPARSE_LIBRARY_VERSION` to `"2.5.0 2026-09-25"`. Updated `JPARSE_UTILS_VERSION` to `"2.2.0 2026-06-25"`. +Sequenced exit codes. + +Updated `JPARSE_UTF8_VERSION` to `"2.1.5 2026-09-27"` for recent changes. + ## Release 2.5.11 2026-08-03