X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=attr.c;h=92704a3f61c6d43d0377d86feb6598e68797022b;hb=182af8343c307436bb5364309aa6d4d46fa5911d;hp=60fe48f3b84d7dfa2d0e3f27852ceae03d7487d4;hpb=e4aee10a2eaf0937d86d046f85ee569a75cae9ac;p=git.git diff --git a/attr.c b/attr.c index 60fe48f3b..92704a3f6 100644 --- a/attr.c +++ b/attr.c @@ -1,6 +1,14 @@ #include "cache.h" #include "attr.h" +const char git_attr__true[] = "(builtin)true"; +const char git_attr__false[] = "\0(builtin)false"; +static const char git_attr__unknown[] = "(builtin)unknown"; +#define ATTR__TRUE git_attr__true +#define ATTR__FALSE git_attr__false +#define ATTR__UNSET NULL +#define ATTR__UNKNOWN git_attr__unknown + /* * The basic design decision here is that we are not going to have * insanely large number of attributes. @@ -83,6 +91,7 @@ struct git_attr *git_attr(const char *name, int len) check_all_attr = xrealloc(check_all_attr, sizeof(*check_all_attr) * attr_nr); check_all_attr[a->attr_nr].attr = a; + check_all_attr[a->attr_nr].value = ATTR__UNKNOWN; return a; } @@ -92,12 +101,14 @@ struct git_attr *git_attr(const char *name, int len) * (1) glob pattern. * (2) whitespace * (3) whitespace separated list of attribute names, each of which - * could be prefixed with '-' to mean "not set". + * could be prefixed with '-' to mean "set to false", '!' to mean + * "unset". */ +/* What does a matched pattern decide? */ struct attr_state { - int unset; struct git_attr *attr; + const char *setto; }; struct match_attr { @@ -112,13 +123,58 @@ struct match_attr { static const char blank[] = " \t\r\n"; +static const char *parse_attr(const char *src, int lineno, const char *cp, + int *num_attr, struct match_attr *res) +{ + const char *ep, *equals; + int len; + + ep = cp + strcspn(cp, blank); + equals = strchr(cp, '='); + if (equals && ep < equals) + equals = NULL; + if (equals) + len = equals - cp; + else + len = ep - cp; + if (!res) { + if (*cp == '-' || *cp == '!') { + cp++; + len--; + } + if (invalid_attr_name(cp, len)) { + fprintf(stderr, + "%.*s is not a valid attribute name: %s:%d\n", + len, cp, src, lineno); + return NULL; + } + } else { + struct attr_state *e; + + e = &(res->state[*num_attr]); + if (*cp == '-' || *cp == '!') { + e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET; + cp++; + len--; + } + else if (!equals) + e->setto = ATTR__TRUE; + else { + e->setto = xmemdupz(equals + 1, ep - equals - 1); + } + e->attr = git_attr(cp, len); + } + (*num_attr)++; + return ep + strspn(ep, blank); +} + 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; + struct match_attr *res = NULL; int pass; int is_macro; @@ -153,42 +209,16 @@ 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) { - const char *ep; - ep = cp + strcspn(cp, blank); - if (!pass) { - if (*cp == '-') - cp++; - if (invalid_attr_name(cp, ep - cp)) { - fprintf(stderr, - "%.*s is not a valid attribute name: %s:%d\n", - (int)(ep - cp), cp, - src, lineno); - return NULL; - } - } else { - struct attr_state *e; - - e = &(res->state[num_attr]); - if (*cp == '-') { - e->unset = 1; - cp++; - } - e->attr = git_attr(cp, ep - cp); - } - num_attr++; - cp = ep + strspn(ep, blank); - } + while (*cp) + cp = parse_attr(src, lineno, cp, &num_attr, res); if (pass) break; - res = xcalloc(1, sizeof(*res) + sizeof(struct attr_state) * num_attr + (is_macro ? 0 : namelen + 1)); - if (is_macro) { + 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); @@ -205,9 +235,9 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, * come from many places. * * (1) .gitattribute file of the same directory; - * (2) .gitattribute file of the parent directory if (1) does not have any match; - * this goes recursively upwards, just like .gitignore - * (3) perhaps $GIT_DIR/info/attributes, as the final fallback. + * (2) .gitattribute file of the parent directory if (1) does not have + * any match; this goes recursively upwards, just like .gitignore. + * (3) $GIT_DIR/info/attributes, which overrides both of the above. * * In the same file, later entries override the earlier match, so in the * global list, we would have entries from info/attributes the earliest @@ -222,6 +252,7 @@ static struct attr_stack { struct attr_stack *prev; char *origin; unsigned num_matches; + unsigned alloc; struct match_attr **attrs; } *attr_stack; @@ -229,8 +260,21 @@ static void free_attr_elem(struct attr_stack *e) { int i; free(e->origin); - for (i = 0; i < e->num_matches; i++) - free(e->attrs[i]); + for (i = 0; i < e->num_matches; i++) { + struct match_attr *a = e->attrs[i]; + int j; + for (j = 0; j < a->num_attr; j++) { + const char *setto = a->state[j].setto; + if (setto == ATTR__TRUE || + setto == ATTR__FALSE || + setto == ATTR__UNSET || + setto == ATTR__UNKNOWN) + ; + else + free((char*) setto); + } + free(a); + } free(e); } @@ -239,6 +283,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; @@ -246,40 +310,91 @@ 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, 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 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 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; +} + +static void *read_index_data(const char *path) +{ + int pos, len; + unsigned long sz; + enum object_type type; + void *data; + + len = strlen(path); + pos = cache_name_pos(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 < active_nr && + !strcmp(active_cache[i]->name, path)); + i++) + if (ce_stage(active_cache[i]) == 2) + pos = i; + } + if (pos < 0) + return NULL; + data = read_sha1_file(active_cache[pos]->sha1, &type, &sz); + if (!data || type != OBJ_BLOB) { + free(data); + return NULL; + } + return data; +} + +static struct attr_stack *read_attr(const char *path, int macro_ok) +{ + struct attr_stack *res; + char *buf, *sp; + int lineno = 0; + + res = read_attr_from_file(path, macro_ok); + if (res) return res; - while (fgets(buf, sizeof(buf), fp)) { - struct match_attr *a; + res = xcalloc(1, sizeof(*res)); - a = parse_attr_line(buf, path, ++lineno, macro_ok); - if (!a) - continue; - res->attrs = xrealloc(res->attrs, res->num_matches + 1); - res->attrs[res->num_matches++] = a; + /* + * 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. + */ + buf = read_index_data(path); + if (!buf) + return 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; } @@ -288,10 +403,19 @@ 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) +static void debug_set(const char *what, const char *match, struct git_attr *attr, void *v) { - fprintf(stderr, "%s: %s => %d (%s)\n", - what, attr->name, set, match); + const char *value = v; + + if (ATTR_TRUE(value)) + value = "set"; + else if (ATTR_FALSE(value)) + value = "unset"; + else if (ATTR_UNSET(value)) + value = "unspecified"; + + fprintf(stderr, "%s: %s => %s (%s)\n", + what, attr->name, (char *) value, match); } #define debug_push(a) debug_info("push", (a)) #define debug_pop(a) debug_info("pop", (a)) @@ -311,13 +435,15 @@ static void bootstrap_attr_stack(void) elem->prev = attr_stack; attr_stack = elem; - elem = read_attr_from_file(GITATTRIBUTES_FILE, 1); + 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; @@ -382,7 +508,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, 0); + elem = read_attr(pathbuf, 0); *cp = '\0'; elem->origin = strdup(pathbuf); elem->prev = attr_stack; @@ -420,56 +546,53 @@ static int path_matches(const char *pathname, int pathlen, return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; } +static int fill_one(const char *what, struct match_attr *a, int rem) +{ + struct git_attr_check *check = check_all_attr; + int i; + + for (i = 0; 0 < rem && i < a->num_attr; i++) { + struct git_attr *attr = a->state[i].attr; + const char **n = &(check[attr->attr_nr].value); + const char *v = a->state[i].setto; + + if (*n == ATTR__UNKNOWN) { + debug_set(what, a->u.pattern, attr, v); + *n = v; + rem--; + } + } + return rem; +} + static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem) { + int i; 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->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; - int *n = &(check[attr->attr_nr].isset); - - if (*n < 0) { - debug_set("fill", a->u.pattern, attr, set); - *n = set; - rem--; - } - } - } + a->u.pattern, base, strlen(base))) + rem = fill_one("fill", a, rem); } return rem; } static int macroexpand(struct attr_stack *stk, int rem) { - int i, j; + int i; 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) + if (check[a->u.attr->attr_nr].value != ATTR__TRUE) 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--; - } - } + rem = fill_one("expand", a, rem); } return rem; } @@ -482,7 +605,7 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) bootstrap_attr_stack(); for (i = 0; i < attr_nr; i++) - check_all_attr[i].isset = -1; + check_all_attr[i].value = ATTR__UNKNOWN; pathlen = strlen(path); cp = strrchr(path, '/'); @@ -498,8 +621,12 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) rem = macroexpand(stk, rem); - for (i = 0; i < num; i++) - check[i].isset = check_all_attr[check[i].attr->attr_nr].isset; + for (i = 0; i < num; i++) { + const char *value = check_all_attr[check[i].attr->attr_nr].value; + if (value == ATTR__UNKNOWN) + value = ATTR__UNSET; + check[i].value = value; + } return 0; }