2021-08-30 02:56:28 +03:00
|
|
|
include "internal";
|
2021-10-30 02:17:14 +03:00
|
|
|
include "options";
|
2022-02-08 20:44:48 +03:00
|
|
|
include "binary";
|
2021-12-04 19:20:12 +03:00
|
|
|
include "decode";
|
2021-08-30 02:56:28 +03:00
|
|
|
include "funcs";
|
2021-09-23 19:35:04 +03:00
|
|
|
include "grep";
|
2021-08-30 02:56:28 +03:00
|
|
|
include "args";
|
2022-02-21 00:25:46 +03:00
|
|
|
include "eval";
|
|
|
|
include "query";
|
2021-09-22 22:08:36 +03:00
|
|
|
include "repl";
|
2022-02-21 00:25:46 +03:00
|
|
|
include "help";
|
|
|
|
# generate torepr, format decode helpers and include format specific functions
|
2021-09-21 17:42:35 +03:00
|
|
|
include "formats";
|
2020-06-08 03:29:51 +03:00
|
|
|
# optional user init
|
|
|
|
include "@config/init?";
|
|
|
|
|
|
|
|
|
2021-12-05 18:24:52 +03:00
|
|
|
def d($opts): display($opts);
|
|
|
|
def d: display({});
|
2022-01-28 20:21:44 +03:00
|
|
|
def da($opts): display({array_truncate: 0} + $opts);
|
|
|
|
def da: da({});
|
|
|
|
def dd($opts): display({array_truncate: 0, display_bytes: 0} + $opts);
|
|
|
|
def dd: dd({});
|
|
|
|
def dv($opts): display({array_truncate: 0, verbose: true} + $opts);
|
|
|
|
def dv: dv({});
|
|
|
|
def ddv($opts): display({array_truncate: 0, display_bytes: 0, verbose: true} + $opts);
|
|
|
|
def ddv: ddv({});
|
2021-12-05 18:24:52 +03:00
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
# next valid input
|
|
|
|
def input:
|
2021-09-01 20:53:12 +03:00
|
|
|
def _input($opts; f):
|
|
|
|
( _input_filenames
|
|
|
|
| if length == 0 then error("break") end
|
|
|
|
| [.[0], .[1:]] as [$h, $t]
|
|
|
|
| _input_filenames($t)
|
|
|
|
| _input_filename(null) as $_
|
2022-01-23 14:35:19 +03:00
|
|
|
| ($h // "<stdin>") as $name
|
2021-09-01 20:53:12 +03:00
|
|
|
| $h
|
|
|
|
| try
|
2021-11-30 14:23:20 +03:00
|
|
|
# null input here means stdin
|
2021-09-01 20:53:12 +03:00
|
|
|
( open
|
2022-01-23 14:35:19 +03:00
|
|
|
| _input_filename($name) as $_
|
2021-09-01 20:53:12 +03:00
|
|
|
| .
|
|
|
|
)
|
|
|
|
catch
|
|
|
|
( . as $err
|
2022-01-23 14:35:19 +03:00
|
|
|
| _input_io_errors(. += {($name): $err}) as $_
|
2021-09-01 20:53:12 +03:00
|
|
|
| $err
|
2022-02-21 00:25:46 +03:00
|
|
|
| (_error_str([$name]) | printerrln)
|
2021-09-01 20:53:12 +03:00
|
|
|
, _input($opts; f)
|
|
|
|
)
|
|
|
|
| try f
|
|
|
|
catch
|
|
|
|
( . as $err
|
2022-01-23 14:35:19 +03:00
|
|
|
| _input_decode_errors(. += {($name): $err}) as $_
|
2022-02-21 00:25:46 +03:00
|
|
|
| [ $opts.decode_format
|
2021-11-22 15:28:18 +03:00
|
|
|
, if $err | type == "string" then ": \($err)"
|
|
|
|
# TODO: if not string assume decode itself failed for now
|
|
|
|
else ": failed to decode (try -d FORMAT)"
|
|
|
|
end
|
|
|
|
] | join("")
|
2022-02-21 00:25:46 +03:00
|
|
|
| (_error_str([$name]) | printerrln)
|
2021-09-01 20:53:12 +03:00
|
|
|
, _input($opts; f)
|
|
|
|
)
|
|
|
|
);
|
2021-11-01 13:40:24 +03:00
|
|
|
def _input_string($opts):
|
|
|
|
( _input_strings_lines
|
|
|
|
| if . then
|
|
|
|
# we're already iterating lines
|
|
|
|
if length == 0 then error("break")
|
|
|
|
else
|
|
|
|
( [.[0], .[1:]] as [$h, $t]
|
|
|
|
| _input_strings_lines($t)
|
|
|
|
| $h
|
|
|
|
)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
( [_repeat_break(_input($opts; tobytes | tostring))]
|
|
|
|
| . as $chunks
|
|
|
|
| if $opts.slurp then
|
|
|
|
# jq --raw-input combined with --slurp reads all inputs into a string
|
|
|
|
# make next input break
|
|
|
|
( _input_strings_lines([]) as $_
|
|
|
|
| $chunks
|
|
|
|
| join("")
|
|
|
|
)
|
|
|
|
else
|
|
|
|
# TODO: different line endings?
|
|
|
|
# jq strips last newline, "a\nb" and "a\nb\n" behaves the same
|
|
|
|
# also jq -R . <(echo -ne 'a\nb') <(echo c) produces "a" and "bc"
|
|
|
|
if ($chunks | length) > 0 then
|
|
|
|
( _input_strings_lines(
|
|
|
|
( $chunks
|
|
|
|
| join("")
|
|
|
|
| rtrimstr("\n")
|
|
|
|
| split("\n")
|
|
|
|
)
|
|
|
|
) as $_
|
|
|
|
| input
|
|
|
|
)
|
|
|
|
else error("break")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
)
|
|
|
|
end
|
|
|
|
);
|
2021-09-01 20:53:12 +03:00
|
|
|
# TODO: don't rebuild options each time
|
|
|
|
( options as $opts
|
2021-11-01 14:02:06 +03:00
|
|
|
# this is a bit strange as jq for --raw-input can return one string
|
|
|
|
# instead of iterating lines
|
2021-11-01 13:40:24 +03:00
|
|
|
| if $opts.string_input then _input_string($opts)
|
2021-10-02 11:53:01 +03:00
|
|
|
else _input($opts; decode)
|
2021-09-01 20:53:12 +03:00
|
|
|
end
|
2020-06-08 03:29:51 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
# iterate all valid inputs
|
2021-09-01 20:53:12 +03:00
|
|
|
def inputs: _repeat_break(input);
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
def input_filename: _input_filename;
|
|
|
|
|
2022-02-21 00:25:46 +03:00
|
|
|
# user expr error, report and continue
|
|
|
|
def _cli_eval_on_expr_error:
|
|
|
|
( if type == "object" then
|
|
|
|
if .error | _eval_is_compile_error then .error | _eval_compile_error_tostring
|
|
|
|
elif .error then .error
|
|
|
|
end
|
|
|
|
else tostring
|
|
|
|
end
|
|
|
|
| . as $err
|
2021-09-22 22:08:36 +03:00
|
|
|
| _cli_last_expr_error($err) as $_
|
2022-02-21 00:25:46 +03:00
|
|
|
| (_error_str([input_filename // empty]) | printerrln)
|
2021-09-22 22:08:36 +03:00
|
|
|
);
|
2022-02-21 00:25:46 +03:00
|
|
|
# other expr error, should not happen, report and halt
|
|
|
|
def _cli_eval_on_error:
|
|
|
|
halt_error(_exit_code_expr_error);
|
|
|
|
# could not compile expr, report and halt
|
|
|
|
def _cli_eval_on_compile_error:
|
|
|
|
( .error
|
|
|
|
| _eval_compile_error_tostring
|
2021-09-22 22:08:36 +03:00
|
|
|
| halt_error(_exit_code_compile_error)
|
|
|
|
);
|
2022-02-21 00:25:46 +03:00
|
|
|
def _cli_repl_error($_):
|
|
|
|
_eval_error("compile"; "repl can only be used from interactive repl");
|
|
|
|
def _cli_slurp_error(_):
|
|
|
|
_eval_error("compile"; "slurp can only be used from interactive repl");
|
|
|
|
# _cli_eval halts on compile errors
|
|
|
|
def _cli_eval($expr; $opts):
|
|
|
|
eval(
|
|
|
|
$expr;
|
|
|
|
$opts + {
|
|
|
|
slurps: {
|
|
|
|
help: "_help_slurp",
|
|
|
|
repl: "_cli_repl_error",
|
|
|
|
slurp: "_cli_slurp_error"
|
|
|
|
},
|
|
|
|
catch_query: _query_func("_cli_eval_on_expr_error")
|
|
|
|
};
|
|
|
|
_cli_eval_on_error;
|
|
|
|
_cli_eval_on_compile_error
|
|
|
|
);
|
2021-09-22 22:08:36 +03:00
|
|
|
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
def _main:
|
2022-02-20 23:48:01 +03:00
|
|
|
def _map_decode_file:
|
|
|
|
map(
|
|
|
|
( . as $a
|
|
|
|
| .[1] |=
|
|
|
|
try (open | decode)
|
|
|
|
catch
|
|
|
|
( "--decode-file \($a[0]): \(.)"
|
|
|
|
| halt_error(_exit_code_args_error)
|
|
|
|
)
|
|
|
|
)
|
2021-08-14 01:11:57 +03:00
|
|
|
);
|
2022-01-26 20:09:59 +03:00
|
|
|
( . as {$version, $os, $arch, $args, args: [$arg0]}
|
2020-06-08 03:29:51 +03:00
|
|
|
# make sure we don't unintentionally use . to make things clearer
|
|
|
|
| null
|
2021-11-16 12:49:18 +03:00
|
|
|
| ( try _args_parse($args[1:]; _opt_cli_opts)
|
2021-08-09 13:47:20 +03:00
|
|
|
catch halt_error(_exit_code_args_error)
|
|
|
|
) as {parsed: $parsed_args, $rest}
|
2022-02-20 23:48:01 +03:00
|
|
|
# combine default fixed opt, parsed args and -o key=value opts
|
|
|
|
| _options_stack([
|
|
|
|
( ( _opt_build_default_fixed
|
|
|
|
+ $parsed_args
|
2022-03-03 15:37:35 +03:00
|
|
|
+ ($parsed_args.option | _opt_cli_arg_tooptions)
|
2022-02-20 23:48:01 +03:00
|
|
|
)
|
|
|
|
| . + _opt_eval($rest)
|
|
|
|
)
|
|
|
|
]) as $_
|
2021-11-16 12:49:18 +03:00
|
|
|
| _opt_build_default_fixed as $default_fixed_opts
|
|
|
|
# combine default fixed opt, --args opts and -o key=value opts
|
2021-09-22 19:03:28 +03:00
|
|
|
| ( $default_fixed_opts
|
2021-09-01 20:53:12 +03:00
|
|
|
+ $parsed_args
|
2022-03-03 15:37:35 +03:00
|
|
|
+ ($parsed_args.option | _opt_cli_arg_tooptions)
|
2021-11-16 12:49:18 +03:00
|
|
|
) as $combined_opts
|
2021-08-26 16:38:49 +03:00
|
|
|
| options as $opts
|
2022-03-03 15:37:35 +03:00
|
|
|
| if $opts.show_help then
|
|
|
|
( if ($opts.show_help | type) == "boolean" then
|
|
|
|
( ("banner", "", "usage", "", "example_usage", "", "args")
|
|
|
|
| if . != "" then _help($arg0; .) end
|
|
|
|
)
|
|
|
|
else _help($arg0; $opts.show_help)
|
|
|
|
end
|
|
|
|
| println
|
|
|
|
)
|
2022-02-20 23:48:01 +03:00
|
|
|
elif $opts.show_version then "\($version) (\($os) \($arch))" | println
|
2021-09-01 16:01:13 +03:00
|
|
|
elif
|
2021-11-30 14:23:20 +03:00
|
|
|
( $opts.filenames == [null] and
|
|
|
|
$opts.null_input == false and
|
2021-09-01 16:01:13 +03:00
|
|
|
($opts.repl | not) and
|
|
|
|
($opts.expr_file | not) and
|
2022-01-05 23:36:22 +03:00
|
|
|
stdin_tty.is_terminal and
|
|
|
|
stdout_tty.is_terminal
|
2021-09-01 16:01:13 +03:00
|
|
|
) then
|
2022-03-03 15:37:35 +03:00
|
|
|
( (_help($arg0; "usage") | printerrln)
|
2021-08-14 01:11:57 +03:00
|
|
|
, null | halt_error(_exit_code_args_error)
|
|
|
|
)
|
2020-06-08 03:29:51 +03:00
|
|
|
else
|
2022-02-21 00:25:46 +03:00
|
|
|
( # store some global state
|
2021-09-05 14:38:13 +03:00
|
|
|
( _include_paths($opts.include_path) as $_
|
2021-09-07 01:26:24 +03:00
|
|
|
| _input_filenames($opts.filenames) as $_
|
2022-02-21 00:25:46 +03:00
|
|
|
| _slurps(
|
2021-09-07 00:42:05 +03:00
|
|
|
( $opts.arg +
|
|
|
|
$opts.argjson +
|
2021-11-01 14:02:06 +03:00
|
|
|
$opts.raw_file +
|
2022-02-20 23:48:01 +03:00
|
|
|
($opts.decode_file | if . then _map_decode_file end)
|
2021-09-07 00:42:05 +03:00
|
|
|
| map({key: .[0], value: .[1]})
|
|
|
|
| from_entries
|
|
|
|
)
|
|
|
|
)
|
2022-02-21 00:25:46 +03:00
|
|
|
) as $_
|
|
|
|
| { filename: $opts.expr_eval_path
|
|
|
|
} as $eval_opts
|
|
|
|
# use _finally as display etc prints and outputs empty
|
|
|
|
| _finally(
|
|
|
|
if $opts.repl then
|
|
|
|
# TODO: share input_query but first have to figure out how to handle
|
|
|
|
# context/interrupts better as open will happen in a sub repl which
|
|
|
|
# context will be cancelled.
|
|
|
|
( def _inputs:
|
|
|
|
if $opts.null_input then null
|
|
|
|
elif $opts.string_input then inputs
|
|
|
|
elif $opts.slurp then [inputs]
|
|
|
|
else inputs
|
|
|
|
end;
|
|
|
|
[_inputs]
|
|
|
|
| map(_cli_eval($opts.expr; $eval_opts))
|
|
|
|
| _repl({})
|
|
|
|
)
|
|
|
|
else
|
|
|
|
( _cli_last_expr_error(null) as $_
|
|
|
|
| _display_default_opts as $default_opts
|
|
|
|
| _cli_eval(
|
|
|
|
$opts.expr;
|
|
|
|
( $eval_opts
|
|
|
|
| .input_query =
|
|
|
|
( if $opts.null_input then _query_null
|
|
|
|
# note that jq --slurp --raw-input (string_input) is special, will concat
|
|
|
|
# all files into one string instead of iterating lines
|
|
|
|
elif $opts.string_input then _query_func("inputs")
|
|
|
|
elif $opts.slurp then _query_func("inputs") | _query_array
|
|
|
|
else _query_func("inputs")
|
|
|
|
end
|
|
|
|
)
|
2021-09-01 16:01:13 +03:00
|
|
|
)
|
2022-02-21 00:25:46 +03:00
|
|
|
)
|
|
|
|
| display($default_opts)
|
2021-09-03 04:30:52 +03:00
|
|
|
)
|
2022-02-21 00:25:46 +03:00
|
|
|
end;
|
|
|
|
# finally
|
|
|
|
( if _input_io_errors then null | halt_error(_exit_code_input_io_error) end
|
|
|
|
| if _input_decode_errors then null | halt_error(_exit_code_input_decode_error) end
|
|
|
|
| if _cli_last_expr_error then null | halt_error(_exit_code_expr_error) end
|
2021-08-13 01:55:29 +03:00
|
|
|
)
|
2020-06-08 03:29:51 +03:00
|
|
|
)
|
2022-02-21 00:25:46 +03:00
|
|
|
)
|
2020-06-08 03:29:51 +03:00
|
|
|
end
|
|
|
|
);
|