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