diff options
Diffstat (limited to 'attr.c')
-rw-r--r-- | attr.c | 225 |
1 files changed, 163 insertions, 62 deletions
@@ -7,13 +7,21 @@ */ #include "cache.h" +#include "alloc.h" #include "config.h" +#include "environment.h" #include "exec-cmd.h" #include "attr.h" #include "dir.h" +#include "gettext.h" #include "utf8.h" #include "quote.h" +#include "revision.h" +#include "object-store.h" +#include "setup.h" #include "thread-utils.h" +#include "tree-walk.h" +#include "object-name.h" const char git_attr__true[] = "(builtin)true"; const char git_attr__false[] = "\0(builtin)false"; @@ -24,7 +32,7 @@ static const char git_attr__unknown[] = "(builtin)unknown"; #define ATTR__UNKNOWN git_attr__unknown struct git_attr { - int attr_nr; /* unique attribute number */ + unsigned int attr_nr; /* unique attribute number */ char name[FLEX_ARRAY]; /* attribute name */ }; @@ -206,7 +214,7 @@ static void report_invalid_attr(const char *name, size_t len, * dictionary. If no entry is found, create a new attribute and store it in * the dictionary. */ -static const struct git_attr *git_attr_internal(const char *name, int namelen) +static const struct git_attr *git_attr_internal(const char *name, size_t namelen) { struct git_attr *a; @@ -222,8 +230,8 @@ static const struct git_attr *git_attr_internal(const char *name, int namelen) a->attr_nr = hashmap_get_size(&g_attr_hashmap.map); attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a); - assert(a->attr_nr == - (hashmap_get_size(&g_attr_hashmap.map) - 1)); + if (a->attr_nr != hashmap_get_size(&g_attr_hashmap.map) - 1) + die(_("unable to add additional attribute")); } hashmap_unlock(&g_attr_hashmap); @@ -268,7 +276,7 @@ struct match_attr { const struct git_attr *attr; } u; char is_macro; - unsigned num_attr; + size_t num_attr; struct attr_state state[FLEX_ARRAY]; }; @@ -289,7 +297,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp, struct attr_state *e) { const char *ep, *equals; - int len; + size_t len; ep = cp + strcspn(cp, blank); equals = strchr(cp, '='); @@ -333,8 +341,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp, static struct match_attr *parse_attr_line(const char *line, const char *src, int lineno, unsigned flags) { - int namelen; - int num_attr, i; + size_t namelen, num_attr, i; const char *cp, *name, *states; struct match_attr *res = NULL; int is_macro; @@ -345,6 +352,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, return NULL; name = cp; + if (strlen(line) >= ATTR_MAX_LINE_LENGTH) { + warning(_("ignoring overly long attributes line %d"), lineno); + return NULL; + } + if (*cp == '"' && !unquote_c_style(&pattern, name, &states)) { name = pattern.buf; namelen = pattern.len; @@ -381,10 +393,9 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, goto fail_return; } - res = xcalloc(1, - sizeof(*res) + - sizeof(struct attr_state) * num_attr + - (is_macro ? 0 : namelen + 1)); + res = xcalloc(1, st_add3(sizeof(*res), + st_mult(sizeof(struct attr_state), num_attr), + is_macro ? 0 : namelen + 1)); if (is_macro) { res->u.attr = git_attr_internal(name, namelen); } else { @@ -447,11 +458,12 @@ struct attr_stack { static void attr_stack_free(struct attr_stack *e) { - int i; + unsigned i; free(e->origin); for (i = 0; i < e->num_matches; i++) { struct match_attr *a = e->attrs[i]; - int j; + size_t j; + for (j = 0; j < a->num_attr; j++) { const char *setto = a->state[j].setto; if (setto == ATTR__TRUE || @@ -599,8 +611,7 @@ struct attr_check *attr_check_dup(const struct attr_check *check) ret->nr = check->nr; ret->alloc = check->alloc; - ALLOC_ARRAY(ret->items, ret->nr); - COPY_ARRAY(ret->items, check->items, ret->nr); + DUP_ARRAY(ret->items, check->items, ret->nr); return ret; } @@ -660,8 +671,8 @@ static void handle_attr_line(struct attr_stack *res, a = parse_attr_line(line, src, lineno, flags); if (!a) return; - ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc); - res->attrs[res->num_matches++] = a; + ALLOC_GROW_BY(res->attrs, res->num_matches, 1, res->alloc); + res->attrs[res->num_matches - 1] = a; } static struct attr_stack *read_attr_from_array(const char **list) @@ -701,11 +712,12 @@ void git_attr_set_direction(enum git_attr_direction new_direction) static struct attr_stack *read_attr_from_file(const char *path, unsigned flags) { + struct strbuf buf = STRBUF_INIT; int fd; FILE *fp; struct attr_stack *res; - char buf[2048]; int lineno = 0; + struct stat st; if (flags & READ_ATTR_NOFOLLOW) fd = open_nofollow(path, O_RDONLY); @@ -717,26 +729,86 @@ static struct attr_stack *read_attr_from_file(const char *path, unsigned flags) return NULL; } fp = xfdopen(fd, "r"); + if (fstat(fd, &st)) { + warning_errno(_("cannot fstat gitattributes file '%s'"), path); + fclose(fp); + return NULL; + } + if (st.st_size >= ATTR_MAX_FILE_SIZE) { + warning(_("ignoring overly large gitattributes file '%s'"), path); + fclose(fp); + return NULL; + } CALLOC_ARRAY(res, 1); - while (fgets(buf, sizeof(buf), fp)) { - char *bufp = buf; - if (!lineno) - skip_utf8_bom(&bufp, strlen(bufp)); - handle_attr_line(res, bufp, path, ++lineno, flags); + while (strbuf_getline(&buf, fp) != EOF) { + if (!lineno && starts_with(buf.buf, utf8_bom)) + strbuf_remove(&buf, 0, strlen(utf8_bom)); + handle_attr_line(res, buf.buf, path, ++lineno, flags); } + fclose(fp); + strbuf_release(&buf); return res; } -static struct attr_stack *read_attr_from_index(struct index_state *istate, - const char *path, - unsigned flags) +static struct attr_stack *read_attr_from_buf(char *buf, const char *path, + unsigned flags) { struct attr_stack *res; - char *buf, *sp; + char *sp; int lineno = 0; + if (!buf) + return NULL; + + CALLOC_ARRAY(res, 1); + for (sp = buf; *sp;) { + char *ep; + int more; + + ep = strchrnul(sp, '\n'); + more = (*ep == '\n'); + *ep = '\0'; + handle_attr_line(res, sp, path, ++lineno, flags); + sp = ep + more; + } + free(buf); + + return res; +} + +static struct attr_stack *read_attr_from_blob(struct index_state *istate, + const struct object_id *tree_oid, + const char *path, unsigned flags) +{ + struct object_id oid; + unsigned long sz; + enum object_type type; + void *buf; + unsigned short mode; + + if (!tree_oid) + return NULL; + + if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode)) + return NULL; + + buf = repo_read_object_file(istate->repo, &oid, &type, &sz); + if (!buf || type != OBJ_BLOB) { + free(buf); + return NULL; + } + + return read_attr_from_buf(buf, path, flags); +} + +static struct attr_stack *read_attr_from_index(struct index_state *istate, + const char *path, unsigned flags) +{ + char *buf; + unsigned long size; + if (!istate) return NULL; @@ -754,32 +826,27 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate, if (!path_in_cone_mode_sparse_checkout(path, istate)) return NULL; - buf = read_blob_data_from_index(istate, path, NULL); + buf = read_blob_data_from_index(istate, path, &size); if (!buf) return NULL; - - CALLOC_ARRAY(res, 1); - for (sp = buf; *sp; ) { - char *ep; - int more; - - ep = strchrnul(sp, '\n'); - more = (*ep == '\n'); - *ep = '\0'; - handle_attr_line(res, sp, path, ++lineno, flags); - sp = ep + more; + if (size >= ATTR_MAX_FILE_SIZE) { + warning(_("ignoring overly large gitattributes blob '%s'"), path); + return NULL; } - free(buf); - return res; + + return read_attr_from_buf(buf, path, flags); } static struct attr_stack *read_attr(struct index_state *istate, + const struct object_id *tree_oid, const char *path, unsigned flags) { struct attr_stack *res = NULL; if (direction == GIT_ATTR_INDEX) { res = read_attr_from_index(istate, path, flags); + } else if (tree_oid) { + res = read_attr_from_blob(istate, tree_oid, path, flags); } else if (!is_bare_repository()) { if (direction == GIT_ATTR_CHECKOUT) { res = read_attr_from_index(istate, path, flags); @@ -839,6 +906,7 @@ static void push_stack(struct attr_stack **attr_stack_p, } static void bootstrap_attr_stack(struct index_state *istate, + const struct object_id *tree_oid, struct attr_stack **stack) { struct attr_stack *e; @@ -864,7 +932,7 @@ static void bootstrap_attr_stack(struct index_state *istate, } /* root directory */ - e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW); + e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW); push_stack(stack, e, xstrdup(""), 0); /* info frame */ @@ -878,6 +946,7 @@ static void bootstrap_attr_stack(struct index_state *istate, } static void prepare_attr_stack(struct index_state *istate, + const struct object_id *tree_oid, const char *path, int dirlen, struct attr_stack **stack) { @@ -899,7 +968,7 @@ static void prepare_attr_stack(struct index_state *istate, * .gitattributes in deeper directories to shallower ones, * and finally use the built-in set as the default. */ - bootstrap_attr_stack(istate, stack); + bootstrap_attr_stack(istate, tree_oid, stack); /* * Pop the "info" one that is always at the top of the stack. @@ -954,7 +1023,7 @@ static void prepare_attr_stack(struct index_state *istate, strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len)); strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE); - next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW); + next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW); /* reset the pathbuf to not include "/.gitattributes" */ strbuf_setlen(&pathbuf, len); @@ -999,12 +1068,12 @@ static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem); static int fill_one(struct all_attrs_item *all_attrs, const struct match_attr *a, int rem) { - int i; + size_t i; - for (i = a->num_attr - 1; rem > 0 && i >= 0; i--) { - const struct git_attr *attr = a->state[i].attr; + for (i = a->num_attr; rem > 0 && i > 0; i--) { + const struct git_attr *attr = a->state[i - 1].attr; const char **n = &(all_attrs[attr->attr_nr].value); - const char *v = a->state[i].setto; + const char *v = a->state[i - 1].setto; if (*n == ATTR__UNKNOWN) { *n = v; @@ -1020,11 +1089,11 @@ static int fill(const char *path, int pathlen, int basename_offset, struct all_attrs_item *all_attrs, int rem) { for (; rem > 0 && stack; stack = stack->prev) { - int i; + unsigned i; const char *base = stack->origin ? stack->origin : ""; - for (i = stack->num_matches - 1; 0 < rem && 0 <= i; i--) { - const struct match_attr *a = stack->attrs[i]; + for (i = stack->num_matches; 0 < rem && 0 < i; i--) { + const struct match_attr *a = stack->attrs[i - 1]; if (a->is_macro) continue; if (path_matches(path, pathlen, basename_offset, @@ -1055,11 +1124,11 @@ static void determine_macros(struct all_attrs_item *all_attrs, const struct attr_stack *stack) { for (; stack; stack = stack->prev) { - int i; - for (i = stack->num_matches - 1; i >= 0; i--) { - const struct match_attr *ma = stack->attrs[i]; + unsigned i; + for (i = stack->num_matches; i > 0; i--) { + const struct match_attr *ma = stack->attrs[i - 1]; if (ma->is_macro) { - int n = ma->u.attr->attr_nr; + unsigned int n = ma->u.attr->attr_nr; if (!all_attrs[n].macro) { all_attrs[n].macro = ma; } @@ -1074,8 +1143,8 @@ static void determine_macros(struct all_attrs_item *all_attrs, * Otherwise all attributes are collected. */ static void collect_some_attrs(struct index_state *istate, - const char *path, - struct attr_check *check) + const struct object_id *tree_oid, + const char *path, struct attr_check *check) { int pathlen, rem, dirlen; const char *cp, *last_slash = NULL; @@ -1094,7 +1163,7 @@ static void collect_some_attrs(struct index_state *istate, dirlen = 0; } - prepare_attr_stack(istate, path, dirlen, &check->stack); + prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack); all_attrs_init(&g_attr_hashmap, check); determine_macros(check->all_attrs, check->stack); @@ -1102,16 +1171,47 @@ static void collect_some_attrs(struct index_state *istate, fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem); } +static const char *default_attr_source_tree_object_name; + +void set_git_attr_source(const char *tree_object_name) +{ + default_attr_source_tree_object_name = xstrdup(tree_object_name); +} + +static void compute_default_attr_source(struct object_id *attr_source) +{ + if (!default_attr_source_tree_object_name) + default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT); + + if (!default_attr_source_tree_object_name || !is_null_oid(attr_source)) + return; + + if (repo_get_oid_treeish(the_repository, default_attr_source_tree_object_name, attr_source)) + die(_("bad --attr-source or GIT_ATTR_SOURCE")); +} + +static struct object_id *default_attr_source(void) +{ + static struct object_id attr_source; + + if (is_null_oid(&attr_source)) + compute_default_attr_source(&attr_source); + if (is_null_oid(&attr_source)) + return NULL; + return &attr_source; +} + void git_check_attr(struct index_state *istate, const char *path, struct attr_check *check) { int i; + const struct object_id *tree_oid = default_attr_source(); - collect_some_attrs(istate, path, check); + collect_some_attrs(istate, tree_oid, path, check); for (i = 0; i < check->nr; i++) { - size_t n = check->items[i].attr->attr_nr; + unsigned int n = check->items[i].attr->attr_nr; const char *value = check->all_attrs[n].value; if (value == ATTR__UNKNOWN) value = ATTR__UNSET; @@ -1123,9 +1223,10 @@ void git_all_attrs(struct index_state *istate, const char *path, struct attr_check *check) { int i; + const struct object_id *tree_oid = default_attr_source(); attr_check_reset(check); - collect_some_attrs(istate, path, check); + collect_some_attrs(istate, tree_oid, path, check); for (i = 0; i < check->all_attrs_nr; i++) { const char *name = check->all_attrs[i].attr->name; |