summaryrefslogtreecommitdiff
path: root/attr.c
diff options
context:
space:
mode:
Diffstat (limited to 'attr.c')
-rw-r--r--attr.c276
1 files changed, 135 insertions, 141 deletions
diff --git a/attr.c b/attr.c
index 33cb4e4d11..cd5469770a 100644
--- a/attr.c
+++ b/attr.c
@@ -11,6 +11,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "attr.h"
+#include "dir.h"
const char git_attr__true[] = "(builtin)true";
const char git_attr__false[] = "\0(builtin)false";
@@ -20,8 +21,6 @@ static const char git_attr__unknown[] = "(builtin)unknown";
#define ATTR__UNSET NULL
#define ATTR__UNKNOWN git_attr__unknown
-static const char *attributes_file;
-
/* This is a randomly chosen prime. */
#define HASHSIZE 257
@@ -98,8 +97,7 @@ static struct git_attr *git_attr_internal(const char *name, int len)
a->attr_nr = attr_nr++;
git_attr_hash[pos] = a;
- check_all_attr = xrealloc(check_all_attr,
- sizeof(*check_all_attr) * attr_nr);
+ REALLOC_ARRAY(check_all_attr, attr_nr);
check_all_attr[a->attr_nr].attr = a;
check_all_attr[a->attr_nr].value = ATTR__UNKNOWN;
return a;
@@ -116,6 +114,13 @@ struct attr_state {
const char *setto;
};
+struct pattern {
+ const char *pattern;
+ int patternlen;
+ int nowildcardlen;
+ int flags; /* EXC_FLAG_* */
+};
+
/*
* One rule, as from a .gitattributes file.
*
@@ -132,7 +137,7 @@ struct attr_state {
*/
struct match_attr {
union {
- char *pattern;
+ struct pattern pat;
struct git_attr *attr;
} u;
char is_macro;
@@ -205,7 +210,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
name = cp;
namelen = strcspn(name, blank);
if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
- !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) {
+ starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
if (!macro_ok) {
fprintf(stderr, "%s not allowed: %s:%d\n",
name, src, lineno);
@@ -242,9 +247,18 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
if (is_macro)
res->u.attr = git_attr_internal(name, namelen);
else {
- res->u.pattern = (char *)&(res->state[num_attr]);
- memcpy(res->u.pattern, name, namelen);
- res->u.pattern[namelen] = 0;
+ char *p = (char *)&(res->state[num_attr]);
+ memcpy(p, name, namelen);
+ res->u.pat.pattern = p;
+ parse_exclude_pattern(&res->u.pat.pattern,
+ &res->u.pat.patternlen,
+ &res->u.pat.flags,
+ &res->u.pat.nowildcardlen);
+ if (res->u.pat.flags & EXC_FLAG_NEGATIVE) {
+ warning(_("Negative patterns are ignored in git attributes\n"
+ "Use '\\!' for literal leading exclamation."));
+ return NULL;
+ }
}
res->is_macro = is_macro;
res->num_attr = num_attr;
@@ -271,13 +285,14 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
* (reading the file from top to bottom), .gitattribute of the root
* directory (again, reading the file from top to bottom) down to the
* current directory, and then scan the list backwards to find the first match.
- * This is exactly the same as what excluded() does in dir.c to deal with
+ * This is exactly the same as what is_excluded() does in dir.c to deal with
* .gitignore
*/
static struct attr_stack {
struct attr_stack *prev;
char *origin;
+ size_t originlen;
unsigned num_matches;
unsigned alloc;
struct match_attr **attrs;
@@ -302,11 +317,12 @@ static void free_attr_elem(struct attr_stack *e)
}
free(a);
}
+ free(e->attrs);
free(e);
}
static const char *builtin_attr[] = {
- "[attr]binary -diff -text",
+ "[attr]binary -diff -merge -text",
NULL,
};
@@ -321,12 +337,7 @@ static void handle_attr_line(struct attr_stack *res,
a = parse_attr_line(line, src, lineno, macro_ok);
if (!a)
return;
- if (res->alloc <= res->num_matches) {
- res->alloc = alloc_nr(res->num_matches);
- res->attrs = xrealloc(res->attrs,
- sizeof(struct match_attr *) *
- res->alloc);
- }
+ ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc);
res->attrs[res->num_matches++] = a;
}
@@ -352,8 +363,11 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
char buf[2048];
int lineno = 0;
- if (!fp)
+ if (!fp) {
+ if (errno != ENOENT && errno != ENOTDIR)
+ warn_on_inaccessible(path);
return NULL;
+ }
res = xcalloc(1, sizeof(*res));
while (fgets(buf, sizeof(buf), fp))
handle_attr_line(res, buf, path, ++lineno, macro_ok);
@@ -361,46 +375,13 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
return res;
}
-static void *read_index_data(const char *path)
-{
- int pos, len;
- unsigned long sz;
- enum object_type type;
- void *data;
- struct index_state *istate = use_index ? use_index : &the_index;
-
- len = strlen(path);
- pos = index_name_pos(istate, path, len);
- if (pos < 0) {
- /*
- * We might be in the middle of a merge, in which
- * case we would read stage #2 (ours).
- */
- int i;
- for (i = -pos - 1;
- (pos < 0 && i < istate->cache_nr &&
- !strcmp(istate->cache[i]->name, path));
- i++)
- if (ce_stage(istate->cache[i]) == 2)
- pos = i;
- }
- if (pos < 0)
- return NULL;
- data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
- if (!data || type != OBJ_BLOB) {
- free(data);
- return NULL;
- }
- return data;
-}
-
static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
{
struct attr_stack *res;
char *buf, *sp;
int lineno = 0;
- buf = read_index_data(path);
+ buf = read_blob_data_from_index(use_index ? use_index : &the_index, path, NULL);
if (!buf)
return NULL;
@@ -494,75 +475,64 @@ static int git_attr_system(void)
return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
}
-static int git_attr_config(const char *var, const char *value, void *dummy)
-{
- if (!strcmp(var, "core.attributesfile"))
- return git_config_pathname(&attributes_file, var, value);
-
- return 0;
-}
-
static void bootstrap_attr_stack(void)
{
- if (!attr_stack) {
- struct attr_stack *elem;
+ struct attr_stack *elem;
+ char *xdg_attributes_file;
- elem = read_attr_from_array(builtin_attr);
- elem->origin = NULL;
- elem->prev = attr_stack;
- attr_stack = elem;
+ if (attr_stack)
+ return;
- if (git_attr_system()) {
- elem = read_attr_from_file(git_etc_gitattributes(), 1);
- if (elem) {
- elem->origin = NULL;
- elem->prev = attr_stack;
- attr_stack = elem;
- }
- }
+ elem = read_attr_from_array(builtin_attr);
+ elem->origin = NULL;
+ elem->prev = attr_stack;
+ attr_stack = elem;
- git_config(git_attr_config, NULL);
- if (attributes_file) {
- elem = read_attr_from_file(attributes_file, 1);
- if (elem) {
- elem->origin = NULL;
- elem->prev = attr_stack;
- attr_stack = elem;
- }
+ if (git_attr_system()) {
+ elem = read_attr_from_file(git_etc_gitattributes(), 1);
+ if (elem) {
+ elem->origin = NULL;
+ elem->prev = attr_stack;
+ attr_stack = elem;
}
+ }
- if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
- elem = read_attr(GITATTRIBUTES_FILE, 1);
- elem->origin = strdup("");
+ if (!git_attributes_file) {
+ home_config_paths(NULL, &xdg_attributes_file, "attributes");
+ git_attributes_file = xdg_attributes_file;
+ }
+ if (git_attributes_file) {
+ elem = read_attr_from_file(git_attributes_file, 1);
+ if (elem) {
+ elem->origin = NULL;
elem->prev = attr_stack;
attr_stack = elem;
- debug_push(elem);
}
+ }
- elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
- if (!elem)
- elem = xcalloc(1, sizeof(*elem));
- elem->origin = NULL;
+ if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
+ elem = read_attr(GITATTRIBUTES_FILE, 1);
+ elem->origin = xstrdup("");
+ elem->originlen = 0;
elem->prev = attr_stack;
attr_stack = elem;
+ debug_push(elem);
}
+
+ elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
+ if (!elem)
+ elem = xcalloc(1, sizeof(*elem));
+ elem->origin = NULL;
+ elem->prev = attr_stack;
+ attr_stack = elem;
}
-static void prepare_attr_stack(const char *path)
+static void prepare_attr_stack(const char *path, int dirlen)
{
struct attr_stack *elem, *info;
- int dirlen, len;
- struct strbuf pathbuf;
+ int len;
const char *cp;
- cp = strrchr(path, '/');
- if (!cp)
- dirlen = 0;
- else
- dirlen = cp - path;
-
- strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE));
-
/*
* At the bottom of the attribute stack is the built-in
* set of attribute definitions, followed by the contents
@@ -588,14 +558,17 @@ static void prepare_attr_stack(const char *path)
/*
* Pop the ones from directories that are not the prefix of
- * the path we are checking.
+ * the path we are checking. Break out of the loop when we see
+ * the root one (whose origin is an empty string "") or the builtin
+ * one (whose origin is NULL) without popping it.
*/
- while (attr_stack && attr_stack->origin) {
+ while (attr_stack->origin) {
int namelen = strlen(attr_stack->origin);
elem = attr_stack;
if (namelen <= dirlen &&
- !strncmp(elem->origin, path, namelen))
+ !strncmp(elem->origin, path, namelen) &&
+ (!namelen || path[namelen] == '/'))
break;
debug_pop(elem);
@@ -607,27 +580,35 @@ static void prepare_attr_stack(const char *path)
* Read from parent directories and push them down
*/
if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
- while (1) {
- char *cp;
+ /*
+ * bootstrap_attr_stack() should have added, and the
+ * above loop should have stopped before popping, the
+ * root element whose attr_stack->origin is set to an
+ * empty string.
+ */
+ struct strbuf pathbuf = STRBUF_INIT;
+ assert(attr_stack->origin);
+ while (1) {
len = strlen(attr_stack->origin);
if (dirlen <= len)
break;
- strbuf_reset(&pathbuf);
- strbuf_add(&pathbuf, path, dirlen);
+ cp = memchr(path + len + 1, '/', dirlen - len - 1);
+ if (!cp)
+ cp = path + dirlen;
+ strbuf_add(&pathbuf, path, cp - path);
strbuf_addch(&pathbuf, '/');
- cp = strchr(pathbuf.buf + len + 1, '/');
- strcpy(cp + 1, GITATTRIBUTES_FILE);
+ strbuf_addstr(&pathbuf, GITATTRIBUTES_FILE);
elem = read_attr(pathbuf.buf, 0);
- *cp = '\0';
- elem->origin = strdup(pathbuf.buf);
+ strbuf_setlen(&pathbuf, cp - path);
+ elem->origin = strbuf_detach(&pathbuf, &elem->originlen);
elem->prev = attr_stack;
attr_stack = elem;
debug_push(elem);
}
- }
- strbuf_release(&pathbuf);
+ strbuf_release(&pathbuf);
+ }
/*
* Finally push the "info" one at the top of the stack.
@@ -637,28 +618,26 @@ static void prepare_attr_stack(const char *path)
}
static int path_matches(const char *pathname, int pathlen,
- const char *pattern,
+ int basename_offset,
+ const struct pattern *pat,
const char *base, int baselen)
{
- if (!strchr(pattern, '/')) {
- /* match basename */
- const char *basename = strrchr(pathname, '/');
- basename = basename ? basename + 1 : pathname;
- return (fnmatch(pattern, basename, 0) == 0);
- }
- /*
- * match with FNM_PATHNAME; the pattern has base implicitly
- * in front of it.
- */
- if (*pattern == '/')
- pattern++;
- if (pathlen < baselen ||
- (baselen && pathname[baselen] != '/') ||
- strncmp(pathname, base, baselen))
+ const char *pattern = pat->pattern;
+ int prefix = pat->nowildcardlen;
+ int isdir = (pathlen && pathname[pathlen - 1] == '/');
+
+ if ((pat->flags & EXC_FLAG_MUSTBEDIR) && !isdir)
return 0;
- if (baselen != 0)
- baselen++;
- return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+
+ if (pat->flags & EXC_FLAG_NODIR) {
+ return match_basename(pathname + basename_offset,
+ pathlen - basename_offset - isdir,
+ pattern, prefix,
+ pat->patternlen, pat->flags);
+ }
+ return match_pathname(pathname, pathlen - isdir,
+ base, baselen,
+ pattern, prefix, pat->patternlen, pat->flags);
}
static int macroexpand_one(int attr_nr, int rem);
@@ -675,7 +654,7 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
if (*n == ATTR__UNKNOWN) {
debug_set(what,
- a->is_macro ? a->u.attr->name : a->u.pattern,
+ a->is_macro ? a->u.attr->name : a->u.pat.pattern,
attr, v);
*n = v;
rem--;
@@ -685,7 +664,8 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
return rem;
}
-static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
+static int fill(const char *path, int pathlen, int basename_offset,
+ struct attr_stack *stk, int rem)
{
int i;
const char *base = stk->origin ? stk->origin : "";
@@ -694,8 +674,8 @@ static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
struct match_attr *a = stk->attrs[i];
if (a->is_macro)
continue;
- if (path_matches(path, pathlen,
- a->u.pattern, base, strlen(base)))
+ if (path_matches(path, pathlen, basename_offset,
+ &a->u.pat, base, stk->originlen))
rem = fill_one("fill", a, rem);
}
return rem;
@@ -732,16 +712,30 @@ static int macroexpand_one(int attr_nr, int rem)
static void collect_all_attrs(const char *path)
{
struct attr_stack *stk;
- int i, pathlen, rem;
+ int i, pathlen, rem, dirlen;
+ const char *cp, *last_slash = NULL;
+ int basename_offset;
+
+ for (cp = path; *cp; cp++) {
+ if (*cp == '/' && cp[1])
+ last_slash = cp;
+ }
+ pathlen = cp - path;
+ if (last_slash) {
+ basename_offset = last_slash + 1 - path;
+ dirlen = last_slash - path;
+ } else {
+ basename_offset = 0;
+ dirlen = 0;
+ }
- prepare_attr_stack(path);
+ prepare_attr_stack(path, dirlen);
for (i = 0; i < attr_nr; i++)
check_all_attr[i].value = ATTR__UNKNOWN;
- pathlen = strlen(path);
rem = attr_nr;
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
- rem = fill(path, pathlen, stk, rem);
+ rem = fill(path, pathlen, basename_offset, stk, rem);
}
int git_check_attr(const char *path, int num, struct git_attr_check *check)