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 *
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 */
133struct _tdirent *
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 =
156#ifdef _WIN64
157 _tfindfirst64i32
158#else
159 _tfindfirst32
160#endif
161 (dirp->dd_name, &(dirp->dd_dta));
162
163 if (dirp->dd_handle == -1)
164 {
165 /* Whoops! Seems there are no files in that
166 * directory. */
167 dirp->dd_stat = -1;
168 }
169 else
170 {
171 dirp->dd_stat = 1;
172 }
173 }
174 else
175 {
176 /* Get the next search entry. */
177 if (
178#ifdef _WIN64
179 _tfindnext64i32
180#else
181 _tfindnext32
182#endif
183 (dirp->dd_handle, &(dirp->dd_dta)))
184 {
185 /* We are off the end or otherwise error.
186 _findnext sets errno to ENOENT if no more file
187 Undo this. */
188 DWORD winerr = GetLastError ();
189 if (winerr == ERROR_NO_MORE_FILES)
190 errno = 0;
191 _findclose (dirp->dd_handle);
192 dirp->dd_handle = -1;
193 dirp->dd_stat = -1;
194 }
195 else
196 {
197 /* Update the status to indicate the correct
198 * number. */
199 dirp->dd_stat++;
200 }
201 }
202
203 if (dirp->dd_stat > 0)
204 {
205 /* Successfully got an entry. Everything about the file is
206 * already appropriately filled in except the length of the
207 * file name. */
208 dirp->dd_dir.d_namlen = _tcslen (dirp->dd_dta.name);
209 _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name);
210 return &dirp->dd_dir;
211 }
212
213 return (struct _tdirent *) 0;
214}
215
216
217/*
218 * closedir
219 *
220 * Frees up resources allocated by opendir.
221 */
222int
223_tclosedir (_TDIR * dirp)
224{
225 int rc;
226
227 errno = 0;
228 rc = 0;
229
230 if (!dirp)
231 {
232 errno = EFAULT;
233 return -1;
234 }
235
236 if (dirp->dd_handle != -1)
237 {
238 rc = _findclose (dirp->dd_handle);
239 }
240
241 /* Delete the dir structure. */
242 free (dirp);
243
244 return rc;
245}
246
247/*
248 * rewinddir
249 *
250 * Return to the beginning of the directory "stream". We simply call findclose
251 * and then reset things like an opendir.
252 */
253void
254_trewinddir (_TDIR * dirp)
255{
256 errno = 0;
257
258 if (!dirp)
259 {
260 errno = EFAULT;
261 return;
262 }
263
264 if (dirp->dd_handle != -1)
265 {
266 _findclose (dirp->dd_handle);
267 }
268
269 dirp->dd_handle = -1;
270 dirp->dd_stat = 0;
271}
272
273/*
274 * telldir
275 *
276 * Returns the "position" in the "directory stream" which can be used with
277 * seekdir to go back to an old entry. We simply return the value in stat.
278 */
279long
280_ttelldir (_TDIR * dirp)
281{
282 errno = 0;
283
284 if (!dirp)
285 {
286 errno = EFAULT;
287 return -1;
288 }
289 return dirp->dd_stat;
290}
291
292/*
293 * seekdir
294 *
295 * Seek to an entry previously returned by telldir. We rewind the directory
296 * and call readdir repeatedly until either dd_stat is the position number
297 * or -1 (off the end). This is not perfect, in that the directory may
298 * have changed while we weren't looking. But that is probably the case with
299 * any such system.
300 */
301void
302_tseekdir (_TDIR * dirp, long lPos)
303{
304 errno = 0;
305
306 if (!dirp)
307 {
308 errno = EFAULT;
309 return;
310 }
311
312 if (lPos < -1)
313 {
314 /* Seeking to an invalid position. */
315 errno = EINVAL;
316 return;
317 }
318 else if (lPos == -1)
319 {
320 /* Seek past end. */
321 if (dirp->dd_handle != -1)
322 {
323 _findclose (dirp->dd_handle);
324 }
325 dirp->dd_handle = -1;
326 dirp->dd_stat = -1;
327 }
328 else
329 {
330 /* Rewind and read forward to the appropriate index. */
331 _trewinddir (dirp);
332
333 while ((dirp->dd_stat < lPos) && _treaddir (dirp))
334 ;
335 }
336}
337