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