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