1 // Filesystem directory iterator utilities -*- C++ -*- 2 3 // Copyright (C) 2014-2019 Free Software Foundation, Inc. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the 7 // terms of the GNU General Public License as published by the 8 // Free Software Foundation; either version 3, or (at your option) 9 // any later version. 10 11 // This library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 16 // Under Section 7 of GPL version 3, you are granted additional 17 // permissions described in the GCC Runtime Library Exception, version 18 // 3.1, as published by the Free Software Foundation. 19 20 // You should have received a copy of the GNU General Public License and 21 // a copy of the GCC Runtime Library Exception along with this program; 22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 // <http://www.gnu.org/licenses/>. 24 25 #ifndef _GLIBCXX_DIR_COMMON_H 26 #define _GLIBCXX_DIR_COMMON_H 1 27 28 #include <string.h> // strcmp 29 #include <errno.h> 30 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 31 #include <wchar.h> // wcscmp 32 #endif 33 #ifdef _GLIBCXX_HAVE_DIRENT_H 34 # ifdef _GLIBCXX_HAVE_SYS_TYPES_H 35 # include <sys/types.h> 36 # endif 37 # include <dirent.h> 38 #endif 39 40 namespace std _GLIBCXX_VISIBILITY(default) 41 { 42 _GLIBCXX_BEGIN_NAMESPACE_VERSION 43 namespace filesystem 44 { 45 namespace __gnu_posix 46 { 47 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 48 // Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*. 49 using char_type = wchar_t; 50 using DIR = ::_WDIR; 51 using dirent = _wdirent; 52 inline DIR* opendir(const wchar_t* path) { return ::_wopendir(path); } 53 inline dirent* readdir(DIR* dir) { return ::_wreaddir(dir); } 54 inline int closedir(DIR* dir) { return ::_wclosedir(dir); } 55 #elif defined _GLIBCXX_HAVE_DIRENT_H 56 using char_type = char; 57 using DIR = ::DIR; 58 typedef struct ::dirent dirent; 59 using ::opendir; 60 using ::readdir; 61 using ::closedir; 62 #else 63 using char_type = char; 64 struct dirent { const char* d_name; }; 65 struct DIR { }; 66 inline DIR* opendir(const char*) { return nullptr; } 67 inline dirent* readdir(DIR*) { return nullptr; } 68 inline int closedir(DIR*) { return -1; } 69 #endif 70 } // namespace __gnu_posix 71 72 namespace posix = __gnu_posix; 73 74 struct _Dir_base 75 { 76 _Dir_base(posix::DIR* dirp = nullptr) : dirp(dirp) { } 77 78 // If no error occurs then dirp is non-null, 79 // otherwise null (whether error ignored or not). 80 _Dir_base(const posix::char_type* pathname, bool skip_permission_denied, 81 error_code& ec) noexcept 82 : dirp(posix::opendir(pathname)) 83 { 84 if (dirp) 85 ec.clear(); 86 else 87 { 88 const int err = errno; 89 if (err == EACCES && skip_permission_denied) 90 ec.clear(); 91 else 92 ec.assign(err, std::generic_category()); 93 } 94 } 95 96 _Dir_base(_Dir_base&& d) : dirp(std::exchange(d.dirp, nullptr)) { } 97 98 _Dir_base& operator=(_Dir_base&&) = delete; 99 100 ~_Dir_base() { if (dirp) posix::closedir(dirp); } 101 102 const posix::dirent* 103 advance(bool skip_permission_denied, error_code& ec) noexcept 104 { 105 ec.clear(); 106 107 int err = std::exchange(errno, 0); 108 const posix::dirent* entp = posix::readdir(dirp); 109 // std::swap cannot be used with Bionic's errno 110 err = std::exchange(errno, err); 111 112 if (entp) 113 { 114 // skip past dot and dot-dot 115 if (is_dot_or_dotdot(entp->d_name)) 116 return advance(skip_permission_denied, ec); 117 return entp; 118 } 119 else if (err) 120 { 121 if (err == EACCES && skip_permission_denied) 122 return nullptr; 123 ec.assign(err, std::generic_category()); 124 return nullptr; 125 } 126 else 127 { 128 // reached the end 129 return nullptr; 130 } 131 } 132 133 static bool is_dot_or_dotdot(const char* s) noexcept 134 { return !strcmp(s, ".") || !strcmp(s, ".."); } 135 136 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS 137 static bool is_dot_or_dotdot(const wchar_t* s) noexcept 138 { return !wcscmp(s, L".") || !wcscmp(s, L".."); } 139 #endif 140 141 posix::DIR* dirp; 142 }; 143 144 } // namespace filesystem 145 146 // BEGIN/END macros must be defined before including this file. 147 _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM 148 149 inline file_type 150 get_file_type(const std::filesystem::__gnu_posix::dirent& d [[gnu::unused]]) 151 { 152 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE 153 switch (d.d_type) 154 { 155 case DT_BLK: 156 return file_type::block; 157 case DT_CHR: 158 return file_type::character; 159 case DT_DIR: 160 return file_type::directory; 161 case DT_FIFO: 162 return file_type::fifo; 163 case DT_LNK: 164 return file_type::symlink; 165 case DT_REG: 166 return file_type::regular; 167 case DT_SOCK: 168 return file_type::socket; 169 case DT_UNKNOWN: 170 return file_type::unknown; 171 default: 172 return file_type::none; 173 } 174 #else 175 return file_type::none; 176 #endif 177 } 178 _GLIBCXX_END_NAMESPACE_FILESYSTEM 179 180 _GLIBCXX_END_NAMESPACE_VERSION 181 } // namespace std 182 183 #endif // _GLIBCXX_DIR_COMMON_H 184