#include "TrackerString.h"
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
TrackerString::TrackerString()
{
}
TrackerString::TrackerString(const char* string)
:
BString(string)
{
}
TrackerString::TrackerString(const TrackerString &string)
:
BString(string)
{
}
TrackerString::TrackerString(const char* string, int32 maxLength)
:
BString(string, maxLength)
{
}
TrackerString::~TrackerString()
{
}
bool
TrackerString::Matches(const char* string, bool caseSensitivity,
TrackerStringExpressionType expressionType) const
{
switch (expressionType) {
default:
case kNone:
return false;
case kStartsWith:
return StartsWith(string, caseSensitivity);
case kEndsWith:
return EndsWith(string, caseSensitivity);
case kContains:
return Contains(string, caseSensitivity);
case kGlobMatch:
return MatchesGlob(string, caseSensitivity);
case kRegexpMatch:
return MatchesRegExp(string, caseSensitivity);
}
}
bool
TrackerString::MatchesRegExp(const char* pattern, bool caseSensitivity) const
{
BString patternString(pattern);
BString textString(String());
if (caseSensitivity == false) {
patternString.ToLower();
textString.ToLower();
}
RegExp expression(patternString);
if (expression.InitCheck() != B_OK)
return false;
return expression.Matches(textString);
}
bool
TrackerString::MatchesGlob(const char* string, bool caseSensitivity) const
{
return StringMatchesPattern(String(), string, caseSensitivity);
}
bool
TrackerString::EndsWith(const char* string, bool caseSensitivity) const
{
int32 position = Length() - (int32)strlen(string);
if (position < 0)
return false;
if (caseSensitivity)
return FindLast(string) == position;
else
return IFindLast(string) == position;
}
bool
TrackerString::StartsWith(const char* string, bool caseSensitivity) const
{
if (caseSensitivity)
return FindFirst(string) == 0;
else
return IFindFirst(string) == 0;
}
bool
TrackerString::Contains(const char* string, bool caseSensitivity) const
{
if (caseSensitivity)
return FindFirst(string) > -1;
else
return IFindFirst(string) > -1;
}
bool
TrackerString::MatchesBracketExpression(const char* string,
const char* pattern, bool caseSensitivity) const
{
bool GlyphMatch = IsStartOfGlyph(string[0]);
if (IsInsideGlyph(string[0]))
return false;
char testChar = ConditionalToLower(string[0], caseSensitivity);
bool match = false;
bool inverse = *pattern == '^' || *pattern == '!';
if (inverse)
pattern++;
while (!match && *pattern != ']' && *pattern != '\0') {
switch (*pattern) {
case '-':
{
char start = ConditionalToLower(*(pattern - 1),
caseSensitivity);
char stop = ConditionalToLower(*(pattern + 1),
caseSensitivity);
if (IsGlyph(start) || IsGlyph(stop))
return false;
if ((islower(start) && islower(stop))
|| (isupper(start) && isupper(stop))
|| (isdigit(start) && isdigit(stop))) {
match = start <= testChar && testChar <= stop;
} else {
return false;
}
break;
}
default:
if (GlyphMatch)
match = UTF8CharsAreEqual(string, pattern);
else
match = CharsAreEqual(testChar, *pattern, caseSensitivity);
break;
}
if (!match) {
pattern++;
if (IsInsideGlyph(pattern[0]))
pattern = MoveToEndOfGlyph(pattern);
}
}
if (*pattern == '\0')
return false;
return (match ^ inverse) != 0;
}
bool
TrackerString::StringMatchesPattern(const char* string, const char* pattern,
bool caseSensitivity) const
{
const int32 kWildCardMaximum = 100;
const char* pStorage[kWildCardMaximum];
const char* sStorage[kWildCardMaximum];
int32 patternLevel = 0;
if (string == NULL || pattern == NULL)
return false;
while (*pattern != '\0') {
switch (*pattern) {
case '?':
pattern++;
string++;
if (IsInsideGlyph(string[0]))
string = MoveToEndOfGlyph(string);
break;
case '*':
{
while (*pattern == '*' || *pattern == '?') {
pattern++;
if (*pattern == '?' && *string != '\0') {
string++;
if (IsInsideGlyph(string[0]))
string = MoveToEndOfGlyph(string);
}
}
if (*pattern == '\0') {
return true;
}
bool match = false;
const char* pBefore = pattern - 1;
if (*pattern == '[') {
pattern++;
while (!match && *string != '\0') {
match = MatchesBracketExpression(string++, pattern,
caseSensitivity);
}
while (*pattern != ']' && *pattern != '\0') {
pattern++;
}
if (*pattern == '\0') {
return false;
}
} else {
while (!match && *string != '\0') {
if (IsGlyph(string[0]))
match = UTF8CharsAreEqual(string++, pattern);
else {
match = CharsAreEqual(*string++, *pattern,
caseSensitivity);
}
}
}
if (!match)
return false;
else {
pStorage[patternLevel] = pBefore;
if (IsInsideGlyph(string[0]))
string = MoveToEndOfGlyph(string);
sStorage[patternLevel++] = string;
if (patternLevel > kWildCardMaximum)
return false;
pattern++;
if (IsInsideGlyph(pattern[0]))
pattern = MoveToEndOfGlyph(pattern);
}
break;
}
case '[':
pattern++;
if (!MatchesBracketExpression(string, pattern,
caseSensitivity)) {
if (patternLevel > 0) {
pattern = pStorage[--patternLevel];
string = sStorage[patternLevel];
} else
return false;
} else {
while (*pattern != ']' && *pattern != '\0')
pattern++;
if (*pattern == '\0')
return false;
string++;
if (IsInsideGlyph(string[0]))
string = MoveToEndOfGlyph(string);
pattern++;
}
break;
default:
{
bool equal = false;
if (IsGlyph(string[0]))
equal = UTF8CharsAreEqual(string, pattern);
else
equal = CharsAreEqual(*string, *pattern, caseSensitivity);
if (equal) {
pattern++;
if (IsInsideGlyph(pattern[0]))
pattern = MoveToEndOfGlyph(pattern);
string++;
if (IsInsideGlyph(string[0]))
string = MoveToEndOfGlyph(string);
} else if (patternLevel > 0) {
pattern = pStorage[--patternLevel];
string = sStorage[patternLevel];
} else
return false;
break;
}
}
if (*pattern == '\0' && *string != '\0' && patternLevel > 0) {
pattern = pStorage[--patternLevel];
string = sStorage[patternLevel];
}
}
return *string == '\0' && *pattern == '\0';
}
bool
TrackerString::UTF8CharsAreEqual(const char* string1,
const char* string2) const
{
const char* s1 = string1;
const char* s2 = string2;
if (IsStartOfGlyph(*s1) && *s1 == *s2) {
s1++;
s2++;
while (IsInsideGlyph(*s1) && *s1 == *s2) {
s1++;
s2++;
}
return !IsInsideGlyph(*s1)
&& !IsInsideGlyph(*s2) && *(s1 - 1) == *(s2 - 1);
} else
return false;
}
const char*
TrackerString::MoveToEndOfGlyph(const char* string) const
{
const char* ptr = string;
while (IsInsideGlyph(*ptr))
ptr++;
return ptr;
}
bool
TrackerString::IsGlyph(char ch) const
{
return (ch & 0x80) == 0x80;
}
bool
TrackerString::IsInsideGlyph(char ch) const
{
return (ch & 0xC0) == 0x80;
}
bool
TrackerString::IsStartOfGlyph(char ch) const
{
return (ch & 0xC0) == 0xC0;
}