1 | /* |
2 | * Copyright 2016-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/portability/Dirent.h> |
18 | |
19 | #ifdef _WIN32 |
20 | #include <stdlib.h> |
21 | #include <string> |
22 | |
23 | #include <folly/portability/Windows.h> |
24 | |
25 | struct DIR { |
26 | dirent dir{}; |
27 | HANDLE searchHandle{INVALID_HANDLE_VALUE}; |
28 | int entriesRead{0}; |
29 | char currentName[MAX_PATH * 3]; |
30 | std::string pattern; |
31 | |
32 | int close() { |
33 | return FindClose(searchHandle) ? 0 : -1; |
34 | } |
35 | |
36 | DIR* open() { |
37 | wchar_t patternBuf[MAX_PATH + 3]; |
38 | size_t len; |
39 | |
40 | if (pattern.empty()) { |
41 | return nullptr; |
42 | } |
43 | |
44 | if (mbstowcs_s(&len, patternBuf, MAX_PATH, pattern.c_str(), MAX_PATH - 2)) { |
45 | return nullptr; |
46 | } |
47 | |
48 | // `len` includes the trailing NUL |
49 | if (len) { |
50 | len--; |
51 | } |
52 | if (len && patternBuf[len - 1] != '/' && patternBuf[len - 1] != '\\') { |
53 | patternBuf[len++] = '\\'; |
54 | } |
55 | patternBuf[len++] = '*'; |
56 | patternBuf[len] = 0; |
57 | |
58 | WIN32_FIND_DATAW fdata; |
59 | HANDLE h = FindFirstFileW(patternBuf, &fdata); |
60 | if (h == INVALID_HANDLE_VALUE) { |
61 | return nullptr; |
62 | } |
63 | |
64 | searchHandle = h; |
65 | dir.d_name = currentName; |
66 | if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) { |
67 | return nullptr; |
68 | } |
69 | |
70 | setEntryType(fdata.dwFileAttributes); |
71 | return this; |
72 | } |
73 | |
74 | dirent* nextDir() { |
75 | if (entriesRead) { |
76 | WIN32_FIND_DATAW fdata; |
77 | if (!FindNextFileW(searchHandle, &fdata)) { |
78 | return nullptr; |
79 | } |
80 | |
81 | if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) { |
82 | errno = EBADF; |
83 | return nullptr; |
84 | } |
85 | setEntryType(fdata.dwFileAttributes); |
86 | } |
87 | |
88 | entriesRead++; |
89 | return &dir; |
90 | } |
91 | |
92 | private: |
93 | void setEntryType(DWORD attr) { |
94 | if (attr & FILE_ATTRIBUTE_DIRECTORY) { |
95 | dir.d_type = DT_DIR; |
96 | } else { |
97 | dir.d_type = DT_REG; |
98 | } |
99 | } |
100 | }; |
101 | |
102 | extern "C" { |
103 | int closedir(DIR* dir) { |
104 | auto ret = dir->close(); |
105 | delete dir; |
106 | return ret; |
107 | } |
108 | |
109 | DIR* opendir(const char* name) { |
110 | auto dir = new DIR(); |
111 | dir->pattern = name; |
112 | if (!dir->open()) { |
113 | delete dir; |
114 | return nullptr; |
115 | } |
116 | return dir; |
117 | } |
118 | |
119 | dirent* readdir(DIR* dir) { |
120 | return dir->nextDir(); |
121 | } |
122 | |
123 | int readdir_r(DIR* dir, dirent* buf, dirent** ent) { |
124 | if (!dir || !buf || !ent) { |
125 | return EBADF; |
126 | } |
127 | *ent = dir->nextDir(); |
128 | // Our normal readdir implementation is actually |
129 | // already reentrant, but we need to do this copy |
130 | // in case the caller expects buf to have the value. |
131 | if (*ent) { |
132 | *buf = dir->dir; |
133 | } |
134 | return 0; |
135 | } |
136 | |
137 | void rewinddir(DIR* dir) { |
138 | dir->close(); |
139 | dir->open(); |
140 | } |
141 | } |
142 | #endif |
143 | |