components/apr/patches/CVE-2011-0419.patch
changeset 472 b4bf6ad34a2d
equal deleted inserted replaced
471:ed3afe437ba0 472:b4bf6ad34a2d
       
     1 
       
     2 https://bugzilla.redhat.com/show_bug.cgi?id=703390
       
     3 
       
     4 http://svn.apache.org/viewvc/apr/apr/branches/1.4.x/strings/apr_fnmatch.c?r1=731029&r2=1098902
       
     5 
       
     6 --- strings/apr_fnmatch.c.fnmatch
       
     7 +++ strings/apr_fnmatch.c
       
     8 @@ -1,50 +1,58 @@
       
     9 -/*
       
    10 - * Copyright (c) 1989, 1993, 1994
       
    11 - *      The Regents of the University of California.  All rights reserved.
       
    12 +/* Licensed to the Apache Software Foundation (ASF) under one or more
       
    13 + * contributor license agreements. See the NOTICE file distributed with
       
    14 + * this work for additional information regarding copyright ownership.
       
    15 + * The ASF licenses this file to You under the Apache License, Version 2.0
       
    16 + * (the "License"); you may not use this file except in compliance with
       
    17 + * the License. You may obtain a copy of the License at
       
    18   *
       
    19 - * This code is derived from software contributed to Berkeley by
       
    20 - * Guido van Rossum.
       
    21 + * http://www.apache.org/licenses/LICENSE-2.0
       
    22   *
       
    23 - * Redistribution and use in source and binary forms, with or without
       
    24 - * modification, are permitted provided that the following conditions
       
    25 - * are met:
       
    26 - * 1. Redistributions of source code must retain the above copyright
       
    27 - *    notice, this list of conditions and the following disclaimer.
       
    28 - * 2. Redistributions in binary form must reproduce the above copyright
       
    29 - *    notice, this list of conditions and the following disclaimer in the
       
    30 - *    documentation and/or other materials provided with the distribution.
       
    31 - * 3. All advertising materials mentioning features or use of this software
       
    32 - *    must display the following acknowledgement:
       
    33 - *      This product includes software developed by the University of
       
    34 - *      California, Berkeley and its contributors.
       
    35 - * 4. Neither the name of the University nor the names of its contributors
       
    36 - *    may be used to endorse or promote products derived from this software
       
    37 - *    without specific prior written permission.
       
    38 + * Unless required by applicable law or agreed to in writing, software
       
    39 + * distributed under the License is distributed on an "AS IS" BASIS,
       
    40 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    41 + * See the License for the specific language governing permissions and
       
    42 + * limitations under the License.
       
    43 + */ 
       
    44 +
       
    45 +
       
    46 +/* Derived from The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008
       
    47 + * as described in;
       
    48 + *   http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html
       
    49 + *
       
    50 + * Filename pattern matches defined in section 2.13, "Pattern Matching Notation"
       
    51 + * from chapter 2. "Shell Command Language"
       
    52 + *   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
       
    53 + * where; 1. A bracket expression starting with an unquoted <circumflex> '^' 
       
    54 + * character CONTINUES to specify a non-matching list; 2. an explicit <period> '.' 
       
    55 + * in a bracket expression matching list, e.g. "[.abc]" does NOT match a leading 
       
    56 + * <period> in a filename; 3. a <left-square-bracket> '[' which does not introduce
       
    57 + * a valid bracket expression is treated as an ordinary character; 4. a differing
       
    58 + * number of consecutive slashes within pattern and string will NOT match;
       
    59 + * 5. a trailing '\' in FNM_ESCAPE mode is treated as an ordinary '\' character.
       
    60 + *
       
    61 + * Bracket expansion defined in section 9.3.5, "RE Bracket Expression",
       
    62 + * from chapter 9, "Regular Expressions"
       
    63 + *   http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05
       
    64 + * with no support for collating symbols, equivalence class expressions or 
       
    65 + * character class expressions.  A partial range expression with a leading 
       
    66 + * hyphen following a valid range expression will match only the ordinary
       
    67 + * <hyphen> and the ending character (e.g. "[a-m-z]" will match characters 
       
    68 + * 'a' through 'm', a <hyphen> '-', or a 'z').
       
    69 + *
       
    70 + * NOTE: Only POSIX/C single byte locales are correctly supported at this time.
       
    71 + * Notably, non-POSIX locales with FNM_CASEFOLD produce undefined results,
       
    72 + * particularly in ranges of mixed case (e.g. "[A-z]") or spanning alpha and
       
    73 + * nonalpha characters within a range.
       
    74   *
       
    75 - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
       
    76 - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    77 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
       
    78 - * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
       
    79 - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
       
    80 - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
       
    81 - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
       
    82 - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
       
    83 - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
       
    84 - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
       
    85 - * SUCH DAMAGE.
       
    86 + * XXX comments below indicate porting required for multi-byte character sets
       
    87 + * and non-POSIX locale collation orders; requires mbr* APIs to track shift
       
    88 + * state of pattern and string (rewinding pattern and string repeatedly).
       
    89 + *
       
    90 + * Certain parts of the code assume 0x00-0x3F are unique with any MBCS (e.g.
       
    91 + * UTF-8, SHIFT-JIS, etc).  Any implementation allowing '\' as an alternate
       
    92 + * path delimiter must be aware that 0x5C is NOT unique within SHIFT-JIS.
       
    93   */
       
    94  
       
    95 -#if defined(LIBC_SCCS) && !defined(lint)
       
    96 -static char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
       
    97 -#endif /* LIBC_SCCS and not lint */
       
    98 -
       
    99 -/*
       
   100 - * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
       
   101 - * Compares a filename or pathname to a pattern.
       
   102 - */
       
   103 -#ifndef WIN32
       
   104 -#include "apr_private.h"
       
   105 -#endif
       
   106  #include "apr_file_info.h"
       
   107  #include "apr_fnmatch.h"
       
   108  #include "apr_tables.h"
       
   109 @@ -55,196 +63,355 @@ static char sccsid[] = "@(#)fnmatch.c	8.
       
   110  # include <ctype.h>
       
   111  #endif
       
   112  
       
   113 -#define	EOS	'\0'
       
   114 -
       
   115 -static const char *rangematch(const char *, int, int);
       
   116  
       
   117 -APR_DECLARE(apr_status_t) apr_fnmatch(const char *pattern, const char *string, int flags)
       
   118 +/* Most MBCS/collation/case issues handled here.  Wildcard '*' is not handled.
       
   119 + * EOS '\0' and the FNM_PATHNAME '/' delimiters are not advanced over, 
       
   120 + * however the "\/" sequence is advanced to '/'.
       
   121 + *
       
   122 + * Both pattern and string are **char to support pointer increment of arbitrary
       
   123 + * multibyte characters for the given locale, in a later iteration of this code
       
   124 + */
       
   125 +static APR_INLINE int fnmatch_ch(const char **pattern, const char **string, int flags)
       
   126  {
       
   127 -    const char *stringstart;
       
   128 -    char c, test;
       
   129 +    const char * const mismatch = *pattern;
       
   130 +    const int nocase = !!(flags & APR_FNM_CASE_BLIND);
       
   131 +    const int escape = !(flags & APR_FNM_NOESCAPE);
       
   132 +    const int slash = !!(flags & APR_FNM_PATHNAME);
       
   133 +    int result = APR_FNM_NOMATCH;
       
   134 +    const char *startch;
       
   135 +    int negate;
       
   136 +
       
   137 +    if (**pattern == '[')
       
   138 +    {
       
   139 +        ++*pattern;
       
   140 +
       
   141 +        /* Handle negation, either leading ! or ^ operators (never both) */
       
   142 +        negate = ((**pattern == '!') || (**pattern == '^'));
       
   143 +        if (negate)
       
   144 +            ++*pattern;
       
   145 +
       
   146 +        while (**pattern)
       
   147 +        {
       
   148 +            /* ']' is an ordinary character at the start of the range pattern */
       
   149 +            if ((**pattern == ']') && (*pattern > mismatch)) {
       
   150 +                ++*pattern;
       
   151 +                /* XXX: Fix for MBCS character width */
       
   152 +                ++*string;
       
   153 +                return (result ^ negate);
       
   154 +            }
       
   155 +
       
   156 +            if (escape && (**pattern == '\\')) {
       
   157 +                ++*pattern;
       
   158 +
       
   159 +                /* Patterns must be terminated with ']', not EOS */
       
   160 +                if (!**pattern)
       
   161 +                    break;
       
   162 +            }
       
   163 +
       
   164 +            /* Patterns must be terminated with ']' not '/' */
       
   165 +            if (slash && (**pattern == '/'))
       
   166 +                break;
       
   167 +
       
   168 +            /* Look at only well-formed range patterns; ']' is allowed only if escaped,
       
   169 +             * while '/' is not allowed at all in FNM_PATHNAME mode.
       
   170 +             */
       
   171 +            /* XXX: Fix for locale/MBCS character width */
       
   172 +            if (((*pattern)[1] == '-') && (*pattern)[2] 
       
   173 +                    && ((escape && ((*pattern)[2] != '\\'))
       
   174 +                          ? (((*pattern)[2] != ']') && (!slash || ((*pattern)[2] != '/')))
       
   175 +                          : (((*pattern)[3]) && (!slash || ((*pattern)[3] != '/'))))) {
       
   176 +                startch = *pattern;
       
   177 +                *pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
       
   178 +
       
   179 +                /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
       
   180 +                if ((**string >= *startch) && (**string <= **pattern))
       
   181 +                    result = 0;
       
   182 +                else if (nocase && (isupper(**string) || isupper(*startch)
       
   183 +                                                      || isupper(**pattern))
       
   184 +                            && (tolower(**string) >= tolower(*startch)) 
       
   185 +                            && (tolower(**string) <= tolower(**pattern)))
       
   186 +                    result = 0;
       
   187 +
       
   188 +                ++*pattern;
       
   189 +                continue;
       
   190 +            }
       
   191 +
       
   192 +            /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
       
   193 +            if ((**string == **pattern))
       
   194 +                result = 0;
       
   195 +            else if (nocase && (isupper(**string) || isupper(**pattern))
       
   196 +                            && (tolower(**string) == tolower(**pattern)))
       
   197 +                result = 0;
       
   198  
       
   199 -    for (stringstart = string;;) {
       
   200 -	switch (c = *pattern++) {
       
   201 -	case EOS:
       
   202 -	    return (*string == EOS ? APR_SUCCESS : APR_FNM_NOMATCH);
       
   203 -	case '?':
       
   204 -	    if (*string == EOS) {
       
   205 -		return (APR_FNM_NOMATCH);
       
   206 -	    }
       
   207 -	    if (*string == '/' && (flags & APR_FNM_PATHNAME)) {
       
   208 -		return (APR_FNM_NOMATCH);
       
   209 -	    }
       
   210 -	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
       
   211 -		(string == stringstart ||
       
   212 -		 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
       
   213 -		return (APR_FNM_NOMATCH);
       
   214 -	    }
       
   215 -	    ++string;
       
   216 -	    break;
       
   217 -	case '*':
       
   218 -	    c = *pattern;
       
   219 -	    /* Collapse multiple stars. */
       
   220 -	    while (c == '*') {
       
   221 -		c = *++pattern;
       
   222 -	    }
       
   223 -
       
   224 -	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
       
   225 -		(string == stringstart ||
       
   226 -		 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
       
   227 -		return (APR_FNM_NOMATCH);
       
   228 -	    }
       
   229 -
       
   230 -	    /* Optimize for pattern with * at end or before /. */
       
   231 -	    if (c == EOS) {
       
   232 -		if (flags & APR_FNM_PATHNAME) {
       
   233 -		    return (strchr(string, '/') == NULL ? APR_SUCCESS : APR_FNM_NOMATCH);
       
   234 -		}
       
   235 -		else {
       
   236 -		    return (APR_SUCCESS);
       
   237 -		}
       
   238 -	    }
       
   239 -	    else if (c == '/' && flags & APR_FNM_PATHNAME) {
       
   240 -	        if ((string = strchr(string, '/')) == NULL) {
       
   241 -		    return (APR_FNM_NOMATCH);
       
   242 -		}
       
   243 -		break;
       
   244 -	    }
       
   245 -
       
   246 -	    /* General case, use recursion. */
       
   247 -	    while ((test = *string) != EOS) {
       
   248 -	        if (!apr_fnmatch(pattern, string, flags & ~APR_FNM_PERIOD)) {
       
   249 -		    return (APR_SUCCESS);
       
   250 -		}
       
   251 -		if (test == '/' && flags & APR_FNM_PATHNAME) {
       
   252 -		    break;
       
   253 -		}
       
   254 -		++string;
       
   255 -	    }
       
   256 -	    return (APR_FNM_NOMATCH);
       
   257 -	case '[':
       
   258 -	    if (*string == EOS) {
       
   259 -		return (APR_FNM_NOMATCH);
       
   260 -	    }
       
   261 -	    if (*string == '/' && flags & APR_FNM_PATHNAME) {
       
   262 -		return (APR_FNM_NOMATCH);
       
   263 -	    }
       
   264 -	    if (*string == '.' && (flags & APR_FNM_PERIOD) &&
       
   265 -		(string == stringstart ||
       
   266 -		 ((flags & APR_FNM_PATHNAME) && *(string - 1) == '/'))) {
       
   267 -	        return (APR_FNM_NOMATCH);
       
   268 -	    }
       
   269 -	    if ((pattern = rangematch(pattern, *string, flags)) == NULL) {
       
   270 -		return (APR_FNM_NOMATCH);
       
   271 -	    }
       
   272 -	    ++string;
       
   273 -	    break;
       
   274 -	case '\\':
       
   275 -	    if (!(flags & APR_FNM_NOESCAPE)) {
       
   276 -		if ((c = *pattern++) == EOS) {
       
   277 -		    c = '\\';
       
   278 -		    --pattern;
       
   279 -		}
       
   280 -	    }
       
   281 -	    /* FALLTHROUGH */
       
   282 -	default:
       
   283 -	    if (flags & APR_FNM_CASE_BLIND) {
       
   284 -	        if (apr_tolower(c) != apr_tolower(*string)) {
       
   285 -		    return (APR_FNM_NOMATCH);
       
   286 -		}
       
   287 -	    }
       
   288 -	    else if (c != *string) {
       
   289 -	        return (APR_FNM_NOMATCH);
       
   290 -	    }
       
   291 -	    string++;
       
   292 -	    break;
       
   293 -	}
       
   294 -    /* NOTREACHED */
       
   295 +            ++*pattern;
       
   296 +        }
       
   297 +
       
   298 +        /* NOT a properly balanced [expr] pattern; Rewind to test '[' literal */
       
   299 +        *pattern = mismatch;
       
   300 +        result = APR_FNM_NOMATCH;
       
   301 +    }
       
   302 +    else if (**pattern == '?') {
       
   303 +        /* Optimize '?' match before unescaping **pattern */
       
   304 +        if (!**string || (slash && (**string == '/')))
       
   305 +            return APR_FNM_NOMATCH;
       
   306 +        result = 0;
       
   307 +        goto fnmatch_ch_success;
       
   308      }
       
   309 +    else if (escape && (**pattern == '\\') && (*pattern)[1]) {
       
   310 +        ++*pattern;
       
   311 +    }
       
   312 +
       
   313 +    /* XXX: handle locale/MBCS comparison, advance by the MBCS char width */
       
   314 +    if (**string == **pattern)
       
   315 +        result = 0;
       
   316 +    else if (nocase && (isupper(**string) || isupper(**pattern))
       
   317 +                    && (tolower(**string) == tolower(**pattern)))
       
   318 +        result = 0;
       
   319 +
       
   320 +    /* Refuse to advance over trailing slash or nulls
       
   321 +     */
       
   322 +    if (!**string || !**pattern || (slash && ((**string == '/') || (**pattern == '/'))))
       
   323 +        return result;
       
   324 +
       
   325 +fnmatch_ch_success:
       
   326 +    ++*pattern;
       
   327 +    ++*string;
       
   328 +    return result;
       
   329  }
       
   330  
       
   331 -static const char *rangematch(const char *pattern, int test, int flags)
       
   332 -{
       
   333 -    int negate, ok;
       
   334 -    char c, c2;
       
   335  
       
   336 -    /*
       
   337 -     * A bracket expression starting with an unquoted circumflex
       
   338 -     * character produces unspecified results (IEEE 1003.2-1992,
       
   339 -     * 3.13.2).  This implementation treats it like '!', for
       
   340 -     * consistency with the regular expression syntax.
       
   341 -     * J.T. Conklin ([email protected])
       
   342 +APR_DECLARE(int) apr_fnmatch(const char *pattern, const char *string, int flags)
       
   343 +{
       
   344 +    static const char dummystring[2] = {' ', 0};
       
   345 +    const int escape = !(flags & APR_FNM_NOESCAPE);
       
   346 +    const int slash = !!(flags & APR_FNM_PATHNAME);
       
   347 +    const char *strendseg;
       
   348 +    const char *dummyptr;
       
   349 +    const char *matchptr;
       
   350 +    int wild;
       
   351 +    /* For '*' wild processing only; surpress 'used before initialization'
       
   352 +     * warnings with dummy initialization values;
       
   353       */
       
   354 -    if ((negate = (*pattern == '!' || *pattern == '^'))) {
       
   355 -	++pattern;
       
   356 -    }
       
   357 +    const char *strstartseg = NULL;
       
   358 +    const char *mismatch = NULL;
       
   359 +    int matchlen = 0;
       
   360 +
       
   361 +    while (*pattern)
       
   362 +    {
       
   363 +        /* Match balanced slashes, starting a new segment pattern
       
   364 +         */
       
   365 +        if (slash && escape && (*pattern == '\\') && (pattern[1] == '/'))
       
   366 +            ++pattern;
       
   367 +        if (slash && (*pattern == '/') && (*string == '/')) {
       
   368 +            ++pattern;
       
   369 +            ++string;
       
   370 +        }            
       
   371 +
       
   372 +        /* At the beginning of each segment, validate leading period behavior.
       
   373 +         */
       
   374 +        if ((flags & APR_FNM_PERIOD) && (*string == '.'))
       
   375 +        {
       
   376 +            if (*pattern == '.')
       
   377 +                ++pattern;
       
   378 +            else if (escape && (*pattern == '\\') && (pattern[1] == '.'))
       
   379 +                pattern += 2;
       
   380 +            else
       
   381 +                return APR_FNM_NOMATCH;
       
   382 +            ++string;
       
   383 +        }
       
   384 +
       
   385 +        /* Determine the end of string segment
       
   386 +         *
       
   387 +         * Presumes '/' character is unique, not composite in any MBCS encoding
       
   388 +         */
       
   389 +        if (slash) {
       
   390 +            strendseg = strchr(string, '/');
       
   391 +            if (!strendseg)
       
   392 +                strendseg = strchr(string, '\0');
       
   393 +        }
       
   394 +        else {
       
   395 +            strendseg = strchr(string, '\0');
       
   396 +        }
       
   397 +
       
   398 +        /* Allow pattern '*' to be consumed even with no remaining string to match
       
   399 +         */
       
   400 +        while (*pattern && !(slash && ((*pattern == '/')
       
   401 +                                       || (escape && (*pattern == '\\')
       
   402 +                                                  && (pattern[1] == '/'))))
       
   403 +                        && ((string < strendseg)
       
   404 +                            || ((*pattern == '*') && (string == strendseg))))
       
   405 +        {
       
   406 +            /* Reduce groups of '*' and '?' to n '?' matches
       
   407 +             * followed by one '*' test for simplicity
       
   408 +             */
       
   409 +            for (wild = 0; ((*pattern == '*') || (*pattern == '?')); ++pattern)
       
   410 +            {
       
   411 +                if (*pattern == '*') {
       
   412 +                    wild = 1;
       
   413 +                }
       
   414 +                else if (string < strendseg) {  /* && (*pattern == '?') */
       
   415 +                    /* XXX: Advance 1 char for MBCS locale */
       
   416 +                    ++string;
       
   417 +                }
       
   418 +                else {  /* (string >= strendseg) && (*pattern == '?') */
       
   419 +                    return APR_FNM_NOMATCH;
       
   420 +                }
       
   421 +            }
       
   422 +
       
   423 +            if (wild)
       
   424 +            {
       
   425 +                strstartseg = string;
       
   426 +                mismatch = pattern;
       
   427 +
       
   428 +                /* Count fixed (non '*') char matches remaining in pattern
       
   429 +                 * excluding '/' (or "\/") and '*'
       
   430 +                 */
       
   431 +                for (matchptr = pattern, matchlen = 0; 1; ++matchlen)
       
   432 +                {
       
   433 +                    if ((*matchptr == '\0') 
       
   434 +                        || (slash && ((*matchptr == '/')
       
   435 +                                      || (escape && (*matchptr == '\\')
       
   436 +                                                 && (matchptr[1] == '/')))))
       
   437 +                    {
       
   438 +                        /* Compare precisely this many trailing string chars,
       
   439 +                         * the resulting match needs no wildcard loop
       
   440 +                         */
       
   441 +                        /* XXX: Adjust for MBCS */
       
   442 +                        if (string + matchlen > strendseg)
       
   443 +                            return APR_FNM_NOMATCH;
       
   444 +
       
   445 +                        string = strendseg - matchlen;
       
   446 +                        wild = 0;
       
   447 +                        break;
       
   448 +                    }
       
   449 +
       
   450 +                    if (*matchptr == '*')
       
   451 +                    {
       
   452 +                        /* Ensure at least this many trailing string chars remain
       
   453 +                         * for the first comparison
       
   454 +                         */
       
   455 +                        /* XXX: Adjust for MBCS */
       
   456 +                        if (string + matchlen > strendseg)
       
   457 +                            return APR_FNM_NOMATCH;
       
   458 +
       
   459 +                        /* Begin first wild comparison at the current position */
       
   460 +                        break;
       
   461 +                    }
       
   462 +
       
   463 +                    /* Skip forward in pattern by a single character match
       
   464 +                     * Use a dummy fnmatch_ch() test to count one "[range]" escape
       
   465 +                     */ 
       
   466 +                    /* XXX: Adjust for MBCS */
       
   467 +                    if (escape && (*matchptr == '\\') && matchptr[1]) {
       
   468 +                        matchptr += 2;
       
   469 +                    }
       
   470 +                    else if (*matchptr == '[') {
       
   471 +                        dummyptr = dummystring;
       
   472 +                        fnmatch_ch(&matchptr, &dummyptr, flags);
       
   473 +                    }
       
   474 +                    else {
       
   475 +                        ++matchptr;
       
   476 +                    }
       
   477 +                }
       
   478 +            }
       
   479 +
       
   480 +            /* Incrementally match string against the pattern
       
   481 +             */
       
   482 +            while (*pattern && (string < strendseg))
       
   483 +            {
       
   484 +                /* Success; begin a new wild pattern search
       
   485 +                 */
       
   486 +                if (*pattern == '*')
       
   487 +                    break;
       
   488 +
       
   489 +                if (slash && ((*string == '/') || (*pattern == '/')
       
   490 +                                               || (escape && (*pattern == '\\')
       
   491 +                                                   && (pattern[1] == '/'))))
       
   492 +                    break;
       
   493 +
       
   494 +                /* Compare ch's (the pattern is advanced over "\/" to the '/',
       
   495 +                 * but slashes will mismatch, and are not consumed)
       
   496 +                 */
       
   497 +                if (!fnmatch_ch(&pattern, &string, flags))
       
   498 +                    continue;
       
   499 +
       
   500 +                /* Failed to match, loop against next char offset of string segment 
       
   501 +                 * until not enough string chars remain to match the fixed pattern
       
   502 +                 */
       
   503 +                if (wild) {
       
   504 +                    /* XXX: Advance 1 char for MBCS locale */
       
   505 +                    string = ++strstartseg;
       
   506 +                    if (string + matchlen > strendseg)
       
   507 +                        return APR_FNM_NOMATCH;
       
   508 +
       
   509 +                    pattern = mismatch;
       
   510 +                    continue;
       
   511 +                }
       
   512 +                else
       
   513 +                    return APR_FNM_NOMATCH;
       
   514 +            }
       
   515 +        }
       
   516 +
       
   517 +        if (*string && (!slash || (*string != '/')))
       
   518 +            return APR_FNM_NOMATCH;
       
   519  
       
   520 -    for (ok = 0; (c = *pattern++) != ']';) {
       
   521 -        if (c == '\\' && !(flags & APR_FNM_NOESCAPE)) {
       
   522 -	    c = *pattern++;
       
   523 -	}
       
   524 -	if (c == EOS) {
       
   525 -	    return (NULL);
       
   526 -	}
       
   527 -	if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
       
   528 -	    pattern += 2;
       
   529 -	    if (c2 == '\\' && !(flags & APR_FNM_NOESCAPE)) {
       
   530 -		c2 = *pattern++;
       
   531 -	    }
       
   532 -	    if (c2 == EOS) {
       
   533 -		return (NULL);
       
   534 -	    }
       
   535 -	    if ((c <= test && test <= c2)
       
   536 -		|| ((flags & APR_FNM_CASE_BLIND)
       
   537 -		    && ((apr_tolower(c) <= apr_tolower(test))
       
   538 -			&& (apr_tolower(test) <= apr_tolower(c2))))) {
       
   539 -		ok = 1;
       
   540 -	    }
       
   541 -	}
       
   542 -	else if ((c == test)
       
   543 -		 || ((flags & APR_FNM_CASE_BLIND)
       
   544 -		     && (apr_tolower(c) == apr_tolower(test)))) {
       
   545 -	    ok = 1;
       
   546 -	}
       
   547 +        if (*pattern && (!slash || ((*pattern != '/')
       
   548 +                                    && (!escape || (*pattern != '\\')
       
   549 +                                                || (pattern[1] != '/')))))
       
   550 +            return APR_FNM_NOMATCH;
       
   551      }
       
   552 -    return (ok == negate ? NULL : pattern);
       
   553 +
       
   554 +    /* pattern is at EOS; if string is also, declare success
       
   555 +     */
       
   556 +    if (!*string)
       
   557 +        return 0;
       
   558 +
       
   559 +    /* pattern didn't match to the end of string */
       
   560 +    return APR_FNM_NOMATCH;
       
   561  }
       
   562  
       
   563  
       
   564 -/* This function is an Apache addition */
       
   565 -/* return non-zero if pattern has any glob chars in it */
       
   566 +/* This function is an Apache addition
       
   567 + * return non-zero if pattern has any glob chars in it
       
   568 + * @bug Function does not distinguish for FNM_PATHNAME mode, which renders
       
   569 + * a false positive for test[/]this (which is not a range, but 
       
   570 + * seperate test[ and ]this segments and no glob.)
       
   571 + * @bug Function does not distinguish for non-FNM_ESCAPE mode.
       
   572 + * @bug Function does not parse []] correctly
       
   573 + * Solution may be to use fnmatch_ch() to walk the patterns?
       
   574 + */
       
   575  APR_DECLARE(int) apr_fnmatch_test(const char *pattern)
       
   576  {
       
   577      int nesting;
       
   578  
       
   579      nesting = 0;
       
   580      while (*pattern) {
       
   581 -	switch (*pattern) {
       
   582 -	case '?':
       
   583 -	case '*':
       
   584 -	    return 1;
       
   585 -
       
   586 -	case '\\':
       
   587 -	    if (*++pattern == '\0') {
       
   588 -		return 0;
       
   589 -	    }
       
   590 -	    break;
       
   591 -
       
   592 -	case '[':	/* '[' is only a glob if it has a matching ']' */
       
   593 -	    ++nesting;
       
   594 -	    break;
       
   595 -
       
   596 -	case ']':
       
   597 -	    if (nesting) {
       
   598 -		return 1;
       
   599 -	    }
       
   600 -	    break;
       
   601 -	}
       
   602 -	++pattern;
       
   603 -    }
       
   604 +        switch (*pattern) {
       
   605 +        case '?':
       
   606 +        case '*':
       
   607 +            return 1;
       
   608 +
       
   609 +        case '\\':
       
   610 +            if (*++pattern == '\0') {
       
   611 +                return 0;
       
   612 +            }
       
   613 +            break;
       
   614 +
       
   615 +        case '[':         /* '[' is only a glob if it has a matching ']' */
       
   616 +            ++nesting;
       
   617 +            break;
       
   618 +
       
   619 +        case ']':
       
   620 +            if (nesting) {
       
   621 +                return 1;
       
   622 +            }
       
   623 +            break;
       
   624 +        }
       
   625 +        ++pattern;    }
       
   626      return 0;
       
   627  }
       
   628  
       
   629 +
       
   630  /* Find all files matching the specified pattern */
       
   631  APR_DECLARE(apr_status_t) apr_match_glob(const char *pattern, 
       
   632                                           apr_array_header_t **result,