- Added [:class:] handling to the character-class code.
[rsync/rsync.git] / lib / wildmatch.c
CommitLineData
446ee5b1
WD
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**
7a1f46b6
WD
8** Modified by Wayne Davison to special-case '/' matching, to make '**'
9** work differently than '*', and to fix the character-class code.
446ee5b1
WD
10*/
11
12#include "rsync.h"
13
14/* What character marks an inverted character class? */
e11c4251
WD
15#define NEGATE_CLASS '!'
16#define NEGATE_CLASS2 '^'
446ee5b1 17
710faea9
WD
18#define FALSE 0
19#define TRUE 1
20b2e9ce
WD
20#define ABORT_ALL -1
21#define ABORT_TO_STARSTAR -2
22
e11c4251
WD
23#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 && strncmp(class, litmatch, len) == 0)
24
d5c973cc
WD
25#ifdef WILD_TEST_ITERATIONS
26int wildmatch_iteration_count;
20b2e9ce 27#endif
446ee5b1 28
e11c4251 29static int domatch(const unsigned char *p, const unsigned char *text)
446ee5b1
WD
30{
31 int matched, special;
e11c4251 32 unsigned char ch, prev;
446ee5b1 33
d5c973cc
WD
34#ifdef WILD_TEST_ITERATIONS
35 wildmatch_iteration_count++;
20b2e9ce
WD
36#endif
37
446ee5b1
WD
38 for ( ; (ch = *p) != '\0'; text++, p++) {
39 if (*text == '\0' && ch != '*')
710faea9 40 return FALSE;
446ee5b1
WD
41 switch (ch) {
42 case '\\':
43 /* Literal match with following character. Note that the test
44 * in "default" handles the p[1] == '\0' failure case. */
45 ch = *++p;
46 /* FALLTHROUGH */
47 default:
48 if (*text != ch)
710faea9 49 return FALSE;
446ee5b1
WD
50 continue;
51 case '?':
52 /* Match anything but '/'. */
53 if (*text == '/')
710faea9 54 return FALSE;
446ee5b1
WD
55 continue;
56 case '*':
57 if (*++p == '*') {
58 while (*++p == '*') {}
710faea9 59 special = TRUE;
446ee5b1
WD
60 }
61 else
710faea9 62 special = FALSE;
446ee5b1 63 if (*p == '\0') {
710faea9
WD
64 /* Trailing "**" matches everything. Trailing "*" matches
65 * only if there are no more slash characters. */
c9a59880 66 return special? TRUE : strchr(text, '/') == NULL;
446ee5b1
WD
67 }
68 for ( ; *text; text++) {
20b2e9ce
WD
69 if ((matched = domatch(p, text)) != FALSE) {
70 if (!special || matched != ABORT_TO_STARSTAR)
71 return matched;
72 }
73 else if (!special && *text == '/')
74 return ABORT_TO_STARSTAR;
446ee5b1 75 }
20b2e9ce 76 return ABORT_ALL;
446ee5b1 77 case '[':
c9a59880 78 ch = *++p;
e11c4251
WD
79#ifdef NEGATE_CLASS2
80 if (ch == NEGATE_CLASS2)
81 ch = NEGATE_CLASS;
82#endif
c9a59880
WD
83 /* Assign literal TRUE/FALSE because of "matched" comparison. */
84 special = ch == NEGATE_CLASS? TRUE : FALSE;
446ee5b1
WD
85 if (special) {
86 /* Inverted character class. */
c9a59880 87 ch = *++p;
446ee5b1
WD
88 }
89 prev = 0;
710faea9 90 matched = FALSE;
c9a59880 91 do {
446ee5b1 92 if (!ch)
710faea9 93 return FALSE;
e11c4251
WD
94 if (ch == '\\') {
95 ch = *++p;
96 if (!ch)
97 return FALSE;
98 if (*text == ch)
99 matched = TRUE;
100 }
101 else if (ch == '-' && prev && p[1] && p[1] != ']') {
102 ch = *++p;
103 if (ch == '\\') {
104 ch = *++p;
105 if (!ch)
106 return FALSE;
107 }
108 if (*text <= ch && *text >= prev)
109 matched = TRUE;
110 ch = 0; /* This makes "prev" get set to 0. */
111 }
112 else if (ch == '[' && p[1] == ':') {
113 unsigned const char *s = p += 2;
114 int i;
115 while ((ch = *p) && (ch != ':' || p[1] != ']')) p++;
116 if (!ch)
117 return FALSE;
118 i = p - s;
119 ch = *text;
120 if ((CC_EQ(s,i, "alnum") && isalnum(ch))
121 || (CC_EQ(s,i, "alpha") && isalpha(ch))
122 || (CC_EQ(s,i, "blank") && isblank(ch))
123 || (CC_EQ(s,i, "cntrl") && iscntrl(ch))
124 || (CC_EQ(s,i, "digit") && isdigit(ch))
125 || (CC_EQ(s,i, "graph") && isgraph(ch))
126 || (CC_EQ(s,i, "lower") && islower(ch))
127 || (CC_EQ(s,i, "print") && isprint(ch))
128 || (CC_EQ(s,i, "punct") && ispunct(ch))
129 || (CC_EQ(s,i, "space") && isspace(ch))
130 || (CC_EQ(s,i, "upper") && isupper(ch))
131 || (CC_EQ(s,i,"xdigit") && isxdigit(ch)))
710faea9 132 matched = TRUE;
e11c4251 133 p++;
7a1f46b6 134 ch = 0; /* This makes "prev" get set to 0. */
446ee5b1
WD
135 }
136 else if (*text == ch)
710faea9 137 matched = TRUE;
c9a59880 138 } while (prev = ch, (ch = *++p) != ']');
446ee5b1 139 if (matched == special)
710faea9 140 return FALSE;
446ee5b1
WD
141 continue;
142 }
143 }
144
145 return *text == '\0';
146}
710faea9 147
e11c4251
WD
148/* Find the pattern (p) in the text string (t). */
149int wildmatch(const char *p, const char *t)
710faea9 150{
d5c973cc
WD
151#ifdef WILD_TEST_ITERATIONS
152 wildmatch_iteration_count = 0;
20b2e9ce 153#endif
e11c4251 154 return domatch((const unsigned char*)p, (const unsigned char*)t) == TRUE;
710faea9 155}