Add $ARGS, --args and --jsonargs (fix #1345)

This commit is contained in:
Nicolas Williams committed 2017-02-25 19:13:28 -06:00
1 parent e8abc0a2f6
commit 66fb962a66
1 file changed
+111 -69
+111 -69
View File
@@ -41,7 +41,7 @@ static const char* progname;
* For a longer help message we could use a better option parsing * For a longer help message we could use a better option parsing
* strategy, one that lets stack options. * strategy, one that lets stack options.
*/ */
static void usage(int code) { static void usage(int code, int keep_it_short) {
FILE *f = stderr; FILE *f = stderr;
if (code == 0) if (code == 0)
@@ -49,7 +49,9 @@ static void usage(int code) {
int ret = fprintf(f, int ret = fprintf(f,
"jq - commandline JSON processor [version %s]\n" "jq - commandline JSON processor [version %s]\n"
"\nUsage:\n\n\t%s [options] <jq filter> [file...]\n\n" "\nUsage:\t%s [options] <jq filter> [file...]\n"
"\t%s [options] --args <jq filter> [strings...]\n"
"\t%s [options] --jsonargs <jq filter> [JSON_TEXTS...]\n\n"
"jq is a tool for processing JSON inputs, applying the given filter to\n" "jq is a tool for processing JSON inputs, applying the given filter to\n"
"its JSON text inputs and producing the filter's results as JSON on\n" "its JSON text inputs and producing the filter's results as JSON on\n"
"standard output.\n\n" "standard output.\n\n"
@@ -59,22 +61,35 @@ static void usage(int code) {
"For more advanced filters see the jq(1) manpage (\"man jq\")\n" "For more advanced filters see the jq(1) manpage (\"man jq\")\n"
"and/or https://stedolan.github.io/jq\n\n" "and/or https://stedolan.github.io/jq\n\n"
"Example:\n\n\t$ echo '{\"foo\": 0}' | jq .\n" "Example:\n\n\t$ echo '{\"foo\": 0}' | jq .\n"
"\t{\n\t\t\"foo\": 0\n\t}\n\n" "\t{\n\t\t\"foo\": 0\n\t}\n\n",
"Some of the options include:\n" JQ_VERSION, progname, progname, progname);
" -c compact instead of pretty-printed output;\n" if (keep_it_short) {
" -n use `null` as the single input value;\n" fprintf(f,
" -e set the exit status code based on the output;\n" "For a listing of options, use %s --help.\n",
" -s read (slurp) all inputs into an array; apply filter to it;\n" progname);
" -r output raw strings, not JSON texts;\n" } else {
" -R read raw strings, not JSON texts;\n" (void) fprintf(f,
" -C colorize JSON;\n" "Some of the options include:\n"
" -M monochrome (don't colorize JSON);\n" " -c compact instead of pretty-printed output;\n"
" -S sort keys of objects on output;\n" " -n use `null` as the single input value;\n"
" --tab use tabs for indentation;\n" " -e set the exit status code based on the output;\n"
" --arg a v set variable $a to value <v>;\n" " -s read (slurp) all inputs into an array; apply filter to it;\n"
" --argjson a v set variable $a to JSON value <v>;\n" " -r output raw strings, not JSON texts;\n"
" --slurpfile a f set variable $a to an array of JSON texts read from <f>;\n" " -R read raw strings, not JSON texts;\n"
"\nSee the manpage for more options.\n", JQ_VERSION, progname); " -C colorize JSON;\n"
" -M monochrome (don't colorize JSON);\n"
" -S sort keys of objects on output;\n"
" --tab use tabs for indentation;\n"
" --arg a v set variable $a to value <v>;\n"
" --argjson a v set variable $a to JSON value <v>;\n"
" --slurpfile a f set variable $a to an array of JSON texts read from <f>;\n"
" --args remaining arguments are string arguments, not files;\n"
" --jsonargs remaining arguments are JSON arguments, not files;\n"
" -- terminates argument processing;\n\n"
"Named arguments are also available as $ARGS.named[], while\n"
"positional arguments are available as $ARGS.positional[].\n"
"\nSee the manpage for more options.\n");
}
exit((ret < 0 && code == 0) ? 2 : code); exit((ret < 0 && code == 0) ? 2 : code);
} }
@@ -200,7 +215,8 @@ int main(int argc, char* argv[]) {
int parser_flags = 0; int parser_flags = 0;
int nfiles = 0; int nfiles = 0;
int badwrite; int badwrite;
jv program_arguments = jv_array(); jv ARGS = jv_array(); /* positional arguments */
jv program_arguments = jv_object(); /* named arguments */
#ifdef WIN32 #ifdef WIN32
fflush(stdout); fflush(stdout);
@@ -234,21 +250,35 @@ int main(int argc, char* argv[]) {
jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb
int further_args_are_files = 0; int further_args_are_strings = 0;
int further_args_are_json = 0;
int args_done = 0;
int jq_flags = 0; int jq_flags = 0;
size_t short_opts = 0; size_t short_opts = 0;
jv lib_search_paths = jv_null(); jv lib_search_paths = jv_null();
for (int i=1; i<argc; i++, short_opts = 0) { for (int i=1; i<argc; i++, short_opts = 0) {
if (further_args_are_files) { if (args_done) {
jq_util_input_add_input(input_state, argv[i]); if (further_args_are_strings) {
nfiles++; ARGS = jv_array_append(ARGS, jv_string(argv[i]));
} else if (!strcmp(argv[i], "--")) { } else if (further_args_are_json) {
if (!program) usage(2); ARGS = jv_array_append(ARGS, jv_parse(argv[i]));
further_args_are_files = 1; } else {
} else if (!isoptish(argv[i])) {
if (program) {
jq_util_input_add_input(input_state, argv[i]); jq_util_input_add_input(input_state, argv[i]);
nfiles++; nfiles++;
}
} else if (!strcmp(argv[i], "--")) {
if (!program) usage(2, 1);
args_done = 1;
} else if (!isoptish(argv[i])) {
if (program) {
if (further_args_are_strings) {
ARGS = jv_array_append(ARGS, jv_string(argv[i]));
} else if (further_args_are_json) {
ARGS = jv_array_append(ARGS, jv_parse(argv[i]));
} else {
jq_util_input_add_input(input_state, argv[i]);
nfiles++;
}
} else { } else {
program = argv[i]; program = argv[i];
} }
@@ -294,7 +324,7 @@ int main(int argc, char* argv[]) {
} }
if (isoption(argv[i], 0, "unbuffered", &short_opts)) { if (isoption(argv[i], 0, "unbuffered", &short_opts)) {
options |= UNBUFFERED_OUTPUT; options |= UNBUFFERED_OUTPUT;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 'S', "sort-keys", &short_opts)) { if (isoption(argv[i], 'S', "sort-keys", &short_opts)) {
options |= SORTED_OUTPUT; options |= SORTED_OUTPUT;
@@ -319,7 +349,7 @@ int main(int argc, char* argv[]) {
if (isoption(argv[i], 0, "tab", &short_opts)) { if (isoption(argv[i], 0, "tab", &short_opts)) {
dumpopts &= ~JV_PRINT_INDENT_FLAGS(7); dumpopts &= ~JV_PRINT_INDENT_FLAGS(7);
dumpopts |= JV_PRINT_TAB | JV_PRINT_PRETTY; dumpopts |= JV_PRINT_TAB | JV_PRINT_PRETTY;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "indent", &short_opts)) { if (isoption(argv[i], 0, "indent", &short_opts)) {
if (i >= argc - 1) { if (i >= argc - 1) {
@@ -334,53 +364,60 @@ int main(int argc, char* argv[]) {
} }
dumpopts |= JV_PRINT_INDENT_FLAGS(indent); dumpopts |= JV_PRINT_INDENT_FLAGS(indent);
i++; i++;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "seq", &short_opts)) { if (isoption(argv[i], 0, "seq", &short_opts)) {
options |= SEQ; options |= SEQ;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "stream", &short_opts)) { if (isoption(argv[i], 0, "stream", &short_opts)) {
parser_flags |= JV_PARSE_STREAMING; parser_flags |= JV_PARSE_STREAMING;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "stream-errors", &short_opts)) { if (isoption(argv[i], 0, "stream-errors", &short_opts)) {
parser_flags |= JV_PARSE_STREAM_ERRORS; parser_flags |= JV_PARSE_STREAM_ERRORS;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 'e', "exit-status", &short_opts)) { if (isoption(argv[i], 'e', "exit-status", &short_opts)) {
options |= EXIT_STATUS; options |= EXIT_STATUS;
if (!short_opts) continue; if (!short_opts) continue;
} }
// FIXME: For --arg* we should check that the varname is acceptable // FIXME: For --arg* we should check that the varname is acceptable
if (isoption(argv[i], 0, "args", &short_opts)) {
further_args_are_strings = 1;
further_args_are_json = 0;
continue;
}
if (isoption(argv[i], 0, "jsonargs", &short_opts)) {
further_args_are_strings = 0;
further_args_are_json = 1;
continue;
}
if (isoption(argv[i], 0, "arg", &short_opts)) { if (isoption(argv[i], 0, "arg", &short_opts)) {
if (i >= argc - 2) { if (i >= argc - 2) {
fprintf(stderr, "%s: --arg takes two parameters (e.g. --arg varname value)\n", progname); fprintf(stderr, "%s: --arg takes two parameters (e.g. --arg varname value)\n", progname);
die(); die();
} }
jv arg = jv_object(); if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1])))
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), jv_string(argv[i+2]));
arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2]));
program_arguments = jv_array_append(program_arguments, arg);
i += 2; // skip the next two arguments i += 2; // skip the next two arguments
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "argjson", &short_opts)) { if (isoption(argv[i], 0, "argjson", &short_opts)) {
if (i >= argc - 2) { if (i >= argc - 2) {
fprintf(stderr, "%s: --argjson takes two parameters (e.g. --argjson varname text)\n", progname); fprintf(stderr, "%s: --argjson takes two parameters (e.g. --argjson varname text)\n", progname);
die(); die();
} }
jv v = jv_parse(argv[i+2]); if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) {
if (!jv_is_valid(v)) { jv v = jv_parse(argv[i+2]);
fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname); if (!jv_is_valid(v)) {
die(); fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname);
die();
}
program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), v);
} }
jv arg = jv_object();
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
arg = jv_object_set(arg, jv_string("value"), v);
program_arguments = jv_array_append(program_arguments, arg);
i += 2; // skip the next two arguments i += 2; // skip the next two arguments
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "argfile", &short_opts) || if (isoption(argv[i], 0, "argfile", &short_opts) ||
isoption(argv[i], 0, "slurpfile", &short_opts)) { isoption(argv[i], 0, "slurpfile", &short_opts)) {
@@ -393,36 +430,34 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which); fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which);
die(); die();
} }
jv arg = jv_object(); if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) {
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); jv data = jv_load_file(argv[i+2], 0);
jv data = jv_load_file(argv[i+2], 0); if (!jv_is_valid(data)) {
if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data);
data = jv_invalid_get_msg(data); fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which,
fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which, argv[i+1], argv[i+2], jv_string_value(data));
argv[i+1], argv[i+2], jv_string_value(data)); jv_free(data);
jv_free(data); ret = 2;
jv_free(arg); goto out;
ret = 2; }
goto out; if (strcmp(which, "argfile") == 0 &&
jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
data = jv_array_get(data, 0);
program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), data);
} }
if (isoption(argv[i], 0, "argfile", &short_opts) &&
jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
data = jv_array_get(data, 0);
arg = jv_object_set(arg, jv_string("value"), data);
program_arguments = jv_array_append(program_arguments, arg);
i += 2; // skip the next two arguments i += 2; // skip the next two arguments
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) { if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) {
options |= DUMP_DISASM; options |= DUMP_DISASM;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 0, "debug-trace", &short_opts)) { if (isoption(argv[i], 0, "debug-trace", &short_opts)) {
jq_flags |= JQ_DEBUG_TRACE; jq_flags |= JQ_DEBUG_TRACE;
if (!short_opts) continue; continue;
} }
if (isoption(argv[i], 'h', "help", &short_opts)) { if (isoption(argv[i], 'h', "help", &short_opts)) {
usage(0); usage(0, 0);
if (!short_opts) continue; if (!short_opts) continue;
} }
if (isoption(argv[i], 'V', "version", &short_opts)) { if (isoption(argv[i], 'V', "version", &short_opts)) {
@@ -489,7 +524,7 @@ int main(int argc, char* argv[]) {
program = "."; program = ".";
#endif #endif
if (!program) usage(2); if (!program) usage(2, 1);
if (options & FROM_FILE) { if (options & FROM_FILE) {
char *program_origin = strdup(program); char *program_origin = strdup(program);
@@ -507,11 +542,17 @@ int main(int argc, char* argv[]) {
goto out; goto out;
} }
jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin)))); jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin))));
ARGS = JV_OBJECT(jv_string("positional"), ARGS,
jv_string("named"), jv_copy(program_arguments));
program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS));
compiled = jq_compile_args(jq, skip_shebang(jv_string_value(data)), jv_copy(program_arguments)); compiled = jq_compile_args(jq, skip_shebang(jv_string_value(data)), jv_copy(program_arguments));
free(program_origin); free(program_origin);
jv_free(data); jv_free(data);
} else { } else {
jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good? jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good?
ARGS = JV_OBJECT(jv_string("positional"), ARGS,
jv_string("named"), jv_copy(program_arguments));
program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS));
compiled = jq_compile_args(jq, program, jv_copy(program_arguments)); compiled = jq_compile_args(jq, program, jv_copy(program_arguments));
} }
if (!compiled){ if (!compiled){
@@ -576,6 +617,7 @@ out:
ret = 2; ret = 2;
} }
jv_free(ARGS);
jv_free(program_arguments); jv_free(program_arguments);
jq_util_input_free(&input_state); jq_util_input_free(&input_state);
jq_teardown(&jq); jq_teardown(&jq);