// Prototypes for wide character equivalents of various standard unix functions. #ifndef FISH_WUTIL_H #define FISH_WUTIL_H #include "config.h" // IWYU pragma: keep #include #include #include #include #include #ifdef __APPLE__ // This include is required on macOS 10.10 for locale_t #include #endif #include #include #include #include #include #include #include "common.h" #include "maybe.h" class autoclose_fd_t; /// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by /// POSIX (hooray). DIR *wopendir(const wcstring &name); /// Wide character version of stat(). int wstat(const wcstring &file_name, struct stat *buf); /// Wide character version of lstat(). int lwstat(const wcstring &file_name, struct stat *buf); /// Wide character version of access(). int waccess(const wcstring &file_name, int mode); /// Wide character version of unlink(). int wunlink(const wcstring &file_name); /// Wide character version of perror(). void wperror(const wchar_t *s); /// Wide character version of getcwd(). wcstring wgetcwd(); /// Wide character version of readlink(). maybe_t wreadlink(const wcstring &file_name); /// Wide character version of realpath function. /// \returns the canonicalized path, or none if the path is invalid. maybe_t wrealpath(const wcstring &pathname); /// Given an input path, "normalize" it: /// 1. Collapse multiple /s into a single /, except maybe at the beginning. /// 2. .. goes up a level. /// 3. Remove /./ in the middle. wcstring normalize_path(const wcstring &path, bool allow_leading_double_slashes = true); /// Given an input path \p path and a working directory \p wd, do a "normalizing join" in a way /// appropriate for cd. That is, return effectively wd + path while resolving leading ../s from /// path. The intent here is to allow 'cd' out of a directory which may no longer exist, without /// allowing 'cd' into a directory that may not exist; see #5341. wcstring path_normalize_for_cd(const wcstring &wd, const wcstring &path); /// Wide character version of dirname(). std::wstring wdirname(std::wstring path); /// Wide character version of basename(). std::wstring wbasename(std::wstring path); /// Wide character wrapper around the gettext function. For historic reasons, unlike the real /// gettext function, wgettext takes care of setting the correct domain, etc. using the textdomain /// and bindtextdomain functions. This should probably be moved out of wgettext, so that wgettext /// will be nothing more than a wrapper around gettext, like all other functions in this file. const wcstring &wgettext(const wchar_t *in); /// Wide character version of mkdir. int wmkdir(const wcstring &name, int mode); /// Wide character version of rename. int wrename(const wcstring &oldName, const wcstring &newv); /// Write a wide string to a file descriptor. This avoids doing any additional allocation. /// This does NOT retry on EINTR or EAGAIN, it simply returns. /// \return -1 on error in which case errno will have been set. In this event, the number of bytes /// actually written cannot be obtained. ssize_t wwrite_to_fd(const wchar_t *input, size_t len, int fd); /// Variant of above that accepts a wcstring. inline ssize_t wwrite_to_fd(const wcstring &s, int fd) { return wwrite_to_fd(s.c_str(), s.size(), fd); } // We need this because there are too many implementations that don't return the proper answer for // some code points. See issue #3050. #ifndef FISH_NO_ISW_WRAPPERS #define iswalnum fish_iswalnum #define iswgraph fish_iswgraph #endif int fish_iswalnum(wint_t wc); int fish_iswgraph(wint_t wc); int fish_wcswidth(const wchar_t *str); int fish_wcswidth(const wcstring &str); // returns an immortal locale_t corresponding to the C locale. locale_t fish_c_locale(); void fish_invalidate_numeric_locale(); locale_t fish_numeric_locale(); int fish_wcstoi(const wchar_t *str, const wchar_t **endptr = nullptr, int base = 10); long fish_wcstol(const wchar_t *str, const wchar_t **endptr = nullptr, int base = 10); long long fish_wcstoll(const wchar_t *str, const wchar_t **endptr = nullptr, int base = 10); unsigned long long fish_wcstoull(const wchar_t *str, const wchar_t **endptr = nullptr, int base = 10); double fish_wcstod(const wchar_t *str, wchar_t **endptr, size_t len); double fish_wcstod(const wchar_t *str, wchar_t **endptr); double fish_wcstod(const wcstring &str, wchar_t **endptr); double fish_wcstod_underscores(const wchar_t *str, wchar_t **endptr); /// Class for representing a file's inode. We use this to detect and avoid symlink loops, among /// other things. While an inode / dev pair is sufficient to distinguish co-existing files, Linux /// seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA /// problem). Therefore we include richer information. struct file_id_t { dev_t device{static_cast(-1LL)}; ino_t inode{static_cast(-1LL)}; uint64_t size{static_cast(-1LL)}; time_t change_seconds{std::numeric_limits::min()}; long change_nanoseconds{-1}; time_t mod_seconds{std::numeric_limits::min()}; long mod_nanoseconds{-1}; constexpr file_id_t() = default; bool operator==(const file_id_t &rhs) const; bool operator!=(const file_id_t &rhs) const; // Used to permit these as keys in std::map. bool operator<(const file_id_t &rhs) const; static file_id_t from_stat(const struct stat &buf); bool older_than(const file_id_t &rhs) const; wcstring dump() const; private: int compare_file_id(const file_id_t &rhs) const; }; /// Types of files that may be in a directory. enum class dir_entry_type_t : uint8_t { fifo = 1, // FIFO file chr, // character device dir, // directory blk, // block device reg, // regular file lnk, // symlink sock, // socket whiteout, // whiteout (from BSD) }; /// Class for iterating over a directory, wrapping readdir(). /// This allows enumerating the contents of a directory, exposing the file type if the filesystem /// itself exposes that from readdir(). stat() is incurred only if necessary: if the entry is a /// symlink, or if the caller asks for the stat buffer. /// Symlinks are followed. class dir_iter_t : noncopyable_t { 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); /// Advance this iterator. /// \return a pointer to the entry, or nullptr if the entry is finished, or an error occurred. /// The returned pointer is only valid until the next call to next(). const entry_t *next(); /// \return the errno value for the last error, or 0 if none. int error() const { return error_; } /// \return if we are valid: successfully opened a directory. bool valid() const { return dir_ != nullptr; } /// \return the underlying file descriptor, or -1 if invalid. int fd() const { return dir_ ? dirfd(dir_) : -1; } /// Rewind the directory to the beginning. void rewind(); ~dir_iter_t(); dir_iter_t(dir_iter_t &&); dir_iter_t &operator=(dir_iter_t &&); /// An entry returned by dir_iter_t. struct entry_t : noncopyable_t { /// File name of this entry. wcstring name{}; /// inode of this entry. ino_t inode{}; /// \return the type of this entry if it is already available, otherwise none(). maybe_t fast_type() const { return type_; } /// \return the type of this entry, falling back to stat() if necessary. /// If stat() fails because the file has disappeared, this will return none(). /// If stat() fails because of a broken symlink, this will return type lnk. maybe_t check_type() const; /// \return whether this is a directory. This may call stat(). bool is_dir() const { return check_type() == dir_entry_type_t::dir; } /// \return the stat buff for this entry, invoking stat() if necessary. const maybe_t &stat() const; private: // Reset our fields. void reset(); // Populate our stat buffer, and type. Errors are silently ignored. void do_stat() const; // Stat buff for this entry, or none if not yet computed. mutable maybe_t stat_{}; // The type of the entry. This is initially none; it may be populated eagerly via readdir() // on some filesystems, or later via stat(). If stat() fails, the error is silently ignored // and the type is left as none(). Note this is an unavoidable race. mutable maybe_t type_{}; // fd of the DIR*, used for fstatat(). int dirfd_{-1}; entry_t(); ~entry_t(); entry_t(entry_t &&) = default; entry_t &operator=(entry_t &&) = default; friend class dir_iter_t; }; private: DIR *dir_{nullptr}; int error_{0}; entry_t entry_; }; #ifndef HASH_FILE_ID #define HASH_FILE_ID 1 namespace std { template <> struct hash { size_t operator()(const file_id_t &f) const { std::hash hasher1; std::hash hasher2; return hasher1(f.device) ^ hasher2(f.inode); } }; } // namespace std #endif file_id_t file_id_for_fd(int fd); file_id_t file_id_for_fd(const autoclose_fd_t &fd); file_id_t file_id_for_path(const wcstring &path); file_id_t file_id_for_path(const std::string &path); extern const file_id_t kInvalidFileID; #endif