Line | Count | Source |
1 | | #define DISABLE_SIGN_COMPARE_WARNINGS |
2 | | |
3 | | #include "git-compat-util.h" |
4 | | #include "config.h" |
5 | | #include "color.h" |
6 | | #include "editor.h" |
7 | | #include "gettext.h" |
8 | | #include "hex-ll.h" |
9 | | #include "pager.h" |
10 | | #include "strbuf.h" |
11 | | |
12 | | static enum git_colorbool git_use_color_default = GIT_COLOR_AUTO; |
13 | | int color_stdout_is_tty = -1; |
14 | | |
15 | | /* |
16 | | * The list of available column colors. |
17 | | */ |
18 | | const char *column_colors_ansi[] = { |
19 | | GIT_COLOR_RED, |
20 | | GIT_COLOR_GREEN, |
21 | | GIT_COLOR_YELLOW, |
22 | | GIT_COLOR_BLUE, |
23 | | GIT_COLOR_MAGENTA, |
24 | | GIT_COLOR_CYAN, |
25 | | GIT_COLOR_BOLD_RED, |
26 | | GIT_COLOR_BOLD_GREEN, |
27 | | GIT_COLOR_BOLD_YELLOW, |
28 | | GIT_COLOR_BOLD_BLUE, |
29 | | GIT_COLOR_BOLD_MAGENTA, |
30 | | GIT_COLOR_BOLD_CYAN, |
31 | | GIT_COLOR_RESET, |
32 | | }; |
33 | | |
34 | | enum { |
35 | | COLOR_BACKGROUND_OFFSET = 10, |
36 | | COLOR_FOREGROUND_ANSI = 30, |
37 | | COLOR_FOREGROUND_RGB = 38, |
38 | | COLOR_FOREGROUND_256 = 38, |
39 | | COLOR_FOREGROUND_BRIGHT_ANSI = 90, |
40 | | }; |
41 | | |
42 | | /* Ignore the RESET at the end when giving the size */ |
43 | | const int column_colors_ansi_max = ARRAY_SIZE(column_colors_ansi) - 1; |
44 | | |
45 | | /* An individual foreground or background color. */ |
46 | | struct color { |
47 | | enum { |
48 | | COLOR_UNSPECIFIED = 0, |
49 | | COLOR_NORMAL, |
50 | | COLOR_ANSI, /* basic 0-7 ANSI colors + "default" (value = 9) */ |
51 | | COLOR_256, |
52 | | COLOR_RGB |
53 | | } type; |
54 | | /* The numeric value for ANSI and 256-color modes */ |
55 | | unsigned char value; |
56 | | /* 24-bit RGB color values */ |
57 | | unsigned char red, green, blue; |
58 | | }; |
59 | | |
60 | | /* |
61 | | * "word" is a buffer of length "len"; does it match the NUL-terminated |
62 | | * "match" exactly? |
63 | | */ |
64 | | static int match_word(const char *word, int len, const char *match) |
65 | 0 | { |
66 | 0 | return !strncasecmp(word, match, len) && !match[len]; |
67 | 0 | } |
68 | | |
69 | | static int get_hex_color(const char **inp, int width, unsigned char *out) |
70 | 0 | { |
71 | 0 | const char *in = *inp; |
72 | 0 | unsigned int val; |
73 | |
|
74 | 0 | assert(width == 1 || width == 2); |
75 | 0 | val = (hexval(in[0]) << 4) | hexval(in[width - 1]); |
76 | 0 | if (val & ~0xff) |
77 | 0 | return -1; |
78 | 0 | *inp += width; |
79 | 0 | *out = val; |
80 | 0 | return 0; |
81 | 0 | } |
82 | | |
83 | | /* |
84 | | * If an ANSI color is recognized in "name", fill "out" and return 0. |
85 | | * Otherwise, leave out unchanged and return -1. |
86 | | */ |
87 | | static int parse_ansi_color(struct color *out, const char *name, int len) |
88 | 0 | { |
89 | | /* Positions in array must match ANSI color codes */ |
90 | 0 | static const char * const color_names[] = { |
91 | 0 | "black", "red", "green", "yellow", |
92 | 0 | "blue", "magenta", "cyan", "white" |
93 | 0 | }; |
94 | 0 | int i; |
95 | 0 | int color_offset = COLOR_FOREGROUND_ANSI; |
96 | |
|
97 | 0 | if (match_word(name, len, "default")) { |
98 | | /* |
99 | | * Restores to the terminal's default color, which may not be |
100 | | * the same as explicitly setting "white" or "black". |
101 | | * |
102 | | * ECMA-48 - Control Functions \ |
103 | | * for Coded Character Sets, 5th edition (June 1991): |
104 | | * > 39 default display colour (implementation-defined) |
105 | | * > 49 default background colour (implementation-defined) |
106 | | * |
107 | | * Although not supported /everywhere/--according to terminfo, |
108 | | * some terminals define "op" (original pair) as a blunt |
109 | | * "set to white on black", or even "send full SGR reset"-- |
110 | | * it's standard and well-supported enough that if a user |
111 | | * asks for it in their config this will do the right thing. |
112 | | */ |
113 | 0 | out->type = COLOR_ANSI; |
114 | 0 | out->value = 9 + color_offset; |
115 | 0 | return 0; |
116 | 0 | } |
117 | | |
118 | 0 | if (strncasecmp(name, "bright", 6) == 0) { |
119 | 0 | color_offset = COLOR_FOREGROUND_BRIGHT_ANSI; |
120 | 0 | name += 6; |
121 | 0 | len -= 6; |
122 | 0 | } |
123 | 0 | for (i = 0; i < ARRAY_SIZE(color_names); i++) { |
124 | 0 | if (match_word(name, len, color_names[i])) { |
125 | 0 | out->type = COLOR_ANSI; |
126 | 0 | out->value = i + color_offset; |
127 | 0 | return 0; |
128 | 0 | } |
129 | 0 | } |
130 | 0 | return -1; |
131 | 0 | } |
132 | | |
133 | | static int parse_color(struct color *out, const char *name, int len) |
134 | 0 | { |
135 | 0 | char *end; |
136 | 0 | long val; |
137 | | |
138 | | /* First try the special word "normal"... */ |
139 | 0 | if (match_word(name, len, "normal")) { |
140 | 0 | out->type = COLOR_NORMAL; |
141 | 0 | return 0; |
142 | 0 | } |
143 | | |
144 | | /* Try a 24- or 12-bit RGB value prefixed with '#' */ |
145 | 0 | if ((len == 7 || len == 4) && name[0] == '#') { |
146 | 0 | int width_per_color = (len == 7) ? 2 : 1; |
147 | 0 | const char *color = name + 1; |
148 | |
|
149 | 0 | if (!get_hex_color(&color, width_per_color, &out->red) && |
150 | 0 | !get_hex_color(&color, width_per_color, &out->green) && |
151 | 0 | !get_hex_color(&color, width_per_color, &out->blue)) { |
152 | 0 | out->type = COLOR_RGB; |
153 | 0 | return 0; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | /* Then pick from our human-readable color names... */ |
158 | 0 | if (parse_ansi_color(out, name, len) == 0) { |
159 | 0 | return 0; |
160 | 0 | } |
161 | | |
162 | | /* And finally try a literal 256-color-mode number */ |
163 | 0 | val = strtol(name, &end, 10); |
164 | 0 | if (end - name == len) { |
165 | | /* |
166 | | * Allow "-1" as an alias for "normal", but other negative |
167 | | * numbers are bogus. |
168 | | */ |
169 | 0 | if (val < -1) |
170 | 0 | ; /* fall through to error */ |
171 | 0 | else if (val < 0) { |
172 | 0 | out->type = COLOR_NORMAL; |
173 | 0 | return 0; |
174 | | /* Rewrite 0-7 as more-portable standard colors. */ |
175 | 0 | } else if (val < 8) { |
176 | 0 | out->type = COLOR_ANSI; |
177 | 0 | out->value = val + COLOR_FOREGROUND_ANSI; |
178 | 0 | return 0; |
179 | | /* Rewrite 8-15 as more-portable aixterm colors. */ |
180 | 0 | } else if (val < 16) { |
181 | 0 | out->type = COLOR_ANSI; |
182 | 0 | out->value = val - 8 + COLOR_FOREGROUND_BRIGHT_ANSI; |
183 | 0 | return 0; |
184 | 0 | } else if (val < 256) { |
185 | 0 | out->type = COLOR_256; |
186 | 0 | out->value = val; |
187 | 0 | return 0; |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | 0 | return -1; |
192 | 0 | } |
193 | | |
194 | | static int parse_attr(const char *name, size_t len) |
195 | 0 | { |
196 | 0 | static const struct { |
197 | 0 | const char *name; |
198 | 0 | size_t len; |
199 | 0 | int val, neg; |
200 | 0 | } attrs[] = { |
201 | 0 | #define ATTR(x, val, neg) { (x), sizeof(x)-1, (val), (neg) } |
202 | 0 | ATTR("bold", 1, 22), |
203 | 0 | ATTR("dim", 2, 22), |
204 | 0 | ATTR("italic", 3, 23), |
205 | 0 | ATTR("ul", 4, 24), |
206 | 0 | ATTR("blink", 5, 25), |
207 | 0 | ATTR("reverse", 7, 27), |
208 | 0 | ATTR("strike", 9, 29) |
209 | 0 | #undef ATTR |
210 | 0 | }; |
211 | 0 | int negate = 0; |
212 | 0 | int i; |
213 | |
|
214 | 0 | if (skip_prefix_mem(name, len, "no", &name, &len)) { |
215 | 0 | skip_prefix_mem(name, len, "-", &name, &len); |
216 | 0 | negate = 1; |
217 | 0 | } |
218 | |
|
219 | 0 | for (i = 0; i < ARRAY_SIZE(attrs); i++) { |
220 | 0 | if (attrs[i].len == len && !memcmp(attrs[i].name, name, len)) |
221 | 0 | return negate ? attrs[i].neg : attrs[i].val; |
222 | 0 | } |
223 | 0 | return -1; |
224 | 0 | } |
225 | | |
226 | | int color_parse(const char *value, char *dst) |
227 | 0 | { |
228 | 0 | return color_parse_mem(value, strlen(value), dst); |
229 | 0 | } |
230 | | |
231 | | /* |
232 | | * Write the ANSI color codes for "c" to "out"; the string should |
233 | | * already have the ANSI escape code in it. "out" should have enough |
234 | | * space in it to fit any color. |
235 | | */ |
236 | | static char *color_output(char *out, int len, const struct color *c, int background) |
237 | 0 | { |
238 | 0 | int offset = 0; |
239 | |
|
240 | 0 | if (background) |
241 | 0 | offset = COLOR_BACKGROUND_OFFSET; |
242 | 0 | switch (c->type) { |
243 | 0 | case COLOR_UNSPECIFIED: |
244 | 0 | case COLOR_NORMAL: |
245 | 0 | break; |
246 | 0 | case COLOR_ANSI: |
247 | 0 | out += xsnprintf(out, len, "%d", c->value + offset); |
248 | 0 | break; |
249 | 0 | case COLOR_256: |
250 | 0 | out += xsnprintf(out, len, "%d;5;%d", COLOR_FOREGROUND_256 + offset, |
251 | 0 | c->value); |
252 | 0 | break; |
253 | 0 | case COLOR_RGB: |
254 | 0 | out += xsnprintf(out, len, "%d;2;%d;%d;%d", |
255 | 0 | COLOR_FOREGROUND_RGB + offset, |
256 | 0 | c->red, c->green, c->blue); |
257 | 0 | break; |
258 | 0 | } |
259 | 0 | return out; |
260 | 0 | } |
261 | | |
262 | | static int color_empty(const struct color *c) |
263 | 0 | { |
264 | 0 | return c->type <= COLOR_NORMAL; |
265 | 0 | } |
266 | | |
267 | | int color_parse_mem(const char *value, int value_len, char *dst) |
268 | 0 | { |
269 | 0 | const char *ptr = value; |
270 | 0 | int len = value_len; |
271 | 0 | char *end = dst + COLOR_MAXLEN; |
272 | 0 | unsigned int has_reset = 0; |
273 | 0 | unsigned int attr = 0; |
274 | 0 | struct color fg = { COLOR_UNSPECIFIED }; |
275 | 0 | struct color bg = { COLOR_UNSPECIFIED }; |
276 | |
|
277 | 0 | while (len > 0 && isspace(*ptr)) { |
278 | 0 | ptr++; |
279 | 0 | len--; |
280 | 0 | } |
281 | |
|
282 | 0 | if (!len) { |
283 | 0 | dst[0] = '\0'; |
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | | /* [reset] [fg [bg]] [attr]... */ |
288 | 0 | while (len > 0) { |
289 | 0 | const char *word = ptr; |
290 | 0 | struct color c = { COLOR_UNSPECIFIED }; |
291 | 0 | int val, wordlen = 0; |
292 | |
|
293 | 0 | while (len > 0 && !isspace(word[wordlen])) { |
294 | 0 | wordlen++; |
295 | 0 | len--; |
296 | 0 | } |
297 | |
|
298 | 0 | ptr = word + wordlen; |
299 | 0 | while (len > 0 && isspace(*ptr)) { |
300 | 0 | ptr++; |
301 | 0 | len--; |
302 | 0 | } |
303 | |
|
304 | 0 | if (match_word(word, wordlen, "reset")) { |
305 | 0 | has_reset = 1; |
306 | 0 | continue; |
307 | 0 | } |
308 | | |
309 | 0 | if (!parse_color(&c, word, wordlen)) { |
310 | 0 | if (fg.type == COLOR_UNSPECIFIED) { |
311 | 0 | fg = c; |
312 | 0 | continue; |
313 | 0 | } |
314 | 0 | if (bg.type == COLOR_UNSPECIFIED) { |
315 | 0 | bg = c; |
316 | 0 | continue; |
317 | 0 | } |
318 | 0 | goto bad; |
319 | 0 | } |
320 | 0 | val = parse_attr(word, wordlen); |
321 | 0 | if (0 <= val) |
322 | 0 | attr |= (1 << val); |
323 | 0 | else |
324 | 0 | goto bad; |
325 | 0 | } |
326 | | |
327 | 0 | #undef OUT |
328 | 0 | #define OUT(x) do { \ |
329 | 0 | if (dst == end) \ |
330 | 0 | BUG("color parsing ran out of space"); \ |
331 | 0 | *dst++ = (x); \ |
332 | 0 | } while(0) |
333 | | |
334 | 0 | if (has_reset || attr || !color_empty(&fg) || !color_empty(&bg)) { |
335 | 0 | int sep = 0; |
336 | 0 | int i; |
337 | |
|
338 | 0 | OUT('\033'); |
339 | 0 | OUT('['); |
340 | | |
341 | 0 | if (has_reset) |
342 | 0 | sep++; |
343 | |
|
344 | 0 | for (i = 0; attr; i++) { |
345 | 0 | unsigned bit = (1 << i); |
346 | 0 | if (!(attr & bit)) |
347 | 0 | continue; |
348 | 0 | attr &= ~bit; |
349 | 0 | if (sep++) |
350 | 0 | OUT(';'); |
351 | 0 | dst += xsnprintf(dst, end - dst, "%d", i); |
352 | 0 | } |
353 | 0 | if (!color_empty(&fg)) { |
354 | 0 | if (sep++) |
355 | 0 | OUT(';'); |
356 | 0 | dst = color_output(dst, end - dst, &fg, 0); |
357 | 0 | } |
358 | 0 | if (!color_empty(&bg)) { |
359 | 0 | if (sep++) |
360 | 0 | OUT(';'); |
361 | 0 | dst = color_output(dst, end - dst, &bg, 1); |
362 | 0 | } |
363 | 0 | OUT('m'); |
364 | 0 | } |
365 | 0 | OUT(0); |
366 | 0 | return 0; |
367 | 0 | bad: |
368 | 0 | return error(_("invalid color value: %.*s"), value_len, value); |
369 | 0 | #undef OUT |
370 | 0 | } |
371 | | |
372 | | enum git_colorbool git_config_colorbool(const char *var, const char *value) |
373 | 0 | { |
374 | 0 | if (value) { |
375 | 0 | if (!strcasecmp(value, "never")) |
376 | 0 | return GIT_COLOR_NEVER; |
377 | 0 | if (!strcasecmp(value, "always")) |
378 | 0 | return GIT_COLOR_ALWAYS; |
379 | 0 | if (!strcasecmp(value, "auto")) |
380 | 0 | return GIT_COLOR_AUTO; |
381 | 0 | } |
382 | | |
383 | 0 | if (!var) |
384 | 0 | return GIT_COLOR_UNKNOWN; |
385 | | |
386 | | /* Missing or explicit false to turn off colorization */ |
387 | 0 | if (!git_config_bool(var, value)) |
388 | 0 | return GIT_COLOR_NEVER; |
389 | | |
390 | | /* any normal truth value defaults to 'auto' */ |
391 | 0 | return GIT_COLOR_AUTO; |
392 | 0 | } |
393 | | |
394 | | static bool check_auto_color(int fd) |
395 | 0 | { |
396 | 0 | static int color_stderr_is_tty = -1; |
397 | 0 | int *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty; |
398 | 0 | if (*is_tty_p < 0) |
399 | 0 | *is_tty_p = isatty(fd); |
400 | 0 | if (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) { |
401 | 0 | if (!is_terminal_dumb()) |
402 | 0 | return true; |
403 | 0 | } |
404 | 0 | return false; |
405 | 0 | } |
406 | | |
407 | | bool want_color_fd(int fd, enum git_colorbool var) |
408 | 0 | { |
409 | | /* |
410 | | * NEEDSWORK: This function is sometimes used from multiple threads, and |
411 | | * we end up using want_auto racily. That "should not matter" since |
412 | | * we always write the same value, but it's still wrong. This function |
413 | | * is listed in .tsan-suppressions for the time being. |
414 | | */ |
415 | |
|
416 | 0 | static int want_auto[3] = { -1, -1, -1 }; |
417 | |
|
418 | 0 | if (fd < 1 || fd >= ARRAY_SIZE(want_auto)) |
419 | 0 | BUG("file descriptor out of range: %d", fd); |
420 | | |
421 | 0 | if (var == GIT_COLOR_UNKNOWN) |
422 | 0 | var = git_use_color_default; |
423 | |
|
424 | 0 | if (var == GIT_COLOR_AUTO) { |
425 | 0 | if (want_auto[fd] < 0) |
426 | 0 | want_auto[fd] = check_auto_color(fd); |
427 | 0 | return want_auto[fd]; |
428 | 0 | } |
429 | 0 | return var == GIT_COLOR_ALWAYS; |
430 | 0 | } |
431 | | |
432 | | int git_color_config(const char *var, const char *value, void *cb UNUSED) |
433 | 0 | { |
434 | 0 | if (!strcmp(var, "color.ui")) { |
435 | 0 | git_use_color_default = git_config_colorbool(var, value); |
436 | 0 | return 0; |
437 | 0 | } |
438 | | |
439 | 0 | return 0; |
440 | 0 | } |
441 | | |
442 | | void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb) |
443 | 0 | { |
444 | 0 | if (*color) |
445 | 0 | fprintf(fp, "%s", color); |
446 | 0 | fprintf(fp, "%s", sb->buf); |
447 | 0 | if (*color) |
448 | 0 | fprintf(fp, "%s", GIT_COLOR_RESET); |
449 | 0 | } |
450 | | |
451 | | static int color_vfprintf(FILE *fp, const char *color, const char *fmt, |
452 | | va_list args, const char *trail) |
453 | 0 | { |
454 | 0 | int r = 0; |
455 | |
|
456 | 0 | if (*color) |
457 | 0 | r += fprintf(fp, "%s", color); |
458 | 0 | r += vfprintf(fp, fmt, args); |
459 | 0 | if (*color) |
460 | 0 | r += fprintf(fp, "%s", GIT_COLOR_RESET); |
461 | 0 | if (trail) |
462 | 0 | r += fprintf(fp, "%s", trail); |
463 | 0 | return r; |
464 | 0 | } |
465 | | |
466 | | int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) |
467 | 0 | { |
468 | 0 | va_list args; |
469 | 0 | int r; |
470 | 0 | va_start(args, fmt); |
471 | 0 | r = color_vfprintf(fp, color, fmt, args, NULL); |
472 | 0 | va_end(args); |
473 | 0 | return r; |
474 | 0 | } |
475 | | |
476 | | int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) |
477 | 0 | { |
478 | 0 | va_list args; |
479 | 0 | int r; |
480 | 0 | va_start(args, fmt); |
481 | 0 | r = color_vfprintf(fp, color, fmt, args, "\n"); |
482 | 0 | va_end(args); |
483 | 0 | return r; |
484 | 0 | } |
485 | | |
486 | | int color_is_nil(const char *c) |
487 | 0 | { |
488 | 0 | return !strcmp(c, "NIL"); |
489 | 0 | } |