/*
 * glob.c
 *
 * MinGW implementation of (approximately) POSIX conforming glob() and
 * globfree() API functions.
 *
 * $Id: glob.c,v ed98e330aaf8 2015/03/12 07:28:43 keithmarshall $
 *
 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
 * Copyright (C) 2011-2014, 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.
 *
 */
#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

#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 substitute whatever may have been predefined as the
 * manifest constant __CRT_GLOB_ESCAPE_CHAR__, (nominally ASCII DEL,
 * a.k.a. RUBOUT), as an alternative escape character.
 */
# ifdef __CRT_GLOB_ESCAPE_CHAR__
#  define GLOB_HARD_ESC		__CRT_GLOB_ESCAPE_CHAR__
# else
#  define GLOB_HARD_ESC		((char)(127))
# endif

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 );
      }
    }

    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 );
}

GLOB_INLINE int
accept_glob_nocheck_match( const char *pattern, int flags )
{
  /* Helper to check whether a pattern may be stored into gl_pathv as is,
   * without attempting to find any file system match; (this is permitted
   * only when the caller has set the GLOB_NOCHECK flag, and the pattern
   * is devoid of any globbing token).
   */
  return (flags & GLOB_NOCHECK) && (is_glob_pattern( pattern, flags ) == 0);
}

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;

  /* When the caller has enabled the GLOB_NOCHECK option, then in the
   * case of any pattern with no prefix, and which contains no explicit
   * globbing token...
   */
  if( (dir == NULL) && accept_glob_nocheck_match( pattern, flags ) )
  {
    /* ...we prefer to store it as is, without any attempt to find
     * a glob match, (which could also induce a case transliteration
     * on MS-Windows' case-insensitive file system)...
     */
    glob_store_entry( glob_strdup( pattern ), gl_buf );
    status = GLOB_SUCCESS;
  }
  /* ...otherwise we initiate glob matching, to find all possible
   * file system matches for the designated pattern, within each of
   * the identified prefix directory paths.
   */
  else 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.
   *
   * FIXME: we'd like to be able to verify the content at "check"
   * against the signature at "magic", but "check" is likely to be
   * an uninitialised pointer, and MS-Windows lamely crashes when the
   * memory it might appear to address cannot be read.  There may be a
   * way we could trap, and effectively handle, the resulting access
   * violation, (likely restricted to WinXP and later); in the absence
   * of a suitable handler, we must restrict our check to require that
   * "check" is a strict alias for "magic".  This will lose, if we have
   * multiple copies of "glob" loaded via distinct DLLs, and we pass a
   * "glob_t" entity which has been initialised in one DLL across the
   * boundary of another; for now, however, checking for strict pointer
   * aliasing seems to be the only reliably safe option available.
   */
  return (check == magic) ? 0 : 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.  (Note that this implementation gratuitously uses
   * gl_data->gl_offs, irrespective of specification of GLOB_DOOFFS
   * in the flags; while the user must accept responsibility for the
   * initialisation of gl_data->gl_offs when specifying GLOB_DOOFFS,
   * this is not the case when GLOB_DOOFFS is not specified; in the
   * latter case, WE must assume the responsibility, ensuring that
   * the required zero value is assigned BEFORE registration).
   */
  if( (gl_data != NULL) && ((flags & GLOB_DOOFFS) == 0) )
    gl_data->gl_offs = 0;

  /* With this pre-registration requirement satisfied, we may now
   * proceed to register the provided gl_data structure.
   */
  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 );
}

/* $RCSfile: glob.c,v $: end of file */
