*/
#ifndef _DIRENT_H_
#define _DIRENT_H_
+#pragma GCC system_header
/* All the headers include this file. */
#include <_mingw.h>
#ifndef RC_INVOKED
-#ifdef __cplusplus
-extern "C" {
-#endif
+_BEGIN_C_DECLS
struct dirent
{
- long d_ino; /* Always zero. */
- unsigned short d_reclen; /* Always zero. */
- unsigned short d_namlen; /* Length of name in d_name. */
- char d_name[FILENAME_MAX]; /* File name. */
+ long d_ino; /* Always zero. */
+ unsigned short d_reclen; /* Always sizeof struct dirent. */
+ unsigned short d_namlen; /* Length of name in d_name. */
+ unsigned d_type; /* File attributes */
+ char d_name[FILENAME_MAX]; /* File name. */
};
-/*
- * This is an internal data structure. Good programmers will not use it
- * except as an argument to one of the functions below.
- * dd_stat field is now int (was short in older versions).
+/* This opaque data type represents the private structure
+ * through which a directory stream is referenced.
*/
-typedef struct
-{
- /* disk transfer area for this dir */
- struct _finddata_t dd_dta;
+typedef struct __dirstream_t DIR;
+
+DIR* __cdecl __MINGW_NOTHROW __mingw_opendir (const char*);
+struct dirent* __cdecl __MINGW_NOTHROW __mingw_readdir (DIR*);
+int __cdecl __MINGW_NOTHROW __mingw_closedir (DIR*);
+void __cdecl __MINGW_NOTHROW __mingw_rewinddir (DIR*);
+long __cdecl __MINGW_NOTHROW __mingw_telldir (DIR*);
+void __cdecl __MINGW_NOTHROW __mingw_seekdir (DIR*, long);
+
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = opendir ))
+DIR* __cdecl __MINGW_NOTHROW opendir (const char *__dirname)
+{ return __mingw_opendir (__dirname); }
- /* 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;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = readdir ))
+struct dirent* __cdecl __MINGW_NOTHROW readdir (DIR *__dir)
+{ return __mingw_readdir (__dir); }
- /* _findnext handle */
- intptr_t dd_handle;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = closedir ))
+int __cdecl __MINGW_NOTHROW closedir (DIR *__dir)
+{ return __mingw_closedir (__dir); }
- /*
- * 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;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = rewinddir ))
+void __cdecl __MINGW_NOTHROW rewinddir (DIR *__dir)
+{ return __mingw_rewinddir (__dir); }
- /* given path for dir with search pattern (struct is extended) */
- char dd_name[1];
-} DIR;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = telldir ))
+long __cdecl __MINGW_NOTHROW telldir (DIR *__dir)
+{ return __mingw_telldir (__dir); }
-DIR* __cdecl __MINGW_NOTHROW opendir (const char*);
-struct dirent* __cdecl __MINGW_NOTHROW readdir (DIR*);
-int __cdecl __MINGW_NOTHROW closedir (DIR*);
-void __cdecl __MINGW_NOTHROW rewinddir (DIR*);
-long __cdecl __MINGW_NOTHROW telldir (DIR*);
-void __cdecl __MINGW_NOTHROW seekdir (DIR*, long);
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = seekdir ))
+void __cdecl __MINGW_NOTHROW seekdir (DIR *__dir, long __loc)
+{ return __mingw_seekdir (__dir, __loc); }
/* wide char versions */
struct _wdirent
{
- long d_ino; /* Always zero. */
- unsigned short d_reclen; /* Always zero. */
- unsigned short d_namlen; /* Length of name in d_name. */
- wchar_t d_name[FILENAME_MAX]; /* File name. */
+ long d_ino; /* Always zero. */
+ unsigned short d_reclen; /* Always size of struct _wdirent. */
+ unsigned short d_namlen; /* Length of name in d_name. */
+ unsigned d_type; /* File attributes */
+ wchar_t d_name[FILENAME_MAX]; /* File name. */
};
-/*
- * This is an internal data structure. Good programmers will not use it
- * except as an argument to one of the functions below.
+/* This opaque data type represents the private structure
+ * through which a wide directory stream is referenced.
*/
-typedef struct
-{
- /* disk transfer area for this dir */
- struct _wfinddata_t dd_dta;
+typedef struct __wdirstream_t _WDIR;
+
+_WDIR* __cdecl __MINGW_NOTHROW __mingw__wopendir (const wchar_t*);
+struct _wdirent* __cdecl __MINGW_NOTHROW __mingw__wreaddir (_WDIR*);
+int __cdecl __MINGW_NOTHROW __mingw__wclosedir (_WDIR*);
+void __cdecl __MINGW_NOTHROW __mingw__wrewinddir (_WDIR*);
+long __cdecl __MINGW_NOTHROW __mingw__wtelldir (_WDIR*);
+void __cdecl __MINGW_NOTHROW __mingw__wseekdir (_WDIR*, long);
+
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = _wopendir ))
+_WDIR* __cdecl __MINGW_NOTHROW _wopendir (const wchar_t *__dirname)
+{ return __mingw__wopendir (__dirname); }
- /* 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;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = _wreaddir ))
+struct _wdirent* __cdecl __MINGW_NOTHROW _wreaddir (_WDIR *__dir)
+{ return __mingw__wreaddir (__dir); }
- /* _findnext handle */
- intptr_t dd_handle;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = _wclosedir ))
+int __cdecl __MINGW_NOTHROW _wclosedir (_WDIR *__dir)
+{ return __mingw__wclosedir (__dir); }
- /*
- * 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;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = _wrewinddir))
+void __cdecl __MINGW_NOTHROW _wrewinddir (_WDIR *__dir)
+{ return __mingw__wrewinddir (__dir); }
- /* given path for dir with search pattern (struct is extended) */
- wchar_t dd_name[1];
-} _WDIR;
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = _wtelldir ))
+long __cdecl __MINGW_NOTHROW _wtelldir (_WDIR *__dir)
+{ return __mingw__wtelldir (__dir); }
-_WDIR* __cdecl __MINGW_NOTHROW _wopendir (const wchar_t*);
-struct _wdirent* __cdecl __MINGW_NOTHROW _wreaddir (_WDIR*);
-int __cdecl __MINGW_NOTHROW _wclosedir (_WDIR*);
-void __cdecl __MINGW_NOTHROW _wrewinddir (_WDIR*);
-long __cdecl __MINGW_NOTHROW _wtelldir (_WDIR*);
-void __cdecl __MINGW_NOTHROW _wseekdir (_WDIR*, long);
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = _wseekdir ))
+void __cdecl __MINGW_NOTHROW _wseekdir (_WDIR *__dir, long __loc)
+{ return __mingw__wseekdir (__dir, __loc); }
+_END_C_DECLS
+
+#if defined(_BSD_SOURCE) || defined(_WIN32)
+/*
+ * BSD-ish systems define manifest constants for the d_type field;
+ * although probably only DT_REG and DT_DIR are useful on Win32, we
+ * try to map them as best we can from the _finddata.attrib field.
+ *
+ * The relevant Microsoft manifest values 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
+ *
+ * Thus, we may immediately define:
+ */
+#define DT_REG _A_NORMAL
+#define DT_DIR _A_SUBDIR
+
+/* The remaining BSD d_type manifest values have no Win32 equivalents;
+ * we will define them artificially, and then we will ensure that our
+ * opendir()/readdir() implementation will never assign them; (we will
+ * substitute DT_UNKNOWN, but it would be unwise to simply make these
+ * equivalent to that, since an application is likely to simply check
+ * for d_type equal to any one of these defined types, and thus could
+ * mistakenly identify DT_UNKNOWN as being of the tested type):
+ */
+#define DT_BLK (((_A_SUBDIR) << 4) | DT_UNKNOWN)
+#define DT_CHR (((_A_SUBDIR) << 5) | DT_UNKNOWN)
+#define DT_FIFO (((_A_SUBDIR) << 6) | DT_UNKNOWN)
+#define DT_LNK (((_A_SUBDIR) << 7) | DT_UNKNOWN)
+#define DT_SOCK (((_A_SUBDIR) << 8) | DT_UNKNOWN)
+
+/* No file system entity can ever be simultaneously a volume label
+ * and a directory; we will exploit this to unambiguously define:
+ */
+#define DT_UNKNOWN (_A_VOLID | _A_SUBDIR)
-#ifdef __cplusplus
-}
-#endif
+#define _DIRENT_HAVE_D_TYPE 1
+#define _DIRENT_HAVE_D_RECLEN 1
+#define _DIRENT_HAVE_D_NAMLEN 1
-#endif /* Not RC_INVOKED */
+#endif /* _BSD_SOURCE */
+#endif /* ! RC_INVOKED */
-#endif /* Not _DIRENT_H_ */
+#endif /* !defined _DIRENT_H_ */
/*
* 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.
*
* DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89
*
* Updated by Jeremy Bettis <jeremy@hksys.com>
- * Significantly revised and rewinddir, seekdir and telldir added by Colin
- * Peters <colin@fu.is.saga-u.ac.jp>
+ * Significantly revised and rewinddir, seekdir and telldir added
+ * by Colin Peters <colin@fu.is.saga-u.ac.jp>
+ * Further significantly revised for improved memory utilisation,
+ * efficiency in operation, and better POSIX standards compliance
+ * by Keith Marshall <keithmarshall@users.sourceforge.net>
*
*/
-
+#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
-#include <io.h>
-#include <direct.h>
#include <dirent.h>
#define WIN32_LEAN_AND_MEAN
-#include <windows.h> /* for GetFileAttributes */
-
+#include <windows.h>
#include <tchar.h>
-#define SUFFIX _T("*")
-#define SLASH _T("\\")
-
-/* 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);
+ /* In a Unicode build, the path name within the _wdirent struct is
+ * represented by a wchar_t string; we use the snwprintf() function
+ * to simultaneously assign the d_name and d_namlen fields, copying
+ * from a temporary WIN32_FIND_DATA struct on the stack...
+ */
+# include <wchar.h>
+# define DIRENT_ASSIGN_NAME snwprintf
+
#else
- return GetFileAttributesA (tPath);
+ /* ...while for a non-Unicode build, the corresponding data within
+ * the dirent structure is represented by a normal char string, and
+ * the assignments are made by the snprintf() function.
+ */
+# define DIRENT_ASSIGN_NAME snprintf
#endif
-}
-/*
- * opendir
- *
- * Returns a pointer to a DIR structure appropriately filled in to begin
- * searching a directory.
+/* This implementation applies a "__mingw_" pseudo-namespace prefix to
+ * the standard POSIX function names, for each each function it defines;
+ * the following pair of macros facilitates this.
*/
-_TDIR *
-_topendir (const _TCHAR *szPath)
-{
- _TDIR *nd;
- unsigned int rc;
- _TCHAR szFullPath[MAX_PATH];
+#define __mingw_impl__(__function__) __mingw_token_prefix__(__function__)
+#define __mingw_token_prefix__(__suffix__) __mingw_##__suffix__
- errno = 0;
+/* The following macros facilitate a conditional abort of any function,
+ * while reporting a specified condition code via "errno".
+ */
+#define DIRENT_RETURN_NOTHING
+#define DIRENT_REJECT( chk, err, rtn ) \
+ do { if( chk ){ errno = (err); return rtn; }} while(0)
- if (!szPath)
- {
- errno = EFAULT;
- return (_TDIR *) 0;
- }
+struct __dirstream_t
+{
+ /* Actual (private) declaration for opaque data type "DIR". */
+
+ /* Encapsulated dirent struct to be returned. (NOTE: this makes
+ * this thread safe as long as only one thread uses a particular
+ * DIR struct at any time)
+ */
+ struct dirent dd_dirent;
+
+ /* Handle, for use when performing FindFirstFile()/FindNextFile()
+ * file system searches.
+ */
+ void * dd_handle;
+
+ /* Index for next FindFirstFile()/FindNextFile() entry:
+ * 0 = not started yet (next entry to read is first entry);
+ * positive value = 0 based index of next entry to be read;
+ * set to -1 when no more matching entries to be read.
+ */
+ int dd_index;
+
+ /* File name pattern to be matched in FindFirstFile() file system
+ * search; note that the actual size will be adjusted at run time,
+ * causing the DIR struct to grow to accommodate the pattern.
+ */
+ char dd_name[1];
+};
+
+struct __wdirstream_t
+{
+ /* Actual (private) declaration for opaque data type "_WDIR". */
+
+ /* Encapsulated dirent struct to be returned. (NOTE: this makes
+ * this thread safe as long as only one thread uses a particular
+ * _WDIR struct at any time)
+ */
+ struct _wdirent dd_dirent;
+
+ /* Handle, for use when performing FindFirstFile()/FindNextFile()
+ * file system searches.
+ */
+ void * dd_handle;
+
+ /* Index for next FindFirstFile()/FindNextFile() entry:
+ * 0 = not started yet (next entry to read is first entry);
+ * positive value = 0 based index of next entry to be read;
+ * set to -1 when no more matching entries to be read.
+ */
+ int dd_index;
+
+ /* File name pattern to be matched in FindFirstFile() file system
+ * search; note that the actual size will be adjusted at run time,
+ * causing the DIR struct to grow to accommodate the pattern.
+ */
+ wchar_t dd_name[1];
+};
+
+/* We map the BSD d_type field in the returned dirent structure
+ * from the Microsoft WIN32_FIND_DATA.dwFileAttribute bits, which we
+ * mask to retrieve these _finddata_t equivalents:
+ *
+ * _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)
+
+/* Microsoft have regally screwed up their _findfirst() and _findnext()
+ * implementation, making its ABI arbitrarily dependent on the state of the
+ * insane _USE_32BIT_TIME_T macro; thus, we can no longer depend on it to
+ * return data in any deterministically structured manner. Furthermore, the
+ * first deterministic alternative, _findfirst64() and _findnext64(), which
+ * they have provided, is not available in all versions of Windows which we
+ * would like to support; thus, we must furnish our own implementation,
+ * based on the more primitive FindFirstFile()/FindNextFile() API.
+ *
+ * Weirdly, when we use the FindFirstFile()/FindNextFile() API, (as
+ * our dirent_findfirst()/dirent_findnext() functions do), _A_NORMAL
+ * is represented by the value 0x0080; we need to suppress this:
+ */
+#define DT_VALID_BITS ~(DT_IGNORED | 0x0080)
- if (szPath[0] == _T('\0'))
- {
- errno = ENOTDIR;
- return (_TDIR *) 0;
- }
+static
+void dirent_update( struct _tdirent *dd, WIN32_FIND_DATA *fd )
+{
+ /* Helper function, used by dirent_findfirst() and dirent_findnext(),
+ * to transfer all relevant data from their respective WIN32_FIND_DATA
+ * buffers to the specified dirent structure.
+ */
+ dd->d_namlen = DIRENT_ASSIGN_NAME( dd->d_name, FILENAME_MAX,
+ _T("%s"), fd->cFileName
+ );
+ if( (dd->d_type = fd->dwFileAttributes & DT_VALID_BITS) > DT_DIR )
+ dd->d_type = DT_UNKNOWN;
+}
- /* 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 */
+static
+void *dirent_findfirst( const _TCHAR *lookup, struct _tdirent *dd )
+{
+ /* Replacement for Microsoft's _findfirst() function; it temporarily
+ * captures the result of a FindFirstFile() call in a local buffer,
+ * whence it updates the specified dirent structure, before returning
+ * an opaque handle for subsequent use by FindNextFile().
+ */
+ void *fd;
+ WIN32_FIND_DATA buf;
+ if( (fd = FindFirstFile( lookup, &buf )) == INVALID_HANDLE_VALUE )
+ {
+ /* The look-up failed: set errno accordingly; (note that this
+ * requires mapping of some system error codes to the equivalent
+ * errno values prescribed by POSIX).
+ */
+ if( (errno = GetLastError()) == ERROR_PATH_NOT_FOUND )
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_name, szFullPath);
-
- /* Add on a slash if the path does not end with one. */
- if (nd->dd_name[0] != _T('\0')
- && _tcsrchr (nd->dd_name, _T('/')) != nd->dd_name
- + _tcslen (nd->dd_name) - 1
- && _tcsrchr (nd->dd_name, _T('\\')) != nd->dd_name
- + _tcslen (nd->dd_name) - 1)
- {
- _tcscat (nd->dd_name, SLASH);
- }
+ else if( errno == ERROR_DIRECTORY )
+ errno = ENOTDIR;
- /* Add on the search pattern */
- _tcscat (nd->dd_name, SUFFIX);
+ else if( errno != ENOENT )
+ errno = EINVAL;
+ }
+ else
+ /* The look-up was successful: update the dirent structure.
+ */
+ dirent_update( dd, &buf );
+
+ /* Ultimately, return the (possibly invalid) search handle
+ * which FindFirstFile() has given us.
+ */
+ return fd;
+}
- /* Initialize handle to -1 so that a premature closedir doesn't try
- * to call _findclose on it. */
- nd->dd_handle = -1;
+#define DIRENT_OPEN(D) \
+ ((D)->dd_handle = dirent_findfirst((D)->dd_name, &((D)->dd_dirent)))
- /* Initialize the status. */
- nd->dd_stat = 0;
+static
+int dirent_findnext( void *fd, struct _tdirent *dd )
+{
+ /* Replacement for Microsoft's _findnext() function; it temporarily
+ * captures the result of calling FindNextFile() in a local buffer,
+ * whence it updates the specified dirent structure, before returning
+ * an appropriate status value.
+ */
+ int status;
+ WIN32_FIND_DATA buf;
+ if( (status = FindNextFile( fd, &buf )) != 0 )
+ {
+ /* The look-up was successful; update the dirent structure, and
+ * immediately return the (non-zero) status.
+ */
+ dirent_update( dd, &buf );
+ return status;
+ }
+
+ /* If we get to here, the look-up was unsuccessful. This may be
+ * simply because there are no more files to find...
+ */
+ else if( (status = GetLastError()) != ERROR_NO_MORE_FILES )
+ /*
+ * ...but in any other case, we must update errno to reflect
+ * an invalid position within the directory stream; (POSIX
+ * prescribes ENOENT, for this error condition).
+ */
+ errno = ENOENT;
+
+ /* In any case, for an unsuccessful look-up we always return
+ * an effective status code of zero.
+ */
+ return 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);
+#define DIRENT_UPDATE(D) \
+ dirent_findnext( (D)->dd_handle, &((D)->dd_dirent) )
- return nd;
-}
+/* For consistency, we also provide this simple wrapper for Microsoft's
+ * FindClose() function, to clean up residual context from our replaced
+ * _findfirst() and _findnext() functions.
+ */
+static __inline__ __attribute__((__always_inline__))
+int dirent_findclose( void *fd ){ return FindClose( fd ); }
-/*
- * readdir
+/*****
+ *
+ * opendir()
+ *
+ * Returns a pointer to a DIR structure appropriately filled in
+ * to begin searching a directory.
*
- * Return a pointer to a dirent structure filled with the information on the
- * next entry in the directory.
*/
-struct _tdirent *
-_treaddir (_TDIR * dirp)
+_TDIR *
+__mingw_impl__(_topendir)( const _TCHAR *path_name )
{
- errno = 0;
-
- /* Check for valid DIR struct. */
- if (!dirp)
- {
- errno = EFAULT;
- return (struct _tdirent *) 0;
- }
-
- if (dirp->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_stat == 0)
- {
- /* We haven't started the search yet. */
- /* Start the search */
- dirp->dd_handle = _tfindfirst (dirp->dd_name, &(dirp->dd_dta));
-
- if (dirp->dd_handle == -1)
- {
- /* Whoops! Seems there are no files in that
- * directory. */
- dirp->dd_stat = -1;
- }
- else
- {
- dirp->dd_stat = 1;
- }
- }
- else
+ _TDIR *nd;
+ _TCHAR abs_path[MAX_PATH];
+
+ /* Reject any request which passes a NULL or an empty path name;
+ * note that POSIX doesn't specify the handling for the NULL case,
+ * and some implementations may simply fail with a segmentation
+ * fault. We will fail more gracefully. Previous versions used
+ * EFAULT here, but EINVAL seems more appropriate; however, POSIX
+ * specifies neither of these for any opendir() failure.
+ */
+ DIRENT_REJECT( (path_name == NULL), EINVAL, (_TDIR *)(NULL) );
+ /*
+ * Conversely, POSIX *does* specify ENOENT for the empty path
+ * name case, where we previously had ENOTDIR; here, we correct
+ * this previous anomaly.
+ */
+ DIRENT_REJECT( (*path_name == _T('\0')), ENOENT, (_TDIR *)(NULL) );
+
+ /* Identify the absolute path name corresponding to the (maybe
+ * relative) path name we are to process; (this ensures that we
+ * may always refer back to this same path name, e.g. to rewind
+ * the "directory stream", even after an intervening change of
+ * current working directory).
+ */
+ _tfullpath( abs_path, path_name, MAX_PATH );
+
+ /* Ensure that the generated absolute path name ends with a
+ * directory separator (backslash) character, so that we may
+ * correctly append a wild-card matching pattern which will
+ * cause dirent_findfirst() and dirent_findnext() to return
+ * every entry in the specified directory; (note that, for now
+ * we may simply assume that abs_path refers to a directory;
+ * we will verify that when we call dirent_findfirst() on it).
+ */
+ if( *abs_path != _T('\0') )
{
- /* Get the next search entry. */
- if (_tfindnext (dirp->dd_handle, &(dirp->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_handle);
- dirp->dd_handle = -1;
- dirp->dd_stat = -1;
- }
- else
- {
- /* Update the status to indicate the correct
- * number. */
- dirp->dd_stat++;
- }
+ size_t offset = _tcslen( abs_path ) - 1;
+ if( (abs_path[offset] != _T('/')) && (abs_path[offset] != _T('\\')) )
+ _tcscat( abs_path, _T("\\") );
}
- if (dirp->dd_stat > 0)
+ /* Now append the "match everything" wild-card pattern.
+ */
+ _tcscat( abs_path, _T("*") );
+
+ /* Allocate space to store DIR structure. The size MUST be
+ * adjusted to accommodate the complete absolute path name for
+ * the specified directory, extended to include the wild-card
+ * matching pattern, as above; (note that we DO NOT need any
+ * special provision for the terminating NUL on the path name,
+ * since the base size of the DIR structure includes it).
+ */
+ nd = (_TDIR *)(malloc(
+ sizeof( _TDIR ) + (_tcslen( abs_path ) * sizeof( _TCHAR ))
+ ));
+
+ /* Bail out, if insufficient memory.
+ */
+ DIRENT_REJECT( (nd == NULL), ENOMEM, (_TDIR *)(NULL) );
+
+ /* Copy the extended absolute path name string into place
+ * within the allocated space for the DIR structure.
+ */
+ _tcscpy( nd->dd_name, abs_path );
+
+ /* Initialize the "directory stream", by calling dirent_findfirst()
+ * on it; this leaves the data for the first directory entry in the
+ * internal dirent buffer, ready to be retrieved by readdir().
+ */
+ if( DIRENT_OPEN( nd ) == INVALID_HANDLE_VALUE )
{
- /* 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_dta.name);
- _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name);
- return &dirp->dd_dir;
+ /* The dirent_findfirst() call, (implied by DIRENT_OPEN), failed;
+ * this will already have set errno appropriately, and we should
+ * now prepare to return a NULL "directory stream" pointer; since
+ * this implies that we will lose our reference pointer to the
+ * block of memory we allocated for the stream, we must free
+ * that before we bail out.
+ */
+ free( nd );
+ return (_TDIR *)(NULL);
}
- return (struct _tdirent *) 0;
+ /* Initialize the status, (i.e. the location index), so that readdir()
+ * will simply return the first directory entry, which has already been
+ * fetched by dirent_findfirst(), without performing an intervening
+ * dirent_findnext() call.
+ */
+ nd->dd_index = 0;
+
+ /* The d_ino field has no relevance in MS-Windows; initialize it
+ * to zero, as a one-time assignment for this DIR instance, and
+ * henceforth forget them; (users should simply ignore it).
+ */
+ nd->dd_dirent.d_ino = 0;
+
+ /* The d_reclen field represents the size, in bytes, of the dirent
+ * struct within this DIR instance; (this may be useful for storage
+ * of packed arrays of dirent structs, with truncated d_name fields,
+ * but here, we need to keep the full size). Initialize accordingly,
+ * and henceforth forget it; we will never change it; users should
+ * regard it as read-only, but we don't care if they do change it,
+ * because we don't ever use it.
+ */
+ nd->dd_dirent.d_reclen = sizeof( struct _tdirent );
+
+ /* We've now completely initialized an instance of a DIR structure,
+ * representing the requested "directory stream"; return a pointer
+ * via which the caller may access it.
+ */
+ return nd;
}
-/*
- * closedir
+/*****
*
- * Frees up resources allocated by opendir.
+ * readdir()
+ *
+ * Return a pointer to a dirent structure filled in with information
+ * on the next available entry, (if any), in the "directory stream".
*/
-int
-_tclosedir (_TDIR * dirp)
+struct _tdirent *
+__mingw_impl__(_treaddir)( _TDIR *dirp )
{
- int rc;
-
- errno = 0;
- rc = 0;
-
- if (!dirp)
- {
- errno = EFAULT;
- return -1;
- }
-
- if (dirp->dd_handle != -1)
- {
- rc = _findclose (dirp->dd_handle);
- }
+ /* Check for a valid DIR stream reference; (we can't really
+ * be certain until we try to read from it, except in the case
+ * of a NULL pointer reference). Where we lack a valid reference,
+ * POSIX mandates reporting EBADF; we previously had EFAULT, so
+ * this version corrects the former anomaly.
+ */
+ DIRENT_REJECT( (dirp == NULL), EBADF, (struct _tdirent *)(NULL) );
+
+ /* Okay to proceed. Unless this is the first readdir() request
+ * following an opendir(), or a rewinddir(), (in which case the
+ * current location index will be zero, and the requisite return
+ * information will have been established already), we need to
+ * update the DIR stream data, retrieving the data for the next
+ * directory entry...
+ */
+ if( (dirp->dd_index++ > 0) && (DIRENT_UPDATE( dirp ) == 0) )
+ /*
+ * ...bailing out, if no such data is retrievable...
+ */
+ return (struct _tdirent *)(NULL);
+
+ /* ...otherwise, returning the requisite data pointer.
+ */
+ return &dirp->dd_dirent;
+}
- /* Delete the dir structure. */
- free (dirp);
- return rc;
+/*****
+ *
+ * closedir()
+ *
+ * Frees up resources allocated by opendir().
+ *
+ */
+int
+__mingw_impl__(_tclosedir)( _TDIR * dirp )
+{
+ /* Attempting to reference a directory stream via a NULL pointer
+ * would cause a segmentation fault; evade this. Since NULL can
+ * never represent an open directory stream, set the EBADF errno
+ * status, as mandated by POSIX, once again correcting previous
+ * anomalous use of EFAULT in this context.
+ */
+ DIRENT_REJECT(
+ ((dirp == NULL) || (dirent_findclose( dirp->dd_handle ) == 0)),
+ EBADF, -1
+ );
+
+ /* If we didn't bail out above, we have a valid DIR structure
+ * with which we have finished; release the memory allocated
+ * to it, before returning "success".
+ */
+ free( dirp );
+ return 0;
}
-/*
- * rewinddir
+
+/*****
+ *
+ * rewinddir()
+ *
+ * Return to the beginning of the directory "stream". We simply call
+ * dirent_findclose(), to clear prior context, then dirent_findfirst()
+ * to restart the directory search, resetting the location index as it
+ * would have been left by opendir().
*
- * Return to the beginning of the directory "stream". We simply call findclose
- * and then reset things like an opendir.
*/
void
-_trewinddir (_TDIR * dirp)
+__mingw_impl__(_trewinddir)( _TDIR * dirp )
{
- errno = 0;
-
- if (!dirp)
- {
- errno = EFAULT;
- return;
- }
-
- if (dirp->dd_handle != -1)
- {
- _findclose (dirp->dd_handle);
- }
-
- dirp->dd_handle = -1;
- dirp->dd_stat = 0;
+ /* This is an XSI extension to POSIX, which specifies no formal
+ * error conditions; we will continue to check for and evade the
+ * potential segmentation fault which would result from passing a
+ * NULL reference pointer. For consistency with the core functions
+ * implemented above, we will again report this as EBADF, rather
+ * than the EFAULT of previous versions.
+ */
+ DIRENT_REJECT(
+ ((dirp == NULL) || (dirent_findclose( dirp->dd_handle ) == 0)),
+ EBADF, DIRENT_RETURN_NOTHING
+ );
+
+ /* We successfully closed the prior search context; reopen...
+ */
+ if( DIRENT_OPEN( dirp ) != INVALID_HANDLE_VALUE )
+ /*
+ * ...and, on success, reset the location index.
+ */
+ dirp->dd_index = 0;
}
-/*
- * telldir
+
+/*****
+ *
+ * telldir()
+ *
+ * Returns the "position" in the "directory stream" which can then
+ * be passed to seekdir(), to return back to a previous entry. We
+ * simply return the current location index.
*
- * 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)
+__mingw_impl__(_ttelldir)( _TDIR * dirp )
{
- errno = 0;
-
- if (!dirp)
- {
- errno = EFAULT;
- return -1;
- }
- return dirp->dd_stat;
+ /* This too is a POSIX-XSI extension, with no mandatory error
+ * conditions. Once again, evade a potential segmentation fault
+ * on passing a NULL reference pointer, again reporting it as
+ * EBADF in preference to the EFAULT of previous versions.
+ */
+ DIRENT_REJECT( (dirp == NULL), EBADF, -1 );
+
+ /* We didn't bail out; just assume dirp is valid, and return
+ * the current location index.
+ */
+ return dirp->dd_index;
}
-/*
- * seekdir
+
+/*****
+ *
+ * seekdir()
+ *
+ * Seek to an entry previously returned by telldir(). We rewind
+ * the "directory stream", then repeatedly call dirent_findnext()
+ * while incrementing its internal location index until it matches
+ * the position requested, or we reach the end of the stream. This
+ * is not perfect, in that the directory may have changed while we
+ * weren't looking, but it is the best we can achieve, and may
+ * likely reproduce the behaviour of other implementations.
*
- * 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)
+__mingw_impl__(_tseekdir)( _TDIR * dirp, long loc )
{
- 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_handle != -1)
- {
- _findclose (dirp->dd_handle);
- }
- dirp->dd_handle = -1;
- dirp->dd_stat = -1;
- }
- else
- {
- /* Rewind and read forward to the appropriate index. */
- _trewinddir (dirp);
-
- while ((dirp->dd_stat < lPos) && _treaddir (dirp))
- ;
- }
+ /* Another POSIX-XSI extension, with no specified mandatory
+ * error conditions; we require a seek location of zero or
+ * greater, and will reject less than zero as EINVAL...
+ */
+ DIRENT_REJECT( (loc < 0L), EINVAL, DIRENT_RETURN_NOTHING );
+
+ /* Other than this, we simply accept any error condition
+ * which arises as we "rewind" the "directory stream"...
+ */
+ __mingw_impl__(_trewinddir)( dirp );
+
+ /* ...and, if this is successful...
+ */
+ if( (loc > 0) && (dirp->dd_handle != INVALID_HANDLE_VALUE) )
+ /*
+ * ...seek forward until the location index within
+ * the DIR structure matches the requested location.
+ */
+ while( (++dirp->dd_index < loc)
+ && (DIRENT_UPDATE( dirp ) != 0) )
+ ;
}
+
+/* $RCSfile$: end of file */