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