Skip to content

Commit cb994c7

Browse files
committed
dynamically allocate JQ_COLORS escapes
valid color escapes can be over 100 characters long, so a static buffer isn't a good option. resolves #2426
1 parent c70ae1a commit cb994c7

File tree

2 files changed

+69
-50
lines changed

2 files changed

+69
-50
lines changed

src/jv_print.c

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,43 +27,71 @@
2727
// Color table. See https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
2828
// for how to choose these. The order is same as jv_kind definition, and
2929
// the last color is used for object keys.
30-
static char color_bufs[8][16];
31-
static const char *color_bufps[8];
32-
static const char *const def_colors[] =
33-
{COL("0;90"), COL("0;39"), COL("0;39"), COL("0;39"),
30+
#define DEF_COLORS \
31+
{COL("0;90"), COL("0;39"), COL("0;39"), COL("0;39"),\
3432
COL("0;32"), COL("1;39"), COL("1;39"), COL("1;34")};
33+
static const char *const def_colors[] = DEF_COLORS;
34+
static const char *colors[] = DEF_COLORS;
35+
#define COLORS_LEN (sizeof(colors) / sizeof(colors[0]))
3536
#define FIELD_COLOR (colors[7])
3637

37-
static const char *const *colors = def_colors;
38-
39-
int
40-
jq_set_colors(const char *c)
41-
{
42-
const char *e;
43-
size_t i;
44-
38+
static char *colors_buf = NULL;
39+
int jq_set_colors(const char *c) {
4540
if (c == NULL)
4641
return 1;
47-
colors = def_colors;
48-
memset(color_bufs, 0, sizeof(color_bufs));
49-
for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]); i++)
50-
color_bufps[i] = def_colors[i];
51-
for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]) && *c != '\0'; i++, c = e) {
52-
if ((e = strchr(c, ':')) == NULL)
53-
e = c + strlen(c);
54-
if ((size_t)(e - c) > sizeof(color_bufs[i]) - 4 /* ESC [ m NUL */)
55-
return 0;
56-
color_bufs[i][0] = ESC[0];
57-
color_bufs[i][1] = '[';
58-
(void) strncpy(&color_bufs[i][2], c, e - c);
59-
if (strspn(&color_bufs[i][2], "0123456789;") < strlen(&color_bufs[i][2]))
42+
const char *offsets[COLORS_LEN + 1]; // extra item for the end of the last string
43+
44+
size_t cl = 0;
45+
size_t cn = 0;
46+
47+
while (cl < COLORS_LEN) {
48+
offsets[cl++] = c;
49+
// gcc won't optimize out strspn
50+
letter:
51+
switch (c++[0]) {
52+
// technically posix doesn't specify ascii so a range wouldn't be portable
53+
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ';':
54+
goto letter;
55+
case ':':
56+
continue;
57+
case '\0':
58+
goto var_end;
59+
default:
6060
return 0;
61-
color_bufs[i][2 + (e - c)] = 'm';
62-
color_bufps[i] = color_bufs[i];
63-
if (e[0] == ':')
64-
e++;
61+
}
62+
}
63+
var_end:
64+
// don't override last color on empty variable or trailing :
65+
if (offsets[--cl] != c - 1) {
66+
offsets[++cl] = c;
67+
} else if (cl == 0) {
68+
if (colors_buf != NULL) {
69+
jv_mem_free(colors_buf);
70+
colors_buf = NULL;
71+
}
72+
goto reset;
73+
}
74+
75+
colors_buf = jv_mem_realloc(
76+
colors_buf,
77+
// add ESC '[' 'm' to each string
78+
// '\0' is already included in difference of offsets
79+
offsets[cl] - offsets[0] + 3 * cl
80+
);
81+
char *cb = colors_buf;
82+
for (; cn < cl; cn++) {
83+
colors[cn] = cb;
84+
cb[0] = ESC[0];
85+
cb[1] = '[';
86+
size_t len = offsets[cn + 1] - 1 - offsets[cn];
87+
memcpy(cb + 2, offsets[cn], len);
88+
cb[len + 2] = 'm';
89+
cb[len + 3] = '\0';
90+
cb += len + 4;
6591
}
66-
colors = color_bufps;
92+
reset:
93+
for (; cn < COLORS_LEN; cn++)
94+
colors[cn] = def_colors[cn];
6795
return 1;
6896
}
6997

tests/shtest

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,16 @@ JQ_COLORS='4;31' $JQ -Ccn . > $d/color
433433
printf '\033[4;31mnull\033[0m\n' > $d/expect
434434
cmp $d/color $d/expect
435435

436+
## Set implicit empty color, null input
437+
$JQ -Ccn . > $d/color
438+
printf '\033[0;90mnull\033[0m\n' > $d/expect
439+
cmp $d/color $d/expect
440+
441+
## Set explicit empty color, null input
442+
JQ_COLORS=':' $JQ -Ccn . > $d/color
443+
printf '\033[mnull\033[0m\n' > $d/expect
444+
cmp $d/color $d/expect
445+
436446
## Default colors, complex input
437447
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color
438448
{
@@ -527,33 +537,14 @@ JQ_COLORS='0;30:0;31:0;32:0;33:0;34:1;35:1;36:1;37' \
527537
} > $d/expect
528538
cmp $d/color $d/expect
529539

530-
# Check garbage in JQ_COLORS. We write each color sequence into a 16
531-
# char buffer that needs to hold ESC [ <color> m NUL, so each color
532-
# sequence can be no more than 12 chars (bytes). These emit a warning
533-
# on stderr.
540+
# Check garbage in JQ_COLORS. These emit a warning on stderr.
534541
set -vx
535542
echo 'Failed to set $JQ_COLORS' > $d/expect_warning
536543
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/expect
537544
JQ_COLORS='garbage;30:*;31:,;3^:0;$%:0;34:1;35:1;36' \
538545
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color 2>$d/warning
539546
cmp $d/color $d/expect
540547
cmp $d/warning $d/expect_warning
541-
JQ_COLORS='1234567890123456789;30:0;31:0;32:0;33:0;34:1;35:1;36' \
542-
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color 2>$d/warning
543-
cmp $d/color $d/expect
544-
cmp $d/warning $d/expect_warning
545-
JQ_COLORS='1;31234567890123456789:0;31:0;32:0;33:0;34:1;35:1;36' \
546-
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color 2>$d/warning
547-
cmp $d/color $d/expect
548-
cmp $d/warning $d/expect_warning
549-
JQ_COLORS='1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456' \
550-
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color 2>$d/warning
551-
cmp $d/color $d/expect
552-
cmp $d/warning $d/expect_warning
553-
JQ_COLORS="0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:" \
554-
$JQ -Ccn '[{"a":true,"b":false},"abc",123,null]' > $d/color 2>$d/warning
555-
cmp $d/color $d/expect
556-
cmp $d/warning $d/expect_warning
557548

558549
# Check $NO_COLOR
559550
test_no_color=true

0 commit comments

Comments
 (0)