]>
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 // 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_BUILTIN_ONLY() wrap a parameter only used with the built-in regex
50 // WXREGEX_CONVERT_TO_MB indicates when the regex lib is using chars and
51 // wxChar is wide, so conversion must be done
53 # define WXREGEX_USING_BUILTIN
54 # define WXREGEX_BUILTIN_ONLY(x) ,x
56 # define WXREGEX_BUILTIN_ONLY(x)
58 # define WXREGEX_CONVERT_TO_MB
62 // ----------------------------------------------------------------------------
64 // ----------------------------------------------------------------------------
66 // the character type used by the regular expression engine
67 #ifndef WXREGEX_CONVERT_TO_MB
68 typedef wxChar wxRegChar
;
70 typedef char wxRegChar
;
73 // the real implementation of wxRegEx
81 // return true if Compile() had been called successfully
82 bool IsValid() const { return m_isCompiled
; }
85 bool Compile(const wxString
& expr
, int flags
= 0);
86 bool Matches(const wxRegChar
*str
, int flags
87 WXREGEX_BUILTIN_ONLY(size_t len
)) const;
88 bool GetMatch(size_t *start
, size_t *len
, size_t index
= 0) const;
89 size_t GetMatchCount() const;
90 int Replace(wxString
*pattern
, const wxString
& replacement
,
91 size_t maxMatches
= 0) const;
94 // return the string containing the error message for the given err code
95 wxString
GetErrorMsg(int errorcode
, bool badconv
) const;
100 m_isCompiled
= false;
105 // free the RE if compiled
116 // free the RE if any and reinit the members
127 // the subexpressions data
128 regmatch_t
*m_Matches
;
131 // true if m_RegEx is valid
135 // ============================================================================
137 // ============================================================================
139 // ----------------------------------------------------------------------------
141 // ----------------------------------------------------------------------------
143 wxRegExImpl::wxRegExImpl()
148 wxRegExImpl::~wxRegExImpl()
153 wxString
wxRegExImpl::GetErrorMsg(int errorcode
, bool badconv
) const
155 #ifdef WXREGEX_CONVERT_TO_MB
156 // currently only needed when using system library in Unicode mode
159 return _("conversion to 8-bit encoding failed");
162 // 'use' badconv to avoid a compiler warning
168 // first get the string length needed
169 int len
= regerror(errorcode
, &m_RegEx
, NULL
, 0);
172 char* szcmbError
= new char[++len
];
174 (void)regerror(errorcode
, &m_RegEx
, szcmbError
, len
);
176 szError
= wxConvertMB2WX(szcmbError
);
177 delete [] szcmbError
;
179 else // regerror() returned 0
181 szError
= _("unknown error");
187 bool wxRegExImpl::Compile(const wxString
& expr
, int flags
)
191 #ifdef WX_NO_REGEX_ADVANCED
192 # define FLAVORS wxRE_BASIC
194 # define FLAVORS (wxRE_ADVANCED | wxRE_BASIC)
195 wxASSERT_MSG( (flags
& FLAVORS
) != FLAVORS
,
196 _T("incompatible flags in wxRegEx::Compile") );
198 wxASSERT_MSG( !(flags
& ~(FLAVORS
| wxRE_ICASE
| wxRE_NOSUB
| wxRE_NEWLINE
)),
199 _T("unrecognized flags in wxRegEx::Compile") );
201 // translate our flags to regcomp() ones
203 if ( !(flags
& wxRE_BASIC
) )
204 #ifndef WX_NO_REGEX_ADVANCED
205 if (flags
& wxRE_ADVANCED
)
206 flagsRE
|= REG_ADVANCED
;
209 flagsRE
|= REG_EXTENDED
;
210 if ( flags
& wxRE_ICASE
)
211 flagsRE
|= REG_ICASE
;
212 if ( flags
& wxRE_NOSUB
)
213 flagsRE
|= REG_NOSUB
;
214 if ( flags
& wxRE_NEWLINE
)
215 flagsRE
|= REG_NEWLINE
;
218 #ifdef WXREGEX_USING_BUILTIN
220 int errorcode
= wx_re_comp(&m_RegEx
, expr
, expr
.length(), flagsRE
);
222 const wxWX2MBbuf conv
= expr
.mbc_str();
223 int errorcode
= conv
? regcomp(&m_RegEx
, conv
, flagsRE
) : REG_BADPAT
;
228 wxLogError(_("Invalid regular expression '%s': %s"),
229 expr
.c_str(), GetErrorMsg(errorcode
, !conv
).c_str());
231 m_isCompiled
= false;
235 // don't allocate the matches array now, but do it later if necessary
236 if ( flags
& wxRE_NOSUB
)
238 // we don't need it at all
243 // we will alloc the array later (only if really needed) but count
244 // the number of sub-expressions in the regex right now
246 // there is always one for the whole expression
249 // and some more for bracketed subexperessions
250 for ( const wxChar
*cptr
= expr
.c_str(); *cptr
; cptr
++ )
252 if ( *cptr
== _T('\\') )
254 // in basic RE syntax groups are inside \(...\)
255 if ( *++cptr
== _T('(') && (flags
& wxRE_BASIC
) )
260 else if ( *cptr
== _T('(') && !(flags
& wxRE_BASIC
) )
262 // we know that the previous character is not an unquoted
263 // backslash because it would have been eaten above, so we
264 // have a bare '(' and this indicates a group start for the
265 // extended syntax. '(?' is used for extensions by perl-
266 // like REs (e.g. advanced), and is not valid for POSIX
267 // extended, so ignore them always.
268 if ( cptr
[1] != _T('?') )
280 bool wxRegExImpl::Matches(const wxRegChar
*str
,
282 WXREGEX_BUILTIN_ONLY(size_t len
)) const
284 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
286 // translate our flags to regexec() ones
287 wxASSERT_MSG( !(flags
& ~(wxRE_NOTBOL
| wxRE_NOTEOL
)),
288 _T("unrecognized flags in wxRegEx::Matches") );
291 if ( flags
& wxRE_NOTBOL
)
292 flagsRE
|= REG_NOTBOL
;
293 if ( flags
& wxRE_NOTEOL
)
294 flagsRE
|= REG_NOTEOL
;
296 // allocate matches array if needed
297 wxRegExImpl
*self
= wxConstCast(this, wxRegExImpl
);
298 if ( !m_Matches
&& m_nMatches
)
300 self
->m_Matches
= new regmatch_t
[m_nMatches
];
304 #ifdef WXREGEX_USING_BUILTIN
305 int rc
= wx_re_exec(&self
->m_RegEx
, str
, len
, NULL
, m_nMatches
, m_Matches
, flagsRE
);
307 int rc
= str
? regexec(&self
->m_RegEx
, str
, m_nMatches
, m_Matches
, flagsRE
) : REG_BADPAT
;
313 // matched successfully
318 wxLogError(_("Failed to find match for regular expression: %s"),
319 GetErrorMsg(rc
, !str
).c_str());
328 bool wxRegExImpl::GetMatch(size_t *start
, size_t *len
, size_t index
) const
330 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
331 wxCHECK_MSG( m_nMatches
, false, _T("can't use with wxRE_NOSUB") );
332 wxCHECK_MSG( m_Matches
, false, _T("must call Matches() first") );
333 wxCHECK_MSG( index
< m_nMatches
, false, _T("invalid match index") );
335 const regmatch_t
& match
= m_Matches
[index
];
337 // we need the casts because rm_so can be a 64 bit quantity
339 *start
= wx_truncate_cast(size_t, match
.rm_so
);
341 *len
= wx_truncate_cast(size_t, match
.rm_eo
- match
.rm_so
);
346 size_t wxRegExImpl::GetMatchCount() const
348 wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") );
349 wxCHECK_MSG( m_nMatches
, 0, _T("can't use with wxRE_NOSUB") );
354 int wxRegExImpl::Replace(wxString
*text
,
355 const wxString
& replacement
,
356 size_t maxMatches
) const
358 wxCHECK_MSG( text
, wxNOT_FOUND
, _T("NULL text in wxRegEx::Replace") );
359 wxCHECK_MSG( IsValid(), wxNOT_FOUND
, _T("must successfully Compile() first") );
362 #ifndef WXREGEX_CONVERT_TO_MB
363 const wxChar
*textstr
= text
->c_str();
364 size_t textlen
= text
->length();
366 const wxWX2MBbuf textstr
= wxConvertWX2MB(*text
);
369 wxLogError(_("Failed to find match for regular expression: %s"),
370 GetErrorMsg(0, true).c_str());
373 size_t textlen
= strlen(textstr
);
377 // the replacement text
380 // the result, allow 25% extra
382 result
.reserve(5 * textlen
/ 4);
384 // attempt at optimization: don't iterate over the string if it doesn't
385 // contain back references at all
386 bool mayHaveBackrefs
=
387 replacement
.find_first_of(_T("\\&")) != wxString::npos
;
389 if ( !mayHaveBackrefs
)
391 textNew
= replacement
;
394 // the position where we start looking for the match
395 size_t matchStart
= 0;
397 // number of replacement made: we won't make more than maxMatches of them
398 // (unless maxMatches is 0 which doesn't limit the number of replacements)
399 size_t countRepl
= 0;
401 // note that "^" shouldn't match after the first call to Matches() so we
402 // use wxRE_NOTBOL to prevent it from happening
403 while ( (!maxMatches
|| countRepl
< maxMatches
) &&
404 Matches(textstr
+ matchStart
,
405 countRepl
? wxRE_NOTBOL
: 0
406 WXREGEX_BUILTIN_ONLY(textlen
- matchStart
)) )
408 // the string possibly contains back references: we need to calculate
409 // the replacement text anew after each match
410 if ( mayHaveBackrefs
)
412 mayHaveBackrefs
= false;
414 textNew
.reserve(replacement
.length());
416 for ( const wxChar
*p
= replacement
.c_str(); *p
; p
++ )
418 size_t index
= (size_t)-1;
420 if ( *p
== _T('\\') )
422 if ( wxIsdigit(*++p
) )
426 index
= (size_t)wxStrtoul(p
, &end
, 10);
427 p
= end
- 1; // -1 to compensate for p++ in the loop
429 //else: backslash used as escape character
431 else if ( *p
== _T('&') )
433 // treat this as "\0" for compatbility with ed and such
437 // do we have a back reference?
438 if ( index
!= (size_t)-1 )
442 if ( !GetMatch(&start
, &len
, index
) )
444 wxFAIL_MSG( _T("invalid back reference") );
450 textNew
+= wxString(textstr
+ matchStart
+ start
,
451 *wxConvCurrent
, len
);
453 mayHaveBackrefs
= true;
456 else // ordinary character
464 if ( !GetMatch(&start
, &len
) )
466 // we did have match as Matches() returned true above!
467 wxFAIL_MSG( _T("internal logic error in wxRegEx::Replace") );
472 // an insurance against implementations that don't grow exponentially
473 // to ensure building the result takes linear time
474 if (result
.capacity() < result
.length() + start
+ textNew
.length())
475 result
.reserve(2 * result
.length());
477 #ifndef WXREGEX_CONVERT_TO_MB
478 result
.append(*text
, matchStart
, start
);
480 result
.append(wxString(textstr
+ matchStart
, *wxConvCurrent
, start
));
483 result
.append(textNew
);
490 #ifndef WXREGEX_CONVERT_TO_MB
491 result
.append(*text
, matchStart
, wxString::npos
);
493 result
.append(wxString(textstr
+ matchStart
, *wxConvCurrent
));
500 // ----------------------------------------------------------------------------
501 // wxRegEx: all methods are mostly forwarded to wxRegExImpl
502 // ----------------------------------------------------------------------------
514 bool wxRegEx::Compile(const wxString
& expr
, int flags
)
518 m_impl
= new wxRegExImpl
;
521 if ( !m_impl
->Compile(expr
, flags
) )
523 // error message already given in wxRegExImpl::Compile
533 bool wxRegEx::Matches(const wxChar
*str
, int flags
, size_t len
) const
535 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
538 #ifdef WXREGEX_CONVERT_TO_MB
539 return m_impl
->Matches(wxConvertWX2MB(str
), flags
);
541 return m_impl
->Matches(str
, flags
WXREGEX_BUILTIN_ONLY(len
));
545 bool wxRegEx::Matches(const wxChar
*str
, int flags
) const
547 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
549 #ifdef WXREGEX_CONVERT_TO_MB
550 return m_impl
->Matches(wxConvertWX2MB(str
), flags
);
552 return m_impl
->Matches(str
, flags
WXREGEX_BUILTIN_ONLY(wxStrlen(str
)));
556 bool wxRegEx::GetMatch(size_t *start
, size_t *len
, size_t index
) const
558 wxCHECK_MSG( IsValid(), false, _T("must successfully Compile() first") );
560 return m_impl
->GetMatch(start
, len
, index
);
563 wxString
wxRegEx::GetMatch(const wxString
& text
, size_t index
) const
566 if ( !GetMatch(&start
, &len
, index
) )
567 return wxEmptyString
;
569 return text
.Mid(start
, len
);
572 size_t wxRegEx::GetMatchCount() const
574 wxCHECK_MSG( IsValid(), 0, _T("must successfully Compile() first") );
576 return m_impl
->GetMatchCount();
579 int wxRegEx::Replace(wxString
*pattern
,
580 const wxString
& replacement
,
581 size_t maxMatches
) const
583 wxCHECK_MSG( IsValid(), wxNOT_FOUND
, _T("must successfully Compile() first") );
585 return m_impl
->Replace(pattern
, replacement
, maxMatches
);
588 #endif // wxUSE_REGEX