| // Filesystem directory iterator utilities -*- C++ -*- |
| |
| // Copyright (C) 2014-2020 Free Software Foundation, Inc. |
| // |
| // This file is part of the GNU ISO C++ Library. This library is free |
| // software; you can redistribute it and/or modify it under the |
| // terms of the GNU General Public License as published by the |
| // Free Software Foundation; either version 3, or (at your option) |
| // any later version. |
| |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| // Under Section 7 of GPL version 3, you are granted additional |
| // permissions described in the GCC Runtime Library Exception, version |
| // 3.1, as published by the Free Software Foundation. |
| |
| // You should have received a copy of the GNU General Public License and |
| // a copy of the GCC Runtime Library Exception along with this program; |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| // <http://www.gnu.org/licenses/>. |
| |
| #ifndef _GLIBCXX_DIR_COMMON_H |
| #define _GLIBCXX_DIR_COMMON_H 1 |
| |
| #include <string.h> // strcmp |
| #include <errno.h> |
| #if _GLIBCXX_FILESYSTEM_IS_WINDOWS |
| #include <wchar.h> // wcscmp |
| #endif |
| #ifdef _GLIBCXX_HAVE_DIRENT_H |
| # ifdef _GLIBCXX_HAVE_SYS_TYPES_H |
| # include <sys/types.h> |
| # endif |
| # include <dirent.h> |
| #endif |
| |
| namespace std _GLIBCXX_VISIBILITY(default) |
| { |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| namespace filesystem |
| { |
| namespace __gnu_posix |
| { |
| #if _GLIBCXX_FILESYSTEM_IS_WINDOWS |
| // Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*. |
| using char_type = wchar_t; |
| using DIR = ::_WDIR; |
| using dirent = _wdirent; |
| inline DIR* opendir(const wchar_t* path) { return ::_wopendir(path); } |
| inline dirent* readdir(DIR* dir) { return ::_wreaddir(dir); } |
| inline int closedir(DIR* dir) { return ::_wclosedir(dir); } |
| #elif defined _GLIBCXX_HAVE_DIRENT_H |
| using char_type = char; |
| using DIR = ::DIR; |
| typedef struct ::dirent dirent; |
| using ::opendir; |
| using ::readdir; |
| using ::closedir; |
| #else |
| using char_type = char; |
| struct dirent { const char* d_name; }; |
| struct DIR { }; |
| inline DIR* opendir(const char*) { return nullptr; } |
| inline dirent* readdir(DIR*) { return nullptr; } |
| inline int closedir(DIR*) { return -1; } |
| #endif |
| } // namespace __gnu_posix |
| |
| namespace posix = __gnu_posix; |
| |
| struct _Dir_base |
| { |
| _Dir_base(posix::DIR* dirp = nullptr) : dirp(dirp) { } |
| |
| // If no error occurs then dirp is non-null, |
| // otherwise null (whether error ignored or not). |
| _Dir_base(const posix::char_type* pathname, bool skip_permission_denied, |
| error_code& ec) noexcept |
| : dirp(posix::opendir(pathname)) |
| { |
| if (dirp) |
| ec.clear(); |
| else |
| { |
| const int err = errno; |
| if (err == EACCES && skip_permission_denied) |
| ec.clear(); |
| else |
| ec.assign(err, std::generic_category()); |
| } |
| } |
| |
| _Dir_base(_Dir_base&& d) : dirp(std::exchange(d.dirp, nullptr)) { } |
| |
| _Dir_base& operator=(_Dir_base&&) = delete; |
| |
| ~_Dir_base() { if (dirp) posix::closedir(dirp); } |
| |
| const posix::dirent* |
| advance(bool skip_permission_denied, error_code& ec) noexcept |
| { |
| ec.clear(); |
| |
| int err = std::exchange(errno, 0); |
| const posix::dirent* entp = posix::readdir(dirp); |
| // std::swap cannot be used with Bionic's errno |
| err = std::exchange(errno, err); |
| |
| if (entp) |
| { |
| // skip past dot and dot-dot |
| if (is_dot_or_dotdot(entp->d_name)) |
| return advance(skip_permission_denied, ec); |
| return entp; |
| } |
| else if (err) |
| { |
| if (err == EACCES && skip_permission_denied) |
| return nullptr; |
| ec.assign(err, std::generic_category()); |
| return nullptr; |
| } |
| else |
| { |
| // reached the end |
| return nullptr; |
| } |
| } |
| |
| static bool is_dot_or_dotdot(const char* s) noexcept |
| { return !strcmp(s, ".") || !strcmp(s, ".."); } |
| |
| #if _GLIBCXX_FILESYSTEM_IS_WINDOWS |
| static bool is_dot_or_dotdot(const wchar_t* s) noexcept |
| { return !wcscmp(s, L".") || !wcscmp(s, L".."); } |
| #endif |
| |
| posix::DIR* dirp; |
| }; |
| |
| inline bool |
| is_permission_denied_error(int e) |
| { |
| if (e == EACCES) |
| return true; |
| #ifdef __APPLE__ |
| if (e == EPERM) // See PR 99533 |
| return true; |
| #endif |
| return false; |
| } |
| |
| } // namespace filesystem |
| |
| // BEGIN/END macros must be defined before including this file. |
| _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM |
| |
| inline file_type |
| get_file_type(const std::filesystem::__gnu_posix::dirent& d [[gnu::unused]]) |
| { |
| #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE |
| switch (d.d_type) |
| { |
| case DT_BLK: |
| return file_type::block; |
| case DT_CHR: |
| return file_type::character; |
| case DT_DIR: |
| return file_type::directory; |
| case DT_FIFO: |
| return file_type::fifo; |
| case DT_LNK: |
| return file_type::symlink; |
| case DT_REG: |
| return file_type::regular; |
| case DT_SOCK: |
| return file_type::socket; |
| case DT_UNKNOWN: |
| return file_type::unknown; |
| default: |
| return file_type::none; |
| } |
| #else |
| return file_type::none; |
| #endif |
| } |
| _GLIBCXX_END_NAMESPACE_FILESYSTEM |
| |
| _GLIBCXX_END_NAMESPACE_VERSION |
| } // namespace std |
| |
| #endif // _GLIBCXX_DIR_COMMON_H |