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
|
||||
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
|
||||
@@ -982,7 +985,25 @@ sections:
|
||||
|
||||
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: |
|
||||
|
@@ -153,6 +153,9 @@ Prepend \fBdirectory\fR to the search list for modules\. If this option is used
|
||||
.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:
|
||||
.
|
||||
@@ -1017,7 +1020,19 @@ jq \'[1,2,empty,3]\'
|
||||
.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\.
|
||||
|
@@ -1031,6 +1031,19 @@ static jv f_env(jq_state *jq, jv input) {
|
||||
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);
|
||||
@@ -1467,6 +1480,8 @@ static const struct cfunction function_list[] = {
|
||||
{(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},
|
||||
|
@@ -1,3 +1,4 @@
|
||||
def halt_error: halt_error(5);
|
||||
def error: error(.);
|
||||
def map(f): [.[] | f];
|
||||
def select(f): if f then . else empty end;
|
||||
|
@@ -40,6 +40,10 @@ struct jq_state {
|
||||
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;
|
||||
@@ -285,6 +289,9 @@ static void jq_reset(jq_state *jq) {
|
||||
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();
|
||||
@@ -320,6 +327,11 @@ jv jq_next(jq_state *jq) {
|
||||
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;
|
||||
|
||||
@@ -932,6 +944,10 @@ jq_state *jq_init(void) {
|
||||
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;
|
||||
|
||||
@@ -1163,3 +1179,28 @@ void jq_get_debug_cb(jq_state *jq, jq_msg_cb *cb, void **data) {
|
||||
*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);
|
||||
}
|
||||
|
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 *);
|
||||
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 **);
|
||||
|
33
src/main.c
33
src/main.c
@@ -137,10 +137,11 @@ enum {
|
||||
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;
|
||||
|
||||
@@ -182,7 +183,29 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) {
|
||||
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);
|
||||
@@ -623,7 +646,7 @@ out:
|
||||
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;
|
||||
}
|
||||
|
@@ -15,9 +15,11 @@ JQ=$JQBASEDIR/jq
|
||||
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
|
||||
|
||||
|
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
|
||||
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
|
||||
|
Reference in New Issue
Block a user