From 1df951685222ae7c346589a8860a1f3dbd2f6cb3 Mon Sep 17 00:00:00 2001 From: liquidaty Date: Tue, 4 Mar 2025 18:53:33 -0800 Subject: [PATCH] add --raw-output option to jq cmd (#365) * add --raw-output option to jq cmd * add --raw-output test; fix jq subcmd bug not separating outputs by newline --- app/jq.c | 4 +++- app/test/Makefile | 2 ++ app/test/expected/test-jq.raw.out | 2 ++ app/utils/jq.c | 12 +++++++++--- include/zsv/utils/jq.h | 2 ++ 5 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 app/test/expected/test-jq.raw.out diff --git a/app/jq.c b/app/jq.c index 6b5f4d59..54ea9553 100644 --- a/app/jq.c +++ b/app/jq.c @@ -41,6 +41,7 @@ int ZSV_MAIN_NO_OPTIONS_FUNC(ZSV_COMMAND)(int argc, const char *argv[]) { int err = 0; const unsigned char *jqfilter = (const unsigned char *)argv[1]; + struct jv_to_json_ctx ctx = {0}; for (int i = 2; !err && i < argc; i++) { // jq filter filename const char *arg = argv[i]; @@ -51,6 +52,8 @@ int ZSV_MAIN_NO_OPTIONS_FUNC(ZSV_COMMAND)(int argc, const char *argv[]) { } } else if (!strcmp(arg, "--csv")) { to_csv = 1; + } else if (!strcmp(arg, "--raw-output")) { + ctx.raw_output = 1; } else if (!strcmp(arg, "-o") || !strcmp(arg, "--output")) { i++; if (!(i < argc)) { @@ -73,7 +76,6 @@ int ZSV_MAIN_NO_OPTIONS_FUNC(ZSV_COMMAND)(int argc, const char *argv[]) { if (!err) { void (*jqfunc)(jv, void *) = to_csv ? jv_to_csv : jv_to_json_func; - struct jv_to_json_ctx ctx; ctx.write1 = zsv_jq_fwrite1; ctx.ctx = f_out; ctx.flags = JV_PRINT_PRETTY | JV_PRINT_SPACE1; diff --git a/app/test/Makefile b/app/test/Makefile index 62cf3ed6..04ce447b 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -524,6 +524,8 @@ test-jq: test-%: ${BUILD_DIR}/bin/zsv_%${EXE} @${TEST_INIT} @(${PREFIX} $< keys ${THIS_MAKEFILE_DIR}/../../docs/db.schema.json ${REDIRECT1} ${TMP_DIR}/$@.out) @${CMP} ${TMP_DIR}/$@.out expected/$@.out && ${TEST_PASS} || ${TEST_FAIL} + (${PREFIX} $< 'keys|.[]' ${THIS_MAKEFILE_DIR}/../../docs/db.schema.json --raw-output ${REDIRECT1} ${TMP_DIR}/$@.raw.out) + ${CMP} ${TMP_DIR}/$@.raw.out expected/$@.raw.out && ${TEST_PASS} || ${TEST_FAIL} test-2json: test-%: ${BUILD_DIR}/bin/zsv_%${EXE} ${BUILD_DIR}/bin/zsv_2db${EXE} ${BUILD_DIR}/bin/zsv_select${EXE} worldcitiespop_mil.csv @${TEST_INIT} diff --git a/app/test/expected/test-jq.raw.out b/app/test/expected/test-jq.raw.out new file mode 100644 index 00000000..4c042144 --- /dev/null +++ b/app/test/expected/test-jq.raw.out @@ -0,0 +1,2 @@ +items +type diff --git a/app/utils/jq.c b/app/utils/jq.c index fd1694c7..78d939a8 100644 --- a/app/utils/jq.c +++ b/app/utils/jq.c @@ -121,9 +121,15 @@ size_t zsv_jq_fwrite1(void *restrict FILE_ptr, const void *restrict buff, size_t void jv_to_json_func(jv value, void *ctx) { struct jv_to_json_ctx *data = ctx; - if (data->write1 == zsv_jq_fwrite1) - jv_dumpf(value, data->ctx, data->flags); - else { + if (data->output_started) + data->write1(data->ctx, "\n", 1); + data->output_started = 1; + if (data->write1 == zsv_jq_fwrite1) { + if (data->raw_output && jv_get_kind(value) == JV_KIND_STRING) + fwrite(jv_string_value(value), 1, jv_string_length_bytes(jv_copy(value)), data->ctx); + else + jv_dumpf(value, data->ctx, data->flags); + } else { // jv_dump_string is memory-inefficient // would be better to create custom dump function that, instead of writing to string buffer, // could directly invoke func() diff --git a/include/zsv/utils/jq.h b/include/zsv/utils/jq.h index 3a238c2b..9b34cfeb 100644 --- a/include/zsv/utils/jq.h +++ b/include/zsv/utils/jq.h @@ -17,6 +17,8 @@ struct jv_to_json_ctx { size_t (*write1)(void *restrict ctx, const void *restrict buff, size_t len); // e.g. zsv_jq_fwrite1 void *ctx; // e.g. FILE * int flags; // passed on to jv_dumpf / jv_dump_string + char raw_output; + char output_started; }; void jv_to_json_w_ctx(jv value, void *jv_to_json_ctx);