]>
git.saurik.com Git - wxWidgets.git/blob - src/common/regex.cpp
c646c915bbb9984593b507c719da71a331a774bc
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/regex.cpp
3 // Purpose: regular expression matching
4 // Author: Karsten Ballüder and Vadim Zeitlin
8 // Copyright: (c) 2000 Karsten Ballüder <ballueder@gmx.net>
9 // 2001 Vadim Zeitlin <vadim@wxwindows.org>
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
13 // ============================================================================
15 // ============================================================================
17 // ----------------------------------------------------------------------------
19 // ----------------------------------------------------------------------------
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
31 #include "wx/object.h"
32 #include "wx/string.h"
37 // FreeBSD, Watcom and DMars require this, CW doesn't have nor need it.
38 // Others also don't seem to need it. If you have an error related to
39 // (not) including <sys/types.h> please report details to
40 // wx-dev@lists.wxwindows.org
41 #if defined(__UNIX__) || defined(__WATCOMC__) || defined(__DIGITALMARS__)
42 # include <sys/types.h>
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
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
57 # define WXREGEX_USING_BUILTIN
58 # define WXREGEX_IF_NEED_LEN(x) ,x
59 # define WXREGEX_CHAR(x) x
61 # ifdef HAVE_RE_SEARCH
62 # define WXREGEX_IF_NEED_LEN(x) ,x
63 # define WXREGEX_USING_RE_SEARCH
65 # define WXREGEX_IF_NEED_LEN(x)
68 # define WXREGEX_CONVERT_TO_MB
70 # define WXREGEX_CHAR(x) wxConvertWX2MB(x)
71 # define wx_regfree regfree
72 # define wx_regerror regerror
75 // ----------------------------------------------------------------------------
77 // ----------------------------------------------------------------------------
79 #ifndef WXREGEX_USING_RE_SEARCH
81 // the array of offsets for the matches, the usual POSIX regmatch_t array.
85 typedef regmatch_t
*match_type
;
87 wxRegExMatches(size_t n
) { m_matches
= new regmatch_t
[n
]; }
88 ~wxRegExMatches() { delete [] m_matches
; }
90 size_t Start(size_t n
) const { return m_matches
[n
].rm_so
; }
91 size_t End(size_t n
) const { return m_matches
[n
].rm_eo
; }
93 regmatch_t
*get() const { return m_matches
; }
96 regmatch_t
*m_matches
;
99 #else // WXREGEX_USING_RE_SEARCH
101 // the array of offsets for the matches, the struct used by the GNU lib
105 typedef re_registers
*match_type
;
107 wxRegExMatches(size_t n
)
109 m_matches
.num_regs
= n
;
110 m_matches
.start
= new regoff_t
[n
];
111 m_matches
.end
= new regoff_t
[n
];
116 delete [] m_matches
.start
;
117 delete [] m_matches
.end
;
120 size_t Start(size_t n
) const { return m_matches
.start
[n
]; }
121 size_t End(size_t n
) const { return m_matches
.end
[n
]; }
123 re_registers
*get() { return &m_matches
; }
126 re_registers m_matches
;
129 #endif // WXREGEX_USING_RE_SEARCH
131 // the character type used by the regular expression engine
132 #ifndef WXREGEX_CONVERT_TO_MB
133 typedef wxChar wxRegChar
;
135 typedef char wxRegChar
;
138 // the real implementation of wxRegEx
146 // return true if Compile() had been called successfully
147 bool IsValid() const { return m_isCompiled
; }
150 bool Compile(const wxString
& expr
, int flags
= 0);
151 bool Matches(const wxRegChar
*str
, int flags
152 WXREGEX_IF_NEED_LEN(size_t len
)) const;
153 bool GetMatch(size_t *start
, size_t *len
, size_t index
= 0) const;
154 size_t GetMatchCount() const;
155 int Replace(wxString
*pattern
, const wxString
& replacement
,
156 size_t maxMatches
= 0) const;
159 // return the string containing the error message for the given err code
160 wxString
GetErrorMsg(int errorcode
, bool badconv
) const;
165 m_isCompiled
= false;
170 // free the RE if compiled
175 wx_regfree(&m_RegEx
);
181 // free the RE if any and reinit the members
191 // the subexpressions data
192 wxRegExMatches
*m_Matches
;
195 // true if m_RegEx is valid
200 // ============================================================================
202 // ============================================================================
204 // ----------------------------------------------------------------------------
206 // ----------------------------------------------------------------------------
208 wxRegExImpl::wxRegExImpl()
213 wxRegExImpl::~wxRegExImpl()
218 wxString
wxRegExImpl::GetErrorMsg(int errorcode
, bool badconv
) const
220 #ifdef WXREGEX_CONVERT_TO_MB
221 // currently only needed when using system library in Unicode mode
224 return _("conversion to 8-bit encoding failed");
227 // 'use' badconv to avoid a compiler warning
233 // first get the string length needed
234 int len
= wx_regerror(errorcode
, &m_RegEx
, NULL
, 0);
237 char* szcmbError
= new char[++len
];
239 (void)wx_regerror(errorcode
, &m_RegEx
, szcmbError
, len
);
241 szError
= wxConvertMB2WX(szcmbError
);
242 delete [] szcmbError
;
244 else // regerror() returned 0
246 szError
= _("unknown error");
252 bool wxRegExImpl::Compile(const wxString
& expr
, int flags
)
256 #ifdef WX_NO_REGEX_ADVANCED
257 # define FLAVORS wxRE_BASIC
259 # define FLAVORS (wxRE_ADVANCED | wxRE_BASIC)
260 wxASSERT_MSG( (flags
& FLAVORS
) != FLAVORS
,
261 _T("incompatible flags in wxRegEx::Compile") );
263 wxASSERT_MSG( !(flags
& ~(FLAVORS
| wxRE_ICASE
| wxRE_NOSUB
| wxRE_NEWLINE
)),
264 _T("unrecognized flags in wxRegEx::Compile") );
266 // translate our flags to regcomp() ones
268 if ( !(flags
& wxRE_BASIC
) )
269 #ifndef WX_NO_REGEX_ADVANCED
270 if (flags
& wxRE_ADVANCED
)
271 flagsRE
|= REG_ADVANCED
;
274 flagsRE
|= REG_EXTENDED
;
275 if ( flags
& wxRE_ICASE
)
276 flagsRE
|= REG_ICASE
;
277 if ( flags
& wxRE_NOSUB
)
278 flagsRE
|= REG_NOSUB
;
279 if ( flags
& wxRE_NEWLINE
)
280 flagsRE
|= REG_NEWLINE
;
283 #ifdef WXREGEX_USING_BUILTIN
285 int errorcode
= wx_re_comp(&m_RegEx
, expr
, expr
.length(), flagsRE
);
287 const wxWX2MBbuf conv
= expr
.mbc_str();
288 int errorcode
= conv
? regcomp(&m_RegEx
, conv
, flagsRE
) : REG_BADPAT
;
293 wxLogError(_("Invalid regular expression '%s': %s"),
294 expr
.c_str(), GetErrorMsg(errorcode
, !conv
).c_str());
296 m_isCompiled
= false;
300 // don't allocate the matches array now, but do it later if necessary
301 if ( flags
& wxRE_NOSUB
)
303 // we don't need it at all
308 // we will alloc the array later (only if really needed) but count
309 // the number of sub-expressions in the regex right now
311 // there is always one for the whole expression
314 // and some more for bracketed subexperessions
315 for ( const wxChar
*cptr
= expr
.c_str(); *cptr
; cptr
++ )
317 if ( *cptr
== _T('\\') )
319 // in basic RE syntax groups are inside \(...\)
320 if ( *++cptr
== _T('(') && (flags
& wxRE_BASIC
) )
325 else if ( *cptr
== _T('(') && !(flags
& wxRE_BASIC
) )
327 // we know that the previous character is not an unquoted
328 // backslash because it would have been eaten above, so we
329 // have a bare '(' and this indicates a group start for the
330 // extended syntax. '(?' is used for extensions by perl-
331 // like REs (e.g. advanced), and is not valid for POSIX
332 // extended, so ignore them always.
333 if ( cptr
[1] != _T('?') )
345 #ifdef WXREGEX_USING_RE_SEARCH
347 // On GNU, regexec is implemented as a wrapper around re_search. re_search
348 // requires a length parameter which the POSIX regexec does not have,
349 // therefore regexec must do a strlen on the search text each time it is
350 // called. This can drastically affect performance when matching is done in
351 // a loop along a string, such as during a search and replace. Therefore if
352 // re_search is detected by configure, it is used directly.
354 static int ReSearch(const regex_t
*preg
,
357 re_registers
*matches
,
360 regex_t
*pattern
= wx_const_cast(regex_t
*, preg
);
362 pattern
->not_bol
= (eflags
& REG_NOTBOL
) != 0;
363 pattern
->not_eol
= (eflags
& REG_NOTEOL
) != 0;
364 pattern
->regs_allocated
= REGS_FIXED
;
366 int ret
= re_search(pattern
, text
, len
, 0, len
, matches
);
367 return ret
>= 0 ? 0 : REG_NOMATCH
;
370 #endif // WXREGEX_USING_RE_SEARCH
372 bool wxRegExImpl::Matches(const wxRegChar
*str
,
374 WXREGEX_IF_NEED_LEN(size_t len
)) const
376 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
378 // translate our flags to regexec() ones
379 wxASSERT_MSG( !(flags
& ~(wxRE_NOTBOL
| wxRE_NOTEOL
)),
380 _T("unrecognized flags in wxRegEx::Matches") );
383 if ( flags
& wxRE_NOTBOL
)
384 flagsRE
|= REG_NOTBOL
;
385 if ( flags
& wxRE_NOTEOL
)
386 flagsRE
|= REG_NOTEOL
;
388 // allocate matches array if needed
389 wxRegExImpl
*self
= wxConstCast(this, wxRegExImpl
);
390 if ( !m_Matches
&& m_nMatches
)
392 self
->m_Matches
= new wxRegExMatches(m_nMatches
);
395 wxRegExMatches::match_type matches
= m_Matches
? m_Matches
->get() : NULL
;
398 #if defined WXREGEX_USING_BUILTIN
399 int rc
= wx_re_exec(&self
->m_RegEx
, str
, len
, NULL
, m_nMatches
, matches
, flagsRE
);
400 #elif defined WXREGEX_USING_RE_SEARCH
401 int rc
= str
? ReSearch(&self
->m_RegEx
, str
, len
, matches
, flagsRE
) : REG_BADPAT
;
403 int rc
= str
? regexec(&self
->m_RegEx
, str
, m_nMatches
, matches
, flagsRE
) : REG_BADPAT
;
409 // matched successfully
414 wxLogError(_("Failed to find match for regular expression: %s"),
415 GetErrorMsg(rc
, !str
).c_str());
424 bool wxRegExImpl::GetMatch(size_t *start
, size_t *len
, size_t index
) const
426 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
427 wxCHECK_MSG( m_nMatches
, false, _T("can't use with wxRE_NOSUB") );
428 wxCHECK_MSG( m_Matches
, false, _T("must call Matches() first") );
429 wxCHECK_MSG( index
< m_nMatches
, false, _T("invalid match index") );
432 *start
= m_Matches
->Start(index
);
434 *len
= m_Matches
->End(index
) - m_Matches
->Start(index
);
439 size_t wxRegExImpl::GetMatchCount() const
441 wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") );
442 wxCHECK_MSG( m_nMatches
, 0, _T("can't use with wxRE_NOSUB") );
447 int wxRegExImpl::Replace(wxString
*text
,
448 const wxString
& replacement
,
449 size_t maxMatches
) const
451 wxCHECK_MSG( text
, wxNOT_FOUND
, _T("NULL text in wxRegEx::Replace") );
452 wxCHECK_MSG( IsValid(), wxNOT_FOUND
, _T("must successfully Compile() first") );
455 #ifndef WXREGEX_CONVERT_TO_MB
456 const wxChar
*textstr
= text
->c_str();
457 size_t textlen
= text
->length();
459 const wxWX2MBbuf textstr
= WXREGEX_CHAR(*text
);
462 wxLogError(_("Failed to find match for regular expression: %s"),
463 GetErrorMsg(0, true).c_str());
466 size_t textlen
= strlen(textstr
);
470 // the replacement text
473 // the result, allow 25% extra
475 result
.reserve(5 * textlen
/ 4);
477 // attempt at optimization: don't iterate over the string if it doesn't
478 // contain back references at all
479 bool mayHaveBackrefs
=
480 replacement
.find_first_of(_T("\\&")) != wxString::npos
;
482 if ( !mayHaveBackrefs
)
484 textNew
= replacement
;
487 // the position where we start looking for the match
488 size_t matchStart
= 0;
490 // number of replacement made: we won't make more than maxMatches of them
491 // (unless maxMatches is 0 which doesn't limit the number of replacements)
492 size_t countRepl
= 0;
494 // note that "^" shouldn't match after the first call to Matches() so we
495 // use wxRE_NOTBOL to prevent it from happening
496 while ( (!maxMatches
|| countRepl
< maxMatches
) &&
497 Matches(textstr
+ matchStart
,
498 countRepl
? wxRE_NOTBOL
: 0
499 WXREGEX_IF_NEED_LEN(textlen
- matchStart
)) )
501 // the string possibly contains back references: we need to calculate
502 // the replacement text anew after each match
503 if ( mayHaveBackrefs
)
505 mayHaveBackrefs
= false;
507 textNew
.reserve(replacement
.length());
509 for ( const wxChar
*p
= replacement
.c_str(); *p
; p
++ )
511 size_t index
= (size_t)-1;
513 if ( *p
== _T('\\') )
515 if ( wxIsdigit(*++p
) )
519 index
= (size_t)wxStrtoul(p
, &end
, 10);
520 p
= end
- 1; // -1 to compensate for p++ in the loop
522 //else: backslash used as escape character
524 else if ( *p
== _T('&') )
526 // treat this as "\0" for compatbility with ed and such
530 // do we have a back reference?
531 if ( index
!= (size_t)-1 )
535 if ( !GetMatch(&start
, &len
, index
) )
537 wxFAIL_MSG( _T("invalid back reference") );
543 textNew
+= wxString(textstr
+ matchStart
+ start
,
544 *wxConvCurrent
, len
);
546 mayHaveBackrefs
= true;
549 else // ordinary character
557 if ( !GetMatch(&start
, &len
) )
559 // we did have match as Matches() returned true above!
560 wxFAIL_MSG( _T("internal logic error in wxRegEx::Replace") );
565 // an insurance against implementations that don't grow exponentially
566 // to ensure building the result takes linear time
567 if (result
.capacity() < result
.length() + start
+ textNew
.length())
568 result
.reserve(2 * result
.length());
570 #ifndef WXREGEX_CONVERT_TO_MB
571 result
.append(*text
, matchStart
, start
);
573 result
.append(wxString(textstr
+ matchStart
, *wxConvCurrent
, start
));
576 result
.append(textNew
);
583 #ifndef WXREGEX_CONVERT_TO_MB
584 result
.append(*text
, matchStart
, wxString::npos
);
586 result
.append(wxString(textstr
+ matchStart
, *wxConvCurrent
));
593 // ----------------------------------------------------------------------------
594 // wxRegEx: all methods are mostly forwarded to wxRegExImpl
595 // ----------------------------------------------------------------------------
607 bool wxRegEx::Compile(const wxString
& expr
, int flags
)
611 m_impl
= new wxRegExImpl
;
614 if ( !m_impl
->Compile(expr
, flags
) )
616 // error message already given in wxRegExImpl::Compile
626 bool wxRegEx::Matches(const wxChar
*str
, int flags
, size_t len
) const
628 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
631 return m_impl
->Matches(WXREGEX_CHAR(str
), flags
WXREGEX_IF_NEED_LEN(len
));
634 bool wxRegEx::Matches(const wxChar
*str
, int flags
) const
636 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
638 return m_impl
->Matches(WXREGEX_CHAR(str
),
640 WXREGEX_IF_NEED_LEN(wxStrlen(str
)));
643 bool wxRegEx::GetMatch(size_t *start
, size_t *len
, size_t index
) const
645 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
647 return m_impl
->GetMatch(start
, len
, index
);
650 wxString
wxRegEx::GetMatch(const wxString
& text
, size_t index
) const
653 if ( !GetMatch(&start
, &len
, index
) )
654 return wxEmptyString
;
656 return text
.Mid(start
, len
);
659 size_t wxRegEx::GetMatchCount() const
661 wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") );
663 return m_impl
->GetMatchCount();
666 int wxRegEx::Replace(wxString
*pattern
,
667 const wxString
& replacement
,
668 size_t maxMatches
) const
670 wxCHECK_MSG( IsValid(), wxNOT_FOUND
, _T("must successfully Compile() first") );
672 return m_impl
->Replace(pattern
, replacement
, maxMatches
);
675 #endif // wxUSE_REGEX