14 #include <processenv.h>
20 #if !defined(HAVE_ISATTY) && defined(HAVE__ISATTY)
22 #define isatty _isatty
25 #if defined(HAVE_ISATTY) || defined(HAVE__ISATTY)
34 #include "src/version.h"
36 int jq_testsuite(jv lib_dirs, int verbose, int argc, char* argv[]);
38 static const char* progname;
41 * For a longer help message we could use a better option parsing
42 * strategy, one that lets stack options.
44 static void usage(int code, int keep_it_short) {
51 "jq - commandline JSON processor [version %s]\n"
52 "\nUsage:\t%s [options] <jq filter> [file...]\n"
53 "\t%s [options] --args <jq filter> [strings...]\n"
54 "\t%s [options] --jsonargs <jq filter> [JSON_TEXTS...]\n\n"
55 "jq is a tool for processing JSON inputs, applying the given filter to\n"
56 "its JSON text inputs and producing the filter's results as JSON on\n"
57 "standard output.\n\n"
58 "The simplest filter is ., which copies jq's input to its output\n"
59 "unmodified (except for formatting, but note that IEEE754 is used\n"
60 "for number representation internally, with all that that implies).\n\n"
61 "For more advanced filters see the jq(1) manpage (\"man jq\")\n"
62 "and/or https://stedolan.github.io/jq\n\n"
63 "Example:\n\n\t$ echo '{\"foo\": 0}' | jq .\n"
64 "\t{\n\t\t\"foo\": 0\n\t}\n\n",
65 JQ_VERSION, progname, progname, progname);
68 "For a listing of options, use %s --help.\n",
72 "Some of the options include:\n"
73 " -c compact instead of pretty-printed output;\n"
74 " -n use `null` as the single input value;\n"
75 " -e set the exit status code based on the output;\n"
76 " -s read (slurp) all inputs into an array; apply filter to it;\n"
77 " -r output raw strings, not JSON texts;\n"
78 " -R read raw strings, not JSON texts;\n"
79 " -C colorize JSON;\n"
80 " -M monochrome (don't colorize JSON);\n"
81 " -S sort keys of objects on output;\n"
82 " --tab use tabs for indentation;\n"
83 " --arg a v set variable $a to value <v>;\n"
84 " --argjson a v set variable $a to JSON value <v>;\n"
85 " --slurpfile a f set variable $a to an array of JSON texts read from <f>;\n"
86 " --rawfile a f set variable $a to a string consisting of the contents of <f>;\n"
87 " --args remaining arguments are string arguments, not files;\n"
88 " --jsonargs remaining arguments are JSON arguments, not files;\n"
89 " -- terminates argument processing;\n\n"
90 "Named arguments are also available as $ARGS.named[], while\n"
91 "positional arguments are available as $ARGS.positional[].\n"
92 "\nSee the manpage for more options.\n");
94 exit((ret < 0 && code == 0) ? 2 : code);
98 fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);
99 fprintf(stderr, "or see the jq manpage, or online docs at https://stedolan.github.io/jq\n");
106 static int isoptish(const char* text) {
107 return text[0] == '-' && (text[1] == '-' || isalpha(text[1]));
110 static int isoption(const char* text, char shortopt, const char* longopt, size_t *short_opts) {
111 if (text[0] != '-' || text[1] == '-')
113 if (text[0] != '-') return 0;
116 if (text[1] == '-' && !strcmp(text+2, longopt)) return 1;
117 else if (text[1] == '-') return 0;
119 // must be short option; check it and...
120 if (!shortopt) return 0;
121 if (strchr(text, shortopt) != NULL) {
122 (*short_opts)++; // ...count it (for option stacking)
135 NO_COLOR_OUTPUT = 128,
139 UNBUFFERED_OUTPUT = 2048,
141 EXIT_STATUS_EXACT = 8192,
147 static int options = 0;
151 JQ_OK_NULL_KIND = -1, /* exit 0 if --exit-status is not set*/
153 JQ_ERROR_COMPILE = 3,
154 JQ_OK_NO_OUTPUT = -4, /* exit 0 if --exit-status is not set*/
155 JQ_ERROR_UNKNOWN = 5,
157 #define jq_exit_with_status(r) exit(abs(r))
158 #define jq_exit(r) exit( r > 0 ? r : 0 )
160 static const char *skip_shebang(const char *p) {
161 if (strncmp(p, "#!", sizeof("#!") - 1) != 0)
163 const char *n = strchr(p, '\n');
164 if (n == NULL || n[1] != '#')
166 n = strchr(n + 1, '\n');
167 if (n == NULL || n[1] == '#' || n[1] == '\0' || n[-1] != '\\' || n[-2] == '\\')
169 n = strchr(n + 1, '\n');
175 static int process(jq_state *jq, jv value, int flags, int dumpopts) {
176 int ret = JQ_OK_NO_OUTPUT; // No valid results && -e -> exit(4)
177 jq_start(jq, value, flags);
179 while (jv_is_valid(result = jq_next(jq))) {
180 if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
181 if (options & ASCII_OUTPUT) {
182 jv_dumpf(jv_copy(result), stdout, JV_PRINT_ASCII);
184 fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
189 if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL)
190 ret = JQ_OK_NULL_KIND;
194 priv_fwrite("\036", 1, stdout, dumpopts & JV_PRINT_ISATTY);
195 jv_dump(result, dumpopts);
197 if (!(options & RAW_NO_LF))
198 priv_fwrite("\n", 1, stdout, dumpopts & JV_PRINT_ISATTY);
199 if (options & UNBUFFERED_OUTPUT)
203 // jq program invoked `halt` or `halt_error`
204 options |= EXIT_STATUS_EXACT;
205 jv exit_code = jq_get_exit_code(jq);
206 if (!jv_is_valid(exit_code))
208 else if (jv_get_kind(exit_code) == JV_KIND_NUMBER)
209 ret = jv_number_value(exit_code);
211 ret = JQ_ERROR_UNKNOWN;
213 jv error_message = jq_get_error_message(jq);
214 if (jv_get_kind(error_message) == JV_KIND_STRING) {
215 fprintf(stderr, "%s", jv_string_value(error_message));
216 } else if (jv_get_kind(error_message) == JV_KIND_NULL) {
217 // Halt with no output
218 } else if (jv_is_valid(error_message)) {
219 error_message = jv_dump_string(jv_copy(error_message), 0);
220 fprintf(stderr, "%s\n", jv_string_value(error_message));
221 } // else no message on stderr; use --debug-trace to see a message
223 jv_free(error_message);
224 } else if (jv_invalid_has_msg(jv_copy(result))) {
225 // Uncaught jq exception
226 jv msg = jv_invalid_get_msg(jv_copy(result));
227 jv input_pos = jq_util_input_get_position(jq);
228 if (jv_get_kind(msg) == JV_KIND_STRING) {
229 fprintf(stderr, "jq: error (at %s): %s\n",
230 jv_string_value(input_pos), jv_string_value(msg));
232 msg = jv_dump_string(msg, 0);
233 fprintf(stderr, "jq: error (at %s) (not a string): %s\n",
234 jv_string_value(input_pos), jv_string_value(msg));
236 ret = JQ_ERROR_UNKNOWN;
244 static void debug_cb(void *data, jv input) {
245 int dumpopts = *(int *)data;
246 jv_dumpf(JV_ARRAY(jv_string("DEBUG:"), input), stderr, dumpopts & ~(JV_PRINT_PRETTY));
247 fprintf(stderr, "\n");
251 int umain(int argc, char* argv[]);
253 int wmain(int argc, wchar_t* wargv[]) {
255 char **argv = alloca(argc * sizeof(wchar_t*));
256 for (int i = 0; i < argc; i++) {
257 argv[i] = alloca((arg_sz = WideCharToMultiByte(CP_UTF8,
261 WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], arg_sz, 0, 0);
263 return umain(argc, argv);
266 int umain(int argc, char* argv[]) {
268 int main(int argc, char* argv[]) {
271 int ret = JQ_OK_NO_OUTPUT;
273 int parser_flags = 0;
275 int last_result = -1; /* -1 = no result, 0=null or false, 1=true */
277 jv ARGS = jv_array(); /* positional arguments */
278 jv program_arguments = jv_object(); /* named arguments */
283 _setmode(fileno(stdout), _O_TEXT | _O_U8TEXT);
284 _setmode(fileno(stderr), _O_TEXT | _O_U8TEXT);
287 if (argc) progname = argv[0];
292 ret = JQ_ERROR_SYSTEM;
296 int dumpopts = JV_PRINT_INDENT_FLAGS(2);
297 const char* program = 0;
299 jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb
301 int further_args_are_strings = 0;
302 int further_args_are_json = 0;
305 size_t short_opts = 0;
306 jv lib_search_paths = jv_null();
307 for (int i=1; i<argc; i++, short_opts = 0) {
309 if (further_args_are_strings) {
310 ARGS = jv_array_append(ARGS, jv_string(argv[i]));
311 } else if (further_args_are_json) {
312 ARGS = jv_array_append(ARGS, jv_parse(argv[i]));
314 jq_util_input_add_input(input_state, argv[i]);
317 } else if (!strcmp(argv[i], "--")) {
318 if (!program) usage(2, 1);
320 } else if (!isoptish(argv[i])) {
322 if (further_args_are_strings) {
323 ARGS = jv_array_append(ARGS, jv_string(argv[i]));
324 } else if (further_args_are_json) {
325 ARGS = jv_array_append(ARGS, jv_parse(argv[i]));
327 jq_util_input_add_input(input_state, argv[i]);
334 if (argv[i][1] == 'L') {
335 if (jv_get_kind(lib_search_paths) == JV_KIND_NULL)
336 lib_search_paths = jv_array();
337 if (argv[i][2] != 0) { // -Lname (faster check than strlen)
338 lib_search_paths = jv_array_append(lib_search_paths, jq_realpath(jv_string(argv[i]+2)));
339 } else if (i >= argc - 1) {
340 fprintf(stderr, "-L takes a parameter: (e.g. -L /search/path or -L/search/path)\n");
343 lib_search_paths = jv_array_append(lib_search_paths, jq_realpath(jv_string(argv[i+1])));
349 if (isoption(argv[i], 's', "slurp", &short_opts)) {
351 if (!short_opts) continue;
353 if (isoption(argv[i], 'r', "raw-output", &short_opts)) {
354 options |= RAW_OUTPUT;
355 if (!short_opts) continue;
357 if (isoption(argv[i], 'c', "compact-output", &short_opts)) {
358 dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));
359 if (!short_opts) continue;
361 if (isoption(argv[i], 'C', "color-output", &short_opts)) {
362 options |= COLOR_OUTPUT;
363 if (!short_opts) continue;
365 if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) {
366 options |= NO_COLOR_OUTPUT;
367 if (!short_opts) continue;
369 if (isoption(argv[i], 'a', "ascii-output", &short_opts)) {
370 options |= ASCII_OUTPUT;
371 if (!short_opts) continue;
373 if (isoption(argv[i], 0, "unbuffered", &short_opts)) {
374 options |= UNBUFFERED_OUTPUT;
377 if (isoption(argv[i], 'S', "sort-keys", &short_opts)) {
378 options |= SORTED_OUTPUT;
379 if (!short_opts) continue;
381 if (isoption(argv[i], 'R', "raw-input", &short_opts)) {
382 options |= RAW_INPUT;
383 if (!short_opts) continue;
385 if (isoption(argv[i], 'n', "null-input", &short_opts)) {
386 options |= PROVIDE_NULL;
387 if (!short_opts) continue;
389 if (isoption(argv[i], 'f', "from-file", &short_opts)) {
390 options |= FROM_FILE;
391 if (!short_opts) continue;
393 if (isoption(argv[i], 'j', "join-output", &short_opts)) {
394 options |= RAW_OUTPUT | RAW_NO_LF;
395 if (!short_opts) continue;
397 if (isoption(argv[i], 'b', "binary", &short_opts)) {
401 _setmode(fileno(stdin), _O_BINARY);
402 _setmode(fileno(stdout), _O_BINARY);
403 _setmode(fileno(stderr), _O_BINARY);
404 if (!short_opts) continue;
407 if (isoption(argv[i], 0, "tab", &short_opts)) {
408 dumpopts &= ~JV_PRINT_INDENT_FLAGS(7);
409 dumpopts |= JV_PRINT_TAB | JV_PRINT_PRETTY;
412 if (isoption(argv[i], 0, "indent", &short_opts)) {
414 fprintf(stderr, "%s: --indent takes one parameter\n", progname);
417 dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));
418 int indent = atoi(argv[i+1]);
419 if (indent < -1 || indent > 7) {
420 fprintf(stderr, "%s: --indent takes a number between -1 and 7\n", progname);
423 dumpopts |= JV_PRINT_INDENT_FLAGS(indent);
427 if (isoption(argv[i], 0, "seq", &short_opts)) {
431 if (isoption(argv[i], 0, "stream", &short_opts)) {
432 parser_flags |= JV_PARSE_STREAMING;
435 if (isoption(argv[i], 0, "stream-errors", &short_opts)) {
436 parser_flags |= JV_PARSE_STREAM_ERRORS;
439 if (isoption(argv[i], 'e', "exit-status", &short_opts)) {
440 options |= EXIT_STATUS;
441 if (!short_opts) continue;
443 // FIXME: For --arg* we should check that the varname is acceptable
444 if (isoption(argv[i], 0, "args", &short_opts)) {
445 further_args_are_strings = 1;
446 further_args_are_json = 0;
449 if (isoption(argv[i], 0, "jsonargs", &short_opts)) {
450 further_args_are_strings = 0;
451 further_args_are_json = 1;
454 if (isoption(argv[i], 0, "arg", &short_opts)) {
456 fprintf(stderr, "%s: --arg takes two parameters (e.g. --arg varname value)\n", progname);
459 if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1])))
460 program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), jv_string(argv[i+2]));
461 i += 2; // skip the next two arguments
464 if (isoption(argv[i], 0, "argjson", &short_opts)) {
466 fprintf(stderr, "%s: --argjson takes two parameters (e.g. --argjson varname text)\n", progname);
469 if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) {
470 jv v = jv_parse(argv[i+2]);
471 if (!jv_is_valid(v)) {
472 fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname);
475 program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), v);
477 i += 2; // skip the next two arguments
480 if (isoption(argv[i], 0, "argfile", &short_opts) ||
481 isoption(argv[i], 0, "rawfile", &short_opts) ||
482 isoption(argv[i], 0, "slurpfile", &short_opts)) {
483 int raw = isoption(argv[i], 0, "rawfile", &short_opts);
485 if (isoption(argv[i], 0, "argfile", &short_opts))
492 fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which);
495 if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) {
496 jv data = jv_load_file(argv[i+2], raw);
497 if (!jv_is_valid(data)) {
498 data = jv_invalid_get_msg(data);
499 fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which,
500 argv[i+1], argv[i+2], jv_string_value(data));
502 ret = JQ_ERROR_SYSTEM;
505 if (strcmp(which, "argfile") == 0 &&
506 jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
507 data = jv_array_get(data, 0);
508 program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), data);
510 i += 2; // skip the next two arguments
513 if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) {
514 options |= DUMP_DISASM;
517 if (isoption(argv[i], 0, "debug-trace=all", &short_opts)) {
518 jq_flags |= JQ_DEBUG_TRACE_ALL;
519 if (!short_opts) continue;
521 if (isoption(argv[i], 0, "debug-trace", &short_opts)) {
522 jq_flags |= JQ_DEBUG_TRACE;
525 if (isoption(argv[i], 'h', "help", &short_opts)) {
527 if (!short_opts) continue;
529 if (isoption(argv[i], 'V', "version", &short_opts)) {
530 printf("jq-%s\n", JQ_VERSION);
534 if (isoption(argv[i], 0, "run-tests", &short_opts)) {
536 // XXX Pass program_arguments, even a whole jq_state *, through;
537 // could be useful for testing
538 ret = jq_testsuite(lib_search_paths,
539 (options & DUMP_DISASM) || (jq_flags & JQ_DEBUG_TRACE),
544 // check for unknown options... if this argument was a short option
545 if (strlen(argv[i]) != short_opts + 1) {
546 fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
553 if (isatty(STDOUT_FILENO)) {
555 dumpopts |= JV_PRINT_ISATTY | JV_PRINT_COLOR;
557 /* Verify we actually have the console, as the NUL device is also regarded as
558 tty. Windows can handle color if ANSICON (or ConEmu) is installed, or
559 Windows 10 supports the virtual terminal */
561 HANDLE con = GetStdHandle(STD_OUTPUT_HANDLE);
562 if (GetConsoleMode(con, &mode)) {
563 dumpopts |= JV_PRINT_ISATTY;
564 if (getenv("ANSICON") != NULL ||
565 SetConsoleMode(con, mode | 4/*ENABLE_VIRTUAL_TERMINAL_PROCESSING*/))
566 dumpopts |= JV_PRINT_COLOR;
571 if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED;
572 if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;
573 if (options & COLOR_OUTPUT) dumpopts |= JV_PRINT_COLOR;
574 if (options & NO_COLOR_OUTPUT) dumpopts &= ~JV_PRINT_COLOR;
576 if (getenv("JQ_COLORS") != NULL && !jq_set_colors(getenv("JQ_COLORS")))
577 fprintf(stderr, "Failed to set $JQ_COLORS\n");
579 if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) {
580 // Default search path list
581 lib_search_paths = JV_ARRAY(jv_string("~/.jq"),
582 jv_string("$ORIGIN/../lib/jq"),
583 jv_string("$ORIGIN/lib"));
585 jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_search_paths);
587 char *origin = strdup(argv[0]);
588 if (origin == NULL) {
589 fprintf(stderr, "Error: out of memory\n");
592 jq_set_attr(jq, jv_string("JQ_ORIGIN"), jv_string(dirname(origin)));
595 if (strchr(JQ_VERSION, '-') == NULL)
596 jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string(JQ_VERSION));
598 jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string_fmt("%.*s-master", (int)(strchr(JQ_VERSION, '-') - JQ_VERSION), JQ_VERSION));
601 if (!program && (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)))
605 if (!program) usage(2, 1);
607 if (options & FROM_FILE) {
608 char *program_origin = strdup(program);
609 if (program_origin == NULL) {
614 jv data = jv_load_file(program, 1);
615 if (!jv_is_valid(data)) {
616 data = jv_invalid_get_msg(data);
617 fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
619 ret = JQ_ERROR_SYSTEM;
622 jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin))));
623 ARGS = JV_OBJECT(jv_string("positional"), ARGS,
624 jv_string("named"), jv_copy(program_arguments));
625 program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS));
626 compiled = jq_compile_args(jq, skip_shebang(jv_string_value(data)), jv_copy(program_arguments));
627 free(program_origin);
630 jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good?
631 ARGS = JV_OBJECT(jv_string("positional"), ARGS,
632 jv_string("named"), jv_copy(program_arguments));
633 program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS));
634 compiled = jq_compile_args(jq, program, jv_copy(program_arguments));
637 ret = JQ_ERROR_COMPILE;
641 if (options & DUMP_DISASM) {
642 jq_dump_disassembly(jq, 0);
647 parser_flags |= JV_PARSE_SEQ;
649 if ((options & RAW_INPUT))
650 jq_util_input_set_parser(input_state, NULL, (options & SLURP) ? 1 : 0);
652 jq_util_input_set_parser(input_state, jv_parser_new(parser_flags), (options & SLURP) ? 1 : 0);
654 // Let jq program read from inputs
655 jq_set_input_cb(jq, jq_util_input_next_input_cb, input_state);
657 // Let jq program call `debug` builtin and have that go somewhere
658 jq_set_debug_cb(jq, debug_cb, &dumpopts);
661 jq_util_input_add_input(input_state, "-");
663 if (options & PROVIDE_NULL) {
664 ret = process(jq, jv_null(), jq_flags, dumpopts);
667 while (jq_util_input_errors(input_state) == 0 &&
668 (jv_is_valid((value = jq_util_input_next_input(input_state))) || jv_invalid_has_msg(jv_copy(value)))) {
669 if (jv_is_valid(value)) {
670 ret = process(jq, value, jq_flags, dumpopts);
671 if (ret <= 0 && ret != JQ_OK_NO_OUTPUT)
672 last_result = (ret != JQ_OK_NULL_KIND);
677 jv msg = jv_invalid_get_msg(value);
678 if (!(options & SEQ)) {
679 // --seq -> errors are not fatal
680 ret = JQ_OK_NO_OUTPUT;
681 fprintf(stderr, "parse error: %s\n", jv_string_value(msg));
685 fprintf(stderr, "ignoring parse error: %s\n", jv_string_value(msg));
690 if (jq_util_input_errors(input_state) != 0)
691 ret = JQ_ERROR_SYSTEM;
694 badwrite = ferror(stdout);
695 if (fclose(stdout)!=0 || badwrite) {
696 fprintf(stderr,"Error: writing output failed: %s\n", strerror(errno));
697 ret = JQ_ERROR_SYSTEM;
701 jv_free(program_arguments);
702 jq_util_input_free(&input_state);
705 if (options & (EXIT_STATUS|EXIT_STATUS_EXACT)) {
706 if (ret != JQ_OK_NO_OUTPUT)
707 jq_exit_with_status(ret);
709 switch (last_result) {
710 case -1: jq_exit_with_status(JQ_OK_NO_OUTPUT);
711 case 0: jq_exit_with_status(JQ_OK_NULL_KIND);
712 default: jq_exit_with_status(JQ_OK);