diff options
Diffstat (limited to 'dir.c')
-rw-r--r-- | dir.c | 223 |
1 files changed, 181 insertions, 42 deletions
@@ -87,6 +87,21 @@ int fill_directory(struct dir_struct *dir, const char **pathspec) return len; } +int within_depth(const char *name, int namelen, + int depth, int max_depth) +{ + const char *cp = name, *cpe = name + namelen; + + while (cp < cpe) { + if (*cp++ != '/') + continue; + depth++; + if (depth > max_depth) + return 0; + } + return 1; +} + /* * Does 'match' match the given name? * A match is found if @@ -184,6 +199,95 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, return retval; } +/* + * Does 'match' match the given name? + * A match is found if + * + * (1) the 'match' string is leading directory of 'name', or + * (2) the 'match' string is a wildcard and matches 'name', or + * (3) the 'match' string is exactly the same as 'name'. + * + * and the return value tells which case it was. + * + * It returns 0 when there is no match. + */ +static int match_pathspec_item(const struct pathspec_item *item, int prefix, + const char *name, int namelen) +{ + /* name/namelen has prefix cut off by caller */ + const char *match = item->match + prefix; + int matchlen = item->len - prefix; + + /* If the match was just the prefix, we matched */ + if (!*match) + return MATCHED_RECURSIVELY; + + if (matchlen <= namelen && !strncmp(match, name, matchlen)) { + if (matchlen == namelen) + return MATCHED_EXACTLY; + + if (match[matchlen-1] == '/' || name[matchlen] == '/') + return MATCHED_RECURSIVELY; + } + + if (item->use_wildcard && !fnmatch(match, name, 0)) + return MATCHED_FNMATCH; + + return 0; +} + +/* + * Given a name and a list of pathspecs, see if the name matches + * any of the pathspecs. The caller is also interested in seeing + * all pathspec matches some names it calls this function with + * (otherwise the user could have mistyped the unmatched pathspec), + * and a mark is left in seen[] array for pathspec element that + * actually matched anything. + */ +int match_pathspec_depth(const struct pathspec *ps, + const char *name, int namelen, + int prefix, char *seen) +{ + int i, retval = 0; + + if (!ps->nr) { + if (!ps->recursive || ps->max_depth == -1) + return MATCHED_RECURSIVELY; + + if (within_depth(name, namelen, 0, ps->max_depth)) + return MATCHED_EXACTLY; + else + return 0; + } + + name += prefix; + namelen -= prefix; + + for (i = ps->nr - 1; i >= 0; i--) { + int how; + if (seen && seen[i] == MATCHED_EXACTLY) + continue; + how = match_pathspec_item(ps->items+i, prefix, name, namelen); + if (ps->recursive && ps->max_depth != -1 && + how && how != MATCHED_FNMATCH) { + int len = ps->items[i].len; + if (name[len] == '/') + len++; + if (within_depth(name+len, namelen-len, 0, ps->max_depth)) + how = MATCHED_EXACTLY; + else + how = 0; + } + if (how) { + if (retval < how) + retval = how; + if (seen && seen[i] < how) + seen[i] = how; + } + } + return retval; +} + static int no_wildcard(const char *string) { return string[strcspn(string, "*?[{\\")] == '\0'; @@ -1001,57 +1105,45 @@ int file_exists(const char *f) } /* - * get_relative_cwd() gets the prefix of the current working directory - * relative to 'dir'. If we are not inside 'dir', it returns NULL. - * - * As a convenience, it also returns NULL if 'dir' is already NULL. The - * reason for this behaviour is that it is natural for functions returning - * directory names to return NULL to say "this directory does not exist" - * or "this directory is invalid". These cases are usually handled the - * same as if the cwd is not inside 'dir' at all, so get_relative_cwd() - * returns NULL for both of them. - * - * Most notably, get_relative_cwd(buffer, size, get_git_work_tree()) - * unifies the handling of "outside work tree" with "no work tree at all". + * Given two normalized paths (a trailing slash is ok), if subdir is + * outside dir, return -1. Otherwise return the offset in subdir that + * can be used as relative path to dir. */ -char *get_relative_cwd(char *buffer, int size, const char *dir) +int dir_inside_of(const char *subdir, const char *dir) { - char *cwd = buffer; + int offset = 0; - if (!dir) - return NULL; - if (!getcwd(buffer, size)) - die_errno("can't find the current directory"); + assert(dir && subdir && *dir && *subdir); - if (!is_absolute_path(dir)) - dir = make_absolute_path(dir); - - while (*dir && *dir == *cwd) { + while (*dir && *subdir && *dir == *subdir) { dir++; - cwd++; - } - if (*dir) - return NULL; - switch (*cwd) { - case '\0': - return cwd; - case '/': - return cwd + 1; - default: - /* - * dir can end with a path separator when it's root - * directory. Return proper prefix in that case. - */ - if (dir[-1] == '/') - return cwd; - return NULL; + subdir++; + offset++; } + + /* hel[p]/me vs hel[l]/yeah */ + if (*dir && *subdir) + return -1; + + if (!*subdir) + return !*dir ? offset : -1; /* same dir */ + + /* foo/[b]ar vs foo/[] */ + if (is_dir_sep(dir[-1])) + return is_dir_sep(subdir[-1]) ? offset : -1; + + /* foo[/]bar vs foo[] */ + return is_dir_sep(*subdir) ? offset + 1 : -1; } int is_inside_dir(const char *dir) { - char buffer[PATH_MAX]; - return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL; + char cwd[PATH_MAX]; + if (!dir) + return 0; + if (!getcwd(cwd, sizeof(cwd))) + die_errno("can't find the current directory"); + return dir_inside_of(cwd, dir) >= 0; } int is_empty_dir(const char *path) @@ -1088,7 +1180,7 @@ int remove_dir_recursively(struct strbuf *path, int flag) dir = opendir(path->buf); if (!dir) - return -1; + return rmdir(path->buf); if (path->buf[original_len - 1] != '/') strbuf_addch(path, '/'); @@ -1151,3 +1243,50 @@ int remove_path(const char *name) return 0; } +static int pathspec_item_cmp(const void *a_, const void *b_) +{ + struct pathspec_item *a, *b; + + a = (struct pathspec_item *)a_; + b = (struct pathspec_item *)b_; + return strcmp(a->match, b->match); +} + +int init_pathspec(struct pathspec *pathspec, const char **paths) +{ + const char **p = paths; + int i; + + memset(pathspec, 0, sizeof(*pathspec)); + if (!p) + return 0; + while (*p) + p++; + pathspec->raw = paths; + pathspec->nr = p - paths; + if (!pathspec->nr) + return 0; + + pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr); + for (i = 0; i < pathspec->nr; i++) { + struct pathspec_item *item = pathspec->items+i; + const char *path = paths[i]; + + item->match = path; + item->len = strlen(path); + item->use_wildcard = !no_wildcard(path); + if (item->use_wildcard) + pathspec->has_wildcard = 1; + } + + qsort(pathspec->items, pathspec->nr, + sizeof(struct pathspec_item), pathspec_item_cmp); + + return 0; +} + +void free_pathspec(struct pathspec *pathspec) +{ + free(pathspec->items); + pathspec->items = NULL; +} |