This is MiscStringPatterns.m in view mode; [Download] [Up]
// // MiscStringPatterns.m -- Pattern matching and replacement routines // Written by Steve Hayman Copyright (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.