diff options
author | Stephen Dolan <mu@netsoc.tcd.ie> | 2012-09-20 19:31:14 +0100 |
---|---|---|
committer | Stephen Dolan <mu@netsoc.tcd.ie> | 2012-09-20 19:31:14 +0100 |
commit | 359d5f33bde0df899985eac3d720f5c6a7f4d580 (patch) | |
tree | 8c5f7fdf86dba42b153925bceb113beb08a30fbe | |
parent | fcd7cf4ddd1025157bc2f8a808082a71937c85e7 (diff) |
Add a bunch of commandline options for controlling I/O format.
Also docs for the above. Closes #1.
-rw-r--r-- | docs/content/3.manual/manual.yml | 54 | ||||
-rw-r--r-- | main.c | 166 |
2 files changed, 190 insertions, 30 deletions
diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index 10998dce..50c63eca 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -28,7 +28,59 @@ body: | simpler: sections: - - title: Basics + - title: Invoking jq + body: | + + jq filters run on a stream of JSON data. The input to jq is + parsed as a sequence of whitespace-separated JSON values which + are passed through the provided filter one at a time. The + output(s) of the filter are written to standard out, again as a + sequence of whitespace-separated JSON data. + + You can affect how jq reads and writes its input and output + using some command-line options: + + * `--slurp`/`-s` + + Instead of running the filter for each JSON object in the + input, read the entire input stream into a large array and run + the filter just once. + + * `--raw-input`/`-R` + + Don't parse the input as JSON. Instead, each line of text is + passed to the filter as a string. If combined with `--slurp`, + then the entire input is passed to the filter as a single long + string. + + * `--null-input`/`-n` + + Don't read any input at all! Instead, the filter is run once + using `null` as the input. This is useful when using jq as a + simple calculator or to construct JSON data from scratch. + + * `--compact-output` / `-c` + + By default, jq pretty-prints JSON output. Using this option + will result in more compact output by instead putting each + JSON object on a single line. + + * `--ascii-output` / `-a` + + jq usually outputs non-ASCII Unicode codepoints as UTF-8, even + if the input specified them as escape sequences (like + "\u03bc"). Using this option, you can force jq to produce pure + ASCII output with every non-ASCII character replaced with the + equivalent escape sequence. + + * `--raw-output` / `-r` + + With this option, if the filter's result is a string then it + will be written directly to standard output rather than being + formatted as a JSON string with quotes. This can be useful for + making jq filters talk to non-JSON-based systems. + + - title: Basic filters entries: - title: "`.`" body: | @@ -1,5 +1,6 @@ #include <stdio.h> #include <string.h> +#include <ctype.h> #include "compile.h" #include "builtin.h" #include "jv.h" @@ -12,17 +13,98 @@ static const char* progname; static void usage() { fprintf(stderr, "\njq - commandline JSON processor\n"); - fprintf(stderr, "Usage: %s <jq filter>\n\n", progname); - fprintf(stderr, "For a description of how to write jq filters and\n"); - fprintf(stderr, "why you might want to, see the jq documentation at\n"); - fprintf(stderr, "http://stedolan.github.com/jq\n\n"); + fprintf(stderr, "Usage: %s [options] <jq filter>\n\n", progname); + 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"); + fprintf(stderr, "see the jq documentation at http://stedolan.github.com/jq\n\n"); exit(1); } +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, +}; +static int options = 0; +static struct bytecode* bc; + +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(); +} + int main(int argc, char* argv[]) { if (argc) progname = argv[0]; - if (argc != 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "--version")) usage(); - struct bytecode* bc = jq_compile(argv[1]); + + const char* program = 0; + for (int i=1; i<argc; i++) { + if (!isoptish(argv[i])) { + if (program) usage(); + 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], 'a', "ascii-output")) { + options |= ASCII_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], 'h', "help")) { + usage(); + } else { + fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]); + die(); + } + } + if (!program) usage(); + + if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) { + fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", program); + die(); + } + + bc = jq_compile(program); if (!bc) return 1; #if JQ_DEBUG @@ -30,33 +112,59 @@ int main(int argc, char* argv[]) { printf("\n"); #endif - struct jv_parser parser; - jv_parser_init(&parser); - while (!feof(stdin)) { - char buf[4096]; - if (!fgets(buf, sizeof(buf), stdin)) buf[0] = 0; - jv_parser_set_buf(&parser, buf, strlen(buf), !feof(stdin)); - jv value; - while (jv_is_valid((value = jv_parser_next(&parser)))) { - jq_init(bc, value); - jv result; - while (jv_is_valid(result = jq_next())) { - jv_dump(result, JV_PRINT_PRETTY); - printf("\n"); + if (options & PROVIDE_NULL) { + process(jv_null()); + } else { + jv slurped; + if (options & SLURP) slurped = jv_invalid(); + int first = 1; + struct jv_parser parser; + jv_parser_init(&parser); + while (!feof(stdin)) { + char buf[4096]; + if (!fgets(buf, sizeof(buf), stdin)) buf[0] = 0; + if (options & RAW_INPUT) { + int len = strlen(buf); + if (len > 0) { + if (options & SLURP) { + if (first) slurped = jv_string(buf); + else slurped = jv_string_concat(slurped, jv_string(buf)); + } else { + if (buf[len-1] == '\n') buf[len-1] = 0; + process(jv_string(buf)); + } + } + } 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) { + if (first) slurped = jv_array(); + slurped = jv_array_append(slurped, value); + } else { + process(value); + } + } + 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); + break; + } else { + jv_free(value); + } } - jv_free(result); - jq_teardown(); + first = 0; } - 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); - break; - } else { - jv_free(value); + jv_parser_free(&parser); + if (options & SLURP) { + if (jv_is_valid(slurped)) { + process(slurped); + } else { + jv_free(slurped); + } } } - jv_parser_free(&parser); bytecode_free(bc); return 0; |