xref: /PHP-7.1/win32/readdir.c (revision 7f6387b5)
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