xref: /freebsd/contrib/sendmail/libsm/match.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
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>
12 SM_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 
44 bool
45 sm_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