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.
35 #include "TrackerString.h"
42 // #pragma mark - TrackerString
45 TrackerString::TrackerString()
50 TrackerString::TrackerString(const char* string
)
57 TrackerString::TrackerString(const TrackerString
&string
)
64 TrackerString::TrackerString(const char* string
, int32 maxLength
)
66 BString(string
, maxLength
)
71 TrackerString::~TrackerString()
77 TrackerString::Matches(const char* string
, bool caseSensitivity
,
78 TrackerStringExpressionType expressionType
) const
80 switch (expressionType
) {
86 return StartsWith(string
, caseSensitivity
);
89 return EndsWith(string
, caseSensitivity
);
92 return Contains(string
, caseSensitivity
);
95 return MatchesGlob(string
, caseSensitivity
);
98 return MatchesRegExp(string
, caseSensitivity
);
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
)
119 return expression
.Matches(textString
);
124 TrackerString::MatchesGlob(const char* string
, bool caseSensitivity
) const
126 return StringMatchesPattern(String(), string
, caseSensitivity
);
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
);
140 return FindLast(string
) == position
;
142 return IFindLast(string
) == position
;
147 TrackerString::StartsWith(const char* string
, bool caseSensitivity
) const
150 return FindFirst(string
) == 0;
152 return IFindFirst(string
) == 0;
157 TrackerString::Contains(const char* string
, bool caseSensitivity
) const
160 return FindFirst(string
) > -1;
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 '[[]').
171 TrackerString::MatchesBracketExpression(const char* string
,
172 const char* pattern
, bool caseSensitivity
) const
174 bool GlyphMatch
= IsStartOfGlyph(string
[0]);
176 if (IsInsideGlyph(string
[0]))
179 char testChar
= ConditionalToLower(string
[0], caseSensitivity
);
182 bool inverse
= *pattern
== '^' || *pattern
== '!';
183 // We allow both ^ and ! as a initial inverting character.
188 while (!match
&& *pattern
!= ']' && *pattern
!= '\0') {
192 char start
= ConditionalToLower(*(pattern
- 1),
194 char stop
= ConditionalToLower(*(pattern
+ 1),
197 if (IsGlyph(start
) || IsGlyph(stop
))
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
;
207 // If no valid range, we've got a syntax error.
216 match
= UTF8CharsAreEqual(string
, pattern
);
218 match
= CharsAreEqual(testChar
, *pattern
, caseSensitivity
);
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')
234 return (match
^ inverse
) != 0;
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
)
254 while (*pattern
!= '\0') {
259 if (IsInsideGlyph(string
[0]))
260 string
= MoveToEndOfGlyph(string
);
265 // Collapse any ** and *? constructions:
266 while (*pattern
== '*' || *pattern
== '?') {
268 if (*pattern
== '?' && string
!= '\0') {
270 if (IsInsideGlyph(string
[0]))
271 string
= MoveToEndOfGlyph(string
);
275 if (*pattern
== '\0') {
276 // An ending * matches all strings.
281 const char* pBefore
= pattern
- 1;
283 if (*pattern
== '[') {
286 while (!match
&& *string
!= '\0') {
287 match
= MatchesBracketExpression(string
++, pattern
,
291 while (*pattern
!= ']' && *pattern
!= '\0') {
292 // Skip the rest of the bracket:
296 if (*pattern
== '\0') {
297 // Failure if no closing bracket;
301 // No bracket, just one character:
302 while (!match
&& *string
!= '\0') {
303 if (IsGlyph(string
[0]))
304 match
= UTF8CharsAreEqual(string
++, pattern
);
306 match
= CharsAreEqual(*string
++, *pattern
,
315 pStorage
[patternLevel
] = pBefore
;
316 if (IsInsideGlyph(string
[0]))
317 string
= MoveToEndOfGlyph(string
);
319 sStorage
[patternLevel
++] = string
;
320 if (patternLevel
> kWildCardMaximum
)
324 if (IsInsideGlyph(pattern
[0]))
325 pattern
= MoveToEndOfGlyph(pattern
);
333 if (!MatchesBracketExpression(string
, pattern
,
335 if (patternLevel
> 0) {
336 pattern
= pStorage
[--patternLevel
];
337 string
= sStorage
[patternLevel
];
341 // Skip the rest of the bracket:
342 while (*pattern
!= ']' && *pattern
!= '\0')
345 // Failure if no closing bracket;
346 if (*pattern
== '\0')
350 if (IsInsideGlyph(string
[0]))
351 string
= MoveToEndOfGlyph(string
);
359 if (IsGlyph(string
[0]))
360 equal
= UTF8CharsAreEqual(string
, pattern
);
362 equal
= CharsAreEqual(*string
, *pattern
, caseSensitivity
);
366 if (IsInsideGlyph(pattern
[0]))
367 pattern
= MoveToEndOfGlyph(pattern
);
369 if (IsInsideGlyph(string
[0]))
370 string
= MoveToEndOfGlyph(string
);
371 } else if (patternLevel
> 0) {
372 pattern
= pStorage
[--patternLevel
];
373 string
= sStorage
[patternLevel
];
380 if (*pattern
== '\0' && *string
!= '\0' && patternLevel
> 0) {
381 pattern
= pStorage
[--patternLevel
];
382 string
= sStorage
[patternLevel
];
386 return *string
== '\0' && *pattern
== '\0';
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
) {
401 while (IsInsideGlyph(*s1
) && *s1
== *s2
) {
406 return !IsInsideGlyph(*s1
)
407 && !IsInsideGlyph(*s2
) && *(s1
- 1) == *(s2
- 1);
414 TrackerString::MoveToEndOfGlyph(const char* string
) const
416 const char* ptr
= string
;
418 while (IsInsideGlyph(*ptr
))
426 TrackerString::IsGlyph(char ch
) const
428 return (ch
& 0x80) == 0x80;
433 TrackerString::IsInsideGlyph(char ch
) const
435 return (ch
& 0xC0) == 0x80;
440 TrackerString::IsStartOfGlyph(char ch
) const
442 return (ch
& 0xC0) == 0xC0;