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
+78 -36
+78 -36
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,7 +61,14 @@ 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",
JQ_VERSION, progname, progname, progname);
if (keep_it_short) {
fprintf(f,
"For a listing of options, use %s --help.\n",
progname);
} else {
(void) fprintf(f,
"Some of the options include:\n" "Some of the options include:\n"
" -c compact instead of pretty-printed output;\n" " -c compact instead of pretty-printed output;\n"
" -n use `null` as the single input value;\n" " -n use `null` as the single input value;\n"
@@ -74,7 +83,13 @@ static void usage(int code) {
" --arg a v set variable $a to value <v>;\n" " --arg a v set variable $a to value <v>;\n"
" --argjson a v set variable $a to JSON 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" " --slurpfile a f set variable $a to an array of JSON texts read from <f>;\n"
"\nSee the manpage for more options.\n", JQ_VERSION, progname); " --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) {
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]); jq_util_input_add_input(input_state, argv[i]);
nfiles++; nfiles++;
}
} else if (!strcmp(argv[i], "--")) { } else if (!strcmp(argv[i], "--")) {
if (!program) usage(2); if (!program) usage(2, 1);
further_args_are_files = 1; args_done = 1;
} else if (!isoptish(argv[i])) { } else if (!isoptish(argv[i])) {
if (program) { 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]); jq_util_input_add_input(input_state, argv[i]);
nfiles++; 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();
} }
if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) {
jv v = jv_parse(argv[i+2]); jv v = jv_parse(argv[i+2]);
if (!jv_is_valid(v)) { if (!jv_is_valid(v)) {
fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname); fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname);
die(); die();
} }
jv arg = jv_object(); program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), v);
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);
jv_free(arg);
ret = 2; ret = 2;
goto out; goto out;
} }
if (isoption(argv[i], 0, "argfile", &short_opts) && if (strcmp(which, "argfile") == 0 &&
jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1) jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
data = jv_array_get(data, 0); data = jv_array_get(data, 0);
arg = jv_object_set(arg, jv_string("value"), data); program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), 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);