static void process(jv value, int flags) { jq_state *jq = NULL; jq_init(bc, value, &jq, 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); jv_free(result); } else { int dumpopts; /* Disable colour by default on Windows builds as Windows terminals tend not to display it correctly */ #ifdef WIN32 dumpopts = 0; #else dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0; #endif if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; 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; jv_dump(result, dumpopts); } printf("\n"); } jv_free(result); jq_teardown(&jq); }
static void php_jq_exec(zval **return_value, char *str, int str_len, char *filter, int filter_len, long flags TSRMLS_DC) { jq_state *jq = php_jq_init(); jv json, result; if (!jq) { PHP_JQ_EXCEPTION(0, "jq object has not been correctly initialized " "by its constructor"); ZVAL_BOOL(*return_value, 0); return; } json = jv_parse_sized(str, str_len); if (!jv_is_valid(json)) { jv_free(json); jq_teardown(&jq); if (PHP_JQ_G(display_errors)) { PHP_JQ_ERR(E_WARNING, "load json parse error"); } ZVAL_BOOL(*return_value, 0); return; } filter[filter_len] = 0; if (!jq_compile(jq, filter)) { jv_free(json); jq_teardown(&jq); if (PHP_JQ_G(display_errors)) { PHP_JQ_ERR(E_WARNING, "filter compile error"); } ZVAL_BOOL(*return_value, 0); return; } php_jq_filter(return_value, jq, json, flags TSRMLS_CC); jv_free(json); jq_teardown(&jq); }
static void process(jv value) { jq_init(bc, value); jv result; while (jv_is_valid(result = jq_next())) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length(jv_copy(result)), stdout); } else { int dumpopts = 0; if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; jv_dump(result, dumpopts); } printf("\n"); } jv_free(result); jq_teardown(); }
static void run_jq_tests(jv lib_dirs, FILE *testdata) { char prog[4096]; char buf[4096]; struct err_data err_msg; int tests = 0, passed = 0, invalid = 0; unsigned int lineno = 0; int must_fail = 0; int check_msg = 0; jq_state *jq = NULL; jq = jq_init(); assert(jq); if (jv_get_kind(lib_dirs) == JV_KIND_NULL) lib_dirs = jv_array(); jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_dirs); while (1) { if (!fgets(prog, sizeof(prog), testdata)) break; lineno++; if (skipline(prog)) continue; if (checkfail(prog)) { must_fail = 1; check_msg = checkerrormsg(prog); jq_set_error_cb(jq, test_err_cb, &err_msg); continue; } if (prog[strlen(prog)-1] == '\n') prog[strlen(prog)-1] = 0; printf("Testing '%s' at line number %u\n", prog, lineno); int pass = 1; tests++; int compiled = jq_compile(jq, prog); if (must_fail) { jq_set_error_cb(jq, NULL, NULL); must_fail = 0; check_msg = 0; if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; } lineno++; if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0; if (compiled) { printf("*** Test program compiled that should not have at line %u: %s\n", lineno, prog); invalid++; continue; } if (check_msg && strcmp(buf, err_msg.buf) != 0) { printf("*** Erroneous test program failed with wrong message (%s) at line %u: %s\n", err_msg.buf, lineno, prog); invalid++; } else { passed++; } continue; } if (!compiled) { printf("*** Test program failed to compile at line %u: %s\n", lineno, prog); invalid++; // skip past test data while (fgets(buf, sizeof(buf), testdata)) { lineno++; if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; } continue; } printf("Disassembly:\n"); jq_dump_disassembly(jq, 2); printf("\n"); if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; } lineno++; jv input = jv_parse(buf); if (!jv_is_valid(input)){ invalid++; continue; } jq_start(jq, input, JQ_DEBUG_TRACE); while (fgets(buf, sizeof(buf), testdata)) { lineno++; if (skipline(buf)) break; jv expected = jv_parse(buf); if (!jv_is_valid(expected)){ invalid++; continue; } jv actual = jq_next(jq); if (!jv_is_valid(actual)) { jv_free(actual); printf("*** Insufficient results for test at line number %u: %s\n", lineno, prog); pass = 0; break; } else if (!jv_equal(jv_copy(expected), jv_copy(actual))) { printf("*** Expected "); jv_dump(jv_copy(expected), 0); printf(", but got "); jv_dump(jv_copy(actual), 0); printf(" for test at line number %u: %s\n", lineno, prog); pass = 0; } jv as_string = jv_dump_string(jv_copy(expected), rand() & ~(JV_PRINT_COLOUR|JV_PRINT_REFCOUNT)); jv reparsed = jv_parse_sized(jv_string_value(as_string), jv_string_length_bytes(jv_copy(as_string))); assert(jv_equal(jv_copy(expected), jv_copy(reparsed))); jv_free(as_string); jv_free(reparsed); jv_free(expected); jv_free(actual); } if (pass) { jv extra = jq_next(jq); if (jv_is_valid(extra)) { printf("*** Superfluous result: "); jv_dump(extra, 0); printf(" for test at line number %u, %s\n", lineno, prog); pass = 0; } else { jv_free(extra); } } passed+=pass; } jq_teardown(&jq); printf("%d of %d tests passed (%d malformed)\n", passed,tests,invalid); if (passed != tests) exit(1); }
static void jv_test() { /// JSON parser regression tests { jv v = jv_parse("{\"a':\"12\"}"); assert(jv_get_kind(v) == JV_KIND_INVALID); v = jv_invalid_get_msg(v); assert(strcmp(jv_string_value(v), "Expected separator between values at line 1, column 9 (while parsing '{\"a':\"12\"}')") == 0); jv_free(v); } /// Arrays and numbers { jv a = jv_array(); assert(jv_get_kind(a) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(a)) == 0); assert(jv_array_length(jv_copy(a)) == 0); a = jv_array_append(a, jv_number(42)); assert(jv_array_length(jv_copy(a)) == 1); assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42); jv a2 = jv_array_append(jv_array(), jv_number(42)); assert(jv_equal(jv_copy(a), jv_copy(a))); assert(jv_equal(jv_copy(a2), jv_copy(a2))); assert(jv_equal(jv_copy(a), jv_copy(a2))); assert(jv_equal(jv_copy(a2), jv_copy(a))); jv_free(a2); a2 = jv_array_append(jv_array(), jv_number(19)); assert(!jv_equal(jv_copy(a), jv_copy(a2))); assert(!jv_equal(jv_copy(a2), jv_copy(a))); jv_free(a2); assert(jv_get_refcnt(a) == 1); a = jv_array_append(a, jv_copy(a)); assert(jv_get_refcnt(a) == 1); assert(jv_array_length(jv_copy(a)) == 2); assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42); for (int i=0; i<10; i++) { jv subarray = jv_array_get(jv_copy(a), 1); assert(jv_get_kind(subarray) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(subarray)) == 1); assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42); jv_free(subarray); } jv subarray = jv_array_get(jv_copy(a), 1); assert(jv_get_kind(subarray) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(subarray)) == 1); assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42); jv sub2 = jv_copy(subarray); sub2 = jv_array_append(sub2, jv_number(19)); assert(jv_get_kind(sub2) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(sub2)) == 2); assert(jv_number_value(jv_array_get(jv_copy(sub2), 0)) == 42); assert(jv_number_value(jv_array_get(jv_copy(sub2), 1)) == 19); assert(jv_get_kind(subarray) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(subarray)) == 1); assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42); jv_free(subarray); void* before = sub2.u.ptr; sub2 = jv_array_append(sub2, jv_number(200)); void* after = sub2.u.ptr; assert(before == after); jv_free(sub2); jv a3 = jv_array_append(jv_copy(a), jv_number(19)); assert(jv_array_length(jv_copy(a3)) == 3); assert(jv_number_value(jv_array_get(jv_copy(a3), 0)) == 42); assert(jv_array_length(jv_array_get(jv_copy(a3), 1)) == 1); assert(jv_number_value(jv_array_get(jv_copy(a3), 2)) == 19); jv_free(a3); jv a4 = jv_array(); a4 = jv_array_append(a4, jv_number(1)); a4 = jv_array_append(a4, jv_number(2)); jv a5 = jv_copy(a4); a4 = jv_array_append(a4, jv_number(3)); a4 = jv_array_slice(a4, 0, 1); assert(jv_array_length(jv_copy(a4)) == 1); a4 = jv_array_append(a4, jv_number(4)); assert(jv_array_length(jv_copy(a4)) == 2); assert(jv_array_length(jv_copy(a5)) == 2); jv_free(a4); jv_free(a5); assert(jv_array_length(jv_copy(a)) == 2); assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42); assert(jv_array_length(jv_array_get(jv_copy(a), 1)) == 1); jv_dump(jv_copy(a), 0); printf("\n"); jv_free(a); } /// Strings { assert(jv_equal(jv_string("foo"), jv_string_sized("foo", 3))); char nasty[] = "foo\0"; jv shortstr = jv_string(nasty), longstr = jv_string_sized(nasty, sizeof(nasty)); assert(jv_string_length_bytes(jv_copy(shortstr)) == (int)strlen(nasty)); assert(jv_string_length_bytes(jv_copy(longstr)) == (int)sizeof(nasty)); jv_free(shortstr); jv_free(longstr); char a1s[] = "hello", a2s[] = "hello", bs[] = "goodbye"; jv a1 = jv_string(a1s), a2 = jv_string(a2s), b = jv_string(bs); assert(jv_equal(jv_copy(a1), jv_copy(a2))); assert(jv_equal(jv_copy(a2), jv_copy(a1))); assert(!jv_equal(jv_copy(a1), jv_copy(b))); assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a1))); assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a2))); assert(jv_string_hash(jv_copy(b)) != jv_string_hash(jv_copy(a1))); jv_free(a1); jv_free(a2); jv_free(b); assert(jv_equal(jv_string("hello42!"), jv_string_fmt("hello%d%s", 42, "!"))); char big[20000]; for (int i=0; i<(int)sizeof(big); i++) big[i] = 'a'; big[sizeof(big)-1] = 0; jv str = jv_string_fmt("%s", big); assert(jv_string_length_bytes(jv_copy(str)) == sizeof(big) - 1); assert(!strcmp(big, jv_string_value(str))); jv_free(str); } /// Objects { jv o1 = jv_object(); o1 = jv_object_set(o1, jv_string("foo"), jv_number(42)); o1 = jv_object_set(o1, jv_string("bar"), jv_number(24)); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("foo"))) == 42); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("bar"))) == 24); jv o2 = jv_object_set(jv_copy(o1), jv_string("foo"), jv_number(420)); o2 = jv_object_set(o2, jv_string("bar"), jv_number(240)); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("foo"))) == 42); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("bar"))) == 24); assert(jv_number_value(jv_object_get(jv_copy(o2), jv_string("foo"))) == 420); jv_free(o1); assert(jv_number_value(jv_object_get(jv_copy(o2), jv_string("bar"))) == 240); jv_dump(jv_copy(o2), 0); printf("\n"); jv_free(o2); } /// Compile errors { jq_state *jq = jq_init(); jq_compile_args(jq, "}", jv_array()); jq_teardown(&jq); } }
int main(int argc, char* argv[]) { jq_state *jq = NULL; int ret = 0; int compiled = 0; char *t = NULL; if (argc) progname = argv[0]; if (argc > 1 && !strcmp(argv[1], "--run-tests")) { return jq_testsuite(argc, argv); } jq = jq_init(); if (jq == NULL) { perror("malloc"); ret = 2; goto out; } const char* program = 0; input_filenames = jv_mem_alloc(sizeof(const char*) * argc); ninput_files = 0; int further_args_are_files = 0; int jq_flags = 0; size_t short_opts = 0; jv program_arguments = jv_array(); jv lib_search_paths = jv_array(); for (int i=1; i<argc; i++, short_opts = 0) { if (further_args_are_files) { input_filenames[ninput_files++] = argv[i]; } else if (!strcmp(argv[i], "--")) { if (!program) usage(2); further_args_are_files = 1; } else if (!isoptish(argv[i])) { if (program) { input_filenames[ninput_files++] = argv[i]; } else { program = argv[i]; } } else { if (argv[i][1] == 'L') { 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)) { options |= COMPACT_OUTPUT; 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], 'i', "in-place", &short_opts)) { options |= IN_PLACE; if (!short_opts) continue; } if (isoption(argv[i], 'e', "exit-status", &short_opts)) { options |= EXIT_STATUS; if (!short_opts) continue; } if (isoption(argv[i], 0, "arg", &short_opts)) { if (i >= argc - 2) { fprintf(stderr, "%s: --arg takes two parameters (e.g. -a 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, "argfile", &short_opts)) { if (i >= argc - 2) { fprintf(stderr, "%s: --argfile takes two parameters (e.g. -a varname filename)\n", progname); 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 --argfile %s %s: %s\n", progname, argv[i+1], argv[i+2], jv_string_value(data)); jv_free(data); ret = 2; goto out; } if (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; } // 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(); } } } char *penv = getenv("JQ_LIBRARY_PATH"); if (penv) { #ifdef WIN32 #define PATH_ENV_SEPARATOR ";" #else #define PATH_ENV_SEPARATOR ":" #endif lib_search_paths = jv_array_concat(lib_search_paths,jv_string_split(jv_string(penv),jv_string(PATH_ENV_SEPARATOR))); #undef PATH_ENV_SEPARATOR } jq_set_lib_dirs(jq,lib_search_paths); char *origin = strdup(argv[0]); if (origin == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(1); } jq_set_lib_origin(jq,jv_string(dirname(origin))); free(origin); #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 & IN_PLACE)) { if (ninput_files == 0) usage(2); if (strcmp(input_filenames[0], "-") == 0) usage(2); size_t tlen = strlen(input_filenames[0]) + 7; t = jv_mem_alloc(tlen); int n = snprintf(t, tlen,"%sXXXXXX", input_filenames[0]); assert(n > 0 && (size_t)n < tlen); if (mkstemp(t) == -1) { fprintf(stderr, "Error: %s creating temporary file", strerror(errno)); exit(3); } if (freopen(t, "w", stdout) == NULL) { fprintf(stderr, "Error: %s redirecting stdout to temporary file", strerror(errno)); exit(3); } } if (ninput_files == 0) current_input = stdin; if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) { fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname); die(); } if (options & FROM_FILE) { 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; } compiled = jq_compile_args(jq, jv_string_value(data), program_arguments); jv_free(data); } else { compiled = jq_compile_args(jq, program, program_arguments); } if (!compiled){ ret = 3; goto out; } if (options & DUMP_DISASM) { jq_dump_disassembly(jq, 0); printf("\n"); } if (options & PROVIDE_NULL) { ret = process(jq, jv_null(), jq_flags); } else { jv slurped; if (options & SLURP) { if (options & RAW_INPUT) { slurped = jv_string(""); } else { slurped = jv_array(); } } struct jv_parser* parser = jv_parser_new(0); char buf[4096]; while (read_more(buf, sizeof(buf))) { if (options & RAW_INPUT) { int len = strlen(buf); if (len > 0) { if (options & SLURP) { slurped = jv_string_concat(slurped, jv_string(buf)); } else { if (buf[len-1] == '\n') buf[len-1] = 0; ret = process(jq, jv_string(buf), jq_flags); } } } else { jv_parser_set_buf(parser, buf, strlen(buf), !feof(stdin)); jv value; while (jv_is_valid((value = jv_parser_next(parser)))) { if (options & SLURP) { slurped = jv_array_append(slurped, value); } else { ret = process(jq, value, jq_flags); } } if (jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); ret = 4; break; } else { jv_free(value); } } } jv_parser_free(parser); if (ret != 0) goto out; if (options & SLURP) { ret = process(jq, slurped, jq_flags); } } if ((options & IN_PLACE)) { #ifdef WIN32 (void) freopen("NUL", "w+", stdout); #else (void) freopen("/dev/null", "w+", stdout); #endif if (rename(t, input_filenames[0]) == -1) { fprintf(stderr, "Error: %s renaming temporary file", strerror(errno)); exit(3); } jv_mem_free(t); } out: jv_mem_free(input_filenames); jq_teardown(&jq); if (ret >= 10 && (options & EXIT_STATUS)) return ret - 10; if (ret >= 10) return 0; return ret; }
void delete_jq_state(jq_state* state) { // Rprintf("deleting state\n"); jq_teardown(&state); }
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; jv_extra_opt extra_opt; extra_opt.array_fold=0; extra_opt.array_fold_indent=1; 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, jq_realpath(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, jq_realpath(jv_string(argv[i+1]))); i++; } continue; } if (isoption(argv[i], 0, "fold", &short_opts)) { if (i >= argc - 1) { fprintf(stderr, "%s: --fold takes one parameter\n", progname); die(); } extra_opt.array_fold = atoi(argv[i+1]); if (extra_opt.array_fold < 1 || extra_opt.array_fold > 255) { fprintf(stderr, "%s: --fold takes a number between 1 and 255\n", progname); die(); } i++; if (!short_opts) 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 |= COLOR_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) { options |= NO_COLOR_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 color by default on Windows builds as Windows terminals tend not to display it correctly */ dumpopts |= JV_PRINT_COLOR; #endif } if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; if (options & COLOR_OUTPUT) dumpopts |= JV_PRINT_COLOR; if (options & NO_COLOR_OUTPUT) dumpopts &= ~JV_PRINT_COLOR; 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) { if (extra_opt.array_fold==0) { ret = process(jq, jv_null(), jq_flags, dumpopts); } else { ret = process_extra_opt(jq, jv_null(), jq_flags, dumpopts, &extra_opt); } } 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)) { if (extra_opt.array_fold==0) { ret = process(jq, value, jq_flags, dumpopts); } else { ret = process_extra_opt(jq, value, jq_flags, dumpopts, &extra_opt); } 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; }
static void run_jq_tests(FILE *testdata) { char buf[4096]; int tests = 0, passed = 0, invalid = 0; jq_state *jq = NULL; while (1) { if (!fgets(buf, sizeof(buf), testdata)) break; if (skipline(buf)) continue; if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0; printf("Testing %s\n", buf); int pass = 1; tests++; struct bytecode* bc = jq_compile(buf); if (!bc) {invalid++; continue;} printf("Disassembly:\n"); dump_disassembly(2, bc); printf("\n"); fgets(buf, sizeof(buf), testdata); jv input = jv_parse(buf); if (!jv_is_valid(input)){ invalid++; continue; } jq_init(bc, input, &jq, JQ_DEBUG_TRACE); while (fgets(buf, sizeof(buf), testdata)) { if (skipline(buf)) break; jv expected = jv_parse(buf); if (!jv_is_valid(expected)){ invalid++; continue; } jv actual = jq_next(jq); if (!jv_is_valid(actual)) { jv_free(actual); printf("*** Insufficient results\n"); pass = 0; break; } else if (!jv_equal(jv_copy(expected), jv_copy(actual))) { printf("*** Expected "); jv_dump(jv_copy(expected), 0); printf(", but got "); jv_dump(jv_copy(actual), 0); printf("\n"); pass = 0; } jv as_string = jv_dump_string(jv_copy(expected), rand() & ~JV_PRINT_COLOUR); jv reparsed = jv_parse_sized(jv_string_value(as_string), jv_string_length_bytes(jv_copy(as_string))); assert(jv_equal(jv_copy(expected), jv_copy(reparsed))); jv_free(as_string); jv_free(reparsed); jv_free(expected); jv_free(actual); } if (pass) { jv extra = jq_next(jq); if (jv_is_valid(extra)) { printf("*** Superfluous result: "); jv_dump(extra, 0); printf("\n"); pass = 0; } else { jv_free(extra); } } jq_teardown(&jq); bytecode_free(bc); passed+=pass; } printf("%d of %d tests passed (%d malformed)\n", passed,tests,invalid); if (passed != tests) exit(1); }
int main(int argc, char* argv[]) { jq_state *jq = NULL; int ret = 0; int compiled = 0; if (argc) progname = argv[0]; if (argc > 1 && !strcmp(argv[1], "--run-tests")) { return jq_testsuite(argc, argv); } jq = jq_init(); if (jq == NULL) { perror("malloc"); ret = 1; goto out; } const char* program = 0; input_filenames = jv_mem_alloc(sizeof(const char*) * argc); ninput_files = 0; int further_args_are_files = 0; int jq_flags = 0; jv_parser_flags parser_flags = 0; jv program_arguments = jv_array(); for (int i=1; i<argc; i++) { if (further_args_are_files) { input_filenames[ninput_files++] = argv[i]; } else if (!strcmp(argv[i], "--")) { if (!program) usage(); further_args_are_files = 1; } else if (!isoptish(argv[i])) { if (program) { input_filenames[ninput_files++] = argv[i]; } else { program = argv[i]; } } else if (isoption(argv[i], 's', "slurp")) { options |= SLURP; } else if (isoption(argv[i], 'r', "raw-output")) { options |= RAW_OUTPUT; } else if (isoption(argv[i], 'c', "compact-output")) { options |= COMPACT_OUTPUT; } else if (isoption(argv[i], 'C', "color-output")) { options |= COLOUR_OUTPUT; } else if (isoption(argv[i], 'M', "monochrome-output")) { options |= NO_COLOUR_OUTPUT; } else if (isoption(argv[i], 'a', "ascii-output")) { options |= ASCII_OUTPUT; } else if (isoption(argv[i], 0, "unbuffered")) { options |= UNBUFFERED_OUTPUT; } else if (isoption(argv[i], 'S', "sort-keys")) { options |= SORTED_OUTPUT; } else if (isoption(argv[i], 'R', "raw-input")) { options |= RAW_INPUT; } else if (isoption(argv[i], 'n', "null-input")) { options |= PROVIDE_NULL; } else if (isoption(argv[i], 'f', "from-file")) { options |= FROM_FILE; } else if (isoption(argv[i], 'e', "exit-status")) { options |= EXIT_STATUS; } else if (isoption(argv[i], 'I', "online-input")) { parser_flags = JV_PARSE_EXPLODE_TOPLEVEL_ARRAY; } else if (isoption(argv[i], 0, "arg")) { if (i >= argc - 2) { fprintf(stderr, "%s: --arg takes two parameters (e.g. -a 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 } else if (isoption(argv[i], 0, "argfile")) { if (i >= argc - 2) { fprintf(stderr, "%s: --argfile takes two parameters (e.g. -a varname filename)\n", progname); 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 --argfile %s %s: %s\n", progname, argv[i+1], argv[i+2], jv_string_value(data)); jv_free(data); ret = 1; goto out; } if (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 } else if (isoption(argv[i], 0, "debug-dump-disasm")) { options |= DUMP_DISASM; } else if (isoption(argv[i], 0, "debug-trace")) { jq_flags |= JQ_DEBUG_TRACE; } else if (isoption(argv[i], 'h', "help")) { usage(); } else if (isoption(argv[i], 'V', "version")) { printf("jq-%s\n", JQ_VERSION); ret = 0; goto out; } else { fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]); die(); } } if (!program) usage(); if (ninput_files == 0) current_input = stdin; if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) { fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname); die(); } if (options & FROM_FILE) { 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 = 1; goto out; } compiled = jq_compile_args(jq, jv_string_value(data), program_arguments); jv_free(data); } else { compiled = jq_compile_args(jq, program, program_arguments); } if (!compiled){ ret = 1; goto out; } if (options & DUMP_DISASM) { jq_dump_disassembly(jq, 0); printf("\n"); } if (options & PROVIDE_NULL) { ret = process(jq, jv_null(), jq_flags); } else { jv slurped; if (options & SLURP) { if (options & RAW_INPUT) { slurped = jv_string(""); } else { slurped = jv_array(); } } struct jv_parser* parser = jv_parser_new(parser_flags); char buf[4096]; while (read_more(buf, sizeof(buf))) { if (options & RAW_INPUT) { int len = strlen(buf); if (len > 0) { if (options & SLURP) { slurped = jv_string_concat(slurped, jv_string(buf)); } else { if (buf[len-1] == '\n') buf[len-1] = 0; ret = process(jq, jv_string(buf), jq_flags); } } } else { jv_parser_set_buf(parser, buf, strlen(buf), !feof(stdin)); jv value; while (jv_is_valid((value = jv_parser_next(parser)))) { if (options & SLURP) { slurped = jv_array_append(slurped, value); } else { ret = process(jq, value, jq_flags); } } if (jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); ret = 1; break; } else { jv_free(value); } } } jv_parser_free(parser); if (ret != 0) goto out; if (options & SLURP) { ret = process(jq, slurped, jq_flags); } } out: jv_mem_free(input_filenames); jq_teardown(&jq); if (ret >= 10 && ret <= 11 && !(options & EXIT_STATUS)) return 0; return ret; }