const char *setto;
};
+struct pattern {
+ const char *pattern;
+ int patternlen;
+ int nowildcardlen;
+ int flags; /* EXC_FLAG_* */
+};
+
/*
* One rule, as from a .gitattributes file.
*
*/
struct match_attr {
union {
- char *pattern;
+ struct pattern pat;
struct git_attr *attr;
} u;
char is_macro;
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)
+ die(_("Negative patterns are forbidden in git attributes\n"
+ "Use '\\!' for literal leading exclamation."));
}
res->is_macro = is_macro;
res->num_attr = num_attr;
* (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;
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);
attr_stack = elem;
}
+static const char *find_basename(const char *path)
+{
+ const char *cp, *last_slash = NULL;
+
+ for (cp = path; *cp; cp++) {
+ if (*cp == '/' && cp[1])
+ last_slash = cp;
+ }
+ return last_slash ? last_slash + 1 : path;
+}
+
static void prepare_attr_stack(const char *path)
{
struct attr_stack *elem, *info;
int dirlen, len;
const char *cp;
- cp = strrchr(path, '/');
- if (!cp)
- dirlen = 0;
- else
- dirlen = cp - path;
+ dirlen = find_basename(path) - path;
/*
* At the bottom of the attribute stack is the built-in
strbuf_addstr(&pathbuf, GITATTRIBUTES_FILE);
elem = read_attr(pathbuf.buf, 0);
strbuf_setlen(&pathbuf, cp - path);
- elem->origin = strbuf_detach(&pathbuf, NULL);
+ elem->origin = strbuf_detach(&pathbuf, &elem->originlen);
elem->prev = attr_stack;
attr_stack = elem;
debug_push(elem);
}
static int path_matches(const char *pathname, int pathlen,
- const char *pattern,
+ const char *basename,
+ 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_icase(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;
+
+ if ((pat->flags & EXC_FLAG_MUSTBEDIR) &&
+ ((!pathlen) || (pathname[pathlen-1] != '/')))
return 0;
- if (baselen != 0)
- baselen++;
- return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+
+ if (pat->flags & EXC_FLAG_NODIR) {
+ return match_basename(basename,
+ pathlen - (basename - pathname),
+ pattern, prefix,
+ pat->patternlen, pat->flags);
+ }
+ return match_pathname(pathname, pathlen,
+ base, baselen,
+ pattern, prefix, pat->patternlen, pat->flags);
}
static int macroexpand_one(int attr_nr, 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--;
return rem;
}
-static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem)
+static int fill(const char *path, int pathlen, const char *basename,
+ struct attr_stack *stk, int rem)
{
int i;
const char *base = stk->origin ? stk->origin : "";
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,
+ &a->u.pat, base, stk->originlen))
rem = fill_one("fill", a, rem);
}
return rem;
{
struct attr_stack *stk;
int i, pathlen, rem;
+ const char *basename;
prepare_attr_stack(path);
for (i = 0; i < attr_nr; i++)
check_all_attr[i].value = ATTR__UNKNOWN;
+ basename = find_basename(path);
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, stk, rem);
}
int git_check_attr(const char *path, int num, struct git_attr_check *check)