Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 1 | #define NO_THE_INDEX_COMPATIBILITY_MACROS |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 2 | #include "cache.h" |
| 3 | #include "attr.h" |
| 4 | |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 5 | const char git_attr__true[] = "(builtin)true"; |
| 6 | const char git_attr__false[] = "\0(builtin)false"; |
| 7 | static const char git_attr__unknown[] = "(builtin)unknown"; |
| 8 | #define ATTR__TRUE git_attr__true |
| 9 | #define ATTR__FALSE git_attr__false |
| 10 | #define ATTR__UNSET NULL |
| 11 | #define ATTR__UNKNOWN git_attr__unknown |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 12 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 13 | /* |
| 14 | * The basic design decision here is that we are not going to have |
| 15 | * insanely large number of attributes. |
| 16 | * |
| 17 | * This is a randomly chosen prime. |
| 18 | */ |
| 19 | #define HASHSIZE 257 |
| 20 | |
| 21 | #ifndef DEBUG_ATTR |
| 22 | #define DEBUG_ATTR 0 |
| 23 | #endif |
| 24 | |
| 25 | struct git_attr { |
| 26 | struct git_attr *next; |
| 27 | unsigned h; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 28 | int attr_nr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 29 | char name[FLEX_ARRAY]; |
| 30 | }; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 31 | static int attr_nr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 32 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 33 | static struct git_attr_check *check_all_attr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 34 | static struct git_attr *(git_attr_hash[HASHSIZE]); |
| 35 | |
| 36 | static unsigned hash_name(const char *name, int namelen) |
| 37 | { |
Linus Torvalds | 48fb7de | 2009-06-17 17:22:27 -0700 | [diff] [blame] | 38 | unsigned val = 0, c; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 39 | |
| 40 | while (namelen--) { |
| 41 | c = *name++; |
| 42 | val = ((val << 7) | (val >> 22)) ^ c; |
| 43 | } |
| 44 | return val; |
| 45 | } |
| 46 | |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 47 | static int invalid_attr_name(const char *name, int namelen) |
| 48 | { |
| 49 | /* |
| 50 | * Attribute name cannot begin with '-' and from |
| 51 | * [-A-Za-z0-9_.]. We'd specifically exclude '=' for now, |
| 52 | * as we might later want to allow non-binary value for |
| 53 | * attributes, e.g. "*.svg merge=special-merge-program-for-svg" |
| 54 | */ |
| 55 | if (*name == '-') |
| 56 | return -1; |
| 57 | while (namelen--) { |
| 58 | char ch = *name++; |
| 59 | if (! (ch == '-' || ch == '.' || ch == '_' || |
| 60 | ('0' <= ch && ch <= '9') || |
| 61 | ('a' <= ch && ch <= 'z') || |
| 62 | ('A' <= ch && ch <= 'Z')) ) |
| 63 | return -1; |
| 64 | } |
| 65 | return 0; |
| 66 | } |
| 67 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 68 | struct git_attr *git_attr(const char *name, int len) |
| 69 | { |
| 70 | unsigned hval = hash_name(name, len); |
| 71 | unsigned pos = hval % HASHSIZE; |
| 72 | struct git_attr *a; |
| 73 | |
| 74 | for (a = git_attr_hash[pos]; a; a = a->next) { |
| 75 | if (a->h == hval && |
| 76 | !memcmp(a->name, name, len) && !a->name[len]) |
| 77 | return a; |
| 78 | } |
| 79 | |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 80 | if (invalid_attr_name(name, len)) |
| 81 | return NULL; |
| 82 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 83 | a = xmalloc(sizeof(*a) + len + 1); |
| 84 | memcpy(a->name, name, len); |
| 85 | a->name[len] = 0; |
| 86 | a->h = hval; |
| 87 | a->next = git_attr_hash[pos]; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 88 | a->attr_nr = attr_nr++; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 89 | git_attr_hash[pos] = a; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 90 | |
| 91 | check_all_attr = xrealloc(check_all_attr, |
| 92 | sizeof(*check_all_attr) * attr_nr); |
| 93 | check_all_attr[a->attr_nr].attr = a; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 94 | check_all_attr[a->attr_nr].value = ATTR__UNKNOWN; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 95 | return a; |
| 96 | } |
| 97 | |
| 98 | /* |
| 99 | * .gitattributes file is one line per record, each of which is |
| 100 | * |
| 101 | * (1) glob pattern. |
| 102 | * (2) whitespace |
| 103 | * (3) whitespace separated list of attribute names, each of which |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 104 | * could be prefixed with '-' to mean "set to false", '!' to mean |
| 105 | * "unset". |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 106 | */ |
| 107 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 108 | /* What does a matched pattern decide? */ |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 109 | struct attr_state { |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 110 | struct git_attr *attr; |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 111 | const char *setto; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 112 | }; |
| 113 | |
| 114 | struct match_attr { |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 115 | union { |
| 116 | char *pattern; |
| 117 | struct git_attr *attr; |
| 118 | } u; |
| 119 | char is_macro; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 120 | unsigned num_attr; |
| 121 | struct attr_state state[FLEX_ARRAY]; |
| 122 | }; |
| 123 | |
| 124 | static const char blank[] = " \t\r\n"; |
| 125 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 126 | static const char *parse_attr(const char *src, int lineno, const char *cp, |
| 127 | int *num_attr, struct match_attr *res) |
| 128 | { |
| 129 | const char *ep, *equals; |
| 130 | int len; |
| 131 | |
| 132 | ep = cp + strcspn(cp, blank); |
| 133 | equals = strchr(cp, '='); |
| 134 | if (equals && ep < equals) |
| 135 | equals = NULL; |
| 136 | if (equals) |
| 137 | len = equals - cp; |
| 138 | else |
| 139 | len = ep - cp; |
| 140 | if (!res) { |
| 141 | if (*cp == '-' || *cp == '!') { |
| 142 | cp++; |
| 143 | len--; |
| 144 | } |
| 145 | if (invalid_attr_name(cp, len)) { |
| 146 | fprintf(stderr, |
| 147 | "%.*s is not a valid attribute name: %s:%d\n", |
| 148 | len, cp, src, lineno); |
| 149 | return NULL; |
| 150 | } |
| 151 | } else { |
| 152 | struct attr_state *e; |
| 153 | |
| 154 | e = &(res->state[*num_attr]); |
| 155 | if (*cp == '-' || *cp == '!') { |
| 156 | e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET; |
| 157 | cp++; |
| 158 | len--; |
| 159 | } |
| 160 | else if (!equals) |
| 161 | e->setto = ATTR__TRUE; |
| 162 | else { |
Pierre Habouzit | 182af83 | 2007-09-16 00:32:36 +0200 | [diff] [blame] | 163 | e->setto = xmemdupz(equals + 1, ep - equals - 1); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 164 | } |
| 165 | e->attr = git_attr(cp, len); |
| 166 | } |
| 167 | (*num_attr)++; |
| 168 | return ep + strspn(ep, blank); |
| 169 | } |
| 170 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 171 | static struct match_attr *parse_attr_line(const char *line, const char *src, |
| 172 | int lineno, int macro_ok) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 173 | { |
| 174 | int namelen; |
| 175 | int num_attr; |
| 176 | const char *cp, *name; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 177 | struct match_attr *res = NULL; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 178 | int pass; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 179 | int is_macro; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 180 | |
| 181 | cp = line + strspn(line, blank); |
| 182 | if (!*cp || *cp == '#') |
| 183 | return NULL; |
| 184 | name = cp; |
| 185 | namelen = strcspn(name, blank); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 186 | if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && |
| 187 | !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) { |
| 188 | if (!macro_ok) { |
| 189 | fprintf(stderr, "%s not allowed: %s:%d\n", |
| 190 | name, src, lineno); |
| 191 | return NULL; |
| 192 | } |
| 193 | is_macro = 1; |
| 194 | name += strlen(ATTRIBUTE_MACRO_PREFIX); |
| 195 | name += strspn(name, blank); |
| 196 | namelen = strcspn(name, blank); |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 197 | if (invalid_attr_name(name, namelen)) { |
| 198 | fprintf(stderr, |
| 199 | "%.*s is not a valid attribute name: %s:%d\n", |
| 200 | namelen, name, src, lineno); |
| 201 | return NULL; |
| 202 | } |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 203 | } |
| 204 | else |
| 205 | is_macro = 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 206 | |
| 207 | for (pass = 0; pass < 2; pass++) { |
| 208 | /* pass 0 counts and allocates, pass 1 fills */ |
| 209 | num_attr = 0; |
| 210 | cp = name + namelen; |
| 211 | cp = cp + strspn(cp, blank); |
Steffen Prohaska | d7b0a09 | 2007-10-18 22:02:35 +0200 | [diff] [blame] | 212 | while (*cp) { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 213 | cp = parse_attr(src, lineno, cp, &num_attr, res); |
Steffen Prohaska | d7b0a09 | 2007-10-18 22:02:35 +0200 | [diff] [blame] | 214 | if (!cp) |
| 215 | return NULL; |
| 216 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 217 | if (pass) |
| 218 | break; |
| 219 | res = xcalloc(1, |
| 220 | sizeof(*res) + |
| 221 | sizeof(struct attr_state) * num_attr + |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 222 | (is_macro ? 0 : namelen + 1)); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 223 | if (is_macro) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 224 | res->u.attr = git_attr(name, namelen); |
| 225 | else { |
Felipe Contreras | 4b25d09 | 2009-05-01 12:06:36 +0300 | [diff] [blame] | 226 | res->u.pattern = (char *)&(res->state[num_attr]); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 227 | memcpy(res->u.pattern, name, namelen); |
| 228 | res->u.pattern[namelen] = 0; |
| 229 | } |
| 230 | res->is_macro = is_macro; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 231 | res->num_attr = num_attr; |
| 232 | } |
| 233 | return res; |
| 234 | } |
| 235 | |
| 236 | /* |
| 237 | * Like info/exclude and .gitignore, the attribute information can |
| 238 | * come from many places. |
| 239 | * |
| 240 | * (1) .gitattribute file of the same directory; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 241 | * (2) .gitattribute file of the parent directory if (1) does not have |
| 242 | * any match; this goes recursively upwards, just like .gitignore. |
| 243 | * (3) $GIT_DIR/info/attributes, which overrides both of the above. |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 244 | * |
| 245 | * In the same file, later entries override the earlier match, so in the |
| 246 | * global list, we would have entries from info/attributes the earliest |
| 247 | * (reading the file from top to bottom), .gitattribute of the root |
| 248 | * directory (again, reading the file from top to bottom) down to the |
| 249 | * current directory, and then scan the list backwards to find the first match. |
| 250 | * This is exactly the same as what excluded() does in dir.c to deal with |
| 251 | * .gitignore |
| 252 | */ |
| 253 | |
| 254 | static struct attr_stack { |
| 255 | struct attr_stack *prev; |
| 256 | char *origin; |
| 257 | unsigned num_matches; |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 258 | unsigned alloc; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 259 | struct match_attr **attrs; |
| 260 | } *attr_stack; |
| 261 | |
| 262 | static void free_attr_elem(struct attr_stack *e) |
| 263 | { |
| 264 | int i; |
| 265 | free(e->origin); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 266 | for (i = 0; i < e->num_matches; i++) { |
| 267 | struct match_attr *a = e->attrs[i]; |
| 268 | int j; |
| 269 | for (j = 0; j < a->num_attr; j++) { |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 270 | const char *setto = a->state[j].setto; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 271 | if (setto == ATTR__TRUE || |
| 272 | setto == ATTR__FALSE || |
| 273 | setto == ATTR__UNSET || |
| 274 | setto == ATTR__UNKNOWN) |
| 275 | ; |
| 276 | else |
Felipe Contreras | 4b25d09 | 2009-05-01 12:06:36 +0300 | [diff] [blame] | 277 | free((char *) setto); |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 278 | } |
| 279 | free(a); |
| 280 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 281 | free(e); |
| 282 | } |
| 283 | |
| 284 | static const char *builtin_attr[] = { |
Junio C Hamano | e4aee10 | 2007-04-15 14:56:09 -0700 | [diff] [blame] | 285 | "[attr]binary -diff -crlf", |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 286 | NULL, |
| 287 | }; |
| 288 | |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 289 | static void handle_attr_line(struct attr_stack *res, |
| 290 | const char *line, |
| 291 | const char *src, |
| 292 | int lineno, |
| 293 | int macro_ok) |
| 294 | { |
| 295 | struct match_attr *a; |
| 296 | |
| 297 | a = parse_attr_line(line, src, lineno, macro_ok); |
| 298 | if (!a) |
| 299 | return; |
| 300 | if (res->alloc <= res->num_matches) { |
| 301 | res->alloc = alloc_nr(res->num_matches); |
| 302 | res->attrs = xrealloc(res->attrs, |
| 303 | sizeof(struct match_attr *) * |
| 304 | res->alloc); |
| 305 | } |
| 306 | res->attrs[res->num_matches++] = a; |
| 307 | } |
| 308 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 309 | static struct attr_stack *read_attr_from_array(const char **list) |
| 310 | { |
| 311 | struct attr_stack *res; |
| 312 | const char *line; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 313 | int lineno = 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 314 | |
| 315 | res = xcalloc(1, sizeof(*res)); |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 316 | while ((line = *(list++)) != NULL) |
| 317 | handle_attr_line(res, line, "[builtin]", ++lineno, 1); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 318 | return res; |
| 319 | } |
| 320 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 321 | static enum git_attr_direction direction; |
| 322 | static struct index_state *use_index; |
| 323 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 324 | static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 325 | { |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 326 | FILE *fp = fopen(path, "r"); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 327 | struct attr_stack *res; |
| 328 | char buf[2048]; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 329 | int lineno = 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 330 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 331 | if (!fp) |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 332 | return NULL; |
| 333 | res = xcalloc(1, sizeof(*res)); |
| 334 | while (fgets(buf, sizeof(buf), fp)) |
| 335 | handle_attr_line(res, buf, path, ++lineno, macro_ok); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 336 | fclose(fp); |
| 337 | return res; |
| 338 | } |
| 339 | |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 340 | static void *read_index_data(const char *path) |
| 341 | { |
| 342 | int pos, len; |
| 343 | unsigned long sz; |
| 344 | enum object_type type; |
| 345 | void *data; |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 346 | struct index_state *istate = use_index ? use_index : &the_index; |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 347 | |
| 348 | len = strlen(path); |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 349 | pos = index_name_pos(istate, path, len); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 350 | if (pos < 0) { |
| 351 | /* |
| 352 | * We might be in the middle of a merge, in which |
| 353 | * case we would read stage #2 (ours). |
| 354 | */ |
| 355 | int i; |
| 356 | for (i = -pos - 1; |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 357 | (pos < 0 && i < istate->cache_nr && |
| 358 | !strcmp(istate->cache[i]->name, path)); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 359 | i++) |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 360 | if (ce_stage(istate->cache[i]) == 2) |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 361 | pos = i; |
| 362 | } |
| 363 | if (pos < 0) |
| 364 | return NULL; |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 365 | data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 366 | if (!data || type != OBJ_BLOB) { |
| 367 | free(data); |
| 368 | return NULL; |
| 369 | } |
| 370 | return data; |
| 371 | } |
| 372 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 373 | static struct attr_stack *read_attr_from_index(const char *path, int macro_ok) |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 374 | { |
| 375 | struct attr_stack *res; |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 376 | char *buf, *sp; |
| 377 | int lineno = 0; |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 378 | |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 379 | buf = read_index_data(path); |
| 380 | if (!buf) |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 381 | return NULL; |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 382 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 383 | res = xcalloc(1, sizeof(*res)); |
Junio C Hamano | 1a9d7e9 | 2007-08-14 01:41:02 -0700 | [diff] [blame] | 384 | for (sp = buf; *sp; ) { |
| 385 | char *ep; |
| 386 | int more; |
| 387 | for (ep = sp; *ep && *ep != '\n'; ep++) |
| 388 | ; |
| 389 | more = (*ep == '\n'); |
| 390 | *ep = '\0'; |
| 391 | handle_attr_line(res, sp, path, ++lineno, macro_ok); |
| 392 | sp = ep + more; |
| 393 | } |
| 394 | free(buf); |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 395 | return res; |
| 396 | } |
| 397 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 398 | static struct attr_stack *read_attr(const char *path, int macro_ok) |
| 399 | { |
| 400 | struct attr_stack *res; |
| 401 | |
| 402 | if (direction == GIT_ATTR_CHECKOUT) { |
| 403 | res = read_attr_from_index(path, macro_ok); |
| 404 | if (!res) |
| 405 | res = read_attr_from_file(path, macro_ok); |
| 406 | } |
Nguyễn Thái Ngọc Duy | 4191e80 | 2009-04-18 00:17:58 +0200 | [diff] [blame] | 407 | else if (direction == GIT_ATTR_CHECKIN) { |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 408 | res = read_attr_from_file(path, macro_ok); |
| 409 | if (!res) |
| 410 | /* |
| 411 | * There is no checked out .gitattributes file there, but |
| 412 | * we might have it in the index. We allow operation in a |
| 413 | * sparsely checked out work tree, so read from it. |
| 414 | */ |
| 415 | res = read_attr_from_index(path, macro_ok); |
| 416 | } |
Nguyễn Thái Ngọc Duy | 4191e80 | 2009-04-18 00:17:58 +0200 | [diff] [blame] | 417 | else |
| 418 | res = read_attr_from_index(path, macro_ok); |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 419 | if (!res) |
| 420 | res = xcalloc(1, sizeof(*res)); |
| 421 | return res; |
| 422 | } |
| 423 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 424 | #if DEBUG_ATTR |
| 425 | static void debug_info(const char *what, struct attr_stack *elem) |
| 426 | { |
| 427 | fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()"); |
| 428 | } |
Junio C Hamano | cf94ccd | 2008-02-07 00:02:08 -0800 | [diff] [blame] | 429 | static void debug_set(const char *what, const char *match, struct git_attr *attr, const void *v) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 430 | { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 431 | const char *value = v; |
| 432 | |
| 433 | if (ATTR_TRUE(value)) |
| 434 | value = "set"; |
| 435 | else if (ATTR_FALSE(value)) |
| 436 | value = "unset"; |
| 437 | else if (ATTR_UNSET(value)) |
| 438 | value = "unspecified"; |
| 439 | |
| 440 | fprintf(stderr, "%s: %s => %s (%s)\n", |
| 441 | what, attr->name, (char *) value, match); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 442 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 443 | #define debug_push(a) debug_info("push", (a)) |
| 444 | #define debug_pop(a) debug_info("pop", (a)) |
| 445 | #else |
| 446 | #define debug_push(a) do { ; } while (0) |
| 447 | #define debug_pop(a) do { ; } while (0) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 448 | #define debug_set(a,b,c,d) do { ; } while (0) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 449 | #endif |
| 450 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 451 | static void drop_attr_stack(void) |
| 452 | { |
| 453 | while (attr_stack) { |
| 454 | struct attr_stack *elem = attr_stack; |
| 455 | attr_stack = elem->prev; |
| 456 | free_attr_elem(elem); |
| 457 | } |
| 458 | } |
| 459 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 460 | static void bootstrap_attr_stack(void) |
| 461 | { |
| 462 | if (!attr_stack) { |
| 463 | struct attr_stack *elem; |
| 464 | |
| 465 | elem = read_attr_from_array(builtin_attr); |
| 466 | elem->origin = NULL; |
| 467 | elem->prev = attr_stack; |
| 468 | attr_stack = elem; |
| 469 | |
Nguyễn Thái Ngọc Duy | 4191e80 | 2009-04-18 00:17:58 +0200 | [diff] [blame] | 470 | if (!is_bare_repository() || direction == GIT_ATTR_INDEX) { |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 471 | elem = read_attr(GITATTRIBUTES_FILE, 1); |
| 472 | elem->origin = strdup(""); |
| 473 | elem->prev = attr_stack; |
| 474 | attr_stack = elem; |
| 475 | debug_push(elem); |
| 476 | } |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 477 | |
| 478 | elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1); |
Junio C Hamano | a441311 | 2007-08-14 01:40:45 -0700 | [diff] [blame] | 479 | if (!elem) |
| 480 | elem = xcalloc(1, sizeof(*elem)); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 481 | elem->origin = NULL; |
| 482 | elem->prev = attr_stack; |
| 483 | attr_stack = elem; |
| 484 | } |
| 485 | } |
| 486 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 487 | static void prepare_attr_stack(const char *path, int dirlen) |
| 488 | { |
| 489 | struct attr_stack *elem, *info; |
| 490 | int len; |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 491 | struct strbuf pathbuf; |
| 492 | |
| 493 | strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE)); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 494 | |
| 495 | /* |
| 496 | * At the bottom of the attribute stack is the built-in |
| 497 | * set of attribute definitions. Then, contents from |
| 498 | * .gitattribute files from directories closer to the |
| 499 | * root to the ones in deeper directories are pushed |
| 500 | * to the stack. Finally, at the very top of the stack |
| 501 | * we always keep the contents of $GIT_DIR/info/attributes. |
| 502 | * |
| 503 | * When checking, we use entries from near the top of the |
| 504 | * stack, preferring $GIT_DIR/info/attributes, then |
| 505 | * .gitattributes in deeper directories to shallower ones, |
| 506 | * and finally use the built-in set as the default. |
| 507 | */ |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 508 | if (!attr_stack) |
| 509 | bootstrap_attr_stack(); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 510 | |
| 511 | /* |
| 512 | * Pop the "info" one that is always at the top of the stack. |
| 513 | */ |
| 514 | info = attr_stack; |
| 515 | attr_stack = info->prev; |
| 516 | |
| 517 | /* |
| 518 | * Pop the ones from directories that are not the prefix of |
| 519 | * the path we are checking. |
| 520 | */ |
| 521 | while (attr_stack && attr_stack->origin) { |
| 522 | int namelen = strlen(attr_stack->origin); |
| 523 | |
| 524 | elem = attr_stack; |
| 525 | if (namelen <= dirlen && |
| 526 | !strncmp(elem->origin, path, namelen)) |
| 527 | break; |
| 528 | |
| 529 | debug_pop(elem); |
| 530 | attr_stack = elem->prev; |
| 531 | free_attr_elem(elem); |
| 532 | } |
| 533 | |
| 534 | /* |
| 535 | * Read from parent directories and push them down |
| 536 | */ |
Nguyễn Thái Ngọc Duy | 4191e80 | 2009-04-18 00:17:58 +0200 | [diff] [blame] | 537 | if (!is_bare_repository() || direction == GIT_ATTR_INDEX) { |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 538 | while (1) { |
| 539 | char *cp; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 540 | |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 541 | len = strlen(attr_stack->origin); |
| 542 | if (dirlen <= len) |
| 543 | break; |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 544 | strbuf_reset(&pathbuf); |
| 545 | strbuf_add(&pathbuf, path, dirlen); |
| 546 | strbuf_addch(&pathbuf, '/'); |
| 547 | cp = strchr(pathbuf.buf + len + 1, '/'); |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 548 | strcpy(cp + 1, GITATTRIBUTES_FILE); |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 549 | elem = read_attr(pathbuf.buf, 0); |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 550 | *cp = '\0'; |
Dmitry Potapov | f66cf96 | 2008-07-16 19:39:55 +0400 | [diff] [blame] | 551 | elem->origin = strdup(pathbuf.buf); |
René Scharfe | 2d35d55 | 2008-06-08 17:16:11 +0200 | [diff] [blame] | 552 | elem->prev = attr_stack; |
| 553 | attr_stack = elem; |
| 554 | debug_push(elem); |
| 555 | } |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 556 | } |
| 557 | |
René Scharfe | d4c9856 | 2009-07-01 00:30:00 +0200 | [diff] [blame] | 558 | strbuf_release(&pathbuf); |
| 559 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 560 | /* |
| 561 | * Finally push the "info" one at the top of the stack. |
| 562 | */ |
| 563 | info->prev = attr_stack; |
| 564 | attr_stack = info; |
| 565 | } |
| 566 | |
| 567 | static int path_matches(const char *pathname, int pathlen, |
| 568 | const char *pattern, |
| 569 | const char *base, int baselen) |
| 570 | { |
| 571 | if (!strchr(pattern, '/')) { |
| 572 | /* match basename */ |
| 573 | const char *basename = strrchr(pathname, '/'); |
| 574 | basename = basename ? basename + 1 : pathname; |
| 575 | return (fnmatch(pattern, basename, 0) == 0); |
| 576 | } |
| 577 | /* |
| 578 | * match with FNM_PATHNAME; the pattern has base implicitly |
| 579 | * in front of it. |
| 580 | */ |
| 581 | if (*pattern == '/') |
| 582 | pattern++; |
| 583 | if (pathlen < baselen || |
Junio C Hamano | cf94ccd | 2008-02-07 00:02:08 -0800 | [diff] [blame] | 584 | (baselen && pathname[baselen] != '/') || |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 585 | strncmp(pathname, base, baselen)) |
| 586 | return 0; |
Matthew Ogilvie | 82881b3 | 2008-04-22 12:19:12 -0600 | [diff] [blame] | 587 | if (baselen != 0) |
| 588 | baselen++; |
| 589 | return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 590 | } |
| 591 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 592 | static int fill_one(const char *what, struct match_attr *a, int rem) |
| 593 | { |
| 594 | struct git_attr_check *check = check_all_attr; |
| 595 | int i; |
| 596 | |
| 597 | for (i = 0; 0 < rem && i < a->num_attr; i++) { |
| 598 | struct git_attr *attr = a->state[i].attr; |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 599 | const char **n = &(check[attr->attr_nr].value); |
| 600 | const char *v = a->state[i].setto; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 601 | |
| 602 | if (*n == ATTR__UNKNOWN) { |
| 603 | debug_set(what, a->u.pattern, attr, v); |
| 604 | *n = v; |
| 605 | rem--; |
| 606 | } |
| 607 | } |
| 608 | return rem; |
| 609 | } |
| 610 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 611 | static int fill(const char *path, int pathlen, struct attr_stack *stk, int rem) |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 612 | { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 613 | int i; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 614 | const char *base = stk->origin ? stk->origin : ""; |
| 615 | |
| 616 | for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { |
| 617 | struct match_attr *a = stk->attrs[i]; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 618 | if (a->is_macro) |
| 619 | continue; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 620 | if (path_matches(path, pathlen, |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 621 | a->u.pattern, base, strlen(base))) |
| 622 | rem = fill_one("fill", a, rem); |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 623 | } |
| 624 | return rem; |
| 625 | } |
| 626 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 627 | static int macroexpand(struct attr_stack *stk, int rem) |
| 628 | { |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 629 | int i; |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 630 | struct git_attr_check *check = check_all_attr; |
| 631 | |
| 632 | for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { |
| 633 | struct match_attr *a = stk->attrs[i]; |
| 634 | if (!a->is_macro) |
| 635 | continue; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 636 | if (check[a->u.attr->attr_nr].value != ATTR__TRUE) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 637 | continue; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 638 | rem = fill_one("expand", a, rem); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 639 | } |
| 640 | return rem; |
| 641 | } |
| 642 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 643 | int git_checkattr(const char *path, int num, struct git_attr_check *check) |
| 644 | { |
| 645 | struct attr_stack *stk; |
| 646 | const char *cp; |
| 647 | int dirlen, pathlen, i, rem; |
| 648 | |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 649 | bootstrap_attr_stack(); |
| 650 | for (i = 0; i < attr_nr; i++) |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 651 | check_all_attr[i].value = ATTR__UNKNOWN; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 652 | |
| 653 | pathlen = strlen(path); |
| 654 | cp = strrchr(path, '/'); |
| 655 | if (!cp) |
| 656 | dirlen = 0; |
| 657 | else |
| 658 | dirlen = cp - path; |
| 659 | prepare_attr_stack(path, dirlen); |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 660 | rem = attr_nr; |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 661 | for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 662 | rem = fill(path, pathlen, stk, rem); |
| 663 | |
| 664 | for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) |
| 665 | rem = macroexpand(stk, rem); |
| 666 | |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 667 | for (i = 0; i < num; i++) { |
Junio C Hamano | a5e92ab | 2007-04-18 16:16:37 -0700 | [diff] [blame] | 668 | const char *value = check_all_attr[check[i].attr->attr_nr].value; |
Junio C Hamano | 515106f | 2007-04-16 21:33:31 -0700 | [diff] [blame] | 669 | if (value == ATTR__UNKNOWN) |
| 670 | value = ATTR__UNSET; |
| 671 | check[i].value = value; |
| 672 | } |
Junio C Hamano | f48fd68 | 2007-04-14 08:54:37 -0700 | [diff] [blame] | 673 | |
Junio C Hamano | d0bfd02 | 2007-04-12 01:07:32 -0700 | [diff] [blame] | 674 | return 0; |
| 675 | } |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 676 | |
| 677 | void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate) |
| 678 | { |
| 679 | enum git_attr_direction old = direction; |
Nguyễn Thái Ngọc Duy | 4191e80 | 2009-04-18 00:17:58 +0200 | [diff] [blame] | 680 | |
| 681 | if (is_bare_repository() && new != GIT_ATTR_INDEX) |
| 682 | die("BUG: non-INDEX attr direction in a bare repo"); |
| 683 | |
Junio C Hamano | 06f33c1 | 2009-03-13 21:24:08 -0700 | [diff] [blame] | 684 | direction = new; |
| 685 | if (new != old) |
| 686 | drop_attr_stack(); |
| 687 | use_index = istate; |
| 688 | } |