2013-05-26 12:42:04 +10:00
|
|
|
#include <stdlib.h>
|
2012-08-16 01:00:30 +01:00
|
|
|
#include <stdio.h>
|
2012-10-22 19:16:27 +01:00
|
|
|
#include <errno.h>
|
2012-09-11 12:24:54 +01:00
|
|
|
#include <string.h>
|
2012-09-20 19:31:14 +01:00
|
|
|
#include <ctype.h>
|
2012-12-03 01:21:07 +00:00
|
|
|
#include <unistd.h>
|
2012-08-16 01:00:30 +01:00
|
|
|
#include "compile.h"
|
2012-09-02 16:31:59 +01:00
|
|
|
#include "jv.h"
|
2013-06-16 08:25:12 -05:00
|
|
|
#include "jq.h"
|
2012-12-18 16:52:47 +00:00
|
|
|
#include "jv_alloc.h"
|
2013-05-24 23:34:34 +01:00
|
|
|
#include "version.h"
|
2012-08-16 01:00:30 +01:00
|
|
|
|
2013-05-05 22:53:45 +01:00
|
|
|
int jq_testsuite(int argc, char* argv[]);
|
|
|
|
|
2012-09-18 23:37:46 +01:00
|
|
|
static const char* progname;
|
|
|
|
|
|
|
|
static void usage() {
|
2013-05-24 23:34:34 +01:00
|
|
|
fprintf(stderr, "\njq - commandline JSON processor [version %s]\n", JQ_VERSION);
|
2012-12-16 19:46:41 +00:00
|
|
|
fprintf(stderr, "Usage: %s [options] <jq filter> [file...]\n\n", progname);
|
2012-09-20 19:31:14 +01:00
|
|
|
fprintf(stderr, "For a description of the command line options and\n");
|
|
|
|
fprintf(stderr, "how to write jq filters (and why you might want to)\n");
|
2012-12-16 19:46:41 +00:00
|
|
|
fprintf(stderr, "see the jq manpage, or the online documentation at\n");
|
|
|
|
fprintf(stderr, "http://stedolan.github.com/jq\n\n");
|
2012-09-18 23:37:46 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2012-09-20 19:31:14 +01:00
|
|
|
static void die() {
|
|
|
|
fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);
|
|
|
|
fprintf(stderr, "or see the jq documentation at http://stedolan.github.com/jq\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
if (text[0] != '-') return 0;
|
|
|
|
if (strlen(text) == 2 && text[1] == shortopt) return 1;
|
|
|
|
if (text[1] == '-' && !strcmp(text+2, longopt)) return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum {
|
|
|
|
SLURP = 1,
|
|
|
|
RAW_INPUT = 2,
|
|
|
|
PROVIDE_NULL = 4,
|
|
|
|
|
|
|
|
RAW_OUTPUT = 8,
|
|
|
|
COMPACT_OUTPUT = 16,
|
|
|
|
ASCII_OUTPUT = 32,
|
2012-12-03 01:21:07 +00:00
|
|
|
COLOUR_OUTPUT = 64,
|
|
|
|
NO_COLOUR_OUTPUT = 128,
|
2013-05-29 11:43:55 +01:00
|
|
|
SORTED_OUTPUT = 256,
|
2013-11-08 12:19:41 +00:00
|
|
|
UNBUFFERED_OUTPUT = 4096,
|
2012-10-22 19:16:27 +01:00
|
|
|
|
2013-05-29 11:43:55 +01:00
|
|
|
FROM_FILE = 512,
|
2013-05-05 22:59:46 +01:00
|
|
|
|
2013-11-29 16:36:15 -06:00
|
|
|
EXIT_STATUS = 8192,
|
|
|
|
|
2013-05-05 22:59:46 +01:00
|
|
|
/* debugging only */
|
|
|
|
DUMP_DISASM = 2048,
|
2012-09-20 19:31:14 +01:00
|
|
|
};
|
|
|
|
static int options = 0;
|
|
|
|
|
2013-11-29 16:36:15 -06:00
|
|
|
static int process(jq_state *jq, jv value, int flags) {
|
|
|
|
int ret = 0;
|
2013-06-06 17:26:15 -05:00
|
|
|
jq_start(jq, value, flags);
|
2012-09-20 19:31:14 +01:00
|
|
|
jv result;
|
2013-04-28 18:33:45 -05:00
|
|
|
while (jv_is_valid(result = jq_next(jq))) {
|
2012-09-20 19:31:14 +01:00
|
|
|
if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
|
2013-05-15 00:37:38 +01:00
|
|
|
fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
|
2013-11-29 16:36:15 -06:00
|
|
|
if (jv_get_kind(result) == JV_KIND_FALSE)
|
|
|
|
ret = 10;
|
|
|
|
else if (jv_get_kind(result) == JV_KIND_NULL)
|
|
|
|
ret = 11;
|
|
|
|
else
|
|
|
|
ret = 0;
|
2012-12-23 15:41:20 +00:00
|
|
|
jv_free(result);
|
2012-09-20 19:31:14 +01:00
|
|
|
} else {
|
2012-12-20 12:16:22 +00:00
|
|
|
int dumpopts;
|
2013-05-08 19:36:52 +01:00
|
|
|
/* Disable colour by default on Windows builds as Windows
|
|
|
|
terminals tend not to display it correctly */
|
|
|
|
#ifdef WIN32
|
|
|
|
dumpopts = 0;
|
2012-12-20 12:16:22 +00:00
|
|
|
#else
|
|
|
|
dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0;
|
|
|
|
#endif
|
2013-05-29 11:43:55 +01:00
|
|
|
if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED;
|
2012-09-20 19:31:14 +01:00
|
|
|
if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY;
|
|
|
|
if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;
|
2012-12-03 01:21:07 +00:00
|
|
|
if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR;
|
|
|
|
if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR;
|
2013-11-08 12:19:41 +00:00
|
|
|
if (options & UNBUFFERED_OUTPUT) dumpopts |= JV_PRINT_UNBUFFERED;
|
2013-11-29 16:36:15 -06:00
|
|
|
if (jv_get_kind(result) == JV_KIND_FALSE)
|
|
|
|
ret = 10;
|
|
|
|
else if (jv_get_kind(result) == JV_KIND_NULL)
|
|
|
|
ret = 11;
|
|
|
|
else
|
|
|
|
ret = 0;
|
2012-09-20 19:31:14 +01:00
|
|
|
jv_dump(result, dumpopts);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
jv_free(result);
|
2013-11-29 16:36:15 -06:00
|
|
|
return ret;
|
2012-09-20 19:31:14 +01:00
|
|
|
}
|
|
|
|
|
2012-12-16 19:46:41 +00:00
|
|
|
FILE* current_input;
|
|
|
|
const char** input_filenames;
|
|
|
|
int ninput_files;
|
|
|
|
int next_input_idx;
|
|
|
|
static int read_more(char* buf, size_t size) {
|
|
|
|
while (!current_input || feof(current_input)) {
|
|
|
|
if (current_input) {
|
|
|
|
fclose(current_input);
|
|
|
|
current_input = 0;
|
|
|
|
}
|
|
|
|
if (next_input_idx == ninput_files) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(input_filenames[next_input_idx], "-")) {
|
|
|
|
current_input = stdin;
|
|
|
|
} else {
|
|
|
|
current_input = fopen(input_filenames[next_input_idx], "r");
|
|
|
|
}
|
|
|
|
if (!current_input) {
|
|
|
|
fprintf(stderr, "%s: %s: %s\n", progname, input_filenames[next_input_idx], strerror(errno));
|
|
|
|
}
|
|
|
|
next_input_idx++;
|
|
|
|
}
|
|
|
|
|
2012-12-27 01:57:09 +00:00
|
|
|
if (!fgets(buf, size, current_input)) buf[0] = 0;
|
2012-12-16 19:46:41 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-08-16 01:00:30 +01:00
|
|
|
int main(int argc, char* argv[]) {
|
2013-06-06 17:26:15 -05:00
|
|
|
jq_state *jq;
|
2013-05-04 17:03:01 -05:00
|
|
|
int ret = 0;
|
2013-06-06 17:26:15 -05:00
|
|
|
int compiled = 0;
|
|
|
|
|
2012-09-18 23:37:46 +01:00
|
|
|
if (argc) progname = argv[0];
|
2012-09-20 19:31:14 +01:00
|
|
|
|
2013-05-05 22:53:45 +01:00
|
|
|
if (argc > 1 && !strcmp(argv[1], "--run-tests")) {
|
2013-06-15 01:55:47 -05:00
|
|
|
return jq_testsuite(argc, argv);
|
2013-05-05 22:53:45 +01:00
|
|
|
}
|
|
|
|
|
2013-06-06 17:26:15 -05:00
|
|
|
jq = jq_init();
|
|
|
|
if (jq == NULL) {
|
|
|
|
perror("malloc");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-09-20 19:31:14 +01:00
|
|
|
const char* program = 0;
|
2012-12-18 16:52:47 +00:00
|
|
|
input_filenames = jv_mem_alloc(sizeof(const char*) * argc);
|
2012-12-16 19:46:41 +00:00
|
|
|
ninput_files = 0;
|
|
|
|
int further_args_are_files = 0;
|
2013-05-05 23:12:10 +01:00
|
|
|
int jq_flags = 0;
|
2013-11-29 17:41:04 -06:00
|
|
|
jv_parser_flags parser_flags = 0;
|
2013-05-06 14:21:00 +01:00
|
|
|
jv program_arguments = jv_array();
|
2012-09-20 19:31:14 +01:00
|
|
|
for (int i=1; i<argc; i++) {
|
2012-12-16 19:46:41 +00:00
|
|
|
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];
|
|
|
|
}
|
2012-09-20 19:31:14 +01:00
|
|
|
} 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;
|
2012-12-03 01:21:07 +00:00
|
|
|
} else if (isoption(argv[i], 'C', "color-output")) {
|
|
|
|
options |= COLOUR_OUTPUT;
|
|
|
|
} else if (isoption(argv[i], 'M', "monochrome-output")) {
|
|
|
|
options |= NO_COLOUR_OUTPUT;
|
2012-09-20 19:31:14 +01:00
|
|
|
} else if (isoption(argv[i], 'a', "ascii-output")) {
|
|
|
|
options |= ASCII_OUTPUT;
|
2013-11-08 12:19:41 +00:00
|
|
|
} else if (isoption(argv[i], 0, "unbuffered")) {
|
|
|
|
options |= UNBUFFERED_OUTPUT;
|
2013-05-29 11:43:55 +01:00
|
|
|
} else if (isoption(argv[i], 'S', "sort-keys")) {
|
|
|
|
options |= SORTED_OUTPUT;
|
2012-09-20 19:31:14 +01:00
|
|
|
} else if (isoption(argv[i], 'R', "raw-input")) {
|
|
|
|
options |= RAW_INPUT;
|
|
|
|
} else if (isoption(argv[i], 'n', "null-input")) {
|
|
|
|
options |= PROVIDE_NULL;
|
2012-10-22 19:16:27 +01:00
|
|
|
} else if (isoption(argv[i], 'f', "from-file")) {
|
|
|
|
options |= FROM_FILE;
|
2013-11-29 16:36:15 -06:00
|
|
|
} else if (isoption(argv[i], 'e', "exit-status")) {
|
|
|
|
options |= EXIT_STATUS;
|
2013-11-29 17:41:04 -06:00
|
|
|
} else if (isoption(argv[i], 'I', "online-input")) {
|
|
|
|
parser_flags = JV_PARSE_EXPLODE_TOPLEVEL_ARRAY;
|
2013-05-06 14:21:00 +01:00
|
|
|
} 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
|
2013-05-06 17:25:19 -05:00
|
|
|
} 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]));
|
2013-06-15 00:08:59 -05:00
|
|
|
jv data = jv_load_file(argv[i+2], 0);
|
2013-05-06 17:25:19 -05:00
|
|
|
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);
|
|
|
|
return 1;
|
|
|
|
}
|
2013-06-17 22:32:44 -05:00
|
|
|
if (jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
|
2013-05-06 17:25:19 -05:00
|
|
|
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
|
2013-05-05 22:59:46 +01:00
|
|
|
} else if (isoption(argv[i], 0, "debug-dump-disasm")) {
|
|
|
|
options |= DUMP_DISASM;
|
2013-05-05 23:12:10 +01:00
|
|
|
} else if (isoption(argv[i], 0, "debug-trace")) {
|
|
|
|
jq_flags |= JQ_DEBUG_TRACE;
|
2012-09-20 19:31:14 +01:00
|
|
|
} else if (isoption(argv[i], 'h', "help")) {
|
|
|
|
usage();
|
2012-10-21 23:26:31 +01:00
|
|
|
} else if (isoption(argv[i], 'V', "version")) {
|
2013-06-03 23:22:59 +01:00
|
|
|
printf("jq-%s\n", JQ_VERSION);
|
2012-10-21 23:26:31 +01:00
|
|
|
return 0;
|
2012-09-20 19:31:14 +01:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
|
|
|
|
die();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!program) usage();
|
2012-12-16 19:46:41 +00:00
|
|
|
if (ninput_files == 0) current_input = stdin;
|
2012-09-20 19:31:14 +01:00
|
|
|
|
|
|
|
if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) {
|
2012-10-22 19:16:27 +01:00
|
|
|
fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname);
|
2012-09-20 19:31:14 +01:00
|
|
|
die();
|
|
|
|
}
|
2012-10-22 19:16:27 +01:00
|
|
|
|
|
|
|
if (options & FROM_FILE) {
|
2013-06-15 00:08:59 -05:00
|
|
|
jv data = jv_load_file(program, 1);
|
2012-10-22 19:16:27 +01:00
|
|
|
if (!jv_is_valid(data)) {
|
|
|
|
data = jv_invalid_get_msg(data);
|
|
|
|
fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
|
|
|
|
jv_free(data);
|
|
|
|
return 1;
|
|
|
|
}
|
2013-06-06 17:26:15 -05:00
|
|
|
compiled = jq_compile_args(jq, jv_string_value(data), program_arguments);
|
2012-10-22 19:16:27 +01:00
|
|
|
jv_free(data);
|
|
|
|
} else {
|
2013-06-06 17:26:15 -05:00
|
|
|
compiled = jq_compile_args(jq, program, program_arguments);
|
2012-10-22 19:16:27 +01:00
|
|
|
}
|
2013-06-06 17:26:15 -05:00
|
|
|
if (!compiled) return 1;
|
2012-09-11 14:52:10 +01:00
|
|
|
|
2013-05-05 22:59:46 +01:00
|
|
|
if (options & DUMP_DISASM) {
|
2013-06-06 17:26:15 -05:00
|
|
|
jq_dump_disassembly(jq, 0);
|
2013-05-05 22:59:46 +01:00
|
|
|
printf("\n");
|
|
|
|
}
|
2012-09-11 14:52:10 +01:00
|
|
|
|
2012-09-20 19:31:14 +01:00
|
|
|
if (options & PROVIDE_NULL) {
|
2013-11-29 16:36:15 -06:00
|
|
|
ret = process(jq, jv_null(), jq_flags);
|
2012-09-20 19:31:14 +01:00
|
|
|
} else {
|
|
|
|
jv slurped;
|
2012-10-22 18:55:56 +01:00
|
|
|
if (options & SLURP) {
|
|
|
|
if (options & RAW_INPUT) {
|
|
|
|
slurped = jv_string("");
|
|
|
|
} else {
|
|
|
|
slurped = jv_array();
|
|
|
|
}
|
|
|
|
}
|
2013-11-29 17:41:04 -06:00
|
|
|
struct jv_parser* parser = jv_parser_new(parser_flags);
|
2012-12-16 19:46:41 +00:00
|
|
|
char buf[4096];
|
|
|
|
while (read_more(buf, sizeof(buf))) {
|
2012-09-20 19:31:14 +01:00
|
|
|
if (options & RAW_INPUT) {
|
|
|
|
int len = strlen(buf);
|
|
|
|
if (len > 0) {
|
|
|
|
if (options & SLURP) {
|
2012-10-22 18:55:56 +01:00
|
|
|
slurped = jv_string_concat(slurped, jv_string(buf));
|
2012-09-20 19:31:14 +01:00
|
|
|
} else {
|
|
|
|
if (buf[len-1] == '\n') buf[len-1] = 0;
|
2013-11-29 16:36:15 -06:00
|
|
|
ret = process(jq, jv_string(buf), jq_flags);
|
2012-09-20 19:31:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-06-23 12:26:49 +01:00
|
|
|
jv_parser_set_buf(parser, buf, strlen(buf), !feof(stdin));
|
2012-09-20 19:31:14 +01:00
|
|
|
jv value;
|
2013-06-23 12:26:49 +01:00
|
|
|
while (jv_is_valid((value = jv_parser_next(parser)))) {
|
2012-09-20 19:31:14 +01:00
|
|
|
if (options & SLURP) {
|
|
|
|
slurped = jv_array_append(slurped, value);
|
|
|
|
} else {
|
2013-11-29 16:36:15 -06:00
|
|
|
ret = process(jq, value, jq_flags);
|
2012-09-20 19:31:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
2013-05-04 17:03:01 -05:00
|
|
|
ret = 1;
|
2012-09-20 19:31:14 +01:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
jv_free(value);
|
|
|
|
}
|
2012-09-11 14:52:10 +01:00
|
|
|
}
|
|
|
|
}
|
2013-06-23 12:26:49 +01:00
|
|
|
jv_parser_free(parser);
|
2013-05-04 17:03:01 -05:00
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
2012-09-20 19:31:14 +01:00
|
|
|
if (options & SLURP) {
|
2013-11-29 16:36:15 -06:00
|
|
|
ret = process(jq, slurped, jq_flags);
|
2012-09-11 14:52:10 +01:00
|
|
|
}
|
|
|
|
}
|
2013-05-04 17:03:01 -05:00
|
|
|
out:
|
2012-12-18 16:52:47 +00:00
|
|
|
jv_mem_free(input_filenames);
|
2013-06-06 17:26:15 -05:00
|
|
|
jq_teardown(&jq);
|
2013-11-29 16:36:15 -06:00
|
|
|
if (ret >= 10 && ret <= 11 && !(options & EXIT_STATUS))
|
|
|
|
return 0;
|
2013-05-04 17:03:01 -05:00
|
|
|
return ret;
|
2012-08-16 01:00:30 +01:00
|
|
|
}
|