Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Source / kwsys / Directory.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
3 #include "kwsysPrivate.h"
4 #include KWSYS_HEADER(Directory.hxx)
5
6 #include KWSYS_HEADER(Configure.hxx)
7
8 #include KWSYS_HEADER(Encoding.hxx)
9
10 #include KWSYS_HEADER(SystemTools.hxx)
11
12 // Work-around CMake dependency scanning limitation.  This must
13 // duplicate the above list of headers.
14 #if 0
15 #  include "Configure.hxx.in"
16 #  include "Directory.hxx.in"
17 #  include "Encoding.hxx.in"
18 #endif
19
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #if defined(_WIN32) && !defined(__CYGWIN__)
25 #  include <windows.h>
26
27 #  include <ctype.h>
28 #  include <fcntl.h>
29 #  include <io.h>
30 #  include <stdio.h>
31 #  include <stdlib.h>
32 #  include <string.h>
33 #  include <sys/stat.h>
34 #  include <sys/types.h>
35 #endif
36
37 namespace KWSYS_NAMESPACE {
38
39 class DirectoryInternals
40 {
41 public:
42   struct FileData
43   {
44     std::string Name;
45 #if defined(_WIN32) && !defined(__CYGWIN__)
46     WIN32_FIND_DATAW FindData;
47 #endif
48     FileData(std::string name
49 #if defined(_WIN32) && !defined(__CYGWIN__)
50              ,
51              WIN32_FIND_DATAW data
52 #endif
53              )
54       : Name(std::move(name))
55 #if defined(_WIN32) && !defined(__CYGWIN__)
56       , FindData(std::move(data))
57 #endif
58     {
59     }
60   };
61   // Array of Files
62   std::vector<FileData> Files;
63
64   // Path to Open'ed directory
65   std::string Path;
66 };
67
68 Directory::Directory()
69 {
70   this->Internal = new DirectoryInternals;
71 }
72
73 Directory::Directory(Directory&& other)
74 {
75   this->Internal = other.Internal;
76   other.Internal = nullptr;
77 }
78
79 Directory& Directory::operator=(Directory&& other)
80 {
81   std::swap(this->Internal, other.Internal);
82   return *this;
83 }
84
85 Directory::~Directory()
86 {
87   delete this->Internal;
88 }
89
90 unsigned long Directory::GetNumberOfFiles() const
91 {
92   return static_cast<unsigned long>(this->Internal->Files.size());
93 }
94
95 const char* Directory::GetFile(unsigned long dindex) const
96 {
97   return this->Internal->Files[dindex].Name.c_str();
98 }
99
100 std::string const& Directory::GetFileName(std::size_t i) const
101 {
102   return this->Internal->Files[i].Name;
103 }
104
105 std::string Directory::GetFilePath(std::size_t i) const
106 {
107   std::string abs = this->Internal->Path;
108   if (!abs.empty() && abs.back() != '/') {
109     abs += '/';
110   }
111   abs += this->Internal->Files[i].Name;
112   return abs;
113 }
114
115 bool Directory::FileIsDirectory(std::size_t i) const
116 {
117 #if defined(_WIN32) && !defined(__CYGWIN__)
118   auto const& data = this->Internal->Files[i].FindData;
119   return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
120 #else
121   std::string const& path = this->GetFilePath(i);
122   return kwsys::SystemTools::FileIsDirectory(path);
123 #endif
124 }
125
126 bool Directory::FileIsSymlink(std::size_t i) const
127 {
128   std::string const& path = this->GetFilePath(i);
129 #if defined(_WIN32) && !defined(__CYGWIN__)
130   auto const& data = this->Internal->Files[i].FindData;
131   return kwsys::SystemTools::FileIsSymlinkWithAttr(
132     Encoding::ToWindowsExtendedPath(path), data.dwFileAttributes);
133 #else
134   return kwsys::SystemTools::FileIsSymlink(path);
135 #endif
136 }
137
138 const char* Directory::GetPath() const
139 {
140   return this->Internal->Path.c_str();
141 }
142
143 void Directory::Clear()
144 {
145   this->Internal->Path.resize(0);
146   this->Internal->Files.clear();
147 }
148
149 } // namespace KWSYS_NAMESPACE
150
151 // First Windows platforms
152
153 #if defined(_WIN32) && !defined(__CYGWIN__)
154
155 namespace KWSYS_NAMESPACE {
156
157 Status Directory::Load(std::string const& name, std::string* errorMessage)
158 {
159   this->Clear();
160   HANDLE srchHandle;
161   char* buf;
162   size_t bufLength;
163   size_t n = name.size();
164   if (name.back() == '/' || name.back() == '\\') {
165     bufLength = n + 1 + 1;
166     buf = new char[bufLength];
167     snprintf(buf, bufLength, "%s*", name.c_str());
168   } else {
169     // Make sure the slashes in the wildcard suffix are consistent with the
170     // rest of the path
171     bufLength = n + 2 + 1;
172     buf = new char[bufLength];
173     if (name.find('\\') != std::string::npos) {
174       snprintf(buf, bufLength, "%s\\*", name.c_str());
175     } else {
176       snprintf(buf, bufLength, "%s/*", name.c_str());
177     }
178   }
179   WIN32_FIND_DATAW data; // data of current file
180
181   // Now put them into the file array
182   srchHandle =
183     FindFirstFileW(Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
184   delete[] buf;
185
186   if (srchHandle == INVALID_HANDLE_VALUE) {
187     Status status = Status::POSIX_errno();
188     if (errorMessage) {
189       *errorMessage = status.GetString();
190     }
191     return status;
192   }
193
194   // Loop through names
195   do {
196     this->Internal->Files.emplace_back(Encoding::ToNarrow(data.cFileName),
197                                        data);
198   } while (FindNextFileW(srchHandle, &data));
199   this->Internal->Path = name;
200   if (!FindClose(srchHandle)) {
201     Status status = Status::POSIX_errno();
202     if (errorMessage) {
203       *errorMessage = status.GetString();
204     }
205     return status;
206   }
207   return Status::Success();
208 }
209
210 unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
211                                                      std::string* errorMessage)
212 {
213   HANDLE srchHandle;
214   char* buf;
215   size_t bufLength;
216   size_t n = name.size();
217   if (name.back() == '/') {
218     bufLength = n + 1 + 1;
219     buf = new char[n + 1 + 1];
220     snprintf(buf, bufLength, "%s*", name.c_str());
221   } else {
222     bufLength = n + 2 + 1;
223     buf = new char[n + 2 + 1];
224     snprintf(buf, bufLength, "%s/*", name.c_str());
225   }
226   WIN32_FIND_DATAW data; // data of current file
227
228   // Now put them into the file array
229   srchHandle = FindFirstFileW(Encoding::ToWide(buf).c_str(), &data);
230   delete[] buf;
231
232   if (srchHandle == INVALID_HANDLE_VALUE) {
233     if (errorMessage) {
234       if (unsigned int errorId = GetLastError()) {
235         LPSTR message = nullptr;
236         DWORD size = FormatMessageA(
237           FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
238             FORMAT_MESSAGE_IGNORE_INSERTS,
239           nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
240           (LPSTR)&message, 0, nullptr);
241         *errorMessage = std::string(message, size);
242         LocalFree(message);
243       } else {
244         *errorMessage = "Unknown error.";
245       }
246     }
247     return 0;
248   }
249
250   // Loop through names
251   unsigned long count = 0;
252   do {
253     count++;
254   } while (FindNextFileW(srchHandle, &data));
255   FindClose(srchHandle);
256   return count;
257 }
258
259 } // namespace KWSYS_NAMESPACE
260
261 #else
262
263 // Now the POSIX style directory access
264
265 #  include <sys/types.h>
266
267 #  include <dirent.h>
268 #  include <errno.h>
269 #  include <string.h>
270
271 // PGI with glibc has trouble with dirent and large file support:
272 //  http://www.pgroup.com/userforum/viewtopic.php?
273 //  p=1992&sid=f16167f51964f1a68fe5041b8eb213b6
274 // Work around the problem by mapping dirent the same way as readdir.
275 #  if defined(__PGI) && defined(__GLIBC__)
276 #    define kwsys_dirent_readdir dirent
277 #    define kwsys_dirent_readdir64 dirent64
278 #    define kwsys_dirent kwsys_dirent_lookup(readdir)
279 #    define kwsys_dirent_lookup(x) kwsys_dirent_lookup_delay(x)
280 #    define kwsys_dirent_lookup_delay(x) kwsys_dirent_##x
281 #  else
282 #    define kwsys_dirent dirent
283 #  endif
284
285 namespace KWSYS_NAMESPACE {
286
287 Status Directory::Load(std::string const& name, std::string* errorMessage)
288 {
289   this->Clear();
290
291   errno = 0;
292   DIR* dir = opendir(name.c_str());
293
294   if (!dir) {
295     if (errorMessage != nullptr) {
296       *errorMessage = std::string(strerror(errno));
297     }
298     return Status::POSIX_errno();
299   }
300
301   errno = 0;
302   for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) {
303     this->Internal->Files.emplace_back(d->d_name);
304   }
305   if (errno != 0) {
306     if (errorMessage != nullptr) {
307       *errorMessage = std::string(strerror(errno));
308     }
309     return Status::POSIX_errno();
310   }
311
312   this->Internal->Path = name;
313   closedir(dir);
314   return Status::Success();
315 }
316
317 unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
318                                                      std::string* errorMessage)
319 {
320   errno = 0;
321   DIR* dir = opendir(name.c_str());
322
323   if (!dir) {
324     if (errorMessage != nullptr) {
325       *errorMessage = std::string(strerror(errno));
326     }
327     return 0;
328   }
329
330   unsigned long count = 0;
331   for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) {
332     count++;
333   }
334   if (errno != 0) {
335     if (errorMessage != nullptr) {
336       *errorMessage = std::string(strerror(errno));
337     }
338     return false;
339   }
340
341   closedir(dir);
342   return count;
343 }
344
345 } // namespace KWSYS_NAMESPACE
346
347 #endif