summaryrefslogtreecommitdiff
path: root/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'dir.c')
-rw-r--r--dir.c653
1 files changed, 512 insertions, 141 deletions
diff --git a/dir.c b/dir.c
index e05b850acf..532bcb65b5 100644
--- a/dir.c
+++ b/dir.c
@@ -18,6 +18,22 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, in
int check_only, const struct path_simplify *simplify);
static int get_dtype(struct dirent *de, const char *path, int len);
+/* helper string functions with support for the ignore_case flag */
+int strcmp_icase(const char *a, const char *b)
+{
+ return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
+}
+
+int strncmp_icase(const char *a, const char *b, size_t count)
+{
+ return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
+}
+
+int fnmatch_icase(const char *pattern, const char *string, int flags)
+{
+ return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
+}
+
static int common_prefix(const char **pathspec)
{
const char *path, *slash, *next;
@@ -31,22 +47,22 @@ static int common_prefix(const char **pathspec)
if (!slash)
return 0;
+ /*
+ * The first 'prefix' characters of 'path' are common leading
+ * path components among the pathspecs we have seen so far,
+ * including the trailing slash.
+ */
prefix = slash - path + 1;
while ((next = *++pathspec) != NULL) {
- int len = strlen(next);
- if (len >= prefix && !memcmp(path, next, prefix))
+ int len, last_matching_slash = -1;
+ for (len = 0; len < prefix && next[len] == path[len]; len++)
+ if (next[len] == '/')
+ last_matching_slash = len;
+ if (len == prefix)
continue;
- len = prefix - 1;
- for (;;) {
- if (!len)
- return 0;
- if (next[--len] != '/')
- continue;
- if (memcmp(path, next, len+1))
- continue;
- prefix = len + 1;
- break;
- }
+ if (last_matching_slash < 0)
+ return 0;
+ prefix = last_matching_slash + 1;
}
return prefix;
}
@@ -71,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
@@ -91,16 +122,30 @@ static int match_one(const char *match, const char *name, int namelen)
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 (ignore_case) {
+ for (;;) {
+ unsigned char c1 = tolower(*match);
+ unsigned char c2 = tolower(*name);
+ if (c1 == '\0' || is_glob_special(c1))
+ break;
+ if (c1 != c2)
+ return 0;
+ match++;
+ name++;
+ namelen--;
+ }
+ } else {
+ 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--;
+ }
}
@@ -109,8 +154,8 @@ static int match_one(const char *match, const char *name, int namelen)
* we need to match by fnmatch
*/
matchlen = strlen(match);
- if (strncmp(match, name, matchlen))
- return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
+ if (strncmp_icase(match, name, matchlen))
+ return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
if (namelen == matchlen)
return MATCHED_EXACTLY;
@@ -154,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->has_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';
@@ -200,36 +334,87 @@ void add_exclude(const char *string, const char *base,
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)
+static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
+{
+ int pos, len;
+ unsigned long sz;
+ enum object_type type;
+ void *data;
+ struct index_state *istate = &the_index;
+
+ len = strlen(path);
+ pos = index_name_pos(istate, path, len);
+ if (pos < 0)
+ return NULL;
+ if (!ce_skip_worktree(istate->cache[pos]))
+ return NULL;
+ data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+ if (!data || type != OBJ_BLOB) {
+ free(data);
+ return NULL;
+ }
+ *size = xsize_t(sz);
+ return data;
+}
+
+void free_excludes(struct exclude_list *el)
+{
+ int i;
+
+ for (i = 0; i < el->nr; i++)
+ free(el->excludes[i]);
+ free(el->excludes);
+
+ el->nr = 0;
+ el->excludes = NULL;
+}
+
+int add_excludes_from_file_to_list(const char *fname,
+ const char *base,
+ int baselen,
+ char **buf_p,
+ struct exclude_list *which,
+ int check_index)
{
struct stat st;
int fd, i;
- size_t size;
+ size_t size = 0;
char *buf, *entry;
fd = open(fname, O_RDONLY);
- if (fd < 0 || fstat(fd, &st) < 0)
- goto err;
- size = xsize_t(st.st_size);
- if (size == 0) {
- close(fd);
- return 0;
+ if (fd < 0 || fstat(fd, &st) < 0) {
+ if (0 <= fd)
+ close(fd);
+ if (!check_index ||
+ (buf = read_skip_worktree_file_from_index(fname, &size)) == NULL)
+ return -1;
+ if (size == 0) {
+ free(buf);
+ return 0;
+ }
+ if (buf[size-1] != '\n') {
+ buf = xrealloc(buf, size+1);
+ buf[size++] = '\n';
+ }
}
- buf = xmalloc(size+1);
- if (read_in_full(fd, buf, size) != size)
- {
- free(buf);
- goto err;
+ else {
+ size = xsize_t(st.st_size);
+ if (size == 0) {
+ close(fd);
+ return 0;
+ }
+ buf = xmalloc(size+1);
+ if (read_in_full(fd, buf, size) != size) {
+ free(buf);
+ close(fd);
+ return -1;
+ }
+ buf[size++] = '\n';
+ close(fd);
}
- close(fd);
if (buf_p)
*buf_p = buf;
- buf[size++] = '\n';
entry = buf;
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
@@ -241,17 +426,12 @@ static int add_excludes_from_file_1(const char *fname,
}
}
return 0;
-
- err:
- if (0 <= fd)
- close(fd);
- return -1;
}
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
{
- if (add_excludes_from_file_1(fname, "", 0, NULL,
- &dir->exclude_list[EXC_FILE]) < 0)
+ if (add_excludes_from_file_to_list(fname, "", 0, NULL,
+ &dir->exclude_list[EXC_FILE], 0) < 0)
die("cannot use %s as an exclude file", fname);
}
@@ -300,9 +480,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
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);
+ add_excludes_from_file_to_list(dir->basebuf,
+ dir->basebuf, stk->baselen,
+ &stk->filebuf, el, 1);
dir->exclude_stack = stk;
current = stk->baselen;
}
@@ -312,9 +492,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
/* Scan the list and let the last match determine the fate.
* Return 1 for exclude, 0 for include and -1 for undecided.
*/
-static int excluded_1(const char *pathname,
- int pathlen, const char *basename, int *dtype,
- struct exclude_list *el)
+int excluded_from_list(const char *pathname,
+ int pathlen, const char *basename, int *dtype,
+ struct exclude_list *el)
{
int i;
@@ -334,14 +514,14 @@ static int excluded_1(const char *pathname,
if (x->flags & EXC_FLAG_NODIR) {
/* match basename */
if (x->flags & EXC_FLAG_NOWILDCARD) {
- if (!strcmp(exclude, basename))
+ if (!strcmp_icase(exclude, basename))
return to_exclude;
} else if (x->flags & EXC_FLAG_ENDSWITH) {
if (x->patternlen - 1 <= pathlen &&
- !strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
+ !strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
return to_exclude;
} else {
- if (fnmatch(exclude, basename, 0) == 0)
+ if (fnmatch_icase(exclude, basename, 0) == 0)
return to_exclude;
}
}
@@ -356,14 +536,14 @@ static int excluded_1(const char *pathname,
if (pathlen < baselen ||
(baselen && pathname[baselen-1] != '/') ||
- strncmp(pathname, x->base, baselen))
+ strncmp_icase(pathname, x->base, baselen))
continue;
if (x->flags & EXC_FLAG_NOWILDCARD) {
- if (!strcmp(exclude, pathname + baselen))
+ if (!strcmp_icase(exclude, pathname + baselen))
return to_exclude;
} else {
- if (fnmatch(exclude, pathname+baselen,
+ if (fnmatch_icase(exclude, pathname+baselen,
FNM_PATHNAME) == 0)
return to_exclude;
}
@@ -382,8 +562,8 @@ int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
prep_exclude(dir, pathname, basename-pathname);
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
- switch (excluded_1(pathname, pathlen, basename,
- dtype_p, &dir->exclude_list[st])) {
+ switch (excluded_from_list(pathname, pathlen, basename,
+ dtype_p, &dir->exclude_list[st])) {
case 0:
return 0;
case 1:
@@ -413,7 +593,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna
return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
}
-static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
{
if (!cache_name_is_other(pathname, len))
return NULL;
@@ -425,10 +605,43 @@ static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pat
enum exist_status {
index_nonexistent = 0,
index_directory,
- index_gitdir,
+ index_gitdir
};
/*
+ * Do not use the alphabetically stored index to look up
+ * the directory name; instead, use the case insensitive
+ * name hash.
+ */
+static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
+{
+ struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+ unsigned char endchar;
+
+ if (!ce)
+ return index_nonexistent;
+ endchar = ce->name[len];
+
+ /*
+ * The cache_entry structure returned will contain this dirname
+ * and possibly additional path components.
+ */
+ if (endchar == '/')
+ return index_directory;
+
+ /*
+ * If there are no additional path components, then this cache_entry
+ * represents a submodule. Submodules, despite being directories,
+ * are stored in the cache without a closing slash.
+ */
+ if (!endchar && S_ISGITLINK(ce->ce_mode))
+ return index_gitdir;
+
+ /* This should never be hit, but it exists just in case. */
+ return index_nonexistent;
+}
+
+/*
* The index sorts alphabetically by entry name, which
* means that a gitlink sorts as '\0' at the end, while
* a directory (which is defined not as an entry, but as
@@ -437,7 +650,12 @@ enum exist_status {
*/
static enum exist_status directory_exists_in_index(const char *dirname, int len)
{
- int pos = cache_name_pos(dirname, len);
+ int pos;
+
+ if (ignore_case)
+ return directory_exists_in_index_icase(dirname, len);
+
+ pos = cache_name_pos(dirname, len);
if (pos < 0)
pos = -pos-1;
while (pos < active_nr) {
@@ -493,7 +711,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
enum directory_treatment {
show_directory,
ignore_directory,
- recurse_into_directory,
+ recurse_into_directory
};
static enum directory_treatment treat_directory(struct dir_struct *dir,
@@ -554,13 +772,29 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli
return 0;
}
-static int in_pathspec(const char *path, int len, const struct path_simplify *simplify)
+/*
+ * This function tells us whether an excluded path matches a
+ * list of "interesting" pathspecs. That is, whether a path matched
+ * by any of the pathspecs could possibly be ignored by excluding
+ * the specified path. This can happen if:
+ *
+ * 1. the path is mentioned explicitly in the pathspec
+ *
+ * 2. the path is a directory prefix of some element in the
+ * pathspec
+ */
+static int exclude_matches_pathspec(const char *path, int len,
+ const struct path_simplify *simplify)
{
if (simplify) {
for (; simplify->path; simplify++) {
if (len == simplify->len
&& !memcmp(path, simplify->path, len))
return 1;
+ if (len < simplify->len
+ && simplify->path[len] == '/'
+ && !memcmp(path, simplify->path, len))
+ return 1;
}
}
return 0;
@@ -625,6 +859,92 @@ static int get_dtype(struct dirent *de, const char *path, int len)
return dtype;
}
+enum path_treatment {
+ path_ignored,
+ path_handled,
+ path_recurse
+};
+
+static enum path_treatment treat_one_path(struct dir_struct *dir,
+ char *path, int *len,
+ const struct path_simplify *simplify,
+ int dtype, struct dirent *de)
+{
+ int exclude = excluded(dir, path, &dtype);
+ if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
+ && exclude_matches_pathspec(path, *len, simplify))
+ dir_add_ignored(dir, path, *len);
+
+ /*
+ * Excluded? If we don't explicitly want to show
+ * ignored files, ignore it
+ */
+ if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
+ return path_ignored;
+
+ if (dtype == DT_UNKNOWN)
+ dtype = get_dtype(de, path, *len);
+
+ /*
+ * Do we want to see just the ignored files?
+ * We still need to recurse into directories,
+ * even if we don't ignore them, since the
+ * directory may contain files that we do..
+ */
+ if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
+ if (dtype != DT_DIR)
+ return path_ignored;
+ }
+
+ switch (dtype) {
+ default:
+ return path_ignored;
+ case DT_DIR:
+ memcpy(path + *len, "/", 2);
+ (*len)++;
+ switch (treat_directory(dir, path, *len, simplify)) {
+ case show_directory:
+ if (exclude != !!(dir->flags
+ & DIR_SHOW_IGNORED))
+ return path_ignored;
+ break;
+ case recurse_into_directory:
+ return path_recurse;
+ case ignore_directory:
+ return path_ignored;
+ }
+ break;
+ case DT_REG:
+ case DT_LNK:
+ break;
+ }
+ return path_handled;
+}
+
+static enum path_treatment treat_path(struct dir_struct *dir,
+ struct dirent *de,
+ char *path, int path_max,
+ int baselen,
+ const struct path_simplify *simplify,
+ int *len)
+{
+ 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))
+ return path_ignored;
+
+ dtype = DTYPE(de);
+ return treat_one_path(dir, path, len, simplify, dtype, de);
+}
+
/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
@@ -634,7 +954,10 @@ static int get_dtype(struct dirent *de, const char *path, int len)
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
-static int read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
+static int read_directory_recursive(struct dir_struct *dir,
+ const char *base, int baselen,
+ int check_only,
+ const struct path_simplify *simplify)
{
DIR *fdir = opendir(*base ? base : ".");
int contents = 0;
@@ -645,70 +968,16 @@ static int read_directory_recursive(struct dir_struct *dir, const char *base, in
memcpy(path, base, baselen);
while ((de = readdir(fdir)) != NULL) {
- int len, dtype;
- int exclude;
-
- if (is_dot_or_dotdot(de->d_name) ||
- !strcmp(de->d_name, ".git"))
- continue;
- len = strlen(de->d_name);
- /* Ignore overly long pathnames! */
- if (len + baselen + 8 > sizeof(path))
+ int len;
+ switch (treat_path(dir, de, path, sizeof(path),
+ baselen, simplify, &len)) {
+ case path_recurse:
+ contents += read_directory_recursive
+ (dir, path, len, 0, simplify);
continue;
- memcpy(path + baselen, de->d_name, len+1);
- len = baselen + len;
- if (simplify_away(path, len, simplify))
- continue;
-
- dtype = DTYPE(de);
- exclude = excluded(dir, path, &dtype);
- if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
- && in_pathspec(path, len, simplify))
- dir_add_ignored(dir, path,len);
-
- /*
- * Excluded? If we don't explicitly want to show
- * ignored files, ignore it
- */
- if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
- continue;
-
- if (dtype == DT_UNKNOWN)
- dtype = get_dtype(de, path, len);
-
- /*
- * Do we want to see just the ignored files?
- * We still need to recurse into directories,
- * even if we don't ignore them, since the
- * directory may contain files that we do..
- */
- if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
- if (dtype != DT_DIR)
- continue;
- }
-
- switch (dtype) {
- default:
+ case path_ignored:
continue;
- case DT_DIR:
- memcpy(path + len, "/", 2);
- len++;
- switch (treat_directory(dir, path, len, simplify)) {
- case show_directory:
- if (exclude != !!(dir->flags
- & DIR_SHOW_IGNORED))
- continue;
- break;
- case recurse_into_directory:
- contents += read_directory_recursive(dir,
- path, len, 0, simplify);
- continue;
- case ignore_directory:
- continue;
- }
- break;
- case DT_REG:
- case DT_LNK:
+ case path_handled:
break;
}
contents++;
@@ -778,6 +1047,41 @@ static void free_simplify(struct path_simplify *simplify)
free(simplify);
}
+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;
+ const char *cp;
+
+ while (len && path[len - 1] == '/')
+ len--;
+ if (!len)
+ return 1;
+ baselen = 0;
+ while (1) {
+ cp = path + baselen + !!baselen;
+ cp = memchr(cp, '/', path + len - cp);
+ if (!cp)
+ 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,
+ DT_DIR, NULL) == path_ignored)
+ return 0; /* do not recurse into it */
+ if (len <= baselen)
+ return 1; /* finished checking */
+ }
+}
+
int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
{
struct path_simplify *simplify;
@@ -786,7 +1090,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char
return dir->nr;
simplify = create_simplify(pathspec);
- read_directory_recursive(dir, path, len, 0, simplify);
+ if (!len || treat_leading_path(dir, path, len, simplify))
+ read_directory_recursive(dir, path, len, 0, simplify);
free_simplify(simplify);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
@@ -823,7 +1128,7 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)
die_errno("can't find the current directory");
if (!is_absolute_path(dir))
- dir = make_absolute_path(dir);
+ dir = real_path(dir);
while (*dir && *dir == *cwd) {
dir++;
@@ -831,9 +1136,20 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)
}
if (*dir)
return NULL;
- if (*cwd == '/')
+ switch (*cwd) {
+ case '\0':
+ return cwd;
+ case '/':
return cwd + 1;
- return cwd;
+ 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;
+ }
}
int is_inside_dir(const char *dir)
@@ -861,14 +1177,22 @@ int is_empty_dir(const char *path)
return ret;
}
-int remove_dir_recursively(struct strbuf *path, int only_empty)
+int remove_dir_recursively(struct strbuf *path, int flag)
{
- DIR *dir = opendir(path->buf);
+ DIR *dir;
struct dirent *e;
int ret = 0, original_len = path->len, len;
+ int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
+ unsigned char submodule_head[20];
+ if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+ !resolve_gitlink_ref(path->buf, "HEAD", submodule_head))
+ /* Do not descend and nuke a nested git work tree. */
+ return 0;
+
+ dir = opendir(path->buf);
if (!dir)
- return -1;
+ return rmdir(path->buf);
if (path->buf[original_len - 1] != '/')
strbuf_addch(path, '/');
@@ -925,9 +1249,56 @@ int remove_path(const char *name)
slash = dirs + (slash - name);
do {
*slash = '\0';
- } while (rmdir(dirs) && (slash = strrchr(dirs, '/')));
+ } while (rmdir(dirs) == 0 && (slash = strrchr(dirs, '/')));
free(dirs);
}
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->has_wildcard = !no_wildcard(path);
+ if (item->has_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;
+}