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