| /************************************************************************ |
| * |
| * fnmatch.cpp - definitions of testsuite helpers |
| * |
| * $Id$ |
| * |
| ************************************************************************ |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| * |
| **************************************************************************/ |
| |
| // expand _TEST_EXPORT macros |
| #define _RWSTD_TEST_SRC |
| #include <rw_fnmatch.h> |
| |
| |
| typedef unsigned char UChar; |
| |
| |
| // matches a character against a POSIX bracket expression |
| static const char* |
| _rw_bracketmatch (const char *pattern, const unsigned char ch, int) |
| { |
| const char *pc = pattern; |
| |
| // negate the match? |
| bool neg = false; |
| |
| // treat '!' and '^' the same (according to POSIX, the latter has |
| // unspecified behavior) |
| if ('!' == *pc || '^' == *pc) { |
| ++pc; |
| neg = true; |
| } |
| |
| if ('-' == *pc || ']' == *pc) { |
| // the '-' and ']' lose their special meaning when they occur |
| // first in the bracket expression, after the initial '!', if |
| // any |
| if (ch == UChar (*pc)) { |
| if (neg) |
| return 0; |
| |
| // skip over the ']' |
| if (']' == *pc) |
| ++pc; |
| |
| // skip over any subsequent characters until the closing |
| // (unescaped) ']' |
| for ( ; *pc && (*pc != ']' || pc [-1] == '\\'); ++pc); |
| return pc; |
| } |
| else if (!neg) |
| return 0; |
| } |
| |
| unsigned first = 0; |
| bool esc = false; |
| |
| for ( ; *pc; ++pc) { |
| if ('\\' == *pc) { |
| if (esc) { |
| if (ch == UChar (*pc)) { |
| if (neg) |
| return 0; |
| |
| // skip over any subsequent characters until the closing |
| // (unescaped) ']' |
| for ( ; *pc && (*pc != ']' || pc [-1] == '\\'); ++pc); |
| return pc; |
| } |
| } |
| esc = true; |
| continue; |
| } |
| |
| if (']' == *pc && !esc) |
| break; |
| |
| if ('-' == *pc && !esc) |
| first = UChar (pc [-1]); |
| else if (first) { |
| // check if character is in the current range |
| if (first <= ch && ch <= UChar (*pc)) { |
| if (neg) |
| return 0; |
| |
| // skip over any subsequent characters until the closing |
| // (unescaped) ']' |
| for ( ; *pc && (*pc != ']' || pc [-1] == '\\'); ++pc); |
| return pc; |
| } |
| |
| first = 0; |
| } |
| else if (ch == UChar (*pc)) { |
| if (neg) |
| return 0; |
| |
| // skip over any subsequent characters until the closing |
| // (unescaped) ']' |
| for ( ; *pc && (*pc != ']' || pc [-1] == '\\'); ++pc); |
| return pc; |
| } |
| } |
| |
| return neg ? pc : 0; |
| } |
| |
| |
| _TEST_EXPORT int |
| rw_fnmatch (const char *pattern, const char *string, int arg) |
| { |
| RW_ASSERT (pattern); |
| RW_ASSERT (string); |
| |
| const char *next = string; |
| |
| bool esc = false; |
| |
| for (const char *pc = pattern; ; ++pc) { |
| |
| switch (const char c = *pc) { |
| case '\0': |
| return !!*next; |
| |
| case '\\': |
| if (esc) { |
| if (c != *next) |
| return 1; |
| |
| esc = false; |
| ++next; |
| } |
| else |
| esc = true; |
| |
| break; |
| |
| case '*': |
| if (esc) { |
| if (c != *next) |
| return 1; |
| |
| esc = false; |
| |
| ++next; |
| } |
| else { |
| |
| // skip any subsequent asterisks as an optimization |
| for ( ; '*' == pc [1]; ++pc); |
| |
| for ( ; *next; ++next) { |
| // try to match the rest of the string against |
| // the rest of the pattern after the asterisk |
| if (0 == rw_fnmatch (pc + 1, next, arg)) |
| return 0; |
| } |
| } |
| |
| break; |
| |
| case '?': |
| if (esc) { |
| if (c != *next) |
| return 1; |
| |
| esc = false; |
| } |
| |
| if ('\0' == *next) |
| return 1; |
| |
| ++next; |
| break; |
| |
| case '[': |
| if (esc) { |
| if (c != *next) |
| return 1; |
| |
| esc = false; |
| } |
| else { |
| if ('\0' == *next) |
| return 1; |
| |
| pc = _rw_bracketmatch (pc + 1, UChar (*next), arg); |
| if (0 == pc) |
| return 1; |
| } |
| |
| ++next; |
| break; |
| |
| default: |
| if (c != *next) |
| return 1; |
| |
| esc = false; |
| |
| ++next; |
| } |
| |
| if ('\0' == *next) { |
| // reached the end of string: this is a match if the end |
| // of the pattern has also been reached or if the rest |
| // of the pattern in all asterisks |
| if (!esc) |
| while ('*' == *++pc); |
| return !!*pc; |
| } |
| } |
| |
| return 1; |
| } |