Home | History | Annotate | Download | only in dirent
      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 (at) hksys.com>
     12  * Significantly revised and rewinddir, seekdir and telldir added by Colin
     13  * Peters <colin (at) 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  */
    150 struct _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  */
    227 int
    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  */
    258 void
    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  */
    284 long
    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  */
    306 void
    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