diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 4e81670fc..66263c300 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -679,7 +679,7 @@ class wildcard_expander_t { } // Helper to resolve using our prefix. - dir_iter_t open_dir(const wcstring &base_dir) const { + dir_iter_t open_dir(const wcstring &base_dir, bool dotdot = false) const { wcstring path = this->working_directory; append_path_component(path, base_dir); if (flags & expand_flag::special_for_cd) { @@ -687,7 +687,7 @@ class wildcard_expander_t { // for example, cd ../ should complete "without resolving symlinks". path = normalize_path(path); } - return dir_iter_t(path); + return dir_iter_t(path, dotdot); } public: @@ -952,7 +952,8 @@ void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc, } } - dir_iter_t dir = open_dir(base_dir); + // return "." and ".." entries if we're doing completions + dir_iter_t dir = open_dir(base_dir, /* return . and .. */ flags & expand_flag::for_completions); if (dir.valid()) { if (is_last_segment) { // Last wildcard segment, nonempty wildcard. diff --git a/src/wutil.cpp b/src/wutil.cpp index f843e3c12..b1fa99d07 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -172,12 +172,13 @@ void dir_iter_t::entry_t::do_stat() const { } } -dir_iter_t::dir_iter_t(const wcstring &path) { +dir_iter_t::dir_iter_t(const wcstring &path, bool withdot) { dir_.reset(wopendir(path)); if (!dir_) { error_ = errno; return; } + withdot_ = withdot; entry_.dirfd_ = dirfd(&*dir_); } @@ -211,8 +212,9 @@ const dir_iter_t::entry_t *dir_iter_t::next() { error_ = errno; return nullptr; } - // Skip . and .. - if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) { + // Skip . and .., + // unless we've been told not to. + if (!withdot_ && (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))) { return next(); } entry_.reset(); diff --git a/src/wutil.h b/src/wutil.h index 378dbd5d4..a0565faee 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -171,12 +171,15 @@ enum class dir_entry_type_t : uint8_t { /// symlink, or if the caller asks for the stat buffer. /// Symlinks are followed. class dir_iter_t : noncopyable_t { + private: + /// Whether this dir_iter considers the "." and ".." filesystem entries. + bool withdot_{false}; public: struct entry_t; /// Open a directory at a given path. On failure, \p error() will return the error code. /// Note opendir is guaranteed to set close-on-exec by POSIX (hooray). - explicit dir_iter_t(const wcstring &path); + explicit dir_iter_t(const wcstring &path, bool withdot = false); /// Advance this iterator. /// \return a pointer to the entry, or nullptr if the entry is finished, or an error occurred. diff --git a/tests/checks/cd.fish b/tests/checks/cd.fish index 3ef82c0fa..98a7757df 100644 --- a/tests/checks/cd.fish +++ b/tests/checks/cd.fish @@ -267,3 +267,7 @@ begin # CHECK: $PWD is absolute cd ../../.. end + +complete -C'cd .' +# CHECK: ../ +# CHECK: ./