OSDN Git Service

Make dirent implementation namespace and time_t clean.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Mon, 3 Nov 2014 21:53:24 +0000 (21:53 +0000)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Mon, 3 Nov 2014 21:53:24 +0000 (21:53 +0000)
mingwrt/ChangeLog
mingwrt/include/_mingw.h
mingwrt/include/dirent.h
mingwrt/mingwex/dirent.c

index f1d9e76..877bed9 100644 (file)
@@ -1,3 +1,42 @@
+2014-11-03  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
+       Make dirent implementation namespace and time_t clean.
+
+       * include/_mingw.h (_EXTERN_C, _BEGIN_C_DECLS, _END_C_DECLS): New
+       macros; define them.  They facilitate identification of symbols which
+       must be compiled with `extern "C"' binding, when compiling C++ code.
+       (__CRT_ALIAS): New macro; define it.  It is intended to replace...
+       (_CRTALIAS): ...this, which is to be considered as deprecated.
+       (__JMPSTUB__, __LIBIMPL__): New macros; define them.  They provide
+       __CRT_ALIAS build hints, while remaining transparent to GCC.
+       (UNICODE, _UNICODE) [user defined]: Define consistently.
+
+       * mingwex/dirent.c: Reimplement; (this is a back-port from the
+       4.0-dev branch, with additional ABI changes to improve stability).
+       (opendir, readdir, telldir, seekdir, rewinddir, closedir)
+       (_wopendir, _wreaddir, _wtelldir, _wseekdir, _wrewinddir, _wclosedir):
+       Add `__mingw_' prefix to all publicly exposed symbol names.
+       (dirent_findfirst, dirent_findnext, dirent_findclose): New locally
+       implemented functions; they replace Microsoft's ambiguously defined
+       _findfirst, _findnext, and _findclose, so avoiding ABI instability
+       resulting from the ambiguous size of Microsoft's time_t.
+
+       * include/dirent.h (struct dirent, struct _wdirent): Redefine them,
+       removing unnecessary time_t and file size fields; (our replacements
+       for _findfirst, and _findnext do not require them to be present).
+       (DIR, _WDIR): Adjust typedefs; they are now opaque structs.
+       (opendir, readdir, telldir, seekdir, rewinddir, closedir)
+       (_wopendir, _wreaddir, _wtelldir, _wseekdir, _wrewinddir, _wclosedir):
+       Adjust prototypes, making them inline aliases for public symbols which
+       are now qualified by the `__mingw_' prefix; add build hints to support
+       compilation of addressable stubs, with the original symbol names.
+       (DT_REG, DT_DIR, DT_UNKNOWN): New manifest constants; define them.
+       (DT_BLK, DT_CHR, DT_FIFO, DT_LNK, DT_SOCK): Likewise; (these are for
+       BSD compatibility, but are fundamentally unsupported on MS-Windows).
+       (_DIRENT_HAVE_D_TYPE, _DIRENT_HAVE_D_RECLEN, _DIRENT_HAVE_D_NAMLEN):
+       New macros; define them.  (These show availability of optional fields
+       within the dirent struct, which client code may wish to access).
+
 2014-10-31  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
        Correct improper naming of assembly language source files.
index 346d5a8..eb51458 100644 (file)
 #endif
 
 #ifdef __cplusplus
-# define __CRT_INLINE inline
+# define _EXTERN_C       extern "C"
+# define _BEGIN_C_DECLS  extern "C" {
+# define _END_C_DECLS    }
+
+# define __CRT_INLINE    inline
+
 #else
+# define _EXTERN_C       extern
+# define _BEGIN_C_DECLS
+# define _END_C_DECLS
+
 # if __GNUC_STDC_INLINE__
-#  define __CRT_INLINE extern inline __attribute__((__gnu_inline__))
+#  define __CRT_INLINE   extern inline __attribute__((__gnu_inline__))
 # else
-#  define __CRT_INLINE extern __inline__
+#  define __CRT_INLINE   extern __inline__
 # endif
 #endif
 
 # ifdef __GNUC__
-#  define _CRTALIAS __CRT_INLINE __attribute__ ((__always_inline__))
+  /* A special form of __CRT_INLINE, to ALWAYS request inlining when
+   * possible is provided; originally specified as _CRTALIAS, this is
+   * now deprecated in favour of __CRT_ALIAS, for syntactic consistency
+   * with __CRT_INLINE itself.
+   */
+#  define  _CRTALIAS   __CRT_INLINE __attribute__((__always_inline__))
+#  define __CRT_ALIAS  __CRT_INLINE __attribute__((__always_inline__))
 # else
-#  define _CRTALIAS __CRT_INLINE
+#  define  _CRTALIAS   __CRT_INLINE    /* deprecated form */
+#  define __CRT_ALIAS  __CRT_INLINE    /* preferred form */
 # endif
+/*
+ * Each function which is implemented as a __CRT_ALIAS should also be
+ * accompanied by an externally visible interface.  The following pair
+ * of macros provide a mechanism for implementing this, either as a stub
+ * redirecting to an alternative external function, or by compilation of
+ * the normally inlined code into free standing object code; each macro
+ * provides a way for us to offer arbitrary hints for use by the build
+ * system, while remaining transparent to the compiler.
+ */
+#define __JMPSTUB__(__BUILD_HINT__)
+#define __LIBIMPL__(__BUILD_HINT__)
 
 #ifdef __cplusplus
 # define __UNUSED_PARAM(x)
@@ -283,4 +310,29 @@ allow GCC to optimize away some EH unwind code, at least in DW2 case.  */
 # endif
 #endif
 
+/* Only Microsoft could attempt to justify this insanity: when building
+ * a UTF-16LE application -- apparently their understanding of Unicode is
+ * limited to this -- the C/C++ runtime requires that the user must define
+ * the _UNICODE macro, while to use the Windows API's UTF-16LE capabilities,
+ * it is the UNICODE macro, (without the leading underscore), which must be
+ * defined.  The (bogus) explanation appears to be that it is the C standard
+ * which dictates the requirement for the leading underscore, to avoid any
+ * possible conflict with a user defined symbol; (bogus because the macro
+ * must be user defined anyway -- it is not a private symbol -- and in
+ * any case, the Windows API already reserves the UNICODE symbol as
+ * a user defined macro, with equivalent intent.
+ *
+ * The real explanation, of course, is that this is just another example
+ * of Microsoft irrationality; in any event, there seems to be no sane
+ * scenario in which defining one without the other would be required,
+ * or indeed would not raise potential for internal inconsistency, so we
+ * ensure that either both are, or neither is defined.
+ */
+#if defined UNICODE && ! defined _UNICODE
+# define _UNICODE  UNICODE
+#endif
+#if defined _UNICODE && ! defined UNICODE
+# define UNICODE  _UNICODE
+#endif
+
 #endif /* __MINGW_H */
index 44fb1c0..a31c4aa 100644 (file)
@@ -7,6 +7,7 @@
  */
 #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_ */
index a006128..e6813c1 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * 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 */