btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / shared / RegExp.cpp
blob3fa44da4479e4fa60e2c4cbe65cf7d23afc0107d
1 /*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2013, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
8 #include <RegExp.h>
10 #include <new>
12 #include <regex.h>
14 #include <String.h>
16 #include <Referenceable.h>
19 // #pragma mark - RegExp::Data
22 struct RegExp::Data : public BReferenceable {
23 Data(const char* pattern, PatternType patternType, bool caseSensitive)
25 BReferenceable()
27 // convert the shell pattern to a regular expression
28 BString patternString;
29 if (patternType == PATTERN_TYPE_WILDCARD) {
30 while (*pattern != '\0') {
31 char c = *pattern++;
32 switch (c) {
33 case '?':
34 patternString += '.';
35 continue;
36 case '*':
37 patternString += ".*";
38 continue;
39 case '[':
41 // find the matching ']' first
42 const char* end = pattern;
43 while (*end != ']') {
44 if (*end++ == '\0') {
45 fError = REG_EBRACK;
46 return;
50 if (pattern == end) {
51 // Empty bracket expression. It will never match
52 // anything. Strictly speaking this is not
53 // considered an error, but we handle it like one.
54 fError = REG_EBRACK;
55 return;
58 patternString += '[';
60 // We need to avoid "[." ... ".]", "[=" ... "=]", and
61 // "[:" ... ":]" sequences, since those have special
62 // meaning in regular expressions. If we encounter
63 // a '[' followed by either of '.', '=', or ':', we
64 // replace the '[' by "[.[.]".
65 while (pattern < end) {
66 c = *pattern++;
67 if (c == '[' && pattern < end) {
68 switch (*pattern) {
69 case '.':
70 case '=':
71 case ':':
72 patternString += "[.[.]";
73 continue;
76 patternString += c;
79 pattern++;
80 patternString += ']';
81 break;
84 case '\\':
86 // Quotes the next character. Works the same way for
87 // regular expressions.
88 if (*pattern == '\0') {
89 fError = REG_EESCAPE;
90 return;
93 patternString += '\\';
94 patternString += *pattern++;
95 break;
98 case '^':
99 case '.':
100 case '$':
101 case '(':
102 case ')':
103 case '|':
104 case '+':
105 case '{':
106 // need to be quoted
107 patternString += '\\';
108 // fall through
109 default:
110 patternString += c;
111 break;
115 pattern = patternString.String();
118 int flags = REG_EXTENDED;
119 if (!caseSensitive)
120 flags |= REG_ICASE;
122 fError = regcomp(&fCompiledExpression, pattern, flags);
125 ~Data()
127 if (fError == 0)
128 regfree(&fCompiledExpression);
131 bool IsValid() const
133 return fError == 0;
136 const regex_t* CompiledExpression() const
138 return &fCompiledExpression;
141 private:
142 int fError;
143 regex_t fCompiledExpression;
147 // #pragma mark - RegExp::MatchResultData
150 struct RegExp::MatchResultData : public BReferenceable {
151 MatchResultData(const regex_t* compiledExpression, const char* string)
153 BReferenceable(),
154 fMatchCount(0),
155 fMatches(NULL)
157 // fMatchCount is always set to the number of matching groups in the
158 // expression (or 0 if an error occured). Some of the "matches" in
159 // the array may still point to the (-1,-1) range if they don't
160 // actually match anything.
161 fMatchCount = compiledExpression->re_nsub + 1;
162 fMatches = new regmatch_t[fMatchCount];
163 if (regexec(compiledExpression, string, fMatchCount, fMatches, 0)
164 != 0) {
165 delete[] fMatches;
166 fMatches = NULL;
167 fMatchCount = 0;
171 ~MatchResultData()
173 delete[] fMatches;
176 size_t MatchCount() const
178 return fMatchCount;
181 const regmatch_t* Matches() const
183 return fMatches;
186 private:
187 size_t fMatchCount;
188 regmatch_t* fMatches;
192 // #pragma mark - RegExp
195 RegExp::RegExp()
197 fData(NULL)
202 RegExp::RegExp(const char* pattern, PatternType patternType,
203 bool caseSensitive)
205 fData(NULL)
207 SetPattern(pattern, patternType, caseSensitive);
211 RegExp::RegExp(const RegExp& other)
213 fData(other.fData)
215 if (fData != NULL)
216 fData->AcquireReference();
220 RegExp::~RegExp()
222 if (fData != NULL)
223 fData->ReleaseReference();
227 bool
228 RegExp::SetPattern(const char* pattern, PatternType patternType,
229 bool caseSensitive)
231 if (fData != NULL) {
232 fData->ReleaseReference();
233 fData = NULL;
236 Data* newData = new(std::nothrow) Data(pattern, patternType, caseSensitive);
237 if (newData == NULL)
238 return false;
240 BReference<Data> dataReference(newData, true);
241 if (!newData->IsValid())
242 return false;
244 fData = dataReference.Detach();
245 return true;
249 RegExp::MatchResult
250 RegExp::Match(const char* string) const
252 if (!IsValid())
253 return MatchResult();
255 return MatchResult(
256 new(std::nothrow) MatchResultData(fData->CompiledExpression(),
257 string));
261 RegExp&
262 RegExp::operator=(const RegExp& other)
264 if (fData != NULL)
265 fData->ReleaseReference();
267 fData = other.fData;
269 if (fData != NULL)
270 fData->AcquireReference();
272 return *this;
276 // #pragma mark - RegExp::MatchResult
279 RegExp::MatchResult::MatchResult()
281 fData(NULL)
286 RegExp::MatchResult::MatchResult(MatchResultData* data)
288 fData(data)
293 RegExp::MatchResult::MatchResult(const MatchResult& other)
295 fData(other.fData)
297 if (fData != NULL)
298 fData->AcquireReference();
302 RegExp::MatchResult::~MatchResult()
304 if (fData != NULL)
305 fData->ReleaseReference();
309 bool
310 RegExp::MatchResult::HasMatched() const
312 return fData != NULL && fData->MatchCount() > 0;
316 size_t
317 RegExp::MatchResult::StartOffset() const
319 return fData != NULL && fData->MatchCount() > 0
320 ? fData->Matches()[0].rm_so : 0;
324 size_t
325 RegExp::MatchResult::EndOffset() const
327 return fData != NULL && fData->MatchCount() > 0
328 ? fData->Matches()[0].rm_eo : 0;
332 size_t
333 RegExp::MatchResult::GroupCount() const
335 if (fData == NULL)
336 return 0;
338 size_t matchCount = fData->MatchCount();
339 return matchCount > 0 ? matchCount - 1 : 0;
343 size_t
344 RegExp::MatchResult::GroupStartOffsetAt(size_t index) const
346 return fData != NULL && fData->MatchCount() > index + 1
347 ? fData->Matches()[index + 1].rm_so : 0;
351 size_t
352 RegExp::MatchResult::GroupEndOffsetAt(size_t index) const
354 return fData != NULL && fData->MatchCount() > index + 1
355 ? fData->Matches()[index + 1].rm_eo : 0;
359 RegExp::MatchResult&
360 RegExp::MatchResult::operator=(const MatchResult& other)
362 if (fData != NULL)
363 fData->ReleaseReference();
365 fData = other.fData;
367 if (fData != NULL)
368 fData->AcquireReference();
370 return *this;