home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / SourceCode / MiscKit1.2.6 / Source / MiscStringPatterns.m < prev    next >
Encoding:
Text File  |  1994-05-19  |  7.3 KB  |  266 lines

  1. //
  2. //    MiscStringPatterns.m -- Pattern matching and replacement routines
  3. //        Written by Steve Hayman (c) 1994 by Steve Hayman.
  4. //                Version 1.95  All rights reserved.
  5. //        This notice may not be removed from this source code.
  6. //
  7. //    This object is included in the MiscKit by permission from the author
  8. //    and its use is governed by the MiscKit license, found in the file
  9. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  10. //    for a list of all applicable permissions and restrictions.
  11. //    
  12.  
  13. #import <misckit/MiscString.h>
  14.  
  15. @implementation MiscString(PatternMatching)
  16. /*
  17.  * Match strings against regular expressions, using re_compile 
  18.  * Steve Hayman
  19.  * November 23, 1993
  20.  */
  21. /*
  22.  * Match a specified pattern.  Returns 1 or 0 depending on whether the
  23.  * pattern is found in the indicated string.  Returns -1 if bogus regexp.
  24.  * Will also optionally fill in strings with the portion of the destination
  25.  * string before the match, the portion that matches, and the portion
  26.  * after the match.
  27.  */
  28. #import <regex.h>
  29.  
  30. #ifdef DONT_COMPILE    // obsolete method
  31. - (int) grep:(const char *)pattern caseSensitive:(BOOL)caseSens before:bstring middle:mstring after:astring
  32. {
  33.     
  34.     struct regex *reg;
  35.     int success;
  36.     char * start, *end;
  37.     MiscString *scratch;
  38.     char *s = (char *)[self stringValue];
  39.     
  40.     if ( s == NULL )
  41.     return NO;        // nothing matches an empty string
  42.     
  43.     // caseSensitive:YES means "fold case: NO", so we pass the opposite
  44.     // to re_compile.
  45.     
  46.     reg = re_compile((char *)pattern, !caseSens);
  47.     
  48.     if ( reg  == NULL )
  49.     return -1;        // bogus regular expression
  50.  
  51.     success = re_match( s, reg );
  52.     
  53.     switch( success ) {
  54.     case 0:    // didn't match
  55.         free(reg);
  56.     return 0;
  57.     case -1:    // bogus regular expression 
  58.  
  59.          free(reg);
  60.  
  61.      return -1;
  62.      
  63.     default:    // matched.
  64.     start = reg->start;
  65.     end = reg->end;
  66.     
  67.     // fill in each of the various substrings, if desired
  68.     
  69.     // the part before the match
  70.     if ( bstring ) {
  71.         scratch = [self midFrom:0 to:start - s - 1];
  72.         [bstring takeStringValue:scratch];
  73.         [scratch free];
  74.     }
  75.     // the part that matched
  76.     
  77.     if ( mstring ) {
  78.         scratch = [self midFrom:(start - s) to:(end - s) - 1];
  79.         [mstring takeStringValue:scratch];
  80.         [scratch free];
  81.     }
  82.     
  83.     // the part after the match
  84.     
  85.     if ( astring ) {
  86.         scratch = [self midFrom: end - s  to: [self length] - 1];
  87.         [astring takeStringValue: scratch];
  88.         [scratch free];
  89.     }
  90.     free(reg);
  91.     return 1;
  92.     }
  93. }
  94. #endif
  95.  
  96. /*
  97.  * Variants on the above.
  98.  */
  99.  
  100. #ifdef DONT_COMPILE    // obsolete method
  101. - (int) grep:(const char *)pattern caseSensitive:(BOOL)caseSens
  102. {
  103.     return [self grep:pattern caseSensitive:caseSens
  104.          before:nil middle:nil after:nil];
  105. }
  106. #endif
  107.  
  108. #ifdef DONT_COMPILE    // obsolete method
  109. - (int) grep:(const char *)pattern
  110. {
  111.     return [self grep:pattern caseSensitive:YES];
  112. }
  113. #endif
  114.  
  115. - (int) grepString:pattern caseSensitive:(BOOL)caseSens before:bstring middle:mstring after:astring
  116. {
  117.     return( [self grep:[pattern stringValue] caseSensitive:caseSens
  118.             before:bstring middle:mstring after:astring] );
  119.     
  120. }
  121.  
  122. - (int) grepString:pattern caseSensitive:(BOOL)caseSens
  123. {
  124.     return [self grepString:pattern caseSensitive:caseSens
  125.          before:nil middle:nil after:nil];
  126. }
  127.  
  128. - (int) grepString:pattern
  129. {
  130.     return [self grepString:pattern caseSensitive:YES];
  131. }
  132.  
  133.  
  134. - (int)replacePattern:(const char *)pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob with:(const char *)replacement
  135. {
  136.     id before = [[MiscString alloc] init];
  137.     id after  = [[MiscString alloc] init];
  138.     id middle = [[MiscString alloc] init];
  139.     
  140.     id newString = [[MiscString alloc] init];
  141.     id newReplacement = [[MiscString alloc] init];
  142.     id grepMe;
  143.     
  144.     int r;
  145.     int replacements = 0;
  146.     
  147.     grepMe = self;
  148.     
  149.     // Do at least one replacement; if "glob" is TRUE, keep going
  150.     // until we can't do any more.
  151.     
  152.     do {
  153.     
  154.     r = [grepMe grep:pattern caseSensitive:caseSens 
  155.             before:before middle:middle after:after];
  156.  
  157.     if ( r <= 0 )
  158.         break;    // no match - or no more matches
  159.         
  160.     if ( [before stringValue] )
  161.         [newString concatenate:before];
  162.     if ( replacement ) {
  163. #ifdef FUTURE_FEATURE
  164.         /*
  165.          * ed has this notion of "&" on the right hand side of
  166.          * a substitution meaning "interpolate the text that was
  167.          * matched", i.e. "s/foo/&bar/" produces "foobar".
  168.          *
  169.          * In addition ed lets you mark out sub-expressions
  170.          * with \(\), and you can use \1, \2 ... to refer to
  171.          * the corresponding matched text.
  172.          *    s/\(A*\)\(B*\)/\2\1/
  173.          * which would turn AAAAABB into BBAAAAA
  174.          * This is ALMOST really easy to do here.
  175.          * To get "&" working it is ALMOST a matter of doing
  176.          *
  177.          *  [newReplacement setStringValue:replacement];
  178.          *  [newReplacement replacePattern:"&" 
  179.          *     caseSensitive:NO globally:YES
  180.          *     withString:middle];
  181.          *  [newString concatentate:newReplacement];
  182.          *
  183.          * which would be a neat use of recursion. 
  184.          * So why haven't I done this?  Well, you should also
  185.          * support "\&" meaning "a literal '&'" on the right
  186.          * hand side, and, well, I couldn't think of a quick
  187.          * two-line way to sneak that in.  Also you would get
  188.          * in trouble if "middle" contained a "&".
  189.          *
  190.          * Also, the regex structure contains
  191.          *    char *braslist[NBRA];
  192.          *    char *braelist[NBRA];
  193.          *
  194.          * These are pointers to the beginning and end of 
  195.          * parenthesized sub-expressions matched in the
  196.          * input text.  You could use these to implement
  197.          * replacements of \1, \2 ... \9, but, again, you would
  198.          * need to be careful that you weren't matching "\\1".
  199.          *
  200.          * Maybe next time.
  201.          * steve
  202.          */
  203.         
  204.         // "&" in the replacement string stands for the text
  205.         // that was matched - just like "ed"
  206.         [newReplacement setStringValue:replacement];
  207.         [newReplacement replacePattern:"&" caseSensitive:NO globally:YES
  208.         withString:middle];
  209.     
  210.  
  211.         [newString concatenate:newReplacement];
  212. #else
  213.         // do the simple-minded substitution in lieu of fanciness above
  214.         
  215.         [newString cat:replacement];
  216. #endif
  217.     }
  218.     replacements ++;
  219.     
  220.     // next time around we match on the remainder of the string
  221.     grepMe = after;
  222.     } while ( glob );
  223.     
  224.    
  225.     if ( [after stringValue] )
  226.         [newString concatenate:after];
  227.     
  228.     // If any changes were made, copy the new string.
  229.     
  230.     if ( replacements )
  231.     [self takeStringValue:newString];
  232.  
  233.     [before free];
  234.     [after free];
  235.     [middle free];
  236.     [newString free];
  237.     [newReplacement free];
  238.     
  239.     // Return number of replacements made, or -1 if bogus regexp.
  240.     return ( r < 0 ? r : replacements );
  241. }
  242.  
  243. /*
  244.  * Various other flavours of replacePattern
  245.  */
  246.  
  247. - (int)replacePattern:(const char *)pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob withString:replacement
  248. {
  249.     return ( [self replacePattern:pattern caseSensitive:caseSens
  250.             globally:glob with:[replacement stringValue] ]);
  251. }
  252.  
  253. - (int)replacePatternString:pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob with:(const char *)replacement
  254. {
  255.     return [self replacePattern:[pattern stringValue] caseSensitive:caseSens globally:glob with:replacement];
  256. }
  257. - (int)replacePatternString:pattern caseSensitive:(BOOL)caseSens globally:(BOOL)glob withString:replacement
  258. {
  259.     return ( [self replacePattern:[pattern stringValue] caseSensitive:caseSens
  260.             globally:glob
  261.         with:[replacement stringValue]] );
  262. }
  263.  
  264.         
  265. @end
  266.