]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/match.c
79699ea0d8740b7cf555af732d3c22ec1dfc9861
[ngircd-alex.git] / src / ngircd / match.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Wildcard pattern matching
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <string.h>
22
23 #include "exp.h"
24 #include "match.h"
25 #include "defines.h"
26 #include "tool.h"
27
28
29 /*
30  * The pattern matching functions [Matche(), Matche_After_Star()] are based
31  * on code of J. Kercheval. Version 1.1 has been released on 1991-03-12 as
32  * "public domain": <http://c.snippets.org/snip_lister.php?fname=match.c>
33  */
34
35
36 static int Matche PARAMS(( const char *p, const char *t ));
37 static int Matche_After_Star PARAMS(( const char *p, const char *t ));
38
39
40 #define MATCH_PATTERN   6       /**< bad pattern */
41 #define MATCH_LITERAL   5       /**< match failure on literal match */
42 #define MATCH_RANGE     4       /**< match failure on [..] construct */
43 #define MATCH_ABORT     3       /**< premature end of text string */
44 #define MATCH_END       2       /**< premature end of pattern string */
45 #define MATCH_VALID     1       /**< valid match */
46
47
48 /**
49  * Match string with pattern.
50  *
51  * @param Pattern       Pattern to match with
52  * @param String        Input string
53  * @return              true if pattern matches
54  */
55 GLOBAL bool
56 Match( const char *Pattern, const char *String )
57 {
58         /* Pattern mit String vergleichen */
59         if( Matche( Pattern, String ) == MATCH_VALID ) return true;
60         else return false;
61 } /* Match */
62
63
64 /**
65  * Match string with pattern case-insensitive.
66  *
67  * @param pattern       Pattern to match with
68  * @param searchme      Input string, at most COMMAND_LEN-1 characters long
69  * @return              true if pattern matches
70  */
71 GLOBAL bool
72 MatchCaseInsensitive(const char *pattern, const char *searchme)
73 {
74         char haystack[COMMAND_LEN];
75
76         strlcpy(haystack, searchme, sizeof(haystack));
77         return Match(pattern, ngt_LowerStr(haystack));
78 } /* MatchCaseInsensitive */
79
80
81 static int
82 Matche( const char *p, const char *t )
83 {
84         register char range_start, range_end;
85         bool invert;
86         bool member_match;
87         bool loop;
88
89         for( ; *p; p++, t++ )
90         {
91                 /* if this is the end of the text then this is the end of the match */
92                 if( ! *t )
93                 {
94                         return ( *p == '*' && *++p == '\0' ) ? MATCH_VALID : MATCH_ABORT;
95                 }
96
97                 /* determine and react to pattern type */
98                 switch( *p )
99                 {
100                         case '?':       /* single any character match */
101                                 break;
102
103                         case '*':       /* multiple any character match */
104                                 return Matche_After_Star( p, t );
105
106                         case '[':       /* [..] construct, single member/exclusion character match */
107                                 /* move to beginning of range */
108                                 p++;
109
110                                 /* check if this is a member match or exclusion match */
111                                 invert = false;
112                                 if( *p == '!' || *p == '^' )
113                                 {
114                                         invert = true;
115                                         p++;
116                                 }
117
118                                 /* if closing bracket here or at range start then we have a malformed pattern */
119                                 if ( *p == ']' ) return MATCH_PATTERN;
120
121                                 member_match = false;
122                                 loop = true;
123
124                                 while( loop )
125                                 {
126                                         /* if end of construct then loop is done */
127                                         if( *p == ']' )
128                                         {
129                                                 loop = false;
130                                                 continue;
131                                         }
132
133                                         /* matching a '!', '^', '-', '\' or a ']' */
134                                         if( *p == '\\' ) range_start = range_end = *++p;
135                                         else  range_start = range_end = *p;
136
137                                         /* if end of pattern then bad pattern (Missing ']') */
138                                         if( ! *p ) return MATCH_PATTERN;
139
140                                         /* check for range bar */
141                                         if( *++p == '-' )
142                                         {
143                                                 /* get the range end */
144                                                 range_end = *++p;
145
146                                                 /* if end of pattern or construct then bad pattern */
147                                                 if( range_end == '\0' || range_end == ']' ) return MATCH_PATTERN;
148
149                                                 /* special character range end */
150                                                 if( range_end == '\\' )
151                                                 {
152                                                         range_end = *++p;
153
154                                                         /* if end of text then we have a bad pattern */
155                                                         if ( ! range_end ) return MATCH_PATTERN;
156                                                 }
157
158                                                 /* move just beyond this range */
159                                                 p++;
160                                         }
161
162                                         /* if the text character is in range then match found. make sure the range
163                                          * letters have the proper relationship to one another before comparison */
164                                         if( range_start < range_end )
165                                         {
166                                                 if( *t >= range_start && *t <= range_end )
167                                                 {
168                                                         member_match = true;
169                                                         loop = false;
170                                                 }
171                                         }
172                                         else
173                                         {
174                                                 if( *t >= range_end && *t <= range_start )
175                                                 {
176                                                         member_match = true;
177                                                         loop = false;
178                                                 }
179                                         }
180                                 }
181
182                                 /* if there was a match in an exclusion set then no match */
183                                 /* if there was no match in a member set then no match */
184                                 if(( invert && member_match ) || ! ( invert || member_match )) return MATCH_RANGE;
185
186                                 /* if this is not an exclusion then skip the rest of the [...]
187                                  * construct that already matched. */
188                                 if( member_match )
189                                 {
190                                         while( *p != ']' )
191                                         {
192                                                 /* bad pattern (Missing ']') */
193                                                 if( ! *p ) return MATCH_PATTERN;
194
195                                                 /* skip exact match */
196                                                 if( *p == '\\' )
197                                                 {
198                                                         p++;
199
200                                                         /* if end of text then we have a bad pattern */
201                                                         if( ! *p ) return MATCH_PATTERN;
202                                                 }
203
204                                                 /* move to next pattern char */
205                                                 p++;
206                                         }
207                                 }
208                                 break;
209                         case '\\':      /* next character is quoted and must match exactly */
210                                 /* move pattern pointer to quoted char and fall through */
211                                 p++;
212
213                                 /* if end of text then we have a bad pattern */
214                                 if( ! *p ) return MATCH_PATTERN;
215
216                                 /* must match this character exactly */
217                         default:
218                                 if( *p != *t ) return MATCH_LITERAL;
219                 }
220         }
221         /* if end of text not reached then the pattern fails */
222
223         if( *t ) return MATCH_END;
224         else return MATCH_VALID;
225 } /* Matche */
226
227
228 static int
229 Matche_After_Star( const char *p, const char *t )
230 {
231         register int nextp, match = 0;
232
233         /* pass over existing ? and * in pattern */
234         while( *p == '?' || *p == '*' )
235         {
236                 /* take one char for each ? and + */
237                 if (*p == '?')
238                 {
239                         /* if end of text then no match */
240                         if( ! *t++ ) return MATCH_ABORT;
241                 }
242
243                 /* move to next char in pattern */
244                 p++;
245         }
246
247         /* if end of pattern we have matched regardless of text left */
248         if( ! *p ) return MATCH_VALID;
249
250         /* get the next character to match which must be a literal or '[' */
251         nextp = *p;
252         if( nextp == '\\' )
253         {
254                 nextp = p[1];
255
256                 /* if end of text then we have a bad pattern */
257                 if( ! nextp ) return MATCH_PATTERN;
258         }
259
260         /* Continue until we run out of text or definite result seen */
261         do
262         {
263                 /* a precondition for matching is that the next character
264                  * in the pattern match the next character in the text or that
265                  * the next pattern char is the beginning of a range.  Increment
266                  * text pointer as we go here */
267                 if( nextp == *t || nextp == '[' ) match = Matche( p, t );
268
269                 /* if the end of text is reached then no match */
270                 if( ! *t++ ) match = MATCH_ABORT;
271         } while( match != MATCH_VALID && match != MATCH_ABORT && match != MATCH_PATTERN );
272
273         /* return result */
274         return match;
275 } /* Matche_After_Star */
276
277
278 /* -eof- */