diff --git a/src/main.c b/src/main.c index 61ae43f9..ebfddf9a 100644 --- a/src/main.c +++ b/src/main.c @@ -146,6 +146,17 @@ enum { }; static int options = 0; +enum { + JQ_OK = 0, + JQ_OK_NULL_KIND = -1, /* exit 0 if --exit-status is not set*/ + JQ_ERROR_SYSTEM = 2, + JQ_ERROR_COMPILE = 3, + JQ_OK_NO_OUTPUT = -4, /* exit 0 if --exit-status is not set*/ + JQ_ERROR_UNKNOWN = 5, +}; +#define jq_exit_with_status(r) exit(abs(r)) +#define jq_exit(r) exit( r > 0 ? r : 0 ) + static const char *skip_shebang(const char *p) { if (strncmp(p, "#!", sizeof("#!") - 1) != 0) return p; @@ -162,7 +173,7 @@ static const char *skip_shebang(const char *p) { } static int process(jq_state *jq, jv value, int flags, int dumpopts) { - int ret = 14; // No valid results && -e -> exit(4) + int ret = JQ_OK_NO_OUTPUT; // No valid results && -e -> exit(4) jq_start(jq, value, flags); jv result; while (jv_is_valid(result = jq_next(jq))) { @@ -172,13 +183,13 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) { } else { fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout); } - ret = 0; + ret = JQ_OK; jv_free(result); } else { if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL) - ret = 11; + ret = JQ_OK_NULL_KIND; else - ret = 0; + ret = JQ_OK; if (options & SEQ) priv_fwrite("\036", 1, stdout, dumpopts & JV_PRINT_ISATTY); jv_dump(result, dumpopts); @@ -193,11 +204,11 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) { options |= EXIT_STATUS_EXACT; jv exit_code = jq_get_exit_code(jq); if (!jv_is_valid(exit_code)) - ret = 0; + ret = JQ_OK; else if (jv_get_kind(exit_code) == JV_KIND_NUMBER) ret = jv_number_value(exit_code); else - ret = 5; + ret = JQ_ERROR_UNKNOWN; jv_free(exit_code); jv error_message = jq_get_error_message(jq); if (jv_get_kind(error_message) == JV_KIND_STRING) { @@ -222,7 +233,7 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) { fprintf(stderr, "jq: error (at %s) (not a string): %s\n", jv_string_value(input_pos), jv_string_value(msg)); } - ret = 5; + ret = JQ_ERROR_UNKNOWN; jv_free(input_pos); jv_free(msg); } @@ -238,10 +249,11 @@ static void debug_cb(void *data, jv input) { int main(int argc, char* argv[]) { jq_state *jq = NULL; - int ret = 0; + int ret = JQ_OK_NO_OUTPUT; int compiled = 0; int parser_flags = 0; int nfiles = 0; + int last_result = -1; /* -1 = no result, 0=null or false, 1=true */ int badwrite; jv ARGS = jv_array(); /* positional arguments */ jv program_arguments = jv_object(); /* named arguments */ @@ -269,7 +281,7 @@ int main(int argc, char* argv[]) { jq = jq_init(); if (jq == NULL) { perror("malloc"); - ret = 2; + ret = JQ_ERROR_SYSTEM; goto out; } @@ -469,7 +481,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which, argv[i+1], argv[i+2], jv_string_value(data)); jv_free(data); - ret = 2; + ret = JQ_ERROR_SYSTEM; goto out; } if (strcmp(which, "argfile") == 0 && @@ -498,7 +510,7 @@ int main(int argc, char* argv[]) { } if (isoption(argv[i], 'V', "version", &short_opts)) { printf("jq-%s\n", JQ_VERSION); - ret = 0; + ret = JQ_OK; goto out; } if (isoption(argv[i], 0, "run-tests", &short_opts)) { @@ -577,7 +589,7 @@ int main(int argc, char* argv[]) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: %s\n", progname, jv_string_value(data)); jv_free(data); - ret = 2; + ret = JQ_ERROR_SYSTEM; goto out; } jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin)))); @@ -595,7 +607,7 @@ int main(int argc, char* argv[]) { compiled = jq_compile_args(jq, program, jv_copy(program_arguments)); } if (!compiled){ - ret = 3; + ret = JQ_ERROR_COMPILE; goto out; } @@ -629,6 +641,8 @@ int main(int argc, char* argv[]) { (jv_is_valid((value = jq_util_input_next_input(input_state))) || jv_invalid_has_msg(jv_copy(value)))) { if (jv_is_valid(value)) { ret = process(jq, value, jq_flags, dumpopts); + if (ret <= 0 && ret != JQ_OK_NO_OUTPUT) + last_result = (ret != JQ_OK_NULL_KIND); continue; } @@ -636,7 +650,7 @@ int main(int argc, char* argv[]) { jv msg = jv_invalid_get_msg(value); if (!(options & SEQ)) { // --seq -> errors are not fatal - ret = 4; + ret = JQ_OK_NO_OUTPUT; fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); break; @@ -647,22 +661,29 @@ int main(int argc, char* argv[]) { } if (jq_util_input_errors(input_state) != 0) - ret = 2; + ret = JQ_ERROR_SYSTEM; out: badwrite = ferror(stdout); if (fclose(stdout)!=0 || badwrite) { fprintf(stderr,"Error: writing output failed: %s\n", strerror(errno)); - ret = 2; + ret = JQ_ERROR_SYSTEM; } jv_free(ARGS); jv_free(program_arguments); jq_util_input_free(&input_state); jq_teardown(&jq); - if (ret >= 10 && (options & EXIT_STATUS)) - return ret - 10; - if (ret >= 10 && !(options & EXIT_STATUS_EXACT)) - return 0; - return ret; + + if (options & (EXIT_STATUS|EXIT_STATUS_EXACT)) { + if (ret != JQ_OK_NO_OUTPUT) + jq_exit_with_status(ret); + else + switch (last_result) { + case -1: jq_exit_with_status(JQ_OK_NO_OUTPUT); + case 0: jq_exit_with_status(JQ_OK_NULL_KIND); + default: jq_exit_with_status(JQ_OK); + } + } else + jq_exit(ret); } diff --git a/tests/shtest b/tests/shtest index 86fec33e..fc2ef152 100755 --- a/tests/shtest +++ b/tests/shtest @@ -96,14 +96,20 @@ cmp $d/out $d/expected cat > $d/expected < $d/out 2>&1 +printf '"foo' | $JQ -c --seq . > $d/out 2>&1 +cmp $d/out $d/expected + +# with -e option should give 4 here as there's no valid output after +# ignoring parse errors with --seq. +printf '"foo' | $JQ -ce --seq . > $d/out 2>&1 || ret=$? +[ $ret -eq 4 ] cmp $d/out $d/expected # Numeric values truncated by EOF are ignored cat > $d/expected < $d/out 2>&1 +printf '1' | $JQ -c --seq . > $d/out 2>&1 cmp $d/out $d/expected cat > $d/expected </dev/null 2> $d/out; then fi cmp $d/out $d/expected + +## Test --exit-status +data='{"i": 1}\n{"i": 2}\n{"i": 3}\n' +echo "$data" | $JQ --exit-status 'select(.i==1)' > /dev/null 2>&1 +echo "$data" | $JQ --exit-status 'select(.i==2)' > /dev/null 2>&1 +echo "$data" | $JQ --exit-status 'select(.i==3)' > /dev/null 2>&1 +ret=0 +echo "$data" | $JQ --exit-status 'select(.i==4)' > /dev/null 2>&1 || ret=$? +[ $ret -eq 4 ] +ret=0 +echo "$data" | $JQ --exit-status 'select(.i==2) | false' > /dev/null 2>&1 || ret=$? +[ $ret -eq 1 ] +echo "$data" | $JQ --exit-status 'select(.i==2) | true' > /dev/null 2>&1 + + # Regression test for #951 printf '"a\n' > $d/input if $VALGRIND $Q $JQ -e . $d/input; then