1 #include <malloc.h>
2 #include <string.h>
3 #include <errno.h>
4
5 #include "php.h"
6 #include "readdir.h"
7 #include "win32/ioutil.h"
8
9 /**********************************************************************
10 * Implement dirent-style opendir/readdir/rewinddir/closedir on Win32
11 *
12 * Functions defined are opendir(), readdir(), rewinddir() and
13 * closedir() with the same prototypes as the normal dirent.h
14 * implementation.
15 *
16 * Does not implement telldir(), seekdir(), or scandir(). The dirent
17 * struct is compatible with Unix, except that d_ino is always 1 and
18 * d_off is made up as we go along.
19 *
20 * The DIR typedef is not compatible with Unix.
21 **********************************************************************/
22
23 #ifdef __cplusplus
24 extern "C" {
25 #endif
26
27 /* typedef DIR - not the same as Unix */
28 struct DIR_W32 {
29 HANDLE handle; /* _findfirst/_findnext handle */
30 int offset; /* offset into directory */
31 short finished; /* 1 if there are not more files */
32 WIN32_FIND_DATAW fileinfo; /* from _findfirst/_findnext */
33 wchar_t *dirw; /* the dir we are reading */
34 struct dirent dent; /* the dirent to return */
35 };
36
opendir(const char * dir)37 DIR *opendir(const char *dir)
38 {
39 DIR *dp;
40 wchar_t *filespecw, *resolvedw;
41 HANDLE handle;
42 char resolved_path_buff[MAXPATHLEN];
43 size_t resolvedw_len, filespecw_len, index;
44 zend_bool might_need_prefix;
45
46 if (!VCWD_REALPATH(dir, resolved_path_buff)) {
47 return NULL;
48 }
49
50 dp = (DIR *) malloc(sizeof(DIR));
51 if (dp == NULL) {
52 return NULL;
53 }
54
55 resolvedw = php_win32_ioutil_conv_any_to_w(resolved_path_buff, PHP_WIN32_CP_IGNORE_LEN, &resolvedw_len);
56 if (!resolvedw) {
57 free(dp);
58 return NULL;
59 }
60
61 might_need_prefix = resolvedw_len >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(resolvedw[0]) && L':' == resolvedw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(resolvedw[2]);
62
63 filespecw_len = resolvedw_len + 2;
64 if (filespecw_len >= _MAX_PATH && might_need_prefix) {
65 filespecw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
66 }
67 filespecw = (wchar_t *)malloc((filespecw_len + 1)*sizeof(wchar_t));
68 if (filespecw == NULL) {
69 free(dp);
70 free(resolvedw);
71 return NULL;
72 }
73
74 if (filespecw_len >= _MAX_PATH && might_need_prefix) {
75 wcscpy(filespecw, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW);
76 wcscpy(filespecw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, resolvedw);
77 index = resolvedw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW - 1;
78 } else {
79 wcscpy(filespecw, resolvedw);
80 index = resolvedw_len - 1;
81 }
82 if (index >= 0 && filespecw[index] == L'/' || index == 0 && filespecw[index] == L'\\')
83 filespecw[index] = L'\0';
84 wcscat(filespecw, L"\\*");
85
86 if ((handle = FindFirstFileW(filespecw, &(dp->fileinfo))) == INVALID_HANDLE_VALUE) {
87 DWORD err = GetLastError();
88 if (err == ERROR_NO_MORE_FILES || err == ERROR_FILE_NOT_FOUND) {
89 dp->finished = 1;
90 } else {
91 free(dp);
92 free(filespecw);
93 free(resolvedw);
94 return NULL;
95 }
96 }
97 dp->dirw = _wcsdup(resolvedw);
98 dp->handle = handle;
99 dp->offset = 0;
100 dp->finished = 0;
101
102 free(filespecw);
103 free(resolvedw);
104
105 return dp;
106 }
107
readdir(DIR * dp)108 struct dirent *readdir(DIR *dp)
109 {
110 char *_tmp;
111
112 if (!dp || dp->finished)
113 return NULL;
114
115 if (dp->offset != 0) {
116 if (FindNextFileW(dp->handle, &(dp->fileinfo)) == 0) {
117 dp->finished = 1;
118 return NULL;
119 }
120 }
121
122 _tmp = php_win32_ioutil_w_to_any(dp->fileinfo.cFileName);
123 if (!_tmp) {
124 /* wide to utf8 failed, should never happen. */
125 return NULL;
126 }
127 strlcpy(dp->dent.d_name, _tmp, _MAX_FNAME+1);
128 dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
129 free(_tmp);
130
131 dp->offset++;
132
133 dp->dent.d_ino = 1;
134 dp->dent.d_off = dp->offset;
135
136 return &(dp->dent);
137 }
138
readdir_r(DIR * dp,struct dirent * entry,struct dirent ** result)139 int readdir_r(DIR *dp, struct dirent *entry, struct dirent **result)
140 {
141 char *_tmp;
142
143 if (!dp || dp->finished) {
144 *result = NULL;
145 return 0;
146 }
147
148 if (dp->offset != 0) {
149 if (FindNextFileW(dp->handle, &(dp->fileinfo)) == 0) {
150 dp->finished = 1;
151 *result = NULL;
152 return 0;
153 }
154 }
155
156 _tmp = php_win32_ioutil_w_to_any(dp->fileinfo.cFileName);
157 if (!_tmp) {
158 /* wide to utf8 failed, should never happen. */
159 result = NULL;
160 return 0;
161 }
162 strlcpy(dp->dent.d_name, _tmp, _MAX_FNAME+1);
163 dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
164 free(_tmp);
165
166 dp->offset++;
167
168 dp->dent.d_ino = 1;
169 dp->dent.d_off = dp->offset;
170
171 memcpy(entry, &dp->dent, sizeof(*entry));
172
173 *result = &dp->dent;
174
175 return 0;
176 }
177
closedir(DIR * dp)178 int closedir(DIR *dp)
179 {
180 if (!dp)
181 return 0;
182 /* It is valid to scan an empty directory but we have an invalid
183 handle in this case (no first file found). */
184 if (dp->handle != INVALID_HANDLE_VALUE) {
185 FindClose(dp->handle);
186 }
187 if (dp->dirw)
188 free(dp->dirw);
189 if (dp)
190 free(dp);
191
192 return 0;
193 }
194
rewinddir(DIR * dp)195 int rewinddir(DIR *dp)
196 {
197 /* Re-set to the beginning */
198 wchar_t *filespecw;
199 HANDLE handle;
200 size_t dirw_len, filespecw_len, index;
201 zend_bool might_need_prefix;
202
203 FindClose(dp->handle);
204
205 dp->offset = 0;
206 dp->finished = 0;
207
208 /* XXX save the dir len into the struct. */
209 dirw_len = wcslen((wchar_t *)dp->dirw);
210
211 might_need_prefix = dirw_len >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(dp->dirw[0]) && L':' == dp->dirw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(dp->dirw[2]);
212
213 filespecw_len = dirw_len + 2;
214 if (filespecw_len >= _MAX_PATH && might_need_prefix) {
215 filespecw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
216 }
217
218 filespecw = (wchar_t *)malloc((filespecw_len + 1)*sizeof(wchar_t));
219 if (filespecw == NULL) {
220 return -1;
221 }
222
223 if (filespecw_len >= _MAX_PATH && might_need_prefix) {
224 wcscpy(filespecw, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW);
225 wcscpy(filespecw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, dp->dirw);
226 index = dirw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW - 1;
227 } else {
228 wcscpy(filespecw, dp->dirw);
229 index = dirw_len - 1;
230 }
231
232 if (index >= 0 && (filespecw[index] == L'/' ||
233 (filespecw[index] == L'\\' && index == 0)))
234 filespecw[index] = L'\0';
235 wcscat(filespecw, L"\\*");
236
237 if ((handle = FindFirstFileW(filespecw, &(dp->fileinfo))) == INVALID_HANDLE_VALUE) {
238 dp->finished = 1;
239 }
240
241 free(filespecw);
242 dp->handle = handle;
243
244 return 0;
245 }
246
247 #ifdef __cplusplus
248 }
249 #endif
250
251 /*
252 * Local variables:
253 * tab-width: 4
254 * c-basic-offset: 4
255 * End:
256 * vim600: sw=4 ts=4 fdm=marker
257 * vim<600: sw=4 ts=4
258 */
259