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