Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / kits / tracker / TrackerString.cpp
blobb559139c6161745670f25d5451601fd8fb1e2c87
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
35 #include "TrackerString.h"
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <strings.h>
42 // #pragma mark - TrackerString
45 TrackerString::TrackerString()
50 TrackerString::TrackerString(const char* string)
52 BString(string)
57 TrackerString::TrackerString(const TrackerString &string)
59 BString(string)
64 TrackerString::TrackerString(const char* string, int32 maxLength)
66 BString(string, maxLength)
71 TrackerString::~TrackerString()
76 bool
77 TrackerString::Matches(const char* string, bool caseSensitivity,
78 TrackerStringExpressionType expressionType) const
80 switch (expressionType) {
81 default:
82 case kNone:
83 return false;
85 case kStartsWith:
86 return StartsWith(string, caseSensitivity);
88 case kEndsWith:
89 return EndsWith(string, caseSensitivity);
91 case kContains:
92 return Contains(string, caseSensitivity);
94 case kGlobMatch:
95 return MatchesGlob(string, caseSensitivity);
97 case kRegexpMatch:
98 return MatchesRegExp(string, caseSensitivity);
103 bool
104 TrackerString::MatchesRegExp(const char* pattern, bool caseSensitivity) const
106 BString patternString(pattern);
107 BString textString(String());
109 if (caseSensitivity == false) {
110 patternString.ToLower();
111 textString.ToLower();
114 RegExp expression(patternString);
116 if (expression.InitCheck() != B_OK)
117 return false;
119 return expression.Matches(textString);
123 bool
124 TrackerString::MatchesGlob(const char* string, bool caseSensitivity) const
126 return StringMatchesPattern(String(), string, caseSensitivity);
130 bool
131 TrackerString::EndsWith(const char* string, bool caseSensitivity) const
133 // If "string" is longer than "this",
134 // we should simply return false
135 int32 position = Length() - (int32)strlen(string);
136 if (position < 0)
137 return false;
139 if (caseSensitivity)
140 return FindLast(string) == position;
141 else
142 return IFindLast(string) == position;
146 bool
147 TrackerString::StartsWith(const char* string, bool caseSensitivity) const
149 if (caseSensitivity)
150 return FindFirst(string) == 0;
151 else
152 return IFindFirst(string) == 0;
156 bool
157 TrackerString::Contains(const char* string, bool caseSensitivity) const
159 if (caseSensitivity)
160 return FindFirst(string) > -1;
161 else
162 return IFindFirst(string) > -1;
166 // MatchesBracketExpression() assumes 'pattern' to point to the
167 // character following the initial '[' in a bracket expression.
168 // The reason is that an encountered '[' will be taken literally.
169 // (Makes it possible to match a '[' with the expression '[[]').
170 bool
171 TrackerString::MatchesBracketExpression(const char* string,
172 const char* pattern, bool caseSensitivity) const
174 bool GlyphMatch = IsStartOfGlyph(string[0]);
176 if (IsInsideGlyph(string[0]))
177 return false;
179 char testChar = ConditionalToLower(string[0], caseSensitivity);
180 bool match = false;
182 bool inverse = *pattern == '^' || *pattern == '!';
183 // We allow both ^ and ! as a initial inverting character.
185 if (inverse)
186 pattern++;
188 while (!match && *pattern != ']' && *pattern != '\0') {
189 switch (*pattern) {
190 case '-':
192 char start = ConditionalToLower(*(pattern - 1),
193 caseSensitivity);
194 char stop = ConditionalToLower(*(pattern + 1),
195 caseSensitivity);
197 if (IsGlyph(start) || IsGlyph(stop))
198 return false;
199 // Not a valid range!
201 if ((islower(start) && islower(stop))
202 || (isupper(start) && isupper(stop))
203 || (isdigit(start) && isdigit(stop))) {
204 // Make sure 'start' and 'stop' are of the same type.
205 match = start <= testChar && testChar <= stop;
206 } else {
207 // If no valid range, we've got a syntax error.
208 return false;
211 break;
214 default:
215 if (GlyphMatch)
216 match = UTF8CharsAreEqual(string, pattern);
217 else
218 match = CharsAreEqual(testChar, *pattern, caseSensitivity);
219 break;
222 if (!match) {
223 pattern++;
224 if (IsInsideGlyph(pattern[0]))
225 pattern = MoveToEndOfGlyph(pattern);
229 // Consider an unmatched bracket a failure
230 // (i.e. when detecting a '\0' instead of a ']'.)
231 if (*pattern == '\0')
232 return false;
234 return (match ^ inverse) != 0;
238 bool
239 TrackerString::StringMatchesPattern(const char* string, const char* pattern,
240 bool caseSensitivity) const
242 // One could do this dynamically, counting the number of *'s,
243 // but then you have to free them at every exit of this
244 // function, which is awkward and ugly.
245 const int32 kWildCardMaximum = 100;
246 const char* pStorage[kWildCardMaximum];
247 const char* sStorage[kWildCardMaximum];
249 int32 patternLevel = 0;
251 if (string == NULL || pattern == NULL)
252 return false;
254 while (*pattern != '\0') {
255 switch (*pattern) {
256 case '?':
257 pattern++;
258 string++;
259 if (IsInsideGlyph(string[0]))
260 string = MoveToEndOfGlyph(string);
261 break;
263 case '*':
265 // Collapse any ** and *? constructions:
266 while (*pattern == '*' || *pattern == '?') {
267 pattern++;
268 if (*pattern == '?' && string != '\0') {
269 string++;
270 if (IsInsideGlyph(string[0]))
271 string = MoveToEndOfGlyph(string);
275 if (*pattern == '\0') {
276 // An ending * matches all strings.
277 return true;
280 bool match = false;
281 const char* pBefore = pattern - 1;
283 if (*pattern == '[') {
284 pattern++;
286 while (!match && *string != '\0') {
287 match = MatchesBracketExpression(string++, pattern,
288 caseSensitivity);
291 while (*pattern != ']' && *pattern != '\0') {
292 // Skip the rest of the bracket:
293 pattern++;
296 if (*pattern == '\0') {
297 // Failure if no closing bracket;
298 return false;
300 } else {
301 // No bracket, just one character:
302 while (!match && *string != '\0') {
303 if (IsGlyph(string[0]))
304 match = UTF8CharsAreEqual(string++, pattern);
305 else {
306 match = CharsAreEqual(*string++, *pattern,
307 caseSensitivity);
312 if (!match)
313 return false;
314 else {
315 pStorage[patternLevel] = pBefore;
316 if (IsInsideGlyph(string[0]))
317 string = MoveToEndOfGlyph(string);
319 sStorage[patternLevel++] = string;
320 if (patternLevel > kWildCardMaximum)
321 return false;
323 pattern++;
324 if (IsInsideGlyph(pattern[0]))
325 pattern = MoveToEndOfGlyph(pattern);
327 break;
330 case '[':
331 pattern++;
333 if (!MatchesBracketExpression(string, pattern,
334 caseSensitivity)) {
335 if (patternLevel > 0) {
336 pattern = pStorage[--patternLevel];
337 string = sStorage[patternLevel];
338 } else
339 return false;
340 } else {
341 // Skip the rest of the bracket:
342 while (*pattern != ']' && *pattern != '\0')
343 pattern++;
345 // Failure if no closing bracket;
346 if (*pattern == '\0')
347 return false;
349 string++;
350 if (IsInsideGlyph(string[0]))
351 string = MoveToEndOfGlyph(string);
352 pattern++;
354 break;
356 default:
358 bool equal = false;
359 if (IsGlyph(string[0]))
360 equal = UTF8CharsAreEqual(string, pattern);
361 else
362 equal = CharsAreEqual(*string, *pattern, caseSensitivity);
364 if (equal) {
365 pattern++;
366 if (IsInsideGlyph(pattern[0]))
367 pattern = MoveToEndOfGlyph(pattern);
368 string++;
369 if (IsInsideGlyph(string[0]))
370 string = MoveToEndOfGlyph(string);
371 } else if (patternLevel > 0) {
372 pattern = pStorage[--patternLevel];
373 string = sStorage[patternLevel];
374 } else
375 return false;
376 break;
380 if (*pattern == '\0' && *string != '\0' && patternLevel > 0) {
381 pattern = pStorage[--patternLevel];
382 string = sStorage[patternLevel];
386 return *string == '\0' && *pattern == '\0';
390 bool
391 TrackerString::UTF8CharsAreEqual(const char* string1,
392 const char* string2) const
394 const char* s1 = string1;
395 const char* s2 = string2;
397 if (IsStartOfGlyph(*s1) && *s1 == *s2) {
398 s1++;
399 s2++;
401 while (IsInsideGlyph(*s1) && *s1 == *s2) {
402 s1++;
403 s2++;
406 return !IsInsideGlyph(*s1)
407 && !IsInsideGlyph(*s2) && *(s1 - 1) == *(s2 - 1);
408 } else
409 return false;
413 const char*
414 TrackerString::MoveToEndOfGlyph(const char* string) const
416 const char* ptr = string;
418 while (IsInsideGlyph(*ptr))
419 ptr++;
421 return ptr;
425 bool
426 TrackerString::IsGlyph(char ch) const
428 return (ch & 0x80) == 0x80;
432 bool
433 TrackerString::IsInsideGlyph(char ch) const
435 return (ch & 0xC0) == 0x80;
439 bool
440 TrackerString::IsStartOfGlyph(char ch) const
442 return (ch & 0xC0) == 0xC0;