/* * dirent.c * This file has no copyright assigned and is placed in the Public Domain. * This file is a part of the mingw-runtime package. * No warranty is given; refer to the file DISCLAIMER within the package. * * Derived from DIRLIB.C by Matt J. Weinstein * This note appears in the DIRLIB.H * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89 * * Updated by Jeremy Bettis * Significantly revised and rewinddir, seekdir and telldir added by Colin * Peters * */ #include #include #include #include #include #include #define WIN32_LEAN_AND_MEAN #include /* for GetFileAttributes */ #include #define SUFFIX _T("*") #define SLASH _T("\\") union __dirstream_t { /* Actual (private) declaration for opaque data type "DIR". */ /* dirent struct to return from dir (NOTE: this makes this thread * safe as long as only one thread uses a particular DIR struct at * a time) */ struct dirent dd_dir; struct __dirstream_private_t { /* Three padding fields, matching the head of dd_dir... */ long dd_ino; /* Always zero. */ unsigned short dd_reclen; /* Always zero. */ unsigned short dd_namlen; /* Length of name in d_name. */ /* ...to keep the start of this disk transfer area for this dir * aligned at the offset of the dd_dir.d_type field */ struct _finddata_t dd_dta; /* _findnext handle */ intptr_t dd_handle; /* Status of search: * (type is now int -- was short in older versions). * 0 = not started yet (next entry to read is first entry) * -1 = off the end * positive = 0 based index of next entry */ int dd_stat; /* given path for dir with search pattern (struct is extended) */ char dd_name[1]; } dd_private; }; union __wdirstream_t { /* Actual (private) declaration for opaque data type "_WDIR". */ /* dirent struct to return from dir (NOTE: this makes this thread * safe as long as only one thread uses a particular DIR struct at * a time) */ struct _wdirent dd_dir; struct __wdirstream_private_t { /* Three padding fields, matching the head of dd_dir... */ long dd_ino; /* Always zero. */ unsigned short dd_reclen; /* Always zero. */ unsigned short dd_namlen; /* Length of name in d_name. */ /* ...to keep the start of this disk transfer area for this dir * aligned at the offset of the dd_dir.d_type field */ struct _wfinddata_t dd_dta; /* _findnext handle */ intptr_t dd_handle; /* Status of search: * 0 = not started yet (next entry to read is first entry) * -1 = off the end * positive = 0 based index of next entry */ int dd_stat; /* given path for dir with search pattern (struct is extended) */ wchar_t dd_name[1]; } dd_private; }; /* We map the BSD d_type field in the returned dirent structure * from the Microsoft _finddata_t dd_dta.attrib bits, which are: * * _A_NORMAL (0x0000) normal file: best fit for DT_REG * _A_RDONLY (0x0001) read-only: no BSD d_type equivalent * _A_HIDDEN (0x0002) hidden entity: no BSD equivalent * _A_SYSTEM (0x0004) system entity: no BSD equivalent * _A_VOLID (0x0008) volume label: no BSD equivalent * _A_SUBDIR (0x0010) directory: best fit for DT_DIR * _A_ARCH (0x0020) "dirty": no BSD equivalent * * Of these, _A_RDONLY, _A_HIDDEN, _A_SYSTEM, and _A_ARCH are * modifier bits, rather than true entity type specifiers; we * will ignore them in the mapping, by applying this mask: */ #define DT_IGNORED (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH) /* Helper for opendir(). */ static inline unsigned _tGetFileAttributes (const _TCHAR * tPath) { #ifdef _UNICODE /* GetFileAttributesW does not work on W9x, so convert to ANSI */ if (_osver & 0x8000) { char aPath [MAX_PATH]; WideCharToMultiByte (CP_ACP, 0, tPath, -1, aPath, MAX_PATH, NULL, NULL); return GetFileAttributesA (aPath); } return GetFileAttributesW (tPath); #else return GetFileAttributesA (tPath); #endif } /* * opendir * * Returns a pointer to a DIR structure appropriately filled in to begin * searching a directory. */ _TDIR * _topendir (const _TCHAR *szPath) { _TDIR *nd; unsigned int rc; _TCHAR szFullPath[MAX_PATH]; errno = 0; if (!szPath) { errno = EFAULT; return (_TDIR *) 0; } if (szPath[0] == _T('\0')) { errno = ENOTDIR; return (_TDIR *) 0; } /* Attempt to determine if the given path really is a directory. */ rc = _tGetFileAttributes (szPath); if (rc == (unsigned int)-1) { /* call GetLastError for more error info */ errno = ENOENT; return (_TDIR *) 0; } if (!(rc & FILE_ATTRIBUTE_DIRECTORY)) { /* Error, entry exists but not a directory. */ errno = ENOTDIR; return (_TDIR *) 0; } /* Make an absolute pathname. */ _tfullpath (szFullPath, szPath, MAX_PATH); /* Allocate enough space to store DIR structure and the complete * directory path given. */ nd = (_TDIR *) malloc (sizeof (_TDIR) + (_tcslen (szFullPath) + _tcslen (SLASH) + _tcslen (SUFFIX) + 1) * sizeof (_TCHAR)); if (!nd) { /* Error, out of memory. */ errno = ENOMEM; return (_TDIR *) 0; } /* Create the search expression. */ _tcscpy (nd->dd_private.dd_name, szFullPath); /* Add on a slash if the path does not end with one. */ if (nd->dd_private.dd_name[0] != _T('\0') && _tcsrchr (nd->dd_private.dd_name, _T('/')) != nd->dd_private.dd_name + _tcslen (nd->dd_private.dd_name) - 1 && _tcsrchr (nd->dd_private.dd_name, _T('\\')) != nd->dd_private.dd_name + _tcslen (nd->dd_private.dd_name) - 1) { _tcscat (nd->dd_private.dd_name, SLASH); } /* Add on the search pattern */ _tcscat (nd->dd_private.dd_name, SUFFIX); /* Initialize handle to -1 so that a premature closedir doesn't try * to call _findclose on it. */ nd->dd_private.dd_handle = -1; /* Initialize the status. */ nd->dd_private.dd_stat = 0; /* Initialize the dirent structure. ino and reclen are invalid under * Win32, and name simply points at the appropriate part of the * findfirst_t structure. */ nd->dd_dir.d_ino = 0; nd->dd_dir.d_reclen = 0; nd->dd_dir.d_namlen = 0; memset (nd->dd_dir.d_name, 0, FILENAME_MAX); return nd; } /* * readdir * * Return a pointer to a dirent structure filled with the information on the * next entry in the directory. */ struct _tdirent * _treaddir (_TDIR * dirp) { errno = 0; /* Check for valid DIR struct. */ if (!dirp) { errno = EFAULT; return (struct _tdirent *) 0; } if (dirp->dd_private.dd_stat < 0) { /* We have already returned all files in the directory * (or the structure has an invalid dd_stat). */ return (struct _tdirent *) 0; } else if (dirp->dd_private.dd_stat == 0) { /* We haven't started the search yet. */ /* Start the search */ dirp->dd_private.dd_handle = _tfindfirst (dirp->dd_private.dd_name, &(dirp->dd_private.dd_dta)); if (dirp->dd_private.dd_handle == -1) { /* Whoops! Seems there are no files in that * directory. */ dirp->dd_private.dd_stat = -1; } else { dirp->dd_private.dd_stat = 1; } } else { /* Get the next search entry. */ if (_tfindnext (dirp->dd_private.dd_handle, &(dirp->dd_private.dd_dta))) { /* We are off the end or otherwise error. _findnext sets errno to ENOENT if no more file Undo this. */ DWORD winerr = GetLastError (); if (winerr == ERROR_NO_MORE_FILES) errno = 0; _findclose (dirp->dd_private.dd_handle); dirp->dd_private.dd_handle = -1; dirp->dd_private.dd_stat = -1; } else { /* Update the status to indicate the correct * number. */ dirp->dd_private.dd_stat++; } } if (dirp->dd_private.dd_stat > 0) { /* Successfully got an entry. Everything about the file is * already appropriately filled in except the length of the * file name... */ dirp->dd_dir.d_namlen = _tcslen (dirp->dd_dir.d_name); /* * ...and the attributes returned in the dd_dta.attrib field; * these require adjustment to their BSD equivalents, which are * returned via the union with the dd_dir.d_type field: */ switch( dirp->dd_dir.d_type &= ~DT_IGNORED ) { case DT_REG: case DT_DIR: /* After stripping out the modifier bits in DT_IGNORED, * (which we ALWAYS ignore), this pair require no further * adjustment... */ break; default: /* ...while nothing else has an appropriate equivalent * in the BSD d_type identification model. */ dirp->dd_dir.d_type = DT_UNKNOWN; } return &dirp->dd_dir; } return (struct _tdirent *) 0; } /* * closedir * * Frees up resources allocated by opendir. */ int _tclosedir (_TDIR * dirp) { int rc; errno = 0; rc = 0; if (!dirp) { errno = EFAULT; return -1; } if (dirp->dd_private.dd_handle != -1) { rc = _findclose (dirp->dd_private.dd_handle); } /* Delete the dir structure. */ free (dirp); return rc; } /* * rewinddir * * Return to the beginning of the directory "stream". We simply call findclose * and then reset things like an opendir. */ void _trewinddir (_TDIR * dirp) { errno = 0; if (!dirp) { errno = EFAULT; return; } if (dirp->dd_private.dd_handle != -1) { _findclose (dirp->dd_private.dd_handle); } dirp->dd_private.dd_handle = -1; dirp->dd_private.dd_stat = 0; } /* * telldir * * Returns the "position" in the "directory stream" which can be used with * seekdir to go back to an old entry. We simply return the value in stat. */ long _ttelldir (_TDIR * dirp) { errno = 0; if (!dirp) { errno = EFAULT; return -1; } return dirp->dd_private.dd_stat; } /* * seekdir * * Seek to an entry previously returned by telldir. We rewind the directory * and call readdir repeatedly until either dd_stat is the position number * or -1 (off the end). This is not perfect, in that the directory may * have changed while we weren't looking. But that is probably the case with * any such system. */ void _tseekdir (_TDIR * dirp, long lPos) { errno = 0; if (!dirp) { errno = EFAULT; return; } if (lPos < -1) { /* Seeking to an invalid position. */ errno = EINVAL; return; } else if (lPos == -1) { /* Seek past end. */ if (dirp->dd_private.dd_handle != -1) { _findclose (dirp->dd_private.dd_handle); } dirp->dd_private.dd_handle = -1; dirp->dd_private.dd_stat = -1; } else { /* Rewind and read forward to the appropriate index. */ _trewinddir (dirp); while ((dirp->dd_private.dd_stat < lPos) && _treaddir (dirp)) ; } }