]>
git.saurik.com Git - wxWidgets.git/blob - src/common/regex.cpp
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 // ----------------------------------------------------------------------------
22 #pragma implementation "regex.h"
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
35 #include "wx/object.h"
36 #include "wx/string.h"
45 // ----------------------------------------------------------------------------
47 // ----------------------------------------------------------------------------
49 // the real implementation of wxRegEx
57 // return TRUE if Compile() had been called successfully
58 bool IsValid() const { return m_isCompiled
; }
61 bool Compile(const wxString
& expr
, int flags
= 0);
62 bool Matches(const wxChar
*str
, int flags
= 0) const;
63 bool GetMatch(size_t *start
, size_t *len
, size_t index
= 0) const;
64 int Replace(wxString
*pattern
, const wxString
& replacement
,
65 size_t maxMatches
= 0) const;
68 // return the string containing the error message for the given err code
69 wxString
GetErrorMsg(int errorcode
) const;
71 // free the RE if compiled
85 // the subexpressions data
86 regmatch_t
*m_Matches
;
89 // TRUE if m_RegEx is valid
93 // ============================================================================
95 // ============================================================================
97 // ----------------------------------------------------------------------------
99 // ----------------------------------------------------------------------------
101 wxRegExImpl::wxRegExImpl()
103 m_isCompiled
= FALSE
;
107 wxRegExImpl::~wxRegExImpl()
114 wxString
wxRegExImpl::GetErrorMsg(int errorcode
) const
118 // first get the string length needed
119 int len
= regerror(errorcode
, &m_RegEx
, NULL
, 0);
124 (void)regerror(errorcode
, &m_RegEx
, msg
.GetWriteBuf(len
), len
);
128 else // regerror() returned 0
130 msg
= _("unknown error");
136 bool wxRegExImpl::Compile(const wxString
& expr
, int flags
)
140 // translate our flags to regcomp() ones
141 wxASSERT_MSG( !(flags
&
142 ~(wxRE_BASIC
| wxRE_ICASE
| wxRE_NOSUB
| wxRE_NEWLINE
)),
143 _T("unrecognized flags in wxRegEx::Compile") );
146 if ( !(flags
& wxRE_BASIC
) )
147 flagsRE
|= REG_EXTENDED
;
148 if ( flags
& wxRE_ICASE
)
149 flagsRE
|= REG_ICASE
;
150 if ( flags
& wxRE_NOSUB
)
151 flagsRE
|= REG_NOSUB
;
152 if ( flags
& wxRE_NEWLINE
)
153 flagsRE
|= REG_NEWLINE
;
156 int errorcode
= regcomp(&m_RegEx
, expr
, flagsRE
);
159 wxLogError(_("Invalid regular expression '%s': %s"),
160 expr
.c_str(), GetErrorMsg(errorcode
).c_str());
162 m_isCompiled
= FALSE
;
166 // don't allocate the matches array now, but do it later if necessary
167 if ( flags
& wxRE_NOSUB
)
169 // we don't need it at all
175 m_nMatches
= WX_REGEX_MAXMATCHES
;
184 bool wxRegExImpl::Matches(const wxChar
*str
, int flags
) const
186 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
188 // translate our flags to regexec() ones
189 wxASSERT_MSG( !(flags
& ~(wxRE_NOTBOL
| wxRE_NOTEOL
)),
190 _T("unrecognized flags in wxRegEx::Matches") );
193 if ( flags
& wxRE_NOTBOL
)
194 flagsRE
|= REG_NOTBOL
;
195 if ( flags
& wxRE_NOTEOL
)
196 flagsRE
|= REG_NOTEOL
;
198 // allocate matches array if needed
199 wxRegExImpl
*self
= wxConstCast(this, wxRegExImpl
);
200 if ( !m_Matches
&& m_nMatches
)
202 self
->m_Matches
= new regmatch_t
[m_nMatches
];
206 int rc
= regexec(&self
->m_RegEx
, str
, m_nMatches
, m_Matches
, flagsRE
);
211 // matched successfully
216 wxLogError(_("Failed to match '%s' in regular expression: %s"),
217 str
, GetErrorMsg(rc
).c_str());
226 bool wxRegExImpl::GetMatch(size_t *start
, size_t *len
, size_t index
) const
228 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
229 wxCHECK_MSG( m_Matches
, FALSE
, _T("can't use with wxRE_NOSUB") );
230 wxCHECK_MSG( index
< m_nMatches
, FALSE
, _T("invalid match index") );
232 const regmatch_t
& match
= m_Matches
[index
];
233 if ( match
.rm_so
== -1 )
237 *start
= match
.rm_so
;
239 *len
= match
.rm_eo
- match
.rm_so
;
244 int wxRegExImpl::Replace(wxString
*text
,
245 const wxString
& replacement
,
246 size_t maxMatches
) const
248 wxCHECK_MSG( text
, -1, _T("NULL text in wxRegEx::Replace") );
249 wxCHECK_MSG( IsValid(), -1, _T("must successfully Compile() first") );
251 // the replacement text
254 // attempt at optimization: don't iterate over the string if it doesn't
255 // contain back references at all
256 bool mayHaveBackrefs
=
257 replacement
.find_first_of(_T("\\&")) != wxString::npos
;
259 if ( !mayHaveBackrefs
)
261 textNew
= replacement
;
264 // the position where we start looking for the match
266 // NB: initial version had a nasty bug because it used a wxChar* instead of
267 // an index but the problem is that replace() in the loop invalidates
268 // all pointers into the string so we have to use indices instead
269 size_t matchStart
= 0;
271 // number of replacement made: we won't make more than maxMatches of them
272 // (unless maxMatches is 0 which doesn't limit the number of replacements)
273 size_t countRepl
= 0;
275 // note that "^" shouldn't match after the first call to Matches() so we
276 // use wxRE_NOTBOL to prevent it from happening
277 while ( (!maxMatches
|| countRepl
< maxMatches
) &&
278 Matches(text
->c_str() + matchStart
, countRepl
? wxRE_NOTBOL
: 0) )
280 // the string possibly contains back references: we need to calculate
281 // the replacement text anew after each match
282 if ( mayHaveBackrefs
)
284 mayHaveBackrefs
= FALSE
;
286 textNew
.reserve(replacement
.length());
288 for ( const wxChar
*p
= replacement
.c_str(); *p
; p
++ )
290 size_t index
= (size_t)-1;
292 if ( *p
== _T('\\') )
294 if ( wxIsdigit(*++p
) )
298 index
= (size_t)wxStrtoul(p
, &end
, 10);
299 p
= end
- 1; // -1 to compensate for p++ in the loop
301 //else: backslash used as escape character
303 else if ( *p
== _T('&') )
305 // treat this as "\0" for compatbility with ed and such
309 // do we have a back reference?
310 if ( index
!= (size_t)-1 )
314 if ( !GetMatch(&start
, &len
, index
) )
316 // we can't do it because GetMatch() returns FALSE
317 // even for a valid back reference index if it didn't
318 // match for this expression (e.g. it when alternative
319 // branches were used and the one contained the back
322 // it would be better to distinguish between this case
323 // and really invalid index, but I don't know how to
326 //wxFAIL_MSG( _T("invalid back reference") );
332 textNew
+= wxString(text
->c_str() + matchStart
+ start
,
335 mayHaveBackrefs
= TRUE
;
338 else // ordinary character
346 if ( !GetMatch(&start
, &len
) )
348 // we did have match as Matches() returned true above!
349 wxFAIL_MSG( _T("internal logic error in wxRegEx::Replace") );
355 text
->replace(matchStart
, len
, textNew
);
359 matchStart
+= textNew
.length();
365 // ----------------------------------------------------------------------------
366 // wxRegEx: all methods are mostly forwarded to wxRegExImpl
367 // ----------------------------------------------------------------------------
380 bool wxRegEx::Compile(const wxString
& expr
, int flags
)
384 m_impl
= new wxRegExImpl
;
387 if ( !m_impl
->Compile(expr
, flags
) )
389 // error message already given in wxRegExImpl::Compile
399 bool wxRegEx::Matches(const wxChar
*str
, int flags
) const
401 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
403 return m_impl
->Matches(str
, flags
);
406 bool wxRegEx::GetMatch(size_t *start
, size_t *len
, size_t index
) const
408 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
410 return m_impl
->GetMatch(start
, len
, index
);
413 wxString
wxRegEx::GetMatch(const wxString
& text
, size_t index
) const
416 if ( !GetMatch(&start
, &len
, index
) )
417 return wxEmptyString
;
419 return text
.Mid(start
, len
);
422 int wxRegEx::Replace(wxString
*pattern
,
423 const wxString
& replacement
,
424 size_t maxMatches
) const
426 wxCHECK_MSG( IsValid(), -1, _T("must successfully Compile() first") );
428 return m_impl
->Replace(pattern
, replacement
, maxMatches
);
431 #endif // wxUSE_REGEX