Skip to content

Commit

Permalink
Rename --nul-output to --raw-output0, abort on string containing NUL
Browse files Browse the repository at this point in the history
  • Loading branch information
itchyny committed Jul 26, 2023
1 parent 13fbe98 commit ff47773
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 28 deletions.
11 changes: 6 additions & 5 deletions docs/content/manual/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,15 @@ sections:
formatted as a JSON string with quotes. This can be useful for
making jq filters talk to non-JSON-based systems.
* `--join-output` / `-j`:
Like `-r` but jq won't print a newline after each output.
* `--nul-output` / `-0`:
* `--raw-output0`:
Like `-r` but jq will print NUL instead of newline after each output.
This can be useful when the values being output can contain newlines.
When the output value contains NUL, jq exits with non-zero code.
* `--join-output` / `-j`:
Like `-r` but jq won't print a newline after each output.
* `--ascii-output` / `-a`:
Expand Down
8 changes: 4 additions & 4 deletions jq.1.prebuilt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 20 additions & 16 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ static void usage(int code, int keep_it_short) {
" -s, --slurp read all inputs into an array and use it as the single input value;\n"
" -c, --compact-output compact instead of pretty-printed output;\n"
" -r, --raw-output output strings directly without escapes and quotes;\n"
" --raw-output0 implies -r and output NUL after each output;\n"
" -j, --join-output implies -r and output without newline after each output;\n"
" -0, --nul-output implies -r and output NUL after each output;\n"
" -a, --ascii-output output strings by only ASCII characters using escape sequences;\n"
" -S, --sort-keys sort keys of each object on output;\n"
" -C, --color-output colorize JSON output;\n"
Expand Down Expand Up @@ -111,11 +111,8 @@ static void die() {
exit(2);
}




static int isoptish(const char* text) {
return text[0] == '-' && (text[1] == '-' || isalpha(text[1]) || text[1] == '0');
return text[0] == '-' && (text[1] == '-' || isalpha(text[1]));
}

static int isoption(const char* text, char shortopt, const char* longopt, size_t *short_opts) {
Expand All @@ -141,7 +138,7 @@ enum {
RAW_INPUT = 2,
PROVIDE_NULL = 4,
RAW_OUTPUT = 8,
RAW_NUL = 16,
RAW_OUTPUT0 = 16,
ASCII_OUTPUT = 32,
COLOR_OUTPUT = 64,
NO_COLOR_OUTPUT = 128,
Expand Down Expand Up @@ -191,11 +188,18 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts, int options)
if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
if (options & ASCII_OUTPUT) {
jv_dumpf(jv_copy(result), stdout, JV_PRINT_ASCII);
ret = JQ_OK;
} else if ((options & RAW_OUTPUT0) && strlen(jv_string_value(result)) != (unsigned long)jv_string_length_bytes(jv_copy(result))) {
jv input_pos = jq_util_input_get_position(jq);
fprintf(stderr, "jq: error (at %s): %s\n",
jv_string_value(input_pos), "Cannot dump a string containing NUL with --raw-output0 option");
jv_free(input_pos);
ret = JQ_ERROR_UNKNOWN;
} else {
priv_fwrite(jv_string_value(result), jv_string_length_bytes(jv_copy(result)),
stdout, dumpopts & JV_PRINT_ISATTY);
ret = JQ_OK;
}
ret = JQ_OK;
jv_free(result);
} else {
if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL)
Expand All @@ -208,7 +212,7 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts, int options)
}
if (!(options & RAW_NO_LF))
priv_fwrite("\n", 1, stdout, dumpopts & JV_PRINT_ISATTY);
if (options & RAW_NUL)
if ((options & RAW_OUTPUT0) && ret != JQ_ERROR_UNKNOWN)
priv_fwrite("\0", 1, stdout, dumpopts & JV_PRINT_ISATTY);
if (options & UNBUFFERED_OUTPUT)
fflush(stdout);
Expand Down Expand Up @@ -394,6 +398,14 @@ int main(int argc, char* argv[]) {
options |= RAW_OUTPUT;
if (!short_opts) continue;
}
if (isoption(argv[i], 0, "raw-output0", &short_opts)) {
options |= RAW_OUTPUT | RAW_NO_LF | RAW_OUTPUT0;
if (!short_opts) continue;
}
if (isoption(argv[i], 'j', "join-output", &short_opts)) {
options |= RAW_OUTPUT | RAW_NO_LF;
if (!short_opts) continue;
}
if (isoption(argv[i], 'c', "compact-output", &short_opts)) {
dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));
if (!short_opts) continue;
Expand Down Expand Up @@ -430,14 +442,6 @@ int main(int argc, char* argv[]) {
options |= FROM_FILE;
if (!short_opts) continue;
}
if (isoption(argv[i], 'j', "join-output", &short_opts)) {
options |= RAW_OUTPUT | RAW_NO_LF;
if (!short_opts) continue;
}
if (isoption(argv[i], '0', "nul-output", &short_opts)) {
options |= RAW_OUTPUT | RAW_NO_LF | RAW_NUL;
if (!short_opts) continue;
}
if (isoption(argv[i], 'b', "binary", &short_opts)) {
#ifdef WIN32
fflush(stdout);
Expand Down
14 changes: 11 additions & 3 deletions tests/shtest
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,21 @@ cmp $d/out $d/expected
printf "[1,2][3,4]\n" | $JQ -cs add > $d/out 2>&1
cmp $d/out $d/expected

# Regression test for -0 / --nul-output
# Regression test for --raw-output0
printf "a\0b\0" > $d/expected
printf '["a", "b"]' | $JQ -0 .[] > $d/out 2>&1
printf '["a", "b"]' | $VALGRIND $Q $JQ --raw-output0 .[] > $d/out
cmp $d/out $d/expected
printf '["a", "b"]' | $JQ --nul-output .[] > $d/out 2>&1
printf '["a", "c\\u0000d", "b"]' | $VALGRIND $Q $JQ --raw-output0 .[] > $d/out
cmp $d/out $d/expected

if printf '{"foo":"foo\\u0000bar"}' | $VALGRIND $Q $JQ --raw-output0 .foo; then
echo "Should exit error on string containing NUL with --raw-output0" 1>&2
exit 1
elif [ $? -ne 5 ]; then
echo "Invalid error code" 1>&2
exit 1
fi

## Test streaming parser

## If we add an option to stream to the `import ... as $symbol;` directive
Expand Down

0 comments on commit ff47773

Please sign in to comment.