aboutsummaryrefslogtreecommitdiffstats
path: root/lib/wildmatch.c
blob: bda50b8ed99bced87d90ac46f818154da043496b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
**  Do shell-style pattern matching for ?, \, [], and * characters.
**  It is 8bit clean.
**
**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
**  Rich $alz is now <rsalz@bbn.com>.
**
**  Modified by Wayne Davison to special-case '/' matching, to make '**'
**  work differently than '*', and to fix the character-class code.
*/

#include "rsync.h"

/* What character marks an inverted character class? */
#define NEGATE_CLASS '!'

#define FALSE 0
#define TRUE 1
#define ABORT_ALL -1
#define ABORT_TO_STARSTAR -2

#ifdef WILD_TEST_ITERATIONS
int wildmatch_iteration_count;
#endif

static int domatch(const char *p, const char *text)
{
    int matched, special;
    char ch, prev;

#ifdef WILD_TEST_ITERATIONS
    wildmatch_iteration_count++;
#endif

    for ( ; (ch = *p) != '\0'; text++, p++) {
	if (*text == '\0' && ch != '*')
	    return FALSE;
	switch (ch) {
	  case '\\':
	    /* Literal match with following character.  Note that the test
	     * in "default" handles the p[1] == '\0' failure case. */
	    ch = *++p;
	    /* FALLTHROUGH */
	  default:
	    if (*text != ch)
		return FALSE;
	    continue;
	  case '?':
	    /* Match anything but '/'. */
	    if (*text == '/')
		return FALSE;
	    continue;
	  case '*':
	    if (*++p == '*') {
		while (*++p == '*') {}
		special = TRUE;
	    }
	    else
		special = FALSE;
	    if (*p == '\0') {
		/* Trailing "**" matches everything.  Trailing "*" matches
		 * only if there are no more slash characters. */
		return special? TRUE : strchr(text, '/') == 0;
	    }
	    for ( ; *text; text++) {
		if ((matched = domatch(p, text)) != FALSE) {
		    if (!special || matched != ABORT_TO_STARSTAR)
			return matched;
		}
		else if (!special && *text == '/')
		    return ABORT_TO_STARSTAR;
	    }
	    return ABORT_ALL;
	  case '[':
	    special = *++p == NEGATE_CLASS ? TRUE : FALSE;
	    if (special) {
		/* Inverted character class. */
		p++;
	    }
	    prev = 0;
	    matched = FALSE;
	    ch = *p;
	    if (ch == ']' || ch == '-') {
		if (*text == ch)
		    matched = TRUE;
		prev = ch;
		ch = *++p;
	    }
	    for ( ; ch != ']'; prev = ch, ch = *++p) {
		if (!ch)
		    return FALSE;
		if (ch == '-' && prev && p[1] && p[1] != ']') {
		    if (*text <= *++p && *text >= prev)
			matched = TRUE;
		    ch = 0; /* This makes "prev" get set to 0. */
		}
		else if (*text == ch)
		    matched = TRUE;
	    }
	    if (matched == special)
		return FALSE;
	    continue;
	}
    }

    return *text == '\0';
}

int wildmatch(const char *p, const char *text)
{
#ifdef WILD_TEST_ITERATIONS
    wildmatch_iteration_count = 0;
#endif
    return domatch(p, text) == TRUE;
}