PageRenderTime 165ms CodeModel.GetById 12ms app.highlight 146ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/update-engine/externals/google-toolbox-for-mac/Foundation/GTMRegex.m

http://macfuse.googlecode.com/
Objective C | 798 lines | 542 code | 115 blank | 141 comment | 94 complexity | 1ea4b1d233a2093e5119da45c0f04d44 MD5 | raw file
  1//
  2//  GTMRegex.m
  3//
  4//  Copyright 2007-2008 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#define GTMREGEX_DEFINE_GLOBALS 1
 20#import "GTMRegex.h"
 21#import "GTMDefines.h"
 22
 23// This is the pattern to use for walking replacement text when doing
 24// substitutions.
 25//
 26// This pattern may look over-escaped, but remember the compiler will consume
 27// one layer of slashes, and then we have to escape the slashes for them to be
 28// seen as we want in the pattern.
 29static NSString *const kReplacementPattern =
 30  @"((^|[^\\\\])(\\\\\\\\)*)(\\\\([0-9]+))";
 31#define kReplacementPatternLeadingTextIndex       1
 32#define kReplacementPatternSubpatternNumberIndex  5
 33
 34@interface GTMRegex (PrivateMethods)
 35- (NSString *)errorMessage:(int)errCode;
 36- (BOOL)runRegexOnUTF8:(const char*)utf8Str
 37                nmatch:(size_t)nmatch
 38                pmatch:(regmatch_t *)pmatch
 39                 flags:(int)flags;
 40@end
 41
 42// private enumerator as impl detail
 43@interface GTMRegexEnumerator : NSEnumerator {
 44 @private
 45  GTMRegex *regex_;
 46  NSData *utf8StrBuf_;
 47  BOOL allSegments_;
 48  BOOL treatStartOfNewSegmentAsBeginningOfString_;
 49  regoff_t curParseIndex_;
 50  __strong regmatch_t *savedRegMatches_;
 51}
 52- (id)initWithRegex:(GTMRegex *)regex
 53      processString:(NSString *)str
 54        allSegments:(BOOL)allSegments;
 55- (void)treatStartOfNewSegmentAsBeginningOfString:(BOOL)yesNo;
 56@end
 57
 58@interface GTMRegexStringSegment (PrivateMethods)
 59- (id)initWithUTF8StrBuf:(NSData *)utf8StrBuf
 60              regMatches:(regmatch_t *)regMatches
 61           numRegMatches:(NSUInteger)numRegMatches
 62                 isMatch:(BOOL)isMatch;
 63@end
 64
 65@implementation GTMRegex
 66
 67+ (id)regexWithPattern:(NSString *)pattern {
 68  return [[[self alloc] initWithPattern:pattern] autorelease];
 69}
 70
 71+ (id)regexWithPattern:(NSString *)pattern options:(GTMRegexOptions)options {
 72  return [[[self alloc] initWithPattern:pattern
 73                                options:options] autorelease];
 74}
 75
 76+ (id)regexWithPattern:(NSString *)pattern
 77               options:(GTMRegexOptions)options
 78             withError:(NSError **)outErrorOrNULL {
 79  return [[[self alloc] initWithPattern:pattern
 80                                options:options
 81                              withError:outErrorOrNULL] autorelease];
 82}
 83
 84+ (NSString *)escapedPatternForString:(NSString *)str {
 85  if (str == nil)
 86    return nil;
 87
 88  // NOTE: this could be done more efficiently by fetching the whole string into
 89  // a unichar buffer and scanning that, along w/ pushing the data over in
 90  // chunks (when possible).
 91
 92  NSUInteger len = [str length];
 93  NSMutableString *result = [NSMutableString stringWithCapacity:len];
 94
 95  for (NSUInteger x = 0; x < len; ++x) {
 96    unichar ch = [str characterAtIndex:x];
 97    switch (ch) {
 98      case '^':
 99      case '.':
100      case '[':
101      case '$':
102      case '(':
103      case ')':
104      case '|':
105      case '*':
106      case '+':
107      case '?':
108      case '{':
109      case '\\':
110        [result appendFormat:@"\\%C", ch];
111        break;
112      default:
113        [result appendFormat:@"%C", ch];
114        break;
115    }
116  }
117
118  return result;
119}
120
121- (id)init {
122  return [self initWithPattern:nil];
123}
124
125- (id)initWithPattern:(NSString *)pattern {
126  return [self initWithPattern:pattern options:0];
127}
128
129- (id)initWithPattern:(NSString *)pattern options:(GTMRegexOptions)options {
130  return [self initWithPattern:pattern options:options withError:nil];
131}
132
133- (id)initWithPattern:(NSString *)pattern
134              options:(GTMRegexOptions)options
135            withError:(NSError **)outErrorOrNULL {
136  
137  self = [super init];
138  if (!self) return nil;
139
140  if (outErrorOrNULL) *outErrorOrNULL = nil;
141
142  if ([pattern length] == 0) {
143    [self release];
144    return nil;
145  }
146
147  // figure out the flags
148  options_ = options;
149  int flags = REG_EXTENDED;
150  if (options_ & kGTMRegexOptionIgnoreCase)
151    flags |= REG_ICASE;
152  if ((options_ & kGTMRegexOptionSupressNewlineSupport) == 0)
153    flags |= REG_NEWLINE;
154
155  // even if regcomp failes we need a flags that we did call regcomp so we'll
156  // call regfree (because the structure can get filled in some to allow better
157  // error info).  we use pattern_ as this flag.
158  pattern_ = [pattern copy];
159  if (!pattern_) {
160     // COV_NF_START - no real way to force this in a unittest
161    [self release];
162    return nil;
163    // COV_NF_END
164  }
165
166  // compile it
167  int compResult = regcomp(&regexData_, [pattern_ UTF8String], flags);
168  if (compResult != 0) {
169    NSString *errorStr = [self errorMessage:compResult];
170    if (outErrorOrNULL) {
171      // include the pattern and patternError message in the userInfo.
172      NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
173                                pattern_, kGTMRegexPatternErrorPattern,
174                                errorStr, kGTMRegexPatternErrorErrorString,
175                                nil];
176      *outErrorOrNULL = [NSError errorWithDomain:kGTMRegexErrorDomain
177                                            code:kGTMRegexPatternParseFailedError
178                                        userInfo:userInfo];
179    } else {
180      // if caller didn't get us an NSError to fill in, we log the error to help
181      // debugging.
182      _GTMDevLog(@"Invalid pattern \"%@\", error: \"%@\"",
183                 pattern_, errorStr);
184    }
185
186    [self release];
187    return nil;
188  }
189
190  return self;
191}
192
193- (void)dealloc {
194  // we used pattern_ as our flag that we initialized the regex_t
195  if (pattern_) {
196    regfree(&regexData_);
197    [pattern_ release];
198    // play it safe and clear it since we use it as a flag for regexData_
199    pattern_ = nil;
200  }
201  [super dealloc];
202}
203
204- (NSUInteger)subPatternCount {
205  return regexData_.re_nsub;
206}
207
208- (BOOL)matchesString:(NSString *)str {
209  regmatch_t regMatch;
210  if (![self runRegexOnUTF8:[str UTF8String]
211                     nmatch:1
212                     pmatch:&regMatch
213                      flags:0]) {
214    // no match
215    return NO;
216  }
217
218  // make sure the match is the full string
219  return (regMatch.rm_so == 0) &&
220    (regMatch.rm_eo == (regoff_t)[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
221}
222
223- (NSArray *)subPatternsOfString:(NSString *)str {
224  NSArray *result = nil;
225
226  NSUInteger count = regexData_.re_nsub + 1;
227  regmatch_t *regMatches = malloc(sizeof(regmatch_t) * count);
228  if (!regMatches)
229    return nil; // COV_NF_LINE - no real way to force this in a unittest
230
231  // wrap it all in a try so we don't leak the malloc
232  @try {
233    const char *utf8Str = [str UTF8String];
234    if (![self runRegexOnUTF8:utf8Str
235                       nmatch:count
236                       pmatch:regMatches
237                        flags:0]) {
238      // no match
239      return nil;
240    }
241
242    // make sure the match is the full string
243    if ((regMatches[0].rm_so != 0) ||
244        (regMatches[0].rm_eo != (regoff_t)[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding])) {
245      // only matched a sub part of the string
246      return nil;
247    }
248
249    NSMutableArray *buildResult = [NSMutableArray arrayWithCapacity:count];
250
251    for (NSUInteger x = 0 ; x < count ; ++x) {
252      if ((regMatches[x].rm_so == -1) && (regMatches[x].rm_eo == -1)) {
253        // add NSNull since it wasn't used
254        [buildResult addObject:[NSNull null]];
255      } else {
256        // fetch the string
257        const char *base = utf8Str + regMatches[x].rm_so;
258        regoff_t len = regMatches[x].rm_eo - regMatches[x].rm_so;
259        NSString *sub =
260          [[[NSString alloc] initWithBytes:base
261                                    length:(NSUInteger)len
262                                  encoding:NSUTF8StringEncoding] autorelease];
263        [buildResult addObject:sub];
264      }
265    }
266
267    result = buildResult;
268  } // COV_NF_LINE - radar 5851992 only reachable w/ an uncaught exception which isn't testable
269  @finally {
270    free(regMatches);
271  }
272
273  return result;
274}
275
276- (NSString *)firstSubStringMatchedInString:(NSString *)str {
277  NSString *result = nil;
278  
279  regmatch_t regMatch;
280  const char *utf8Str = [str UTF8String];
281  if ([self runRegexOnUTF8:utf8Str
282                    nmatch:1
283                    pmatch:&regMatch
284                     flags:0]) {
285    // fetch the string
286    const char *base = utf8Str + regMatch.rm_so;
287    regoff_t len = regMatch.rm_eo - regMatch.rm_so;
288    result =
289      [[[NSString alloc] initWithBytes:base
290                                length:(NSUInteger)len
291                              encoding:NSUTF8StringEncoding] autorelease];
292  }
293  return result;
294}
295
296- (BOOL)matchesSubStringInString:(NSString *)str {
297  regmatch_t regMatch;
298  if ([self runRegexOnUTF8:[str UTF8String]
299                    nmatch:1
300                    pmatch:&regMatch
301                     flags:0]) {
302    // don't really care what matched, just report the match
303    return YES;
304  }
305  return NO;
306}
307
308- (NSEnumerator *)segmentEnumeratorForString:(NSString *)str {
309  return [[[GTMRegexEnumerator alloc] initWithRegex:self
310                                      processString:str
311                                        allSegments:YES] autorelease];
312}
313
314- (NSEnumerator *)matchSegmentEnumeratorForString:(NSString *)str {
315  return [[[GTMRegexEnumerator alloc] initWithRegex:self
316                                      processString:str
317                                        allSegments:NO] autorelease];
318}
319
320- (NSString *)stringByReplacingMatchesInString:(NSString *)str
321                               withReplacement:(NSString *)replacementPattern {
322  if (!str)
323    return nil;
324
325  // if we have a replacement, we go ahead and crack it now.  if the replacement
326  // is just an empty string (or nil), just use the nil marker.
327  NSArray *replacements = nil;
328  if ([replacementPattern length]) {
329    // don't need newline support, just match the start of the pattern for '^'
330    GTMRegex *replacementRegex =
331      [GTMRegex regexWithPattern:kReplacementPattern
332                         options:kGTMRegexOptionSupressNewlineSupport];
333#ifdef DEBUG
334    if (!replacementRegex) {
335      _GTMDevLog(@"failed to parse out replacement regex!!!"); // COV_NF_LINE
336    }
337#endif
338    GTMRegexEnumerator *relacementEnumerator =
339      [[[GTMRegexEnumerator alloc] initWithRegex:replacementRegex
340                                        processString:replacementPattern
341                                          allSegments:YES] autorelease];
342    // We turn on treatStartOfNewSegmentAsBeginningOfLine for this enumerator.
343    // As complex as kReplacementPattern is, it can't completely do what we want
344    // with the normal string walk.  The problem is this, backreferences are a
345    // slash follow by a number ("\0"), but the replacement pattern might
346    // actually need to use backslashes (they have to be escaped).  So if a
347    // replacement were "\\0", then there is no backreference, instead the
348    // replacement is a backslash and a zero.  Generically this means an even
349    // number of backslashes are all escapes, and an odd are some number of
350    // literal backslashes followed by our backreference.  Think of it as a "an
351    // odd number of slashes that comes after a non-backslash character."  There
352    // is no way to rexpress this in re_format(7) extended expressions.  Instead
353    // we look for a non-blackslash or string start followed by an optional even
354    // number of slashes followed by the backreference; and use the special
355    // flag; so after each match, we restart claiming it's the start of the
356    // string.  (the problem match w/o this flag is a substition of "\2\1")
357    [relacementEnumerator treatStartOfNewSegmentAsBeginningOfString:YES];
358    // pull them all into an array so we can walk this as many times as needed.
359    replacements = [relacementEnumerator allObjects];
360    if (!replacements) {
361      // COV_NF_START - no real way to force this in a unittest
362      _GTMDevLog(@"failed to create the replacements for substitutions");
363      return nil;
364      // COV_NF_END
365    }
366  }
367
368  NSMutableString *result = [NSMutableString stringWithCapacity:[str length]];
369
370  NSEnumerator *enumerator = [self segmentEnumeratorForString:str];
371  GTMRegexStringSegment *segment = nil;
372  while ((segment = [enumerator nextObject]) != nil) {
373    if (![segment isMatch]) {
374      // not a match, just move this chunk over
375      [result appendString:[segment string]];
376    } else {
377      // match...
378      if (!replacements) {
379        // no replacements, they want to eat matches, nothing to do
380      } else {
381        // spin over the split up replacement
382        GTMRegexStringSegment *replacementSegment = nil;
383        GTM_FOREACH_OBJECT(replacementSegment, replacements) {
384          if (![replacementSegment isMatch]) {
385            // not a match, raw text to put in
386            [result appendString:[replacementSegment string]];
387          } else {
388            // match...
389
390            // first goes any leading text
391            NSString *leading =
392              [replacementSegment subPatternString:kReplacementPatternLeadingTextIndex];
393            if (leading)
394              [result appendString:leading];
395            // then use the subpattern number to find what goes in from the
396            // original string match.
397            int subPatternNum =
398              [[replacementSegment subPatternString:kReplacementPatternSubpatternNumberIndex] intValue];
399            NSString *matchSubPatStr = [segment subPatternString:subPatternNum];
400            // handle an unused subpattern (ie-nil result)
401            if (matchSubPatStr)
402              [result appendString:matchSubPatStr];
403          }
404        }
405      }
406    }
407  }
408  return result;
409}
410
411- (NSString *)description {
412  NSMutableString *result =
413    [NSMutableString stringWithFormat:@"%@<%p> { pattern=\"%@\", rawNumSubPatterns=%zd, options=(",
414      [self class], self, pattern_, regexData_.re_nsub];
415  if (options_) {
416    if (options_ & kGTMRegexOptionIgnoreCase)
417      [result appendString:@" IgnoreCase"];
418    if ((options_ & kGTMRegexOptionSupressNewlineSupport) == kGTMRegexOptionSupressNewlineSupport)
419      [result appendString:@" NoNewlineSupport"];
420  } else {
421    [result appendString:@" None(Default)"];
422  }
423  [result appendString:@" ) }"];
424  return result;
425}
426
427@end
428
429@implementation GTMRegex (PrivateMethods)
430
431- (NSString *)errorMessage:(int)errCode {
432  NSString *result = @"internal error";
433
434  // size the buffer we need
435  size_t len = regerror(errCode, &regexData_, NULL, 0);
436  char *buffer = (char*)malloc(sizeof(char) * len);
437  if (buffer) {
438    // fetch the error
439    if (len == regerror(errCode, &regexData_, buffer, len)) {
440      NSString *generatedError = [NSString stringWithUTF8String:buffer];
441      if (generatedError)
442        result = generatedError;
443    }
444    free(buffer);
445  }
446  return result;
447}
448
449// private helper to run the regex on a block
450- (BOOL)runRegexOnUTF8:(const char*)utf8Str
451                nmatch:(size_t)nmatch
452                pmatch:(regmatch_t *)pmatch
453                 flags:(int)flags {
454  if (!utf8Str)
455    return NO;
456
457  int execResult = regexec(&regexData_, utf8Str, nmatch, pmatch, flags);
458  if (execResult != 0) {
459#ifdef DEBUG
460    if (execResult != REG_NOMATCH) {
461      // COV_NF_START - no real way to force this in a unittest
462      NSString *errorStr = [self errorMessage:execResult];
463      _GTMDevLog(@"%@: matching string \"%.20s...\", had error: \"%@\"",
464                 self, utf8Str, errorStr);
465      // COV_NF_END
466    }
467#endif
468    return NO;
469  }
470  return YES;
471}
472
473@end
474
475@implementation GTMRegexEnumerator
476
477// we don't block init because the class isn't exported, so no one can
478// create one, or if they do, they get whatever happens...
479
480- (id)initWithRegex:(GTMRegex *)regex
481      processString:(NSString *)str
482        allSegments:(BOOL)allSegments {
483  self = [super init];
484  if (!self) return nil;
485
486  // collect args
487  regex_ = [regex retain];
488  utf8StrBuf_ = [[str dataUsingEncoding:NSUTF8StringEncoding] retain];
489  allSegments_ = allSegments;
490
491  // arg check
492  if (!regex_ || !utf8StrBuf_) {
493    [self release];
494    return nil;
495  }
496
497  // parsing state initialized to zero for us by object creation
498
499  return self;
500}
501
502// Don't need a finalize because savedRegMatches_ is marked __strong
503- (void)dealloc {
504  free(savedRegMatches_);
505  [regex_ release];
506  [utf8StrBuf_ release];
507  [super dealloc];
508}
509
510- (void)treatStartOfNewSegmentAsBeginningOfString:(BOOL)yesNo {
511  // The way regexec works, it assumes the first char it's looking at to the
512  // start of the string.  In normal use, this makes sense; but in this case,
513  // we're going to walk the entry string splitting it up by our pattern.  That
514  // means for the first call, it is the string start, but for all future calls,
515  // it is NOT the string start, so we will pass regexec the flag to let it
516  // know.  However, (you knew that was coming), there are some cases where you
517  // actually want the each pass to be considered as the start of the string
518  // (usually the cases are where a pattern can't express what's needed w/o
519  // this).  There is no really good way to explain this behavior w/o all this
520  // text and lot of examples, so for now this is not in the public api, and
521  // just here. (Hint: see what w/in this file uses this for why we have it)
522  treatStartOfNewSegmentAsBeginningOfString_ = yesNo;
523}
524
525- (id)nextObject {
526
527  GTMRegexStringSegment *result = nil;
528  regmatch_t *nextMatches = nil;
529  BOOL isMatch = NO;
530
531  // we do all this w/in a try, so if something throws, the memory we malloced
532  // will still get cleaned up
533  @try {
534
535    // if we have a saved match, use that...
536    if (savedRegMatches_) {
537      nextMatches = savedRegMatches_;
538      savedRegMatches_ = nil;
539      isMatch = YES; // if we have something saved, it was a pattern match
540    }
541    // have we reached the end?
542    else if (curParseIndex_ >= (regoff_t)[utf8StrBuf_ length]) {
543      // done, do nothing, we'll return nil
544    }
545    // do the search.
546    else {
547
548      // alloc the match structure (extra space for the zero (full) match)
549      size_t matchBufSize = ([regex_ subPatternCount] + 1) * sizeof(regmatch_t);
550      nextMatches = malloc(matchBufSize);
551      if (!nextMatches)
552        return nil; // COV_NF_LINE - no real way to force this in a unittest
553
554      // setup our range to work on
555      nextMatches[0].rm_so = curParseIndex_;
556      nextMatches[0].rm_eo = [utf8StrBuf_ length];
557
558      // figure out our flags
559      int flags = REG_STARTEND;
560      if ((!treatStartOfNewSegmentAsBeginningOfString_) &&
561          (curParseIndex_ != 0)) {
562        // see -treatStartOfNewSegmentAsBeginningOfString: for why we have
563        // this check here.
564        flags |= REG_NOTBOL;
565      }
566
567      // call for the match
568      if ([regex_ runRegexOnUTF8:[utf8StrBuf_ bytes]
569                          nmatch:([regex_ subPatternCount] + 1)
570                          pmatch:nextMatches
571                           flags:flags]) {
572        // match
573
574        if (allSegments_ &&
575            (nextMatches[0].rm_so != curParseIndex_)) {
576          // we should return all segments (not just matches), and there was
577          // something before this match.  So safe off this match for later
578          // and create a range for this.
579
580          savedRegMatches_ = nextMatches;
581          nextMatches = malloc(matchBufSize);
582          if (!nextMatches)
583            return nil; // COV_NF_LINE - no real way to force this in a unittest
584
585          isMatch = NO;
586          // mark everything but the zero slot w/ not used
587          for (NSUInteger x = [regex_ subPatternCount]; x > 0; --x) {
588            nextMatches[x].rm_so = nextMatches[x].rm_eo = -1;
589          }
590          nextMatches[0].rm_so = curParseIndex_;
591          nextMatches[0].rm_eo = savedRegMatches_[0].rm_so;
592
593          // advance our marker
594          curParseIndex_ = savedRegMatches_[0].rm_eo;
595
596        } else {
597          // we only return matches or are pointed at a match
598
599          // no real work to do, just fall through to return to return the
600          // current match.
601          isMatch = YES;
602
603          // advance our marker
604          curParseIndex_ = nextMatches[0].rm_eo;
605        }
606
607      } else {
608        // no match
609
610        // should we return the last non matching segment?
611        if (allSegments_) {
612          isMatch = NO;
613          // mark everything but the zero slot w/ not used
614          for (NSUInteger x = [regex_ subPatternCount]; x > 0; --x) {
615            nextMatches[x].rm_so = nextMatches[x].rm_eo = -1;
616          }
617          nextMatches[0].rm_so = curParseIndex_;
618          nextMatches[0].rm_eo = [utf8StrBuf_ length];
619        } else {
620          // drop match set, we don't want it
621          free(nextMatches);
622          nextMatches = nil;
623        }
624
625        // advance our marker since we're done
626        curParseIndex_ = [utf8StrBuf_ length];
627
628      }
629    }
630
631    // create the segment to return
632    if (nextMatches) {
633      result =
634        [[[GTMRegexStringSegment alloc] initWithUTF8StrBuf:utf8StrBuf_
635                                                regMatches:nextMatches
636                                             numRegMatches:[regex_ subPatternCount]
637                                                   isMatch:isMatch] autorelease];
638      nextMatches = nil;
639    }
640  } @catch (id e) { // COV_NF_START - no real way to force this in a test
641    _GTMDevLog(@"Exceptions while trying to advance enumeration (%@)", e);
642    // if we still have something in our temp, free it
643    free(nextMatches);
644  } // COV_NF_END
645
646  return result;
647}
648
649- (NSString *)description {
650  return [NSString stringWithFormat:@"%@<%p> { regex=\"%@\", allSegments=%s, string=\"%.20s...\" }",
651    [self class], self,
652    regex_,
653    (allSegments_ ? "YES" : "NO"),
654    [utf8StrBuf_ bytes]];
655}
656
657@end
658
659@implementation GTMRegexStringSegment
660
661- (id)init {
662  // make sure init is never called, the class in in the header so someone
663  // could try to create it by mistake.
664  // Call super init and release so we don't leak
665  [[super init] autorelease];
666  [self doesNotRecognizeSelector:_cmd];
667  return nil; // COV_NF_LINE - return is just here to keep gcc happy
668}
669
670- (void)dealloc {
671  free(regMatches_);
672  [utf8StrBuf_ release];
673  [super dealloc];
674}
675
676- (BOOL)isMatch {
677  return isMatch_;
678}
679
680- (NSString *)string {
681  // fetch match zero
682  return [self subPatternString:0];
683}
684
685- (NSString *)subPatternString:(NSUInteger)patternIndex {
686  if (patternIndex > numRegMatches_)
687    return nil;
688
689  // pick off when it wasn't found
690  if ((regMatches_[patternIndex].rm_so == -1) && 
691      (regMatches_[patternIndex].rm_eo == -1))
692    return nil;
693
694  // fetch the string
695  const char *base = (const char*)[utf8StrBuf_ bytes] 
696    + regMatches_[patternIndex].rm_so;
697  regoff_t len = regMatches_[patternIndex].rm_eo 
698    - regMatches_[patternIndex].rm_so;
699  return [[[NSString alloc] initWithBytes:base
700                                   length:(NSUInteger)len
701                                 encoding:NSUTF8StringEncoding] autorelease];
702}
703
704- (NSString *)description {
705  NSMutableString *result =
706    [NSMutableString stringWithFormat:@"%@<%p> { isMatch=\"%s\", subPatterns=(",
707      [self class], self, (isMatch_ ? "YES" : "NO")];
708  for (NSUInteger x = 0; x <= numRegMatches_; ++x) {
709    int length = (int)(regMatches_[x].rm_eo - regMatches_[x].rm_so);
710    const char* string 
711      = (((const char*)[utf8StrBuf_ bytes]) + regMatches_[x].rm_so);
712    if (x == 0) {
713      [result appendFormat:@" \"%.*s\"", length , string];
714    } else {
715      [result appendFormat:@", \"%.*s\"", length , string];
716    }
717  }
718  [result appendString:@" ) }"];
719
720  return result;
721}
722
723@end
724
725@implementation GTMRegexStringSegment (PrivateMethods)
726
727- (id)initWithUTF8StrBuf:(NSData *)utf8StrBuf
728              regMatches:(regmatch_t *)regMatches
729           numRegMatches:(NSUInteger)numRegMatches
730                 isMatch:(BOOL)isMatch {
731  self = [super init];
732  if (!self) return nil;
733
734  utf8StrBuf_ = [utf8StrBuf retain];
735  regMatches_ = regMatches;
736  numRegMatches_ = numRegMatches;
737  isMatch_ = isMatch;
738
739  // check the args
740  if (!utf8StrBuf_ || !regMatches_) {
741    // COV_NF_START
742    // this could only happen something messed w/ our internal state.
743    [self release];
744    return nil;
745    // COV_NF_END
746  }
747
748  return self;
749}
750
751@end
752
753@implementation NSString (GTMRegexAdditions)
754
755- (BOOL)gtm_matchesPattern:(NSString *)pattern {
756  GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
757  return [regex matchesString:self];
758}
759
760- (NSArray *)gtm_subPatternsOfPattern:(NSString *)pattern {
761  GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
762  return [regex subPatternsOfString:self];
763}
764
765- (NSString *)gtm_firstSubStringMatchedByPattern:(NSString *)pattern {
766  GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
767  return [regex firstSubStringMatchedInString:self];
768}
769
770- (BOOL)gtm_subStringMatchesPattern:(NSString *)pattern {
771  GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
772  return [regex matchesSubStringInString:self];
773}
774
775- (NSArray *)gtm_allSubstringsMatchedByPattern:(NSString *)pattern {
776  NSEnumerator *enumerator = [self gtm_matchSegmentEnumeratorForPattern:pattern];
777  NSArray *allSegments = [enumerator allObjects];
778  return [allSegments valueForKey:@"string"];
779}
780
781- (NSEnumerator *)gtm_segmentEnumeratorForPattern:(NSString *)pattern {
782  GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
783  return [regex segmentEnumeratorForString:self];
784}
785
786- (NSEnumerator *)gtm_matchSegmentEnumeratorForPattern:(NSString *)pattern {
787  GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
788  return [regex matchSegmentEnumeratorForString:self];
789}
790
791- (NSString *)gtm_stringByReplacingMatchesOfPattern:(NSString *)pattern
792                                    withReplacement:(NSString *)replacementPattern {
793  GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
794  return [regex stringByReplacingMatchesInString:self
795                                 withReplacement:replacementPattern];
796}
797
798@end