Skip to content

Commit

Permalink
Support true color when setting colors from JQ_COLORS
Browse files Browse the repository at this point in the history
The buffers for colors were fixed-size and we cannot use longer color
definitions like true color, e.g. `JQ_COLORS='38;2;135;200;250'`, or
both foreground and background colors, e.g. `JQ_COLORS='38;5;81;48;5;19'`.
This commit fixes the implementation to support longer color definitions
by allocating the buffers dynamically. This resolves #2426.
  • Loading branch information
itchyny committed Mar 6, 2025
1 parent 668871b commit 44ab720
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 50 deletions.
49 changes: 24 additions & 25 deletions src/jv_print.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,43 +27,42 @@
// Color table. See https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
// for how to choose these. The order is same as jv_kind definition, and
// the last color is used for object keys.
static char color_bufs[8][16];
static const char *color_bufps[8];
static const char *const def_colors[] =
static const char *const default_colors[] =
{COL("0;90"), COL("0;39"), COL("0;39"), COL("0;39"),
COL("0;32"), COL("1;39"), COL("1;39"), COL("1;34")};
#define FIELD_COLOR (colors[7])

static const char *const *colors = def_colors;
static const char *const *colors = default_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]))

static const char *color_bufs[8];
for (int i = 0; i < 8; i++)
color_bufs[i] = default_colors[i];

const char *e;
for (int i = 0; i < 8 && *c != '\0'; i++, c = e) {
size_t l;
if ((e = strchr(c, ':')) != NULL)
l = e++ - c;
else
e = c + (l = strlen(c));
char *color_buf = jv_mem_alloc(l + 4);
color_buf[0] = ESC[0];
color_buf[1] = '[';
strncpy(&color_buf[2], c, l);
color_buf[2 + l] = 'm';
color_buf[2 + l + 1] = '\0';
if (strspn(&color_buf[2], "0123456789;") < l)
return 0;
color_bufs[i][2 + (e - c)] = 'm';
color_bufps[i] = color_bufs[i];
if (e[0] == ':')
e++;
color_bufs[i] = color_buf;
}
colors = color_bufps;

colors = color_bufs;
return 1;
}

Expand Down
82 changes: 57 additions & 25 deletions tests/shtest
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,30 @@ JQ_COLORS='0;30:0;31:0;32:0;33:0;34:1;35:1;36:1;37' \
} > $d/expect
cmp $d/color $d/expect

## Set non-default colors only for literals, complex input
JQ_COLORS='0;37:0;31:0;35:0;34:0;36' \
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color
{
printf '\033[1;39m[\033[0m'
printf '\033[1;39m{\033[0m'
printf '\033[1;34m"a"\033[0m'
printf '\033[1;39m:\033[0m'
printf '\033[0;35mtrue\033[0m'
printf '\033[1;39m,\033[0m'
printf '\033[1;34m"b"\033[0m'
printf '\033[1;39m:\033[0m'
printf '\033[0;31mfalse\033[0m'
printf '\033[1;39m}\033[0m'
printf '\033[1;39m,\033[0m'
printf '\033[0;36m"abc"\033[0m'
printf '\033[1;39m,\033[0m'
printf '\033[0;34m123\033[0m'
printf '\033[1;39m,\033[0m'
printf '\033[0;37mnull\033[0m'
printf '\033[1;39m]\033[0m\n'
} > $d/expect
cmp $d/color $d/expect

## Default colors, complex input, indented
$JQ -Cn '[{"a":true,"b":false},"abc",123,null]' > $d/color
{
Expand Down Expand Up @@ -527,33 +551,41 @@ JQ_COLORS='0;30:0;31:0;32:0;33:0;34:1;35:1;36:1;37' \
} > $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 [ <color> m NUL, so each color
# sequence can be no more than 12 chars (bytes). These emit a warning
# on stderr.
set -vx
## Set true color, complex input, indented
JQ_COLORS=$(printf '38;2;%s:' \
'255;173;173' '255;214;165' '253;255;182' '202;255;191' \
'155;246;255' '160;196;255' '189;178;255' '255;198;255') \
$JQ -Cn '[{"a":true,"b":false},"abc",123,null]' > $d/color
{
printf '\033[38;2;160;196;255m[\033[0m\n'
printf ' \033[38;2;189;178;255m{\033[0m\n'
printf ' \033[38;2;255;198;255m"a"\033[0m'
printf '\033[38;2;189;178;255m:\033[0m '
printf '\033[38;2;253;255;182mtrue\033[0m'
printf '\033[38;2;189;178;255m,\033[0m\n'
printf ' \033[38;2;255;198;255m"b"\033[0m'
printf '\033[38;2;189;178;255m:\033[0m '
printf '\033[38;2;255;214;165mfalse\033[0m\n'
printf ' \033[38;2;189;178;255m}\033[0m'
printf '\033[38;2;160;196;255m,\033[0m\n'
printf ' \033[38;2;155;246;255m"abc"\033[0m'
printf '\033[38;2;160;196;255m,\033[0m\n'
printf ' \033[38;2;202;255;191m123\033[0m'
printf '\033[38;2;160;196;255m,\033[0m\n'
printf ' \033[38;2;255;173;173mnull\033[0m\n'
printf '\033[38;2;160;196;255m]\033[0m\n'
} > $d/expect
cmp $d/color $d/expect

# Check invalid JQ_COLORS
echo 'Failed to set $JQ_COLORS' > $d/expect_warning
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/expect
JQ_COLORS='garbage;30:*;31:,;3^:0;$%:0;34:1;35:1;36' \
$JQ -Ccn '[{"a":true,"b":false},"abc",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},"abc",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},"abc",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},"abc",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},"abc",123,null]' > $d/color 2>$d/warning
cmp $d/color $d/expect
cmp $d/warning $d/expect_warning
for colors in '/' '[30' '30m' '30:31m:32' '30:*:31' 'invalid'; do
JQ_COLORS=$colors \
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color 2>$d/warning
cmp $d/color $d/expect
cmp $d/warning $d/expect_warning
done

# Check $NO_COLOR
test_no_color=true
Expand Down

0 comments on commit 44ab720

Please sign in to comment.