This is MiscStringPatterns.m in view mode; [Download] [Up]
//
// MiscStringPatterns.m -- Pattern matching and replacement routines
// Written by Steve Hayman (c) 1994 by Steve Hayman.
// Version 1.95 All rights reserved.
// This notice may not be removed from this source code.
//
// This object is included in the MiscKit by permission from the author
// and its use is governed by the MiscKit license, found in the file
// "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
// for a list of all applicable permissions and restrictions.
//
#import <misckit/MiscString.h>
@implementation MiscString(PatternMatching)
/*
* Match strings against regular expressions, using re_compile
* Steve Hayman
* November 23, 1993
*/
/*
* Match a specified pattern. Returns 1 or 0 depending on whether the
* pattern is found in the indicated string. Returns -1 if bogus regexp.
* Will also optionally fill in strings with the portion of the destination
* string before the match, the portion that matches, and the portion
* after the match.
*/
#import <regex.h>
#ifdef DONT_COMPILE // obsolete method
- (int) grep:(const char *)pattern caseSensitive:(BOOL)caseSens before:bstring middle:mstring after:astring
{
struct regex *reg;
int success;
char * start, *end;
MiscString *scratch;
char *s = (char *)[self stringValue];
if ( s == NULL )
return NO; // nothing matches an empty string
// caseSensitive:YES means "fold case: NO", so we pass the opposite
// to re_compile.
reg = re_compile((char *)pattern, !caseSens);
if ( reg == NULL )
return -1; // bogus regular expression
success = re_match( s, reg );
switch( success ) {
case 0: // didn't match
free(reg);
return 0;
case -1: // bogus regular expression
free(reg);
return -1;
default: // matched.
start = reg->start;
end = reg->end;
// fill in each of the various substrings, if desired
// the part before the match
if ( bstring ) {
scratch = [self midFrom:0 to:start - s - 1];
[bstring takeStringValue:scratch];
[scratch free];
}
// the part that matched
if ( mstring ) {
scratch = [self midFrom:(start - s) to:(end - s) - 1];
[mstring takeStringValue:scratch];
[scratch free];
}
// the part after the match
if ( astring ) {
scratch = [self midFrom: end - s to: [self length] - 1];
[astring takeStringValue: scratch];
[scratch free];
}
free(reg);
return 1;
}
}
#endif
/*
* Variants on the above.
*/
#ifdef DONT_COMPILE // obsolete method
- (int) grep:(const char *)pattern caseSensitive:(BOOL)caseSens
{
return [self grep:pattern caseSensitive:caseSens
before:nil middle:nil after:nil];
}
#endif
#ifdef DONT_COMPILE // obsolete method
- (int) grep:(const char *)pattern
{
return [self grep:pattern caseSensitive:YES];
}
#endif
- (int) grepString:pattern caseSensitive:(BOOL)caseSens before:bstring middle:mstring after:astring
{
return( [self grep:[pattern stringValue] caseSensitive:caseSens
before:bstring middle:mstring after:astring] );
}
- (int) grepString:pattern caseSensitive:(BOOL)caseSens
{
return [self grepString:pattern caseSensitive:caseSens
before:nil middle:nil after:nil];
}
- (int) grepString:pattern
{
return [self grepString:pattern caseSensitive:YES];
}
- (int)replacePattern:(const char *)pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob with:(const char *)replacement
{
id before = [[MiscString alloc] init];
id after = [[MiscString alloc] init];
id middle = [[MiscString alloc] init];
id newString = [[MiscString alloc] init];
id newReplacement = [[MiscString alloc] init];
id grepMe;
int r;
int replacements = 0;
grepMe = self;
// Do at least one replacement; if "glob" is TRUE, keep going
// until we can't do any more.
do {
r = [grepMe grep:pattern caseSensitive:caseSens
before:before middle:middle after:after];
if ( r <= 0 )
break; // no match - or no more matches
if ( [before stringValue] )
[newString concatenate:before];
if ( replacement ) {
#ifdef FUTURE_FEATURE
/*
* ed has this notion of "&" on the right hand side of
* a substitution meaning "interpolate the text that was
* matched", i.e. "s/foo/&bar/" produces "foobar".
*
* In addition ed lets you mark out sub-expressions
* with \(\), and you can use \1, \2 ... to refer to
* the corresponding matched text.
* s/\(A*\)\(B*\)/\2\1/
* which would turn AAAAABB into BBAAAAA
* This is ALMOST really easy to do here.
* To get "&" working it is ALMOST a matter of doing
*
* [newReplacement setStringValue:replacement];
* [newReplacement replacePattern:"&"
* caseSensitive:NO globally:YES
* withString:middle];
* [newString concatentate:newReplacement];
*
* which would be a neat use of recursion.
* So why haven't I done this? Well, you should also
* support "\&" meaning "a literal '&'" on the right
* hand side, and, well, I couldn't think of a quick
* two-line way to sneak that in. Also you would get
* in trouble if "middle" contained a "&".
*
* Also, the regex structure contains
* char *braslist[NBRA];
* char *braelist[NBRA];
*
* These are pointers to the beginning and end of
* parenthesized sub-expressions matched in the
* input text. You could use these to implement
* replacements of \1, \2 ... \9, but, again, you would
* need to be careful that you weren't matching "\\1".
*
* Maybe next time.
* steve
*/
// "&" in the replacement string stands for the text
// that was matched - just like "ed"
[newReplacement setStringValue:replacement];
[newReplacement replacePattern:"&" caseSensitive:NO globally:YES
withString:middle];
[newString concatenate:newReplacement];
#else
// do the simple-minded substitution in lieu of fanciness above
[newString cat:replacement];
#endif
}
replacements ++;
// next time around we match on the remainder of the string
grepMe = after;
} while ( glob );
if ( [after stringValue] )
[newString concatenate:after];
// If any changes were made, copy the new string.
if ( replacements )
[self takeStringValue:newString];
[before free];
[after free];
[middle free];
[newString free];
[newReplacement free];
// Return number of replacements made, or -1 if bogus regexp.
return ( r < 0 ? r : replacements );
}
/*
* Various other flavours of replacePattern
*/
- (int)replacePattern:(const char *)pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob withString:replacement
{
return ( [self replacePattern:pattern caseSensitive:caseSens
globally:glob with:[replacement stringValue] ]);
}
- (int)replacePatternString:pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob with:(const char *)replacement
{
return [self replacePattern:[pattern stringValue] caseSensitive:caseSens globally:glob with:replacement];
}
- (int)replacePatternString:pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob withString:replacement
{
return ( [self replacePattern:[pattern stringValue] caseSensitive:caseSens
globally:glob
with:[replacement stringValue]] );
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.