diff options
-rw-r--r-- | attr.c | 179 | ||||
-rw-r--r-- | cache.h | 1 |
2 files changed, 132 insertions, 48 deletions
@@ -16,9 +16,12 @@ struct git_attr { struct git_attr *next; unsigned h; + int attr_nr; char name[FLEX_ARRAY]; }; +static int attr_nr; +static struct git_attr_check *check_all_attr; static struct git_attr *(git_attr_hash[HASHSIZE]); static unsigned hash_name(const char *name, int namelen) @@ -50,7 +53,12 @@ struct git_attr *git_attr(const char *name, int len) a->name[len] = 0; a->h = hval; a->next = git_attr_hash[pos]; + a->attr_nr = attr_nr++; git_attr_hash[pos] = a; + + check_all_attr = xrealloc(check_all_attr, + sizeof(*check_all_attr) * attr_nr); + check_all_attr[a->attr_nr].attr = a; return a; } @@ -69,26 +77,46 @@ struct attr_state { }; struct match_attr { - char *pattern; + union { + char *pattern; + struct git_attr *attr; + } u; + char is_macro; unsigned num_attr; struct attr_state state[FLEX_ARRAY]; }; static const char blank[] = " \t\r\n"; -static struct match_attr *parse_attr_line(const char *line) +static struct match_attr *parse_attr_line(const char *line, const char *src, + int lineno, int macro_ok) { int namelen; int num_attr; const char *cp, *name; struct match_attr *res = res; int pass; + int is_macro; cp = line + strspn(line, blank); if (!*cp || *cp == '#') return NULL; name = cp; namelen = strcspn(name, blank); + if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && + !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) { + if (!macro_ok) { + fprintf(stderr, "%s not allowed: %s:%d\n", + name, src, lineno); + return NULL; + } + is_macro = 1; + name += strlen(ATTRIBUTE_MACRO_PREFIX); + name += strspn(name, blank); + namelen = strcspn(name, blank); + } + else + is_macro = 0; for (pass = 0; pass < 2; pass++) { /* pass 0 counts and allocates, pass 1 fills */ @@ -113,13 +141,19 @@ static struct match_attr *parse_attr_line(const char *line) } if (pass) break; + res = xcalloc(1, sizeof(*res) + sizeof(struct attr_state) * num_attr + - namelen + 1); - res->pattern = (char*)&(res->state[num_attr]); - memcpy(res->pattern, name, namelen); - res->pattern[namelen] = 0; + (is_macro ? 0 : namelen + 1)); + if (is_macro) + res->u.attr = git_attr(name, namelen); + else { + res->u.pattern = (char*)&(res->state[num_attr]); + memcpy(res->u.pattern, name, namelen); + res->u.pattern[namelen] = 0; + } + res->is_macro = is_macro; res->num_attr = num_attr; } return res; @@ -167,10 +201,13 @@ static struct attr_stack *read_attr_from_array(const char **list) { struct attr_stack *res; const char *line; + int lineno = 0; res = xcalloc(1, sizeof(*res)); while ((line = *(list++)) != NULL) { - struct match_attr *a = parse_attr_line(line); + struct match_attr *a; + + a = parse_attr_line(line, "[builtin]", ++lineno, 1); if (!a) continue; res->attrs = xrealloc(res->attrs, res->num_matches + 1); @@ -179,11 +216,12 @@ static struct attr_stack *read_attr_from_array(const char **list) return res; } -static struct attr_stack *read_attr_from_file(const char *path) +static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) { FILE *fp; struct attr_stack *res; char buf[2048]; + int lineno = 0; res = xcalloc(1, sizeof(*res)); fp = fopen(path, "r"); @@ -191,7 +229,9 @@ static struct attr_stack *read_attr_from_file(const char *path) return res; while (fgets(buf, sizeof(buf), fp)) { - struct match_attr *a = parse_attr_line(buf); + struct match_attr *a; + + a = parse_attr_line(buf, path, ++lineno, macro_ok); if (!a) continue; res->attrs = xrealloc(res->attrs, res->num_matches + 1); @@ -206,13 +246,42 @@ static void debug_info(const char *what, struct attr_stack *elem) { fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()"); } +static void debug_set(const char *what, const char *match, struct git_attr *attr, int set) +{ + fprintf(stderr, "%s: %s => %d (%s)\n", + what, attr->name, set, match); +} #define debug_push(a) debug_info("push", (a)) #define debug_pop(a) debug_info("pop", (a)) #else #define debug_push(a) do { ; } while (0) #define debug_pop(a) do { ; } while (0) +#define debug_set(a,b,c,d) do { ; } while (0) #endif +static void bootstrap_attr_stack(void) +{ + if (!attr_stack) { + struct attr_stack *elem; + + elem = read_attr_from_array(builtin_attr); + elem->origin = NULL; + elem->prev = attr_stack; + attr_stack = elem; + + elem = read_attr_from_file(GITATTRIBUTES_FILE, 1); + elem->origin = strdup(""); + elem->prev = attr_stack; + attr_stack = elem; + debug_push(elem); + + elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1); + elem->origin = NULL; + elem->prev = attr_stack; + attr_stack = elem; + } +} + static void prepare_attr_stack(const char *path, int dirlen) { struct attr_stack *elem, *info; @@ -232,23 +301,8 @@ static void prepare_attr_stack(const char *path, int dirlen) * .gitattributes in deeper directories to shallower ones, * and finally use the built-in set as the default. */ - if (!attr_stack) { - elem = read_attr_from_array(builtin_attr); - elem->origin = NULL; - elem->prev = attr_stack; - attr_stack = elem; - - elem = read_attr_from_file(GITATTRIBUTES_FILE); - elem->origin = strdup(""); - elem->prev = attr_stack; - attr_stack = elem; - debug_push(elem); - - elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE)); - elem->origin = NULL; - elem->prev = attr_stack; - attr_stack = elem; - } + if (!attr_stack) + bootstrap_attr_stack(); /* * Pop the "info" one that is always at the top of the stack. @@ -286,7 +340,7 @@ static void prepare_attr_stack(const char *path, int dirlen) memcpy(pathbuf + dirlen, "/", 2); cp = strchr(pathbuf + len + 1, '/'); strcpy(cp + 1, GITATTRIBUTES_FILE); - elem = read_attr_from_file(pathbuf); + elem = read_attr_from_file(pathbuf, 0); *cp = '\0'; elem->origin = strdup(pathbuf); elem->prev = attr_stack; @@ -324,31 +378,26 @@ static int path_matches(const char *pathname, int pathlen, return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; } -/* - * I do not like this at all. Only because we allow individual - * attribute to be set or unset incrementally by individual - * lines in .gitattribute files, we need to do this triple - * loop which looks quite wasteful. - */ -static int fill(const char *path, int pathlen, - struct attr_stack *stk, struct git_attr_check *check, - int num, int rem) +static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem) { - int i, j, k; const char *base = stk->origin ? stk->origin : ""; + int i, j; + struct git_attr_check *check = check_all_attr; for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { struct match_attr *a = stk->attrs[i]; + if (a->is_macro) + continue; if (path_matches(path, pathlen, - a->pattern, base, strlen(base))) { - for (j = 0; j < a->num_attr; j++) { + a->u.pattern, base, strlen(base))) { + for (j = 0; 0 < rem && j < a->num_attr; j++) { struct git_attr *attr = a->state[j].attr; int set = !a->state[j].unset; - for (k = 0; k < num; k++) { - if (0 <= check[k].isset || - check[k].attr != attr) - continue; - check[k].isset = set; + int *n = &(check[attr->attr_nr].isset); + + if (*n < 0) { + debug_set("fill", a->u.pattern, attr, set); + *n = set; rem--; } } @@ -357,14 +406,41 @@ static int fill(const char *path, int pathlen, return rem; } +static int macroexpand(struct attr_stack *stk, int rem) +{ + int i, j; + struct git_attr_check *check = check_all_attr; + + for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { + struct match_attr *a = stk->attrs[i]; + if (!a->is_macro) + continue; + if (check[a->u.attr->attr_nr].isset < 0) + continue; + for (j = 0; 0 < rem && j < a->num_attr; j++) { + struct git_attr *attr = a->state[j].attr; + int set = !a->state[j].unset; + int *n = &(check[attr->attr_nr].isset); + + if (*n < 0) { + debug_set("expand", a->u.attr->name, attr, set); + *n = set; + rem--; + } + } + } + return rem; +} + int git_checkattr(const char *path, int num, struct git_attr_check *check) { struct attr_stack *stk; const char *cp; int dirlen, pathlen, i, rem; - for (i = 0; i < num; i++) - check[i].isset = -1; + bootstrap_attr_stack(); + for (i = 0; i < attr_nr; i++) + check_all_attr[i].isset = -1; pathlen = strlen(path); cp = strrchr(path, '/'); @@ -373,8 +449,15 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) else dirlen = cp - path; prepare_attr_stack(path, dirlen); - rem = num; + rem = attr_nr; + for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) + rem = fill(path, pathlen, stk, rem); + for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) - rem = fill(path, pathlen, stk, check, num, rem); + rem = macroexpand(stk, rem); + + for (i = 0; i < num; i++) + check[i].isset = check_all_attr[check[i].attr->attr_nr].isset; + return 0; } @@ -153,6 +153,7 @@ enum object_type { #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH" #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" +#define ATTRIBUTE_MACRO_PREFIX "[attr]" extern int is_bare_repository_cfg; extern int is_bare_repository(void); |