]>
Commit | Line | Data |
---|---|---|
1 | /////////////////////////////////////////////////////////////////////////////// | |
2 | // Name: src/common/regex.cpp | |
3 | // Purpose: regular expression matching | |
4 | // Author: Karsten Ballueder and Vadim Zeitlin | |
5 | // Modified by: | |
6 | // Created: 13.07.01 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) 2000 Karsten Ballueder <ballueder@gmx.net> | |
9 | // 2001 Vadim Zeitlin <vadim@wxwindows.org> | |
10 | // Licence: wxWindows licence | |
11 | /////////////////////////////////////////////////////////////////////////////// | |
12 | ||
13 | // ============================================================================ | |
14 | // declarations | |
15 | // ============================================================================ | |
16 | ||
17 | // ---------------------------------------------------------------------------- | |
18 | // headers | |
19 | // ---------------------------------------------------------------------------- | |
20 | ||
21 | // For compilers that support precompilation, includes "wx.h". | |
22 | #include "wx/wxprec.h" | |
23 | ||
24 | #ifdef __BORLANDC__ | |
25 | #pragma hdrstop | |
26 | #endif | |
27 | ||
28 | #if wxUSE_REGEX | |
29 | ||
30 | #ifndef WX_PRECOMP | |
31 | #include "wx/object.h" | |
32 | #include "wx/string.h" | |
33 | #include "wx/log.h" | |
34 | #include "wx/intl.h" | |
35 | #include "wx/crt.h" | |
36 | #endif //WX_PRECOMP | |
37 | ||
38 | // FreeBSD, Watcom and DMars require this, CW doesn't have nor need it. | |
39 | // Others also don't seem to need it. If you have an error related to | |
40 | // (not) including <sys/types.h> please report details to | |
41 | // wx-dev@lists.wxwindows.org | |
42 | #if defined(__UNIX__) || defined(__WATCOMC__) || defined(__DIGITALMARS__) | |
43 | # include <sys/types.h> | |
44 | #endif | |
45 | ||
46 | #include <regex.h> | |
47 | #include "wx/regex.h" | |
48 | ||
49 | // WXREGEX_USING_BUILTIN defined when using the built-in regex lib | |
50 | // WXREGEX_USING_RE_SEARCH defined when using re_search in the GNU regex lib | |
51 | // WXREGEX_IF_NEED_LEN() wrap the len parameter only used with the built-in | |
52 | // or GNU regex | |
53 | // WXREGEX_CONVERT_TO_MB defined when the regex lib is using chars and | |
54 | // wxChar is wide, so conversion must be done | |
55 | // WXREGEX_CHAR(x) Convert wxChar to wxRegChar | |
56 | // | |
57 | #ifdef __REG_NOFRONT | |
58 | # define WXREGEX_USING_BUILTIN | |
59 | # define WXREGEX_IF_NEED_LEN(x) ,x | |
60 | # if wxUSE_UNICODE | |
61 | # define WXREGEX_CHAR(x) (x).wc_str() | |
62 | # else | |
63 | # define WXREGEX_CHAR(x) (x).mb_str() | |
64 | # endif | |
65 | #else | |
66 | # ifdef HAVE_RE_SEARCH | |
67 | # define WXREGEX_IF_NEED_LEN(x) ,x | |
68 | # define WXREGEX_USING_RE_SEARCH | |
69 | # else | |
70 | # define WXREGEX_IF_NEED_LEN(x) | |
71 | # endif | |
72 | # if wxUSE_UNICODE | |
73 | # define WXREGEX_CONVERT_TO_MB | |
74 | # endif | |
75 | # define WXREGEX_CHAR(x) (x).mb_str() | |
76 | # define wx_regfree regfree | |
77 | # define wx_regerror regerror | |
78 | #endif | |
79 | ||
80 | // ---------------------------------------------------------------------------- | |
81 | // private classes | |
82 | // ---------------------------------------------------------------------------- | |
83 | ||
84 | #ifndef WXREGEX_USING_RE_SEARCH | |
85 | ||
86 | // the array of offsets for the matches, the usual POSIX regmatch_t array. | |
87 | class wxRegExMatches | |
88 | { | |
89 | public: | |
90 | typedef regmatch_t *match_type; | |
91 | ||
92 | wxRegExMatches(size_t n) { m_matches = new regmatch_t[n]; } | |
93 | ~wxRegExMatches() { delete [] m_matches; } | |
94 | ||
95 | // we just use casts here because the fields of regmatch_t struct may be 64 | |
96 | // bit but we're limited to size_t in our public API and are not going to | |
97 | // change it because operating on strings longer than 4GB using it is | |
98 | // absolutely impractical anyhow | |
99 | size_t Start(size_t n) const | |
100 | { | |
101 | return wx_truncate_cast(size_t, m_matches[n].rm_so); | |
102 | } | |
103 | ||
104 | size_t End(size_t n) const | |
105 | { | |
106 | return wx_truncate_cast(size_t, m_matches[n].rm_eo); | |
107 | } | |
108 | ||
109 | regmatch_t *get() const { return m_matches; } | |
110 | ||
111 | private: | |
112 | regmatch_t *m_matches; | |
113 | }; | |
114 | ||
115 | #else // WXREGEX_USING_RE_SEARCH | |
116 | ||
117 | // the array of offsets for the matches, the struct used by the GNU lib | |
118 | class wxRegExMatches | |
119 | { | |
120 | public: | |
121 | typedef re_registers *match_type; | |
122 | ||
123 | wxRegExMatches(size_t n) | |
124 | { | |
125 | m_matches.num_regs = n; | |
126 | m_matches.start = new regoff_t[n]; | |
127 | m_matches.end = new regoff_t[n]; | |
128 | } | |
129 | ||
130 | ~wxRegExMatches() | |
131 | { | |
132 | delete [] m_matches.start; | |
133 | delete [] m_matches.end; | |
134 | } | |
135 | ||
136 | size_t Start(size_t n) const { return m_matches.start[n]; } | |
137 | size_t End(size_t n) const { return m_matches.end[n]; } | |
138 | ||
139 | re_registers *get() { return &m_matches; } | |
140 | ||
141 | private: | |
142 | re_registers m_matches; | |
143 | }; | |
144 | ||
145 | #endif // WXREGEX_USING_RE_SEARCH | |
146 | ||
147 | // the character type used by the regular expression engine | |
148 | #ifndef WXREGEX_CONVERT_TO_MB | |
149 | typedef wxChar wxRegChar; | |
150 | #else | |
151 | typedef char wxRegChar; | |
152 | #endif | |
153 | ||
154 | // the real implementation of wxRegEx | |
155 | class wxRegExImpl | |
156 | { | |
157 | public: | |
158 | // ctor and dtor | |
159 | wxRegExImpl(); | |
160 | ~wxRegExImpl(); | |
161 | ||
162 | // return true if Compile() had been called successfully | |
163 | bool IsValid() const { return m_isCompiled; } | |
164 | ||
165 | // RE operations | |
166 | bool Compile(const wxString& expr, int flags = 0); | |
167 | bool Matches(const wxRegChar *str, int flags | |
168 | WXREGEX_IF_NEED_LEN(size_t len)) const; | |
169 | bool GetMatch(size_t *start, size_t *len, size_t index = 0) const; | |
170 | size_t GetMatchCount() const; | |
171 | int Replace(wxString *pattern, const wxString& replacement, | |
172 | size_t maxMatches = 0) const; | |
173 | ||
174 | private: | |
175 | // return the string containing the error message for the given err code | |
176 | wxString GetErrorMsg(int errorcode, bool badconv) const; | |
177 | ||
178 | // init the members | |
179 | void Init() | |
180 | { | |
181 | m_isCompiled = false; | |
182 | m_Matches = NULL; | |
183 | m_nMatches = 0; | |
184 | } | |
185 | ||
186 | // free the RE if compiled | |
187 | void Free() | |
188 | { | |
189 | if ( IsValid() ) | |
190 | { | |
191 | wx_regfree(&m_RegEx); | |
192 | } | |
193 | ||
194 | delete m_Matches; | |
195 | } | |
196 | ||
197 | // free the RE if any and reinit the members | |
198 | void Reinit() | |
199 | { | |
200 | Free(); | |
201 | Init(); | |
202 | } | |
203 | ||
204 | // compiled RE | |
205 | regex_t m_RegEx; | |
206 | ||
207 | // the subexpressions data | |
208 | wxRegExMatches *m_Matches; | |
209 | size_t m_nMatches; | |
210 | ||
211 | // true if m_RegEx is valid | |
212 | bool m_isCompiled; | |
213 | }; | |
214 | ||
215 | ||
216 | // ============================================================================ | |
217 | // implementation | |
218 | // ============================================================================ | |
219 | ||
220 | // ---------------------------------------------------------------------------- | |
221 | // wxRegExImpl | |
222 | // ---------------------------------------------------------------------------- | |
223 | ||
224 | wxRegExImpl::wxRegExImpl() | |
225 | { | |
226 | Init(); | |
227 | } | |
228 | ||
229 | wxRegExImpl::~wxRegExImpl() | |
230 | { | |
231 | Free(); | |
232 | } | |
233 | ||
234 | wxString wxRegExImpl::GetErrorMsg(int errorcode, bool badconv) const | |
235 | { | |
236 | #ifdef WXREGEX_CONVERT_TO_MB | |
237 | // currently only needed when using system library in Unicode mode | |
238 | if ( badconv ) | |
239 | { | |
240 | return _("conversion to 8-bit encoding failed"); | |
241 | } | |
242 | #else | |
243 | // 'use' badconv to avoid a compiler warning | |
244 | (void)badconv; | |
245 | #endif | |
246 | ||
247 | wxString szError; | |
248 | ||
249 | // first get the string length needed | |
250 | int len = wx_regerror(errorcode, &m_RegEx, NULL, 0); | |
251 | if ( len > 0 ) | |
252 | { | |
253 | char* szcmbError = new char[++len]; | |
254 | ||
255 | (void)wx_regerror(errorcode, &m_RegEx, szcmbError, len); | |
256 | ||
257 | szError = wxConvLibc.cMB2WX(szcmbError); | |
258 | delete [] szcmbError; | |
259 | } | |
260 | else // regerror() returned 0 | |
261 | { | |
262 | szError = _("unknown error"); | |
263 | } | |
264 | ||
265 | return szError; | |
266 | } | |
267 | ||
268 | bool wxRegExImpl::Compile(const wxString& expr, int flags) | |
269 | { | |
270 | Reinit(); | |
271 | ||
272 | #ifdef WX_NO_REGEX_ADVANCED | |
273 | # define FLAVORS wxRE_BASIC | |
274 | #else | |
275 | # define FLAVORS (wxRE_ADVANCED | wxRE_BASIC) | |
276 | wxASSERT_MSG( (flags & FLAVORS) != FLAVORS, | |
277 | _T("incompatible flags in wxRegEx::Compile") ); | |
278 | #endif | |
279 | wxASSERT_MSG( !(flags & ~(FLAVORS | wxRE_ICASE | wxRE_NOSUB | wxRE_NEWLINE)), | |
280 | _T("unrecognized flags in wxRegEx::Compile") ); | |
281 | ||
282 | // translate our flags to regcomp() ones | |
283 | int flagsRE = 0; | |
284 | if ( !(flags & wxRE_BASIC) ) | |
285 | #ifndef WX_NO_REGEX_ADVANCED | |
286 | if (flags & wxRE_ADVANCED) | |
287 | flagsRE |= REG_ADVANCED; | |
288 | else | |
289 | #endif | |
290 | flagsRE |= REG_EXTENDED; | |
291 | if ( flags & wxRE_ICASE ) | |
292 | flagsRE |= REG_ICASE; | |
293 | if ( flags & wxRE_NOSUB ) | |
294 | flagsRE |= REG_NOSUB; | |
295 | if ( flags & wxRE_NEWLINE ) | |
296 | flagsRE |= REG_NEWLINE; | |
297 | ||
298 | // compile it | |
299 | #ifdef WXREGEX_USING_BUILTIN | |
300 | bool conv = true; | |
301 | // FIXME-UTF8: use wc_str() after removing ANSI build | |
302 | int errorcode = wx_re_comp(&m_RegEx, expr.c_str(), expr.length(), flagsRE); | |
303 | #else | |
304 | // FIXME-UTF8: this is potentially broken, we shouldn't even try it | |
305 | // and should always use builtin regex library (or PCRE?) | |
306 | const wxWX2MBbuf conv = expr.mbc_str(); | |
307 | int errorcode = conv ? regcomp(&m_RegEx, conv, flagsRE) : REG_BADPAT; | |
308 | #endif | |
309 | ||
310 | if ( errorcode ) | |
311 | { | |
312 | wxLogError(_("Invalid regular expression '%s': %s"), | |
313 | expr.c_str(), GetErrorMsg(errorcode, !conv).c_str()); | |
314 | ||
315 | m_isCompiled = false; | |
316 | } | |
317 | else // ok | |
318 | { | |
319 | // don't allocate the matches array now, but do it later if necessary | |
320 | if ( flags & wxRE_NOSUB ) | |
321 | { | |
322 | // we don't need it at all | |
323 | m_nMatches = 0; | |
324 | } | |
325 | else | |
326 | { | |
327 | // we will alloc the array later (only if really needed) but count | |
328 | // the number of sub-expressions in the regex right now | |
329 | ||
330 | // there is always one for the whole expression | |
331 | m_nMatches = 1; | |
332 | ||
333 | // and some more for bracketed subexperessions | |
334 | for ( const wxChar *cptr = expr.c_str(); *cptr; cptr++ ) | |
335 | { | |
336 | if ( *cptr == _T('\\') ) | |
337 | { | |
338 | // in basic RE syntax groups are inside \(...\) | |
339 | if ( *++cptr == _T('(') && (flags & wxRE_BASIC) ) | |
340 | { | |
341 | m_nMatches++; | |
342 | } | |
343 | } | |
344 | else if ( *cptr == _T('(') && !(flags & wxRE_BASIC) ) | |
345 | { | |
346 | // we know that the previous character is not an unquoted | |
347 | // backslash because it would have been eaten above, so we | |
348 | // have a bare '(' and this indicates a group start for the | |
349 | // extended syntax. '(?' is used for extensions by perl- | |
350 | // like REs (e.g. advanced), and is not valid for POSIX | |
351 | // extended, so ignore them always. | |
352 | if ( cptr[1] != _T('?') ) | |
353 | m_nMatches++; | |
354 | } | |
355 | } | |
356 | } | |
357 | ||
358 | m_isCompiled = true; | |
359 | } | |
360 | ||
361 | return IsValid(); | |
362 | } | |
363 | ||
364 | #ifdef WXREGEX_USING_RE_SEARCH | |
365 | ||
366 | // On GNU, regexec is implemented as a wrapper around re_search. re_search | |
367 | // requires a length parameter which the POSIX regexec does not have, | |
368 | // therefore regexec must do a strlen on the search text each time it is | |
369 | // called. This can drastically affect performance when matching is done in | |
370 | // a loop along a string, such as during a search and replace. Therefore if | |
371 | // re_search is detected by configure, it is used directly. | |
372 | // | |
373 | static int ReSearch(const regex_t *preg, | |
374 | const char *text, | |
375 | size_t len, | |
376 | re_registers *matches, | |
377 | int eflags) | |
378 | { | |
379 | regex_t *pattern = wx_const_cast(regex_t*, preg); | |
380 | ||
381 | pattern->not_bol = (eflags & REG_NOTBOL) != 0; | |
382 | pattern->not_eol = (eflags & REG_NOTEOL) != 0; | |
383 | pattern->regs_allocated = REGS_FIXED; | |
384 | ||
385 | int ret = re_search(pattern, text, len, 0, len, matches); | |
386 | return ret >= 0 ? 0 : REG_NOMATCH; | |
387 | } | |
388 | ||
389 | #endif // WXREGEX_USING_RE_SEARCH | |
390 | ||
391 | bool wxRegExImpl::Matches(const wxRegChar *str, | |
392 | int flags | |
393 | WXREGEX_IF_NEED_LEN(size_t len)) const | |
394 | { | |
395 | wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") ); | |
396 | ||
397 | // translate our flags to regexec() ones | |
398 | wxASSERT_MSG( !(flags & ~(wxRE_NOTBOL | wxRE_NOTEOL)), | |
399 | _T("unrecognized flags in wxRegEx::Matches") ); | |
400 | ||
401 | int flagsRE = 0; | |
402 | if ( flags & wxRE_NOTBOL ) | |
403 | flagsRE |= REG_NOTBOL; | |
404 | if ( flags & wxRE_NOTEOL ) | |
405 | flagsRE |= REG_NOTEOL; | |
406 | ||
407 | // allocate matches array if needed | |
408 | wxRegExImpl *self = wxConstCast(this, wxRegExImpl); | |
409 | if ( !m_Matches && m_nMatches ) | |
410 | { | |
411 | self->m_Matches = new wxRegExMatches(m_nMatches); | |
412 | } | |
413 | ||
414 | wxRegExMatches::match_type matches = m_Matches ? m_Matches->get() : NULL; | |
415 | ||
416 | // do match it | |
417 | #if defined WXREGEX_USING_BUILTIN | |
418 | int rc = wx_re_exec(&self->m_RegEx, str, len, NULL, m_nMatches, matches, flagsRE); | |
419 | #elif defined WXREGEX_USING_RE_SEARCH | |
420 | int rc = str ? ReSearch(&self->m_RegEx, str, len, matches, flagsRE) : REG_BADPAT; | |
421 | #else | |
422 | int rc = str ? regexec(&self->m_RegEx, str, m_nMatches, matches, flagsRE) : REG_BADPAT; | |
423 | #endif | |
424 | ||
425 | switch ( rc ) | |
426 | { | |
427 | case 0: | |
428 | // matched successfully | |
429 | return true; | |
430 | ||
431 | default: | |
432 | // an error occurred | |
433 | wxLogError(_("Failed to find match for regular expression: %s"), | |
434 | GetErrorMsg(rc, !str).c_str()); | |
435 | // fall through | |
436 | ||
437 | case REG_NOMATCH: | |
438 | // no match | |
439 | return false; | |
440 | } | |
441 | } | |
442 | ||
443 | bool wxRegExImpl::GetMatch(size_t *start, size_t *len, size_t index) const | |
444 | { | |
445 | wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") ); | |
446 | wxCHECK_MSG( m_nMatches, false, _T("can't use with wxRE_NOSUB") ); | |
447 | wxCHECK_MSG( m_Matches, false, _T("must call Matches() first") ); | |
448 | wxCHECK_MSG( index < m_nMatches, false, _T("invalid match index") ); | |
449 | ||
450 | if ( start ) | |
451 | *start = m_Matches->Start(index); | |
452 | if ( len ) | |
453 | *len = m_Matches->End(index) - m_Matches->Start(index); | |
454 | ||
455 | return true; | |
456 | } | |
457 | ||
458 | size_t wxRegExImpl::GetMatchCount() const | |
459 | { | |
460 | wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") ); | |
461 | wxCHECK_MSG( m_nMatches, 0, _T("can't use with wxRE_NOSUB") ); | |
462 | ||
463 | return m_nMatches; | |
464 | } | |
465 | ||
466 | int wxRegExImpl::Replace(wxString *text, | |
467 | const wxString& replacement, | |
468 | size_t maxMatches) const | |
469 | { | |
470 | wxCHECK_MSG( text, wxNOT_FOUND, _T("NULL text in wxRegEx::Replace") ); | |
471 | wxCHECK_MSG( IsValid(), wxNOT_FOUND, _T("must successfully Compile() first") ); | |
472 | ||
473 | // the input string | |
474 | #ifndef WXREGEX_CONVERT_TO_MB | |
475 | const wxChar *textstr = text->c_str(); | |
476 | size_t textlen = text->length(); | |
477 | #else | |
478 | const wxWX2MBbuf textstr = WXREGEX_CHAR(*text); | |
479 | if (!textstr) | |
480 | { | |
481 | wxLogError(_("Failed to find match for regular expression: %s"), | |
482 | GetErrorMsg(0, true).c_str()); | |
483 | return 0; | |
484 | } | |
485 | size_t textlen = strlen(textstr); | |
486 | text->clear(); | |
487 | #endif | |
488 | ||
489 | // the replacement text | |
490 | wxString textNew; | |
491 | ||
492 | // the result, allow 25% extra | |
493 | wxString result; | |
494 | result.reserve(5 * textlen / 4); | |
495 | ||
496 | // attempt at optimization: don't iterate over the string if it doesn't | |
497 | // contain back references at all | |
498 | bool mayHaveBackrefs = | |
499 | replacement.find_first_of(_T("\\&")) != wxString::npos; | |
500 | ||
501 | if ( !mayHaveBackrefs ) | |
502 | { | |
503 | textNew = replacement; | |
504 | } | |
505 | ||
506 | // the position where we start looking for the match | |
507 | size_t matchStart = 0; | |
508 | ||
509 | // number of replacement made: we won't make more than maxMatches of them | |
510 | // (unless maxMatches is 0 which doesn't limit the number of replacements) | |
511 | size_t countRepl = 0; | |
512 | ||
513 | // note that "^" shouldn't match after the first call to Matches() so we | |
514 | // use wxRE_NOTBOL to prevent it from happening | |
515 | while ( (!maxMatches || countRepl < maxMatches) && | |
516 | #ifndef WXREGEX_CONVERT_TO_MB | |
517 | Matches(textstr + matchStart, | |
518 | #else | |
519 | Matches(textstr.data() + matchStart, | |
520 | #endif | |
521 | countRepl ? wxRE_NOTBOL : 0 | |
522 | WXREGEX_IF_NEED_LEN(textlen - matchStart)) ) | |
523 | { | |
524 | // the string possibly contains back references: we need to calculate | |
525 | // the replacement text anew after each match | |
526 | if ( mayHaveBackrefs ) | |
527 | { | |
528 | mayHaveBackrefs = false; | |
529 | textNew.clear(); | |
530 | textNew.reserve(replacement.length()); | |
531 | ||
532 | for ( const wxChar *p = replacement.c_str(); *p; p++ ) | |
533 | { | |
534 | size_t index = (size_t)-1; | |
535 | ||
536 | if ( *p == _T('\\') ) | |
537 | { | |
538 | if ( wxIsdigit(*++p) ) | |
539 | { | |
540 | // back reference | |
541 | wxChar *end; | |
542 | index = (size_t)wxStrtoul(p, &end, 10); | |
543 | p = end - 1; // -1 to compensate for p++ in the loop | |
544 | } | |
545 | //else: backslash used as escape character | |
546 | } | |
547 | else if ( *p == _T('&') ) | |
548 | { | |
549 | // treat this as "\0" for compatbility with ed and such | |
550 | index = 0; | |
551 | } | |
552 | ||
553 | // do we have a back reference? | |
554 | if ( index != (size_t)-1 ) | |
555 | { | |
556 | // yes, get its text | |
557 | size_t start, len; | |
558 | if ( !GetMatch(&start, &len, index) ) | |
559 | { | |
560 | wxFAIL_MSG( _T("invalid back reference") ); | |
561 | ||
562 | // just eat it... | |
563 | } | |
564 | else | |
565 | { | |
566 | #ifndef WXREGEX_CONVERT_TO_MB | |
567 | textNew += wxString(textstr + matchStart + start, | |
568 | #else | |
569 | textNew += wxString(textstr.data() + matchStart + | |
570 | start, | |
571 | #endif | |
572 | *wxConvCurrent, len); | |
573 | ||
574 | mayHaveBackrefs = true; | |
575 | } | |
576 | } | |
577 | else // ordinary character | |
578 | { | |
579 | textNew += *p; | |
580 | } | |
581 | } | |
582 | } | |
583 | ||
584 | size_t start, len; | |
585 | if ( !GetMatch(&start, &len) ) | |
586 | { | |
587 | // we did have match as Matches() returned true above! | |
588 | wxFAIL_MSG( _T("internal logic error in wxRegEx::Replace") ); | |
589 | ||
590 | return wxNOT_FOUND; | |
591 | } | |
592 | ||
593 | // an insurance against implementations that don't grow exponentially | |
594 | // to ensure building the result takes linear time | |
595 | if (result.capacity() < result.length() + start + textNew.length()) | |
596 | result.reserve(2 * result.length()); | |
597 | ||
598 | #ifndef WXREGEX_CONVERT_TO_MB | |
599 | result.append(*text, matchStart, start); | |
600 | #else | |
601 | result.append(wxString(textstr.data() + matchStart, *wxConvCurrent, | |
602 | start)); | |
603 | #endif | |
604 | matchStart += start; | |
605 | result.append(textNew); | |
606 | ||
607 | countRepl++; | |
608 | ||
609 | matchStart += len; | |
610 | } | |
611 | ||
612 | #ifndef WXREGEX_CONVERT_TO_MB | |
613 | result.append(*text, matchStart, wxString::npos); | |
614 | #else | |
615 | result.append(wxString(textstr.data() + matchStart, *wxConvCurrent)); | |
616 | #endif | |
617 | *text = result; | |
618 | ||
619 | return countRepl; | |
620 | } | |
621 | ||
622 | // ---------------------------------------------------------------------------- | |
623 | // wxRegEx: all methods are mostly forwarded to wxRegExImpl | |
624 | // ---------------------------------------------------------------------------- | |
625 | ||
626 | void wxRegEx::Init() | |
627 | { | |
628 | m_impl = NULL; | |
629 | } | |
630 | ||
631 | wxRegEx::~wxRegEx() | |
632 | { | |
633 | delete m_impl; | |
634 | } | |
635 | ||
636 | bool wxRegEx::Compile(const wxString& expr, int flags) | |
637 | { | |
638 | if ( !m_impl ) | |
639 | { | |
640 | m_impl = new wxRegExImpl; | |
641 | } | |
642 | ||
643 | if ( !m_impl->Compile(expr, flags) ) | |
644 | { | |
645 | // error message already given in wxRegExImpl::Compile | |
646 | delete m_impl; | |
647 | m_impl = NULL; | |
648 | ||
649 | return false; | |
650 | } | |
651 | ||
652 | return true; | |
653 | } | |
654 | ||
655 | bool wxRegEx::Matches(const wxString& str, int flags) const | |
656 | { | |
657 | wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") ); | |
658 | ||
659 | return m_impl->Matches(WXREGEX_CHAR(str), flags | |
660 | WXREGEX_IF_NEED_LEN(str.length())); | |
661 | } | |
662 | ||
663 | bool wxRegEx::GetMatch(size_t *start, size_t *len, size_t index) const | |
664 | { | |
665 | wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") ); | |
666 | ||
667 | return m_impl->GetMatch(start, len, index); | |
668 | } | |
669 | ||
670 | wxString wxRegEx::GetMatch(const wxString& text, size_t index) const | |
671 | { | |
672 | size_t start, len; | |
673 | if ( !GetMatch(&start, &len, index) ) | |
674 | return wxEmptyString; | |
675 | ||
676 | return text.Mid(start, len); | |
677 | } | |
678 | ||
679 | size_t wxRegEx::GetMatchCount() const | |
680 | { | |
681 | wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") ); | |
682 | ||
683 | return m_impl->GetMatchCount(); | |
684 | } | |
685 | ||
686 | int wxRegEx::Replace(wxString *pattern, | |
687 | const wxString& replacement, | |
688 | size_t maxMatches) const | |
689 | { | |
690 | wxCHECK_MSG( IsValid(), wxNOT_FOUND, _T("must successfully Compile() first") ); | |
691 | ||
692 | return m_impl->Replace(pattern, replacement, maxMatches); | |
693 | } | |
694 | ||
695 | #endif // wxUSE_REGEX |