Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <string.h> | ||
2 | #include "style.h" | ||
3 | #include "color.h" | ||
4 | #include "util/array.h" | ||
5 | #include "util/ascii.h" | ||
6 | #include "util/debug.h" | ||
7 | #include "util/numtostr.h" | ||
8 | #include "util/str-util.h" | ||
9 | #include "util/strtonum.h" | ||
10 | #include "util/xmalloc.h" | ||
11 | |||
12 | typedef struct { | ||
13 | char name[15]; | ||
14 | uint8_t len; | ||
15 | } ShortStr; | ||
16 | |||
17 | #define S(str) {str, STRLEN(str)} | ||
18 | |||
19 | static const ShortStr attr_names[] = { | ||
20 | S("keep"), | ||
21 | S("underline"), | ||
22 | S("reverse"), | ||
23 | S("blink"), | ||
24 | S("dim"), | ||
25 | S("bold"), | ||
26 | S("invisible"), | ||
27 | S("italic"), | ||
28 | S("strikethrough"), | ||
29 | }; | ||
30 | |||
31 | static const ShortStr color_names[] = { | ||
32 | S("keep"), | ||
33 | S("default"), | ||
34 | S("black"), | ||
35 | S("red"), | ||
36 | S("green"), | ||
37 | S("yellow"), | ||
38 | S("blue"), | ||
39 | S("magenta"), | ||
40 | S("cyan"), | ||
41 | S("gray"), | ||
42 | S("darkgray"), | ||
43 | S("lightred"), | ||
44 | S("lightgreen"), | ||
45 | S("lightyellow"), | ||
46 | S("lightblue"), | ||
47 | S("lightmagenta"), | ||
48 | S("lightcyan"), | ||
49 | S("white"), | ||
50 | }; | ||
51 | |||
52 | 18 | UNITTEST { | |
53 | 18 | CHECK_STRUCT_ARRAY(attr_names, name); | |
54 | 18 | CHECK_STRUCT_ARRAY(color_names, name); | |
55 | 18 | } | |
56 | |||
57 | 292 | static unsigned int lookup_attr(const char *str) | |
58 | { | ||
59 |
2/2✓ Branch 0 (6→3) taken 1531 times.
✓ Branch 1 (6→7) taken 23 times.
|
1554 | for (size_t i = 0; i < ARRAYLEN(attr_names); i++) { |
60 |
2/2✓ Branch 0 (3→4) taken 269 times.
✓ Branch 1 (3→5) taken 1262 times.
|
1531 | if (streq(str, attr_names[i].name)) { |
61 | 269 | return 1U << i; | |
62 | } | ||
63 | } | ||
64 |
2/2✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→9) taken 22 times.
|
23 | if (streq(str, "lowintensity")) { |
65 | 1 | return ATTR_DIM; | |
66 | } | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | 800 | static int32_t lookup_color(const char *str) | |
71 | { | ||
72 | 800 | return SSTR_TO_ENUM_WITH_OFFSET(str, color_names, name, COLOR_INVALID, -2); | |
73 | } | ||
74 | |||
75 | 852 | static int32_t parse_color(const char *str) | |
76 | { | ||
77 | 852 | size_t len = strlen(str); | |
78 |
2/2✓ Branch 0 (2→3) taken 850 times.
✓ Branch 1 (2→21) taken 2 times.
|
852 | if (unlikely(len == 0)) { |
79 | return COLOR_INVALID; | ||
80 | } | ||
81 | |||
82 | // Parse #rgb or #rrggbb | ||
83 |
2/2✓ Branch 0 (3→4) taken 12 times.
✓ Branch 1 (3→5) taken 838 times.
|
850 | if (str[0] == '#') { |
84 | 12 | return parse_rgb(str + 1, len - 1); | |
85 | } | ||
86 | |||
87 | // Parse r/g/b | ||
88 |
4/4✓ Branch 0 (5→6) taken 97 times.
✓ Branch 1 (5→11) taken 741 times.
✓ Branch 2 (6→7) taken 10 times.
✓ Branch 3 (6→11) taken 87 times.
|
838 | if (len == 5 && str[1] == '/') { |
89 | 10 | const unsigned char *u_str = str; | |
90 | 10 | uint8_t r = u_str[0] - '0'; | |
91 | 10 | uint8_t g = u_str[2] - '0'; | |
92 | 10 | uint8_t b = u_str[4] - '0'; | |
93 |
5/6✓ Branch 0 (7→8) taken 8 times.
✓ Branch 1 (7→21) taken 2 times.
✓ Branch 2 (8→9) taken 6 times.
✓ Branch 3 (8→21) taken 2 times.
✓ Branch 4 (9→10) taken 6 times.
✗ Branch 5 (9→21) not taken.
|
10 | if (unlikely(r > 5 || g > 5 || b > 5 || str[3] != '/')) { |
94 | return COLOR_INVALID; | ||
95 | } | ||
96 | // Convert to color index 16..231 (xterm 6x6x6 color cube) | ||
97 | 6 | return 16 + (r * 36) + (g * 6) + b; | |
98 | } | ||
99 | |||
100 | // Parse -2 .. 255 | ||
101 |
6/6✓ Branch 0 (11→12) taken 92 times.
✓ Branch 1 (11→20) taken 736 times.
✓ Branch 2 (12→13) taken 89 times.
✓ Branch 3 (12→14) taken 3 times.
✓ Branch 4 (13→14) taken 25 times.
✓ Branch 5 (13→20) taken 64 times.
|
828 | if (len <= 3 && (str[0] == '-' || ascii_isdigit(str[0]))) { |
102 | 28 | int x; | |
103 |
5/6✓ Branch 0 (15→16) taken 28 times.
✗ Branch 1 (15→19) not taken.
✓ Branch 2 (16→17) taken 27 times.
✓ Branch 3 (16→19) taken 1 times.
✓ Branch 4 (17→18) taken 26 times.
✓ Branch 5 (17→19) taken 1 times.
|
28 | if (unlikely(!str_to_int(str, &x) || x < -2 || x > 255)) { |
104 | return COLOR_INVALID; | ||
105 | } | ||
106 | 26 | return x; | |
107 | } | ||
108 | |||
109 | 800 | return lookup_color(str); | |
110 | } | ||
111 | |||
112 | // Note: this function returns the number of valid strings parsed, or -1 if | ||
113 | // more than 2 valid colors were encountered. Thus, success is indicated by | ||
114 | // a return value equal to `nstrs`. | ||
115 | 599 | ssize_t parse_term_style(TermStyle *style, char **strs, size_t nstrs) | |
116 | { | ||
117 | 599 | int32_t colors[2] = {COLOR_DEFAULT, COLOR_DEFAULT}; | |
118 | 599 | unsigned int attrs = 0; | |
119 | 599 | size_t i = 0; | |
120 | |||
121 |
2/2✓ Branch 0 (13→3) taken 852 times.
✓ Branch 1 (13→14) taken 575 times.
|
1427 | for (size_t nr_colors = 0; i < nstrs; i++) { |
122 | 852 | const char *str = strs[i]; | |
123 | 852 | int32_t c = parse_color(str); | |
124 |
2/2✓ Branch 0 (4→5) taken 292 times.
✓ Branch 1 (4→8) taken 560 times.
|
852 | if (c == COLOR_INVALID) { |
125 | 292 | unsigned int attr = lookup_attr(str); | |
126 |
2/2✓ Branch 0 (5→6) taken 270 times.
✓ Branch 1 (5→7) taken 22 times.
|
292 | if (likely(attr)) { |
127 | 270 | attrs |= attr; | |
128 | 270 | continue; | |
129 | } | ||
130 | // Invalid color or attribute | ||
131 | 22 | return i; | |
132 | } | ||
133 |
2/2✓ Branch 0 (8→9) taken 29 times.
✓ Branch 1 (8→11) taken 531 times.
|
560 | if (nr_colors == ARRAYLEN(colors)) { |
134 |
2/2✓ Branch 0 (9→10) taken 27 times.
✓ Branch 1 (9→15) taken 2 times.
|
29 | if (likely(c == COLOR_KEEP)) { |
135 | // "keep" is also a valid attribute | ||
136 | 27 | attrs |= ATTR_KEEP; | |
137 | 27 | continue; | |
138 | } | ||
139 | // Too many colors | ||
140 | return -1; | ||
141 | } | ||
142 | 531 | colors[nr_colors++] = c; | |
143 | } | ||
144 | |||
145 | 575 | *style = (TermStyle) { | |
146 | 575 | .fg = colors[0], | |
147 | 575 | .bg = colors[1], | |
148 | .attr = attrs | ||
149 | }; | ||
150 | 575 | return i; | |
151 | } | ||
152 | |||
153 | 1 | void collect_colors_and_attributes(PointerArray *a, const char *prefix) | |
154 | { | ||
155 | 1 | size_t prefix_len = strlen(prefix); | |
156 |
2/2✓ Branch 0 (7→3) taken 17 times.
✓ Branch 1 (7→12) taken 1 times.
|
18 | for (size_t i = 1; i < ARRAYLEN(color_names); i++) { |
157 | 17 | const ShortStr *s = &color_names[i]; | |
158 |
1/2✓ Branch 0 (3→4) taken 17 times.
✗ Branch 1 (3→6) not taken.
|
17 | if (str_has_strn_prefix(s->name, prefix, prefix_len)) { |
159 | 17 | ptr_array_append(a, xmemdup(s->name, s->len + 1)); | |
160 | } | ||
161 | } | ||
162 |
2/2✓ Branch 0 (12→8) taken 9 times.
✓ Branch 1 (12→13) taken 1 times.
|
10 | for (size_t i = 0; i < ARRAYLEN(attr_names); i++) { |
163 | 9 | const ShortStr *s = &attr_names[i]; | |
164 |
1/2✓ Branch 0 (8→9) taken 9 times.
✗ Branch 1 (8→11) not taken.
|
9 | if (str_has_strn_prefix(s->name, prefix, prefix_len)) { |
165 | 9 | ptr_array_append(a, xmemdup(s->name, s->len + 1)); | |
166 | } | ||
167 | } | ||
168 | 1 | } | |
169 | |||
170 | 113 | size_t color_to_str(char buf[COLOR_STR_BUFSIZE], int32_t color) | |
171 | { | ||
172 | 113 | BUG_ON(!color_is_valid(color)); | |
173 |
2/2✓ Branch 0 (4→5) taken 94 times.
✓ Branch 1 (4→6) taken 19 times.
|
113 | if (color < 16) { |
174 | 94 | const ShortStr *s = &color_names[color + 2]; | |
175 | 94 | static_assert(sizeof(s->name) <= COLOR_STR_BUFSIZE); | |
176 | 94 | memcpy(buf, s->name, sizeof(s->name)); | |
177 | 94 | return s->len; | |
178 | } | ||
179 | |||
180 |
2/2✓ Branch 0 (6→7) taken 9 times.
✓ Branch 1 (6→8) taken 10 times.
|
19 | if (color < 256) { |
181 | 9 | return buf_u8_to_str((uint8_t)color, buf); | |
182 | } | ||
183 | |||
184 | 10 | size_t i = 0; | |
185 | 10 | buf[i++] = '#'; | |
186 | 10 | i += hex_encode_byte(buf + i, color_r(color)); | |
187 | 10 | i += hex_encode_byte(buf + i, color_g(color)); | |
188 | 10 | i += hex_encode_byte(buf + i, color_b(color)); | |
189 | 10 | BUG_ON(i != 7); | |
190 | return i; | ||
191 | } | ||
192 | |||
193 | 71 | const char *term_style_to_string(char buf[TERM_STYLE_BUFSIZE], const TermStyle *style) | |
194 | { | ||
195 | 71 | size_t pos = color_to_str(buf, style->fg); | |
196 | |||
197 |
4/4✓ Branch 0 (3→4) taken 51 times.
✓ Branch 1 (3→5) taken 20 times.
✓ Branch 2 (4→5) taken 1 times.
✓ Branch 3 (4→12) taken 50 times.
|
71 | if (style->bg != COLOR_DEFAULT || (style->attr & ATTR_KEEP) != 0) { |
198 | 21 | buf[pos++] = ' '; | |
199 | 21 | pos += color_to_str(buf + pos, style->bg); | |
200 | } | ||
201 | |||
202 |
2/2✓ Branch 0 (13→7) taken 639 times.
✓ Branch 1 (13→14) taken 71 times.
|
710 | for (size_t i = 0; i < ARRAYLEN(attr_names); i++) { |
203 |
2/2✓ Branch 0 (7→8) taken 42 times.
✓ Branch 1 (7→11) taken 597 times.
|
639 | if (style->attr & (1U << i)) { |
204 | 42 | const ShortStr *s = &attr_names[i]; | |
205 | 42 | BUG_ON(pos + 2 + sizeof(s->name) >= TERM_STYLE_BUFSIZE); | |
206 | 42 | buf[pos++] = ' '; | |
207 | 42 | memcpy(buf + pos, s->name, sizeof(s->name)); | |
208 | 42 | pos += s->len; | |
209 | } | ||
210 | } | ||
211 | |||
212 | 71 | buf[pos] = '\0'; | |
213 | 71 | return buf; | |
214 | } | ||
215 |