added wxRegEx::GetMatch
[wxWidgets.git] / src / common / regex.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/regex.cpp
3 // Purpose: regular expression matching
4 // Author: Karsten Ballüder and Vadim Zeitlin
5 // Modified by:
6 // Created: 13.07.01
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 Karsten Ballüder <ballueder@gmx.net>
9 // 2001 Vadim Zeitlin <vadim@wxwindows.org>
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 #ifdef __GNUG__
22 #pragma implementation "regex.h"
23 #endif
24
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 #if wxUSE_REGEX
33
34 #ifndef WX_PRECOMP
35 #include "wx/object.h"
36 #include "wx/string.h"
37 #include "wx/log.h"
38 #include "wx/intl.h"
39 #endif //WX_PRECOMP
40
41 #include <regex.h>
42
43 #include "wx/regex.h"
44
45 // ----------------------------------------------------------------------------
46 // private classes
47 // ----------------------------------------------------------------------------
48
49 // the real implementation of wxRegEx
50 class wxRegExImpl
51 {
52 public:
53 // ctor and dtor
54 wxRegExImpl();
55 ~wxRegExImpl();
56
57 // return TRUE if Compile() had been called successfully
58 bool IsValid() const { return m_isCompiled; }
59
60 // RE operations
61 bool Compile(const wxString& expr, int flags);
62 bool Matches(const wxString& str, int flags) const;
63 bool GetMatch(size_t *start, size_t *len, size_t index) const;
64 int Replace(wxString *pattern, const wxString& replacement) const;
65
66 private:
67 // return the string containing the error message for the given err code
68 wxString GetErrorMsg(int errorcode) const;
69
70 // free the RE if compiled
71 void Free()
72 {
73 if ( IsValid() )
74 {
75 regfree(&m_RegEx);
76
77 m_isCompiled = FALSE;
78 }
79 }
80
81 // compiled RE
82 regex_t m_RegEx;
83
84 // the subexpressions data
85 regmatch_t *m_Matches;
86 size_t m_nMatches;
87
88 // TRUE if m_RegEx is valid
89 bool m_isCompiled;
90 };
91
92 // ============================================================================
93 // implementation
94 // ============================================================================
95
96 // ----------------------------------------------------------------------------
97 // wxRegExImpl
98 // ----------------------------------------------------------------------------
99
100 wxRegExImpl::wxRegExImpl()
101 {
102 m_isCompiled = FALSE;
103 m_Matches = NULL;
104 }
105
106 wxRegExImpl::~wxRegExImpl()
107 {
108 Free();
109
110 delete [] m_Matches;
111 }
112
113 wxString wxRegExImpl::GetErrorMsg(int errorcode) const
114 {
115 wxString msg;
116
117 // first get the string length needed
118 int len = regerror(errorcode, &m_RegEx, NULL, 0);
119 if ( len > 0 )
120 {
121 len++;
122
123 (void)regerror(errorcode, &m_RegEx, msg.GetWriteBuf(len), len);
124
125 msg.UngetWriteBuf();
126 }
127 else // regerror() returned 0
128 {
129 msg = _("unknown error");
130 }
131
132 return msg;
133 }
134
135 bool wxRegExImpl::Compile(const wxString& expr, int flags)
136 {
137 Free();
138
139 // translate our flags to regcomp() ones
140 wxASSERT_MSG( !(flags &
141 ~(wxRE_BASIC | wxRE_ICASE | wxRE_NOSUB | wxRE_NEWLINE)),
142 _T("unrecognized flags in wxRegEx::Compile") );
143
144 int flagsRE = 0;
145 if ( !(flags & wxRE_BASIC) )
146 flagsRE |= REG_EXTENDED;
147 if ( flags & wxRE_ICASE )
148 flagsRE |= REG_ICASE;
149 if ( flags & wxRE_NOSUB )
150 flagsRE |= REG_NOSUB;
151 if ( flags & wxRE_NEWLINE )
152 flagsRE |= REG_NEWLINE;
153
154 // compile it
155 int errorcode = regcomp(&m_RegEx, expr, flagsRE);
156 if ( errorcode )
157 {
158 wxLogError(_("Invalid regular expression '%s': %s"),
159 expr.c_str(), GetErrorMsg(errorcode).c_str());
160
161 m_isCompiled = FALSE;
162 }
163 else // ok
164 {
165 // don't allocate the matches array now, but do it later if necessary
166 if ( flags & wxRE_NOSUB )
167 {
168 // we don't need it at all
169 m_nMatches = 0;
170 }
171 else
172 {
173 // will alloc later
174 m_nMatches = WX_REGEX_MAXMATCHES;
175 }
176
177 m_isCompiled = TRUE;
178 }
179
180 return IsValid();
181 }
182
183 bool wxRegExImpl::Matches(const wxString& str, int flags) const
184 {
185 wxCHECK_MSG( IsValid(), FALSE, _T("must successfully Compile() first") );
186
187 // translate our flags to regexec() ones
188 wxASSERT_MSG( !(flags & ~(wxRE_NOTBOL | wxRE_NOTEOL)),
189 _T("unrecognized flags in wxRegEx::Matches") );
190
191 int flagsRE = 0;
192 if ( flags & wxRE_NOTBOL )
193 flagsRE |= REG_NOTBOL;
194 if ( flags & wxRE_NOTEOL )
195 flagsRE |= REG_NOTEOL;
196
197 // allocate matches array if needed
198 wxRegExImpl *self = wxConstCast(this, wxRegExImpl);
199 if ( !m_Matches && m_nMatches )
200 {
201 self->m_Matches = new regmatch_t[m_nMatches];
202 }
203
204 // do match it
205 int rc = regexec(&self->m_RegEx, str, m_nMatches, m_Matches, flagsRE);
206
207 switch ( rc )
208 {
209 case 0:
210 // matched successfully
211 return TRUE;
212
213 default:
214 // an error occured
215 wxLogError(_("Failed to match '%s' in regular expression: %s"),
216 str.c_str(), GetErrorMsg(rc).c_str());
217 // fall through
218
219 case REG_NOMATCH:
220 // no match
221 return FALSE;
222 }
223 }
224
225 bool wxRegExImpl::GetMatch(size_t *start, size_t *len, size_t index) const
226 {
227 wxCHECK_MSG( IsValid(), FALSE, _T("must successfully Compile() first") );
228 wxCHECK_MSG( m_Matches, FALSE, _T("can't use with wxRE_NOSUB") );
229 wxCHECK_MSG( index < m_nMatches, FALSE, _T("invalid match index") );
230
231 const regmatch_t& match = m_Matches[index];
232 if ( match.rm_so == -1 )
233 return FALSE;
234
235 if ( start )
236 *start = match.rm_so;
237 if ( len )
238 *len = match.rm_eo - match.rm_so;
239
240 return TRUE;
241 }
242
243 int wxRegExImpl::Replace(wxString *pattern, const wxString& replacement) const
244 {
245 wxCHECK_MSG( pattern, -1, _T("NULL pattern in wxRegEx::Replace") );
246
247 wxCHECK_MSG( IsValid(), FALSE, _T("must successfully Compile() first") );
248
249 int replaced = 0;
250 size_t lastpos = 0;
251 wxString newstring;
252
253 for ( size_t idx = 0;
254 m_Matches[idx].rm_so != -1 && idx < m_nMatches;
255 idx++ )
256 {
257 // copy non-matching bits:
258 newstring << pattern->Mid(lastpos, m_Matches[idx].rm_so - lastpos);
259 // copy replacement:
260 newstring << replacement;
261 // remember how far we got:
262 lastpos = m_Matches[idx].rm_eo;
263 replaced ++;
264 }
265 if(replaced > 0)
266 *pattern = newstring;
267 return replaced;
268 }
269
270 // ----------------------------------------------------------------------------
271 // wxRegEx: all methods are mostly forwarded to wxRegExImpl
272 // ----------------------------------------------------------------------------
273
274 void wxRegEx::Init()
275 {
276 m_impl = NULL;
277 }
278
279
280 wxRegEx::~wxRegEx()
281 {
282 delete m_impl;
283 }
284
285 bool wxRegEx::Compile(const wxString& expr, int flags)
286 {
287 if ( !m_impl )
288 {
289 m_impl = new wxRegExImpl;
290 }
291
292 if ( !m_impl->Compile(expr, flags) )
293 {
294 // error message already given in wxRegExImpl::Compile
295 delete m_impl;
296 m_impl = NULL;
297
298 return FALSE;
299 }
300
301 return TRUE;
302 }
303
304 bool wxRegEx::Matches(const wxString& str, int flags) const
305 {
306 wxCHECK_MSG( IsValid(), FALSE, _T("must successfully Compile() first") );
307
308 return m_impl->Matches(str, flags);
309 }
310
311 bool wxRegEx::GetMatch(size_t *start, size_t *len, size_t index) const
312 {
313 wxCHECK_MSG( IsValid(), -1, _T("must successfully Compile() first") );
314
315 return m_impl->GetMatch(start, len, index);
316 }
317
318 wxString wxRegEx::GetMatch(const wxString& text, size_t index) const
319 {
320 size_t start, len;
321 if ( !GetMatch(&start, &len, index) )
322 return wxEmptyString;
323
324 return text.Mid(start, len);
325 }
326
327 int wxRegEx::Replace(wxString *pattern, const wxString& replacement) const
328 {
329 wxCHECK_MSG( IsValid(), -1, _T("must successfully Compile() first") );
330
331 return m_impl->Replace(pattern, replacement);
332 }
333
334 #endif // wxUSE_REGEX