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