]>
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>
56 #define regerror wx_regerror
57 #define regfree wx_regfree
60 // ----------------------------------------------------------------------------
62 // ----------------------------------------------------------------------------
64 // the real implementation of wxRegEx
72 // return TRUE if Compile() had been called successfully
73 bool IsValid() const { return m_isCompiled
; }
76 bool Compile(const wxString
& expr
, int flags
= 0);
77 bool Matches(const wxChar
*str
, int flags
= 0) const;
78 bool GetMatch(size_t *start
, size_t *len
, size_t index
= 0) const;
79 int Replace(wxString
*pattern
, const wxString
& replacement
,
80 size_t maxMatches
= 0) const;
83 // return the string containing the error message for the given err code
84 wxString
GetErrorMsg(int errorcode
) const;
94 // free the RE if compiled
105 // free the RE if any and reinit the members
116 // the subexpressions data
117 regmatch_t
*m_Matches
;
120 // TRUE if m_RegEx is valid
124 // ============================================================================
126 // ============================================================================
128 // ----------------------------------------------------------------------------
130 // ----------------------------------------------------------------------------
132 wxRegExImpl::wxRegExImpl()
137 wxRegExImpl::~wxRegExImpl()
142 wxString
wxRegExImpl::GetErrorMsg(int errorcode
) const
146 // first get the string length needed
147 int len
= regerror(errorcode
, &m_RegEx
, NULL
, 0);
153 wxCharBuffer
buf(len
);
155 (void)regerror(errorcode
, &m_RegEx
, (char *)buf
.data(), len
);
157 msg
= wxString(buf
.data(), wxConvLibc
);
159 (void)regerror(errorcode
, &m_RegEx
, msg
.GetWriteBuf(len
), len
);
164 else // regerror() returned 0
166 msg
= _("unknown error");
172 bool wxRegExImpl::Compile(const wxString
& expr
, int flags
)
176 // translate our flags to regcomp() ones
177 wxASSERT_MSG( !(flags
&
178 ~(wxRE_BASIC
| wxRE_ICASE
| wxRE_NOSUB
| wxRE_NEWLINE
)),
179 _T("unrecognized flags in wxRegEx::Compile") );
182 if ( !(flags
& wxRE_BASIC
) )
183 flagsRE
|= REG_EXTENDED
;
184 if ( flags
& wxRE_ICASE
)
185 flagsRE
|= REG_ICASE
;
186 if ( flags
& wxRE_NOSUB
)
187 flagsRE
|= REG_NOSUB
;
188 if ( flags
& wxRE_NEWLINE
)
189 flagsRE
|= REG_NEWLINE
;
194 int errorcode
= wx_regcomp(&m_RegEx
, expr
, expr
.Length(), flagsRE
);
196 int errorcode
= regcomp(&m_RegEx
, expr
.mb_str(), flagsRE
);
201 wxLogError(_("Invalid regular expression '%s': %s"),
202 expr
.c_str(), GetErrorMsg(errorcode
).c_str());
204 m_isCompiled
= FALSE
;
208 // don't allocate the matches array now, but do it later if necessary
209 if ( flags
& wxRE_NOSUB
)
211 // we don't need it at all
216 // we will alloc the array later (only if really needed) but count
217 // the number of sub-expressions in the regex right now
219 // there is always one for the whole expression
222 // and some more for bracketed subexperessions
223 for ( const wxChar
*cptr
= expr
.c_str(); *cptr
; cptr
++ )
225 if ( *cptr
== _T('\\') )
227 // in basic RE syntax groups are inside \(...\)
228 if ( *++cptr
== _T('(') && (flags
& wxRE_BASIC
) )
233 else if ( *cptr
== _T('(') && !(flags
& wxRE_BASIC
) )
235 // we know that the previous character is not an unquoted
236 // backslash because it would have been eaten above, so we
237 // have a bar '(' and this indicates a group start for the
250 bool wxRegExImpl::Matches(const wxChar
*str
, int flags
) const
252 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
254 // translate our flags to regexec() ones
255 wxASSERT_MSG( !(flags
& ~(wxRE_NOTBOL
| wxRE_NOTEOL
)),
256 _T("unrecognized flags in wxRegEx::Matches") );
259 if ( flags
& wxRE_NOTBOL
)
260 flagsRE
|= REG_NOTBOL
;
261 if ( flags
& wxRE_NOTEOL
)
262 flagsRE
|= REG_NOTEOL
;
264 // allocate matches array if needed
265 wxRegExImpl
*self
= wxConstCast(this, wxRegExImpl
);
266 if ( !m_Matches
&& m_nMatches
)
268 self
->m_Matches
= new regmatch_t
[m_nMatches
];
274 int rc
= wx_regexec(&self
->m_RegEx
, str
, wxStrlen(str
), &rd
, m_nMatches
, m_Matches
, flagsRE
);
276 int rc
= regexec(&self
->m_RegEx
, wxConvertWX2MB(str
), m_nMatches
, m_Matches
, flagsRE
);
282 // matched successfully
287 wxLogError(_("Failed to match '%s' in regular expression: %s"),
288 str
, GetErrorMsg(rc
).c_str());
297 bool wxRegExImpl::GetMatch(size_t *start
, size_t *len
, size_t index
) const
299 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
300 wxCHECK_MSG( m_Matches
, FALSE
, _T("can't use with wxRE_NOSUB") );
301 wxCHECK_MSG( index
< m_nMatches
, FALSE
, _T("invalid match index") );
303 const regmatch_t
& match
= m_Matches
[index
];
306 *start
= match
.rm_so
;
308 *len
= match
.rm_eo
- match
.rm_so
;
313 int wxRegExImpl::Replace(wxString
*text
,
314 const wxString
& replacement
,
315 size_t maxMatches
) const
317 wxCHECK_MSG( text
, -1, _T("NULL text in wxRegEx::Replace") );
318 wxCHECK_MSG( IsValid(), -1, _T("must successfully Compile() first") );
320 // the replacement text
323 // attempt at optimization: don't iterate over the string if it doesn't
324 // contain back references at all
325 bool mayHaveBackrefs
=
326 replacement
.find_first_of(_T("\\&")) != wxString::npos
;
328 if ( !mayHaveBackrefs
)
330 textNew
= replacement
;
333 // the position where we start looking for the match
335 // NB: initial version had a nasty bug because it used a wxChar* instead of
336 // an index but the problem is that replace() in the loop invalidates
337 // all pointers into the string so we have to use indices instead
338 size_t matchStart
= 0;
340 // number of replacement made: we won't make more than maxMatches of them
341 // (unless maxMatches is 0 which doesn't limit the number of replacements)
342 size_t countRepl
= 0;
344 // note that "^" shouldn't match after the first call to Matches() so we
345 // use wxRE_NOTBOL to prevent it from happening
346 while ( (!maxMatches
|| countRepl
< maxMatches
) &&
347 Matches(text
->c_str() + matchStart
, countRepl
? wxRE_NOTBOL
: 0) )
349 // the string possibly contains back references: we need to calculate
350 // the replacement text anew after each match
351 if ( mayHaveBackrefs
)
353 mayHaveBackrefs
= FALSE
;
355 textNew
.reserve(replacement
.length());
357 for ( const wxChar
*p
= replacement
.c_str(); *p
; p
++ )
359 size_t index
= (size_t)-1;
361 if ( *p
== _T('\\') )
363 if ( wxIsdigit(*++p
) )
367 index
= (size_t)wxStrtoul(p
, &end
, 10);
368 p
= end
- 1; // -1 to compensate for p++ in the loop
370 //else: backslash used as escape character
372 else if ( *p
== _T('&') )
374 // treat this as "\0" for compatbility with ed and such
378 // do we have a back reference?
379 if ( index
!= (size_t)-1 )
383 if ( !GetMatch(&start
, &len
, index
) )
385 wxFAIL_MSG( _T("invalid back reference") );
391 textNew
+= wxString(text
->c_str() + matchStart
+ start
,
394 mayHaveBackrefs
= TRUE
;
397 else // ordinary character
405 if ( !GetMatch(&start
, &len
) )
407 // we did have match as Matches() returned true above!
408 wxFAIL_MSG( _T("internal logic error in wxRegEx::Replace") );
414 text
->replace(matchStart
, len
, textNew
);
418 matchStart
+= textNew
.length();
424 // ----------------------------------------------------------------------------
425 // wxRegEx: all methods are mostly forwarded to wxRegExImpl
426 // ----------------------------------------------------------------------------
439 bool wxRegEx::Compile(const wxString
& expr
, int flags
)
443 m_impl
= new wxRegExImpl
;
446 if ( !m_impl
->Compile(expr
, flags
) )
448 // error message already given in wxRegExImpl::Compile
458 bool wxRegEx::Matches(const wxChar
*str
, int flags
) const
460 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
462 return m_impl
->Matches(str
, flags
);
465 bool wxRegEx::GetMatch(size_t *start
, size_t *len
, size_t index
) const
467 wxCHECK_MSG( IsValid(), FALSE
, _T("must successfully Compile() first") );
469 return m_impl
->GetMatch(start
, len
, index
);
472 wxString
wxRegEx::GetMatch(const wxString
& text
, size_t index
) const
475 if ( !GetMatch(&start
, &len
, index
) )
476 return wxEmptyString
;
478 return text
.Mid(start
, len
);
481 int wxRegEx::Replace(wxString
*pattern
,
482 const wxString
& replacement
,
483 size_t maxMatches
) const
485 wxCHECK_MSG( IsValid(), -1, _T("must successfully Compile() first") );
487 return m_impl
->Replace(pattern
, replacement
, maxMatches
);
492 /** Locale functions */
496 int wx_isdigit(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIsdigit((unsigned char) c
));}
497 int wx_isalpha(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIsalpha((unsigned char) c
));}
498 int wx_isalnum(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIsalnum((unsigned char) c
));}
499 int wx_isupper(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIsupper((unsigned char) c
));}
500 int wx_islower(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIslower((unsigned char) c
));}
501 int wx_isgraph(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIsgraph((unsigned char) c
));}
502 int wx_ispunct(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIspunct((unsigned char) c
));}
503 int wx_isspace(wx_wchar c
) {return (c
>= 0 && c
<= UCHAR_MAX
&& wxIsspace((unsigned char) c
));}
505 wx_wchar
wx_toupper(wx_wchar c
)
507 if (c
>= 0 && c
<= UCHAR_MAX
)
508 return wxToupper((unsigned char) c
);
513 wx_wchar
wx_tolower(wx_wchar c
)
515 if (c
>= 0 && c
<= UCHAR_MAX
)
516 return wxTolower((unsigned char) c
);
524 #endif // wxUSE_REGEX