Git fork
at reftables-rust 489 lines 11 kB view raw
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 12static enum git_colorbool git_use_color_default = GIT_COLOR_AUTO; 13int color_stdout_is_tty = -1; 14 15/* 16 * The list of available column colors. 17 */ 18const 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 34enum { 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 */ 43const int column_colors_ansi_max = ARRAY_SIZE(column_colors_ansi) - 1; 44 45/* An individual foreground or background color. */ 46struct 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 */ 64static int match_word(const char *word, int len, const char *match) 65{ 66 return !strncasecmp(word, match, len) && !match[len]; 67} 68 69static int get_hex_color(const char **inp, int width, unsigned char *out) 70{ 71 const char *in = *inp; 72 unsigned int val; 73 74 assert(width == 1 || width == 2); 75 val = (hexval(in[0]) << 4) | hexval(in[width - 1]); 76 if (val & ~0xff) 77 return -1; 78 *inp += width; 79 *out = val; 80 return 0; 81} 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 */ 87static int parse_ansi_color(struct color *out, const char *name, int len) 88{ 89 /* Positions in array must match ANSI color codes */ 90 static const char * const color_names[] = { 91 "black", "red", "green", "yellow", 92 "blue", "magenta", "cyan", "white" 93 }; 94 int i; 95 int color_offset = COLOR_FOREGROUND_ANSI; 96 97 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 out->type = COLOR_ANSI; 114 out->value = 9 + color_offset; 115 return 0; 116 } 117 118 if (strncasecmp(name, "bright", 6) == 0) { 119 color_offset = COLOR_FOREGROUND_BRIGHT_ANSI; 120 name += 6; 121 len -= 6; 122 } 123 for (i = 0; i < ARRAY_SIZE(color_names); i++) { 124 if (match_word(name, len, color_names[i])) { 125 out->type = COLOR_ANSI; 126 out->value = i + color_offset; 127 return 0; 128 } 129 } 130 return -1; 131} 132 133static int parse_color(struct color *out, const char *name, int len) 134{ 135 char *end; 136 long val; 137 138 /* First try the special word "normal"... */ 139 if (match_word(name, len, "normal")) { 140 out->type = COLOR_NORMAL; 141 return 0; 142 } 143 144 /* Try a 24- or 12-bit RGB value prefixed with '#' */ 145 if ((len == 7 || len == 4) && name[0] == '#') { 146 int width_per_color = (len == 7) ? 2 : 1; 147 const char *color = name + 1; 148 149 if (!get_hex_color(&color, width_per_color, &out->red) && 150 !get_hex_color(&color, width_per_color, &out->green) && 151 !get_hex_color(&color, width_per_color, &out->blue)) { 152 out->type = COLOR_RGB; 153 return 0; 154 } 155 } 156 157 /* Then pick from our human-readable color names... */ 158 if (parse_ansi_color(out, name, len) == 0) { 159 return 0; 160 } 161 162 /* And finally try a literal 256-color-mode number */ 163 val = strtol(name, &end, 10); 164 if (end - name == len) { 165 /* 166 * Allow "-1" as an alias for "normal", but other negative 167 * numbers are bogus. 168 */ 169 if (val < -1) 170 ; /* fall through to error */ 171 else if (val < 0) { 172 out->type = COLOR_NORMAL; 173 return 0; 174 /* Rewrite 0-7 as more-portable standard colors. */ 175 } else if (val < 8) { 176 out->type = COLOR_ANSI; 177 out->value = val + COLOR_FOREGROUND_ANSI; 178 return 0; 179 /* Rewrite 8-15 as more-portable aixterm colors. */ 180 } else if (val < 16) { 181 out->type = COLOR_ANSI; 182 out->value = val - 8 + COLOR_FOREGROUND_BRIGHT_ANSI; 183 return 0; 184 } else if (val < 256) { 185 out->type = COLOR_256; 186 out->value = val; 187 return 0; 188 } 189 } 190 191 return -1; 192} 193 194static int parse_attr(const char *name, size_t len) 195{ 196 static const struct { 197 const char *name; 198 size_t len; 199 int val, neg; 200 } attrs[] = { 201#define ATTR(x, val, neg) { (x), sizeof(x)-1, (val), (neg) } 202 ATTR("bold", 1, 22), 203 ATTR("dim", 2, 22), 204 ATTR("italic", 3, 23), 205 ATTR("ul", 4, 24), 206 ATTR("blink", 5, 25), 207 ATTR("reverse", 7, 27), 208 ATTR("strike", 9, 29) 209#undef ATTR 210 }; 211 int negate = 0; 212 int i; 213 214 if (skip_prefix_mem(name, len, "no", &name, &len)) { 215 skip_prefix_mem(name, len, "-", &name, &len); 216 negate = 1; 217 } 218 219 for (i = 0; i < ARRAY_SIZE(attrs); i++) { 220 if (attrs[i].len == len && !memcmp(attrs[i].name, name, len)) 221 return negate ? attrs[i].neg : attrs[i].val; 222 } 223 return -1; 224} 225 226int color_parse(const char *value, char *dst) 227{ 228 return color_parse_mem(value, strlen(value), dst); 229} 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 */ 236static char *color_output(char *out, int len, const struct color *c, int background) 237{ 238 int offset = 0; 239 240 if (background) 241 offset = COLOR_BACKGROUND_OFFSET; 242 switch (c->type) { 243 case COLOR_UNSPECIFIED: 244 case COLOR_NORMAL: 245 break; 246 case COLOR_ANSI: 247 out += xsnprintf(out, len, "%d", c->value + offset); 248 break; 249 case COLOR_256: 250 out += xsnprintf(out, len, "%d;5;%d", COLOR_FOREGROUND_256 + offset, 251 c->value); 252 break; 253 case COLOR_RGB: 254 out += xsnprintf(out, len, "%d;2;%d;%d;%d", 255 COLOR_FOREGROUND_RGB + offset, 256 c->red, c->green, c->blue); 257 break; 258 } 259 return out; 260} 261 262static int color_empty(const struct color *c) 263{ 264 return c->type <= COLOR_NORMAL; 265} 266 267int color_parse_mem(const char *value, int value_len, char *dst) 268{ 269 const char *ptr = value; 270 int len = value_len; 271 char *end = dst + COLOR_MAXLEN; 272 unsigned int has_reset = 0; 273 unsigned int attr = 0; 274 struct color fg = { COLOR_UNSPECIFIED }; 275 struct color bg = { COLOR_UNSPECIFIED }; 276 277 while (len > 0 && isspace(*ptr)) { 278 ptr++; 279 len--; 280 } 281 282 if (!len) { 283 dst[0] = '\0'; 284 return 0; 285 } 286 287 /* [reset] [fg [bg]] [attr]... */ 288 while (len > 0) { 289 const char *word = ptr; 290 struct color c = { COLOR_UNSPECIFIED }; 291 int val, wordlen = 0; 292 293 while (len > 0 && !isspace(word[wordlen])) { 294 wordlen++; 295 len--; 296 } 297 298 ptr = word + wordlen; 299 while (len > 0 && isspace(*ptr)) { 300 ptr++; 301 len--; 302 } 303 304 if (match_word(word, wordlen, "reset")) { 305 has_reset = 1; 306 continue; 307 } 308 309 if (!parse_color(&c, word, wordlen)) { 310 if (fg.type == COLOR_UNSPECIFIED) { 311 fg = c; 312 continue; 313 } 314 if (bg.type == COLOR_UNSPECIFIED) { 315 bg = c; 316 continue; 317 } 318 goto bad; 319 } 320 val = parse_attr(word, wordlen); 321 if (0 <= val) 322 attr |= (1 << val); 323 else 324 goto bad; 325 } 326 327#undef OUT 328#define OUT(x) do { \ 329 if (dst == end) \ 330 BUG("color parsing ran out of space"); \ 331 *dst++ = (x); \ 332} while(0) 333 334 if (has_reset || attr || !color_empty(&fg) || !color_empty(&bg)) { 335 int sep = 0; 336 int i; 337 338 OUT('\033'); 339 OUT('['); 340 341 if (has_reset) 342 sep++; 343 344 for (i = 0; attr; i++) { 345 unsigned bit = (1 << i); 346 if (!(attr & bit)) 347 continue; 348 attr &= ~bit; 349 if (sep++) 350 OUT(';'); 351 dst += xsnprintf(dst, end - dst, "%d", i); 352 } 353 if (!color_empty(&fg)) { 354 if (sep++) 355 OUT(';'); 356 dst = color_output(dst, end - dst, &fg, 0); 357 } 358 if (!color_empty(&bg)) { 359 if (sep++) 360 OUT(';'); 361 dst = color_output(dst, end - dst, &bg, 1); 362 } 363 OUT('m'); 364 } 365 OUT(0); 366 return 0; 367bad: 368 return error(_("invalid color value: %.*s"), value_len, value); 369#undef OUT 370} 371 372enum git_colorbool git_config_colorbool(const char *var, const char *value) 373{ 374 if (value) { 375 if (!strcasecmp(value, "never")) 376 return GIT_COLOR_NEVER; 377 if (!strcasecmp(value, "always")) 378 return GIT_COLOR_ALWAYS; 379 if (!strcasecmp(value, "auto")) 380 return GIT_COLOR_AUTO; 381 } 382 383 if (!var) 384 return GIT_COLOR_UNKNOWN; 385 386 /* Missing or explicit false to turn off colorization */ 387 if (!git_config_bool(var, value)) 388 return GIT_COLOR_NEVER; 389 390 /* any normal truth value defaults to 'auto' */ 391 return GIT_COLOR_AUTO; 392} 393 394static bool check_auto_color(int fd) 395{ 396 static int color_stderr_is_tty = -1; 397 int *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty; 398 if (*is_tty_p < 0) 399 *is_tty_p = isatty(fd); 400 if (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) { 401 if (!is_terminal_dumb()) 402 return true; 403 } 404 return false; 405} 406 407bool want_color_fd(int fd, enum git_colorbool var) 408{ 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 static int want_auto[3] = { -1, -1, -1 }; 417 418 if (fd < 1 || fd >= ARRAY_SIZE(want_auto)) 419 BUG("file descriptor out of range: %d", fd); 420 421 if (var == GIT_COLOR_UNKNOWN) 422 var = git_use_color_default; 423 424 if (var == GIT_COLOR_AUTO) { 425 if (want_auto[fd] < 0) 426 want_auto[fd] = check_auto_color(fd); 427 return want_auto[fd]; 428 } 429 return var == GIT_COLOR_ALWAYS; 430} 431 432int git_color_config(const char *var, const char *value, void *cb UNUSED) 433{ 434 if (!strcmp(var, "color.ui")) { 435 git_use_color_default = git_config_colorbool(var, value); 436 return 0; 437 } 438 439 return 0; 440} 441 442void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb) 443{ 444 if (*color) 445 fprintf(fp, "%s", color); 446 fprintf(fp, "%s", sb->buf); 447 if (*color) 448 fprintf(fp, "%s", GIT_COLOR_RESET); 449} 450 451static int color_vfprintf(FILE *fp, const char *color, const char *fmt, 452 va_list args, const char *trail) 453{ 454 int r = 0; 455 456 if (*color) 457 r += fprintf(fp, "%s", color); 458 r += vfprintf(fp, fmt, args); 459 if (*color) 460 r += fprintf(fp, "%s", GIT_COLOR_RESET); 461 if (trail) 462 r += fprintf(fp, "%s", trail); 463 return r; 464} 465 466int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) 467{ 468 va_list args; 469 int r; 470 va_start(args, fmt); 471 r = color_vfprintf(fp, color, fmt, args, NULL); 472 va_end(args); 473 return r; 474} 475 476int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) 477{ 478 va_list args; 479 int r; 480 va_start(args, fmt); 481 r = color_vfprintf(fp, color, fmt, args, "\n"); 482 va_end(args); 483 return r; 484} 485 486int color_is_nil(const char *c) 487{ 488 return !strcmp(c, "NIL"); 489}