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

  1. //
  2. //    MiscStringRegex.m 
  3. //         Regular expression matching and replacement routines using regexpr.c
  4. //
  5. //        Written by Carl Lindberg (c) 1994 by Carl Lindberg.
  6. //            Version 1.95  All rights reserved.
  7. //        This notice may not be removed from this source code.
  8. //
  9. //    This object is included in the MiscKit by permission from the author
  10. //    and its use is governed by the MiscKit license, found in the file
  11. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  12. //    for a list of all applicable permissions and restrictions.
  13. //    
  14.  
  15. /*   I decided to keep with MiscStringPatterns' -grep method, except to add
  16.  * an occurrenceNum: parameter.  The old calls to this method should work
  17.  * exactly as they used to.   I did not include the -grepString methods, as
  18.  * I'm not sure how useful they would be, and there are a fair number of
  19.  * methods here already.  If anyone thinks they were indeed useful, I can add
  20.  * them in very easily.
  21.  *   On the other hand, MiscStringPatterns' -replace methods have been
  22.  * rearranged.  To be more parallel with the rest of the MiscString class,
  23.  * I made the global replace its own method, and added an occurrenceNum: 
  24.  * parameter to the basic replace method.
  25.  *   There are other possible methods here: -indexOfRegex and -rindexOfRegex 
  26.  * methods to go along with -spotOfRegex, and maybe a method to simply return
  27.  * a matched portion in a new MiscString.  I'm going to let these go for the
  28.  * time being; if people think they are useful I'll add them in then then.
  29.  */
  30.  
  31. #import <misckit/MiscString.h>
  32.  
  33. @implementation MiscString(Regex)
  34.  
  35. - (int)grep:(const char *)regex occurrenceNum:(int)n caseSensitive:(BOOL)sense before:(id)b middle:(id)m after:(id)a
  36. {
  37.   int spot,len=0;
  38.   id tmpStr;
  39.   
  40.   if (!regex) return -1;
  41.   if (n == MISC_STRING_LAST) 
  42.     spot = [self rspotOfRegex:regex occurrenceNum:0 caseSensitive:sense length:&len];
  43.   else
  44.     spot = [self spotOfRegex:regex occurrenceNum:n caseSensitive:sense length:&len];
  45.  
  46.  // I decided on -setStringValue over -takeStringValueFrom....
  47.   if ((spot >= 0) && (len > 0)) {
  48.     if (b && [b respondsTo:@selector(setStringValue:)]) {
  49.       tmpStr = [self midFrom:0 to:spot-1];
  50.       if (tmpStr) [b setStringValue:[tmpStr stringValue]];
  51.       else [b setStringValue:""];
  52.       [tmpStr free];
  53.      }
  54.     if (a && [a respondsTo:@selector(setStringValue:)]) {
  55.       tmpStr = [self midFrom:spot+len to:length-1];
  56.       if (tmpStr) [a setStringValue:[tmpStr stringValue]];
  57.       else [a setStringValue:""];
  58.       [tmpStr free];
  59.      }
  60.     if (m && [m respondsTo:@selector(setStringValue:)]) {
  61.       tmpStr = [self midFrom:spot length:len];
  62.       if (tmpStr) [m setStringValue:[tmpStr stringValue]];
  63.       else [m setStringValue:""];
  64.       [tmpStr free];
  65.      }
  66.    }
  67.   else { // I'm not sure what I should do here.  Leave them alone?
  68.       if (a && [a respondsTo:@selector(setStringValue:)]) 
  69.         [a setStringValue:""];
  70.       if (b && [b respondsTo:@selector(setStringValue:)]) 
  71.         [b setStringValue:""];
  72.       if (m && [m respondsTo:@selector(setStringValue:)]) 
  73.         [m setStringValue:""];
  74.      }
  75.  
  76.   //spotOfRegex returns -2 if a problem, -1 if not found, and >=0 if found.
  77.   //Therefore, add one, then make everything above one equal one.
  78.   if (++spot > 1) spot = 1;
  79.   return spot;
  80. }
  81.  
  82.  
  83. - (int)numOfRegex:(const char *)regex caseSensitive:(BOOL)sense
  84. {
  85.   struct re_pattern_buffer pat;  // or regexp_t (which is a pointer)
  86.   int currnum=0, currspot=0,len=0, i, pos;
  87.   char fm[256], tr[256];
  88.   char *errstr;
  89.   
  90.   if (!regex) return -1;
  91.   
  92.   memset(&pat,0,sizeof(pat));
  93.   for (i=0;i<256;i++) tr[i] = i;
  94.   if (!sense) 
  95.     for (i='A';i<='Z';i++) tr[i] = i- 'A' + 'a';
  96.   pat.translate=tr;
  97.   pat.fastmap=fm;
  98.   errstr = re_compile_pattern((char *)regex,strlen(regex),&pat);
  99.   if (errstr) {
  100.     if (pat.buffer) free(pat.buffer);
  101.     return -1;
  102.    }
  103.    
  104.   while ((pos = re_search_pattern(&pat, buffer, length, currspot, length-currspot,0)) >= 0) {
  105.     len = re_match_pattern(&pat,buffer,length,pos,0);
  106.     if(len>0){
  107.       currspot = (pos+len);
  108.       currnum++;
  109.      }
  110.     else {
  111.       if (pat.buffer) free(pat.buffer);
  112.       return currnum;
  113.      }
  114.    }
  115.  
  116.   if (pat.buffer) free(pat.buffer);   
  117.   return currnum;
  118. }
  119.  
  120.  
  121. - (int)replaceEveryOccurrenceOfRegex:(const char *)regex with:(const char *)aString caseSensitive:(BOOL)sense
  122. {
  123.   struct re_pattern_buffer pat;
  124.   int currnum=0, currspot=0,len=0;
  125.   char fm[256], tr[256];
  126.   char *errstr;
  127.   int i, pos;
  128.   id tmpStr;
  129.   
  130.   if (!regex) return -1;
  131.   memset(&pat,0,sizeof(pat));
  132.   for (i=0;i<256;i++) tr[i] = i;
  133.   if (!sense) 
  134.     for (i='A';i<='Z';i++) tr[i] = i- 'A' + 'a';
  135.   pat.translate=tr;
  136.   pat.fastmap=fm;
  137.   errstr = re_compile_pattern((char *)regex,strlen(regex),&pat);
  138.   if (errstr) {
  139.     if (pat.buffer) free(pat.buffer);
  140.     return -1;
  141.    }
  142.    
  143.   tmpStr = [[[self class] alloc] allocateBuffer:length];
  144.   while ((pos = re_search_pattern(&pat, buffer, length, currspot, length-currspot,0)) >= 0) {
  145.     len = re_match_pattern(&pat,buffer,length,pos,0);
  146.     [tmpStr cat:buffer+currspot n:pos-currspot];
  147.     if (len>0) {
  148.       [tmpStr cat:aString];
  149.       currnum++;
  150.       currspot = (pos+len);
  151.      }
  152.     else {
  153.       currspot = pos;
  154.       break;
  155.      }
  156.    }
  157.  [tmpStr cat:buffer+currspot n:length - currspot];
  158.  [self takeStringValueFrom:tmpStr];
  159.  [tmpStr free];
  160.  if (pat.buffer) free(pat.buffer);
  161.  return currnum;
  162. }
  163.  
  164.  
  165. - (int)spotOfRegex:(const char *)regex occurrenceNum:(int)n caseSensitive:(BOOL)sense length:(int *)matchlen
  166. {
  167.   struct re_pattern_buffer pat;  // or regexp_t (which is a pointer)
  168.   int currnum=0, currspot=0, len=0;
  169.   char fm[256], tr[256];
  170.   char *errstr;
  171.   int i, pos=-1;
  172.   
  173.   if (!regex) {
  174.     if (matchlen) *matchlen = 0;
  175.     return -2;
  176.    }
  177.   if (n<0) {
  178.     if (matchlen) *matchlen = 0;
  179.     return -1;
  180.    }
  181.   memset(&pat,0,sizeof(pat));
  182.   for (i=0;i<256;i++) tr[i] = i;
  183.   if (!sense) 
  184.     for (i='A';i<='Z';i++) tr[i] = i- 'A' + 'a';
  185.   pat.translate=tr;
  186.   pat.fastmap=fm;
  187.   errstr = re_compile_pattern((char *)regex,strlen(regex),&pat);
  188.   if (errstr) {
  189.     if (matchlen) *matchlen = 0;
  190.     if (pat.buffer) free(pat.buffer);
  191.     return -2;
  192.    }
  193.    
  194.   while ((currnum <= n) && ((pos = re_search_pattern(&pat, buffer, length, currspot, length-currspot,0)) >= 0)) {
  195.     len = re_match_pattern(&pat,buffer,length,pos,0);
  196.     if(len>0 && currnum <= n){
  197.       currspot = (pos+len);
  198.       currnum++;
  199.      }
  200.     else {
  201.       pos = -1;
  202.       break;
  203.      }
  204.    }
  205.   if (pos < 0) len = 0;
  206.   if (matchlen) *matchlen = len;
  207.   if (pat.buffer) free(pat.buffer);
  208.   return pos;
  209. }
  210.  
  211. - (int)rspotOfRegex:(const char *)regex occurrenceNum:(int)n caseSensitive:(BOOL)sense length:(int *)matchlen
  212. {
  213.   int num = [self numOfRegex:regex caseSensitive:sense];
  214.   return [self spotOfRegex:regex occurrenceNum:num-1-n caseSensitive:sense length:matchlen];
  215. }
  216.  
  217. - replaceRegex:(const char *)regex with:(const char *)aString occurrenceNum:(int)n caseSensitive:(BOOL)sense
  218. {
  219.   int spot, len;
  220.   
  221.   spot = [self spotOfRegex:regex occurrenceNum:n caseSensitive:sense length:&len];
  222.   if ((spot >= 0) && (len > 0)) 
  223.     [self replaceFrom:spot length:len with:aString];
  224.   return self;
  225. }
  226.  
  227. // I thought about having this return a negative number on an error, but I decided
  228. // to return 0 on both error or not found, and the length of the matched 
  229. // portion otherwise.  That enables this method to be used kind of like a BOOL
  230. // in an if statement.
  231. - (int)matchesRegex:(const char *)regex caseSensitive:(BOOL)sense
  232. {
  233.   int spot, len=0;
  234.   if (!regex) return 0;
  235.   spot = [self spotOfRegex:regex occurrenceNum:0 caseSensitive:sense length:&len];
  236.   if (spot == 0) return len;
  237.   return 0;
  238. }
  239.  
  240. //Now all the convenience methods....
  241. - (int)grep:(const char *)regex
  242. { return [self grep:regex occurrenceNum:0 caseSensitive:YES before:nil middle:nil after:nil];}
  243.  
  244. - (int)grep:(const char *)regex caseSensitive:(BOOL)sense
  245. { return [self grep:regex occurrenceNum:0 caseSensitive:sense before:nil middle:nil after:nil];}
  246.  
  247. - (int)grep:(const char *)regex occurrenceNum:(int)n
  248. { return [self grep:regex occurrenceNum:n caseSensitive:YES before:nil middle:nil after:nil];}
  249.  
  250. - (int)grep:(const char *)regex occurrenceNum:(int)n caseSensitive:(BOOL)sense
  251. { return [self grep:regex occurrenceNum:n caseSensitive:sense before:nil middle:nil after:nil];}
  252.  
  253. - (int)grep:(const char *)regex before:(id)b middle:(id)m after:(id)a
  254. { return [self grep:regex occurrenceNum:0 caseSensitive:YES before:b middle:m after:a];}
  255.  
  256. - (int)grep:(const char *)regex caseSensitive:(BOOL)sense before:(id)b middle:(id)m after:(id)a
  257. { return [self grep:regex occurrenceNum:0 caseSensitive:sense before:b middle:m after:a];}
  258.  
  259. - (int)grep:(const char *)regex occurrenceNum:(int)n before:(id)b middle:(id)m after:(id)a
  260. { return [self grep:regex occurrenceNum:n caseSensitive:YES before:b middle:m after:a];}
  261.  
  262. - (int)numOfRegex:(const char *)regex
  263. { return [self numOfRegex:regex caseSensitive:YES];}
  264.  
  265. - (int)replaceEveryOccurrenceOfRegex:(const char *)regex with:(const char *)aString
  266. { return [self replaceEveryOccurrenceOfRegex:regex with:aString caseSensitive:YES];}
  267.  
  268. - (int)replaceEveryOccurrenceOfRegex:(const char *)regex withChar:(char)aChar caseSensitive:(BOOL)sense
  269. {
  270.   char str[2];
  271.  
  272.   if (!aChar) return -1; //or should we let this go?
  273.   str[1] = 0;
  274.   str[0] = aChar;
  275.   return [self replaceEveryOccurrenceOfRegex:regex with:str caseSensitive:sense];
  276. }
  277.  
  278. - (int)replaceEveryOccurrenceOfRegex:(const char *)regex withChar:(char)aChar
  279. { return [self replaceEveryOccurrenceOfRegex:regex withChar:aChar caseSensitive:YES];}
  280.  
  281. - (int)replaceEveryOccurrenceOfRegex:(const char *)regex withString:(id)sender
  282. { return [self replaceEveryOccurrenceOfRegex:regex withString:sender caseSensitive:YES];}
  283.  
  284. - (int)replaceEveryOccurrenceOfRegex:(const char *)regex withString:(id)sender caseSensitive:(BOOL)sense
  285. {
  286.   if (![sender respondsTo:@selector(stringValue)]) return -1;
  287.   return [self replaceEveryOccurrenceOfRegex:regex 
  288.                with:[sender stringValue]
  289.            caseSensitive:sense];
  290. }
  291.  
  292. - (int)spotOfRegex:(const char *)regex
  293. { return [self spotOfRegex:regex occurrenceNum:0 caseSensitive:YES length:NULL];}
  294.  
  295. - (int)spotOfRegex:(const char *)regex caseSensitive:(BOOL)sense
  296. { return [self spotOfRegex:regex occurrenceNum:0 caseSensitive:sense length:NULL];}
  297.  
  298. - (int)spotOfRegex:(const char *)regex occurrenceNum:(int)n
  299. { return [self spotOfRegex:regex occurrenceNum:n caseSensitive:YES length:NULL];}
  300.  
  301. - (int)spotOfRegex:(const char *)regex occurrenceNum:(int)n caseSensitive:(BOOL)sense
  302. { return [self spotOfRegex:regex occurrenceNum:n caseSensitive:sense length:NULL];}
  303.  
  304. - (int)spotOfRegex:(const char *)regex length:(int *)matchlen
  305. { return [self spotOfRegex:regex occurrenceNum:0 caseSensitive:YES length:matchlen];}
  306.  
  307. - (int)spotOfRegex:(const char *)regex caseSensitive:(BOOL)sense length:(int *)matchlen
  308. { return [self spotOfRegex:regex occurrenceNum:0 caseSensitive:sense length:matchlen];}
  309.  
  310. - (int)spotOfRegex:(const char *)regex occurrenceNum:(int)n length:(int *)matchlen
  311. { return [self spotOfRegex:regex occurrenceNum:n caseSensitive:YES length:matchlen];}
  312.  
  313.  
  314. - (int)rspotOfRegex:(const char *)regex
  315. { return [self rspotOfRegex:regex occurrenceNum:0 caseSensitive:YES length:NULL];}
  316.  
  317. - (int)rspotOfRegex:(const char *)regex caseSensitive:(BOOL)sense
  318. { return [self rspotOfRegex:regex occurrenceNum:0 caseSensitive:sense length:NULL];}
  319.  
  320. - (int)rspotOfRegex:(const char *)regex occurrenceNum:(int)n
  321. { return [self rspotOfRegex:regex occurrenceNum:n caseSensitive:YES length:NULL];}
  322.  
  323. - (int)rspotOfRegex:(const char *)regex occurrenceNum:(int)n caseSensitive:(BOOL)sense
  324. { return [self rspotOfRegex:regex occurrenceNum:n caseSensitive:sense length:NULL];}
  325.  
  326. - (int)rspotOfRegex:(const char *)regex length:(int *)matchlen
  327. { return [self rspotOfRegex:regex occurrenceNum:0 caseSensitive:YES length:matchlen];}
  328.  
  329. - (int)rspotOfRegex:(const char *)regex caseSensitive:(BOOL)sense length:(int *)matchlen
  330. { return [self rspotOfRegex:regex occurrenceNum:0 caseSensitive:sense length:matchlen];}
  331.  
  332. - (int)rspotOfRegex:(const char *)regex occurrenceNum:(int)n length:(int *)matchlen
  333. { return [self rspotOfRegex:regex occurrenceNum:n caseSensitive:YES length:matchlen];}
  334.  
  335.  
  336. - replaceRegex:(const char *)regex with:(const char *)aString
  337. { return [self replaceRegex:regex with:aString occurrenceNum:0 caseSensitive:YES];}
  338.  
  339. - replaceRegex:(const char *)regex with:(const char *)aString caseSensitive:(BOOL)sense
  340. { return [self replaceRegex:regex with:aString occurrenceNum:0 caseSensitive:sense];}
  341.  
  342. - replaceRegex:(const char *)regex with:(const char *)aString occurrenceNum:(int)n
  343. { return [self replaceRegex:regex with:aString occurrenceNum:n caseSensitive:YES];}
  344.  
  345. - replaceRegex:(const char *)regex withChar:(char)aChar
  346. { return [self replaceRegex:regex withChar:aChar occurrenceNum:0 caseSensitive:YES];}
  347.  
  348. - replaceRegex:(const char *)regex withChar:(char)aChar caseSensitive:(BOOL)sense
  349. { return [self replaceRegex:regex withChar:aChar occurrenceNum:0 caseSensitive:sense];}
  350.  
  351. - replaceRegex:(const char *)regex withChar:(char)aChar occurrenceNum:(int)n
  352. { return [self replaceRegex:regex withChar:aChar occurrenceNum:n caseSensitive:YES]; }
  353.  
  354. - replaceRegex:(const char *)regex withChar:(char)aChar occurrenceNum:(int)n caseSensitive:(BOOL)sense
  355. {
  356.   char str[2];
  357.   if (!aChar) return nil; //or self?  or check for this at all?
  358.   str[1] = 0;
  359.   str[0] = aChar;
  360.   return [self replaceRegex:regex with:str occurrenceNum:n caseSensitive:sense];
  361. }
  362.  
  363. - replaceRegex:(const char *)regex withString:(id)sender
  364. { return [self replaceRegex:regex withString:sender occurrenceNum:0 caseSensitive:YES];}
  365.  
  366. - replaceRegex:(const char *)regex withString:(id)sender caseSensitive:(BOOL)sense
  367. { return [self replaceRegex:regex withString:sender occurrenceNum:0 caseSensitive:sense]; }
  368.  
  369. - replaceRegex:(const char *)regex withString:(id)sender occurrenceNum:(int)n
  370. { return [self replaceRegex:regex withString:sender occurrenceNum:n caseSensitive:YES]; }
  371.  
  372. - replaceRegex:(const char *)regex withString:(id)sender occurrenceNum:(int)n caseSensitive:(BOOL)sense
  373. {
  374.   if (![sender respondsTo:@selector(stringValue)]) return self; //hmmm
  375.   return [self replaceRegex:regex with:[sender stringValue] 
  376.               occurrenceNum:n caseSensitive:sense];
  377. }
  378.  
  379. - (int)matchesRegex:(const char *)regex
  380.  { return [self matchesRegex:regex caseSensitive:YES];}
  381.  
  382. @end