log-tree: show_log() should respect the setting of diffopt->line_termination
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 1bf1ed096759cb99ec75cd35caaa8644418f0e02..895c1376559cc2aaa1e6c25361a9a6b831c9a18a 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -17,39 +17,15 @@ static int diff_detect_rename_default = 0;
 static int diff_rename_limit_default = -1;
 static int diff_use_color_default = 0;
 
-enum color_diff {
-       DIFF_RESET = 0,
-       DIFF_PLAIN = 1,
-       DIFF_METAINFO = 2,
-       DIFF_FRAGINFO = 3,
-       DIFF_FILE_OLD = 4,
-       DIFF_FILE_NEW = 5,
-};
-
-#define COLOR_NORMAL  ""
-#define COLOR_BOLD    "\033[1m"
-#define COLOR_DIM     "\033[2m"
-#define COLOR_UL      "\033[4m"
-#define COLOR_BLINK   "\033[5m"
-#define COLOR_REVERSE "\033[7m"
-#define COLOR_RESET   "\033[m"
-
-#define COLOR_BLACK   "\033[30m"
-#define COLOR_RED     "\033[31m"
-#define COLOR_GREEN   "\033[32m"
-#define COLOR_YELLOW  "\033[33m"
-#define COLOR_BLUE    "\033[34m"
-#define COLOR_MAGENTA "\033[35m"
-#define COLOR_CYAN    "\033[36m"
-#define COLOR_WHITE   "\033[37m"
-
-static const char *diff_colors[] = {
-       [DIFF_RESET]    = COLOR_RESET,
-       [DIFF_PLAIN]    = COLOR_NORMAL,
-       [DIFF_METAINFO] = COLOR_BOLD,
-       [DIFF_FRAGINFO] = COLOR_CYAN,
-       [DIFF_FILE_OLD] = COLOR_RED,
-       [DIFF_FILE_NEW] = COLOR_GREEN,
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+static char diff_colors[][24] = {
+       "\033[m",       /* reset */
+       "",             /* normal */
+       "\033[1m",      /* bold */
+       "\033[36m",     /* cyan */
+       "\033[31m",     /* red */
+       "\033[32m",     /* green */
+       "\033[33m"      /* yellow */
 };
 
 static int parse_diff_color_slot(const char *var, int ofs)
@@ -64,45 +40,131 @@ static int parse_diff_color_slot(const char *var, int ofs)
                return DIFF_FILE_OLD;
        if (!strcasecmp(var+ofs, "new"))
                return DIFF_FILE_NEW;
+       if (!strcasecmp(var+ofs, "commit"))
+               return DIFF_COMMIT;
        die("bad config variable '%s'", var);
 }
 
-static const char *parse_diff_color_value(const char *value, const char *var)
-{
-       if (!strcasecmp(value, "normal"))
-               return COLOR_NORMAL;
-       if (!strcasecmp(value, "bold"))
-               return COLOR_BOLD;
-       if (!strcasecmp(value, "dim"))
-               return COLOR_DIM;
-       if (!strcasecmp(value, "ul"))
-               return COLOR_UL;
-       if (!strcasecmp(value, "blink"))
-               return COLOR_BLINK;
-       if (!strcasecmp(value, "reverse"))
-               return COLOR_REVERSE;
-       if (!strcasecmp(value, "reset"))
-               return COLOR_RESET;
-       if (!strcasecmp(value, "black"))
-               return COLOR_BLACK;
-       if (!strcasecmp(value, "red"))
-               return COLOR_RED;
-       if (!strcasecmp(value, "green"))
-               return COLOR_GREEN;
-       if (!strcasecmp(value, "yellow"))
-               return COLOR_YELLOW;
-       if (!strcasecmp(value, "blue"))
-               return COLOR_BLUE;
-       if (!strcasecmp(value, "magenta"))
-               return COLOR_MAGENTA;
-       if (!strcasecmp(value, "cyan"))
-               return COLOR_CYAN;
-       if (!strcasecmp(value, "white"))
-               return COLOR_WHITE;
+static int parse_color(const char *name, int len)
+{
+       static const char * const color_names[] = {
+               "normal", "black", "red", "green", "yellow",
+               "blue", "magenta", "cyan", "white"
+       };
+       char *end;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+               const char *str = color_names[i];
+               if (!strncasecmp(name, str, len) && !str[len])
+                       return i - 1;
+       }
+       i = strtol(name, &end, 10);
+       if (*name && !*end && i >= -1 && i <= 255)
+               return i;
+       return -2;
+}
+
+static int parse_attr(const char *name, int len)
+{
+       static const int attr_values[] = { 1, 2, 4, 5, 7 };
+       static const char * const attr_names[] = {
+               "bold", "dim", "ul", "blink", "reverse"
+       };
+       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];
+       }
+       return -1;
+}
+
+static void parse_diff_color_value(const char *value, const char *var, char *dst)
+{
+       const char *ptr = value;
+       int attr = -1;
+       int fg = -2;
+       int bg = -2;
+
+       if (!strcasecmp(value, "reset")) {
+               strcpy(dst, "\033[m");
+               return;
+       }
+
+       /* [fg [bg]] [attr] */
+       while (*ptr) {
+               const char *word = ptr;
+               int val, len = 0;
+
+               while (word[len] && !isspace(word[len]))
+                       len++;
+
+               ptr = word + len;
+               while (*ptr && isspace(*ptr))
+                       ptr++;
+
+               val = parse_color(word, len);
+               if (val >= -1) {
+                       if (fg == -2) {
+                               fg = val;
+                               continue;
+                       }
+                       if (bg == -2) {
+                               bg = val;
+                               continue;
+                       }
+                       goto bad;
+               }
+               val = parse_attr(word, len);
+               if (val < 0 || attr != -1)
+                       goto bad;
+               attr = val;
+       }
+
+       if (attr >= 0 || fg >= 0 || bg >= 0) {
+               int sep = 0;
+
+               *dst++ = '\033';
+               *dst++ = '[';
+               if (attr >= 0) {
+                       *dst++ = '0' + attr;
+                       sep++;
+               }
+               if (fg >= 0) {
+                       if (sep++)
+                               *dst++ = ';';
+                       if (fg < 8) {
+                               *dst++ = '3';
+                               *dst++ = '0' + fg;
+                       } else {
+                               dst += sprintf(dst, "38;5;%d", fg);
+                       }
+               }
+               if (bg >= 0) {
+                       if (sep++)
+                               *dst++ = ';';
+                       if (bg < 8) {
+                               *dst++ = '4';
+                               *dst++ = '0' + bg;
+                       } else {
+                               dst += sprintf(dst, "48;5;%d", bg);
+                       }
+               }
+               *dst++ = 'm';
+       }
+       *dst = 0;
+       return;
+bad:
        die("bad config value '%s' for variable '%s'", value, var);
 }
 
-int git_diff_config(const char *var, const char *value)
+/*
+ * These are to give UI layer defaults.
+ * The core-level commands such as git-diff-files should
+ * never be affected by the setting of diff.renames
+ * the user happens to have in the configuration file.
+ */
+int git_diff_ui_config(const char *var, const char *value)
 {
        if (!strcmp(var, "diff.renamelimit")) {
                diff_rename_limit_default = git_config_int(var, value);
@@ -111,8 +173,14 @@ int git_diff_config(const char *var, const char *value)
        if (!strcmp(var, "diff.color")) {
                if (!value)
                        diff_use_color_default = 1; /* bool */
-               else if (!strcasecmp(value, "auto"))
-                       diff_use_color_default = isatty(1);
+               else if (!strcasecmp(value, "auto")) {
+                       diff_use_color_default = 0;
+                       if (isatty(1) || (pager_in_use && pager_use_color)) {
+                               char *term = getenv("TERM");
+                               if (term && strcmp(term, "dumb"))
+                                       diff_use_color_default = 1;
+                       }
+               }
                else if (!strcasecmp(value, "never"))
                        diff_use_color_default = 0;
                else if (!strcasecmp(value, "always"))
@@ -133,7 +201,7 @@ int git_diff_config(const char *var, const char *value)
        }
        if (!strncmp(var, "diff.color.", 11)) {
                int slot = parse_diff_color_slot(var, 11);
-               diff_colors[slot] = parse_diff_color_value(value, var);
+               parse_diff_color_value(value, var, diff_colors[slot]);
                return 0;
        }
        return git_default_config(var, value);
@@ -296,7 +364,7 @@ struct emit_callback {
        const char **label_path;
 };
 
-static inline const char *get_color(int diff_use_color, enum color_diff ix)
+const char *diff_get_color(int diff_use_color, enum color_diff ix)
 {
        if (diff_use_color)
                return diff_colors[ix];
@@ -307,8 +375,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
        int i;
        struct emit_callback *ecbdata = priv;
-       const char *set = get_color(ecbdata->color_diff, DIFF_METAINFO);
-       const char *reset = get_color(ecbdata->color_diff, DIFF_RESET);
+       const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
+       const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
 
        if (ecbdata->label_path[0]) {
                printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
@@ -323,7 +391,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                ;
        if (2 <= i && i < len && line[i] == ' ') {
                ecbdata->nparents = i - 1;
-               set = get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+               set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        }
        else if (len < ecbdata->nparents)
                set = reset;
@@ -336,7 +404,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                        else if (line[i] == '+')
                                color = DIFF_FILE_NEW;
                }
-               set = get_color(ecbdata->color_diff, color);
+               set = diff_get_color(ecbdata->color_diff, color);
        }
        if (len > 0 && line[len-1] == '\n')
                len--;
@@ -693,8 +761,8 @@ static void builtin_diff(const char *name_a,
        mmfile_t mf1, mf2;
        const char *lbl[2];
        char *a_one, *b_two;
-       const char *set = get_color(o->color_diff, DIFF_METAINFO);
-       const char *reset = get_color(o->color_diff, DIFF_RESET);
+       const char *set = diff_get_color(o->color_diff, DIFF_METAINFO);
+       const char *reset = diff_get_color(o->color_diff, DIFF_RESET);
 
        a_one = quote_two("a/", name_a);
        b_two = quote_two("b/", name_b);
@@ -1718,13 +1786,9 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
                                 struct diff_filespec *one,
                                 struct diff_filespec *two)
 {
-       struct diff_filepair *dp = xmalloc(sizeof(*dp));
+       struct diff_filepair *dp = xcalloc(1, sizeof(*dp));
        dp->one = one;
        dp->two = two;
-       dp->score = 0;
-       dp->status = 0;
-       dp->source_stays = 0;
-       dp->broken_pair = 0;
        if (queue)
                diff_q(queue, dp);
        return dp;