Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/kwsys/Directory.cxx
Line
Count
Source
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
3.79M
      : Name(std::move(name))
55
#if defined(_WIN32) && !defined(__CYGWIN__)
56
      , FindData(std::move(data))
57
#endif
58
3.79M
    {
59
3.79M
    }
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
63.0k
{
70
63.0k
  this->Internal = new DirectoryInternals;
71
63.0k
}
72
73
Directory::Directory(Directory&& other)
74
0
{
75
0
  this->Internal = other.Internal;
76
0
  other.Internal = nullptr;
77
0
}
78
79
Directory& Directory::operator=(Directory&& other)
80
0
{
81
0
  std::swap(this->Internal, other.Internal);
82
0
  return *this;
83
0
}
84
85
Directory::~Directory()
86
63.0k
{
87
63.0k
  delete this->Internal;
88
63.0k
}
89
90
unsigned long Directory::GetNumberOfFiles() const
91
157k
{
92
157k
  return static_cast<unsigned long>(this->Internal->Files.size());
93
157k
}
94
95
char const* Directory::GetFile(unsigned long dindex) const
96
0
{
97
0
  return this->Internal->Files[dindex].Name.c_str();
98
0
}
99
100
std::string const& Directory::GetFileName(std::size_t i) const
101
141k
{
102
141k
  return this->Internal->Files[i].Name;
103
141k
}
104
105
std::string Directory::GetFilePath(std::size_t i) const
106
0
{
107
0
  std::string abs = this->Internal->Path;
108
0
  if (!abs.empty() && abs.back() != '/') {
109
0
    abs += '/';
110
0
  }
111
0
  abs += this->Internal->Files[i].Name;
112
0
  return abs;
113
0
}
114
115
bool Directory::FileIsDirectory(std::size_t i) const
116
0
{
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
0
  std::string const& path = this->GetFilePath(i);
122
0
  return kwsys::SystemTools::FileIsDirectory(path);
123
0
#endif
124
0
}
125
126
bool Directory::FileIsSymlink(std::size_t i) const
127
0
{
128
0
  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
0
  return kwsys::SystemTools::FileIsSymlink(path);
135
0
#endif
136
0
}
137
138
char const* Directory::GetPath() const
139
0
{
140
0
  return this->Internal->Path.c_str();
141
0
}
142
143
void Directory::Clear()
144
63.0k
{
145
63.0k
  this->Internal->Path.resize(0);
146
63.0k
  this->Internal->Files.clear();
147
63.0k
}
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::Windows_GetLastError();
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::Windows_GetLastError();
202
    if (errorMessage) {
203
      *errorMessage = status.GetString();
204
    }
205
    return status;
206
  }
207
  return Status::Success();
208
}
209
210
unsigned long Directory::GetNumberOfFilesInDirectory(std::string const& 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
//  https://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
54.9k
#    define kwsys_dirent dirent
283
#  endif
284
285
namespace KWSYS_NAMESPACE {
286
287
Status Directory::Load(std::string const& name, std::string* errorMessage)
288
63.0k
{
289
63.0k
  this->Clear();
290
291
63.0k
  errno = 0;
292
63.0k
  DIR* dir = opendir(name.c_str());
293
294
63.0k
  if (!dir) {
295
8.04k
    if (errorMessage) {
296
0
      *errorMessage = std::string(strerror(errno));
297
0
    }
298
8.04k
    return Status::POSIX_errno();
299
8.04k
  }
300
301
63.0k
  errno = 0;
302
3.85M
  for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) {
303
3.79M
    this->Internal->Files.emplace_back(d->d_name);
304
3.79M
  }
305
54.9k
  if (errno != 0) {
306
0
    if (errorMessage) {
307
0
      *errorMessage = std::string(strerror(errno));
308
0
    }
309
0
    return Status::POSIX_errno();
310
0
  }
311
312
54.9k
  this->Internal->Path = name;
313
54.9k
  closedir(dir);
314
54.9k
  return Status::Success();
315
54.9k
}
316
317
unsigned long Directory::GetNumberOfFilesInDirectory(std::string const& name,
318
                                                     std::string* errorMessage)
319
0
{
320
0
  errno = 0;
321
0
  DIR* dir = opendir(name.c_str());
322
323
0
  if (!dir) {
324
0
    if (errorMessage) {
325
0
      *errorMessage = std::string(strerror(errno));
326
0
    }
327
0
    return 0;
328
0
  }
329
330
0
  unsigned long count = 0;
331
0
  for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) {
332
0
    count++;
333
0
  }
334
0
  if (errno != 0) {
335
0
    if (errorMessage) {
336
0
      *errorMessage = std::string(strerror(errno));
337
0
    }
338
0
    return false;
339
0
  }
340
341
0
  closedir(dir);
342
0
  return count;
343
0
}
344
345
} // namespace KWSYS_NAMESPACE
346
347
#endif