summaryrefslogtreecommitdiff
path: root/gnu/usr.sbin/sendmail/libsm/match.c
blob: 59213e124572929cb59147f085f072981ffcc1e8 (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 */

#include <sm/gen.h>
SM_RCSID("@(#)$Sendmail: match.c,v 1.10 2001/09/11 04:04:48 gshapiro Exp $")

#include <sm/string.h>

/*
**  SM_MATCH -- Match a character string against a glob pattern.
**
**	Parameters:
**		str -- string.
**		par -- pattern to find in str.
**
**	Returns:
**		true on match, false on non-match.
**
**  A pattern consists of normal characters, which match themselves,
**  and meta-sequences.  A * matches any sequence of characters.
**  A ? matches any single character.  A [ introduces a character class.
**  A ] marks the end of a character class; if the ] is missing then
**  the [ matches itself rather than introducing a character class.
**  A character class matches any of the characters between the brackets.
**  The range of characters from X to Y inclusive is written X-Y.
**  If the first character after the [ is ! then the character class is
**  complemented.
**
**  To include a ] in a character class, make it the first character
**  listed (after the !, if any).  To include a -, make it the first
**  character listed (after the !, if any) or the last character.
**  It is impossible for a ] to be the final character in a range.
**  For glob patterns that literally match "*", "?" or "[",
**  use [*], [?] or [[].
*/

bool
sm_match(str, pat)
	const char *str;
	const char *pat;
{
	bool ccnot, ccmatch, ccfirst;
	const char *ccstart;
	char c, c2;

	for (;;)
	{
		switch (*pat)
		{
		  case '\0':
			return *str == '\0';
		  case '?':
			if (*str == '\0')
				return false;
			++pat;
			++str;
			continue;
		  case '*':
			++pat;
			if (*pat == '\0')
			{
				/* optimize case of trailing '*' */
				return true;
			}
			for (;;)
			{
				if (sm_match(pat, str))
					return true;
				if (*str == '\0')
					return false;
				++str;
			}
			/* NOTREACHED */
		  case '[':
			ccstart = pat++;
			ccnot = false;
			if (*pat == '!')
			{
				ccnot = true;
				++pat;
			}
			ccmatch = false;
			ccfirst = true;
			for (;;)
			{
				if (*pat == '\0')
				{
					pat = ccstart;
					goto defl;
				}
				if (*pat == ']' && !ccfirst)
					break;
				c = *pat++;
				ccfirst = false;
				if (*pat == '-' && pat[1] != ']')
				{
					++pat;
					if (*pat == '\0')
					{
						pat = ccstart;
						goto defl;
					}
					c2 = *pat++;
					if (*str >= c && *str <= c2)
						ccmatch = true;
				}
				else
				{
					if (*str == c)
						ccmatch = true;
				}
			}
			if (ccmatch ^ ccnot)
			{
				++pat;
				++str;
			}
			else
				return false;
			continue;
		default:
		defl:
			if (*pat != *str)
				return false;
			++pat;
			++str;
			continue;
		}
	}
}