mirror of
https://github.com/stedolan/jq.git
synced 2024-05-11 05:55:39 +00:00
Add halt, halt_error builtins (fix #386)
This commit is contained in:
@@ -209,6 +209,9 @@ sections:
|
|||||||
problem or system error, 3 if there was a jq program compile
|
problem or system error, 3 if there was a jq program compile
|
||||||
error, or 0 if the jq program ran.
|
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`:
|
* `--arg name value`:
|
||||||
|
|
||||||
This option passes a value to the jq program as a predefined
|
This option passes a value to the jq program as a predefined
|
||||||
@@ -982,7 +985,25 @@ sections:
|
|||||||
|
|
||||||
Produces an error, just like `.a` applied to values other than
|
Produces an error, just like `.a` applied to values other than
|
||||||
null and objects would, but with the given message as the
|
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__`"
|
- title: "`$__loc__`"
|
||||||
body: |
|
body: |
|
||||||
|
|||||||
@@ -153,6 +153,9 @@ Prepend \fBdirectory\fR to the search list for modules\. If this option is used
|
|||||||
.IP
|
.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\.
|
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
|
.IP "\(bu" 4
|
||||||
\fB\-\-arg name value\fR:
|
\fB\-\-arg name value\fR:
|
||||||
.
|
.
|
||||||
@@ -1017,7 +1020,19 @@ jq \'[1,2,empty,3]\'
|
|||||||
.IP "" 0
|
.IP "" 0
|
||||||
.
|
.
|
||||||
.SS "error(message)"
|
.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__"
|
.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\.
|
Produces an object with a "file" key and a "line" key, with the filename and line number where \fB$__loc__\fR occurs, as values\.
|
||||||
|
|||||||
@@ -1031,6 +1031,19 @@ static jv f_env(jq_state *jq, jv input) {
|
|||||||
return env;
|
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) {
|
static jv f_get_search_list(jq_state *jq, jv input) {
|
||||||
jv_free(input);
|
jv_free(input);
|
||||||
return jq_get_lib_dirs(jq);
|
return jq_get_lib_dirs(jq);
|
||||||
@@ -1467,6 +1480,8 @@ static const struct cfunction function_list[] = {
|
|||||||
{(cfunction_ptr)f_error, "error", 2},
|
{(cfunction_ptr)f_error, "error", 2},
|
||||||
{(cfunction_ptr)f_format, "format", 2},
|
{(cfunction_ptr)f_format, "format", 2},
|
||||||
{(cfunction_ptr)f_env, "env", 1},
|
{(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_search_list, "get_search_list", 1},
|
||||||
{(cfunction_ptr)f_get_prog_origin, "get_prog_origin", 1},
|
{(cfunction_ptr)f_get_prog_origin, "get_prog_origin", 1},
|
||||||
{(cfunction_ptr)f_get_jq_origin, "get_jq_origin", 1},
|
{(cfunction_ptr)f_get_jq_origin, "get_jq_origin", 1},
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
def halt_error: halt_error(5);
|
||||||
def error: error(.);
|
def error: error(.);
|
||||||
def map(f): [.[] | f];
|
def map(f): [.[] | f];
|
||||||
def select(f): if f then . else empty end;
|
def select(f): if f then . else empty end;
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ struct jq_state {
|
|||||||
int initial_execution;
|
int initial_execution;
|
||||||
unsigned next_label;
|
unsigned next_label;
|
||||||
|
|
||||||
|
int halted;
|
||||||
|
jv exit_code;
|
||||||
|
jv error_message;
|
||||||
|
|
||||||
jv attrs;
|
jv attrs;
|
||||||
jq_input_cb input_cb;
|
jq_input_cb input_cb;
|
||||||
void *input_cb_data;
|
void *input_cb_data;
|
||||||
@@ -285,6 +289,9 @@ static void jq_reset(jq_state *jq) {
|
|||||||
jv_free(jq->error);
|
jv_free(jq->error);
|
||||||
jq->error = jv_null();
|
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)
|
if (jv_get_kind(jq->path) != JV_KIND_INVALID)
|
||||||
jv_free(jq->path);
|
jv_free(jq->path);
|
||||||
jq->path = jv_null();
|
jq->path = jv_null();
|
||||||
@@ -320,6 +327,11 @@ jv jq_next(jq_state *jq) {
|
|||||||
jq->initial_execution = 0;
|
jq->initial_execution = 0;
|
||||||
assert(jv_get_kind(jq->error) == JV_KIND_NULL);
|
assert(jv_get_kind(jq->error) == JV_KIND_NULL);
|
||||||
while (1) {
|
while (1) {
|
||||||
|
if (jq->halted) {
|
||||||
|
if (jq->debug_trace_enabled)
|
||||||
|
printf("\t<halted>\n");
|
||||||
|
return jv_invalid();
|
||||||
|
}
|
||||||
uint16_t opcode = *pc;
|
uint16_t opcode = *pc;
|
||||||
raising = 0;
|
raising = 0;
|
||||||
|
|
||||||
@@ -932,6 +944,10 @@ jq_state *jq_init(void) {
|
|||||||
jq->curr_frame = 0;
|
jq->curr_frame = 0;
|
||||||
jq->error = jv_null();
|
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 = default_err_cb;
|
||||||
jq->err_cb_data = stderr;
|
jq->err_cb_data = stderr;
|
||||||
|
|
||||||
@@ -1163,3 +1179,28 @@ void jq_get_debug_cb(jq_state *jq, jq_msg_cb *cb, void **data) {
|
|||||||
*cb = jq->debug_cb;
|
*cb = jq->debug_cb;
|
||||||
*data = jq->debug_cb_data;
|
*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);
|
||||||
|
}
|
||||||
|
|||||||
5
src/jq.h
5
src/jq.h
@@ -22,6 +22,11 @@ void jq_start(jq_state *, jv value, int);
|
|||||||
jv jq_next(jq_state *);
|
jv jq_next(jq_state *);
|
||||||
void jq_teardown(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 *);
|
typedef jv (*jq_input_cb)(jq_state *, void *);
|
||||||
void jq_set_input_cb(jq_state *, jq_input_cb, 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_get_input_cb(jq_state *, jq_input_cb *, void **);
|
||||||
|
|||||||
33
src/main.c
33
src/main.c
@@ -137,10 +137,11 @@ enum {
|
|||||||
RAW_NO_LF = 1024,
|
RAW_NO_LF = 1024,
|
||||||
UNBUFFERED_OUTPUT = 2048,
|
UNBUFFERED_OUTPUT = 2048,
|
||||||
EXIT_STATUS = 4096,
|
EXIT_STATUS = 4096,
|
||||||
SEQ = 8192,
|
EXIT_STATUS_EXACT = 8192,
|
||||||
RUN_TESTS = 16384,
|
SEQ = 16384,
|
||||||
|
RUN_TESTS = 32768,
|
||||||
/* debugging only */
|
/* debugging only */
|
||||||
DUMP_DISASM = 32768,
|
DUMP_DISASM = 65536,
|
||||||
};
|
};
|
||||||
static int options = 0;
|
static int options = 0;
|
||||||
|
|
||||||
@@ -182,7 +183,29 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) {
|
|||||||
if (options & UNBUFFERED_OUTPUT)
|
if (options & UNBUFFERED_OUTPUT)
|
||||||
fflush(stdout);
|
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
|
// Uncaught jq exception
|
||||||
jv msg = jv_invalid_get_msg(jv_copy(result));
|
jv msg = jv_invalid_get_msg(jv_copy(result));
|
||||||
jv input_pos = jq_util_input_get_position(jq);
|
jv input_pos = jq_util_input_get_position(jq);
|
||||||
@@ -623,7 +646,7 @@ out:
|
|||||||
jq_teardown(&jq);
|
jq_teardown(&jq);
|
||||||
if (ret >= 10 && (options & EXIT_STATUS))
|
if (ret >= 10 && (options & EXIT_STATUS))
|
||||||
return ret - 10;
|
return ret - 10;
|
||||||
if (ret >= 10)
|
if (ret >= 10 && !(options & EXIT_STATUS_EXACT))
|
||||||
return 0;
|
return 0;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ JQ=$JQBASEDIR/jq
|
|||||||
if [ -z "${NO_VALGRIND-}" ] && which valgrind > /dev/null; then
|
if [ -z "${NO_VALGRIND-}" ] && which valgrind > /dev/null; then
|
||||||
VALGRIND="valgrind --error-exitcode=1 --leak-check=full \
|
VALGRIND="valgrind --error-exitcode=1 --leak-check=full \
|
||||||
--suppressions=$JQTESTDIR/onig.supp"
|
--suppressions=$JQTESTDIR/onig.supp"
|
||||||
|
VG_EXIT0=--error-exitcode=0
|
||||||
Q=-q
|
Q=-q
|
||||||
else
|
else
|
||||||
VALGRIND=
|
VALGRIND=
|
||||||
|
VG_EXIT0=
|
||||||
Q=
|
Q=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
33
tests/shtest
33
tests/shtest
@@ -211,4 +211,37 @@ if ! $VALGRIND $Q $JQ -L tests/modules -ne 'import "test_bind_order" as check; c
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
exit 0
|
||||||
|
|||||||
Reference in New Issue
Block a user