dte test coverage


Directory: ./
File: src/terminal/style.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 91 91 100.0%
Functions: 8 8 100.0%
Branches: 58 62 93.5%

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