wildmatch.con commit wildmatch: adjust "**" behavior (40bbee0)
   1/*
   2**  Do shell-style pattern matching for ?, \, [], and * characters.
   3**  It is 8bit clean.
   4**
   5**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
   6**  Rich $alz is now <rsalz@bbn.com>.
   7**
   8**  Modified by Wayne Davison to special-case '/' matching, to make '**'
   9**  work differently than '*', and to fix the character-class code.
  10*/
  11
  12#include "cache.h"
  13#include "wildmatch.h"
  14
  15typedef unsigned char uchar;
  16
  17/* What character marks an inverted character class? */
  18#define NEGATE_CLASS    '!'
  19#define NEGATE_CLASS2   '^'
  20
  21#define FALSE 0
  22#define TRUE 1
  23
  24#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
  25                                    && *(class) == *(litmatch) \
  26                                    && strncmp((char*)class, litmatch, len) == 0)
  27
  28#if defined STDC_HEADERS || !defined isascii
  29# define ISASCII(c) 1
  30#else
  31# define ISASCII(c) isascii(c)
  32#endif
  33
  34#ifdef isblank
  35# define ISBLANK(c) (ISASCII(c) && isblank(c))
  36#else
  37# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
  38#endif
  39
  40#ifdef isgraph
  41# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
  42#else
  43# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
  44#endif
  45
  46#define ISPRINT(c) (ISASCII(c) && isprint(c))
  47#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
  48#define ISALNUM(c) (ISASCII(c) && isalnum(c))
  49#define ISALPHA(c) (ISASCII(c) && isalpha(c))
  50#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
  51#define ISLOWER(c) (ISASCII(c) && islower(c))
  52#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
  53#define ISSPACE(c) (ISASCII(c) && isspace(c))
  54#define ISUPPER(c) (ISASCII(c) && isupper(c))
  55#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
  56
  57/* Match pattern "p" against "text" */
  58static int dowild(const uchar *p, const uchar *text, int force_lower_case)
  59{
  60        uchar p_ch;
  61
  62        for ( ; (p_ch = *p) != '\0'; text++, p++) {
  63                int matched, special;
  64                uchar t_ch, prev_ch;
  65                if ((t_ch = *text) == '\0' && p_ch != '*')
  66                        return ABORT_ALL;
  67                if (force_lower_case && ISUPPER(t_ch))
  68                        t_ch = tolower(t_ch);
  69                if (force_lower_case && ISUPPER(p_ch))
  70                        p_ch = tolower(p_ch);
  71                switch (p_ch) {
  72                case '\\':
  73                        /* Literal match with following character.  Note that the test
  74                         * in "default" handles the p[1] == '\0' failure case. */
  75                        p_ch = *++p;
  76                        /* FALLTHROUGH */
  77                default:
  78                        if (t_ch != p_ch)
  79                                return NOMATCH;
  80                        continue;
  81                case '?':
  82                        /* Match anything but '/'. */
  83                        if (t_ch == '/')
  84                                return NOMATCH;
  85                        continue;
  86                case '*':
  87                        if (*++p == '*') {
  88                                const uchar *prev_p = p - 2;
  89                                while (*++p == '*') {}
  90                                if ((prev_p == text || *prev_p == '/') ||
  91                                    (*p == '\0' || *p == '/' ||
  92                                     (p[0] == '\\' && p[1] == '/'))) {
  93                                        special = TRUE;
  94                                } else
  95                                        return ABORT_MALFORMED;
  96                        } else
  97                                special = FALSE;
  98                        if (*p == '\0') {
  99                                /* Trailing "**" matches everything.  Trailing "*" matches
 100                                 * only if there are no more slash characters. */
 101                                if (!special) {
 102                                        if (strchr((char*)text, '/') != NULL)
 103                                                return NOMATCH;
 104                                }
 105                                return MATCH;
 106                        }
 107                        while (1) {
 108                                if (t_ch == '\0')
 109                                        break;
 110                                if ((matched = dowild(p, text,  force_lower_case)) != NOMATCH) {
 111                                        if (!special || matched != ABORT_TO_STARSTAR)
 112                                                return matched;
 113                                } else if (!special && t_ch == '/')
 114                                        return ABORT_TO_STARSTAR;
 115                                t_ch = *++text;
 116                        }
 117                        return ABORT_ALL;
 118                case '[':
 119                        p_ch = *++p;
 120#ifdef NEGATE_CLASS2
 121                        if (p_ch == NEGATE_CLASS2)
 122                                p_ch = NEGATE_CLASS;
 123#endif
 124                        /* Assign literal TRUE/FALSE because of "matched" comparison. */
 125                        special = p_ch == NEGATE_CLASS? TRUE : FALSE;
 126                        if (special) {
 127                                /* Inverted character class. */
 128                                p_ch = *++p;
 129                        }
 130                        prev_ch = 0;
 131                        matched = FALSE;
 132                        do {
 133                                if (!p_ch)
 134                                        return ABORT_ALL;
 135                                if (p_ch == '\\') {
 136                                        p_ch = *++p;
 137                                        if (!p_ch)
 138                                                return ABORT_ALL;
 139                                        if (t_ch == p_ch)
 140                                                matched = TRUE;
 141                                } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
 142                                        p_ch = *++p;
 143                                        if (p_ch == '\\') {
 144                                                p_ch = *++p;
 145                                                if (!p_ch)
 146                                                        return ABORT_ALL;
 147                                        }
 148                                        if (t_ch <= p_ch && t_ch >= prev_ch)
 149                                                matched = TRUE;
 150                                        p_ch = 0; /* This makes "prev_ch" get set to 0. */
 151                                } else if (p_ch == '[' && p[1] == ':') {
 152                                        const uchar *s;
 153                                        int i;
 154                                        for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
 155                                        if (!p_ch)
 156                                                return ABORT_ALL;
 157                                        i = p - s - 1;
 158                                        if (i < 0 || p[-1] != ':') {
 159                                                /* Didn't find ":]", so treat like a normal set. */
 160                                                p = s - 2;
 161                                                p_ch = '[';
 162                                                if (t_ch == p_ch)
 163                                                        matched = TRUE;
 164                                                continue;
 165                                        }
 166                                        if (CC_EQ(s,i, "alnum")) {
 167                                                if (ISALNUM(t_ch))
 168                                                        matched = TRUE;
 169                                        } else if (CC_EQ(s,i, "alpha")) {
 170                                                if (ISALPHA(t_ch))
 171                                                        matched = TRUE;
 172                                        } else if (CC_EQ(s,i, "blank")) {
 173                                                if (ISBLANK(t_ch))
 174                                                        matched = TRUE;
 175                                        } else if (CC_EQ(s,i, "cntrl")) {
 176                                                if (ISCNTRL(t_ch))
 177                                                        matched = TRUE;
 178                                        } else if (CC_EQ(s,i, "digit")) {
 179                                                if (ISDIGIT(t_ch))
 180                                                        matched = TRUE;
 181                                        } else if (CC_EQ(s,i, "graph")) {
 182                                                if (ISGRAPH(t_ch))
 183                                                        matched = TRUE;
 184                                        } else if (CC_EQ(s,i, "lower")) {
 185                                                if (ISLOWER(t_ch))
 186                                                        matched = TRUE;
 187                                        } else if (CC_EQ(s,i, "print")) {
 188                                                if (ISPRINT(t_ch))
 189                                                        matched = TRUE;
 190                                        } else if (CC_EQ(s,i, "punct")) {
 191                                                if (ISPUNCT(t_ch))
 192                                                        matched = TRUE;
 193                                        } else if (CC_EQ(s,i, "space")) {
 194                                                if (ISSPACE(t_ch))
 195                                                        matched = TRUE;
 196                                        } else if (CC_EQ(s,i, "upper")) {
 197                                                if (ISUPPER(t_ch))
 198                                                        matched = TRUE;
 199                                        } else if (CC_EQ(s,i, "xdigit")) {
 200                                                if (ISXDIGIT(t_ch))
 201                                                        matched = TRUE;
 202                                        } else /* malformed [:class:] string */
 203                                                return ABORT_ALL;
 204                                        p_ch = 0; /* This makes "prev_ch" get set to 0. */
 205                                } else if (t_ch == p_ch)
 206                                        matched = TRUE;
 207                        } while (prev_ch = p_ch, (p_ch = *++p) != ']');
 208                        if (matched == special || t_ch == '/')
 209                                return NOMATCH;
 210                        continue;
 211                }
 212        }
 213
 214        return *text ? NOMATCH : MATCH;
 215}
 216
 217/* Match the "pattern" against the "text" string. */
 218int wildmatch(const char *pattern, const char *text, int flags)
 219{
 220        return dowild((const uchar*)pattern, (const uchar*)text,
 221                      flags & FNM_CASEFOLD ? 1 :0);
 222}