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