Annotation of embedaddon/sudo/compat/fnmatch.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (c) 2008, 2010 Todd C. Miller <Todd.Miller@courtesan.com>
                      3:  * Copyright (c) 1989, 1993, 1994
                      4:  *     The Regents of the University of California.  All rights reserved.
                      5:  *
                      6:  * This code is derived from software contributed to Berkeley by
                      7:  * Guido van Rossum.
                      8:  *
                      9:  * Redistribution and use in source and binary forms, with or without
                     10:  * modification, are permitted provided that the following conditions
                     11:  * are met:
                     12:  * 1. Redistributions of source code must retain the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer.
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer in the
                     16:  *    documentation and/or other materials provided with the distribution.
                     17:  * 3. Neither the name of the University nor the names of its contributors
                     18:  *    may be used to endorse or promote products derived from this software
                     19:  *    without specific prior written permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     22:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     23:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     24:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     25:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     26:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     27:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     28:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     29:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     31:  * SUCH DAMAGE.
                     32:  */
                     33: 
                     34: /*
                     35:  * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
                     36:  * Compares a filename or pathname to a pattern.
                     37:  */
                     38: 
                     39: #include <config.h>
                     40: 
                     41: #include <sys/types.h>
                     42: 
                     43: #include <stdio.h>
                     44: #include <ctype.h>
                     45: #ifdef HAVE_STRING_H
                     46: # include <string.h>
                     47: #endif /* HAVE_STRING_H */
                     48: #ifdef HAVE_STRINGS_H
                     49: # include <strings.h>
                     50: #endif /* HAVE_STRINGS_H */
                     51: 
                     52: #include "missing.h"
                     53: #include "compat/charclass.h"
                     54: #include "compat/fnmatch.h"
                     55: 
                     56: #undef EOS
                     57: #define        EOS     '\0'
                     58: 
                     59: #define        RANGE_MATCH     1
                     60: #define        RANGE_NOMATCH   0
                     61: #define        RANGE_ERROR     (-1)
                     62: 
                     63: #if defined(LIBC_SCCS) && !defined(lint)
                     64: __unused static const char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $";
                     65: #endif /* LIBC_SCCS and not lint */
                     66: 
                     67: static int rangematch(const char *, int, int, char **);
                     68: static int classmatch(const char *, int, int, const char **);
                     69: 
                     70: int
                     71: rpl_fnmatch(const char *pattern, const char *string, int flags)
                     72: {
                     73:        const char *stringstart;
                     74:        char *newp;
                     75:        char c, test;
                     76: 
                     77:        for (stringstart = string;;)
                     78:                switch (c = *pattern++) {
                     79:                case EOS:
                     80:                        if (ISSET(flags, FNM_LEADING_DIR) && *string == '/')
                     81:                                return 0;
                     82:                        return *string == EOS ? 0 : FNM_NOMATCH;
                     83:                case '?':
                     84:                        if (*string == EOS)
                     85:                                return FNM_NOMATCH;
                     86:                        if (*string == '/' && ISSET(flags, FNM_PATHNAME))
                     87:                                return FNM_NOMATCH;
                     88:                        if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
                     89:                            (string == stringstart ||
                     90:                            (ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
                     91:                                return FNM_NOMATCH;
                     92:                        ++string;
                     93:                        break;
                     94:                case '*':
                     95:                        c = *pattern;
                     96:                        /* Collapse multiple stars. */
                     97:                        while (c == '*')
                     98:                                c = *++pattern;
                     99: 
                    100:                        if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
                    101:                            (string == stringstart ||
                    102:                            (ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
                    103:                                return FNM_NOMATCH;
                    104: 
                    105:                        /* Optimize for pattern with * at end or before /. */
                    106:                        if (c == EOS) {
                    107:                                if (ISSET(flags, FNM_PATHNAME))
                    108:                                        return (ISSET(flags, FNM_LEADING_DIR) ||
                    109:                                            strchr(string, '/') == NULL ?
                    110:                                            0 : FNM_NOMATCH);
                    111:                                else
                    112:                                        return 0;
                    113:                        } else if (c == '/' && ISSET(flags, FNM_PATHNAME)) {
                    114:                                if ((string = strchr(string, '/')) == NULL)
                    115:                                        return FNM_NOMATCH;
                    116:                                break;
                    117:                        }
                    118: 
                    119:                        /* General case, use recursion. */
                    120:                        while ((test = *string) != EOS) {
                    121:                                if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
                    122:                                        return 0;
                    123:                                if (test == '/' && ISSET(flags, FNM_PATHNAME))
                    124:                                        break;
                    125:                                ++string;
                    126:                        }
                    127:                        return FNM_NOMATCH;
                    128:                case '[':
                    129:                        if (*string == EOS)
                    130:                                return FNM_NOMATCH;
                    131:                        if (*string == '/' && ISSET(flags, FNM_PATHNAME))
                    132:                                return FNM_NOMATCH;
                    133:                        if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
                    134:                            (string == stringstart ||
                    135:                            (ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
                    136:                                return FNM_NOMATCH;
                    137: 
                    138:                        switch (rangematch(pattern, *string, flags, &newp)) {
                    139:                        case RANGE_ERROR:
                    140:                                /* not a good range, treat as normal text */
                    141:                                goto normal;
                    142:                        case RANGE_MATCH:
                    143:                                pattern = newp;
                    144:                                break;
                    145:                        case RANGE_NOMATCH:
                    146:                                return FNM_NOMATCH;
                    147:                        }
                    148:                        ++string;
                    149:                        break;
                    150:                case '\\':
                    151:                        if (!ISSET(flags, FNM_NOESCAPE)) {
                    152:                                if ((c = *pattern++) == EOS) {
                    153:                                        c = '\\';
                    154:                                        --pattern;
                    155:                                }
                    156:                        }
                    157:                        /* FALLTHROUGH */
                    158:                default:
                    159:                normal:
                    160:                        if (c != *string && !(ISSET(flags, FNM_CASEFOLD) &&
                    161:                                 (tolower((unsigned char)c) ==
                    162:                                 tolower((unsigned char)*string))))
                    163:                                return FNM_NOMATCH;
                    164:                        ++string;
                    165:                        break;
                    166:                }
                    167:        /* NOTREACHED */
                    168: }
                    169: 
                    170: static int
                    171: rangematch(const char *pattern, int test, int flags, char **newp)
                    172: {
                    173:        int negate, ok, rv;
                    174:        char c, c2;
                    175: 
                    176:        /*
                    177:         * A bracket expression starting with an unquoted circumflex
                    178:         * character produces unspecified results (IEEE 1003.2-1992,
                    179:         * 3.13.2).  This implementation treats it like '!', for
                    180:         * consistency with the regular expression syntax.
                    181:         * J.T. Conklin (conklin@ngai.kaleida.com)
                    182:         */
                    183:        if ((negate = (*pattern == '!' || *pattern == '^')))
                    184:                ++pattern;
                    185: 
                    186:        if (ISSET(flags, FNM_CASEFOLD))
                    187:                test = tolower(test);
                    188: 
                    189:        /*
                    190:         * A right bracket shall lose its special meaning and represent
                    191:         * itself in a bracket expression if it occurs first in the list.
                    192:         * -- POSIX.2 2.8.3.2
                    193:         */
                    194:        ok = 0;
                    195:        c = *pattern++;
                    196:        do {
                    197:                if (c == '[' && *pattern == ':') {
                    198:                        do {
                    199:                                rv = classmatch(pattern + 1, test,
                    200:                                    (flags & FNM_CASEFOLD), &pattern);
                    201:                                if (rv == RANGE_MATCH)
                    202:                                        ok = 1;
                    203:                                c = *pattern++;
                    204:                        } while (rv != RANGE_ERROR && c == '[' && *pattern == ':');
                    205:                        if (c == ']')
                    206:                        break;
                    207:                }
                    208:                if (c == '\\' && !ISSET(flags, FNM_NOESCAPE))
                    209:                        c = *pattern++;
                    210:                if (c == EOS)
                    211:                        return RANGE_ERROR;
                    212:                if (c == '/' && ISSET(flags, FNM_PATHNAME))
                    213:                        return RANGE_NOMATCH;
                    214:                if (ISSET(flags, FNM_CASEFOLD))
                    215:                        c = tolower((unsigned char)c);
                    216:                if (*pattern == '-'
                    217:                    && (c2 = *(pattern+1)) != EOS && c2 != ']') {
                    218:                        pattern += 2;
                    219:                        if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE))
                    220:                                c2 = *pattern++;
                    221:                        if (c2 == EOS)
                    222:                                return RANGE_ERROR;
                    223:                        if (ISSET(flags, FNM_CASEFOLD))
                    224:                                c2 = tolower((unsigned char)c2);
                    225:                        if (c <= test && test <= c2)
                    226:                                ok = 1;
                    227:                } else if (c == test)
                    228:                        ok = 1;
                    229:        } while ((c = *pattern++) != ']');
                    230: 
                    231:        *newp = (char *)pattern;
                    232:        return ok == negate ? RANGE_NOMATCH : RANGE_MATCH;
                    233: }
                    234: 
                    235: static int
                    236: classmatch(const char *pattern, int test, int foldcase, const char **ep)
                    237: {
                    238:        struct cclass *cc;
                    239:        const char *colon;
                    240:        size_t len;
                    241:        int rval = RANGE_NOMATCH;
                    242: 
                    243:        if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') {
                    244:                *ep = pattern - 2;
                    245:                return RANGE_ERROR;
                    246:        }
                    247:        *ep = colon + 2;
                    248:        len = (size_t)(colon - pattern);
                    249: 
                    250:        if (foldcase && strncmp(pattern, "upper:]", 7) == 0)
                    251:                pattern = "lower:]";
                    252:        for (cc = cclasses; cc->name != NULL; cc++) {
                    253:                if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') {
                    254:                        if (cc->isctype(test))
                    255:                                rval = RANGE_MATCH;
                    256:                        break;
                    257:                }
                    258:        }
                    259:        if (cc->name == NULL) {
                    260:                /* invalid character class, return EOS */
                    261:                *ep = colon + strlen(colon);
                    262:                rval = RANGE_ERROR;
                    263:        }
                    264:        return rval;
                    265: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>