diff options
Diffstat (limited to 'src/diff_driver.c')
-rw-r--r-- | src/diff_driver.c | 257 |
1 files changed, 177 insertions, 80 deletions
diff --git a/src/diff_driver.c b/src/diff_driver.c index bd5a8fbd9..c3c5f365b 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -14,6 +14,7 @@ #include "strmap.h" #include "map.h" #include "buf_text.h" +#include "config.h" #include "repository.h" GIT__USE_STRMAP; @@ -25,10 +26,13 @@ typedef enum { DIFF_DRIVER_PATTERNLIST = 3, } git_diff_driver_t; +typedef struct { + regex_t re; + int flags; +} git_diff_driver_pattern; + enum { - DIFF_CONTEXT_FIND_NORMAL = 0, - DIFF_CONTEXT_FIND_ICASE = (1 << 0), - DIFF_CONTEXT_FIND_EXT = (1 << 1), + REG_NEGATE = (1 << 15) /* get out of the way of existing flags */ }; /* data for finding function context for a given file type */ @@ -36,11 +40,13 @@ struct git_diff_driver { git_diff_driver_t type; uint32_t binary_flags; uint32_t other_flags; - git_array_t(regex_t) fn_patterns; + git_array_t(git_diff_driver_pattern) fn_patterns; regex_t word_pattern; char name[GIT_FLEX_ARRAY]; }; +#include "userdiff.h" + struct git_diff_driver_registry { git_strmap *drivers; }; @@ -60,7 +66,7 @@ git_diff_driver_registry *git_diff_driver_registry_new() if (!reg) return NULL; - if ((reg->drivers = git_strmap_alloc()) == NULL) { + if (git_strmap_alloc(®->drivers) < 0) { git_diff_driver_registry_free(reg); return NULL; } @@ -80,34 +86,59 @@ void git_diff_driver_registry_free(git_diff_driver_registry *reg) git__free(reg); } -static int diff_driver_add_funcname( - git_diff_driver *drv, const char *name, int regex_flags) +static int diff_driver_add_patterns( + git_diff_driver *drv, const char *regex_str, int regex_flags) { - int error; - regex_t re, *re_ptr; - - if ((error = regcomp(&re, name, regex_flags)) != 0) { - /* TODO: warning about bad regex instead of failure */ - error = giterr_set_regex(&re, error); - regfree(&re); - return error; + int error = 0; + const char *scan, *end; + git_diff_driver_pattern *pat = NULL; + git_buf buf = GIT_BUF_INIT; + + for (scan = regex_str; scan; scan = end) { + /* get pattern to fill in */ + if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) { + error = -1; + break; + } + + pat->flags = regex_flags; + if (*scan == '!') { + pat->flags |= REG_NEGATE; + ++scan; + } + + if ((end = strchr(scan, '\n')) != NULL) { + error = git_buf_set(&buf, scan, end - scan); + end++; + } else { + error = git_buf_sets(&buf, scan); + } + if (error < 0) + break; + + if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) < 0) { + /* if regex fails to compile, warn? fail? */ + error = giterr_set_regex(&pat->re, error); + regfree(&pat->re); + break; + } } - re_ptr = git_array_alloc(drv->fn_patterns); - GITERR_CHECK_ALLOC(re_ptr); + if (error && pat != NULL) + (void)git_array_pop(drv->fn_patterns); /* release last item */ + git_buf_free(&buf); - memcpy(re_ptr, &re, sizeof(re)); - return 0; + return error; } static int diff_driver_xfuncname(const git_config_entry *entry, void *payload) { - return diff_driver_add_funcname(payload, entry->value, REG_EXTENDED); + return diff_driver_add_patterns(payload, entry->value, REG_EXTENDED); } static int diff_driver_funcname(const git_config_entry *entry, void *payload) { - return diff_driver_add_funcname(payload, entry->value, 0); + return diff_driver_add_patterns(payload, entry->value, 0); } static git_diff_driver_registry *git_repository_driver_registry( @@ -127,34 +158,79 @@ static git_diff_driver_registry *git_repository_driver_registry( return repo->diff_drivers; } +static int git_diff_driver_builtin( + git_diff_driver **out, + git_diff_driver_registry *reg, + const char *driver_name) +{ + int error = 0; + git_diff_driver_definition *ddef = NULL; + git_diff_driver *drv = NULL; + size_t namelen, idx; + + for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) { + if (!strcasecmp(driver_name, builtin_defs[idx].name)) { + ddef = &builtin_defs[idx]; + break; + } + } + if (!ddef) + goto done; + + namelen = strlen(ddef->name); + + drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); + GITERR_CHECK_ALLOC(drv); + + drv->type = DIFF_DRIVER_PATTERNLIST; + memcpy(drv->name, ddef->name, namelen); + + if (ddef->fns && + (error = diff_driver_add_patterns( + drv, ddef->fns, ddef->flags | REG_EXTENDED)) < 0) + goto done; + + if (ddef->words && + (error = regcomp( + &drv->word_pattern, ddef->words, ddef->flags | REG_EXTENDED))) + { + error = giterr_set_regex(&drv->word_pattern, error); + goto done; + } + + git_strmap_insert(reg->drivers, drv->name, drv, error); + if (error > 0) + error = 0; + +done: + if (error && drv) + git_diff_driver_free(drv); + else + *out = drv; + + return error; +} + static int git_diff_driver_load( git_diff_driver **out, git_repository *repo, const char *driver_name) { - int error = 0, bval; + int error = 0; git_diff_driver_registry *reg; - git_diff_driver *drv; + git_diff_driver *drv = NULL; size_t namelen = strlen(driver_name); khiter_t pos; git_config *cfg; git_buf name = GIT_BUF_INIT; - const char *val; + const git_config_entry *ce; bool found_driver = false; - reg = git_repository_driver_registry(repo); - if (!reg) + if ((reg = git_repository_driver_registry(repo)) == NULL) return -1; - else { - pos = git_strmap_lookup_index(reg->drivers, driver_name); - if (git_strmap_valid_index(reg->drivers, pos)) { - *out = git_strmap_value_at(reg->drivers, pos); - return 0; - } - } - /* if you can't read config for repo, just use default driver */ - if (git_repository_config__weakptr(&cfg, repo) < 0) { - giterr_clear(); - return GIT_ENOTFOUND; + pos = git_strmap_lookup_index(reg->drivers, driver_name); + if (git_strmap_valid_index(reg->drivers, pos)) { + *out = git_strmap_value_at(reg->drivers, pos); + return 0; } drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); @@ -162,25 +238,29 @@ static int git_diff_driver_load( drv->type = DIFF_DRIVER_AUTO; memcpy(drv->name, driver_name, namelen); + /* if you can't read config for repo, just use default driver */ + if (git_repository_config_snapshot(&cfg, repo) < 0) { + giterr_clear(); + goto done; + } + if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) goto done; - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - /* diff.<driver>.binary unspecified, so just continue */ - giterr_clear(); - } else if (git_config_parse_bool(&bval, val) < 0) { - /* TODO: warn that diff.<driver>.binary has invalid value */ - giterr_clear(); - } else if (bval) { + + switch (git_config__get_bool_force(cfg, name.ptr, -1)) { + case true: /* if diff.<driver>.binary is true, just return the binary driver */ *out = &global_drivers[DIFF_DRIVER_BINARY]; goto done; - } else { + case false: /* if diff.<driver>.binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ drv->binary_flags = GIT_DIFF_FORCE_TEXT; found_driver = true; + break; + default: + /* diff.<driver>.binary unspecified or "auto", so just continue */ + break; } /* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */ @@ -211,16 +291,16 @@ static int git_diff_driver_load( git_buf_truncate(&name, namelen + strlen("diff..")); git_buf_put(&name, "wordregex", strlen("wordregex")); - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - giterr_clear(); /* no diff.<driver>.wordregex, so just continue */ - } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { - /* TODO: warning about bad regex instead of failure */ - error = giterr_set_regex(&drv->word_pattern, error); + if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0) goto done; - } else { + if (!ce || !ce->value) + /* no diff.<driver>.wordregex, so just continue */; + else if (!(error = regcomp(&drv->word_pattern, ce->value, REG_EXTENDED))) found_driver = true; + else { + /* TODO: warn about bad regex instead of failure */ + error = giterr_set_regex(&drv->word_pattern, error); + goto done; } /* TODO: look up diff.<driver>.algorithm to turn on minimal / patience @@ -235,14 +315,19 @@ static int git_diff_driver_load( git_strmap_insert(reg->drivers, drv->name, drv, error); if (error < 0) goto done; + error = 0; *out = drv; done: git_buf_free(&name); + git_config_free(cfg); - if (!*out) - *out = &global_drivers[DIFF_DRIVER_AUTO]; + if (!*out) { + int error2 = git_diff_driver_builtin(out, reg, driver_name); + if (!error) + error = error2; + } if (drv && drv != *out) git_diff_driver_free(drv); @@ -257,14 +342,13 @@ int git_diff_driver_lookup( const char *value; assert(out); + *out = NULL; if (!repo || !path || !strlen(path)) - goto use_auto; - - if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) - return error; - - if (GIT_ATTR_UNSPECIFIED(value)) + /* just use the auto value */; + else if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) + /* return error below */; + else if (GIT_ATTR_UNSPECIFIED(value)) /* just use the auto value */; else if (GIT_ATTR_FALSE(value)) *out = &global_drivers[DIFF_DRIVER_BINARY]; @@ -273,17 +357,16 @@ int git_diff_driver_lookup( /* otherwise look for driver information in config and build driver */ else if ((error = git_diff_driver_load(out, repo, value)) < 0) { - if (error != GIT_ENOTFOUND) - return error; - else + if (error == GIT_ENOTFOUND) { + error = 0; giterr_clear(); + } } -use_auto: if (!*out) *out = &global_drivers[DIFF_DRIVER_AUTO]; - return 0; + return error; } void git_diff_driver_free(git_diff_driver *driver) @@ -294,7 +377,7 @@ void git_diff_driver_free(git_diff_driver *driver) return; for (i = 0; i < git_array_size(driver->fn_patterns); ++i) - regfree(git_array_get(driver->fn_patterns, i)); + regfree(& git_array_get(driver->fn_patterns, i)->re); git_array_clear(driver->fn_patterns); regfree(&driver->word_pattern); @@ -314,7 +397,11 @@ void git_diff_driver_update_options( int git_diff_driver_content_is_binary( git_diff_driver *driver, const char *content, size_t content_len) { - const git_buf search = { (char *)content, 0, min(content_len, 4000) }; + git_buf search; + + search.ptr = (char *)content; + search.size = min(content_len, GIT_FILTER_BYTES_TO_CHECK_NUL); + search.asize = 0; GIT_UNUSED(driver); @@ -331,23 +418,34 @@ int git_diff_driver_content_is_binary( } static int diff_context_line__simple( - git_diff_driver *driver, const char *line, size_t line_len) + git_diff_driver *driver, git_buf *line) { + char firstch = line->ptr[0]; GIT_UNUSED(driver); - GIT_UNUSED(line_len); - return (git__isalpha(*line) || *line == '_' || *line == '$'); + return (git__isalpha(firstch) || firstch == '_' || firstch == '$'); } static int diff_context_line__pattern_match( - git_diff_driver *driver, const char *line, size_t line_len) + git_diff_driver *driver, git_buf *line) { - size_t i; + size_t i, maxi = git_array_size(driver->fn_patterns); + regmatch_t pmatch[2]; + + for (i = 0; i < maxi; ++i) { + git_diff_driver_pattern *pat = git_array_get(driver->fn_patterns, i); - GIT_UNUSED(line_len); + if (!regexec(&pat->re, line->ptr, 2, pmatch, 0)) { + if (pat->flags & REG_NEGATE) + return false; + + /* use pmatch data to trim line data */ + i = (pmatch[1].rm_so >= 0) ? 1 : 0; + git_buf_consume(line, git_buf_cstr(line) + pmatch[i].rm_so); + git_buf_truncate(line, pmatch[i].rm_eo - pmatch[i].rm_so); + git_buf_rtrim(line); - for (i = 0; i < git_array_size(driver->fn_patterns); ++i) { - if (!regexec(git_array_get(driver->fn_patterns, i), line, 0, NULL, 0)) return true; + } } return false; @@ -369,8 +467,7 @@ static long diff_context_find( if (!ctxt->line.size) return -1; - if (!ctxt->match_line || - !ctxt->match_line(ctxt->driver, ctxt->line.ptr, ctxt->line.size)) + if (!ctxt->match_line || !ctxt->match_line(ctxt->driver, &ctxt->line)) return -1; if (out_size > (long)ctxt->line.size) |