fa27b2e25498a7b1053b33accd7be039ff1f2069
[platform/framework/native/appfw.git] / src / base / utility / FBaseUtil_RegularExpressionImpl.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /**
19  * @File        :       FBaseUtil_RegularExpressionImpl.cpp
20  * @brief       :   Implementation for _RegularExpressionImpl Class
21  */
22
23 //Includes
24 //#include "FBaseSysInternal.h"
25 #include <stdlib.h>
26 #include <new>
27 #include <unique_ptr.h>
28 #include <FBaseUtilRegularExpression.h>
29 #include <FBaseSysLog.h>
30 #include "FBaseUtil_RegularExpressionImpl.h"
31 #include "pcre.h"
32
33 #define REGEX_MAX_GROUP_COUNT       16
34
35 using namespace Tizen::Base;
36 using namespace Tizen::Base::Collection;
37
38 namespace Tizen { namespace Base { namespace Utility
39 {
40
41
42 static int allOptions =
43         (REGEX_CASELESS |
44          REGEX_MULTI_LINE |
45          REGEX_DOTALL |
46          REGEX_EXTENDED |
47          REGEX_DOLLAR_ENDONLY |
48          REGEX_UNGREEDY |
49          REGEX_UNICODE
50         );
51
52 static char emptyString[1] = {'\0'};
53
54 struct RegExInfo
55 {
56         void* pRe;           // pcre object
57         char* pPattern;          // regular expression pattern
58         unsigned long options;      // options used while compiling
59         int* pOffsets;           // array to store offsets while match
60         int offsetCount;        // entries in offsets array
61
62         // Valid in case of error
63         char* pError;            // Error string if compilation fails
64         int errOffset;          // Error offset in patter in case compilation fails
65 };
66
67 void
68 RegexFree(RegExInfo* pRegex)
69 {
70         if (pRegex)
71         {
72                 if (pRegex->pRe != null)
73                 {
74                         pcre_free(pRegex->pRe);
75                 }
76
77                 if (pRegex->pPattern != emptyString)
78                 {
79                         free(pRegex->pPattern);
80                 }
81
82                 if (pRegex->pError != emptyString)
83                 {
84                         free(pRegex->pError);
85                 }
86
87                 if (pRegex->pOffsets != null)
88                 {
89                         free(pRegex->pOffsets);
90                 }
91
92                 free(pRegex);
93         }
94 }
95
96 int
97 RegexGetGroupCountInPattern(const char* pattern, int len)
98 {
99         int grpCount = 0;
100         const char* pos = pattern + 1;
101         while ((--len) && (grpCount < REGEX_MAX_GROUP_COUNT))
102         {
103                 if (*pos == '(')
104                 {
105                         if (*(pos - 1) != '\\')
106                         {
107                                 grpCount++;
108                         }
109                 }
110                 pos++;
111         }
112
113         return grpCount;
114 //      return REGEX_MAX_GROUP_COUNT;
115 }
116 void
117 RegexAllocateOffset(RegExInfo* pRegex, int maxGroupCount)
118 {
119         if (pRegex)
120         {
121                 if (pRegex->pOffsets)
122                 {
123                         free(pRegex->pOffsets);
124                 }
125
126                 if (maxGroupCount < REGEX_MAX_GROUP_COUNT)
127                 {
128                         maxGroupCount = REGEX_MAX_GROUP_COUNT;
129                 }
130
131                 pRegex->offsetCount = (maxGroupCount + 1) * 3;
132                 pRegex->pOffsets = (int*) calloc(pRegex->offsetCount, sizeof(int));
133         }
134 }
135
136
137 _RegularExpressionImpl::_RegularExpressionImpl()
138         : __pReFull(null)
139         , __pRePartial(null)
140         , __options(0)
141         , __isCompilationRequired(true)
142         , __isLastMatchPartial(true)
143 {
144 }
145
146 _RegularExpressionImpl::~_RegularExpressionImpl()
147 {
148         Cleanup();
149 }
150
151 void
152 _RegularExpressionImpl::Cleanup(void)
153 {
154         if (__pReFull != null)
155         {
156                 RegexFree((RegExInfo*) __pReFull);
157                 __pReFull = null;
158         }
159
160         if (__pRePartial != null)
161         {
162                 RegexFree((RegExInfo*) __pRePartial);
163                 __pRePartial = null;
164         }
165 }
166
167 result
168 _RegularExpressionImpl::Construct(const String& pattern, unsigned long options)
169 {
170         if (options & (~allOptions))
171         {
172                 return E_INVALID_ARG;
173         }
174
175         result r = E_SUCCESS;
176         r = Init(pattern, options);
177         return r;
178 }
179
180
181 result
182 _RegularExpressionImpl::Init(const String& pattern, const int options)
183 {
184         result r = E_SUCCESS;
185         __pattern = pattern;
186         __options = options;
187         __pReFull = null;
188         __pRePartial = null;
189
190         __options |= PCRE_UTF8;
191
192         __pRePartial = Compile(__pattern, __options, false);
193         if (null != __pRePartial)
194         {
195                 __pReFull = Compile(__pattern, __options, true);
196         }
197
198         r = GetLastResult();
199         __isCompilationRequired = (r == E_SUCCESS) ? false : true;
200
201         return r;
202 }
203
204 void*
205 _RegularExpressionImpl::Compile(const String& pattern, int options, bool forFullMatch)
206 {
207         RegExInfo* pRegex = null;
208         int lenPattern = 0;
209         result r = E_SUCCESS;
210         String pat = pattern;
211         if (forFullMatch)
212         {
213                 r = pat.Insert("(?:", 0);
214                 SysTryReturn(NID_BASE_UTIL, !IsFailed(r), null, r, "[%s] : Failed to insert string", GetErrorMessage(r));
215                 pat.Append(")\\z");
216         }
217
218         std::unique_ptr< ByteBuffer > pTmpBuf(StringUtil::StringToUtf8N(pat));
219
220         if (pTmpBuf)
221         {
222                 int errorCode = 0;
223                 lenPattern = pTmpBuf->GetLimit() - 1;
224                 pRegex = (RegExInfo*) calloc(1, sizeof(RegExInfo));
225                 if(pRegex != null)
226                 {
227                         pRegex->pError = emptyString;
228                         pRegex->options = options;
229                         pTmpBuf->SetByte(lenPattern, '\0');
230                         pRegex->pRe = pcre_compile2((const char*) pTmpBuf->GetPointer(),             // Pattern to compile
231                                                                            options, // Compile options
232                                                                            &errorCode, // Error code return by PCRE
233                                                                            (const char**) &pRegex->pError, // Pointer to string containing error message
234                                                                            &pRegex->errOffset, // Index in pattern, where error occurs
235                                                                            null // Passing null for pointer to character table so it will use default table.
236                                                                            );
237                 }
238
239                 if (0 != errorCode)
240                 {
241                         result r = PcreCompileErrorToSystemError(errorCode);
242                         SetLastResult(r);
243                         free(pRegex);
244                         return null;
245                 }
246                 if(pRegex != null)
247                 {
248                         pRegex->pPattern = (char*) calloc(lenPattern, sizeof(char));
249                         if(pRegex->pPattern != null)
250                         {
251                                 strncpy(pRegex->pPattern, (const char*) pTmpBuf->GetPointer(), lenPattern);
252                         }
253                         else
254                         {
255                                 delete pRegex;
256                                 pRegex = null;
257                                 return null;
258                         }
259                 }
260
261                 // Allocating memory for offset array, to be used during match operation
262                 RegexAllocateOffset(pRegex, RegexGetGroupCountInPattern((const char*) pTmpBuf->GetPointer(), lenPattern));
263
264         }
265         else
266         {
267                 r = GetLastResult();
268         }
269
270         SetLastResult(r);
271         return (void*) pRegex;
272 }
273
274 bool
275 _RegularExpressionImpl::Match(const Tizen::Base::String& text, bool fullMatch, IList* pMatchedString)
276 {
277         int matchCount = 0;
278         ClearLastResult();
279
280         matchCount = Match(fullMatch, text, 0, 0);
281         if (pMatchedString && matchCount > 0)
282         {
283                 GetLastMatchedGroups(text, *pMatchedString);
284         }
285
286         SetLastResult(E_SUCCESS);
287         return(matchCount > 0);
288
289 }
290
291 int
292 _RegularExpressionImpl::Match(bool isFull, const String& text, int startPos, int matchOptions)
293 {
294         result r = E_SUCCESS;   
295         std::unique_ptr< ByteBuffer > pTmpBuf(null);
296
297         bool containsNonAsciiChars = false;
298
299         if (__isCompilationRequired || ((isFull) == false ? __pReFull : __pRePartial))
300         {
301                 if (__pRePartial && __pReFull)
302                 {
303                         Cleanup();
304                 }
305                 Init(__pattern, __options);
306         }
307
308         RegExInfo* pRegex = static_cast<RegExInfo*>((isFull) ? __pReFull : __pRePartial);
309         if ((pRegex == null))
310         {
311                 SetLastResult(E_INVALID_STATE);
312                 return -1;
313         }
314         if ((pRegex->pRe == null) || (pRegex->pOffsets == null))
315         {
316                 SetLastResult(E_INVALID_ARG);
317                 return -1;
318         }
319
320         if (isFull)
321         {
322                 matchOptions |= PCRE_ANCHORED;
323         }
324
325         if (text.IsEmpty())
326         {
327                 pTmpBuf.reset(new (std::nothrow) ByteBuffer);
328                 if (!pTmpBuf)
329                 {
330                         SetLastResult(E_OUT_OF_MEMORY);
331                         return -1;
332                 }
333                 r = pTmpBuf->Construct(1);
334                 if(IsFailed(r) == true)
335                 {
336                         SetLastResult(r);
337                         return -1;
338                 }
339                 r = pTmpBuf->SetByte('\0');
340                 if(IsFailed(r) == true)
341                 {
342                         SetLastResult(r);
343                         return -1;
344                 }
345
346         }
347         else
348         {
349                 pTmpBuf.reset(StringUtil::StringToUtf8N(text));
350                 if (!pTmpBuf)
351                 {
352                         return -1;
353                 }
354
355                 pTmpBuf->SetByte(pTmpBuf->GetLimit() - 1, '\0');
356
357                 if ((pTmpBuf->GetLimit() - 1) != text.GetLength())
358                 {
359                         containsNonAsciiChars = true;
360                         startPos = StringToUtf8Index(text, startPos);
361                 }
362         }
363
364         // Ressting offset array to invalid values
365         memset(pRegex->pOffsets, -1, pRegex->offsetCount * sizeof(int));
366         int matchCount = pcre_exec((pcre*) (pRegex->pRe), // the regular expression object
367                                                            null,    // &extra,
368                                                            (const char*) pTmpBuf->GetPointer(), // pointer to subject string
369                                                            pTmpBuf->GetLimit() - 1, // length of subject string
370                                                            startPos, // where to start in the subject string
371                                                            matchOptions, // option bits
372                                                            pRegex->pOffsets, // points to a vector of ints to be filled in with offsets
373                                                            pRegex->offsetCount // the number of elements in the vector
374                                                            );
375         if (matchCount == PCRE_ERROR_NOMATCH)
376         {
377                 return 0;
378         }
379
380         if (matchCount < 0)
381         {
382                 SetLastResult(PcreExecErrorToSystemError(matchCount));
383                 return -1;
384         }
385
386         // To handle cases where captured strings count is grater than offset count
387         if (matchCount == 0)
388         {
389                 matchCount = pRegex->offsetCount / 2;
390         }
391
392         if (containsNonAsciiChars)
393         {
394                 int offsetCnt = matchCount * 2;
395                 for (int i = 0; i < offsetCnt; i += 2)
396                 {
397                         pRegex->pOffsets[i] = Utf8ToStringIndex((char*) pTmpBuf->GetPointer(), pRegex->pOffsets[i]);
398                         pRegex->pOffsets[i + 1] = Utf8ToStringIndex((char*) pTmpBuf->GetPointer(), pRegex->pOffsets[i + 1]);
399                 }
400         }
401
402         SetLastResult(E_SUCCESS);
403
404         __isLastMatchPartial = !isFull;
405         return matchCount;
406 }
407
408 bool
409 _RegularExpressionImpl::Consume(Tizen::Base::String& text, Tizen::Base::Collection::IList* pMatchedString)
410 {
411         result r = E_SUCCESS;
412         int matchCount = 0;
413         ClearLastResult();
414
415         matchCount = Match(false, text, 0, PCRE_ANCHORED);
416         if (matchCount > 0)
417         {
418                 if (pMatchedString)
419                 {
420                         r = GetLastMatchedGroups(text, *pMatchedString);
421                         if (IsFailed(r))
422                         {
423                                 goto CATCH;
424                         }
425                 }
426
427                 r = text.Remove(0, GetLastMatchEnd());
428                 if (IsFailed(r))
429                 {
430                         goto CATCH;
431                 }
432
433                 SetLastResult(E_SUCCESS);
434                 return true;
435         }
436 CATCH:
437         SetLastResult(r);
438         return false;
439
440 }
441
442 bool
443 _RegularExpressionImpl::FindAndConsume(Tizen::Base::String& text, Tizen::Base::Collection::IList* pMatchedString)
444 {
445         result r = E_SUCCESS;
446         int matchCount = 0;
447         ClearLastResult();
448
449         matchCount = Match(false, text, 0, 0);
450         if (matchCount > 0)
451         {
452                 if (pMatchedString)
453                 {
454                         r = GetLastMatchedGroups(text, *pMatchedString);
455                         if (IsFailed(r))
456                         {
457                                 goto CATCH;
458                         }
459                 }
460
461                 r = text.Remove(0, GetLastMatchEnd());
462                 if (IsFailed(r))
463                 {
464                         goto CATCH;
465                 }
466         }
467
468         SetLastResult(r);
469         return true;
470
471 CATCH:
472         SetLastResult(r);
473         return false;
474 }
475
476 bool
477 _RegularExpressionImpl::Replace(Tizen::Base::String& text, const Tizen::Base::String& rewrite, bool globalReplace, int startPos)
478 {
479         result r = E_SUCCESS;
480         int count = 0;
481         String out;
482         int lenText = text.GetLength();
483         bool isMatchEmpty = false;
484
485         ClearLastResult();
486
487         if (startPos > 0)
488         {
489                 r = text.SubString(0, startPos, out);
490                 if (IsFailed(r))
491                 {
492                         goto CATCH;
493                 }
494         }
495
496         while (startPos <= text.GetLength())
497         {
498                 int matchCount = 0;
499                 int matchStart = 0;
500                 int matchEnd = 0;
501                 String out1;
502
503                 if (isMatchEmpty)
504                 {
505                         matchCount = Match(false, text, startPos, PCRE_ANCHORED | PCRE_NOTEMPTY);
506                         if ((matchCount <= 0))
507                         {
508                                 if (startPos < text.GetLength())
509                                 {
510                                         r = out.Append(text[startPos]);
511                                         if (IsFailed(r))
512                                         {
513                                                 goto CATCH;
514                                         }
515                                 }
516
517                                 startPos++;
518                                 isMatchEmpty = false;
519                                 continue;
520                         }
521                 }
522                 else
523                 {
524                         matchCount = Match(false, text, startPos, 0);
525                         if (matchCount <= 0)
526                         {
527                                 break;
528                         }
529                 }
530
531                 matchStart = GetLastMatchStart();
532                 matchEnd = GetLastMatchEnd();
533
534                 if ((matchStart < startPos) || (matchEnd < matchStart))
535                 {
536                         r = GetLastResult();
537                         goto CATCH;
538                 }
539
540                 if (matchStart > startPos)
541                 {
542                         r = text.SubString(startPos, matchStart - startPos, out1);
543                         if (IsFailed(r))
544                         {
545                                 goto CATCH;
546                         }
547                         out.Append(out1);
548                 }
549
550                 r = Rewrite(rewrite, text, out1);
551                 if (IsFailed(r))
552                 {
553                         goto CATCH;
554                 }
555                 out.Append(out1);
556
557                 startPos = matchEnd;
558                 count++;
559                 isMatchEmpty = (matchStart == matchEnd);
560
561                 if (!globalReplace)
562                 {
563                         break;
564                 }
565
566         }
567
568         if (count == 0)
569         {
570                 r = GetLastResult();
571                 goto CATCH;
572         }
573
574         if (startPos < lenText)
575         {
576                 String out1;
577                 r = text.SubString(startPos, lenText - startPos, out1);
578                 if (IsFailed(r))
579                 {
580                         goto CATCH;
581                 }
582                 out.Append(out1);
583         }
584
585         text = out;
586
587         SetLastResult(E_SUCCESS);
588         return true;
589
590 CATCH:
591         SetLastResult(r);
592         return false;
593 }
594
595 bool
596 _RegularExpressionImpl::Extract(const Tizen::Base::String& text, const Tizen::Base::String& rewrite, Tizen::Base::String& out)
597 {
598         result r = E_SUCCESS;
599         int matchCount = 0;
600
601         ClearLastResult();
602
603         matchCount = Match(false, text, 0, 0);
604         if (matchCount <= 0)
605         {
606                 goto CATCH;
607         }
608
609         r = Rewrite(rewrite, text, out);
610         if (IsFailed(r))
611         {
612                 goto CATCH;
613         }
614
615         return true;
616
617 CATCH:
618         SetLastResult(r);
619         return false;
620 }
621
622 Tizen::Base::String
623 _RegularExpressionImpl::GetPattern(void) const
624 {
625         return __pattern;
626 }
627
628 result
629 _RegularExpressionImpl::SetOptions(unsigned long options)
630 {
631         if (options & (~allOptions))
632         {
633                 return E_INVALID_ARG;
634         }
635
636         __options = options;
637
638         if (!(options && PCRE_NOTEMPTY))
639         {
640                 __isCompilationRequired = true;
641         }
642         return E_SUCCESS;
643 }
644
645 unsigned long
646 _RegularExpressionImpl::GetOptions(void) const
647 {
648         return __options;
649 }
650
651
652 result
653 _RegularExpressionImpl::GetLastMatchedGroups(const String& text, IList& matchedGrpStrList)
654 {
655         result r = E_SUCCESS;
656         RegExInfo* pRegex = static_cast<RegExInfo*>((__isLastMatchPartial) ? __pRePartial : __pReFull);
657         if (pRegex)
658         {
659                 int grpCount = GetLastGroupCount();
660                 if (grpCount < 0)
661                 {
662                         return E_INVALID_STATE;
663                 }
664
665                 grpCount = (grpCount + 1) * 2;
666                 for (int i = 0; i < grpCount; i += 2)
667                 {
668                         String* pTmpStr = new (std::nothrow) String();
669                         if (pRegex->pOffsets[i] >= 0 && (pRegex->pOffsets[i] < pRegex->pOffsets[i + 1]))
670                         {
671                                 r = text.SubString(pRegex->pOffsets[i], pRegex->pOffsets[i + 1] - pRegex->pOffsets[i], *pTmpStr);
672                                 if (IsFailed(r))
673                                 {
674                                         delete pTmpStr;
675                                         pTmpStr = null;
676                                         goto FAILED;
677                                 }
678                         }
679
680                         r = matchedGrpStrList.Add(*pTmpStr);
681                         if (IsFailed(r))
682                         {
683                                 goto FAILED;
684                         }
685                 }
686                 return E_SUCCESS;
687         }
688
689 FAILED:
690         matchedGrpStrList.RemoveAll(true);
691         return r;
692 }
693
694 int
695 _RegularExpressionImpl::GetLastGroupCount(void)
696 {
697         int grpCount = 0;
698         RegExInfo* pRegex = static_cast<RegExInfo*>((__isLastMatchPartial) ? __pRePartial : __pReFull);
699         if (pRegex == null)
700         {
701
702                 SetLastResult(E_INVALID_STATE);
703                 return -1;
704         }
705         if (pRegex->pRe == null)
706         {
707                 SetLastResult(E_INVALID_ARG);
708                 return -1;
709         }
710
711         int retValue = pcre_fullinfo((pcre*) (pRegex->pRe),  // the regular expression object
712                                                                  null,  // points extra data, or null
713                                                                  PCRE_INFO_CAPTURECOUNT, // what information is required
714                                                                  (void*) &grpCount // where to put the information
715                                                                  );
716
717 // Handling error
718         if ((0 != retValue) || (grpCount < -1))
719         {
720                 return -1;
721         }
722
723         return grpCount;
724 }
725 int
726 _RegularExpressionImpl::StringToUtf8Index(const Tizen::Base::String text, int index)
727 {
728         int newIndex = -1;
729         String tmpStr;
730
731         if (index == 0)
732         {
733                 return 0;
734         }
735         result r = text.SubString(0, index, tmpStr);
736         if (!IsFailed(r))
737         {
738                 std::unique_ptr< ByteBuffer > pTmpBuf(StringUtil::StringToUtf8N(tmpStr));
739                 if (pTmpBuf)
740                 {
741                         newIndex = pTmpBuf->GetLimit() - 1;
742                 }
743         }
744
745 //      SysLogExceptionxception("[%s] Regex: StringToUtf8Index failed", GetErrorMessage(r));
746         return newIndex;
747 }
748 int
749 _RegularExpressionImpl::Utf8ToStringIndex(char* pUtf8String, int index)
750 {
751         if (index == 0)
752         {
753                 return 0;
754         }
755
756         int newIndex = -1;
757         String tmpStr;
758         if (pUtf8String)
759         {
760                 int byteAtIndex = pUtf8String[index];
761                 pUtf8String[index] = '\0';
762
763                 result r = StringUtil::Utf8ToString(pUtf8String, tmpStr);
764                 if (!IsFailed(r))
765                 {
766                         newIndex = tmpStr.GetLength();
767                 }
768                 else
769                 {
770 //                      SysLogExceptionxception("[%s] Regex: Utf8ToStringIndex failed", GetErrorMessage(r));
771                 }
772
773                 pUtf8String[index] = byteAtIndex;
774         }
775
776         return newIndex;
777 }
778
779 int
780 _RegularExpressionImpl::CheckRewritePattern(const Tizen::Base::String& rewrite)
781 {
782         byte c = '\0';
783         result r = E_SUCCESS;
784         int maxGrpNum = -1;
785         std::unique_ptr< ByteBuffer > pTmpBuf(StringUtil::StringToUtf8N(rewrite));
786         if (pTmpBuf)
787         {
788                 int limit = pTmpBuf->GetLimit();
789                 while (limit > pTmpBuf->GetPosition())
790                 {
791                         if (IsFailed(pTmpBuf->GetByte(c)))
792                         {
793                                 break;
794                         }
795
796                         if (c == '\\')
797                         {
798                                 if (IsFailed(pTmpBuf->GetByte(c)))
799                                 {
800                                         r = E_INVALID_ARG;
801                                         break;
802                                 }
803
804                                 if ((c >= '0') && (c <= '9'))
805                                 {
806                                         int n = (c - '0');
807                                         if (n > maxGrpNum)
808                                         {
809                                                 maxGrpNum = n;
810                                         }
811                                 }
812                                 else if (c != '\\')
813                                 {
814                                         r = E_INVALID_ARG;
815                                         break;
816                                 }
817                         }
818                 }
819         }
820
821         SetLastResult(r);
822         return maxGrpNum;
823 }
824
825 result
826 _RegularExpressionImpl::Rewrite(const String& rewrite, const String& text, String& out)
827 {
828         result r = E_UNKNOWN;
829         int index = 0;
830         int maxCount = 0;
831         ArrayList matchedGrpStrList;
832
833         maxCount = CheckRewritePattern(rewrite);
834         r = GetLastResult();
835         if (IsFailed(r))
836         {
837                 return r;
838         }
839
840         if (maxCount == -1)
841         {
842                 out = rewrite;
843                 return E_SUCCESS;
844         }
845
846         if (maxCount == 0)
847         {
848                 String matchedStr = GetLastMatchedString(text);
849                 out = rewrite;
850                 return out.Replace("\\0", matchedStr);
851         }
852
853         String out1(rewrite);
854         r = matchedGrpStrList.Construct(REGEX_MAX_GROUP_COUNT);
855         if (IsFailed(r))
856         {
857                 goto FAILED;
858         }
859
860         r = GetLastMatchedGroups(text, matchedGrpStrList);
861         if (maxCount >= matchedGrpStrList.GetCount())
862         {
863                 goto FAILED;
864         }
865
866         if (!IsFailed(r))
867         {
868                 for (index = 0; index <= maxCount; index++)
869                 {
870                         String* pTemp = static_cast<String*>(matchedGrpStrList.GetAt(index));
871                         if (pTemp)
872                         {
873                                 String tmp1("\\");
874                                 r = tmp1.Append(index);
875                                 if (!IsFailed(r))
876                                 {
877                                         r = out1.Replace(tmp1, *pTemp);
878                                 }
879
880                                 if (IsFailed(r))
881                                 {
882                                         goto FAILED;
883                                 }
884                         }
885                 }
886         }
887
888         if (!IsFailed(r))
889         {
890                 out = out1;
891         }
892
893 FAILED:
894         matchedGrpStrList.RemoveAll(true);
895         return r;
896 }
897
898
899 int
900 _RegularExpressionImpl::GetLastMatchStart(void)
901 {
902         RegExInfo* pRegEx = static_cast<RegExInfo*>((__isLastMatchPartial) ? __pRePartial : __pReFull);
903         if (pRegEx)
904         {
905                 return pRegEx->pOffsets[0];
906         }
907
908         return -1;
909 }
910
911 int
912 _RegularExpressionImpl::GetLastMatchEnd(void)
913 {
914         RegExInfo* pRegEx = static_cast<RegExInfo*>((__isLastMatchPartial) ? __pRePartial : __pReFull);
915         if (pRegEx)
916         {
917                 return pRegEx->pOffsets[1];
918         }
919
920         return -1;
921 }
922
923 String
924 _RegularExpressionImpl::GetLastMatchedString(const String& text)
925 {
926         String out("");
927         result r = E_SUCCESS;
928         RegExInfo* pRegEx = static_cast<RegExInfo*>((__isLastMatchPartial) ? __pRePartial : __pReFull);
929         if (pRegEx && pRegEx->pOffsets[0] > -1 && pRegEx->pOffsets[1] > -1)
930         {
931                 r = text.SubString(pRegEx->pOffsets[0], pRegEx->pOffsets[1] - pRegEx->pOffsets[0], out);
932
933         }
934
935         SetLastResult(r);
936         return out;
937 }
938 result
939 _RegularExpressionImpl::PcreCompileErrorToSystemError(int compileErr)
940 {
941         switch (compileErr)
942         {
943         case 23:
944                 //fall through
945         case 52:
946                 return E_SYSTEM;
947
948         case 21:
949                 return E_OUT_OF_MEMORY;
950
951         default:
952                 return E_INVALID_ARG;
953         }
954 }
955
956 result
957 _RegularExpressionImpl::PcreExecErrorToSystemError(int err)
958 {
959         switch (err)
960         {
961         case PCRE_ERROR_NOMATCH:
962                 return E_SUCCESS;
963
964 //              case PCRE_ERROR_NULL:
965         case PCRE_ERROR_NOSUBSTRING:
966         //fall through
967         case PCRE_ERROR_MATCHLIMIT:
968         //fall through
969         case PCRE_ERROR_CALLOUT:
970         //fall through
971         case PCRE_ERROR_PARTIAL:
972         //fall through
973         case PCRE_ERROR_BADPARTIAL:
974         //fall through
975         case PCRE_ERROR_INTERNAL:
976         //fall through
977         case PCRE_ERROR_BADCOUNT:
978         //fall through
979         case PCRE_ERROR_DFA_UITEM:
980         //fall through
981         case PCRE_ERROR_DFA_UCOND:
982         //fall through
983         case PCRE_ERROR_DFA_UMLIMIT:
984         //fall through
985         case PCRE_ERROR_DFA_WSSIZE:
986         //fall through
987         case PCRE_ERROR_DFA_RECURSE:
988         //fall through
989         case PCRE_ERROR_RECURSIONLIMIT:
990         //fall through
991         case PCRE_ERROR_NULLWSLIMIT:
992                 return E_SYSTEM;
993
994         case PCRE_ERROR_NULL:
995         //fall through
996         case PCRE_ERROR_BADOPTION:
997         //fall through
998         case PCRE_ERROR_BADMAGIC:
999         //fall through
1000         case PCRE_ERROR_UNKNOWN_OPCODE:
1001         //fall through
1002 //              case PCRE_ERROR_UNKNOWN_NODE:
1003         case PCRE_ERROR_BADUTF8:
1004         //fall through
1005         case PCRE_ERROR_BADUTF8_OFFSET:
1006         //fall through
1007         case PCRE_ERROR_BADNEWLINE:
1008 //              case PCRE_ERROR_BADOFFSET:
1009 //              case PCRE_ERROR_SHORTUTF8:
1010                 return E_INVALID_ARG;
1011
1012         case PCRE_ERROR_NOMEMORY:
1013                 return E_OUT_OF_MEMORY;
1014         }
1015         return E_SUCCESS;
1016 }
1017 }}}