1 /*
2 * dirent.c
3 * This file has no copyright assigned and is placed in the Public Domain.
4 * This file is a part of the mingw-runtime package.
5 * No warranty is given; refer to the file DISCLAIMER within the package.
6 *
7 * Derived from DIRLIB.C by Matt J. Weinstein
8 * This note appears in the DIRLIB.H
9 * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89
10 *
11 * Updated by Jeremy Bettis <jeremy@hksys.com>
12 * Significantly revised and rewinddir, seekdir and telldir added by Colin
13 * Peters <colin@fu.is.saga-u.ac.jp>
14 *
15 */
16
17 #include <stdlib.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <io.h>
21 #include <direct.h>
22
23 #include "dirent.h"
24
25 #define WIN32_LEAN_AND_MEAN
26 #include <windows.h> /* for GetFileAttributes */
27
28 #include <tchar.h>
29
30 #ifdef _UNICODE
31 #define _tdirent _wdirent
32 #define _TDIR _WDIR
33 #define _topendir _wopendir
34 #define _tclosedir _wclosedir
35 #define _treaddir _wreaddir
36 #define _trewinddir _wrewinddir
37 #define _ttelldir _wtelldir
38 #define _tseekdir _wseekdir
39 #else
40 #define _tdirent dirent
41 #define _TDIR DIR
42 #define _topendir opendir
43 #define _tclosedir closedir
44 #define _treaddir readdir
45 #define _trewinddir rewinddir
46 #define _ttelldir telldir
47 #define _tseekdir seekdir
48 #endif
49
50 #define SUFFIX _T("*")
51 #define SLASH _T("\\")
52
53
54 /*
55 * opendir
56 *
57 * Returns a pointer to a DIR structure appropriately filled in to begin
58 * searching a directory.
59 */
60 _TDIR *
_topendir(const _TCHAR * szPath)61 _topendir (const _TCHAR *szPath)
62 {
63 _TDIR *nd;
64 unsigned int rc;
65 _TCHAR szFullPath[MAX_PATH];
66
67 errno = 0;
68
69 if (!szPath)
70 {
71 errno = EFAULT;
72 return (_TDIR *) 0;
73 }
74
75 if (szPath[0] == _T('\0'))
76 {
77 errno = ENOTDIR;
78 return (_TDIR *) 0;
79 }
80
81 /* Attempt to determine if the given path really is a directory. */
82 rc = GetFileAttributes (szPath);
83 if (rc == (unsigned int)-1)
84 {
85 /* call GetLastError for more error info */
86 errno = ENOENT;
87 return (_TDIR *) 0;
88 }
89 if (!(rc & FILE_ATTRIBUTE_DIRECTORY))
90 {
91 /* Error, entry exists but not a directory. */
92 errno = ENOTDIR;
93 return (_TDIR *) 0;
94 }
95
96 /* Make an absolute pathname. */
97 _tfullpath (szFullPath, szPath, MAX_PATH);
98
99 /* Allocate enough space to store DIR structure and the complete
100 * directory path given. */
101 nd = (_TDIR *) malloc (sizeof (_TDIR) + (_tcslen(szFullPath) + _tcslen (SLASH) +
102 _tcslen(SUFFIX) + 1) * sizeof(_TCHAR));
103
104 if (!nd)
105 {
106 /* Error, out of memory. */
107 errno = ENOMEM;
108 return (_TDIR *) 0;
109 }
110
111 /* Create the search expression. */
112 _tcscpy (nd->dd_name, szFullPath);
113
114 /* Add on a slash if the path does not end with one. */
115 if (nd->dd_name[0] != _T('\0') &&
116 nd->dd_name[_tcslen (nd->dd_name) - 1] != _T('/') &&
117 nd->dd_name[_tcslen (nd->dd_name) - 1] != _T('\\'))
118 {
119 _tcscat (nd->dd_name, SLASH);
120 }
121
122 /* Add on the search pattern */
123 _tcscat (nd->dd_name, SUFFIX);
124
125 /* Initialize handle to -1 so that a premature closedir doesn't try
126 * to call _findclose on it. */
127 nd->dd_handle = -1;
128
129 /* Initialize the status. */
130 nd->dd_stat = 0;
131
132 /* Initialize the dirent structure. ino and reclen are invalid under
133 * Win32, and name simply points at the appropriate part of the
134 * findfirst_t structure. */
135 nd->dd_dir.d_ino = 0;
136 nd->dd_dir.d_reclen = 0;
137 nd->dd_dir.d_namlen = 0;
138 memset (nd->dd_dir.d_name, 0, FILENAME_MAX);
139
140 return nd;
141 }
142
143
144 /*
145 * readdir
146 *
147 * Return a pointer to a dirent structure filled with the information on the
148 * next entry in the directory.
149 */
150 struct _tdirent *
_treaddir(_TDIR * dirp)151 _treaddir (_TDIR * dirp)
152 {
153 errno = 0;
154
155 /* Check for valid DIR struct. */
156 if (!dirp)
157 {
158 errno = EFAULT;
159 return (struct _tdirent *) 0;
160 }
161
162 if (dirp->dd_stat < 0)
163 {
164 /* We have already returned all files in the directory
165 * (or the structure has an invalid dd_stat). */
166 return (struct _tdirent *) 0;
167 }
168 else if (dirp->dd_stat == 0)
169 {
170 /* We haven't started the search yet. */
171 /* Start the search */
172 dirp->dd_handle = _tfindfirst (dirp->dd_name, &(dirp->dd_dta));
173
174 if (dirp->dd_handle == -1)
175 {
176 /* Whoops! Seems there are no files in that
177 * directory. */
178 dirp->dd_stat = -1;
179 }
180 else
181 {
182 dirp->dd_stat = 1;
183 }
184 }
185 else
186 {
187 /* Get the next search entry. */
188 if (_tfindnext (dirp->dd_handle, &(dirp->dd_dta)))
189 {
190 /* We are off the end or otherwise error.
191 _findnext sets errno to ENOENT if no more file
192 Undo this. */
193 DWORD winerr = GetLastError();
194 if (winerr == ERROR_NO_MORE_FILES)
195 errno = 0;
196 _findclose (dirp->dd_handle);
197 dirp->dd_handle = -1;
198 dirp->dd_stat = -1;
199 }
200 else
201 {
202 /* Update the status to indicate the correct
203 * number. */
204 dirp->dd_stat++;
205 }
206 }
207
208 if (dirp->dd_stat > 0)
209 {
210 /* Successfully got an entry. Everything about the file is
211 * already appropriately filled in except the length of the
212 * file name. */
213 dirp->dd_dir.d_namlen = _tcslen (dirp->dd_dta.name);
214 _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name);
215 return &dirp->dd_dir;
216 }
217
218 return (struct _tdirent *) 0;
219 }
220
221
222 /*
223 * closedir
224 *
225 * Frees up resources allocated by opendir.
226 */
227 int
_tclosedir(_TDIR * dirp)228 _tclosedir (_TDIR * dirp)
229 {
230 int rc;
231
232 errno = 0;
233 rc = 0;
234
235 if (!dirp)
236 {
237 errno = EFAULT;
238 return -1;
239 }
240
241 if (dirp->dd_handle != -1)
242 {
243 rc = _findclose (dirp->dd_handle);
244 }
245
246 /* Delete the dir structure. */
247 free (dirp);
248
249 return rc;
250 }
251
252 /*
253 * rewinddir
254 *
255 * Return to the beginning of the directory "stream". We simply call findclose
256 * and then reset things like an opendir.
257 */
258 void
_trewinddir(_TDIR * dirp)259 _trewinddir (_TDIR * dirp)
260 {
261 errno = 0;
262
263 if (!dirp)
264 {
265 errno = EFAULT;
266 return;
267 }
268
269 if (dirp->dd_handle != -1)
270 {
271 _findclose (dirp->dd_handle);
272 }
273
274 dirp->dd_handle = -1;
275 dirp->dd_stat = 0;
276 }
277
278 /*
279 * telldir
280 *
281 * Returns the "position" in the "directory stream" which can be used with
282 * seekdir to go back to an old entry. We simply return the value in stat.
283 */
284 long
_ttelldir(_TDIR * dirp)285 _ttelldir (_TDIR * dirp)
286 {
287 errno = 0;
288
289 if (!dirp)
290 {
291 errno = EFAULT;
292 return -1;
293 }
294 return dirp->dd_stat;
295 }
296
297 /*
298 * seekdir
299 *
300 * Seek to an entry previously returned by telldir. We rewind the directory
301 * and call readdir repeatedly until either dd_stat is the position number
302 * or -1 (off the end). This is not perfect, in that the directory may
303 * have changed while we weren't looking. But that is probably the case with
304 * any such system.
305 */
306 void
_tseekdir(_TDIR * dirp,long lPos)307 _tseekdir (_TDIR * dirp, long lPos)
308 {
309 errno = 0;
310
311 if (!dirp)
312 {
313 errno = EFAULT;
314 return;
315 }
316
317 if (lPos < -1)
318 {
319 /* Seeking to an invalid position. */
320 errno = EINVAL;
321 return;
322 }
323 else if (lPos == -1)
324 {
325 /* Seek past end. */
326 if (dirp->dd_handle != -1)
327 {
328 _findclose (dirp->dd_handle);
329 }
330 dirp->dd_handle = -1;
331 dirp->dd_stat = -1;
332 }
333 else
334 {
335 /* Rewind and read forward to the appropriate index. */
336 _trewinddir (dirp);
337
338 while ((dirp->dd_stat < lPos) && _treaddir (dirp))
339 ;
340 }
341 }
342