]>
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 // ----------------------------------------------------------------------------
21 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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"
41 // FreeBSD, Watcom and DMars require this, CW doesn't have nor need it.
42 // Others also don't seem to need it. If you have an error related to
43 // (not) including <sys/types.h> please report details to
44 // wx-dev@lists.wxwindows.org
45 #if defined(__UNIX__) || defined(__WATCOMC__) || defined(__DIGITALMARS__)
46 # include <sys/types.h>
52 // ----------------------------------------------------------------------------
54 // ----------------------------------------------------------------------------
56 // the real implementation of wxRegEx
64 // return true if Compile() had been called successfully
65 bool IsValid() const { return m_isCompiled
; }
68 bool Compile(const wxString
& expr
, int flags
= 0);
69 bool Matches(const wxChar
*str
, int flags
= 0) const;
70 bool GetMatch(size_t *start
, size_t *len
, size_t index
= 0) const;
71 size_t GetMatchCount() const;
72 int Replace(wxString
*pattern
, const wxString
& replacement
,
73 size_t maxMatches
= 0) const;
76 // return the string containing the error message for the given err code
77 wxString
GetErrorMsg(int errorcode
, bool badconv
) const;
87 // free the RE if compiled
98 // free the RE if any and reinit the members
109 // the subexpressions data
110 regmatch_t
*m_Matches
;
113 // true if m_RegEx is valid
117 // ============================================================================
119 // ============================================================================
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 wxRegExImpl::wxRegExImpl()
130 wxRegExImpl::~wxRegExImpl()
135 wxString
wxRegExImpl::GetErrorMsg(int errorcode
, bool badconv
) const
137 #if wxUSE_UNICODE && !defined(__REG_NOFRONT)
138 // currently only needed when using system library in Unicode mode
141 return _("conversion to 8-bit encoding failed");
144 // 'use' badconv to avoid a compiler warning
150 // first get the string length needed
151 int len
= regerror(errorcode
, &m_RegEx
, NULL
, 0);
154 char* szcmbError
= new char[++len
];
156 (void)regerror(errorcode
, &m_RegEx
, szcmbError
, len
);
158 szError
= wxConvertMB2WX(szcmbError
);
159 delete [] szcmbError
;
161 else // regerror() returned 0
163 szError
= _("unknown error");
169 bool wxRegExImpl::Compile(const wxString
& expr
, int flags
)
173 #ifdef WX_NO_REGEX_ADVANCED
174 # define FLAVORS wxRE_BASIC
176 # define FLAVORS (wxRE_ADVANCED | wxRE_BASIC)
177 wxASSERT_MSG( (flags
& FLAVORS
) != FLAVORS
,
178 _T("incompatible flags in wxRegEx::Compile") );
180 wxASSERT_MSG( !(flags
& ~(FLAVORS
| wxRE_ICASE
| wxRE_NOSUB
| wxRE_NEWLINE
)),
181 _T("unrecognized flags in wxRegEx::Compile") );
183 // translate our flags to regcomp() ones
185 if ( !(flags
& wxRE_BASIC
) )
186 #ifndef WX_NO_REGEX_ADVANCED
187 if (flags
& wxRE_ADVANCED
)
188 flagsRE
|= REG_ADVANCED
;
191 flagsRE
|= REG_EXTENDED
;
192 if ( flags
& wxRE_ICASE
)
193 flagsRE
|= REG_ICASE
;
194 if ( flags
& wxRE_NOSUB
)
195 flagsRE
|= REG_NOSUB
;
196 if ( flags
& wxRE_NEWLINE
)
197 flagsRE
|= REG_NEWLINE
;
202 int errorcode
= wx_re_comp(&m_RegEx
, expr
, expr
.length(), flagsRE
);
204 const wxWX2MBbuf conv
= expr
.mbc_str();
205 int errorcode
= conv
? regcomp(&m_RegEx
, conv
, flagsRE
) : REG_BADPAT
;
210 wxLogError(_("Invalid regular expression '%s': %s"),
211 expr
.c_str(), GetErrorMsg(errorcode
, !conv
).c_str());
213 m_isCompiled
= false;
217 // don't allocate the matches array now, but do it later if necessary
218 if ( flags
& wxRE_NOSUB
)
220 // we don't need it at all
225 // we will alloc the array later (only if really needed) but count
226 // the number of sub-expressions in the regex right now
228 // there is always one for the whole expression
231 // and some more for bracketed subexperessions
232 for ( const wxChar
*cptr
= expr
.c_str(); *cptr
; cptr
++ )
234 if ( *cptr
== _T('\\') )
236 // in basic RE syntax groups are inside \(...\)
237 if ( *++cptr
== _T('(') && (flags
& wxRE_BASIC
) )
242 else if ( *cptr
== _T('(') && !(flags
& wxRE_BASIC
) )
244 // we know that the previous character is not an unquoted
245 // backslash because it would have been eaten above, so we
246 // have a bare '(' and this indicates a group start for the
247 // extended syntax. '(?' is used for extensions by perl-
248 // like REs (e.g. advanced), and is not valid for POSIX
249 // extended, so ignore them always.
250 if ( cptr
[1] != _T('?') )
262 bool wxRegExImpl::Matches(const wxChar
*str
, int flags
) const
264 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
266 // translate our flags to regexec() ones
267 wxASSERT_MSG( !(flags
& ~(wxRE_NOTBOL
| wxRE_NOTEOL
)),
268 _T("unrecognized flags in wxRegEx::Matches") );
271 if ( flags
& wxRE_NOTBOL
)
272 flagsRE
|= REG_NOTBOL
;
273 if ( flags
& wxRE_NOTEOL
)
274 flagsRE
|= REG_NOTEOL
;
276 // allocate matches array if needed
277 wxRegExImpl
*self
= wxConstCast(this, wxRegExImpl
);
278 if ( !m_Matches
&& m_nMatches
)
280 self
->m_Matches
= new regmatch_t
[m_nMatches
];
286 int rc
= wx_re_exec(&self
->m_RegEx
, str
, wxStrlen(str
), NULL
, m_nMatches
, m_Matches
, flagsRE
);
288 const wxWX2MBbuf conv
= wxConvertWX2MB(str
);
289 int rc
= conv
? regexec(&self
->m_RegEx
, conv
, m_nMatches
, m_Matches
, flagsRE
) : REG_BADPAT
;
295 // matched successfully
300 wxLogError(_("Failed to match '%s' in regular expression: %s"),
301 str
, GetErrorMsg(rc
, !conv
).c_str());
310 bool wxRegExImpl::GetMatch(size_t *start
, size_t *len
, size_t index
) const
312 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
313 wxCHECK_MSG( m_nMatches
, false, _T("can't use with wxRE_NOSUB") );
314 wxCHECK_MSG( m_Matches
, false, _T("must call Matches() first") );
315 wxCHECK_MSG( index
< m_nMatches
, false, _T("invalid match index") );
317 const regmatch_t
& match
= m_Matches
[index
];
320 *start
= match
.rm_so
;
322 *len
= match
.rm_eo
- match
.rm_so
;
327 size_t wxRegExImpl::GetMatchCount() const
329 wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") );
330 wxCHECK_MSG( m_nMatches
, 0, _T("can't use with wxRE_NOSUB") );
335 int wxRegExImpl::Replace(wxString
*text
,
336 const wxString
& replacement
,
337 size_t maxMatches
) const
339 wxCHECK_MSG( text
, wxNOT_FOUND
, _T("NULL text in wxRegEx::Replace") );
340 wxCHECK_MSG( IsValid(), wxNOT_FOUND
, _T("must successfully Compile() first") );
342 // the replacement text
345 // attempt at optimization: don't iterate over the string if it doesn't
346 // contain back references at all
347 bool mayHaveBackrefs
=
348 replacement
.find_first_of(_T("\\&")) != wxString::npos
;
350 if ( !mayHaveBackrefs
)
352 textNew
= replacement
;
355 // the position where we start looking for the match
357 // NB: initial version had a nasty bug because it used a wxChar* instead of
358 // an index but the problem is that replace() in the loop invalidates
359 // all pointers into the string so we have to use indices instead
360 size_t matchStart
= 0;
362 // number of replacement made: we won't make more than maxMatches of them
363 // (unless maxMatches is 0 which doesn't limit the number of replacements)
364 size_t countRepl
= 0;
366 // note that "^" shouldn't match after the first call to Matches() so we
367 // use wxRE_NOTBOL to prevent it from happening
368 while ( (!maxMatches
|| countRepl
< maxMatches
) &&
369 Matches(text
->c_str() + matchStart
, countRepl
? wxRE_NOTBOL
: 0) )
371 // the string possibly contains back references: we need to calculate
372 // the replacement text anew after each match
373 if ( mayHaveBackrefs
)
375 mayHaveBackrefs
= false;
377 textNew
.reserve(replacement
.length());
379 for ( const wxChar
*p
= replacement
.c_str(); *p
; p
++ )
381 size_t index
= (size_t)-1;
383 if ( *p
== _T('\\') )
385 if ( wxIsdigit(*++p
) )
389 index
= (size_t)wxStrtoul(p
, &end
, 10);
390 p
= end
- 1; // -1 to compensate for p++ in the loop
392 //else: backslash used as escape character
394 else if ( *p
== _T('&') )
396 // treat this as "\0" for compatbility with ed and such
400 // do we have a back reference?
401 if ( index
!= (size_t)-1 )
405 if ( !GetMatch(&start
, &len
, index
) )
407 wxFAIL_MSG( _T("invalid back reference") );
413 textNew
+= wxString(text
->c_str() + matchStart
+ start
,
416 mayHaveBackrefs
= true;
419 else // ordinary character
427 if ( !GetMatch(&start
, &len
) )
429 // we did have match as Matches() returned true above!
430 wxFAIL_MSG( _T("internal logic error in wxRegEx::Replace") );
436 text
->replace(matchStart
, len
, textNew
);
440 matchStart
+= textNew
.length();
446 // ----------------------------------------------------------------------------
447 // wxRegEx: all methods are mostly forwarded to wxRegExImpl
448 // ----------------------------------------------------------------------------
461 bool wxRegEx::Compile(const wxString
& expr
, int flags
)
465 m_impl
= new wxRegExImpl
;
468 if ( !m_impl
->Compile(expr
, flags
) )
470 // error message already given in wxRegExImpl::Compile
480 bool wxRegEx::Matches(const wxChar
*str
, int flags
) const
482 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
484 return m_impl
->Matches(str
, flags
);
487 bool wxRegEx::GetMatch(size_t *start
, size_t *len
, size_t index
) const
489 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
491 return m_impl
->GetMatch(start
, len
, index
);
494 wxString
wxRegEx::GetMatch(const wxString
& text
, size_t index
) const
497 if ( !GetMatch(&start
, &len
, index
) )
498 return wxEmptyString
;
500 return text
.Mid(start
, len
);
503 size_t wxRegEx::GetMatchCount() const
505 wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") );
507 return m_impl
->GetMatchCount();
510 int wxRegEx::Replace(wxString
*pattern
,
511 const wxString
& replacement
,
512 size_t maxMatches
) const
514 wxCHECK_MSG( IsValid(), wxNOT_FOUND
, _T("must successfully Compile() first") );
516 return m_impl
->Replace(pattern
, replacement
, maxMatches
);
519 #endif // wxUSE_REGEX