problem or system error, 3 if there was a jq program compile
error, or 0 if the jq program ran.
+ Another way to set the exit status is with the `halt_error`
+ builtin function.
+
* `--arg name value`:
This option passes a value to the jq program as a predefined
Produces an error, just like `.a` applied to values other than
null and objects would, but with the given message as the
- error's value.
+ error's value. Errors can be caught with try/catch; see below.
+
+ - title: "`halt`"
+ body: |
+
+ Stops the jq program with no further outputs. jq will exit
+ with exit status `0`.
+
+ - title: "`halt_error`, `halt_error(exit_code)`"
+ body: |
+
+ Stops the jq program with no further outputs. The input will
+ be printed on `stderr` as raw output (i.e., strings will not
+ have double quotes) with no decoration, not even a newline.
+
+ The given `exit_code` (defaulting to `5`) will be jq's exit
+ status.
+
+ For example, `"Error: somthing went wrong\n"|halt_error(1)`.
- title: "`$__loc__`"
body: |
.IP
Sets the exit status of jq to 0 if the last output values was neither \fBfalse\fR nor \fBnull\fR, 1 if the last output value was either \fBfalse\fR or \fBnull\fR, or 4 if no valid result was ever produced\. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran\.
.
+.IP
+Another way to set the exit status is with the \fBhalt_error\fR builtin function\.
+.
.IP "\(bu" 4
\fB\-\-arg name value\fR:
.
.IP "" 0
.
.SS "error(message)"
-Produces an error, just like \fB\.a\fR applied to values other than null and objects would, but with the given message as the error\'s value\.
+Produces an error, just like \fB\.a\fR applied to values other than null and objects would, but with the given message as the error\'s value\. Errors can be caught with try/catch; see below\.
+.
+.SS "halt"
+Stops the jq program with no further outputs\. jq will exit with exit status \fB0\fR\.
+.
+.SS "halt_error, halt_error(exit_code)"
+Stops the jq program with no further outputs\. The input will be printed on \fBstderr\fR as raw output (i\.e\., strings will not have double quotes) with no decoration, not even a newline\.
+.
+.P
+The given \fBexit_code\fR (defaulting to \fB5\fR) will be jq\'s exit status\.
+.
+.P
+For example, \fB"Error: somthing went wrong\en"|halt_error(1)\fR\.
.
.SS "$__loc__"
Produces an object with a "file" key and a "line" key, with the filename and line number where \fB$__loc__\fR occurs, as values\.
return env;
}
+static jv f_halt(jq_state *jq, jv input) {
+ jv_free(input);
+ jq_halt(jq, jv_invalid(), jv_invalid());
+ return jv_true();
+}
+
+static jv f_halt_error(jq_state *jq, jv input, jv a) {
+ if (jv_get_kind(a) != JV_KIND_NUMBER)
+ return type_error(input, "halt_error/1: number required"); \
+ jq_halt(jq, a, input);
+ return jv_true();
+}
+
static jv f_get_search_list(jq_state *jq, jv input) {
jv_free(input);
return jq_get_lib_dirs(jq);
{(cfunction_ptr)f_error, "error", 2},
{(cfunction_ptr)f_format, "format", 2},
{(cfunction_ptr)f_env, "env", 1},
+ {(cfunction_ptr)f_halt, "halt", 1},
+ {(cfunction_ptr)f_halt_error, "halt_error", 2},
{(cfunction_ptr)f_get_search_list, "get_search_list", 1},
{(cfunction_ptr)f_get_prog_origin, "get_prog_origin", 1},
{(cfunction_ptr)f_get_jq_origin, "get_jq_origin", 1},
+def halt_error: halt_error(5);
def error: error(.);
def map(f): [.[] | f];
def select(f): if f then . else empty end;
int initial_execution;
unsigned next_label;
+ int halted;
+ jv exit_code;
+ jv error_message;
+
jv attrs;
jq_input_cb input_cb;
void *input_cb_data;
jv_free(jq->error);
jq->error = jv_null();
+ jq->halted = 0;
+ jv_free(jq->exit_code);
+ jv_free(jq->error_message);
if (jv_get_kind(jq->path) != JV_KIND_INVALID)
jv_free(jq->path);
jq->path = jv_null();
jq->initial_execution = 0;
assert(jv_get_kind(jq->error) == JV_KIND_NULL);
while (1) {
+ if (jq->halted) {
+ if (jq->debug_trace_enabled)
+ printf("\t<halted>\n");
+ return jv_invalid();
+ }
uint16_t opcode = *pc;
raising = 0;
jq->curr_frame = 0;
jq->error = jv_null();
+ jq->halted = 0;
+ jq->exit_code = jv_invalid();
+ jq->error_message = jv_invalid();
+
jq->err_cb = default_err_cb;
jq->err_cb_data = stderr;
*cb = jq->debug_cb;
*data = jq->debug_cb_data;
}
+
+void
+jq_halt(jq_state *jq, jv exit_code, jv error_message)
+{
+ assert(!jq->halted);
+ jq->halted = 1;
+ jq->exit_code = exit_code;
+ jq->error_message = error_message;
+}
+
+int
+jq_halted(jq_state *jq)
+{
+ return jq->halted;
+}
+
+jv jq_get_exit_code(jq_state *jq)
+{
+ return jv_copy(jq->exit_code);
+}
+
+jv jq_get_error_message(jq_state *jq)
+{
+ return jv_copy(jq->error_message);
+}
jv jq_next(jq_state *);
void jq_teardown(jq_state **);
+void jq_halt(jq_state *, jv, jv);
+int jq_halted(jq_state *);
+jv jq_get_exit_code(jq_state *);
+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 **);
RAW_NO_LF = 1024,
UNBUFFERED_OUTPUT = 2048,
EXIT_STATUS = 4096,
- SEQ = 8192,
- RUN_TESTS = 16384,
+ EXIT_STATUS_EXACT = 8192,
+ SEQ = 16384,
+ RUN_TESTS = 32768,
/* debugging only */
- DUMP_DISASM = 32768,
+ DUMP_DISASM = 65536,
};
static int options = 0;
if (options & UNBUFFERED_OUTPUT)
fflush(stdout);
}
- if (jv_invalid_has_msg(jv_copy(result))) {
+ if (jq_halted(jq)) {
+ // jq program invoked `halt` or `halt_error`
+ options |= EXIT_STATUS_EXACT;
+ jv exit_code = jq_get_exit_code(jq);
+ if (!jv_is_valid(exit_code))
+ ret = 0;
+ else if (jv_get_kind(exit_code) == JV_KIND_NUMBER)
+ ret = jv_number_value(exit_code);
+ else
+ ret = 5;
+ jv_free(exit_code);
+ jv error_message = jq_get_error_message(jq);
+ if (jv_get_kind(error_message) == JV_KIND_STRING) {
+ fprintf(stderr, "%s", jv_string_value(error_message));
+ } else if (jv_get_kind(error_message) == JV_KIND_NULL) {
+ // Halt with no output
+ } else if (jv_is_valid(error_message)) {
+ error_message = jv_dump_string(jv_copy(error_message), 0);
+ fprintf(stderr, "%s\n", jv_string_value(error_message));
+ } // else no message on stderr; use --debug-trace to see a message
+ fflush(stderr);
+ jv_free(error_message);
+ } else if (jv_invalid_has_msg(jv_copy(result))) {
// Uncaught jq exception
jv msg = jv_invalid_get_msg(jv_copy(result));
jv input_pos = jq_util_input_get_position(jq);
jq_teardown(&jq);
if (ret >= 10 && (options & EXIT_STATUS))
return ret - 10;
- if (ret >= 10)
+ if (ret >= 10 && !(options & EXIT_STATUS_EXACT))
return 0;
return ret;
}
if [ -z "${NO_VALGRIND-}" ] && which valgrind > /dev/null; then
VALGRIND="valgrind --error-exitcode=1 --leak-check=full \
--suppressions=$JQTESTDIR/onig.supp"
+ VG_EXIT0=--error-exitcode=0
Q=-q
else
VALGRIND=
+ VG_EXIT0=
Q=
fi
exit 1
fi
+## Halt
+
+if ! $VALGRIND $Q $JQ -n halt; then
+ echo "jq halt didn't work as expected" 1>&2
+ exit 1
+fi
+if $VALGRIND $Q $VG_EXIT0 $JQ -n 'halt_error(1)'; then
+ echo "jq halt_error(1) didn't work as expected" 1>&2
+ exit 1
+elif [ $? -ne 1 ]; then
+ echo "jq halt_error(1) had wrong error code" 1>&2
+ exit 1
+fi
+if $VALGRIND $Q $VG_EXIT0 $JQ -n 'halt_error(11)'; then
+ echo "jq halt_error(11) didn't work as expected" 1>&2
+ exit 1
+elif [ $? -ne 11 ]; then
+ echo "jq halt_error(11) had wrong error code" 1>&2
+ exit 1
+fi
+if [ -n "`$VALGRIND $Q $JQ -n 'halt_error(1)' 2>&1`" ]; then
+ echo "jq halt_error(1) had unexpected output" 1>&2
+ exit 1
+fi
+if [ -n "`$VALGRIND $Q $JQ -n '"xyz\n"|halt_error(1)' 2>/dev/null`" ]; then
+ echo "jq halt_error(1) had unexpected output on stdout" 1>&2
+ exit 1
+fi
+if [ "`$VALGRIND $Q $JQ -n '"xyz\n"|halt_error(1)' 2>&1`" != xyz ]; then
+ echo "jq halt_error(1) had unexpected output" 1>&2
+ exit 1
+fi
+
exit 0