Changed some names since "depth" wasn't really the right term.
[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? */
15#define NEGATE_CLASS '!'
16
710faea9
WD
17#define FALSE 0
18#define TRUE 1
20b2e9ce
WD
19#define ABORT_ALL -1
20#define ABORT_TO_STARSTAR -2
21
d5c973cc
WD
22#ifdef WILD_TEST_ITERATIONS
23int wildmatch_iteration_count;
20b2e9ce 24#endif
446ee5b1 25
710faea9 26static int domatch(const char *p, const char *text)
446ee5b1
WD
27{
28 int matched, special;
29 char ch, prev;
30
d5c973cc
WD
31#ifdef WILD_TEST_ITERATIONS
32 wildmatch_iteration_count++;
20b2e9ce
WD
33#endif
34
446ee5b1
WD
35 for ( ; (ch = *p) != '\0'; text++, p++) {
36 if (*text == '\0' && ch != '*')
710faea9 37 return FALSE;
446ee5b1
WD
38 switch (ch) {
39 case '\\':
40 /* Literal match with following character. Note that the test
41 * in "default" handles the p[1] == '\0' failure case. */
42 ch = *++p;
43 /* FALLTHROUGH */
44 default:
45 if (*text != ch)
710faea9 46 return FALSE;
446ee5b1
WD
47 continue;
48 case '?':
49 /* Match anything but '/'. */
50 if (*text == '/')
710faea9 51 return FALSE;
446ee5b1
WD
52 continue;
53 case '*':
54 if (*++p == '*') {
55 while (*++p == '*') {}
710faea9 56 special = TRUE;
446ee5b1
WD
57 }
58 else
710faea9 59 special = FALSE;
446ee5b1 60 if (*p == '\0') {
710faea9
WD
61 /* Trailing "**" matches everything. Trailing "*" matches
62 * only if there are no more slash characters. */
63 return special? TRUE : strchr(text, '/') == 0;
446ee5b1
WD
64 }
65 for ( ; *text; text++) {
20b2e9ce
WD
66 if ((matched = domatch(p, text)) != FALSE) {
67 if (!special || matched != ABORT_TO_STARSTAR)
68 return matched;
69 }
70 else if (!special && *text == '/')
71 return ABORT_TO_STARSTAR;
446ee5b1 72 }
20b2e9ce 73 return ABORT_ALL;
446ee5b1 74 case '[':
710faea9 75 special = *++p == NEGATE_CLASS ? TRUE : FALSE;
446ee5b1
WD
76 if (special) {
77 /* Inverted character class. */
78 p++;
79 }
80 prev = 0;
710faea9 81 matched = FALSE;
446ee5b1
WD
82 ch = *p;
83 if (ch == ']' || ch == '-') {
84 if (*text == ch)
710faea9 85 matched = TRUE;
446ee5b1
WD
86 prev = ch;
87 ch = *++p;
88 }
89 for ( ; ch != ']'; prev = ch, ch = *++p) {
90 if (!ch)
710faea9 91 return FALSE;
446ee5b1
WD
92 if (ch == '-' && prev && p[1] && p[1] != ']') {
93 if (*text <= *++p && *text >= prev)
710faea9 94 matched = TRUE;
7a1f46b6 95 ch = 0; /* This makes "prev" get set to 0. */
446ee5b1
WD
96 }
97 else if (*text == ch)
710faea9 98 matched = TRUE;
446ee5b1
WD
99 }
100 if (matched == special)
710faea9 101 return FALSE;
446ee5b1
WD
102 continue;
103 }
104 }
105
106 return *text == '\0';
107}
710faea9
WD
108
109int wildmatch(const char *p, const char *text)
110{
d5c973cc
WD
111#ifdef WILD_TEST_ITERATIONS
112 wildmatch_iteration_count = 0;
20b2e9ce 113#endif
710faea9
WD
114 return domatch(p, text) == TRUE;
115}