Added some portability code for the ctype functions.
[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
f2ac84c3
WD
25#if defined STDC_HEADERS || !defined isascii
26# define ISASCII(c) 1
27#else
28# define ISASCII(c) isascii(c)
29#endif
30
31#ifdef isblank
32# define ISBLANK(c) (ISASCII(c) && isblank(c))
33#else
34# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
35#endif
36
37#ifdef isgraph
38# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
39#else
40# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
41#endif
42
43#define ISPRINT(c) (ISASCII(c) && isprint(c))
44#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
45#define ISALNUM(c) (ISASCII(c) && isalnum(c))
46#define ISALPHA(c) (ISASCII(c) && isalpha(c))
47#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
48#define ISLOWER(c) (ISASCII(c) && islower(c))
49#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
50#define ISSPACE(c) (ISASCII(c) && isspace(c))
51#define ISUPPER(c) (ISASCII(c) && isupper(c))
52#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
53
d5c973cc
WD
54#ifdef WILD_TEST_ITERATIONS
55int wildmatch_iteration_count;
20b2e9ce 56#endif
446ee5b1 57
e11c4251 58static int domatch(const unsigned char *p, const unsigned char *text)
446ee5b1
WD
59{
60 int matched, special;
e11c4251 61 unsigned char ch, prev;
446ee5b1 62
d5c973cc
WD
63#ifdef WILD_TEST_ITERATIONS
64 wildmatch_iteration_count++;
20b2e9ce
WD
65#endif
66
446ee5b1
WD
67 for ( ; (ch = *p) != '\0'; text++, p++) {
68 if (*text == '\0' && ch != '*')
710faea9 69 return FALSE;
446ee5b1
WD
70 switch (ch) {
71 case '\\':
72 /* Literal match with following character. Note that the test
73 * in "default" handles the p[1] == '\0' failure case. */
74 ch = *++p;
75 /* FALLTHROUGH */
76 default:
77 if (*text != ch)
710faea9 78 return FALSE;
446ee5b1
WD
79 continue;
80 case '?':
81 /* Match anything but '/'. */
82 if (*text == '/')
710faea9 83 return FALSE;
446ee5b1
WD
84 continue;
85 case '*':
86 if (*++p == '*') {
87 while (*++p == '*') {}
710faea9 88 special = TRUE;
446ee5b1
WD
89 }
90 else
710faea9 91 special = FALSE;
446ee5b1 92 if (*p == '\0') {
710faea9
WD
93 /* Trailing "**" matches everything. Trailing "*" matches
94 * only if there are no more slash characters. */
c9a59880 95 return special? TRUE : strchr(text, '/') == NULL;
446ee5b1
WD
96 }
97 for ( ; *text; text++) {
20b2e9ce
WD
98 if ((matched = domatch(p, text)) != FALSE) {
99 if (!special || matched != ABORT_TO_STARSTAR)
100 return matched;
101 }
102 else if (!special && *text == '/')
103 return ABORT_TO_STARSTAR;
446ee5b1 104 }
20b2e9ce 105 return ABORT_ALL;
446ee5b1 106 case '[':
c9a59880 107 ch = *++p;
e11c4251
WD
108#ifdef NEGATE_CLASS2
109 if (ch == NEGATE_CLASS2)
110 ch = NEGATE_CLASS;
111#endif
c9a59880
WD
112 /* Assign literal TRUE/FALSE because of "matched" comparison. */
113 special = ch == NEGATE_CLASS? TRUE : FALSE;
446ee5b1
WD
114 if (special) {
115 /* Inverted character class. */
c9a59880 116 ch = *++p;
446ee5b1
WD
117 }
118 prev = 0;
710faea9 119 matched = FALSE;
c9a59880 120 do {
446ee5b1 121 if (!ch)
710faea9 122 return FALSE;
e11c4251
WD
123 if (ch == '\\') {
124 ch = *++p;
125 if (!ch)
126 return FALSE;
127 if (*text == ch)
128 matched = TRUE;
129 }
130 else if (ch == '-' && prev && p[1] && p[1] != ']') {
131 ch = *++p;
132 if (ch == '\\') {
133 ch = *++p;
134 if (!ch)
135 return FALSE;
136 }
137 if (*text <= ch && *text >= prev)
138 matched = TRUE;
139 ch = 0; /* This makes "prev" get set to 0. */
140 }
141 else if (ch == '[' && p[1] == ':') {
142 unsigned const char *s = p += 2;
143 int i;
144 while ((ch = *p) && (ch != ':' || p[1] != ']')) p++;
145 if (!ch)
146 return FALSE;
147 i = p - s;
148 ch = *text;
f2ac84c3
WD
149 if ((CC_EQ(s,i, "alnum") && ISALNUM(ch))
150 || (CC_EQ(s,i, "alpha") && ISALPHA(ch))
151 || (CC_EQ(s,i, "blank") && ISBLANK(ch))
152 || (CC_EQ(s,i, "cntrl") && ISCNTRL(ch))
153 || (CC_EQ(s,i, "digit") && ISDIGIT(ch))
154 || (CC_EQ(s,i, "graph") && ISGRAPH(ch))
155 || (CC_EQ(s,i, "lower") && ISLOWER(ch))
156 || (CC_EQ(s,i, "print") && ISPRINT(ch))
157 || (CC_EQ(s,i, "punct") && ISPUNCT(ch))
158 || (CC_EQ(s,i, "space") && ISSPACE(ch))
159 || (CC_EQ(s,i, "upper") && ISUPPER(ch))
160 || (CC_EQ(s,i,"xdigit") && ISXDIGIT(ch)))
710faea9 161 matched = TRUE;
e11c4251 162 p++;
7a1f46b6 163 ch = 0; /* This makes "prev" get set to 0. */
446ee5b1
WD
164 }
165 else if (*text == ch)
710faea9 166 matched = TRUE;
c9a59880 167 } while (prev = ch, (ch = *++p) != ']');
446ee5b1 168 if (matched == special)
710faea9 169 return FALSE;
446ee5b1
WD
170 continue;
171 }
172 }
173
174 return *text == '\0';
175}
710faea9 176
e11c4251
WD
177/* Find the pattern (p) in the text string (t). */
178int wildmatch(const char *p, const char *t)
710faea9 179{
d5c973cc
WD
180#ifdef WILD_TEST_ITERATIONS
181 wildmatch_iteration_count = 0;
20b2e9ce 182#endif
e11c4251 183 return domatch((const unsigned char*)p, (const unsigned char*)t) == TRUE;
710faea9 184}