diff options
Diffstat (limited to 'attr.c')
-rw-r--r-- | attr.c | 250 |
1 files changed, 189 insertions, 61 deletions
@@ -1,3 +1,4 @@ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "attr.h" @@ -34,8 +35,7 @@ static struct git_attr *(git_attr_hash[HASHSIZE]); static unsigned hash_name(const char *name, int namelen) { - unsigned val = 0; - unsigned char c; + unsigned val = 0, c; while (namelen--) { c = *name++; @@ -65,7 +65,7 @@ static int invalid_attr_name(const char *name, int namelen) return 0; } -struct git_attr *git_attr(const char *name, int len) +static struct git_attr *git_attr_internal(const char *name, int len) { unsigned hval = hash_name(name, len); unsigned pos = hval % HASHSIZE; @@ -95,6 +95,11 @@ struct git_attr *git_attr(const char *name, int len) return a; } +struct git_attr *git_attr(const char *name) +{ + return git_attr_internal(name, strlen(name)); +} + /* * .gitattributes file is one line per record, each of which is * @@ -160,14 +165,9 @@ static const char *parse_attr(const char *src, int lineno, const char *cp, else if (!equals) e->setto = ATTR__TRUE; else { - char *value; - int vallen = ep - equals; - value = xmalloc(vallen); - memcpy(value, equals+1, vallen-1); - value[vallen-1] = 0; - e->setto = value; + e->setto = xmemdupz(equals + 1, ep - equals - 1); } - e->attr = git_attr(cp, len); + e->attr = git_attr_internal(cp, len); } (*num_attr)++; return ep + strspn(ep, blank); @@ -214,8 +214,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, num_attr = 0; cp = name + namelen; cp = cp + strspn(cp, blank); - while (*cp) + while (*cp) { cp = parse_attr(src, lineno, cp, &num_attr, res); + if (!cp) + return NULL; + } if (pass) break; res = xcalloc(1, @@ -223,9 +226,9 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, sizeof(struct attr_state) * num_attr + (is_macro ? 0 : namelen + 1)); if (is_macro) - res->u.attr = git_attr(name, namelen); + res->u.attr = git_attr_internal(name, namelen); else { - res->u.pattern = (char*)&(res->state[num_attr]); + res->u.pattern = (char *)&(res->state[num_attr]); memcpy(res->u.pattern, name, namelen); res->u.pattern[namelen] = 0; } @@ -257,6 +260,7 @@ static struct attr_stack { struct attr_stack *prev; char *origin; unsigned num_matches; + unsigned alloc; struct match_attr **attrs; } *attr_stack; @@ -275,7 +279,7 @@ static void free_attr_elem(struct attr_stack *e) setto == ATTR__UNKNOWN) ; else - free((char*) setto); + free((char *) setto); } free(a); } @@ -287,6 +291,26 @@ static const char *builtin_attr[] = { NULL, }; +static void handle_attr_line(struct attr_stack *res, + const char *line, + const char *src, + int lineno, + int macro_ok) +{ + struct match_attr *a; + + 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); + } + res->attrs[res->num_matches++] = a; +} + static struct attr_stack *read_attr_from_array(const char **list) { struct attr_stack *res; @@ -294,42 +318,111 @@ static struct attr_stack *read_attr_from_array(const char **list) int lineno = 0; res = xcalloc(1, sizeof(*res)); - while ((line = *(list++)) != NULL) { - struct match_attr *a; - - a = parse_attr_line(line, "[builtin]", ++lineno, 1); - if (!a) - continue; - res->attrs = xrealloc(res->attrs, - sizeof(struct match_attr *) * (res->num_matches + 1)); - res->attrs[res->num_matches++] = a; - } + while ((line = *(list++)) != NULL) + handle_attr_line(res, line, "[builtin]", ++lineno, 1); return res; } +static enum git_attr_direction direction; +static struct index_state *use_index; + static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) { - FILE *fp; + FILE *fp = fopen(path, "r"); struct attr_stack *res; char buf[2048]; int lineno = 0; - res = xcalloc(1, sizeof(*res)); - fp = fopen(path, "r"); if (!fp) - return res; + return NULL; + res = xcalloc(1, sizeof(*res)); + while (fgets(buf, sizeof(buf), fp)) + handle_attr_line(res, buf, path, ++lineno, macro_ok); + fclose(fp); + return res; +} - while (fgets(buf, sizeof(buf), fp)) { - struct match_attr *a; +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; +} - a = parse_attr_line(buf, path, ++lineno, macro_ok); - if (!a) - continue; - res->attrs = xrealloc(res->attrs, - sizeof(struct match_attr *) * (res->num_matches + 1)); - res->attrs[res->num_matches++] = a; +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); + if (!buf) + return NULL; + + res = xcalloc(1, sizeof(*res)); + for (sp = buf; *sp; ) { + char *ep; + int more; + for (ep = sp; *ep && *ep != '\n'; ep++) + ; + more = (*ep == '\n'); + *ep = '\0'; + handle_attr_line(res, sp, path, ++lineno, macro_ok); + sp = ep + more; } - fclose(fp); + free(buf); + return res; +} + +static struct attr_stack *read_attr(const char *path, int macro_ok) +{ + struct attr_stack *res; + + if (direction == GIT_ATTR_CHECKOUT) { + res = read_attr_from_index(path, macro_ok); + if (!res) + res = read_attr_from_file(path, macro_ok); + } + else if (direction == GIT_ATTR_CHECKIN) { + res = read_attr_from_file(path, macro_ok); + if (!res) + /* + * There is no checked out .gitattributes file there, but + * we might have it in the index. We allow operation in a + * sparsely checked out work tree, so read from it. + */ + res = read_attr_from_index(path, macro_ok); + } + else + res = read_attr_from_index(path, macro_ok); + if (!res) + res = xcalloc(1, sizeof(*res)); return res; } @@ -338,7 +431,7 @@ 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, void *v) +static void debug_set(const char *what, const char *match, struct git_attr *attr, const void *v) { const char *value = v; @@ -360,6 +453,15 @@ static void debug_set(const char *what, const char *match, struct git_attr *attr #define debug_set(a,b,c,d) do { ; } while (0) #endif +static void drop_attr_stack(void) +{ + while (attr_stack) { + struct attr_stack *elem = attr_stack; + attr_stack = elem->prev; + free_attr_elem(elem); + } +} + static void bootstrap_attr_stack(void) { if (!attr_stack) { @@ -370,13 +472,17 @@ static void bootstrap_attr_stack(void) 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); + if (!is_bare_repository() || direction == GIT_ATTR_INDEX) { + elem = read_attr(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); + if (!elem) + elem = xcalloc(1, sizeof(*elem)); elem->origin = NULL; elem->prev = attr_stack; attr_stack = elem; @@ -387,7 +493,9 @@ static void prepare_attr_stack(const char *path, int dirlen) { struct attr_stack *elem, *info; int len; - char pathbuf[PATH_MAX]; + struct strbuf pathbuf; + + strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE)); /* * At the bottom of the attribute stack is the built-in @@ -431,24 +539,29 @@ static void prepare_attr_stack(const char *path, int dirlen) /* * Read from parent directories and push them down */ - while (1) { - char *cp; - - len = strlen(attr_stack->origin); - if (dirlen <= len) - break; - memcpy(pathbuf, path, dirlen); - memcpy(pathbuf + dirlen, "/", 2); - cp = strchr(pathbuf + len + 1, '/'); - strcpy(cp + 1, GITATTRIBUTES_FILE); - elem = read_attr_from_file(pathbuf, 0); - *cp = '\0'; - elem->origin = strdup(pathbuf); - elem->prev = attr_stack; - attr_stack = elem; - debug_push(elem); + if (!is_bare_repository() || direction == GIT_ATTR_INDEX) { + while (1) { + char *cp; + + len = strlen(attr_stack->origin); + if (dirlen <= len) + break; + strbuf_reset(&pathbuf); + strbuf_add(&pathbuf, path, dirlen); + strbuf_addch(&pathbuf, '/'); + cp = strchr(pathbuf.buf + len + 1, '/'); + strcpy(cp + 1, GITATTRIBUTES_FILE); + elem = read_attr(pathbuf.buf, 0); + *cp = '\0'; + elem->origin = strdup(pathbuf.buf); + elem->prev = attr_stack; + attr_stack = elem; + debug_push(elem); + } } + strbuf_release(&pathbuf); + /* * Finally push the "info" one at the top of the stack. */ @@ -473,9 +586,11 @@ static int path_matches(const char *pathname, int pathlen, if (*pattern == '/') pattern++; if (pathlen < baselen || - (baselen && pathname[baselen - 1] != '/') || + (baselen && pathname[baselen] != '/') || strncmp(pathname, base, baselen)) return 0; + if (baselen != 0) + baselen++; return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; } @@ -563,3 +678,16 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) return 0; } + +void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate) +{ + enum git_attr_direction old = direction; + + if (is_bare_repository() && new != GIT_ATTR_INDEX) + die("BUG: non-INDEX attr direction in a bare repo"); + + direction = new; + if (new != old) + drop_attr_stack(); + use_index = istate; +} |