git-clone -s: document problems with git gc --prune
[gitweb.git] / attr.c
diff --git a/attr.c b/attr.c
index b3496a6eb529c111ff0378a517b13ca89cb74d5a..741db3b468c6a6ebbcd1414e42b4ef7d6ab3cc9d 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -1,7 +1,13 @@
 #include "cache.h"
 #include "attr.h"
 
-#define ATTR__UNKNOWN  ((void *) -2)
+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
@@ -102,7 +108,7 @@ struct git_attr *git_attr(const char *name, int len)
 /* What does a matched pattern decide? */
 struct attr_state {
        struct git_attr *attr;
-       void *setto;
+       const char *setto;
 };
 
 struct match_attr {
@@ -154,12 +160,7 @@ 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);
        }
@@ -208,8 +209,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,
@@ -251,6 +255,7 @@ static struct attr_stack {
        struct attr_stack *prev;
        char *origin;
        unsigned num_matches;
+       unsigned alloc;
        struct match_attr **attrs;
 } *attr_stack;
 
@@ -262,14 +267,14 @@ static void free_attr_elem(struct attr_stack *e)
                struct match_attr *a = e->attrs[i];
                int j;
                for (j = 0; j < a->num_attr; j++) {
-                       void *setto = a->state[j].setto;
+                       const char *setto = a->state[j].setto;
                        if (setto == ATTR__TRUE ||
                            setto == ATTR__FALSE ||
                            setto == ATTR__UNSET ||
                            setto == ATTR__UNKNOWN)
                                ;
                        else
-                               free(setto);
+                               free((char*) setto);
                }
                free(a);
        }
@@ -281,6 +286,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;
@@ -288,40 +313,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;
 }
 
@@ -362,13 +438,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;
@@ -433,7 +511,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;
@@ -478,8 +556,8 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
 
        for (i = 0; 0 < rem && i < a->num_attr; i++) {
                struct git_attr *attr = a->state[i].attr;
-               void **n = &(check[attr->attr_nr].value);
-               void *v = a->state[i].setto;
+               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);
@@ -547,7 +625,7 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
                rem = macroexpand(stk, rem);
 
        for (i = 0; i < num; i++) {
-               void *value = check_all_attr[check[i].attr->attr_nr].value;
+               const char *value = check_all_attr[check[i].attr->attr_nr].value;
                if (value == ATTR__UNKNOWN)
                        value = ATTR__UNSET;
                check[i].value = value;