receive-pack: receive.denyDeleteCurrent
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index 3e345c2fc508eb82e66f4ffe2c825670f0bddd99..cfd1ea587d9cce825e238ca81ea99b752543dada 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -17,6 +17,7 @@ struct path_simplify {
 static int read_directory_recursive(struct dir_struct *dir,
        const char *path, const char *base, int baselen,
        int check_only, const struct path_simplify *simplify);
+static int get_dtype(struct dirent *de, const char *path);
 
 int common_prefix(const char **pathspec)
 {
@@ -68,18 +69,31 @@ static int match_one(const char *match, const char *name, int namelen)
        int matchlen;
 
        /* If the match was just the prefix, we matched */
-       matchlen = strlen(match);
-       if (!matchlen)
+       if (!*match)
                return MATCHED_RECURSIVELY;
 
+       for (;;) {
+               unsigned char c1 = *match;
+               unsigned char c2 = *name;
+               if (c1 == '\0' || is_glob_special(c1))
+                       break;
+               if (c1 != c2)
+                       return 0;
+               match++;
+               name++;
+               namelen--;
+       }
+
+
        /*
         * If we don't match the matchstring exactly,
         * we need to match by fnmatch
         */
+       matchlen = strlen(match);
        if (strncmp(match, name, matchlen))
                return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
 
-       if (!name[matchlen])
+       if (namelen == matchlen)
                return MATCHED_EXACTLY;
        if (match[matchlen-1] == '/' || name[matchlen] == '/')
                return MATCHED_RECURSIVELY;
@@ -94,25 +108,28 @@ static int match_one(const char *match, const char *name, int namelen)
  * and a mark is left in seen[] array for pathspec element that
  * actually matched anything.
  */
-int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
+int match_pathspec(const char **pathspec, const char *name, int namelen,
+               int prefix, char *seen)
 {
-       int retval;
-       const char *match;
+       int i, retval = 0;
+
+       if (!pathspec)
+               return 1;
 
        name += prefix;
        namelen -= prefix;
 
-       for (retval = 0; (match = *pathspec++) != NULL; seen++) {
+       for (i = 0; pathspec[i] != NULL; i++) {
                int how;
-               if (retval && *seen == MATCHED_EXACTLY)
+               const char *match = pathspec[i] + prefix;
+               if (seen && seen[i] == MATCHED_EXACTLY)
                        continue;
-               match += prefix;
                how = match_one(match, name, namelen);
                if (how) {
                        if (retval < how)
                                retval = how;
-                       if (*seen < how)
-                               *seen = how;
+                       if (seen && seen[i] < how)
+                               seen[i] = how;
                }
        }
        return retval;
@@ -126,18 +143,34 @@ static int no_wildcard(const char *string)
 void add_exclude(const char *string, const char *base,
                 int baselen, struct exclude_list *which)
 {
-       struct exclude *x = xmalloc(sizeof (*x));
+       struct exclude *x;
+       size_t len;
+       int to_exclude = 1;
+       int flags = 0;
 
-       x->to_exclude = 1;
        if (*string == '!') {
-               x->to_exclude = 0;
+               to_exclude = 0;
                string++;
        }
-       x->pattern = string;
+       len = strlen(string);
+       if (len && string[len - 1] == '/') {
+               char *s;
+               x = xmalloc(sizeof(*x) + len);
+               s = (char*)(x+1);
+               memcpy(s, string, len - 1);
+               s[len - 1] = '\0';
+               string = s;
+               x->pattern = s;
+               flags = EXC_FLAG_MUSTBEDIR;
+       } else {
+               x = xmalloc(sizeof(*x));
+               x->pattern = string;
+       }
+       x->to_exclude = to_exclude;
        x->patternlen = strlen(string);
        x->base = base;
        x->baselen = baselen;
-       x->flags = 0;
+       x->flags = flags;
        if (!strchr(string, '/'))
                x->flags |= EXC_FLAG_NODIR;
        if (no_wildcard(string))
@@ -261,7 +294,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
  * Return 1 for exclude, 0 for include and -1 for undecided.
  */
 static int excluded_1(const char *pathname,
-                     int pathlen, const char *basename,
+                     int pathlen, const char *basename, int *dtype,
                      struct exclude_list *el)
 {
        int i;
@@ -272,6 +305,13 @@ static int excluded_1(const char *pathname,
                        const char *exclude = x->pattern;
                        int to_exclude = x->to_exclude;
 
+                       if (x->flags & EXC_FLAG_MUSTBEDIR) {
+                               if (*dtype == DT_UNKNOWN)
+                                       *dtype = get_dtype(NULL, pathname);
+                               if (*dtype != DT_DIR)
+                                       continue;
+                       }
+
                        if (x->flags & EXC_FLAG_NODIR) {
                                /* match basename */
                                if (x->flags & EXC_FLAG_NOWILDCARD) {
@@ -314,7 +354,7 @@ static int excluded_1(const char *pathname,
        return -1; /* undecided */
 }
 
-int excluded(struct dir_struct *dir, const char *pathname)
+int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
 {
        int pathlen = strlen(pathname);
        int st;
@@ -323,7 +363,8 @@ int excluded(struct dir_struct *dir, const char *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])) {
+               switch (excluded_1(pathname, pathlen, basename,
+                                  dtype_p, &dir->exclude_list[st])) {
                case 0:
                        return 0;
                case 1:
@@ -344,16 +385,16 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
        return ent;
 }
 
-struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
+static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
-       if (cache_name_pos(pathname, len) >= 0)
+       if (cache_name_exists(pathname, len, ignore_case))
                return NULL;
 
        ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
        return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
 }
 
-struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
 {
        if (cache_name_pos(pathname, len) >= 0)
                return NULL;
@@ -391,7 +432,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
                        break;
                if (endchar == '/')
                        return index_directory;
-               if (!endchar && S_ISGITLINK(ntohl(ce->ce_mode)))
+               if (!endchar && S_ISGITLINK(ce->ce_mode))
                        return index_gitdir;
        }
        return index_nonexistent;
@@ -508,7 +549,7 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si
 
 static int get_dtype(struct dirent *de, const char *path)
 {
-       int dtype = DTYPE(de);
+       int dtype = de ? DTYPE(de) : DT_UNKNOWN;
        struct stat st;
 
        if (dtype != DT_UNKNOWN)
@@ -547,10 +588,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        int len, dtype;
                        int exclude;
 
-                       if ((de->d_name[0] == '.') &&
-                           (de->d_name[1] == 0 ||
-                            !strcmp(de->d_name + 1, ".") ||
-                            !strcmp(de->d_name + 1, "git")))
+                       if (is_dot_or_dotdot(de->d_name) ||
+                            !strcmp(de->d_name, ".git"))
                                continue;
                        len = strlen(de->d_name);
                        /* Ignore overly long pathnames! */
@@ -560,7 +599,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        if (simplify_away(fullname, baselen + len, simplify))
                                continue;
 
-                       exclude = excluded(dir, fullname);
+                       dtype = DTYPE(de);
+                       exclude = excluded(dir, fullname, &dtype);
                        if (exclude && dir->collect_ignored
                            && in_pathspec(fullname, baselen + len, simplify))
                                dir_add_ignored(dir, fullname, baselen + len);
@@ -572,7 +612,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        if (exclude && !dir->show_ignored)
                                continue;
 
-                       dtype = get_dtype(de, fullname);
+                       if (dtype == DT_UNKNOWN)
+                               dtype = get_dtype(de, fullname);
 
                        /*
                         * Do we want to see just the ignored files?
@@ -635,17 +676,12 @@ static int cmp_name(const void *p1, const void *p2)
  */
 static int simple_length(const char *match)
 {
-       const char special[256] = {
-               [0] = 1, ['?'] = 1,
-               ['\\'] = 1, ['*'] = 1,
-               ['['] = 1
-       };
        int len = -1;
 
        for (;;) {
                unsigned char c = *match++;
                len++;
-               if (special[c])
+               if (c == '\0' || is_glob_special(c))
                        return len;
        }
 }
@@ -677,14 +713,17 @@ static struct path_simplify *create_simplify(const char **pathspec)
 
 static void free_simplify(struct path_simplify *simplify)
 {
-       if (simplify)
-               free(simplify);
+       free(simplify);
 }
 
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
 {
-       struct path_simplify *simplify = create_simplify(pathspec);
+       struct path_simplify *simplify;
+
+       if (has_symlink_leading_path(strlen(path), path))
+               return dir->nr;
 
+       simplify = create_simplify(pathspec);
        read_directory_recursive(dir, path, base, baselen, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
@@ -741,6 +780,25 @@ int is_inside_dir(const char *dir)
        return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
 }
 
+int is_empty_dir(const char *path)
+{
+       DIR *dir = opendir(path);
+       struct dirent *e;
+       int ret = 1;
+
+       if (!dir)
+               return 0;
+
+       while ((e = readdir(dir)) != NULL)
+               if (!is_dot_or_dotdot(e->d_name)) {
+                       ret = 0;
+                       break;
+               }
+
+       closedir(dir);
+       return ret;
+}
+
 int remove_dir_recursively(struct strbuf *path, int only_empty)
 {
        DIR *dir = opendir(path->buf);
@@ -755,10 +813,8 @@ int remove_dir_recursively(struct strbuf *path, int only_empty)
        len = path->len;
        while ((e = readdir(dir)) != NULL) {
                struct stat st;
-               if ((e->d_name[0] == '.') &&
-                   ((e->d_name[1] == 0) ||
-                    ((e->d_name[1] == '.') && e->d_name[2] == 0)))
-                       continue; /* "." and ".." */
+               if (is_dot_or_dotdot(e->d_name))
+                       continue;
 
                strbuf_setlen(path, len);
                strbuf_addstr(path, e->d_name);
@@ -793,3 +849,23 @@ void setup_standard_excludes(struct dir_struct *dir)
        if (excludes_file && !access(excludes_file, R_OK))
                add_excludes_from_file(dir, excludes_file);
 }
+
+int remove_path(const char *name)
+{
+       char *slash;
+
+       if (unlink(name) && errno != ENOENT)
+               return -1;
+
+       slash = strrchr(name, '/');
+       if (slash) {
+               char *dirs = xstrdup(name);
+               slash = dirs + (slash - name);
+               do {
+                       *slash = '\0';
+               } while (rmdir(dirs) && (slash = strrchr(dirs, '/')));
+               free(dirs);
+       }
+       return 0;
+}
+