From 6d89e297febdbcbad4ecf201e56fc8ec99f67137 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Sat, 29 Apr 2017 13:00:52 -0500 Subject: Add JQ_COLORS env var for color config (fix #1252) --- docs/content/3.manual/manual.yml | 42 ++++++++++++++++++++ jq.1.prebuilt | 84 ++++++++++++++++++++++++++++++++++++++++ src/jq.h | 2 + src/jv_print.c | 37 +++++++++++++++++- src/main.c | 3 ++ tests/shtest | 54 ++++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 1 deletion(-) diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index f666ceb1..fcca8412 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -160,6 +160,9 @@ sections: terminal. You can force it to produce color even if writing to a pipe or a file using `-C`, and disable color with `-M`. + Colors can be configured with the `JQ_COLORS` environment + variable (see below). + * `--ascii-output` / `-a`: jq usually outputs non-ASCII Unicode codepoints as UTF-8, even @@ -3195,3 +3198,42 @@ sections: could then use to, for example, search for, download, and install missing dependencies. + - title: Colors + body: | + + To configure alternative colors just set the `JQ_COLORS` + environment variable to colon-delimited list of partial terminal + escape sequences like `"1;31"`, in this order: + + - color for `null` + - color for `false` + - color for `true` + - color for numbers + - color for strings + - color for arrays + - color for objects + + The default color scheme is the same as setting + `"JQ_COLORS=1;30:0;39:0;39:0;39:0;32:1;39:1;39"`. + + This is not a manual for VT100/ANSI escapes. However, each of + these color specifications should consist of two numbers separated + by a semi-colon, where the first number is one of these: + + - 1 (bright) + - 2 (dim) + - 4 (underscore) + - 5 (blink) + - 7 (reverse) + - 8 (hidden) + + and the second is one of these: + + - 30 (black) + - 31 (red) + - 32 (green) + - 33 (yellow) + - 34 (blue) + - 35 (magenta) + - 36 (cyan) + - 37 (white) diff --git a/jq.1.prebuilt b/jq.1.prebuilt index ef04d741..21a0fcd0 100644 --- a/jq.1.prebuilt +++ b/jq.1.prebuilt @@ -105,6 +105,9 @@ Use the given number of spaces (no more than 8) for indentation\. .IP By default, jq outputs colored JSON if writing to a terminal\. You can force it to produce color even if writing to a pipe or a file using \fB\-C\fR, and disable color with \fB\-M\fR\. . +.IP +Colors can be configured with the \fBJQ_COLORS\fR environment variable (see below)\. +. .IP "\(bu" 4 \fB\-\-ascii\-output\fR / \fB\-a\fR: . @@ -3373,6 +3376,87 @@ Takes a module name as input and outputs the module\'s metadata as an object, wi .P Programs can use this to query a module\'s metadata, which they could then use to, for example, search for, download, and install missing dependencies\. . +.SH "COLORS" +To configure alternative colors just set the \fBJQ_COLORS\fR environment variable to colon\-delimited list of partial terminal escape sequences like \fB"1;31"\fR, in this order: +. +.IP "\(bu" 4 +color for \fBnull\fR +. +.IP "\(bu" 4 +color for \fBfalse\fR +. +.IP "\(bu" 4 +color for \fBtrue\fR +. +.IP "\(bu" 4 +color for numbers +. +.IP "\(bu" 4 +color for strings +. +.IP "\(bu" 4 +color for arrays +. +.IP "\(bu" 4 +color for objects +. +.IP "" 0 +. +.P +The default color scheme is the same as setting \fB"JQ_COLORS=1;30:0;39:0;39:0;39:0;32:1;39:1;39"\fR\. +. +.P +This is not a manual for VT100/ANSI escapes\. However, each of these color specifications should consist of two numbers separated by a semi\-colon, where the first number is one of these: +. +.IP "\(bu" 4 +1 (bright) +. +.IP "\(bu" 4 +2 (dim) +. +.IP "\(bu" 4 +4 (underscore) +. +.IP "\(bu" 4 +5 (blink) +. +.IP "\(bu" 4 +7 (reverse) +. +.IP "\(bu" 4 +8 (hidden) +. +.IP "" 0 +. +.P +and the second is one of these: +. +.IP "\(bu" 4 +30 (black) +. +.IP "\(bu" 4 +31 (red) +. +.IP "\(bu" 4 +32 (green) +. +.IP "\(bu" 4 +33 (yellow) +. +.IP "\(bu" 4 +34 (blue) +. +.IP "\(bu" 4 +35 (magenta) +. +.IP "\(bu" 4 +36 (cyan) +. +.IP "\(bu" 4 +37 (white) +. +.IP "" 0 +. .SH "BUGS" Presumably\. Report them or discuss them at: . diff --git a/src/jq.h b/src/jq.h index 3067d8ae..5269de3f 100644 --- a/src/jq.h +++ b/src/jq.h @@ -66,4 +66,6 @@ jv jq_util_input_get_position(jq_state*); jv jq_util_input_get_current_filename(jq_state*); jv jq_util_input_get_current_line(jq_state*); +int jq_set_colors(const char *); + #endif /* !JQ_H */ diff --git a/src/jv_print.c b/src/jv_print.c index 3fcf8378..5ebc01e6 100644 --- a/src/jv_print.c +++ b/src/jv_print.c @@ -27,11 +27,46 @@ static const jv_kind color_kinds[] = {JV_KIND_NULL, JV_KIND_FALSE, JV_KIND_TRUE, JV_KIND_NUMBER, JV_KIND_STRING, JV_KIND_ARRAY, JV_KIND_OBJECT}; -static const char* const colors[] = +static char color_bufs[sizeof(color_kinds)/sizeof(color_kinds[0])][16]; +static const char *color_bufps[8]; +static const char* def_colors[] = {COL("1;30"), COL("0;39"), COL("0;39"), COL("0;39"), COL("0;32"), COL("1;39"), COL("1;39")}; #define FIELD_COLOR COL("34;1") +static const char **colors = def_colors; + +int +jq_set_colors(const char *c) +{ + const char *e; + size_t i; + + if (c == NULL) + return 1; + colors = def_colors; + memset(color_bufs, 0, sizeof(color_bufs)); + for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]); i++) + color_bufps[i] = def_colors[i]; + for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]) && *c != '\0'; i++, c = e) { + if ((e = strchr(c, ':')) == NULL) + e = c + strlen(c); + if ((size_t)(e - c) > sizeof(color_bufs[i]) - 4 /* ESC [ m NUL */) + return 0; + color_bufs[i][0] = ESC[0]; + color_bufs[i][1] = '['; + (void) strncpy(&color_bufs[i][2], c, e - c); + if (strspn(&color_bufs[i][2], "0123456789;") < strlen(&color_bufs[i][2])) + return 0; + color_bufs[i][2 + (e - c)] = 'm'; + color_bufps[i] = color_bufs[i]; + if (e[0] == ':') + e++; + } + colors = color_bufps; + return 1; +} + static void put_buf(const char *s, int len, FILE *fout, jv *strout, int is_tty) { if (strout) { *strout = jv_string_append_buf(*strout, s, len); diff --git a/src/main.c b/src/main.c index 3cbf3152..a7f50b51 100644 --- a/src/main.c +++ b/src/main.c @@ -525,6 +525,9 @@ int main(int argc, char* argv[]) { if (options & COLOR_OUTPUT) dumpopts |= JV_PRINT_COLOR; if (options & NO_COLOR_OUTPUT) dumpopts &= ~JV_PRINT_COLOR; + if (getenv("JQ_COLORS") != NULL && !jq_set_colors(getenv("JQ_COLORS"))) + fprintf(stderr, "Failed to set $JQ_COLORS\n"); + if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) { // Default search path list lib_search_paths = JV_ARRAY(jv_string("~/.jq"), diff --git a/tests/shtest b/tests/shtest index cec1fc59..af1b159e 100755 --- a/tests/shtest +++ b/tests/shtest @@ -244,4 +244,58 @@ if [ "`$VALGRIND $Q $JQ -n '"xyz\n"|halt_error(1)' 2>&1`" != xyz ]; then exit 1 fi +# Check $JQ_COLORS +$JQ -Ccn . > $d/color +printf '\033[1;30mnull\033[0m\n' > $d/expect +cmp $d/color $d/expect +JQ_COLORS='4;31' $JQ -Ccn . > $d/color +printf '\033[4;31mnull\033[0m\n' > $d/expect +cmp $d/color $d/expect +JQ_COLORS='1;30:0;31:0;32:0;33:0;34:1;35:1;36' \ + $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color +( +printf '\033[1;35m[\033[1;36m{' +printf '\033[0m\033[34;1m"a"\033[' +printf '0m\033[1;36m:\033[0m\033[' +printf '0;32mtrue\033[0m\033[1' +printf ';36m,\033[0m\033[34;1m' +printf '"b"\033[0m\033[1;36m:\033' +printf '[0m\033[0;31mfalse\033' +printf '[0m\033[1;36m\033[1;36' +printf 'm}\033[0m\033[1;35m,\033[' +printf '0;33m123\033[0m\033[1;' +printf '35m,\033[1;30mnull\033' +printf '[0m\033[1;35m\033[1;35' +printf 'm]\033[0m\n' +) > $d/expect +cmp $d/color $d/expect + +# Check garbage in JQ_COLORS. We write each color sequence into a 16 +# char buffer that needs to hold ESC [ m NUL, so each color +# sequence can be no more than 12 chars (bytes). These emit a warning +# on stderr. +set -vx +echo 'Failed to set $JQ_COLORS' > $d/expect_warning +$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/expect +JQ_COLORS='garbage;30:*;31:,;3^:0;$%:0;34:1;35:1;36' \ + $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning +cmp $d/color $d/expect +cmp $d/warning $d/expect_warning +JQ_COLORS='1234567890123456789;30:0;31:0;32:0;33:0;34:1;35:1;36' \ + $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning +cmp $d/color $d/expect +cmp $d/warning $d/expect_warning +JQ_COLORS='1;31234567890123456789:0;31:0;32:0;33:0;34:1;35:1;36' \ + $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning +cmp $d/color $d/expect +cmp $d/warning $d/expect_warning +JQ_COLORS='1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456' \ + $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning +cmp $d/color $d/expect +cmp $d/warning $d/expect_warning +JQ_COLORS="0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:" \ + $JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning +cmp $d/color $d/expect +cmp $d/warning $d/expect_warning + exit 0 -- cgit v1.2.3