treat any file with NUL as binary
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index 5bcc764c97dca5e584d0a4227bfb299efb5699ce..3e345c2fc508eb82e66f4ffe2c825670f0bddd99 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -144,17 +144,14 @@ void add_exclude(const char *string, const char *base,
                x->flags |= EXC_FLAG_NOWILDCARD;
        if (*string == '*' && no_wildcard(string+1))
                x->flags |= EXC_FLAG_ENDSWITH;
-       if (which->nr == which->alloc) {
-               which->alloc = alloc_nr(which->alloc);
-               which->excludes = xrealloc(which->excludes,
-                                          which->alloc * sizeof(x));
-       }
+       ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
        which->excludes[which->nr++] = x;
 }
 
 static int add_excludes_from_file_1(const char *fname,
                                    const char *base,
                                    int baselen,
+                                   char **buf_p,
                                    struct exclude_list *which)
 {
        struct stat st;
@@ -172,9 +169,14 @@ static int add_excludes_from_file_1(const char *fname,
        }
        buf = xmalloc(size+1);
        if (read_in_full(fd, buf, size) != size)
+       {
+               free(buf);
                goto err;
+       }
        close(fd);
 
+       if (buf_p)
+               *buf_p = buf;
        buf[size++] = '\n';
        entry = buf;
        for (i = 0; i < size; i++) {
@@ -196,31 +198,63 @@ static int add_excludes_from_file_1(const char *fname,
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
-       if (add_excludes_from_file_1(fname, "", 0,
+       if (add_excludes_from_file_1(fname, "", 0, NULL,
                                     &dir->exclude_list[EXC_FILE]) < 0)
                die("cannot use %s as an exclude file", fname);
 }
 
-int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
+static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
 {
-       char exclude_file[PATH_MAX];
-       struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
-       int current_nr = el->nr;
-
-       if (dir->exclude_per_dir) {
-               memcpy(exclude_file, base, baselen);
-               strcpy(exclude_file + baselen, dir->exclude_per_dir);
-               add_excludes_from_file_1(exclude_file, base, baselen, el);
+       struct exclude_list *el;
+       struct exclude_stack *stk = NULL;
+       int current;
+
+       if ((!dir->exclude_per_dir) ||
+           (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
+               return; /* too long a path -- ignore */
+
+       /* Pop the ones that are not the prefix of the path being checked. */
+       el = &dir->exclude_list[EXC_DIRS];
+       while ((stk = dir->exclude_stack) != NULL) {
+               if (stk->baselen <= baselen &&
+                   !strncmp(dir->basebuf, base, stk->baselen))
+                       break;
+               dir->exclude_stack = stk->prev;
+               while (stk->exclude_ix < el->nr)
+                       free(el->excludes[--el->nr]);
+               free(stk->filebuf);
+               free(stk);
        }
-       return current_nr;
-}
 
-void pop_exclude_per_directory(struct dir_struct *dir, int stk)
-{
-       struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
+       /* Read from the parent directories and push them down. */
+       current = stk ? stk->baselen : -1;
+       while (current < baselen) {
+               struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
+               const char *cp;
 
-       while (stk < el->nr)
-               free(el->excludes[--el->nr]);
+               if (current < 0) {
+                       cp = base;
+                       current = 0;
+               }
+               else {
+                       cp = strchr(base + current + 1, '/');
+                       if (!cp)
+                               die("oops in prep_exclude");
+                       cp++;
+               }
+               stk->prev = dir->exclude_stack;
+               stk->baselen = cp - base;
+               stk->exclude_ix = el->nr;
+               memcpy(dir->basebuf + current, base + current,
+                      stk->baselen - current);
+               strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
+               add_excludes_from_file_1(dir->basebuf,
+                                        dir->basebuf, stk->baselen,
+                                        &stk->filebuf, el);
+               dir->exclude_stack = stk;
+               current = stk->baselen;
+       }
+       dir->basebuf[baselen] = '\0';
 }
 
 /* Scan the list and let the last match determines the fate.
@@ -287,6 +321,7 @@ int excluded(struct dir_struct *dir, const char *pathname)
        const char *basename = strrchr(pathname, '/');
        basename = (basename) ? basename+1 : pathname;
 
+       prep_exclude(dir, pathname, basename-pathname);
        for (st = EXC_CMDL; st <= EXC_FILE; st++) {
                switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
                case 0:
@@ -298,7 +333,8 @@ int excluded(struct dir_struct *dir, const char *pathname)
        return 0;
 }
 
-static struct dir_entry *dir_entry_new(const char *pathname, int len) {
+static struct dir_entry *dir_entry_new(const char *pathname, int len)
+{
        struct dir_entry *ent;
 
        ent = xmalloc(sizeof(*ent) + len + 1);
@@ -503,13 +539,10 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
        int contents = 0;
 
        if (fdir) {
-               int exclude_stk;
                struct dirent *de;
                char fullname[PATH_MAX + 1];
                memcpy(fullname, base, baselen);
 
-               exclude_stk = push_exclude_per_directory(dir, base, baselen);
-
                while ((de = readdir(fdir)) != NULL) {
                        int len, dtype;
                        int exclude;
@@ -583,8 +616,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                }
 exit_early:
                closedir(fdir);
-
-               pop_exclude_per_directory(dir, exclude_stk);
        }
 
        return contents;
@@ -654,32 +685,6 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
 {
        struct path_simplify *simplify = create_simplify(pathspec);
 
-       /*
-        * Make sure to do the per-directory exclude for all the
-        * directories leading up to our base.
-        */
-       if (baselen) {
-               if (dir->exclude_per_dir) {
-                       char *p, *pp = xmalloc(baselen+1);
-                       memcpy(pp, base, baselen+1);
-                       p = pp;
-                       while (1) {
-                               char save = *p;
-                               *p = 0;
-                               push_exclude_per_directory(dir, pp, p-pp);
-                               *p++ = save;
-                               if (!save)
-                                       break;
-                               p = strchr(p, '/');
-                               if (p)
-                                       p++;
-                               else
-                                       p = pp + baselen;
-                       }
-                       free(pp);
-               }
-       }
-
        read_directory_recursive(dir, path, base, baselen, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
@@ -687,11 +692,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
        return dir->nr;
 }
 
-int
-file_exists(const char *f)
+int file_exists(const char *f)
 {
-  struct stat sb;
-  return stat(f, &sb) == 0;
+       struct stat sb;
+       return lstat(f, &sb) == 0;
 }
 
 /*
@@ -777,3 +781,15 @@ int remove_dir_recursively(struct strbuf *path, int only_empty)
                ret = rmdir(path->buf);
        return ret;
 }
+
+void setup_standard_excludes(struct dir_struct *dir)
+{
+       const char *path;
+
+       dir->exclude_per_dir = ".gitignore";
+       path = git_path("info/exclude");
+       if (!access(path, R_OK))
+               add_excludes_from_file(dir, path);
+       if (excludes_file && !access(excludes_file, R_OK))
+               add_excludes_from_file(dir, excludes_file);
+}