		/*
		 * Copyright (c) 1989, 1993, 1994
		 *      The Regents of the University of California.  All rights reserved.
		 *
		 * This code is derived from software contributed to Berkeley by
		 * Guido van Rossum.
		 *
		 * Redistribution and use in source and binary forms, with or without
		 * modification, are permitted provided that the following conditions
		 * are met:
		 * 1. Redistributions of source code must retain the above copyright
		 *    notice, this list of conditions and the following disclaimer.
		 * 2. Redistributions in binary form must reproduce the above copyright
		 *    notice, this list of conditions and the following disclaimer in the
		 *    documentation and/or other materials provided with the distribution.
		 * 3. All advertising materials mentioning features or use of this software
		 *    must display the following acknowledgement:
		 *      This product includes software developed by the University of
		 *      California, Berkeley and its contributors.
		 * 4. Neither the name of the University nor the names of its contributors
		 *    may be used to endorse or promote products derived from this software
		 *    without specific prior written permission.
		 *
		 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
		 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
		 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
		 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
		 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
		 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
		 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
		 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
		 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
		 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
		 * SUCH DAMAGE.
		 */
		
		#if defined(LIBC_SCCS) && !defined(lint)
		static char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
		#endif /* LIBC_SCCS and not lint */
		
		/*
		 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
		 * Compares a filename or pathname to a pattern.
		 */
		#ifndef WIN32
		#include "apr_private.h"
		#endif
		#include "apr_file_info.h"
		#include "apr_fnmatch.h"
		#include "apr_tables.h"
		#include "apr_lib.h"
		#include "apr_strings.h"
		#include <string.h>
		#if APR_HAVE_CTYPE_H
		# include <ctype.h>
		#endif
		
		#define	EOS	'\0'
		
		static const char *rangematch(const char *, int, int);
		
		APR_DECLARE(apr_status_t) apr_fnmatch(const char *pattern, const char *string, int flags)
          78    {
          78        const char *stringstart;
          78        char c, test;
		
         100        for (stringstart = string;;) {
         100    	switch (c = *pattern++) {
			case EOS:
           4    	    return (*string == EOS ? APR_SUCCESS : APR_FNM_NOMATCH);
			case '?':
      ######    	    if (*string == EOS) {
      ######    		return (APR_FNM_NOMATCH);
			    }
      ######    	    if (*string == '/' && (flags & APR_FNM_PATHNAME)) {
      ######    		return (APR_FNM_NOMATCH);
			    }
      ######    	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
				(string == stringstart ||
				 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
      ######    		return (APR_FNM_NOMATCH);
			    }
      ######    	    ++string;
      ######    	    break;
			case '*':
          10    	    c = *pattern;
			    /* Collapse multiple stars. */
          10    	    while (c == '*') {
      ######    		c = *++pattern;
			    }
		
          10    	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
				(string == stringstart ||
				 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
      ######    		return (APR_FNM_NOMATCH);
			    }
		
			    /* Optimize for pattern with * at end or before /. */
          10    	    if (c == EOS) {
      ######    		if (flags & APR_FNM_PATHNAME) {
      ######    		    return (strchr(string, '/') == NULL ? APR_SUCCESS : APR_FNM_NOMATCH);
				}
				else {
      ######    		    return (APR_SUCCESS);
				}
			    }
          10    	    else if (c == '/' && flags & APR_FNM_PATHNAME) {
      ######    	        if ((string = strchr(string, '/')) == NULL) {
      ######    		    return (APR_FNM_NOMATCH);
				}
          10    		break;
			    }
		
			    /* General case, use recursion. */
          74    	    while ((test = *string) != EOS) {
          68    	        if (!apr_fnmatch(pattern, string, flags & ~APR_FNM_PERIOD)) {
           4    		    return (APR_SUCCESS);
				}
          64    		if (test == '/' && flags & APR_FNM_PATHNAME) {
      ######    		    break;
				}
          64    		++string;
			    }
           6    	    return (APR_FNM_NOMATCH);
			case '[':
      ######    	    if (*string == EOS) {
      ######    		return (APR_FNM_NOMATCH);
			    }
      ######    	    if (*string == '/' && flags & APR_FNM_PATHNAME) {
      ######    		return (APR_FNM_NOMATCH);
			    }
      ######    	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
				(string == stringstart ||
				 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
      ######    	        return (APR_FNM_NOMATCH);
			    }
      ######    	    if ((pattern = rangematch(pattern, *string, flags)) == NULL) {
      ######    		return (APR_FNM_NOMATCH);
			    }
      ######    	    ++string;
      ######    	    break;
			case '\\':
      ######    	    if (!(flags & APR_FNM_NOESCAPE)) {
      ######    		if ((c = *pattern++) == EOS) {
      ######    		    c = '\\';
      ######    		    --pattern;
				}
			    }
			    /* FALLTHROUGH */
			default:
          86    	    if (flags & APR_FNM_CASE_BLIND) {
      ######    	        if (apr_tolower(c) != apr_tolower(*string)) {
      ######    		    return (APR_FNM_NOMATCH);
				}
			    }
          86    	    else if (c != *string) {
          64    	        return (APR_FNM_NOMATCH);
			    }
          22    	    string++;
          22    	    break;
			}
		    /* NOTREACHED */
		    }
		}
		
		static const char *rangematch(const char *pattern, int test, int flags)
      ######    {
      ######        int negate, ok;
      ######        char c, c2;
		
		    /*
		     * A bracket expression starting with an unquoted circumflex
		     * character produces unspecified results (IEEE 1003.2-1992,
		     * 3.13.2).  This implementation treats it like '!', for
		     * consistency with the regular expression syntax.
		     * J.T. Conklin (conklin@ngai.kaleida.com)
		     */
      ######        if ((negate = (*pattern == '!' || *pattern == '^'))) {
      ######    	++pattern;
		    }
		
      ######        for (ok = 0; (c = *pattern++) != ']';) {
      ######            if (c == '\\' && !(flags & APR_FNM_NOESCAPE)) {
      ######    	    c = *pattern++;
			}
      ######    	if (c == EOS) {
      ######    	    return (NULL);
			}
      ######    	if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
      ######    	    pattern += 2;
      ######    	    if (c2 == '\\' && !(flags & APR_FNM_NOESCAPE)) {
      ######    		c2 = *pattern++;
			    }
      ######    	    if (c2 == EOS) {
      ######    		return (NULL);
			    }
      ######    	    if ((c <= test && test <= c2)
				|| ((flags & APR_FNM_CASE_BLIND)
				    && ((apr_tolower(c) <= apr_tolower(test))
					&& (apr_tolower(test) <= apr_tolower(c2))))) {
      ######    		ok = 1;
			    }
			}
      ######    	else if ((c == test)
				 || ((flags & APR_FNM_CASE_BLIND)
				     && (apr_tolower(c) == apr_tolower(test)))) {
      ######    	    ok = 1;
			}
		    }
      ######        return (ok == negate ? NULL : pattern);
		}
		
		
		/* This function is an Apache addition */
		/* return non-zero if pattern has any glob chars in it */
		APR_DECLARE(int) apr_fnmatch_test(const char *pattern)
      ######    {
      ######        int nesting;
		
      ######        nesting = 0;
      ######        while (*pattern) {
      ######    	switch (*pattern) {
			case '?':
			case '*':
      ######    	    return 1;
		
			case '\\':
      ######    	    if (*pattern++ == '\0') {
      ######    		return 0;
			    }
      ######    	    break;
		
			case '[':	/* '[' is only a glob if it has a matching ']' */
      ######    	    ++nesting;
      ######    	    break;
		
			case ']':
      ######    	    if (nesting) {
      ######    		return 1;
			    }
      ######    	    break;
			}
      ######    	++pattern;
		    }
      ######        return 0;
		}
		
		/* Find all files matching the specified pattern */
		APR_DECLARE(apr_status_t) apr_match_glob(const char *pattern, 
		                                         apr_array_header_t **result,
		                                         apr_pool_t *p)
           2    {
           2        apr_dir_t *dir;
           2        apr_finfo_t finfo;
           2        apr_status_t rv;
           2        char *path;
		
		    /* XXX So, this is kind of bogus.  Basically, I need to strip any leading
		     * directories off the pattern, but there is no portable way to do that.
		     * So, for now we just find the last occurance of '/' and if that doesn't
		     * return anything, then we look for '\'.  This means that we could
		     * screw up on unix if the pattern is something like "foo\.*"  That '\'
		     * isn't a directory delimiter, it is a part of the filename.  To fix this,
		     * we really need apr_filepath_basename, which will be coming as soon as
		     * I get to it.  rbb
		     */
           2        char *idx = strrchr(pattern, '/');
		    
           2        if (idx == NULL) {
           2            idx = strrchr(pattern, '\\');
		    }
           2        if (idx == NULL) {
           1            path = ".";
		    }
		    else {
           1            path = apr_pstrndup(p, pattern, idx - pattern);
           1            pattern = idx + 1;
		    }
		
           2        *result = apr_array_make(p, 0, sizeof(char *));
           2        rv = apr_dir_open(&dir, path, p);
           2        if (rv != APR_SUCCESS) {
      ######            return rv;
		    }
		
          12        while (apr_dir_read(&finfo, APR_FINFO_NAME, dir) == APR_SUCCESS) {
          10            if (apr_fnmatch(pattern, finfo.name, 0) == APR_SUCCESS) {
           4                *(const char **)apr_array_push(*result) = apr_pstrdup(p, finfo.name);
		        }
		    }
           2        apr_dir_close(dir);
           2        return APR_SUCCESS;
		}
