mirror of
				https://github.com/stedolan/jq.git
				synced 2024-05-11 05:55:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			567 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			567 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <assert.h>
 | |
| #include <ctype.h>
 | |
| #include <errno.h>
 | |
| #include <libgen.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #ifdef WIN32
 | |
| #include <windows.h>
 | |
| #include <io.h>
 | |
| #include <fcntl.h>
 | |
| #include <processenv.h>
 | |
| #include <shellapi.h>
 | |
| #include <wchar.h>
 | |
| #include <wtypes.h>
 | |
| #endif
 | |
| 
 | |
| #include "compile.h"
 | |
| #include "jv.h"
 | |
| #include "jq.h"
 | |
| #include "jv_alloc.h"
 | |
| #include "util.h"
 | |
| #include "version.h"
 | |
| 
 | |
| int jq_testsuite(jv lib_dirs, int verbose, int argc, char* argv[]);
 | |
| 
 | |
| static const char* progname;
 | |
| 
 | |
| /*
 | |
|  * For a longer help message we could use a better option parsing
 | |
|  * strategy, one that lets stack options.
 | |
|  */
 | |
| static void usage(int code) {
 | |
|   FILE *f = stderr;
 | |
| 
 | |
|   if (code == 0)
 | |
|     f = stdout;
 | |
| 
 | |
|   int ret = fprintf(f,
 | |
|     "jq - commandline JSON processor [version %s]\n"
 | |
|     "Usage: %s [options] <jq filter> [file...]\n\n"
 | |
|     "\tjq is a tool for processing JSON inputs, applying the\n"
 | |
|     "\tgiven filter to its JSON text inputs and producing the\n"
 | |
|     "\tfilter's results as JSON on standard output.\n"
 | |
|     "\tThe simplest filter is ., which is the identity filter,\n"
 | |
|     "\tcopying jq's input to its output unmodified (except for\n"
 | |
|     "\tformatting).\n"
 | |
|     "\tFor more advanced filters see the jq(1) manpage (\"man jq\")\n"
 | |
|     "\tand/or http://stedolan.github.io/jq\n\n"
 | |
|     "\tSome of the options include:\n"
 | |
|     "\t -c\t\tcompact instead of pretty-printed output;\n"
 | |
|     "\t -n\t\tuse `null` as the single input value;\n"
 | |
|     "\t -e\t\tset the exit status code based on the output;\n"
 | |
|     "\t -s\t\tread (slurp) all inputs into an array; apply filter to it;\n"
 | |
|     "\t -r\t\toutput raw strings, not JSON texts;\n"
 | |
|     "\t -R\t\tread raw strings, not JSON texts;\n"
 | |
|     "\t -C\t\tcolorize JSON;\n"
 | |
|     "\t -M\t\tmonochrome (don't colorize JSON);\n"
 | |
|     "\t -S\t\tsort keys of objects on output;\n"
 | |
|     "\t --tab\tuse tabs for indentation;\n"
 | |
|     "\t --arg a v\tset variable $a to value <v>;\n"
 | |
|     "\t --argjson a v\tset variable $a to JSON value <v>;\n"
 | |
|     "\t --slurpfile a f\tset variable $a to an array of JSON texts read from <f>;\n"
 | |
|     "\tSee the manpage for more options.\n", JQ_VERSION, progname);
 | |
|   exit((ret < 0 && code == 0) ? 2 : code);
 | |
| }
 | |
| 
 | |
| static void die() {
 | |
|   fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);
 | |
|   fprintf(stderr, "or see the jq manpage, or online docs  at http://stedolan.github.io/jq\n");
 | |
|   exit(2);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| static int isoptish(const char* text) {
 | |
|   return text[0] == '-' && (text[1] == '-' || isalpha(text[1]));
 | |
| }
 | |
| 
 | |
| static int isoption(const char* text, char shortopt, const char* longopt, size_t *short_opts) {
 | |
|   if (text[0] != '-' || text[1] == '-')
 | |
|     *short_opts = 0;
 | |
|   if (text[0] != '-') return 0;
 | |
| 
 | |
|   // check long option
 | |
|   if (text[1] == '-' && !strcmp(text+2, longopt)) return 1;
 | |
|   else if (text[1] == '-') return 0;
 | |
| 
 | |
|   // must be short option; check it and...
 | |
|   if (!shortopt) return 0;
 | |
|   if (strchr(text, shortopt) != NULL) {
 | |
|     (*short_opts)++; // ...count it (for option stacking)
 | |
|     return 1;
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| enum {
 | |
|   SLURP                 = 1,
 | |
|   RAW_INPUT             = 2,
 | |
|   PROVIDE_NULL          = 4,
 | |
|   RAW_OUTPUT            = 8,
 | |
|   ASCII_OUTPUT          = 32,
 | |
|   COLOUR_OUTPUT         = 64,
 | |
|   NO_COLOUR_OUTPUT      = 128,
 | |
|   SORTED_OUTPUT         = 256,
 | |
|   FROM_FILE             = 512,
 | |
|   RAW_NO_LF             = 1024,
 | |
|   UNBUFFERED_OUTPUT     = 2048,
 | |
|   EXIT_STATUS           = 4096,
 | |
|   SEQ                   = 8192,
 | |
|   RUN_TESTS             = 16384,
 | |
|   /* debugging only */
 | |
|   DUMP_DISASM           = 32768,
 | |
| };
 | |
| static int options = 0;
 | |
| 
 | |
| static int process(jq_state *jq, jv value, int flags, int dumpopts) {
 | |
|   int ret = 14; // No valid results && -e -> exit(4)
 | |
|   jq_start(jq, value, flags);
 | |
|   jv result;
 | |
|   while (jv_is_valid(result = jq_next(jq))) {
 | |
|     if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
 | |
|       fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
 | |
|       ret = 0;
 | |
|       jv_free(result);
 | |
|     } else {
 | |
|       if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL)
 | |
|         ret = 11;
 | |
|       else
 | |
|         ret = 0;
 | |
|       if (options & SEQ)
 | |
|         priv_fwrite("\036", 1, stdout, dumpopts & JV_PRINT_ISATTY);
 | |
|       jv_dump(result, dumpopts);
 | |
|     }
 | |
|     if (!(options & RAW_NO_LF))
 | |
|       priv_fwrite("\n", 1, stdout, dumpopts & JV_PRINT_ISATTY);
 | |
|     if (options & UNBUFFERED_OUTPUT)
 | |
|       fflush(stdout);
 | |
|   }
 | |
|   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);
 | |
|     if (jv_get_kind(msg) == JV_KIND_STRING) {
 | |
|       fprintf(stderr, "jq: error (at %s): %s\n",
 | |
|               jv_string_value(input_pos), jv_string_value(msg));
 | |
|     } else {
 | |
|       msg = jv_dump_string(msg, 0);
 | |
|       fprintf(stderr, "jq: error (at %s) (not a string): %s\n",
 | |
|               jv_string_value(input_pos), jv_string_value(msg));
 | |
|     }
 | |
|     ret = 5;
 | |
|     jv_free(input_pos);
 | |
|     jv_free(msg);
 | |
|   }
 | |
|   jv_free(result);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static void debug_cb(void *data, jv input) {
 | |
|   int dumpopts = *(int *)data;
 | |
|   jv_dumpf(JV_ARRAY(jv_string("DEBUG:"), input), stderr, dumpopts & ~(JV_PRINT_PRETTY));
 | |
|   fprintf(stderr, "\n");
 | |
| }
 | |
| 
 | |
| int main(int argc, char* argv[]) {
 | |
|   jq_state *jq = NULL;
 | |
|   int ret = 0;
 | |
|   int compiled = 0;
 | |
|   int parser_flags = 0;
 | |
|   int nfiles = 0;
 | |
|   int badwrite;
 | |
|   jv program_arguments = jv_array();
 | |
| 
 | |
| #ifdef WIN32
 | |
|   SetConsoleOutputCP(CP_UTF8);
 | |
|   fflush(stdout);
 | |
|   fflush(stderr);
 | |
|   _setmode(fileno(stdout), _O_TEXT | _O_U8TEXT);
 | |
|   _setmode(fileno(stderr), _O_TEXT | _O_U8TEXT);
 | |
|   int wargc;
 | |
|   wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);
 | |
|   assert(wargc == argc);
 | |
|   size_t arg_sz;
 | |
|   for (int i = 0; i < argc; i++) {
 | |
|     argv[i] = alloca((arg_sz = WideCharToMultiByte(CP_UTF8,
 | |
|                                                    0,
 | |
|                                                    wargv[i],
 | |
|                                                    -1, 0, 0, 0, 0)));
 | |
|     WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], arg_sz, 0, 0);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (argc) progname = argv[0];
 | |
| 
 | |
|   jq = jq_init();
 | |
|   if (jq == NULL) {
 | |
|     perror("malloc");
 | |
|     ret = 2;
 | |
|     goto out;
 | |
|   }
 | |
| 
 | |
|   int dumpopts = JV_PRINT_INDENT_FLAGS(2);
 | |
|   const char* program = 0;
 | |
| 
 | |
|   jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb
 | |
| 
 | |
|   int further_args_are_files = 0;
 | |
|   int jq_flags = 0;
 | |
|   size_t short_opts = 0;
 | |
|   jv lib_search_paths = jv_null();
 | |
|   for (int i=1; i<argc; i++, short_opts = 0) {
 | |
|     if (further_args_are_files) {
 | |
|       jq_util_input_add_input(input_state, argv[i]);
 | |
|       nfiles++;
 | |
|     } else if (!strcmp(argv[i], "--")) {
 | |
|       if (!program) usage(2);
 | |
|       further_args_are_files = 1;
 | |
|     } else if (!isoptish(argv[i])) {
 | |
|       if (program) {
 | |
|         jq_util_input_add_input(input_state, argv[i]);
 | |
|         nfiles++;
 | |
|       } else {
 | |
|         program = argv[i];
 | |
|       }
 | |
|     } else {
 | |
|       if (argv[i][1] == 'L') {
 | |
|         if (jv_get_kind(lib_search_paths) == JV_KIND_NULL)
 | |
|           lib_search_paths = jv_array();
 | |
|         if (argv[i][2] != 0) { // -Lname (faster check than strlen)
 | |
|             lib_search_paths = jv_array_append(lib_search_paths, jv_string(argv[i]+2));
 | |
|         } else if (i >= argc - 1) {
 | |
|           fprintf(stderr, "-L takes a parameter: (e.g. -L /search/path or -L/search/path)\n");
 | |
|           die();
 | |
|         } else {
 | |
|           lib_search_paths = jv_array_append(lib_search_paths, jv_string(argv[i+1]));
 | |
|           i++;
 | |
|         }
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       if (isoption(argv[i], 's', "slurp", &short_opts)) {
 | |
|         options |= SLURP;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'r', "raw-output", &short_opts)) {
 | |
|         options |= RAW_OUTPUT;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'c', "compact-output", &short_opts)) {
 | |
|         dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'C', "color-output", &short_opts)) {
 | |
|         options |= COLOUR_OUTPUT;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) {
 | |
|         options |= NO_COLOUR_OUTPUT;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'a', "ascii-output", &short_opts)) {
 | |
|         options |= ASCII_OUTPUT;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "unbuffered", &short_opts)) {
 | |
|         options |= UNBUFFERED_OUTPUT;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'S', "sort-keys", &short_opts)) {
 | |
|         options |= SORTED_OUTPUT;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'R', "raw-input", &short_opts)) {
 | |
|         options |= RAW_INPUT;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'n', "null-input", &short_opts)) {
 | |
|         options |= PROVIDE_NULL;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'f', "from-file", &short_opts)) {
 | |
|         options |= FROM_FILE;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'j', "join-output", &short_opts)) {
 | |
|         options |= RAW_OUTPUT | RAW_NO_LF;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "tab", &short_opts)) {
 | |
|         dumpopts &= ~JV_PRINT_INDENT_FLAGS(7);
 | |
|         dumpopts |= JV_PRINT_TAB | JV_PRINT_PRETTY;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "indent", &short_opts)) {
 | |
|         if (i >= argc - 1) {
 | |
|           fprintf(stderr, "%s: --indent takes one parameter\n", progname);
 | |
|           die();
 | |
|         }
 | |
|         dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));
 | |
|         int indent = atoi(argv[i+1]);
 | |
|         if (indent < -1 || indent > 7) {
 | |
|           fprintf(stderr, "%s: --indent takes a number between -1 and 7\n", progname);
 | |
|           die();
 | |
|         }
 | |
|         dumpopts |= JV_PRINT_INDENT_FLAGS(indent);
 | |
|         i++;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "seq", &short_opts)) {
 | |
|         options |= SEQ;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "stream", &short_opts)) {
 | |
|         parser_flags |= JV_PARSE_STREAMING;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "stream-errors", &short_opts)) {
 | |
|         parser_flags |= JV_PARSE_STREAM_ERRORS;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'e', "exit-status", &short_opts)) {
 | |
|         options |= EXIT_STATUS;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       // FIXME: For --arg* we should check that the varname is acceptable
 | |
|       if (isoption(argv[i], 0, "arg", &short_opts)) {
 | |
|         if (i >= argc - 2) {
 | |
|           fprintf(stderr, "%s: --arg takes two parameters (e.g. --arg varname value)\n", progname);
 | |
|           die();
 | |
|         }
 | |
|         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"), jv_string(argv[i+2]));
 | |
|         program_arguments = jv_array_append(program_arguments, arg);
 | |
|         i += 2; // skip the next two arguments
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "argjson", &short_opts)) {
 | |
|         if (i >= argc - 2) {
 | |
|           fprintf(stderr, "%s: --argjson takes two parameters (e.g. --argjson varname text)\n", progname);
 | |
|           die();
 | |
|         }
 | |
|         jv v = jv_parse(argv[i+2]);
 | |
|         if (!jv_is_valid(v)) {
 | |
|           fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname);
 | |
|           die();
 | |
|         }
 | |
|         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
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "argfile", &short_opts) ||
 | |
|           isoption(argv[i], 0, "slurpfile", &short_opts)) {
 | |
|         const char *which;
 | |
|         if (isoption(argv[i], 0, "argfile", &short_opts))
 | |
|           which = "argfile";
 | |
|         else
 | |
|           which = "slurpfile";
 | |
|         if (i >= argc - 2) {
 | |
|           fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which);
 | |
|           die();
 | |
|         }
 | |
|         jv arg = jv_object();
 | |
|         arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
 | |
|         jv data = jv_load_file(argv[i+2], 0);
 | |
|         if (!jv_is_valid(data)) {
 | |
|           data = jv_invalid_get_msg(data);
 | |
|           fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which,
 | |
|                   argv[i+1], argv[i+2], jv_string_value(data));
 | |
|           jv_free(data);
 | |
|           jv_free(arg);
 | |
|           ret = 2;
 | |
|           goto out;
 | |
|         }
 | |
|         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
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i],  0,  "debug-dump-disasm", &short_opts)) {
 | |
|         options |= DUMP_DISASM;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i],  0,  "debug-trace", &short_opts)) {
 | |
|         jq_flags |= JQ_DEBUG_TRACE;
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'h', "help", &short_opts)) {
 | |
|         usage(0);
 | |
|         if (!short_opts) continue;
 | |
|       }
 | |
|       if (isoption(argv[i], 'V', "version", &short_opts)) {
 | |
|         printf("jq-%s\n", JQ_VERSION);
 | |
|         ret = 0;
 | |
|         goto out;
 | |
|       }
 | |
|       if (isoption(argv[i], 0, "run-tests", &short_opts)) {
 | |
|         i++;
 | |
|         // XXX Pass program_arguments, even a whole jq_state *, through;
 | |
|         // could be useful for testing
 | |
|         ret = jq_testsuite(lib_search_paths,
 | |
|                            (options & DUMP_DISASM) || (jq_flags & JQ_DEBUG_TRACE),
 | |
|                            argc - i, argv + i);
 | |
|         goto out;
 | |
|       }
 | |
| 
 | |
|       // check for unknown options... if this argument was a short option
 | |
|       if (strlen(argv[i]) != short_opts + 1) {
 | |
|         fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
 | |
|         die();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (isatty(fileno(stdout))) {
 | |
|     dumpopts |= JV_PRINT_ISATTY;
 | |
| #ifndef WIN32
 | |
|   /* Disable colour by default on Windows builds as Windows
 | |
|      terminals tend not to display it correctly */
 | |
|     dumpopts |= JV_PRINT_COLOUR;
 | |
| #endif
 | |
|   }
 | |
|   if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED;
 | |
|   if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;
 | |
|   if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR;
 | |
|   if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR;
 | |
| 
 | |
|   if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) {
 | |
|     // Default search path list
 | |
|     lib_search_paths = JV_ARRAY(jv_string("~/.jq"),
 | |
|                                 jv_string("$ORIGIN/../lib/jq"),
 | |
|                                 jv_string("$ORIGIN/lib"));
 | |
|   }
 | |
|   jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_search_paths);
 | |
| 
 | |
|   char *origin = strdup(argv[0]);
 | |
|   if (origin == NULL) {
 | |
|     fprintf(stderr, "Error: out of memory\n");
 | |
|     exit(1);
 | |
|   }
 | |
|   jq_set_attr(jq, jv_string("JQ_ORIGIN"), jv_string(dirname(origin)));
 | |
|   free(origin);
 | |
| 
 | |
|   if (strchr(JQ_VERSION, '-') == NULL)
 | |
|     jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string(JQ_VERSION));
 | |
|   else
 | |
|     jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string_fmt("%.*s-master", (int)(strchr(JQ_VERSION, '-') - JQ_VERSION), JQ_VERSION));
 | |
| 
 | |
| #if (!defined(WIN32) && defined(HAVE_ISATTY)) || defined(HAVE__ISATTY)
 | |
| 
 | |
| #if defined(HAVE__ISATTY) && defined(isatty)
 | |
| #undef isatty
 | |
| #define isatty _isatty
 | |
| #endif
 | |
| 
 | |
|   if (!program && isatty(STDOUT_FILENO) && !isatty(STDIN_FILENO))
 | |
|     program = ".";
 | |
| #endif
 | |
| 
 | |
|   if (!program) usage(2);
 | |
| 
 | |
|   if (options & FROM_FILE) {
 | |
|     char *program_origin = strdup(program);
 | |
|     if (program_origin == NULL) {
 | |
|       perror("malloc");
 | |
|       exit(2);
 | |
|     }
 | |
| 
 | |
|     jv data = jv_load_file(program, 1);
 | |
|     if (!jv_is_valid(data)) {
 | |
|       data = jv_invalid_get_msg(data);
 | |
|       fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
 | |
|       jv_free(data);
 | |
|       ret = 2;
 | |
|       goto out;
 | |
|     }
 | |
|     jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin))));
 | |
|     compiled = jq_compile_args(jq, jv_string_value(data), jv_copy(program_arguments));
 | |
|     free(program_origin);
 | |
|     jv_free(data);
 | |
|   } else {
 | |
|     jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good?
 | |
|     compiled = jq_compile_args(jq, program, jv_copy(program_arguments));
 | |
|   }
 | |
|   if (!compiled){
 | |
|     ret = 3;
 | |
|     goto out;
 | |
|   }
 | |
| 
 | |
|   if (options & DUMP_DISASM) {
 | |
|     jq_dump_disassembly(jq, 0);
 | |
|     printf("\n");
 | |
|   }
 | |
| 
 | |
|   if ((options & SEQ))
 | |
|     parser_flags |= JV_PARSE_SEQ;
 | |
| 
 | |
|   if ((options & RAW_INPUT))
 | |
|     jq_util_input_set_parser(input_state, NULL, (options & SLURP) ? 1 : 0);
 | |
|   else
 | |
|     jq_util_input_set_parser(input_state, jv_parser_new(parser_flags), (options & SLURP) ? 1 : 0);
 | |
| 
 | |
|   // Let jq program read from inputs
 | |
|   jq_set_input_cb(jq, jq_util_input_next_input_cb, input_state);
 | |
| 
 | |
|   // Let jq program call `debug` builtin and have that go somewhere
 | |
|   jq_set_debug_cb(jq, debug_cb, &dumpopts);
 | |
| 
 | |
|   if (nfiles == 0)
 | |
|     jq_util_input_add_input(input_state, "-");
 | |
| 
 | |
|   if (options & PROVIDE_NULL) {
 | |
|     ret = process(jq, jv_null(), jq_flags, dumpopts);
 | |
|   } else {
 | |
|     jv value;
 | |
|     while (jq_util_input_errors(input_state) == 0 &&
 | |
|            (jv_is_valid((value = jq_util_input_next_input(input_state))) || jv_invalid_has_msg(jv_copy(value)))) {
 | |
|       if (jv_is_valid(value)) {
 | |
|         ret = process(jq, value, jq_flags, dumpopts);
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // Parse error
 | |
|       jv msg = jv_invalid_get_msg(value);
 | |
|       if (!(options & SEQ)) {
 | |
|         // --seq -> errors are not fatal
 | |
|         ret = 4;
 | |
|         fprintf(stderr, "parse error: %s\n", jv_string_value(msg));
 | |
|         jv_free(msg);
 | |
|         break;
 | |
|       }
 | |
|       fprintf(stderr, "ignoring parse error: %s\n", jv_string_value(msg));
 | |
|       jv_free(msg);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (jq_util_input_errors(input_state) != 0)
 | |
|     ret = 2;
 | |
| 
 | |
| out:
 | |
|   badwrite = ferror(stdout);
 | |
|   if (fclose(stdout)!=0 || badwrite) {
 | |
|     fprintf(stderr,"Error: writing output failed: %s\n", strerror(errno));
 | |
|     ret = 2;
 | |
|   }
 | |
| 
 | |
|   jv_free(program_arguments);
 | |
|   jq_util_input_free(&input_state);
 | |
|   jq_teardown(&jq);
 | |
|   if (ret >= 10 && (options & EXIT_STATUS))
 | |
|     return ret - 10;
 | |
|   if (ret >= 10)
 | |
|     return 0;
 | |
|   return ret;
 | |
| }
 |