d520c1440425695776256b186ea6612f6a9d4115
[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     _wfinddata_t FindData;
47 #endif
48     FileData(std::string name
49 #if defined(_WIN32) && !defined(__CYGWIN__)
50              ,
51              _wfinddata_t 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   _wfinddata_t const& data = this->Internal->Files[i].FindData;
119   return (data.attrib & 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   _wfinddata_t const& data = this->Internal->Files[i].FindData;
131   return kwsys::SystemTools::FileIsSymlinkWithAttr(
132     Encoding::ToWindowsExtendedPath(path), data.attrib);
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   intptr_t 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   struct _wfinddata_t data; // data of current file
180
181   // Now put them into the file array
182   srchHandle =
183     _wfindfirst((wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
184   delete[] buf;
185
186   if (srchHandle == -1) {
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.name), data);
197   } while (_wfindnext(srchHandle, &data) != -1);
198   this->Internal->Path = name;
199   if (_findclose(srchHandle) == -1) {
200     Status status = Status::POSIX_errno();
201     if (errorMessage) {
202       *errorMessage = status.GetString();
203     }
204     return status;
205   }
206   return Status::Success();
207 }
208
209 unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
210                                                      std::string* errorMessage)
211 {
212   intptr_t srchHandle;
213   char* buf;
214   size_t bufLength;
215   size_t n = name.size();
216   if (name.back() == '/') {
217     bufLength = n + 1 + 1;
218     buf = new char[n + 1 + 1];
219     snprintf(buf, bufLength, "%s*", name.c_str());
220   } else {
221     bufLength = n + 2 + 1;
222     buf = new char[n + 2 + 1];
223     snprintf(buf, bufLength, "%s/*", name.c_str());
224   }
225   struct _wfinddata_t data; // data of current file
226
227   // Now put them into the file array
228   srchHandle = _wfindfirst((wchar_t*)Encoding::ToWide(buf).c_str(), &data);
229   delete[] buf;
230
231   if (srchHandle == -1) {
232     if (errorMessage) {
233       if (unsigned int errorId = GetLastError()) {
234         LPSTR message = nullptr;
235         DWORD size = FormatMessageA(
236           FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
237             FORMAT_MESSAGE_IGNORE_INSERTS,
238           nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
239           (LPSTR)&message, 0, nullptr);
240         *errorMessage = std::string(message, size);
241         LocalFree(message);
242       } else {
243         *errorMessage = "Unknown error.";
244       }
245     }
246     return 0;
247   }
248
249   // Loop through names
250   unsigned long count = 0;
251   do {
252     count++;
253   } while (_wfindnext(srchHandle, &data) != -1);
254   _findclose(srchHandle);
255   return count;
256 }
257
258 } // namespace KWSYS_NAMESPACE
259
260 #else
261
262 // Now the POSIX style directory access
263
264 #  include <sys/types.h>
265
266 #  include <dirent.h>
267 #  include <errno.h>
268 #  include <string.h>
269
270 // PGI with glibc has trouble with dirent and large file support:
271 //  http://www.pgroup.com/userforum/viewtopic.php?
272 //  p=1992&sid=f16167f51964f1a68fe5041b8eb213b6
273 // Work around the problem by mapping dirent the same way as readdir.
274 #  if defined(__PGI) && defined(__GLIBC__)
275 #    define kwsys_dirent_readdir dirent
276 #    define kwsys_dirent_readdir64 dirent64
277 #    define kwsys_dirent kwsys_dirent_lookup(readdir)
278 #    define kwsys_dirent_lookup(x) kwsys_dirent_lookup_delay(x)
279 #    define kwsys_dirent_lookup_delay(x) kwsys_dirent_##x
280 #  else
281 #    define kwsys_dirent dirent
282 #  endif
283
284 namespace KWSYS_NAMESPACE {
285
286 Status Directory::Load(std::string const& name, std::string* errorMessage)
287 {
288   this->Clear();
289
290   errno = 0;
291   DIR* dir = opendir(name.c_str());
292
293   if (!dir) {
294     if (errorMessage != nullptr) {
295       *errorMessage = std::string(strerror(errno));
296     }
297     return Status::POSIX_errno();
298   }
299
300   errno = 0;
301   for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) {
302     this->Internal->Files.emplace_back(d->d_name);
303   }
304   if (errno != 0) {
305     if (errorMessage != nullptr) {
306       *errorMessage = std::string(strerror(errno));
307     }
308     return Status::POSIX_errno();
309   }
310
311   this->Internal->Path = name;
312   closedir(dir);
313   return Status::Success();
314 }
315
316 unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
317                                                      std::string* errorMessage)
318 {
319   errno = 0;
320   DIR* dir = opendir(name.c_str());
321
322   if (!dir) {
323     if (errorMessage != nullptr) {
324       *errorMessage = std::string(strerror(errno));
325     }
326     return 0;
327   }
328
329   unsigned long count = 0;
330   for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) {
331     count++;
332   }
333   if (errno != 0) {
334     if (errorMessage != nullptr) {
335       *errorMessage = std::string(strerror(errno));
336     }
337     return false;
338   }
339
340   closedir(dir);
341   return count;
342 }
343
344 } // namespace KWSYS_NAMESPACE
345
346 #endif