summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Dolan <mu@netsoc.tcd.ie>2012-09-20 19:31:14 +0100
committerStephen Dolan <mu@netsoc.tcd.ie>2012-09-20 19:31:14 +0100
commit359d5f33bde0df899985eac3d720f5c6a7f4d580 (patch)
tree8c5f7fdf86dba42b153925bceb113beb08a30fbe
parentfcd7cf4ddd1025157bc2f8a808082a71937c85e7 (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.yml54
-rw-r--r--main.c166
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: |
diff --git a/main.c b/main.c
index 92a7c652..48934cc4 100644
--- a/main.c
+++ b/main.c
@@ -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;