mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-28 19:13:49 +02:00
297 lines
8.6 KiB
Objective-C
297 lines
8.6 KiB
Objective-C
/*
|
|
* XADRegex.m
|
|
*
|
|
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
#import "XADRegex.h"
|
|
|
|
static BOOL IsRegexSpecialCharacter(unichar c)
|
|
{
|
|
return c=='^'||c=='.'||c=='['||c=='$'||c=='('||c==')'||
|
|
c=='|'||c=='*'||c=='+'||c=='?'||c=='{'||c=='\\';
|
|
}
|
|
|
|
@implementation XADRegex
|
|
|
|
+(XADRegex *)regexWithPattern:(NSString *)pattern options:(int)options
|
|
{ return [[[XADRegex alloc] initWithPattern:pattern options:options] autorelease]; }
|
|
|
|
+(XADRegex *)regexWithPattern:(NSString *)pattern
|
|
{ return [[[XADRegex alloc] initWithPattern:pattern options:0] autorelease]; }
|
|
|
|
+(NSString *)null
|
|
{
|
|
static NSString *nullstring=nil;
|
|
if(!nullstring) nullstring=[[NSMutableString alloc] initWithString:@""];
|
|
return nullstring;
|
|
}
|
|
|
|
+(NSString *)patternForLiteralString:(NSString *)string
|
|
{
|
|
long len=[string length];
|
|
NSMutableString *escaped=[NSMutableString stringWithCapacity:len];
|
|
|
|
for(long i=0;i<len;i++)
|
|
{
|
|
unichar c=[string characterAtIndex:i];
|
|
if(IsRegexSpecialCharacter(c)) [escaped appendFormat:@"\\%C",c];
|
|
else [escaped appendFormat:@"%C",c];
|
|
}
|
|
return [NSString stringWithString:escaped];
|
|
}
|
|
|
|
+(NSString *)patternForGlob:(NSString *)glob
|
|
{
|
|
long len=[glob length];
|
|
NSMutableString *pattern=[NSMutableString stringWithCapacity:len+2];
|
|
|
|
[pattern appendString:@"^"];
|
|
|
|
for(long i=0;i<len;i++)
|
|
{
|
|
unichar c=[glob characterAtIndex:i];
|
|
if(c=='\\')
|
|
{
|
|
if(i==len-1) [pattern appendString:@"\\"];
|
|
else
|
|
{
|
|
i++;
|
|
unichar c=[glob characterAtIndex:i];
|
|
if(IsRegexSpecialCharacter(c)) [pattern appendFormat:@"\\%C",c];
|
|
else [pattern appendFormat:@"%C",c];
|
|
}
|
|
}
|
|
else if(c=='*')
|
|
{
|
|
[pattern appendString:@".*"];
|
|
}
|
|
else if(c=='?')
|
|
{
|
|
[pattern appendString:@"."];
|
|
}
|
|
else if(c=='[')
|
|
{
|
|
[pattern appendString:@"["];
|
|
}
|
|
else if(c==']')
|
|
{
|
|
[pattern appendString:@"]"];
|
|
}
|
|
else if(IsRegexSpecialCharacter(c))
|
|
{
|
|
[pattern appendFormat:@"\\%C",c];
|
|
}
|
|
else
|
|
{
|
|
[pattern appendFormat:@"%C",c];
|
|
}
|
|
}
|
|
|
|
[pattern appendString:@"$"];
|
|
|
|
return [NSString stringWithString:pattern];
|
|
}
|
|
|
|
-(id)initWithPattern:(NSString *)pattern options:(int)options
|
|
{
|
|
if((self=[super init]))
|
|
{
|
|
patternstring=[pattern retain];
|
|
currdata=nil;
|
|
matches=NULL;
|
|
|
|
int err=regcomp(&preg,[pattern UTF8String],options|REG_EXTENDED);
|
|
if(err)
|
|
{
|
|
[self autorelease];
|
|
char errbuf[256];
|
|
regerror(err,&preg,errbuf,sizeof(errbuf));
|
|
[NSException raise:@"XADRegexException" format:@"Could not compile regex \"%@\": %s",pattern,errbuf];
|
|
}
|
|
|
|
matches=calloc(sizeof(regmatch_t),preg.re_nsub+1);
|
|
if(!matches)
|
|
{
|
|
[self autorelease];
|
|
[NSException raise:NSMallocException format:@"Out of memory when creating regex \"%@\"",pattern];
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[patternstring release];
|
|
regfree(&preg);
|
|
free(matches);
|
|
[currdata release];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(void)beginMatchingString:(NSString *)string { [self beginMatchingData:[string dataUsingEncoding:NSUTF8StringEncoding]]; }
|
|
|
|
-(void)beginMatchingData:(NSData *)data { [self beginMatchingData:data range:NSMakeRange(0,[data length])]; }
|
|
|
|
-(void)beginMatchingData:(NSData *)data range:(NSRange)range
|
|
{
|
|
matchrange=range;
|
|
if(data==currdata) return;
|
|
[currdata release];
|
|
currdata=[data retain];
|
|
}
|
|
|
|
-(void)finishMatching { [currdata release]; currdata=nil; }
|
|
|
|
-(BOOL)matchNext
|
|
{
|
|
matches[0].rm_so=matchrange.location;
|
|
matches[0].rm_eo=matchrange.location+matchrange.length;
|
|
if(regexec(&preg,[currdata bytes],preg.re_nsub+1,matches,REG_STARTEND)==0)
|
|
{
|
|
matchrange.length-=matches[0].rm_eo-matchrange.location;
|
|
matchrange.location=(long)matches[0].rm_eo;
|
|
return YES;
|
|
}
|
|
[self finishMatching];
|
|
return NO;
|
|
}
|
|
|
|
-(NSString *)stringForMatch:(int)n
|
|
{
|
|
if(n>preg.re_nsub||n<0) [NSException raise:NSRangeException format:@"Index %d out of range for regex \"%@\"",n,self];
|
|
if(matches[n].rm_so==-1&&matches[n].rm_eo==-1) return nil;
|
|
return [[[NSString alloc] initWithBytes:[currdata bytes]+matches[n].rm_so
|
|
length:(long)(matches[n].rm_eo-matches[n].rm_so) encoding:NSUTF8StringEncoding] autorelease];
|
|
}
|
|
|
|
-(NSArray *)allMatches
|
|
{
|
|
NSMutableArray *array=[NSMutableArray arrayWithCapacity:preg.re_nsub+1];
|
|
for(int i=0;i<=preg.re_nsub;i++)
|
|
{
|
|
NSString *str=[self stringForMatch:i];
|
|
if(str) [array addObject:str];
|
|
else [array addObject:[XADRegex null]];
|
|
}
|
|
return [NSArray arrayWithArray:array];
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)matchesString:(NSString *)string
|
|
{
|
|
[self beginMatchingString:string];
|
|
BOOL res=[self matchNext];
|
|
[self finishMatching];
|
|
return res;
|
|
}
|
|
|
|
-(NSString *)matchedSubstringOfString:(NSString *)string
|
|
{
|
|
[self beginMatchingString:string];
|
|
NSString *res=nil;
|
|
if([self matchNext]) res=[self stringForMatch:0];
|
|
[self finishMatching];
|
|
return res;
|
|
}
|
|
|
|
-(NSArray *)capturedSubstringsOfString:(NSString *)string
|
|
{
|
|
[self beginMatchingString:string];
|
|
NSArray *res=nil;
|
|
if([self matchNext]) res=[self allMatches];
|
|
[self finishMatching];
|
|
return res;
|
|
}
|
|
|
|
-(NSArray *)allMatchedSubstringsOfString:(NSString *)string
|
|
{
|
|
[self beginMatchingString:string];
|
|
NSMutableArray *array=[NSMutableArray array];
|
|
while([self matchNext]) [array addObject:[self stringForMatch:0]];
|
|
[self finishMatching];
|
|
return [NSArray arrayWithArray:array];
|
|
}
|
|
|
|
-(NSArray *)allCapturedSubstringsOfString:(NSString *)string
|
|
{
|
|
[self beginMatchingString:string];
|
|
NSMutableArray *array=[NSMutableArray array];
|
|
while([self matchNext]) [array addObject:[self allMatches]];
|
|
[self finishMatching];
|
|
return [NSArray arrayWithArray:array];
|
|
}
|
|
|
|
-(NSArray *)componentsOfSeparatedString:(NSString *)string
|
|
{
|
|
[self beginMatchingString:string];
|
|
NSMutableArray *array=[NSMutableArray array];
|
|
|
|
regoff_t prevstart=0;
|
|
const char *bytes=[currdata bytes];
|
|
while([self matchNext])
|
|
{
|
|
[array addObject:[[[NSString alloc] initWithBytes:bytes+prevstart length:(long)(matches[0].rm_so-prevstart)
|
|
encoding:NSUTF8StringEncoding] autorelease]];
|
|
prevstart=matches[0].rm_eo;
|
|
}
|
|
[array addObject:[[[NSString alloc] initWithBytes:bytes+prevstart length:(long)([currdata length]-prevstart)
|
|
encoding:NSUTF8StringEncoding] autorelease]];
|
|
|
|
[self finishMatching];
|
|
return [NSArray arrayWithArray:array];
|
|
}
|
|
|
|
-(NSString *)pattern { return patternstring; }
|
|
|
|
-(NSString *)description { return patternstring; }
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSString (XADRegex)
|
|
|
|
-(BOOL)matchedByPattern:(NSString *)pattern { return [self matchedByPattern:pattern options:0]; }
|
|
-(BOOL)matchedByPattern:(NSString *)pattern options:(int)options
|
|
{ return [[XADRegex regexWithPattern:pattern options:options] matchesString:self]; }
|
|
|
|
-(NSString *)substringMatchedByPattern:(NSString *)pattern { return [self substringMatchedByPattern:pattern options:0]; }
|
|
-(NSString *)substringMatchedByPattern:(NSString *)pattern options:(int)options
|
|
{ return [[XADRegex regexWithPattern:pattern options:options] matchedSubstringOfString:self]; }
|
|
|
|
-(NSArray *)substringsCapturedByPattern:(NSString *)pattern { return [self substringsCapturedByPattern:pattern options:0]; }
|
|
-(NSArray *)substringsCapturedByPattern:(NSString *)pattern options:(int)options
|
|
{ return [[XADRegex regexWithPattern:pattern options:options] capturedSubstringsOfString:self]; }
|
|
|
|
-(NSArray *)allSubstringsMatchedByPattern:(NSString *)pattern { return [self allSubstringsMatchedByPattern:pattern options:0]; }
|
|
-(NSArray *)allSubstringsMatchedByPattern:(NSString *)pattern options:(int)options
|
|
{ return [[XADRegex regexWithPattern:pattern options:options] allMatchedSubstringsOfString:self]; }
|
|
|
|
-(NSArray *)allSubstringsCapturedByPattern:(NSString *)pattern { return [self allSubstringsCapturedByPattern:pattern options:0]; }
|
|
-(NSArray *)allSubstringsCapturedByPattern:(NSString *)pattern options:(int)options
|
|
{ return [[XADRegex regexWithPattern:pattern options:options] allCapturedSubstringsOfString:self]; }
|
|
|
|
-(NSArray *)componentsSeparatedByPattern:(NSString *)pattern { return [self componentsSeparatedByPattern:pattern options:0]; }
|
|
-(NSArray *)componentsSeparatedByPattern:(NSString *)pattern options:(int)options
|
|
{ return [[XADRegex regexWithPattern:pattern options:options] componentsOfSeparatedString:self]; }
|
|
|
|
-(NSString *)escapedPattern { return [XADRegex patternForLiteralString:self]; }
|
|
|
|
@end
|
|
|