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)
{
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;
* 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;
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))
* 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;
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) {
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;
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:
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;
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;
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)
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! */
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);
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?
*/
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;
}
}
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);
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);
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);
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;
+}
+