From 16cd5e78d13f31b21472e07bbe6353d44110f272 Mon Sep 17 00:00:00 2001 From: itchyny Date: Sun, 23 Jul 2023 11:47:20 +0900 Subject: [PATCH] Fix stderr to output string with no decoration (fix #2063) --- src/builtin.c | 6 +++++- src/execute.c | 15 +++++++++++++++ src/jq.h | 3 ++- src/main.c | 16 ++++++++++++++++ tests/shtest | 10 ++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/builtin.c b/src/builtin.c index b38d4c2f4f..8113ecd46d 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1246,7 +1246,11 @@ static jv f_debug(jq_state *jq, jv input) { } static jv f_stderr(jq_state *jq, jv input) { - jv_dumpf(jv_copy(input), stderr, 0); + jq_msg_cb cb; + void *data; + jq_get_stderr_cb(jq, &cb, &data); + if (cb != NULL) + cb(data, jv_copy(input)); return input; } diff --git a/src/execute.c b/src/execute.c index adf3773799..34bb63dc93 100644 --- a/src/execute.c +++ b/src/execute.c @@ -49,6 +49,8 @@ struct jq_state { void *input_cb_data; jq_msg_cb debug_cb; void *debug_cb_data; + jq_msg_cb stderr_cb; + void *stderr_cb_data; }; struct closure { @@ -1018,6 +1020,9 @@ jq_state *jq_init(void) { jq->debug_cb = NULL; jq->debug_cb_data = NULL; + jq->stderr_cb = NULL; + jq->stderr_cb_data = NULL; + jq->err_cb = default_err_cb; jq->err_cb_data = stderr; @@ -1249,6 +1254,16 @@ void jq_get_debug_cb(jq_state *jq, jq_msg_cb *cb, void **data) { *data = jq->debug_cb_data; } +void jq_set_stderr_cb(jq_state *jq, jq_msg_cb cb, void *data) { + jq->stderr_cb = cb; + jq->stderr_cb_data = data; +} + +void jq_get_stderr_cb(jq_state *jq, jq_msg_cb *cb, void **data) { + *cb = jq->stderr_cb; + *data = jq->stderr_cb_data; +} + void jq_halt(jq_state *jq, jv exit_code, jv error_message) { diff --git a/src/jq.h b/src/jq.h index 5269de3ff8..b44a6a5521 100644 --- a/src/jq.h +++ b/src/jq.h @@ -34,9 +34,10 @@ jv jq_get_error_message(jq_state *); typedef jv (*jq_input_cb)(jq_state *, void *); void jq_set_input_cb(jq_state *, jq_input_cb, void *); void jq_get_input_cb(jq_state *, jq_input_cb *, void **); - void jq_set_debug_cb(jq_state *, jq_msg_cb, void *); void jq_get_debug_cb(jq_state *, jq_msg_cb *, void **); +void jq_set_stderr_cb(jq_state *, jq_msg_cb, void *); +void jq_get_stderr_cb(jq_state *, jq_msg_cb *, void **); void jq_set_attrs(jq_state *, jv); jv jq_get_attrs(jq_state *); diff --git a/src/main.c b/src/main.c index 48af5a31c1..dcf3657148 100644 --- a/src/main.c +++ b/src/main.c @@ -251,6 +251,19 @@ static void debug_cb(void *data, jv input) { int dumpopts = *(int *)data; jv_dumpf(JV_ARRAY(jv_string("DEBUG:"), input), stderr, dumpopts & ~(JV_PRINT_PRETTY)); fprintf(stderr, "\n"); + jv_free(input); +} + +static void stderr_cb(void *data, jv input) { + if (jv_get_kind(input) == JV_KIND_STRING) { + int dumpopts = *(int *)data; + priv_fwrite(jv_string_value(input), jv_string_length_bytes(jv_copy(input)), + stderr, dumpopts & JV_PRINT_ISATTY); + } else { + input = jv_dump_string(input, 0); + fprintf(stderr, "%s", jv_string_value(input)); + } + jv_free(input); } #ifdef WIN32 @@ -684,6 +697,9 @@ int main(int argc, char* argv[]) { // Let jq program call `debug` builtin and have that go somewhere jq_set_debug_cb(jq, debug_cb, &dumpopts); + // Let jq program call `stderr` builtin and have that go somewhere + jq_set_stderr_cb(jq, stderr_cb, &dumpopts); + if (nfiles == 0) jq_util_input_add_input(input_state, "-"); diff --git a/tests/shtest b/tests/shtest index b2b8571d86..48d63d3a66 100755 --- a/tests/shtest +++ b/tests/shtest @@ -232,6 +232,16 @@ grep "Expected string key after '{', not '\\['" $d/err > /dev/null echo '{"x":"y",["a","b"]}' | $JQ --stream > /dev/null 2> $d/err || true grep "Expected string key after ',' in object, not '\\['" $d/err > /dev/null +# debug, stderr +$VALGRIND $Q $JQ -n '"test", {} | debug, stderr' >/dev/null 2>&1 +$JQ -n -c -j '"hello\nworld", null, [false, 0], {"foo":["bar"]}, "\n" | stderr' >$d/out 2>$d/err +cat > $d/expected <<'EOF' +hello +worldnull[false,0]{"foo":["bar"]} +EOF +cmp $d/out $d/expected +cmp $d/err $d/expected + # --arg, --argjson, $ARGS.named $VALGRIND $JQ -n -c --arg foo 1 --argjson bar 2 '{$foo, $bar} | ., . == $ARGS.named' > $d/out printf '{"foo":"1","bar":2}\ntrue\n' > $d/expected