OSDN Git Service

Work around Microsoft breakage of command line globbing.
authorEarnie Boyd <earnie@users.sourceforge.net>
Wed, 31 Oct 2012 13:56:04 +0000 (09:56 -0400)
committerEarnie Boyd <earnie@users.sourceforge.net>
Wed, 31 Oct 2012 13:56:04 +0000 (09:56 -0400)
* include/glob.h: New file; it declares the API for a (mostly) POSIX
compatible path name globbing facility for use in MinGW applications.

* src/libcrt/misc/glob.c: New file; it implements the glob API...
(__mingw_glob, __mingw_globfree): ...through this pair of functions.

* Makefile.in (misc_SOURCES): Add glob.c
(mingwrt_lib_INCLUDES): Add glob.h

* include/_mingw.h (BEGIN_C_DECLS, END_C_DECLS): New macros;
they facilitate...
[__cplusplus]: ...demarcation of 'extern "C"' declarations for this.
(__CRT_GLOB_ESCAPE_CHAR__): New manifest constant; define it.
(__CRT_GLOB_USE_MSVCRT__, __CRT_GLOB_USE_MINGW__): Likewise; these act
as mutually exclusive feature selectors for assignment in CRTglob.c
(__CRT_GLOB_USE_SINGLE_QUOTE__, __CRT_GLOB_BRACKET_GROUPS__):
(__CRT_GLOB_CASE_SENSITIVE__): New bit-flag definitions; with...
[_CRT_glob & __CRT_GLOB_USE_MINGW__]: ...they enable additional
command line globbing capabilities.

* src/libcrt/crt/CRTglob.c (_CRT_glob): New default value; enable...
[_CRT_glob & __CRT_GLOB_USE_MINGW__]: ...this feature preference.

* src/libcrt/crt/init.c (_CRT_GLOB_OPT, ARGV_INLINE): New macros.
(ARGV_ESCAPE, ARGV_SQUOTE, ARGV_NOGROUP): New manifest constants;
define them as aliases for the new bit-flags from _mingw.h
(do_glob, backslash, unquote, __mingw_setargv): New static functions.
(_mingw32_init_mainargs): Use them to implement globbing, when...
[_CRT_glob & __CRT_GLOB_USE_MINGW__]: ...this is enabled.

ChangeLog
Makefile.in
include/_mingw.h
include/glob.h [new file with mode: 0644]
src/libcrt/crt/CRTglob.c
src/libcrt/crt/init.c
src/libcrt/misc/glob.c [new file with mode: 0644]

index 62cbf44..070a298 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2012-10-31  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
+       Work around Microsoft breakage of command line globbing.
+
+       * include/glob.h: New file; it declares the API for a (mostly) POSIX
+       compatible path name globbing facility for use in MinGW applications.
+
+       * src/libcrt/misc/glob.c: New file; it implements the glob API...
+       (__mingw_glob, __mingw_globfree): ...through this pair of functions.
+
+       * Makefile.in (misc_SOURCES): Add glob.c
+       (mingwrt_lib_INCLUDES): Add glob.h
+
+       * include/_mingw.h (BEGIN_C_DECLS, END_C_DECLS): New macros;
+       they facilitate...
+       [__cplusplus]: ...demarcation of 'extern "C"' declarations for this.
+       (__CRT_GLOB_ESCAPE_CHAR__): New manifest constant; define it.
+       (__CRT_GLOB_USE_MSVCRT__, __CRT_GLOB_USE_MINGW__): Likewise; these act
+       as mutually exclusive feature selectors for assignment in CRTglob.c
+       (__CRT_GLOB_USE_SINGLE_QUOTE__, __CRT_GLOB_BRACKET_GROUPS__):
+       (__CRT_GLOB_CASE_SENSITIVE__): New bit-flag definitions; with...
+       [_CRT_glob & __CRT_GLOB_USE_MINGW__]: ...they enable additional
+       command line globbing capabilities.
+
+       * src/libcrt/crt/CRTglob.c (_CRT_glob): New default value; enable...
+       [_CRT_glob & __CRT_GLOB_USE_MINGW__]: ...this feature preference.
+
+       * src/libcrt/crt/init.c (_CRT_GLOB_OPT, ARGV_INLINE): New macros.
+       (ARGV_ESCAPE, ARGV_SQUOTE, ARGV_NOGROUP): New manifest constants;
+       define them as aliases for the new bit-flags from _mingw.h
+       (do_glob, backslash, unquote, __mingw_setargv): New static functions.
+       (_mingw32_init_mainargs): Use them to implement globbing, when...
+       [_CRT_glob & __CRT_GLOB_USE_MINGW__]: ...this is enabled.
+
 2012-10-30  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
        Use C compiler selected by running configure script.
index 0ff3f9b..b0eb68b 100644 (file)
@@ -1,6 +1,6 @@
 ##
 # @file Makefile.in
-# @copy 2012 MinGW.org project
+# Copyright (C) 2012 MinGW.org Project
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
 # copy of this software and associated documentation files (the "Software"),
@@ -21,6 +21,9 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 ##
+#
+# @configure_input@
+#
 VPATH=$(srcdir)
 PACKAGE_TARNAME=@PACKAGE_TARNAME@
 PACKAGE_VERSION=@PACKAGE_VERSION@
@@ -488,7 +491,8 @@ misc_SOURCES := \
   $(SRCDIR)/getopt.c \
   $(SRCDIR)/membarrier.c \
   $(SRCDIR)/mingw-aligned-malloc.c \
-  $(SRCDIR)/mingw-fseek.c
+  $(SRCDIR)/mingw-fseek.c \
+  $(SRCDIR)/glob.c
 
 SRCDIR := src/libcrt/posix/libgen
 posix_libgen_SOURCES := $(SRCDIR)/basename.c $(SRCDIR)/dirname.c
@@ -928,9 +932,9 @@ mingwrt_bin_LIBRARIES := mingwm10.dll
 mingwrt_lib_LIBRARIES := $(wildcard libmsvcr*.a)
 mingwrt_lib_LIBRARIES += $(wildcard libmoldname*.a)
 mingwrt_lib_INCLUDES := \
-  _mingw.h   dirent.h  getopt.h    math.h      setjmp.h  strings.h  varargs.h \
-  assert.h   dos.h     inttypes.h  mbctype.h   share.h   wchar.h \
-  complex.h  errno.h   io.h        mbstring.h  signal.h  tchar.h    wctype.h \
+  _mingw.h   dirent.h  glob.h      getopt.h    math.h    setjmp.h  strings.h \
+  assert.h   dos.h     inttypes.h  mbctype.h   share.h   wchar.h   varargs.h \
+  complex.h  errno.h   io.h        mbstring.h  signal.h  tchar.h   wctype.h \
   conio.h    excpt.h   libgen.h    mem.h       stdint.h  time.h \
   ctype.h    fcntl.h   limits.h    memory.h    stdio.h   unistd.h \
   dir.h      fenv.h    locale.h    process.h   stdlib.h  utime.h \
index 6d387b1..84d1c55 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @file _mingw.h
- * @copy 2012 MinGW.org project
+ * Copyright (C) 2012 MinGW.org Project
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+
+/* -------------------------------------------------------------------------
+ * Implementation of MinGW specific macros; these are to be included by
+ * all other WSL header files.
+ * -------------------------------------------------------------------------
+ */
 #ifndef _MINGW_H
 #define _MINGW_H
 #pragma GCC system_header
 #include <sdkddkver.h>
 
-/*
- * Mingw specific macros.
- */
-
 #define __MINGW_VERSION             4.0
 #define __MINGW_MAJOR_VERSION       4
 #define __MINGW_MINOR_VERSION       0
 #define __MINGW_PATCHLEVEL          0
 
-// These four macros are deprecated and will be removed in the next major
-// version release.
+/* The following four macros are deprecated and will be removed
+ * in the release greater than 4.1.
+ */
 #define __MINGW32_VERSION           3.20
 #define __MINGW32_MAJOR_VERSION     3
 #define __MINGW32_MINOR_VERSION     20
 #endif
 
 /* These are defined by the user (or the compiler)
-   to specify how identifiers are imported from a DLL.
-
-   __MINGW_IMPORT                  The attribute definition to specify imported
-                                   variables/functions.
-   _CRTIMP                         As above.  For MS compatibility.
-   __MINGW_VERSION                 Runtime version.
-   __MINGW_MAJOR_VERSION           Runtime major version.
-   __MINGW_MINOR_VERSION           Runtime minor version.
-   __MINGW_BUILD_DATE              Runtime build date.
+ * to specify how identifiers are imported from a DLL.
+ *
+ * __MINGW_IMPORT                  The attribute definition to specify imported
+ *                                 variables/functions.
+ * _CRTIMP                         As above.  For MS compatibility.
+ * __MINGW_VERSION                 Runtime version.
+ * __MINGW_MAJOR_VERSION           Runtime major version.
+ * __MINGW_MINOR_VERSION           Runtime minor version.
+ * __MINGW_BUILD_DATE              Runtime build date.
+ *
+ * Macros to enable MinGW features which deviate from standard MSVC
+ * compatible behaviour; these may be specified directly in user code,
+ * activated implicitly, (e.g. by specifying _POSIX_C_SOURCE or such),
+ * or by inclusion in __MINGW_FEATURES__:
+ *
+ * __USE_MINGW_ANSI_STDIO          Select a more ANSI C99 compatible
+ *                                 implementation of printf() and friends.
+ *
+ * Other macros:
+ *
+ * __int64                         define to be long long.  Using a typedef
+ *                                 doesn't work for "unsigned __int64"
+ *
+ *
+ * Manifest definitions for flags to control globbing of the command line
+ * during application start up, (before main() is called).  The first pair,
+ * when assigned as bit flags within _CRT_glob, select the globbing algorithm
+ * to be used; (the MINGW algorithm overrides MSCVRT, if both are specified).
+ * Prior to mingwrt-3.21, only the MSVCRT option was supported; this choice
+ * may produce different results, depending on which particular version of
+ * MSVCRT.DLL is in use; (in recent versions, it seems to have become
+ * definitively broken, when globbing within double quotes).
+ */
+#define __CRT_GLOB_USE_MSVCRT__        0x0001
 
-   Macros to enable MinGW features which deviate from standard MSVC
-   compatible behaviour; these may be specified directly in user code,
-   activated implicitly, (e.g. by specifying _POSIX_C_SOURCE or such),
-   or by inclusion in __MINGW_FEATURES__:
+/* From mingwrt-3.21 onward, this should be the preferred choice; it will
+ * produce consistent results, regardless of the MSVCRT.DLL version in use.
+ */
+#define __CRT_GLOB_USE_MINGW__         0x0002
 
-   __USE_MINGW_ANSI_STDIO          Select a more ANSI C99 compatible
-                                   implementation of printf() and friends.
+/* When the __CRT_GLOB_USE_MINGW__ flag is set, within _CRT_glob, the
+ * following additional options are also available, (but are not enabled
+ * by default):
+ *
+ *    __CRT_GLOB_USE_SINGLE_QUOTE__    allows use of single (apostrophe)
+ *                                     quoting characters, analogously to
+ *                                     POSIX usage, as an alternative to
+ *                                     double quotes, for collection of
+ *                                     arguments separated by white space
+ *                                     into a single logical argument.
+ *
+ *    __CRT_GLOB_BRACKET_GROUPS__      enable interpretation of bracketed
+ *                                     character groups as POSIX compatible
+ *                                     globbing patterns, matching any one
+ *                                     character which is either included
+ *                                     in, or excluded from the group.
+ *
+ *    __CRT_GLOB_CASE_SENSITIVE__      enable case sensitive matching for
+ *                                     globbing patterns; this is default
+ *                                     behaviour for POSIX, but because of
+ *                                     the case insensitive nature of the
+ *                                     MS-Windows file system, it is more
+ *                                     appropriate to use case insensitive
+ *                                     globbing as the MinGW default.
+ *
+ */
+#define __CRT_GLOB_USE_SINGLE_QUOTE__  0x0010
+#define __CRT_GLOB_BRACKET_GROUPS__    0x0020
+#define __CRT_GLOB_CASE_SENSITIVE__    0x0040
 
-   Other macros:
+/* The MinGW globbing algorithm uses the ASCII DEL control code as a marker
+ * for globbing characters which were embedded within quoted arguments; (the
+ * quotes are stripped away BEFORE the argument is globbed; the globbing code
+ * treats the marked character as immutable, and strips out the DEL markers,
+ * before storing the resultant argument).  The DEL code is mapped to this
+ * function here; DO NOT change it, without rebuilding the runtime.
+ */
+#define __CRT_GLOB_ESCAPE_CHAR__       (char)(127)
 
-   __int64                         define to be long long.  Using a typedef
-                                   doesn't work for "unsigned __int64"
 
 /* Manifest definitions identifying the flag bits, controlling activation
  * of MinGW features, as specified by the user in __MINGW_FEATURES__.
 #undef __attribute__
 
 #ifndef __MINGW_IMPORT
-  /* Note the extern. This is needed to work around GCC's
-     limitations in handling dllimport attribute.  */
+ /* Note the extern. This is needed to work around GCC's
+  * limitations in handling dllimport attribute.
+  */
 # define __MINGW_IMPORT  extern __attribute__ ((__dllimport__))
 #endif
 #ifndef _CRTIMP
 #endif
 
 #define __MINGW_GNUC_PREREQ(major, minor) \
-  (__GNUC__ > (major) \
-   || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
+ (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
 
 #ifdef __cplusplus
 # define __CRT_INLINE inline
 #define _CRTALIAS __CRT_INLINE __attribute__ ((__always_inline__))
 
 #ifdef __cplusplus
+# define   BEGIN_C_DECLS       extern "C" {
 # define __UNUSED_PARAM(x)
+# define   END_C_DECLS         }
 #else
-# define __UNUSED_PARAM(x) x __attribute__ ((__unused__))
+# define   BEGIN_C_DECLS
+# define __UNUSED_PARAM(x)     x __attribute__ ((__unused__))
+# define   END_C_DECLS
 #endif
 
 #define __MINGW_ATTRIB_NORETURN __attribute__ ((__noreturn__))
 #define __MINGW_NOTHROW __attribute__ ((__nothrow__))
 
 
-/* TODO: Mark (almost) all CRT functions as __MINGW_NOTHROW.  This will
-allow GCC to optimize away some EH unwind code, at least in DW2 case.  */
-
 /* Activation of MinGW specific extended features:
+ *
+ * TODO: Mark (almost) all CRT functions as __MINGW_NOTHROW.  This will
+ * allow GCC to optimize away some EH unwind code, at least in DW2 case.
  */
 #ifndef __USE_MINGW_ANSI_STDIO
 /*
@@ -198,13 +262,14 @@ typedef struct localeinfo_struct {
  (!defined(__TEST_SQL_NOUNICODEMAP) && defined(UNICODE)) || \
  (!defined(__TEST_SQL_NOUNICODEMAP) && defined(_UNICODE)) || \
  defined(FORCE_UNICODE) || \
- (defined(__TEST_SQL_NOUNICODEMAP) && !defined(SQL_NOUNICODEMAP) && (defined(UNICODE) || defined(_UNICODE))) \
-)
-#define __AW(AW) __AW__(AW, W)
-#define __STR(AW) __AW__(L, AW)
+ (defined(__TEST_SQL_NOUNICODEMAP) && !defined(SQL_NOUNICODEMAP) && \
+   (defined(UNICODE) || defined(_UNICODE))) \
+ )
+# define __AW(AW) __AW__(AW, W)
+# define __STR(AW) __AW__(L, AW)
 #else
-#define __AW(AW) __AW__(AW, A)
-#define __STR(AW) __AW__(, AW)
+# define __AW(AW) __AW__(AW, A)
+# define __STR(AW) __AW__(, AW)
 #endif
 
-#endif /* __MINGW_H */
+#endif /* _MINGW_H */
diff --git a/include/glob.h b/include/glob.h
new file mode 100644 (file)
index 0000000..60625b2
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * @file glob.h
+ * Copyright (C) 2012 MinGW.org project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, this permission notice, and the following
+ * disclaimer shall be included in all copies or substantial portions of
+ * the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+/* ---------------------------------------------------------------------------
+ *
+ * Header file supporting a MinGW implementation of an (approximately)
+ * POSIX conforming glob() and globfree() API.
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2011, 2012, MinGW Project.
+ *
+ * ---------------------------------------------------------------------------
+ */
+#ifndef _GLOB_H
+#define _GLOB_H  1
+#include <_mingw.h>
+#pragma GCC system_header
+
+#ifndef RC_INVOKED
+/* POSIX requires glob.h to define the size_t type; we need to
+ * get this from GCC, just as sys/types.h does.
+ */
+#define __need_size_t
+#include <stddef.h>
+
+typedef
+struct glob_t
+{ /* The structure, in which glob() returns the list of file system
+   * entities which it has matched.
+   */
+  void     * gl_magic; /* reserved field; pointer to a glob signature  */
+  size_t     gl_pathc; /* counter for paths matched                    */
+  char    ** gl_pathv; /* list of matching path names                  */
+  size_t     gl_offs;  /* number of initial unused slots in gl_pathv   */
+} glob_t;
+
+/* A macro to facilitate definition of the flags which are used to
+ * control the operation of glob().
+ */
+#define __GLOB_FLAG__(NAME)  (1 << __GLOB_##NAME##_OFFSET)
+enum {
+  /* Identify the zero-based offset values which are used to specify
+   * the individual bit positions for each __GLOB_FLAG; the initial
+   * list specifies the standard set of flags required by POSIX.
+   */
+  __GLOB_APPEND_OFFSET = 0,
+  __GLOB_DOOFFS_OFFSET,
+  __GLOB_ERR_OFFSET,
+  __GLOB_MARK_OFFSET,
+  __GLOB_NOCHECK_OFFSET,
+  __GLOB_NOESCAPE_OFFSET,
+  __GLOB_NOSORT_OFFSET,
+  /*
+   * GNU's implementation of glob() supports a supplementary set of
+   * options, none of which are required by POSIX.  We include these
+   * for reference, and to reserve the flag identities for a possible
+   * future implementation; the current MinGW implementation does not
+   * support them.
+   */
+  __GLOB_TILDE_OFFSET,
+  __GLOB_TILDE_CHECK_OFFSET,
+  __GLOB_PERIOD_OFFSET,
+  __GLOB_BRACE_OFFSET,
+  __GLOB_ONLYDIR_OFFSET,
+  __GLOB_ALTDIRFUNC_OFFSET,
+  __GLOB_NOMAGIC_OFFSET,
+  /*
+   * This MinGW implementation DOES add support for the following
+   * custom options, which offer improved handling of MS-Windows
+   * specific peculiarities:--
+   *
+   *   GLOB_CASEMATCH      makes glob() respect case sensitivity
+   *                       in path name matches; this is similar
+   *                       to default behaviour on POSIX systems,
+   *                       but to better support the MS-Windows
+   *                       file system, the MinGW implementation
+   *                       of glob() performs a CASE INSENSITIVE
+   *                       character match by default, (except
+   *                       when matching within character group
+   *                       patterns, which are ALWAYS assumed to
+   *                       require CASE SENSITIVE matching).
+   */
+  __GLOB_CASEMATCH_OFFSET,
+  /*
+   * The following is a convenience, to mark the end of the enumeration;
+   * it is NEVER used to locate any user visible __GLOB_FLAG__, but it
+   * MUST remain as the final entry in the enumerated list.
+   */
+  __GLOB_FLAG_OFFSET_HIGH_WATER_MARK
+};
+
+/* Definitions of the mandatory flags, as specified by POSIX.
+ */
+#define GLOB_APPEND      __GLOB_FLAG__(APPEND)
+#define GLOB_DOOFFS      __GLOB_FLAG__(DOOFFS)
+#define GLOB_ERR         __GLOB_FLAG__(ERR)
+#define GLOB_MARK        __GLOB_FLAG__(MARK)
+#define GLOB_NOCHECK     __GLOB_FLAG__(NOCHECK)
+#define GLOB_NOESCAPE    __GLOB_FLAG__(NOESCAPE)
+#define GLOB_NOSORT      __GLOB_FLAG__(NOSORT)
+
+/* Additional flags definitions, for MinGW specific extensions.
+ */
+#define GLOB_CASEMATCH   __GLOB_FLAG__(CASEMATCH)
+
+BEGIN_C_DECLS
+/*
+ * Function prototypes.  Formally POSIX mandates:
+ *
+ *  int glob( const char *, int, int (*)( const char *, int ), glob_t * );
+ *  void globfree( glob_t * );
+ *
+ * However, our actual function implementations are provided via this
+ * pair of reserved function names...
+ */
+int __mingw_glob( const char *, int, int (*)( const char *, int ), glob_t * );
+void __mingw_globfree( glob_t * );
+
+/* ...to which the standard names are then mapped as aliases,
+ * via inline function expansion.
+ */
+#define GLOB_INLINE  static __inline__ __attribute__((__always_inline__))
+
+GLOB_INLINE int glob
+( const char *__pattern, int __flags, int (*__errfunc)(), glob_t *__data )
+{ return __mingw_glob( __pattern, __flags, __errfunc, __data ); }
+
+GLOB_INLINE void globfree( glob_t *__data )
+{ return __mingw_globfree( __data ); }
+
+END_C_DECLS
+
+/* Manifest definitions for the possible status values
+ * which glob() may return.
+ */
+#define GLOB_SUCCESS   (0)
+#define GLOB_ABORTED   (1)
+#define GLOB_NOMATCH   (2)
+#define GLOB_NOSPACE   (3)
+
+#endif /* ! RC_INVOKED */
+#endif /* ! defined _GLOB_H */
index 532da9c..ac0a764 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @file CRTglob.c
- * @copy 2012 MinGW.org project
+ * Copyright (C) 2004, 2012, MinGW.org Project
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-
-/*
- * Include this object file to set _CRT_glob to a state that will
- * turn on command line globbing by default.  NOTE: _CRT_glob has a default
- * state of on.  Specify CRT_noglob.o to turn off globbing by default.
+/* ---------------------------------------------------------------------------
+ *
+ * This file is implicitly linked into any executable using libmingwex.a; it
+ * defines the global variable, _CRT_glob, with a default value which enables
+ * globbing of command line arguments, using mingw32's basic implementation
+ * of the POSIX globbing algorithm.  To override this default behaviour:
+ *
+ * - Explicitly include the object module CRTnoglob.o in your link command;
+ *   this defines _CRT_glob = 0, so disabling command line globbing.
+ *
+ * - Alternatively, explicitly define _CRT_glob as a global variable in one
+ *   of your own source modules, assigning any of the related values defined
+ *   in _mingw.h, to enable alternative globbing algorithms and/or features,
+ *   or zero to disable globbing.
  *
- * To use this object include the object file in your link command:
- * gcc -o foo.exe foo.o CRTglob.o
  */
+#include <_mingw.h>
 
-int _CRT_glob = -1;
+int _CRT_glob = __CRT_GLOB_USE_MINGW__;
index 01230bd..d452763 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @file init.c
- * @copy 2012 MinGW.org project
+ * Copyright (C) 2012 MinGW.org project
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  * DEALINGS IN THE SOFTWARE.
  */
 
-/*
- * Code to initialize standard file handles and command line arguments.
- * This file is #included in both crt1.c and dllcrt1.c.
+/* ---------------------------------------------------------------------------
+ * Implements the runtime initialisation code to populate the argument
+ * vector, which will subsequently be passed to the main() function.
+ *
+ * Written by Rob Savoye <rob@cygnus.com>
+ * Updated by Mumit Khan <khan@xraylith.wisc.edu>,
+ *   Earnie Boyd <earnie@users.sourceforge.net>, and
+ *   Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 1997, 1998, 1999, 2004, 2012, MinGW Project
+ *
+ * This file was formerly #included by both crt1.c and dllcrt1.c;
+ * it is now used only by crt1.c
+ *
+ * ---------------------------------------------------------------------------
  */
+#include <glob.h>
 
-/*
- * Access to a standard 'main'-like argument count and list. Also included
+/* Access to a standard 'main'-like argument count and list. Also included
  * is a table of environment variables.
  */
-int _argc = 0;
-char **_argv = 0;
+int     _argc = 0;
+char ** _argv = 0;
 
 /* NOTE: Thanks to Pedro A. Aranda Gutiirrez <paag@tid.es> for pointing
  * this out to me. GetMainArgs (used below) takes a fourth argument
@@ -47,28 +58,236 @@ char **_argv = 0;
  *  int _CRT_glob = 0;
  */
 extern int _CRT_glob;
+#define _CRT_GLOB_OPT  _CRT_glob & __CRT_GLOB_USE_MSVCRT__
 
-typedef struct {
-  int newmode;
-} _startupinfo;
-extern void __getmainargs (int *, char ***, char ***, int, _startupinfo *);
+#define ARGV_INLINE  static __inline__ __attribute__((__always_inline__))
 
-/*
- * Initialize the _argc, _argv and environ variables.
- */
-static void
-_mingw32_init_mainargs ()
+typedef struct { int newmode; } _startupinfo;
+extern void __getmainargs( int *, char ***, char ***, int, _startupinfo * );
+
+#define ARGV_ESCAPE     __CRT_GLOB_ESCAPE_CHAR__
+#define ARGV_SQUOTE     __CRT_GLOB_USE_SINGLE_QUOTE__
+#define ARGV_NOGROUP    __CRT_GLOB_BRACKET_GROUPS__
+
+ARGV_INLINE
+int do_glob( const char *pattern, int flags, int (*errfn)(), glob_t *gl_buf )
+{
+  /* Helper used by the MinGW replacement command line globbing handler,
+   * to invoke the glob() function, while ensuring that the GLOB_APPEND
+   * option is enabled at the appropriate time.
+   */
+  if( gl_buf->gl_pathc > 0 )
+    flags |= GLOB_APPEND;
+  return __mingw_glob( pattern, flags, errfn, gl_buf );
+}
+
+ARGV_INLINE
+char *backslash( int count, char *buf )
+{
+  /* Helper used by the MinGW replacement command line globbing handler,
+   * to provide appropriate handling of backslashes while preparing the
+   * command line arguments for globbing.
+   */
+  while( count-- )
+    *buf++ = '\\';
+  return buf;
+}
+
+ARGV_INLINE
+char *unquote( int quote, int altquote, int escape, int *state, char *buf )
 {
-  /* The environ variable is provided directly in stdlib.h through
-   * a dll function call. */
-  char **dummy_environ;
-  _startupinfo start_info;
-  start_info.newmode = 0;
-
-  /*
-   * Microsoft's runtime provides a function for doing just that.
+  /* Helper used by the MinGW replacement command line globbing handler,
+   * to provide a single level of reduction for balanced quotation marks,
+   * while preparing the command line arguments for globbing.
    */
-  (void) __getmainargs (&_argc, &_argv, &dummy_environ, _CRT_glob, 
-                        &start_info);
+  buf = backslash( escape >> 1, buf );
+  if( (escape & 1) || (*state == altquote) )
+    /*
+     * In this case, the quotation mark is to be interpreted as a literal,
+     * and is NOT a candidate for reduction...
+     */
+    *buf++ = quote;
+  else
+    /* ...while this is the more usual case, of a quotation mark used to
+     * delimit a single argument; it must be reduced.
+     */
+    *state ^= quote;
+  return buf;
 }
 
+ARGV_INLINE
+void __mingw_setargv( const char *cmdline )
+{
+  /* Implementation of the MinGW replacement command line interpreter.
+   */
+  char cmdbuf[1 + strlen( cmdline ) << 1];
+  int c, gotarg = 0, quoted = 0, bracket = 0, bslash = 0;
+  char *argptr = cmdbuf; const char *cmdptr = cmdline;
+  glob_t gl_argv;
+
+  /* Capture any non-default globbing options, which the user may have
+   * specified via a custom setting for _CRT_glob.
+   */
+  int gl_opts = GLOB_NOCHECK;
+  if( _CRT_glob & __CRT_GLOB_CASE_SENSITIVE__ )
+    gl_opts |= GLOB_CASEMATCH;
+
+  /* We explicitly DO NOT use the GLOB_DOOFFS capability; ensure that
+   * the associated field, in the glob_t structure, is initialised to
+   * correctly reflect this.
+   */
+  gl_argv.gl_offs = 0;
+
+  /* Scan the command line, and prepare it for globbing.
+   */
+  while( c = *cmdptr++ )
+  {
+    /* Got a character to process...
+     */
+    switch( c )
+    {
+      /* Specific characters, which serve as globbing tokens,
+       * need special handling.
+       */
+      case '\\':
+       /* We don't (yet) know if this is a literal backslash,
+        * (directory separator), or an escape for a following
+        * quote character; just note its presence, until we
+        * have looked far enough ahead to decide.
+        */
+       ++bslash;
+       break;
+
+      case '[':
+       /* POSIX defines this as a globbing token, (introducing
+        * a character group); we don't support this by default,
+        * so defeat it, unless the extended behaviour has been
+        * requested by the user.
+        */
+       bracket = (_CRT_glob & ARGV_NOGROUP) ? 0 : ARGV_NOGROUP;
+
+      case '*':
+      case '?':
+       /* These standard globbing tokens...
+        */
+      case ARGV_ESCAPE:
+       /* ...and the escape character itself, need to be escaped
+        * when they appear in any context in which they should be
+        * interpreted literally, rather than globbed.
+        */
+       argptr = backslash( bslash, argptr );
+       if( quoted || (bracket == ARGV_NOGROUP) || (c == ARGV_ESCAPE) )
+         *argptr++ = ARGV_ESCAPE;
+       bracket = bslash = 0;
+       *argptr++ = c;
+       break;
+
+      case '"':
+       /* The double quote always acts as an argument quoting
+        * character, (unless escaped); handle it accordingly.
+        */
+       argptr = unquote( c, '\'', bslash, &quoted, argptr );
+       gotarg = 1; bslash = 0;
+       break;
+
+      case '\'':
+       /* POSIX also defines the single quote as a quoting
+        * character, but MS-Windows does not; we offer this
+        * extended handling...
+        */
+       if( _CRT_glob & ARGV_SQUOTE )
+       {
+         /* ...only when the user has explicitly enabled the
+          * POSIX compatible extended quoting option.
+          */
+         argptr = unquote( c, '"', bslash, &quoted, argptr );
+         gotarg = 1; bslash = 0;
+         break;
+       }
+
+      default:
+       /* With one exception, any other character is handled
+        * literally, after flushing out any pending backslashes.
+        */
+       argptr = backslash( bslash, argptr );
+       if( (quoted == 0) && isspace( c ) )
+       {
+         /* The one exception is any white space character,
+          * when it is not contained within quotes; this acts
+          * as an argument separator, (or is simply discarded
+          * if there is no argument already collected)...
+          */
+         if( gotarg || (argptr > cmdbuf) )
+         {
+           /* ...so, when there is a argument pending, we may
+            * now add it to the globbed argument vector.
+            */
+           *argptr = '\0';
+           do_glob( argptr = cmdbuf, gl_opts, NULL, &gl_argv );
+           gotarg = 0;
+         }
+       }
+       else
+         /* In every other case, we simply collect the current
+          * literal character into the next pending argument.
+          */
+         *argptr++ = c;
+
+       /* Irrespective of how we handled the current character,
+        * we can be certain that there are no pending backslashes
+        * by the time we get to here.
+        */
+       bslash = 0;
+    }
+  }
+  /* Finally, when we've run out of command line characters to process,
+   * flush out any final pending backslashes, ...
+   */
+  argptr = backslash( bslash, argptr );
+  if( gotarg || (argptr > cmdbuf) )
+  {
+    /* ...and add any final pending argument to the globbed vector.
+     */
+    *argptr = '\0';
+    do_glob( argptr = cmdbuf, gl_opts, NULL, &gl_argv );
+  }
+  /* ...and store the resultant globbed vector into the "argc" and "argv"
+   * variables to be passed to main(); note that this allows us to safely
+   * discard our working glob_t structure, but we MUST NOT globfree() it,
+   * as that would destroy the content of "argv".
+   */
+  _argc = gl_argv.gl_pathc;
+  _argv = gl_argv.gl_pathv;
+}
+
+static void _mingw32_init_mainargs()
+{
+  /* Initialise the _argc, _argv and environ variables.
+   *
+   * FIXME: consider renaming to _mingw_init_mainargs?
+   * (Both here, and in crt1.c)?
+   */
+  if( (_CRT_glob & __CRT_GLOB_USE_MINGW__) == 0 )
+  {
+    /* This is the old start-up mechanism, in which we use a start-up
+     * hook provided by Microsoft's runtime library to initialise the
+     * argument and environment vectors.
+     *
+     * Note that the preferred method for accessing the environment
+     * vector is via a direct pointer retrieved from the runtime DLL,
+     * using a system call declared in stdlib.h; thus, we don't need
+     * to preserve the pointer returned by the start-up hook, so we
+     * may simply capture it locally, and subsequently discard it.
+     */
+    char **dummy_envp; _startupinfo start_info = { 0 };
+    __getmainargs( &_argc, &_argv, &dummy_envp, _CRT_GLOB_OPT, &start_info );
+  }
+  else
+    /* Here, we implement a new, more POSIX compatible mechanism,
+     * for initialising the argument vector; note that we delegate
+     * to the previously defined inline function, which avoids the
+     * broken globbing behaviour of some more recent versions of
+     * MSVCRT.DLL
+     */
+    __mingw_setargv( GetCommandLine() );
+}
diff --git a/src/libcrt/misc/glob.c b/src/libcrt/misc/glob.c
new file mode 100644 (file)
index 0000000..f4d04ae
--- /dev/null
@@ -0,0 +1,1029 @@
+/**
+ * @file glob.c
+ * Copyright (C) 2012 MinGW.org project.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, this permission notice, and the following
+ * disclaimer shall be included in all copies or substantial portions of
+ * the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/* ---------------------------------------------------------------------------
+ * MinGW implementation of (approximately) POSIX conforming glob() and
+ * globfree() API functions.
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2011, 2012, MinGW Project.
+ * ---------------------------------------------------------------------------
+ */
+#include <glob.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <errno.h>
+
+enum {
+  /* Extend the flags offset enumeration, beyond the user visible
+   * high water mark, to accommodate some additional flags which are
+   * required for private use by the implementation.
+   */
+  __GLOB_DIRONLY_OFFSET = __GLOB_FLAG_OFFSET_HIGH_WATER_MARK,
+  __GLOB_PERIOD_PRIVATE_OFFSET,
+  /*
+   * For congruency, set a new high water mark above the private data
+   * range, (which we don't otherwise use).
+   */
+  __GLOB_PRIVATE_FLAGS_HIGH_WATER_MARK
+};
+
+#define GLOB_DIRONLY   __GLOB_FLAG__(DIRONLY)
+#ifndef GLOB_PERIOD
+# define GLOB_PERIOD   __GLOB_FLAG__(PERIOD_PRIVATE)
+#endif
+
+#ifndef GLOB_INLINE
+# define GLOB_INLINE   static __inline__ __attribute__((__always_inline__))
+#endif
+
+#define GLOB_HARD_ESC  __CRT_GLOB_ESCAPE_CHAR__
+
+#if defined _WIN32 || defined __MS_DOS__
+/*
+ * For the Microsoft platforms, we treat '\' and '/' interchangeably
+ * as directory separator characters...
+ */
+#define GLOB_DIRSEP            ('\\')
+# define glob_is_dirsep( c )   (((c) == ('/')) || ((c) == GLOB_DIRSEP))
+/*
+ * ...and we use the ASCII ESC code as our escape character.
+ */
+static int glob_escape_char = GLOB_HARD_ESC;
+
+GLOB_INLINE char *glob_strdup( const char *pattern )
+{
+  /* An inline wrapper around the standard strdup() function;
+   * this strips instances of the GLOB_HARD_ESC character, which
+   * have not themselves been escaped, from the strdup()ed copy.
+   */
+  char buf[1 + strlen( pattern )];
+  char *copy = buf; const char *origin = pattern;
+  do { if( *origin == GLOB_HARD_ESC ) ++origin;
+       *copy++ = *origin;
+     } while( *origin++ );
+  return strdup( buf );
+}
+
+#else
+/* Otherwise, we assume only the POSIX standard '/'...
+ */
+#define GLOB_DIRSEP            ('/')
+# define glob_is_dirsep( c )   ((c) == GLOB_DIRSEP)
+/*
+ * ...and we interpret '\', as specified by POSIX, as
+ * the escape character.
+ */
+static int glob_escape_char = '\\';
+
+#define glob_strdup    strdup
+#endif
+
+static int is_glob_pattern( const char *pattern, int flags )
+{
+  /* Check if "pattern" represents a globbing pattern
+   * with included wild card characters.
+   */
+  register const char *p;
+  register int c;
+
+  /* Proceed only if specified pattern is not NULL...
+   */
+  if( (p = pattern) != NULL )
+  {
+    /* ...initially, with no bracketted character set open;
+     * (none can be, because we haven't yet had any opportunity
+     * to see the opening bracket).
+     */
+    int bracket = 0;
+
+    /* Check each character in "pattern" in turn...
+     */
+    while( (c = *p++) != '\0' )
+    {
+      /* ...proceeding since we have not yet reached the NUL terminator.
+       */
+      if(  ((flags & GLOB_NOESCAPE) == 0)
+      &&  (c == glob_escape_char) && (*p++ == '\0')  )
+       /*
+        * We found an escape character, (and the escape mechanism has
+        * not been disabled), but there is no following character to
+        * escape; it may be malformed, but this certainly doesn't look
+        * like a candidate globbing pattern.
+        */
+       return 0;
+
+      else if( bracket == 0 )
+      {
+       /* Still outside of any bracketted character set...
+        */
+       if( (c == '*') || (c == '?') )
+         /*
+          * ...either of these makes "pattern" an explicit
+          * globbing pattern...
+          */
+         return 1;
+
+       if( c == '[' )
+         /*
+          * ...while this marks the start of a bracketted
+          * character set.
+          */
+         bracket++;
+      }
+
+      else if( (bracket > 1) && (c == ']') )
+       /*
+        * Within a bracketted character set, where it is not
+        * the first character, ']' marks the end of the set,
+        * making "pattern" a globbing pattern.
+        */
+       return 1;
+
+      else if( c != '!' )
+       /*
+        * Also within a bracketted character set, '!' is special
+        * when the first character, and shouldn't be counted; note
+        * that it should be counted when not the first character,
+        * but the short count resulting from ignoring it doesn't
+        * affect our desired outcome.
+        */
+       bracket++;
+    }
+  }
+
+  /* If we get to here, then we ran off the end of "pattern" without
+   * identifying it as a globbing pattern.
+   */
+  return 0;
+}
+
+static const char *glob_set_adjusted( const char *pattern, int flags )
+{
+  /* Adjust the globbing pattern pointer, to make it refer to the
+   * next character (if any) following a character set specification;
+   * this adjustment is required when pattern matching is to resume
+   * after matching a set specification, irrespective of whether the
+   * match was successful or not; (a failed match is the desired
+   * outcome for an excluded character set).
+   */
+  register const char *p = pattern;
+
+  /* We need to move the pointer forward, until we find the ']'
+   * which marks the end of the set specification.
+   */
+  while( *p != ']' )
+  {
+    /* We haven't found it yet; advance by one character...
+     */
+    if( (*p == glob_escape_char) && ((flags & GLOB_NOESCAPE) == 0) )
+      /*
+       * ...or maybe even two, when we identify a need to
+       * step over any character which has been escaped...
+       */
+      p++;
+
+    if( *p++ == '\0' )
+      /*
+       * ...but if we find a NUL on the way, then the pattern
+       * is malformed, so we return NULL to report a bad match.
+       */
+      return NULL;
+  }
+  /* We found the expected ']'; return a pointer to the NEXT
+   * character, (which may be ANYTHING; even NUL is okay).
+   */
+  return ++p;
+}
+
+static const char *glob_in_set( const char *set, int test, int flags )
+{
+  /* Check if the single character "test" is present in the set
+   * of characters represented by "set", (a specification of the
+   * form "[SET]", or "[!SET]" in the case of an excluded set).
+   *
+   * On entry, "set" always points to the first character in the
+   * set to be tested, i.e. the character FOLLOWING the '[' which
+   * opens an inclusive set, or FOLLOWING the initial '!' which
+   * marks the set as exclusive.
+   *
+   * Matching is ALWAYS performed as if checking an inclusive set;
+   * return value is a pointer to the globbing pattern character
+   * following the closing ']' of "set", when "test" IS in "set",
+   * or NULL when it is not.  Caller performing an inclusive match
+   * should handle NULL as a failed match, and non-NULL as success.
+   * Caller performing an exclusive match should handle non-NULL as
+   * a failed match, with NULL indicating success, and should call
+   * glob_set_adjusted() before resuming pattern matching in the
+   * case of a successful match.
+   */
+  register int c, lastc;
+  if( ((lastc = *set) == ']') || (lastc == '-') )
+  {
+    /* This is the special case of matching ']' or '-' as the
+     * first character in the set, where it must match literally...
+     */
+    if( lastc == test )
+      /*
+       * ...which it does, so immediately report it so.
+       */
+      return glob_set_adjusted( ++set, flags );
+
+    /* ...otherwise we didn't match this special case of ']' or '-',
+     * so we simply ignore this special set entry, thus handling it
+     * as an implicitly escaped literal which has not been matched.
+     */
+    set++;
+  }
+  while( (c = *set++) != ']' )
+  {
+    /* We are still scanning the set, and have not yet reached the
+     * closing ']' sentinel character.
+     */
+    if( (c == '-') && (*set != ']') && ((c = *set++) != '\0') )
+    {
+      /* Since the current character is a '-', and is not immediately
+       * followed by the set's closing sentinel, nor is it at the end
+       * of the (malformed) pattern, it specifies a character range,
+       * running from the last character scanned...
+       */
+      while( lastc < c )
+      {
+       /* ...in incremental collating sequence order, to the next
+        * character following the '-'...
+        */
+       if( lastc++ == test )
+         /*
+          * ...returning immediately on a successful match...
+          */
+         return glob_set_adjusted( set, flags );
+      }
+      while( lastc > c )
+      {
+       /* ...or failing that, consider the possibility that the
+        * range may have been specified in decrementing collating
+        * sequence order...
+        */
+       if( lastc-- == test )
+         /*
+          * ...once again, return immediately on a successful match.
+          */
+         return glob_set_adjusted( set, flags );
+      }
+    }
+
+    /* Within a set, the escape character is to be parsed as
+     * a literal; this should be unnecessary...
+    if( (c == glob_escape_char) && ((flags & GLOB_NOESCAPE) == 0) )
+      c = *set++;
+     */
+
+    if( (c == '\0')
+      /*
+       * This is a malformed set; (not closed before the end of
+       * the pattern)...
+       */
+    ||  glob_is_dirsep( c )  )
+      /*
+       * ...or it attempts to explicitly match a directory separator,
+       * which is invalid in this context.  We MUST fail it, in either
+       * case, reporting a mismatch.
+       */
+      return NULL;
+
+    if( c == test )
+      /*
+       * We found the test character within the set; adjust the pattern
+       * reference, to resume after the end of the set, and return the
+       * successful match.
+       */
+      return glob_set_adjusted( set, flags );
+
+    /* If we get to here, we haven't yet found the test character within
+     * this set; remember the character within the set which we just tried
+     * to match, as it may represent the start of a character range, then
+     * continue the scan, until we exhaust the set.
+     */
+    lastc = c;
+  }
+  /* Having exhausted the set, without finding a match, we return NULL
+   * to indicate that the test character was NOT found in the set.
+   */
+  return NULL;
+}
+
+GLOB_INLINE int glob_case_match( int flags, int check, int match )
+{
+  /* Local helper function, used to facilitate the case insensitive
+   * glob character matching appropriate for MS-Windows systems.
+   */
+  return (flags & GLOB_CASEMATCH) ? check - match
+    : tolower( check ) - tolower( match );
+}
+
+static int glob_strcmp( const char *pattern, const char *text, int flags )
+{
+  /* Compare "text" to a specified globbing "pattern" using semantics
+   * comparable to "strcmp()"; returns zero for a complete match, else
+   * non-zero for a mismatch.
+   *
+   * Within "pattern":
+   *   '?'     matches any one character in "text" (except '\0')
+   *   '*'     matches any sequence of zero or more characters in "text"
+   *   [SET]   matches any one character in "text" which is also in "SET"
+   *   [!SET]  matches any one character in "text" which is NOT in "SET"
+   */
+  register const char *p = pattern, *t = text;
+  register int c;
+
+  if( (*t == '.') && (*p != '.') && ((flags & GLOB_PERIOD) == 0) )
+    /*
+     * The special GNU extension allowing wild cards to match a period
+     * as first character is NOT in effect; "text" DOES have an initial
+     * period character AND "pattern" DOES NOT match it EXPLICITLY, so
+     * this comparison must report a MISMATCH.
+     */
+    return *p - *t;
+
+  /* Attempt to match "pattern", character by character...
+   */
+  while( (c = *p++) != '\0' )
+  {
+    /* ...so long as we haven't exhausted it...
+     */
+    switch( c )
+    {
+      case '?':
+       /* Match any one character...
+        */
+       if( *t++ == '\0' )
+         /* ...but when there isn't one left to be matched,
+          * then we must report a mismatch.
+          */
+         return '?';
+       break;
+
+      case '*':
+       /* Match any sequence of zero or more characters...
+        */
+       while( *p == '*' )
+         /*
+          * ...ignoring any repeated '*' in the pattern...
+          */
+         p++;
+
+       /* ...and if we've exhausted the pattern...
+        */
+       if( *p == '\0' )
+         /*
+          * ...then we simply match all remaining characters,
+          * to the end of "text", so we may return immediately,
+          * reporting a successful match.
+          */
+         return 0;
+
+       /* When we haven't exhausted the pattern, then we may
+        * attempt to recursively match the remainder of the
+        * pattern to some terminal substring of "text"; we do
+        * this iteratively, stepping over as many characters
+        * of "text" as necessary, (and which thus match the '*'
+        * in "pattern"), until we either find the start of this
+        * matching substring, or we exhaust "text" without any
+        * possible match...
+        */
+       do { c = glob_strcmp( p, t, flags | GLOB_PERIOD );
+          } while( (c != 0) && (*t++ != '\0') );
+       /*
+        * ...and ultimately, we return the result of this
+        * recursive attempt to find a match.
+        */
+       return c;
+
+      case '[':
+       /* Here we need to match (or not match) exactly one
+        * character from the candidate text with any one of
+        * a set of characters in the pattern...
+        */
+       if( (c = *t++) == '\0' )
+         /*
+          * ...but, we must return a mismatch if there is no
+          * candidate character left to match.
+          */
+         return '[';
+
+       if( *p == '!' )
+       {
+         /* Match any one character which is NOT in the SET
+          * specified by [!SET].
+          */
+         if( glob_in_set( ++p, c, flags ) == NULL )
+         {
+           if( *p == ']' )
+             p++;
+           p = glob_set_adjusted( p, flags );
+         }
+       }
+       else
+       { /* Match any one character which IS in the SET
+          * specified by [SET].
+          */
+         p = glob_in_set( p, c, flags );
+       }
+       if( p == NULL )
+         /*
+          * The character under test didn't satisfy the SET
+          * matching criterion; return as unmatched.
+          */
+         return ']';
+       break;
+
+      default:
+       /* The escape character cannot be handled as a regular
+        * switch case, because the escape character is specified
+        * as a variable, (to better support Microsoft nuisances).
+        * The escape mechanism may have been disabled within the
+        * glob() call...
+        */
+       if(  ((flags & GLOB_NOESCAPE) == 0)
+         /*
+          * ...but when it is active, and we find an escape
+          * character without exhausting the pattern...
+          */
+       && (c == glob_escape_char) && ((c = *p) != 0)  )
+         /*
+          * ...then we handle the escaped character here, as
+          * a literal, and step over it, within the pattern.
+          */
+         ++p;
+
+       /* When we get to here, a successful match requires that
+        * the current pattern character "c" is an exact literal
+        * match for the next available character "t", if any,
+        * in the candidate text string...
+        */
+       if( (*t == '\0') || (glob_case_match( flags, c, *t ) != 0) )
+         /*
+          * ...otherwise we return a mismatch.
+          */
+         return c - *t;
+
+       /* No mismatch yet; proceed to test the following character
+        * within the candidate text string.
+        */
+       t++;
+    }
+  }
+  /* When we've exhausted the pattern, then this final check will return
+   * a match if we've simultaneously exhausted the candidate text string,
+   * or a mismatch otherwise.
+   */
+  return c - *t;
+}
+
+#ifdef DT_DIR
+/*
+ * When this is defined, we assume that we can safely interrogate
+ * the d_type member of a globbed dirent structure, to determine if
+ * the referenced directory entry is itself a subdirectory entry.
+ */
+# define GLOB_ISDIR( ent )     ((ent)->d_type == DT_DIR)
+
+#else
+/* We can't simply check for (ent)->d_type == DT_DIR, so we must
+ * use stat() to identify subdirectory entries.
+ */
+# include <sys/stat.h>
+
+  GLOB_INLINE
+  int GLOB_ISDIR( const struct *dirent ent )
+  {
+    struct stat entinfo;
+    if( stat( ent->d_name, &entinfo ) == 0 )
+      return S_ISDIR( entinfo.st_mode );
+    return 0;
+  }
+#endif
+
+#if _DIRENT_HAVE_D_NAMLEN
+/*
+ * Our DIRENT implementation provides a direct indication
+ * of the length of the file system entity name returned by
+ * the last readdir operation...
+ */
+# define D_NAMLEN( entry )  ((entry)->d_namlen)
+#else
+/*
+ * ...otherwise, we have to scan for it.
+ */
+# define D_NAMLEN( entry )  (strlen( (entry)->d_name ))
+#endif
+
+static int glob_initialise( glob_t *gl_data )
+{
+  /* Helper routine to initialise a glob_t structure
+   * for first time use.
+   */
+  if( gl_data != NULL )
+  {
+    /* Caller gave us a valid pointer to what we assume has been
+     * defined as a glob_t structure; allocate space on the heap,
+     * for storage of the globbed paths vector...
+     */
+    int entries = gl_data->gl_offs + 1;
+    if( (gl_data->gl_pathv = malloc( entries * sizeof( char ** ) )) == NULL )
+      /*
+       * ...bailing out, if insufficient free heap memory.
+       */
+      return GLOB_NOSPACE;
+
+    /* On successful allocation, clear the initial path count...
+     */
+    gl_data->gl_pathc = 0;
+    while( entries > 0 )
+      /*
+       * ...and place a NULL pointer in each allocated slot...
+       */
+      gl_data->gl_pathv[--entries] = NULL;
+  }
+  /* ...ultimately returning a successful initialisation status.
+   */
+  return GLOB_SUCCESS;
+}
+
+GLOB_INLINE int glob_expand( glob_t *gl_buf )
+{
+  /* Inline helper to compute the new size allocation required
+   * for buf->gl_pathv, prior to adding a new glob result.
+   */
+  return ((2 + gl_buf->gl_pathc + gl_buf->gl_offs) * sizeof( char ** ));
+}
+
+static int glob_store_entry( char *path, glob_t *gl_buf )
+{
+  /* Local helper routine to add a single path name entity
+   * to the globbed path vector, after first expanding the
+   * allocated memory space to accommodate it.
+   */
+  char **pathv;
+  if(  (path != NULL)  &&  (gl_buf != NULL)
+  &&  ((pathv = realloc( gl_buf->gl_pathv, glob_expand( gl_buf ))) != NULL)  )
+  {
+    /* Memory expansion was successful; store the new path name
+     * in place of the former NULL pointer at the end of the old
+     * vector...
+     */
+    gl_buf->gl_pathv = pathv;
+    gl_buf->gl_pathv[gl_buf->gl_offs + gl_buf->gl_pathc++] = path;
+    /*
+     * ...then place a further NULL pointer into the newly allocated
+     * slot, to mark the new end of the vector...
+     */
+    gl_buf->gl_pathv[gl_buf->gl_offs + gl_buf->gl_pathc] = NULL;
+    /*
+     * ...before returning a successful completion status.
+     */
+    return GLOB_SUCCESS;
+  }
+  /* If we get to here, then we were unsuccessful.
+   */
+  return GLOB_ABORTED;
+}
+
+struct glob_collator
+{
+  /* A private data structure, used to keep an ordered collection
+   * of globbed path names in collated sequence within a (possibly
+   * unbalanced) binary tree.
+   */
+  struct glob_collator *prev;
+  struct glob_collator *next;
+  char                 *entry;
+};
+
+GLOB_INLINE struct glob_collator
+*glob_collate_entry( struct glob_collator *collator, char *entry, int flags )
+{
+  /* Inline helper function to construct a binary tree representation
+   * of a collated collection of globbed path name entities.
+   */
+  int seq = 0;
+  struct glob_collator *ref = collator;
+  struct glob_collator *lastref = collator;
+  while( ref != NULL )
+  {
+    /* Walk the tree, to find the leaf node representing the insertion
+     * point, in correctly collated sequence order, for the new entry,
+     * noting whether we must insert the new entry before or after the
+     * original entry at that leaf.
+     */
+    lastref = ref;
+    if( flags & GLOB_CASEMATCH )
+      seq = strcoll( entry, ref->entry );
+    else
+      seq = stricoll( entry, ref->entry );
+    ref = (seq > 0) ? ref->next : ref->prev;
+  }
+  /* Allocate storage for a new leaf node, and if successful...
+   */
+  if( (ref = malloc( sizeof( struct glob_collator ))) != NULL )
+  {
+    /* ...place the new entry on this new leaf...
+     */
+    ref->entry = entry;
+    ref->prev = ref->next = NULL;
+
+    /* ...and attach it to the tree...
+     */
+    if( lastref != NULL )
+    {
+      /* ...either...
+       */
+      if( seq > 0 )
+       /*
+        * ...after...
+        */
+       lastref->next = ref;
+
+      else
+       /* ...or before...
+        */
+       lastref->prev = ref;
+
+      /* ...the original leaf,as appropriate. */
+    }
+  }
+  /* When done, return a pointer to the root node of the resultant tree.
+   */
+  return (collator == NULL) ? ref : collator;
+}
+
+static void
+glob_store_collated_entries( struct glob_collator *collator, glob_t *gl_buf )
+{
+  /* A local helper routine to store a collated collection of globbed
+   * path name entities into the path vector within a glob_t structure;
+   * it performs a recursive inorder traversal of a glob_collator tree,
+   * deleting it leaf by leaf, branch by branch, as it stores the path
+   * data contained thereon.
+   */
+  if( collator->prev != NULL )
+    /*
+     * Recurse into the sub-tree of entries which collate before the
+     * root of the current (sub-)tree.
+     */
+    glob_store_collated_entries( collator->prev, gl_buf );
+
+  /* Store the path name entry at the root of the current (sub-)tree.
+   */
+  glob_store_entry( collator->entry, gl_buf );
+
+  if( collator->next != NULL )
+    /*
+     * Recurse into the sub-tree of entries which collate after the
+     * root of the current (sub-)tree.
+     */
+    glob_store_collated_entries( collator->next, gl_buf );
+
+  /* Finally, delete the root node of the current (sub-)tree; since
+   * recursion visits every node of the tree, ultimately considering
+   * each leaf as a sub-tree of only one node, unwinding recursion
+   * will cause this to delete the entire tree.
+   */
+  free( collator );
+}
+
+static int
+glob_match( const char *pattern, int flags, int (*errfn)(), glob_t *gl_buf )
+{
+  /* Local helper function; it provides the backbone of the glob()
+   * implementation, recursively decomposing the pattern into separate
+   * globbable path components, to collect the union of all possible
+   * matches to the pattern, in all possible matching directories.
+   */
+  glob_t local_gl_buf;
+  int status = GLOB_SUCCESS;
+
+  /* Begin by separating out any path prefix from the glob pattern.
+   */
+  char dirbuf[1 + strlen( pattern )];
+  const char *dir = dirname( memcpy( dirbuf, pattern, sizeof( dirbuf )) );
+  char **dirp, preferred_dirsep = GLOB_DIRSEP;
+
+  /* Initialise a temporary local glob_t structure, to capture the
+   * intermediate results at the current level of recursion...
+   */
+  local_gl_buf.gl_offs = 0;
+  if( (status = glob_initialise( &local_gl_buf )) != GLOB_SUCCESS )
+    /*
+     * ...bailing out if unsuccessful.
+     */
+    return status;
+
+  /* Check if there are any globbing tokens in the path prefix...
+   */
+  if( is_glob_pattern( dir, flags ) )
+    /*
+     * ...and recurse to identify all possible matching prefixes,
+     * as may be necessary...
+     */
+    status = glob_match( dir, flags | GLOB_DIRONLY, errfn, &local_gl_buf );
+
+  else
+    /* ...or simply store the current prefix, if not.
+     */
+    status = glob_store_entry( glob_strdup( dir ), &local_gl_buf );
+
+  /* Check nothing has gone wrong, so far...
+   */
+  if( status != GLOB_SUCCESS )
+    /*
+     * ...and bail out if necessary.
+     */
+    return status;
+
+  /* The original "pattern" argument may have included a path name
+   * prefix, which we used "dirname()" to isolate.  If there was no
+   * such prefix, then "dirname()" would have reported an effective
+   * prefix which is identically equal to "."; however, this would
+   * also be the case if the prefix was "./" (or ".\\" in the case
+   * of a WIN32 host).  Thus, we may deduce that...
+   */
+  if( glob_is_dirsep( pattern[1] ) || (strcmp( dir, "." ) != 0) )
+  {
+    /* ...when the prefix is not reported as ".", or even if it is
+     * but the original pattern had "./" (or ".\\") as the prefix,
+     * then we must adjust to identify the effective pattern with
+     * its original prefix stripped away...
+     */
+    const char *tail = pattern + strlen( dir );
+    while( (tail > pattern) && ! glob_is_dirsep( *tail ) )
+      --tail;
+    while( glob_is_dirsep( *tail ) )
+      preferred_dirsep = *tail++;
+    pattern = tail;
+  }
+
+  else
+    /* ...otherwise, we simply note that there was no prefix.
+     */
+    dir = NULL;
+
+  /* We now have a globbed list of prefix directories, returned from
+   * recursive processing, in local_gl_buf.gl_pathv, and we also have
+   * a separate pattern which we may attempt to match in each of them;
+   * at the outset, we have yet to match this pattern to anything.
+   */
+  status = GLOB_NOMATCH;
+  for( dirp = local_gl_buf.gl_pathv; *dirp != NULL; free( *dirp++ ) )
+  {
+    /* Provided an earlier cycle hasn't scheduled an abort...
+     */
+    if( status != GLOB_ABORTED )
+    {
+      /* ...take each candidate directory in turn, and prepare
+       * to collate any matched entities within it...
+       */
+      struct glob_collator *collator = NULL;
+
+      /* ...attempt to open the current candidate directory...
+       */
+      DIR *dp;
+      if( (dp = opendir( *dirp )) != NULL )
+      {
+       /* ...and when successful, instantiate a dirent structure...
+        */
+       struct dirent *entry;
+       size_t dirlen = (dir == NULL) ? 0 : strlen( *dirp );
+       while( (entry = readdir( dp )) != NULL )
+       {
+         /* ...into which we read each entry from the candidate
+          * directory, in turn, then...
+          */ 
+         if( (((flags & GLOB_DIRONLY) == 0) || GLOB_ISDIR( entry ))
+           /*
+            * ...provided we don't require it to be a subdirectory,
+            * or it actually is one...
+            */
+         && (glob_strcmp( pattern, entry->d_name, flags ) == 0)   )
+         {
+           /* ...and it is a globbed match for the pattern, then
+            * we allocate a temporary local buffer of sufficient
+            * size to assemble the matching path name...
+            */
+           char *found;
+           size_t prefix;
+           size_t matchlen = D_NAMLEN( entry );
+           char matchpath[2 + dirlen + matchlen];
+           if( (prefix = dirlen) > 0 )
+           {
+             /* ...first copying the prefix, if any,
+              * followed by a directory name separator...
+              */
+             memcpy( matchpath, *dirp, dirlen );
+             if( ! glob_is_dirsep( matchpath[prefix - 1] ) )
+               matchpath[prefix++] = preferred_dirsep;
+           }
+           /* ...and append the matching dirent entry.
+            */
+           memcpy( matchpath + prefix, entry->d_name, matchlen + 1 );
+
+           /* Duplicate the content of the temporary buffer to
+            * the heap, for assignment into gl_buf->gl_pathv...
+            */
+           if( (found = glob_strdup( matchpath )) == NULL )
+             /*
+              * ...setting the appropriate error code, in the
+              * event that the heap memory has been exhausted.
+              */
+             status = GLOB_NOSPACE;
+
+           else
+           { /* This glob match has been successfully recorded on
+              * the heap, ready for assignment to gl_buf->gl_pathv;
+              * if this is the first match assigned to this gl_buf,
+              * and we haven't trapped any prior error...
+              */
+             if( status == GLOB_NOMATCH )
+               /*
+                * ...then record this successful match.
+                */
+               status = GLOB_SUCCESS;
+
+             if( (flags & GLOB_NOSORT) == 0 )
+             {
+               /* The results of this glob are to be sorted in
+                * collating sequence order; divert the current
+                * match into the collator.
+                */
+               collator = glob_collate_entry( collator, found, flags );
+             }
+             else
+             { /* Sorting has been suppressed for this glob;
+                * just add the current match directly into the
+                * result vector at gl_buf->gl_pathv.
+                */
+               glob_store_entry( found, gl_buf );
+             }
+           }
+         }
+       }
+       /* When we've processed all of the entries in the current
+        * prefix directory, we may close it.
+        */
+       closedir( dp );
+      }
+      /* In the event of failure to open the candidate prefix directory...
+       */
+      else if( (flags & GLOB_ERR) || ((errfn != NULL) && errfn( *dirp, errno )) )
+       /*
+        * ...and when the caller has set the GLOB_ERR flag, or has provided
+        * an error handler which returns non-zero for the failure condition,
+        * then we schedule an abort.
+        */
+       status = GLOB_ABORTED;
+
+      /* When we diverted the glob results for collation...
+       */
+      if( collator != NULL )
+       /*
+        * ...then we redirect them to gl_buf->gl_pathv now, before we
+        * begin a new cycle, to process any further prefix directories
+        * which may have been identified; note that we do this even if
+        * we scheduled an abort, so that we may return any results we
+        * may have already collected before the error occurred.
+        */
+       glob_store_collated_entries( collator, gl_buf );
+    }
+  }
+  /* Finally, free the memory block allocated for the results vector
+   * in the internal glob buffer, to avoid leaking memory, before we
+   * return the resultant status code.
+   */
+  free( local_gl_buf.gl_pathv );
+  return status;
+}
+
+#define GLOB_INIT      (0x100 << 0)
+#define GLOB_FREE      (0x100 << 1)
+
+GLOB_INLINE int glob_signed( const char *check, const char *magic )
+{
+  /* Inline helper function, used exclusively by the glob_registry()
+   * function, to confirm that the gl_magic field within a glob_t data
+   * structure has been set, to indicate a properly initialised state.
+   */
+  return (check == magic) ? 0 : (check != NULL) ? strcmp( check, magic ) : 1;
+}
+
+static glob_t *glob_registry( int request, glob_t *gl_data )
+{
+  /* Helper function to verify proper registration (initialisation)
+   * of a glob_t data structure, prior to first use; it also provides
+   * the core implementation for the globfree() function.
+   */
+  static const char *glob_magic = "glob-1.0-mingw32";
+
+  /* We must be prepared to handle either of...
+   */
+  switch( request )
+  {
+    /* ...a registration (initialisation) request...
+     */
+    case GLOB_INIT:
+      if( glob_signed( gl_data->gl_magic, glob_magic ) != 0 )
+      {
+       /* The gl_magic field doesn't (yet) indicate that the
+        * data structure has been initialised; assume that this
+        * is first use, and initialise it now.
+        */
+       glob_initialise( gl_data );
+       gl_data->gl_magic = (void *)(glob_magic);
+      }
+      break;
+
+    /* ...or a de-registration (globfree()) request; here we
+     * perform a sanity check, to ensure that the passed glob_t
+     * structure is a valid, previously initialised structure,
+     * before we attempt to free it.
+     */
+    case GLOB_FREE:
+      if( glob_signed( gl_data->gl_magic, glob_magic ) == 0 )
+      {
+       /* On passing the sanity check, we may proceed to free
+        * all dynamically (strdup) allocated string buffers in
+        * the gl_pathv list, and the reference pointer table
+        * itself, thus completing the globfree() activity.
+        */
+       int base = gl_data->gl_offs;
+       int argc = gl_data->gl_pathc;
+       while( argc-- > 0 )
+         free( gl_data->gl_pathv[base++] );
+       free( gl_data->gl_pathv );
+      }
+  }
+  /* In either case, we return the original glob_t data pointer.
+   */
+  return gl_data;
+}
+
+int
+__mingw_glob( const char *pattern, int flags, int (*errfn)(), glob_t *gl_data )
+{
+  /* Module entry point for the glob() function.
+   */
+  int status;
+  /* First, consult the glob "registry", to ensure that the
+   * glob data structure passed by the caller, has been properly
+   * initialised.
+   */
+  gl_data = glob_registry( GLOB_INIT, gl_data );
+
+  /* The actual globbing function is performed by glob_match()...
+   */
+  status = glob_match( pattern, flags, errfn, gl_data );
+  if( (status == GLOB_NOMATCH) && ((flags & GLOB_NOCHECK) != 0) )
+    /*
+     * ...ultimately delegating to glob_strdup() and glob_store_entry()
+     * to handle any unmatched globbing pattern which the user specified
+     * options may require to be stored anyway.
+     */
+    glob_store_entry( glob_strdup( pattern ), gl_data );
+
+  /* We always return the status reported by glob_match().
+   */
+  return status;
+}
+
+void
+__mingw_globfree( glob_t *gl_data )
+{
+  /* Module entry point for globfree() function; the activity is
+   * entirely delegated to the glob "registry" helper function.
+   */
+  glob_registry( GLOB_FREE, gl_data );
+}