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