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