Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/diff_driver.c')
-rw-r--r--src/diff_driver.c257
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(&reg->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)