X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=dir.c;h=4c17d3643eebf0d9071003065b5c120db130f9ab;hb=e2b7eaf0ca3897940961d23392d4ff718867ea9f;hp=8d8faf5d788b402d5fd4f5d724954fd2ee178fdb;hpb=4b7f59af2a5b072a0a3950c956842e4d6223a167;p=git.git diff --git a/dir.c b/dir.c index 8d8faf5d7..4c17d3643 100644 --- a/dir.c +++ b/dir.c @@ -443,6 +443,24 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si return 0; } +static int get_dtype(struct dirent *de, const char *path) +{ + int dtype = DTYPE(de); + struct stat st; + + if (dtype != DT_UNKNOWN) + return dtype; + if (lstat(path, &st)) + return dtype; + if (S_ISREG(st.st_mode)) + return DT_REG; + if (S_ISDIR(st.st_mode)) + return DT_DIR; + if (S_ISLNK(st.st_mode)) + return DT_LNK; + return dtype; +} + /* * Read a directory tree. We currently ignore anything but * directories, regular files and symlinks. That's because git @@ -466,7 +484,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co exclude_stk = push_exclude_per_directory(dir, base, baselen); while ((de = readdir(fdir)) != NULL) { - int len; + int len, dtype; int exclude; if ((de->d_name[0] == '.') && @@ -486,24 +504,30 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co if (exclude && dir->collect_ignored && in_pathspec(fullname, baselen + len, simplify)) dir_add_ignored(dir, fullname, baselen + len); - if (exclude != dir->show_ignored) { - if (!dir->show_ignored || DTYPE(de) != DT_DIR) { + + /* + * Excluded? If we don't explicitly want to show + * ignored files, ignore it + */ + if (exclude && !dir->show_ignored) + continue; + + dtype = get_dtype(de, fullname); + + /* + * 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->show_ignored) { + if (dtype != DT_DIR) continue; - } } - switch (DTYPE(de)) { - struct stat st; + switch (dtype) { default: continue; - case DT_UNKNOWN: - if (lstat(fullname, &st)) - continue; - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) - break; - if (!S_ISDIR(st.st_mode)) - continue; - /* fallthrough */ case DT_DIR: memcpy(fullname + baselen + len, "/", 2); len++; @@ -642,3 +666,87 @@ file_exists(const char *f) struct stat sb; return stat(f, &sb) == 0; } + +/* + * 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". + */ +char *get_relative_cwd(char *buffer, int size, const char *dir) +{ + char *cwd = buffer; + + if (!dir) + return NULL; + if (!getcwd(buffer, size)) + die("can't find the current directory: %s", strerror(errno)); + + if (!is_absolute_path(dir)) + dir = make_absolute_path(dir); + + while (*dir && *dir == *cwd) { + dir++; + cwd++; + } + if (*dir) + return NULL; + if (*cwd == '/') + return cwd + 1; + return cwd; +} + +int is_inside_dir(const char *dir) +{ + char buffer[PATH_MAX]; + return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL; +} + +int remove_dir_recursively(struct strbuf *path, int only_empty) +{ + DIR *dir = opendir(path->buf); + struct dirent *e; + int ret = 0, original_len = path->len, len; + + if (!dir) + return -1; + if (path->buf[original_len - 1] != '/') + strbuf_addch(path, '/'); + + 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 ".." */ + + strbuf_setlen(path, len); + strbuf_addstr(path, e->d_name); + if (lstat(path->buf, &st)) + ; /* fall thru */ + else if (S_ISDIR(st.st_mode)) { + if (!remove_dir_recursively(path, only_empty)) + continue; /* happy */ + } else if (!only_empty && !unlink(path->buf)) + continue; /* happy, too */ + + /* path too long, stat fails, or non-directory still exists */ + ret = -1; + break; + } + closedir(dir); + + strbuf_setlen(path, original_len); + if (!ret) + ret = rmdir(path->buf); + return ret; +}