2006-11-20 W. Andy Carrel <wac@google.com>
[WebKit-https.git] / JavaScriptCore / kjs / regexp.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #include "config.h"
23 #include "regexp.h"
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 namespace KJS {
31
32 RegExp::RegExp(const UString &p, int flags)
33   : _flags(flags), _numSubPatterns(0)
34 {
35 #if HAVE(PCREPOSIX)
36
37   int options = PCRE_UTF8;
38   // Note: the Global flag is already handled by RegExpProtoFunc::execute.
39   // FIXME: That last comment is dubious. Not all RegExps get run through RegExpProtoFunc::execute.
40   if (flags & IgnoreCase)
41     options |= PCRE_CASELESS;
42   if (flags & Multiline)
43     options |= PCRE_MULTILINE;
44
45   const char *errorMessage;
46   int errorOffset;
47   
48   UString pattern(p);
49   
50   pattern.append('\0');
51   _regex = pcre_compile(reinterpret_cast<const uint16_t*>(pattern.data()),
52                         options, &errorMessage, &errorOffset, NULL);
53   if (!_regex) {
54     // Try again, this time handle any \u we might find.
55     UString uPattern = sanitizePattern(pattern);
56     _regex = pcre_compile(reinterpret_cast<const uint16_t*>(uPattern.data()),
57                           options, &errorMessage, &errorOffset, NULL);
58     if (!_regex) 
59       return;
60   }
61
62 #ifdef PCRE_INFO_CAPTURECOUNT
63   // Get number of subpatterns that will be returned.
64   pcre_fullinfo(_regex, NULL, PCRE_INFO_CAPTURECOUNT, &_numSubPatterns);
65 #endif
66
67 #else /* HAVE(PCREPOSIX) */
68
69   int regflags = 0;
70 #ifdef REG_EXTENDED
71   regflags |= REG_EXTENDED;
72 #endif
73 #ifdef REG_ICASE
74   if ( f & IgnoreCase )
75     regflags |= REG_ICASE;
76 #endif
77
78   //NOTE: Multiline is not feasible with POSIX regex.
79   //if ( f & Multiline )
80   //    ;
81   // Note: the Global flag is already handled by RegExpProtoFunc::execute
82
83   regcomp(&_regex, p.ascii(), regflags);
84   /* TODO check for errors */
85
86 #endif
87 }
88
89 RegExp::~RegExp()
90 {
91 #if HAVE(PCREPOSIX)
92   pcre_free(_regex);
93 #else
94   /* TODO: is this really okay after an error ? */
95   regfree(&_regex);
96 #endif
97 }
98
99 UString RegExp::match(const UString &s, int i, int *pos, int **ovector)
100 {
101   if (i < 0)
102     i = 0;
103   int dummyPos;
104   if (!pos)
105     pos = &dummyPos;
106   *pos = -1;
107   if (ovector)
108     *ovector = 0;
109
110   if (i > s.size() || s.isNull())
111     return UString::null();
112
113 #if HAVE(PCREPOSIX)
114
115   if (!_regex)
116     return UString::null();
117
118   // Set up the offset vector for the result.
119   // First 2/3 used for result, the last third used by PCRE.
120   int *offsetVector;
121   int offsetVectorSize;
122   int fixedSizeOffsetVector[3];
123   if (!ovector) {
124     offsetVectorSize = 3;
125     offsetVector = fixedSizeOffsetVector;
126   } else {
127     offsetVectorSize = (_numSubPatterns + 1) * 3;
128     offsetVector = new int [offsetVectorSize];
129   }
130
131   const int numMatches = pcre_exec(_regex, NULL, reinterpret_cast<const uint16_t *>(s.data()), s.size(), i, 0, offsetVector, offsetVectorSize);
132
133   if (numMatches < 0) {
134 #ifndef NDEBUG
135     if (numMatches != PCRE_ERROR_NOMATCH)
136       fprintf(stderr, "KJS: pcre_exec() failed with result %d\n", numMatches);
137 #endif
138     if (offsetVector != fixedSizeOffsetVector)
139       delete [] offsetVector;
140     return UString::null();
141   }
142
143   *pos = offsetVector[0];
144   if (ovector)
145     *ovector = offsetVector;
146   return s.substr(offsetVector[0], offsetVector[1] - offsetVector[0]);
147
148 #else
149
150   const unsigned maxMatch = 10;
151   regmatch_t rmatch[maxMatch];
152
153   char *str = strdup(s.ascii()); // TODO: why ???
154   if (regexec(&_regex, str + i, maxMatch, rmatch, 0)) {
155     free(str);
156     return UString::null();
157   }
158   free(str);
159
160   if (!ovector) {
161     *pos = rmatch[0].rm_so + i;
162     return s.substr(rmatch[0].rm_so + i, rmatch[0].rm_eo - rmatch[0].rm_so);
163   }
164
165   // map rmatch array to ovector used in PCRE case
166   _numSubPatterns = 0;
167   for(unsigned j = 1; j < maxMatch && rmatch[j].rm_so >= 0 ; j++)
168       _numSubPatterns++;
169   int ovecsize = (_numSubPatterns+1)*3; // see above
170   *ovector = new int[ovecsize];
171   for (unsigned j = 0; j < _numSubPatterns + 1; j++) {
172     if (j>maxMatch)
173       break;
174     (*ovector)[2*j] = rmatch[j].rm_so + i;
175     (*ovector)[2*j+1] = rmatch[j].rm_eo + i;
176   }
177
178   *pos = (*ovector)[0];
179   return s.substr((*ovector)[0], (*ovector)[1] - (*ovector)[0]);
180
181 #endif
182 }
183
184 UString RegExp::sanitizePattern(const UString& p)
185 {
186   UString newPattern;
187   
188   int startPos = 0;
189   int pos = p.find("\\u", 0) + 2; // Skip the \u
190   
191   while (pos != 1) { // p.find failing is -1 + 2 = 1 
192     if (pos + 3 < p.size()) {
193       if (isHexDigit(p[pos]) && isHexDigit(p[pos + 1]) &&
194           isHexDigit(p[pos + 2]) && isHexDigit(p[pos + 3])) {
195         newPattern.append(p.substr(startPos, pos - startPos - 2));
196         UChar escapedUnicode(convertUnicode(p[pos], p[pos + 1], 
197                                             p[pos + 2], p[pos + 3]));
198         // \u encoded characters should be treated as if they were escaped,
199         // so add an escape for certain characters that need it.
200         switch (escapedUnicode.unicode()) {
201           case '|':
202           case '+':
203           case '*':
204           case '(':
205           case ')':
206           case '[':
207           case ']':
208           case '{':
209           case '}':
210           case '?':
211           case '\\':
212             newPattern.append('\\');
213         }
214         newPattern.append(escapedUnicode);
215
216         startPos = pos + 4;
217       }
218     }
219     pos = p.find("\\u", pos) + 2;
220   }
221   newPattern.append(p.substr(startPos, p.size() - startPos));
222
223   return newPattern;
224 }
225
226 bool RegExp::isHexDigit(UChar uc)
227 {
228   int c = uc.unicode();
229   return (c >= '0' && c <= '9' ||
230           c >= 'a' && c <= 'f' ||
231           c >= 'A' && c <= 'F');
232 }
233
234 unsigned char RegExp::convertHex(int c)
235 {
236   if (c >= '0' && c <= '9')
237     return static_cast<unsigned char>(c - '0');
238   if (c >= 'a' && c <= 'f')
239     return static_cast<unsigned char>(c - 'a' + 10);
240   return static_cast<unsigned char>(c - 'A' + 10);
241 }
242
243 unsigned char RegExp::convertHex(int c1, int c2)
244 {
245   return ((convertHex(c1) << 4) + convertHex(c2));
246 }
247
248 UChar RegExp::convertUnicode(UChar uc1, UChar uc2, UChar uc3, UChar uc4)
249 {
250   int c1 = uc1.unicode();
251   int c2 = uc2.unicode();
252   int c3 = uc3.unicode();
253   int c4 = uc4.unicode();
254   return UChar((convertHex(c1) << 4) + convertHex(c2),
255                (convertHex(c3) << 4) + convertHex(c4));
256 }
257
258 } // namespace KJS