diff.c: associate a flag with each pattern and use it for compiling regex
[git/git.git] / diff.c
diff --git a/diff.c b/diff.c
index a07812c..6881cf4 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -94,32 +94,37 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val
  * to define a customized regexp to find the beginning of a function to
  * be used for hunk header lines of "diff -p" style output.
  */
-static struct funcname_pattern {
+struct funcname_pattern_entry {
        char *name;
        char *pattern;
-       struct funcname_pattern *next;
+       int cflags;
+};
+static struct funcname_pattern_list {
+       struct funcname_pattern_list *next;
+       struct funcname_pattern_entry e;
 } *funcname_pattern_list;
 
-static int parse_funcname_pattern(const char *var, const char *ep, const char *value)
+static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags)
 {
        const char *name;
        int namelen;
-       struct funcname_pattern *pp;
+       struct funcname_pattern_list *pp;
 
        name = var + 5; /* "diff." */
        namelen = ep - name;
 
        for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
+               if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen])
                        break;
        if (!pp) {
                pp = xcalloc(1, sizeof(*pp));
-               pp->name = xmemdupz(name, namelen);
+               pp->e.name = xmemdupz(name, namelen);
                pp->next = funcname_pattern_list;
                funcname_pattern_list = pp;
        }
-       free(pp->pattern);
-       pp->pattern = xstrdup(value);
+       free(pp->e.pattern);
+       pp->e.pattern = xstrdup(value);
+       pp->e.cflags = cflags;
        return 0;
 }
 
@@ -131,10 +136,6 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v
  */
 int git_diff_ui_config(const char *var, const char *value, void *cb)
 {
-       if (!strcmp(var, "diff.renamelimit")) {
-               diff_rename_limit_default = git_config_int(var, value);
-               return 0;
-       }
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                diff_use_color_default = git_config_colorbool(var, value, -1);
                return 0;
@@ -167,6 +168,11 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
 
 int git_diff_basic_config(const char *var, const char *value, void *cb)
 {
+       if (!strcmp(var, "diff.renamelimit")) {
+               diff_rename_limit_default = git_config_int(var, value);
+               return 0;
+       }
+
        if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                if (!value)
@@ -181,7 +187,8 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                        if (!strcmp(ep, ".funcname")) {
                                if (!value)
                                        return config_error_nonbool(var);
-                               return parse_funcname_pattern(var, ep, value);
+                               return parse_funcname_pattern(var, ep, value,
+                                       0);
                        }
                }
        }
@@ -510,13 +517,20 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
 
 static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
 {
-       int has_trailing_newline = (len > 0 && line[len-1] == '\n');
+       int has_trailing_newline, has_trailing_carriage_return;
+
+       has_trailing_newline = (len > 0 && line[len-1] == '\n');
        if (has_trailing_newline)
                len--;
+       has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+       if (has_trailing_carriage_return)
+               len--;
 
        fputs(set, file);
        fwrite(line, len, 1, file);
        fputs(reset, file);
+       if (has_trailing_carriage_return)
+               fputc('\r', file);
        if (has_trailing_newline)
                fputc('\n', file);
 }
@@ -1053,6 +1067,13 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
        return this_dir;
 }
 
+static int dirstat_compare(const void *_a, const void *_b)
+{
+       const struct dirstat_file *a = _a;
+       const struct dirstat_file *b = _b;
+       return strcmp(a->name, b->name);
+}
+
 static void show_dirstat(struct diff_options *options)
 {
        int i;
@@ -1064,7 +1085,7 @@ static void show_dirstat(struct diff_options *options)
        dir.alloc = 0;
        dir.nr = 0;
        dir.percent = options->dirstat_percent;
-       dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
+       dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
 
        changed = 0;
        for (i = 0; i < q->nr; i++) {
@@ -1112,6 +1133,7 @@ static void show_dirstat(struct diff_options *options)
                return;
 
        /* Show all directories with more than x% of the changes */
+       qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
        gather_dirstat(options->file, &dir, changed, "", 0);
 }
 
@@ -1361,31 +1383,37 @@ int diff_filespec_is_binary(struct diff_filespec *one)
        return one->is_binary;
 }
 
-static const char *funcname_pattern(const char *ident)
+static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
 {
-       struct funcname_pattern *pp;
+       struct funcname_pattern_list *pp;
 
        for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strcmp(ident, pp->name))
-                       return pp->pattern;
+               if (!strcmp(ident, pp->e.name))
+                       return &pp->e;
        return NULL;
 }
 
-static struct builtin_funcname_pattern {
-       const char *name;
-       const char *pattern;
-} builtin_funcname_pattern[] = {
+static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
        { "java", "!^[  ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
                        "new\\|return\\|switch\\|throw\\|while\\)\n"
                        "^[     ]*\\(\\([       ]*"
                        "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
-                       "[      ]*([^;]*\\)$" },
-       { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
+                       "[      ]*([^;]*\\)$", 0 },
+       { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|"
+                       "destructor\\|interface\\|implementation\\|"
+                       "initialization\\|finalization\\)[ \t]*.*\\)$"
+                       "\\|"
+                       "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$",
+                       0 },
+       { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 },
+       { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 },
+       { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$", 0 },
 };
 
-static const char *diff_funcname_pattern(struct diff_filespec *one)
+static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
 {
-       const char *ident, *pattern;
+       const char *ident;
+       const struct funcname_pattern_entry *pe;
        int i;
 
        diff_filespec_check_attr(one);
@@ -1400,9 +1428,9 @@ static const char *diff_funcname_pattern(struct diff_filespec *one)
                return funcname_pattern("default");
 
        /* Look up custom "funcname.$ident" regexp from config. */
-       pattern = funcname_pattern(ident);
-       if (pattern)
-               return pattern;
+       pe = funcname_pattern(ident);
+       if (pe)
+               return pe;
 
        /*
         * And define built-in fallback patterns here.  Note that
@@ -1410,7 +1438,7 @@ static const char *diff_funcname_pattern(struct diff_filespec *one)
         */
        for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
                if (!strcmp(ident, builtin_funcname_pattern[i].name))
-                       return builtin_funcname_pattern[i].pattern;
+                       return &builtin_funcname_pattern[i];
 
        return NULL;
 }
@@ -1488,11 +1516,11 @@ static void builtin_diff(const char *name_a,
                xdemitconf_t xecfg;
                xdemitcb_t ecb;
                struct emit_callback ecbdata;
-               const char *funcname_pattern;
+               const struct funcname_pattern_entry *pe;
 
-               funcname_pattern = diff_funcname_pattern(one);
-               if (!funcname_pattern)
-                       funcname_pattern = diff_funcname_pattern(two);
+               pe = diff_funcname_pattern(one);
+               if (!pe)
+                       pe = diff_funcname_pattern(two);
 
                memset(&xecfg, 0, sizeof(xecfg));
                memset(&ecbdata, 0, sizeof(ecbdata));
@@ -1504,8 +1532,8 @@ static void builtin_diff(const char *name_a,
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
-               if (funcname_pattern)
-                       xdiff_set_find_func(&xecfg, funcname_pattern);
+               if (pe)
+                       xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
                        ;
                else if (!prefixcmp(diffopts, "--unified="))
@@ -1618,12 +1646,14 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
                xdemitcb_t ecb;
 
                memset(&xecfg, 0, sizeof(xecfg));
+               xecfg.ctxlen = 1; /* at least one context line */
                xpp.flags = XDF_NEED_MINIMAL;
                ecb.outf = xdiff_outf;
                ecb.priv = &data;
                xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 
-               if (data.trailing_blanks_start) {
+               if ((data.ws_rule & WS_TRAILING_SPACE) &&
+                   data.trailing_blanks_start) {
                        fprintf(o->file, "%s:%d: ends with blank lines.\n",
                                data.filename, data.trailing_blanks_start);
                        data.status = 1; /* report errors */
@@ -2279,6 +2309,7 @@ void diff_setup(struct diff_options *options)
        options->break_opt = -1;
        options->rename_limit = -1;
        options->dirstat_percent = 3;
+       DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
        options->context = 3;
 
        options->change = diff_change;
@@ -2451,8 +2482,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent))
                options->output_format |= DIFF_FORMAT_DIRSTAT;
-       else if (!strcmp(arg, "--cumulative"))
-               options->output_format |= DIFF_FORMAT_CUMULATIVE;
+       else if (!strcmp(arg, "--cumulative")) {
+               options->output_format |= DIFF_FORMAT_DIRSTAT;
+               DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+       }
        else if (!strcmp(arg, "--check"))
                options->output_format |= DIFF_FORMAT_CHECKDIFF;
        else if (!strcmp(arg, "--summary"))
@@ -3223,11 +3256,10 @@ void diff_flush(struct diff_options *options)
 
        if (output_format & DIFF_FORMAT_PATCH) {
                if (separator) {
+                       putc(options->line_termination, options->file);
                        if (options->stat_sep) {
                                /* attach patch instead of inline */
                                fputs(options->stat_sep, options->file);
-                       } else {
-                               putc(options->line_termination, options->file);
                        }
                }