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