1/*
2 * Copyright (c) 2000-2001 Proofpoint, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: match.c,v 1.11 2013-11-22 20:51:43 ca Exp $")
13
14#include <sm/string.h>
15
16/*
17**  SM_MATCH -- Match a character string against a glob pattern.
18**
19**	Parameters:
20**		str -- string.
21**		par -- pattern to find in str.
22**
23**	Returns:
24**		true on match, false on non-match.
25**
26**  A pattern consists of normal characters, which match themselves,
27**  and meta-sequences.  A * matches any sequence of characters.
28**  A ? matches any single character.  A [ introduces a character class.
29**  A ] marks the end of a character class; if the ] is missing then
30**  the [ matches itself rather than introducing a character class.
31**  A character class matches any of the characters between the brackets.
32**  The range of characters from X to Y inclusive is written X-Y.
33**  If the first character after the [ is ! then the character class is
34**  complemented.
35**
36**  To include a ] in a character class, make it the first character
37**  listed (after the !, if any).  To include a -, make it the first
38**  character listed (after the !, if any) or the last character.
39**  It is impossible for a ] to be the final character in a range.
40**  For glob patterns that literally match "*", "?" or "[",
41**  use [*], [?] or [[].
42*/
43
44bool
45sm_match(str, pat)
46	const char *str;
47	const char *pat;
48{
49	bool ccnot, ccmatch, ccfirst;
50	const char *ccstart;
51	char c, c2;
52
53	for (;;)
54	{
55		switch (*pat)
56		{
57		  case '\0':
58			return *str == '\0';
59		  case '?':
60			if (*str == '\0')
61				return false;
62			++pat;
63			++str;
64			continue;
65		  case '*':
66			++pat;
67			if (*pat == '\0')
68			{
69				/* optimize case of trailing '*' */
70				return true;
71			}
72			for (;;)
73			{
74				if (sm_match(pat, str))
75					return true;
76				if (*str == '\0')
77					return false;
78				++str;
79			}
80			/* NOTREACHED */
81		  case '[':
82			ccstart = pat++;
83			ccnot = false;
84			if (*pat == '!')
85			{
86				ccnot = true;
87				++pat;
88			}
89			ccmatch = false;
90			ccfirst = true;
91			for (;;)
92			{
93				if (*pat == '\0')
94				{
95					pat = ccstart;
96					goto defl;
97				}
98				if (*pat == ']' && !ccfirst)
99					break;
100				c = *pat++;
101				ccfirst = false;
102				if (*pat == '-' && pat[1] != ']')
103				{
104					++pat;
105					if (*pat == '\0')
106					{
107						pat = ccstart;
108						goto defl;
109					}
110					c2 = *pat++;
111					if (*str >= c && *str <= c2)
112						ccmatch = true;
113				}
114				else
115				{
116					if (*str == c)
117						ccmatch = true;
118				}
119			}
120			if (ccmatch ^ ccnot)
121			{
122				++pat;
123				++str;
124			}
125			else
126				return false;
127			continue;
128		default:
129		defl:
130			if (*pat != *str)
131				return false;
132			++pat;
133			++str;
134			continue;
135		}
136	}
137}
138