#include "cache.h"
+#include "config.h"
#include "color.h"
static int git_use_color_default = GIT_COLOR_AUTO;
GIT_COLOR_RESET,
};
+enum {
+ COLOR_BACKGROUND_OFFSET = 10,
+ COLOR_FOREGROUND_ANSI = 30,
+ COLOR_FOREGROUND_RGB = 38,
+ COLOR_FOREGROUND_256 = 38,
+ COLOR_FOREGROUND_BRIGHT_ANSI = 90,
+};
+
/* Ignore the RESET at the end when giving the size */
const int column_colors_ansi_max = ARRAY_SIZE(column_colors_ansi) - 1;
return 0;
}
-static int parse_color(struct color *out, const char *name, int len)
+/*
+ * If an ANSI color is recognized in "name", fill "out" and return 0.
+ * Otherwise, leave out unchanged and return -1.
+ */
+static int parse_ansi_color(struct color *out, const char *name, int len)
{
/* Positions in array must match ANSI color codes */
static const char * const color_names[] = {
"black", "red", "green", "yellow",
"blue", "magenta", "cyan", "white"
};
- char *end;
int i;
+ int color_offset = COLOR_FOREGROUND_ANSI;
+
+ if (strncasecmp(name, "bright", 6) == 0) {
+ color_offset = COLOR_FOREGROUND_BRIGHT_ANSI;
+ name += 6;
+ len -= 6;
+ }
+ for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+ if (match_word(name, len, color_names[i])) {
+ out->type = COLOR_ANSI;
+ out->value = i + color_offset;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int parse_color(struct color *out, const char *name, int len)
+{
+ char *end;
long val;
/* First try the special word "normal"... */
}
/* Then pick from our human-readable color names... */
- for (i = 0; i < ARRAY_SIZE(color_names); i++) {
- if (match_word(name, len, color_names[i])) {
- out->type = COLOR_ANSI;
- out->value = i;
- return 0;
- }
+ if (parse_ansi_color(out, name, len) == 0) {
+ return 0;
}
/* And finally try a literal 256-color-mode number */
else if (val < 0) {
out->type = COLOR_NORMAL;
return 0;
- /* Rewrite low numbers as more-portable standard colors. */
+ /* Rewrite 0-7 as more-portable standard colors. */
} else if (val < 8) {
out->type = COLOR_ANSI;
- out->value = val;
+ out->value = val + COLOR_FOREGROUND_ANSI;
+ return 0;
+ /* Rewrite 8-15 as more-portable aixterm colors. */
+ } else if (val < 16) {
+ out->type = COLOR_ANSI;
+ out->value = val - 8 + COLOR_FOREGROUND_BRIGHT_ANSI;
return 0;
} else if (val < 256) {
out->type = COLOR_256;
return -1;
}
-static int parse_attr(const char *name, int len)
+static int parse_attr(const char *name, size_t len)
{
- static const int attr_values[] = { 1, 2, 4, 5, 7,
- 22, 22, 24, 25, 27 };
- static const char * const attr_names[] = {
- "bold", "dim", "ul", "blink", "reverse",
- "nobold", "nodim", "noul", "noblink", "noreverse"
+ static const struct {
+ const char *name;
+ size_t len;
+ int val, neg;
+ } attrs[] = {
+#define ATTR(x, val, neg) { (x), sizeof(x)-1, (val), (neg) }
+ ATTR("bold", 1, 22),
+ ATTR("dim", 2, 22),
+ ATTR("italic", 3, 23),
+ ATTR("ul", 4, 24),
+ ATTR("blink", 5, 25),
+ ATTR("reverse", 7, 27),
+ ATTR("strike", 9, 29)
+#undef ATTR
};
+ int negate = 0;
int i;
- for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
- const char *str = attr_names[i];
- if (!strncasecmp(name, str, len) && !str[len])
- return attr_values[i];
+
+ if (skip_prefix_mem(name, len, "no", &name, &len)) {
+ skip_prefix_mem(name, len, "-", &name, &len);
+ negate = 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(attrs); i++) {
+ if (attrs[i].len == len && !memcmp(attrs[i].name, name, len))
+ return negate ? attrs[i].neg : attrs[i].val;
}
return -1;
}
* already have the ANSI escape code in it. "out" should have enough
* space in it to fit any color.
*/
-static char *color_output(char *out, const struct color *c, char type)
+static char *color_output(char *out, int len, const struct color *c, int background)
{
+ int offset = 0;
+
+ if (background)
+ offset = COLOR_BACKGROUND_OFFSET;
switch (c->type) {
case COLOR_UNSPECIFIED:
case COLOR_NORMAL:
break;
case COLOR_ANSI:
- *out++ = type;
- *out++ = '0' + c->value;
+ out += xsnprintf(out, len, "%d", c->value + offset);
break;
case COLOR_256:
- out += sprintf(out, "%c8;5;%d", type, c->value);
+ out += xsnprintf(out, len, "%d;5;%d", COLOR_FOREGROUND_256 + offset,
+ c->value);
break;
case COLOR_RGB:
- out += sprintf(out, "%c8;2;%d;%d;%d", type,
- c->red, c->green, c->blue);
+ out += xsnprintf(out, len, "%d;2;%d;%d;%d",
+ COLOR_FOREGROUND_RGB + offset,
+ c->red, c->green, c->blue);
break;
}
return out;
{
const char *ptr = value;
int len = value_len;
+ char *end = dst + COLOR_MAXLEN;
unsigned int attr = 0;
struct color fg = { COLOR_UNSPECIFIED };
struct color bg = { COLOR_UNSPECIFIED };
- if (!strncasecmp(value, "reset", len)) {
- strcpy(dst, GIT_COLOR_RESET);
+ while (len > 0 && isspace(*ptr)) {
+ ptr++;
+ len--;
+ }
+
+ if (!len) {
+ dst[0] = '\0';
+ return 0;
+ }
+
+ if (!strncasecmp(ptr, "reset", len)) {
+ xsnprintf(dst, end - dst, GIT_COLOR_RESET);
return 0;
}
/* [fg [bg]] [attr]... */
while (len > 0) {
const char *word = ptr;
- struct color c;
+ struct color c = { COLOR_UNSPECIFIED };
int val, wordlen = 0;
while (len > 0 && !isspace(word[wordlen])) {
goto bad;
}
+#undef OUT
+#define OUT(x) do { \
+ if (dst == end) \
+ BUG("color parsing ran out of space"); \
+ *dst++ = (x); \
+} while(0)
+
if (attr || !color_empty(&fg) || !color_empty(&bg)) {
int sep = 0;
int i;
- *dst++ = '\033';
- *dst++ = '[';
+ OUT('\033');
+ OUT('[');
for (i = 0; attr; i++) {
unsigned bit = (1 << i);
continue;
attr &= ~bit;
if (sep++)
- *dst++ = ';';
- dst += sprintf(dst, "%d", i);
+ OUT(';');
+ dst += xsnprintf(dst, end - dst, "%d", i);
}
if (!color_empty(&fg)) {
if (sep++)
- *dst++ = ';';
- /* foreground colors are all in the 3x range */
- dst = color_output(dst, &fg, '3');
+ OUT(';');
+ dst = color_output(dst, end - dst, &fg, 0);
}
if (!color_empty(&bg)) {
if (sep++)
- *dst++ = ';';
- /* background colors are all in the 4x range */
- dst = color_output(dst, &bg, '4');
+ OUT(';');
+ dst = color_output(dst, end - dst, &bg, 1);
}
- *dst++ = 'm';
+ OUT('m');
}
- *dst = 0;
+ OUT(0);
return 0;
bad:
return error(_("invalid color value: %.*s"), value_len, value);
+#undef OUT
}
int git_config_colorbool(const char *var, const char *value)
return GIT_COLOR_AUTO;
}
-static int check_auto_color(void)
+static int check_auto_color(int fd)
{
- if (color_stdout_is_tty < 0)
- color_stdout_is_tty = isatty(1);
- if (color_stdout_is_tty || (pager_in_use() && pager_use_color)) {
- char *term = getenv("TERM");
- if (term && strcmp(term, "dumb"))
+ static int color_stderr_is_tty = -1;
+ int *is_tty_p = fd == 1 ? &color_stdout_is_tty : &color_stderr_is_tty;
+ if (*is_tty_p < 0)
+ *is_tty_p = isatty(fd);
+ if (*is_tty_p || (fd == 1 && pager_in_use() && pager_use_color)) {
+ if (!is_terminal_dumb())
return 1;
}
return 0;
}
-int want_color(int var)
+int want_color_fd(int fd, int var)
{
- static int want_auto = -1;
+ /*
+ * NEEDSWORK: This function is sometimes used from multiple threads, and
+ * we end up using want_auto racily. That "should not matter" since
+ * we always write the same value, but it's still wrong. This function
+ * is listed in .tsan-suppressions for the time being.
+ */
+
+ static int want_auto[3] = { -1, -1, -1 };
+
+ if (fd < 1 || fd >= ARRAY_SIZE(want_auto))
+ BUG("file descriptor out of range: %d", fd);
if (var < 0)
var = git_use_color_default;
if (var == GIT_COLOR_AUTO) {
- if (want_auto < 0)
- want_auto = check_auto_color();
- return want_auto;
+ if (want_auto[fd] < 0)
+ want_auto[fd] = check_auto_color(fd);
+ return want_auto[fd];
}
return var;
}
return r;
}
-
-
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
{
va_list args;