1 /*
2 * dirent.c
3 *
4 * Derived from DIRLIB.C by Matt J. Weinstein
5 * This note appears in the DIRLIB.H
6 * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89
7 *
8 * Updated by Jeremy Bettis <jeremy@hksys.com>
9 * Significantly revised and rewinddir, seekdir and telldir added by Colin
10 * Peters <colin@fu.is.saga-u.ac.jp>
11 *
12 * $Revision$
13 * $Author$
14 * $Date$
15 *
16 */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <io.h>
26 #include <direct.h>
27 #include <dirent.h>
28
29 #define WIN32_LEAN_AND_MEAN
30 #include <windows.h> /* for GetFileAttributes */
31
32 #include <tchar.h>
33 #define SUFFIX _T("*")
34 #define SLASH _T("\\")
35
36 #ifndef _UNICODE
37 #define _TDIR DIR
38 #define _tdirent dirent
39
40 #define _topendir opendir
41 #define _treaddir readdir
42 #define _tclosedir closedir
43 #define _tseekdir seekdir
44 #define _ttelldir telldir
45 #define _trewinddir rewinddir
46 #else
47 #define _TDIR _WDIR
48 #define _tdirent _wdirent
49
50 #define _topendir _wopendir
51 #define _treaddir _wreaddir
52 #define _tclosedir _wclosedir
53 #define _tseekdir _wseekdir
54 #define _ttelldir _wtelldir
55 #define _trewinddir _wrewinddir
56 #endif
57
58 /*
59 * opendir
60 *
61 * Returns a pointer to a DIR structure appropriately filled in to begin
62 * searching a directory.
63 */
64 _TDIR *
_topendir(const _TCHAR * szPath)65 _topendir (const _TCHAR * szPath)
66 {
67 _TDIR *nd;
68 unsigned int rc;
69 _TCHAR szFullPath[MAX_PATH];
70
71 errno = 0;
72
73 if (!szPath) {
74 errno = EFAULT;
75 return (_TDIR *) 0;
76 }
77
78 if (szPath[0] == _T ('\0')) {
79 errno = ENOTDIR;
80 return (_TDIR *) 0;
81 }
82
83 /* Attempt to determine if the given path really is a directory. */
84 rc = GetFileAttributes (szPath);
85 if (rc == (unsigned int) -1) {
86 /* call GetLastError for more error info */
87 errno = ENOENT;
88 return (_TDIR *) 0;
89 }
90 if (!(rc & FILE_ATTRIBUTE_DIRECTORY)) {
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) +
102 (_tcslen (szFullPath) + _tcslen (SLASH) +
103 _tcslen (SUFFIX) + 1) * sizeof (_TCHAR));
104
105 if (!nd) {
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 _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 to
125 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 errno = EFAULT;
157 return (struct _tdirent *) 0;
158 }
159
160 if (dirp->dd_stat < 0) {
161 /* We have already returned all files in the directory (or the
162 structure has an invalid dd_stat). */
163 return (struct _tdirent *) 0;
164 } else if (dirp->dd_stat == 0) {
165 /* We haven't started the search yet. */
166 /* Start the search */
167 dirp->dd_handle = _tfindfirst (dirp->dd_name, &(dirp->dd_dta));
168
169 if (dirp->dd_handle == -1) {
170 /* Whoops! Seems there are no files in that directory. */
171 dirp->dd_stat = -1;
172 } else {
173 dirp->dd_stat = 1;
174 }
175 } else {
176 /* Get the next search entry. */
177 if (_tfindnext (dirp->dd_handle, &(dirp->dd_dta))) {
178 /* We are off the end or otherwise error. _findnext
179 sets errno to ENOENT if no more file Undo this. */
180 DWORD winerr = GetLastError ();
181
182 if (winerr == ERROR_NO_MORE_FILES)
183 errno = 0;
184 _findclose (dirp->dd_handle);
185 dirp->dd_handle = -1;
186 dirp->dd_stat = -1;
187 } else {
188 /* Update the status to indicate the correct number. */
189 dirp->dd_stat++;
190 }
191 }
192
193 if (dirp->dd_stat > 0) {
194 /* Successfully got an entry. Everything about the file is
195 already appropriately filled in except the length of the
196 file name. */
197 dirp->dd_dir.d_namlen = _tcslen (dirp->dd_dta.name);
198 _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name);
199 return &dirp->dd_dir;
200 }
201
202 return (struct _tdirent *) 0;
203 }
204
205
206 /*
207 * closedir
208 *
209 * Frees up resources allocated by opendir.
210 */
211 int
_tclosedir(_TDIR * dirp)212 _tclosedir (_TDIR * dirp)
213 {
214 int rc;
215
216 errno = 0;
217 rc = 0;
218
219 if (!dirp) {
220 errno = EFAULT;
221 return -1;
222 }
223
224 if (dirp->dd_handle != -1) {
225 rc = _findclose (dirp->dd_handle);
226 }
227
228 /* Delete the dir structure. */
229 free (dirp);
230
231 return rc;
232 }
233
234 /*
235 * rewinddir
236 *
237 * Return to the beginning of the directory "stream". We simply call findclose
238 * and then reset things like an opendir.
239 */
240 void
_trewinddir(_TDIR * dirp)241 _trewinddir (_TDIR * dirp)
242 {
243 errno = 0;
244
245 if (!dirp) {
246 errno = EFAULT;
247 return;
248 }
249
250 if (dirp->dd_handle != -1) {
251 _findclose (dirp->dd_handle);
252 }
253
254 dirp->dd_handle = -1;
255 dirp->dd_stat = 0;
256 }
257
258 /*
259 * telldir
260 *
261 * Returns the "position" in the "directory stream" which can be used with
262 * seekdir to go back to an old entry. We simply return the value in stat.
263 */
264 long
_ttelldir(_TDIR * dirp)265 _ttelldir (_TDIR * dirp)
266 {
267 errno = 0;
268
269 if (!dirp) {
270 errno = EFAULT;
271 return -1;
272 }
273 return dirp->dd_stat;
274 }
275
276 /*
277 * seekdir
278 *
279 * Seek to an entry previously returned by telldir. We rewind the directory
280 * and call readdir repeatedly until either dd_stat is the position number
281 * or -1 (off the end). This is not perfect, in that the directory may
282 * have changed while we weren't looking. But that is probably the case with
283 * any such system.
284 */
285 void
_tseekdir(_TDIR * dirp,long lPos)286 _tseekdir (_TDIR * dirp, long lPos)
287 {
288 errno = 0;
289
290 if (!dirp) {
291 errno = EFAULT;
292 return;
293 }
294
295 if (lPos < -1) {
296 /* Seeking to an invalid position. */
297 errno = EINVAL;
298 return;
299 } else if (lPos == -1) {
300 /* Seek past end. */
301 if (dirp->dd_handle != -1) {
302 _findclose (dirp->dd_handle);
303 }
304 dirp->dd_handle = -1;
305 dirp->dd_stat = -1;
306 } else {
307 /* Rewind and read forward to the appropriate index. */
308 _trewinddir (dirp);
309
310 while ((dirp->dd_stat < lPos) && _treaddir (dirp));
311 }
312 }
313