Merge branch 'nd/index-errno' into maint-1.7.11
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index e98760c72deb94d86a911f706f7c6c2beca58e5e..2c02b312b77f3363c234bdc66faaeafd5899586d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -74,7 +74,6 @@ char *common_prefix(const char **pathspec)
 
 int fill_directory(struct dir_struct *dir, const char **pathspec)
 {
-       const char *path;
        size_t len;
 
        /*
@@ -82,15 +81,9 @@ int fill_directory(struct dir_struct *dir, const char **pathspec)
         * use that to optimize the directory walk
         */
        len = common_prefix_len(pathspec);
-       path = "";
-
-       if (len)
-               path = xmemdupz(*pathspec, len);
 
        /* Read the directory and prune it */
-       read_directory(dir, path, len, pathspec);
-       if (*path)
-               free((char *)path);
+       read_directory(dir, pathspec ? *pathspec : "", len, pathspec);
        return len;
 }
 
@@ -560,7 +553,7 @@ int excluded_from_list(const char *pathname,
        return -1; /* undecided */
 }
 
-int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
+static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
 {
        int pathlen = strlen(pathname);
        int st;
@@ -580,6 +573,64 @@ int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
        return 0;
 }
 
+void path_exclude_check_init(struct path_exclude_check *check,
+                            struct dir_struct *dir)
+{
+       check->dir = dir;
+       strbuf_init(&check->path, 256);
+}
+
+void path_exclude_check_clear(struct path_exclude_check *check)
+{
+       strbuf_release(&check->path);
+}
+
+/*
+ * Is this name excluded?  This is for a caller like show_files() that
+ * do not honor directory hierarchy and iterate through paths that are
+ * possibly in an ignored directory.
+ *
+ * A path to a directory known to be excluded is left in check->path to
+ * optimize for repeated checks for files in the same excluded directory.
+ */
+int path_excluded(struct path_exclude_check *check,
+                 const char *name, int namelen, int *dtype)
+{
+       int i;
+       struct strbuf *path = &check->path;
+
+       /*
+        * we allow the caller to pass namelen as an optimization; it
+        * must match the length of the name, as we eventually call
+        * excluded() on the whole name string.
+        */
+       if (namelen < 0)
+               namelen = strlen(name);
+
+       if (path->len &&
+           path->len <= namelen &&
+           !memcmp(name, path->buf, path->len) &&
+           (!name[path->len] || name[path->len] == '/'))
+               return 1;
+
+       strbuf_setlen(path, 0);
+       for (i = 0; name[i]; i++) {
+               int ch = name[i];
+
+               if (ch == '/') {
+                       int dt = DT_DIR;
+                       if (excluded(check->dir, path->buf, &dt))
+                               return 1;
+               }
+               strbuf_addch(path, ch);
+       }
+
+       /* An entry in the index; cannot be a directory with subentries */
+       strbuf_setlen(path, 0);
+
+       return excluded(check->dir, name, dtype);
+}
+
 static struct dir_entry *dir_entry_new(const char *pathname, int len)
 {
        struct dir_entry *ent;
@@ -873,14 +924,14 @@ enum path_treatment {
 };
 
 static enum path_treatment treat_one_path(struct dir_struct *dir,
-                                         char *path, int *len,
+                                         struct strbuf *path,
                                          const struct path_simplify *simplify,
                                          int dtype, struct dirent *de)
 {
-       int exclude = excluded(dir, path, &dtype);
+       int exclude = excluded(dir, path->buf, &dtype);
        if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
-           && exclude_matches_pathspec(path, *len, simplify))
-               dir_add_ignored(dir, path, *len);
+           && exclude_matches_pathspec(path->buf, path->len, simplify))
+               dir_add_ignored(dir, path->buf, path->len);
 
        /*
         * Excluded? If we don't explicitly want to show
@@ -890,7 +941,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                return path_ignored;
 
        if (dtype == DT_UNKNOWN)
-               dtype = get_dtype(de, path, *len);
+               dtype = get_dtype(de, path->buf, path->len);
 
        /*
         * Do we want to see just the ignored files?
@@ -907,9 +958,8 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
        default:
                return path_ignored;
        case DT_DIR:
-               memcpy(path + *len, "/", 2);
-               (*len)++;
-               switch (treat_directory(dir, path, *len, simplify)) {
+               strbuf_addch(path, '/');
+               switch (treat_directory(dir, path->buf, path->len, simplify)) {
                case show_directory:
                        if (exclude != !!(dir->flags
                                          & DIR_SHOW_IGNORED))
@@ -930,26 +980,21 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
 
 static enum path_treatment treat_path(struct dir_struct *dir,
                                      struct dirent *de,
-                                     char *path, int path_max,
+                                     struct strbuf *path,
                                      int baselen,
-                                     const struct path_simplify *simplify,
-                                     int *len)
+                                     const struct path_simplify *simplify)
 {
        int dtype;
 
        if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
                return path_ignored;
-       *len = strlen(de->d_name);
-       /* Ignore overly long pathnames! */
-       if (*len + baselen + 8 > path_max)
-               return path_ignored;
-       memcpy(path + baselen, de->d_name, *len + 1);
-       *len += baselen;
-       if (simplify_away(path, *len, simplify))
+       strbuf_setlen(path, baselen);
+       strbuf_addstr(path, de->d_name);
+       if (simplify_away(path->buf, path->len, simplify))
                return path_ignored;
 
        dtype = DTYPE(de);
-       return treat_one_path(dir, path, len, simplify, dtype, de);
+       return treat_one_path(dir, path, simplify, dtype, de);
 }
 
 /*
@@ -966,22 +1011,23 @@ static int read_directory_recursive(struct dir_struct *dir,
                                    int check_only,
                                    const struct path_simplify *simplify)
 {
-       DIR *fdir = opendir(*base ? base : ".");
+       DIR *fdir;
        int contents = 0;
        struct dirent *de;
-       char path[PATH_MAX + 1];
+       struct strbuf path = STRBUF_INIT;
 
-       if (!fdir)
-               return 0;
+       strbuf_add(&path, base, baselen);
 
-       memcpy(path, base, baselen);
+       fdir = opendir(path.len ? path.buf : ".");
+       if (!fdir)
+               goto out;
 
        while ((de = readdir(fdir)) != NULL) {
-               int len;
-               switch (treat_path(dir, de, path, sizeof(path),
-                                  baselen, simplify, &len)) {
+               switch (treat_path(dir, de, &path, baselen, simplify)) {
                case path_recurse:
-                       contents += read_directory_recursive(dir, path, len, 0, simplify);
+                       contents += read_directory_recursive(dir, path.buf,
+                                                            path.len, 0,
+                                                            simplify);
                        continue;
                case path_ignored:
                        continue;
@@ -990,12 +1036,12 @@ static int read_directory_recursive(struct dir_struct *dir,
                }
                contents++;
                if (check_only)
-                       goto exit_early;
-               else
-                       dir_add_name(dir, path, len);
+                       break;
+               dir_add_name(dir, path.buf, path.len);
        }
-exit_early:
        closedir(fdir);
+ out:
+       strbuf_release(&path);
 
        return contents;
 }
@@ -1058,8 +1104,8 @@ static int treat_leading_path(struct dir_struct *dir,
                              const char *path, int len,
                              const struct path_simplify *simplify)
 {
-       char pathbuf[PATH_MAX];
-       int baselen, blen;
+       struct strbuf sb = STRBUF_INIT;
+       int baselen, rc = 0;
        const char *cp;
 
        while (len && path[len - 1] == '/')
@@ -1074,19 +1120,22 @@ static int treat_leading_path(struct dir_struct *dir,
                        baselen = len;
                else
                        baselen = cp - path;
-               memcpy(pathbuf, path, baselen);
-               pathbuf[baselen] = '\0';
-               if (!is_directory(pathbuf))
-                       return 0;
-               if (simplify_away(pathbuf, baselen, simplify))
-                       return 0;
-               blen = baselen;
-               if (treat_one_path(dir, pathbuf, &blen, simplify,
+               strbuf_setlen(&sb, 0);
+               strbuf_add(&sb, path, baselen);
+               if (!is_directory(sb.buf))
+                       break;
+               if (simplify_away(sb.buf, sb.len, simplify))
+                       break;
+               if (treat_one_path(dir, &sb, simplify,
                                   DT_DIR, NULL) == path_ignored)
-                       return 0; /* do not recurse into it */
-               if (len <= baselen)
-                       return 1; /* finished checking */
+                       break; /* do not recurse into it */
+               if (len <= baselen) {
+                       rc = 1;
+                       break; /* finished checking */
+               }
        }
+       strbuf_release(&sb);
+       return rc;
 }
 
 int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)