home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1994 June / NEBULA_SE.ISO / SourceCode / MiscKit / Source / MiscStringFields.m < prev    next >
Encoding:
Text File  |  1994-02-06  |  7.6 KB  |  267 lines

  1. //
  2. //    MiscStringFields.m
  3. //        Written by Don Yacktman (c) 1993 by Don Yacktman.
  4. //                Version 1.7  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(Fields)
  16.  
  17. // This category is composed of methods which locate specific fields
  18. // within a MiscString.  Mostly useful for various types of parsing.
  19.  
  20. - subStringRight:subString
  21. {
  22.     const char *sub, *sub2;
  23.     if (!buffer) return nil;
  24.     if ([subString respondsTo:@selector(stringValue)])
  25.         sub = [subString stringValue];
  26.     else return nil;    // error if can't get string value
  27.     if (!sub) return nil;
  28.     sub2 = strstr(buffer,sub);
  29.     if (!sub2) return nil;
  30.     return [[[self class] allocFromZone:[self zone]]
  31.             initString:sub2];
  32. }
  33.  
  34. - subStringLeft:subString
  35. {
  36.     const char *sub;
  37.     char *sub2;
  38.     int spot;
  39.  
  40.     if ([subString respondsTo:@selector(stringValue)])
  41.         sub = [subString stringValue];
  42.     else return nil;    // error if can't get string value
  43.     if (!sub) return nil;
  44.     if (!(sub2 = strstr(buffer, sub))) return nil;
  45.     spot = (int)sub2 - (int)buffer - 1;
  46.     if (spot < 0) return nil;
  47.     return [self midFrom:0 to:spot];
  48. }
  49.  
  50. - left:(int)count
  51. {
  52.     return [self left:count fromZone:[self zone]];
  53. }
  54.  
  55. - right:(int)count
  56. {
  57.     return [self right:count fromZone:[self zone]];
  58. }
  59.  
  60. - midFrom:(int)start to:(int)end
  61. {
  62.     return [self midFrom:start to:end fromZone:[self zone]];
  63. }
  64.  
  65. - midFrom:(int)start length:(int)len
  66. {
  67.     return [self midFrom:start length:len fromZone:[self zone]];
  68. }
  69.  
  70. - left:(int)count fromZone:(NXZone *)zone
  71. {
  72.     char smash = buffer[count];
  73.     id newString;
  74.     // perhaps I should return an empty MiscString here rather than nil?
  75.     if ((count <= 0) || !buffer) return nil;
  76.     if (count >= length) return [self copyFromZone:zone];
  77.     newString = [[[self class] allocFromZone:zone] init];
  78.     buffer[count] = '\0';
  79.     [newString setStringValue:buffer fromZone:zone];
  80.     buffer[count] = smash;
  81.     return newString;
  82. }
  83.  
  84. - right:(int)count fromZone:(NXZone *)zone
  85. {
  86.     id newString;
  87.     // perhaps I should return an empty MiscString here rather than nil?
  88.     if ((count <= 0) || !buffer) return nil;
  89.     if (count >= length) return [self copyFromZone:zone];
  90.     newString = [[[self class] allocFromZone:zone] init];
  91.     [newString setStringValue:&buffer[length - count] fromZone:zone];
  92.     return newString;
  93. }
  94.  
  95. - midFrom:(int)start to:(int)end fromZone:(NXZone *)zone
  96. {
  97.     char smash;
  98.     id newString;
  99.     if (!buffer) return nil;
  100.     if ((end < 0) || (start >= length)) return nil;
  101.     if (end >= length) end = length - 1;
  102.     if (start < 0) start = 0;
  103.     newString = [[[self class] allocFromZone:zone] init];
  104.     smash = buffer[end+1];
  105.     buffer[end+1] = '\0'; // inclusive; end is not.
  106.     [newString setStringValue:&buffer[start] fromZone:zone];
  107.     buffer[end+1] = smash;
  108.     return newString;
  109. }
  110.  
  111. - midFrom:(int)start length:(int)len fromZone:(NXZone *)zone
  112. {
  113.     return [self midFrom:start to:(start + len - 1) fromZone:zone];
  114. /*    faster to have our own code here, but rather than maintain this,
  115.     we use the cover above.  I'm keeping the code around in case I
  116.     decide to do MiscFastString.
  117.     
  118.     register int spot = start + len;
  119.     char smash = buffer[spot];
  120.     id newString;
  121.     if (!buffer) return nil;
  122.     if ((end < 0) || (start >= length)) return nil;
  123.     if (start < 0) start = 0;
  124.     if (start+len-1 >= length) len = length-start;
  125.     newString = [[[self class] allocFromZone:zone] init];
  126.     buffer[spot] = '\0';
  127.     [newString setStringValue:&buffer[start] fromZone:zone];
  128.     buffer[spot] = smash;
  129.     return newString;
  130. */
  131. }
  132.  
  133. - extractPart:(int)n useAsDelimiter:(char)c caseSensitive:(BOOL)sense fromZone:(NXZone *)zone
  134. {    // code has to return an empty object if field is empty, but nil if
  135.     // requested field # is > # of fields
  136.  
  137.     // What we will do is use the midFrom:to: to grab a segment of the
  138.     // string, and so we need to know where the left and right bounds
  139.     // are on the field in question.
  140.     int left, right;
  141.     switch (n) {
  142.         case MISC_STRING_FIRST : {
  143.             left = 0;
  144.             right = [self spotOf:c occurrenceNum:0 caseSensitive:sense] - 1;
  145.             if (right < -1) return [self copy]; // only one field
  146.             if (right == -1) // field is empty
  147.                 return [[self class] newWithString:""];
  148.             break;
  149.         }
  150.         case MISC_STRING_LAST : {
  151.             left = [self rspotOf:c occurrenceNum:0 caseSensitive:sense] + 1;
  152.             if (left < 1) return [self copy]; // only one field
  153.             right = length - 1;
  154.             if (right < left) // field is empty
  155.                 return [[self class] newWithString:""];
  156.             break;
  157.         }
  158.         default : { // won't work for field #0, but MISC_STRING_FIRST == 0
  159.             left = [self spotOf:c occurrenceNum:(n-1) caseSensitive:sense] + 1;
  160.             right = [self spotOf:c occurrenceNum:n caseSensitive:sense] - 1;
  161.             if (left >= 1) { // if left == 0, then no field, so leave right
  162.                 // at -1 to trigger midFrom:to:fromZone: to return nil.
  163.                 if (right == -2) right = length - 1; // if it's the last field
  164.                 if (right < left) // field is empty
  165.                     return [[self class] newWithString:""];
  166.             }
  167.         }
  168.     }
  169.     return [self midFrom:left to:right fromZone:zone];
  170. }
  171.  
  172. - extractPart:(int)n useAsDelimiter:(char)c caseSensitive:(BOOL)sense
  173. {
  174.     return [self extractPart:n useAsDelimiter:c
  175.             caseSensitive:sense fromZone:[self zone]];
  176. }
  177.  
  178. - extractPart:(int)n useAsDelimiter:(char)c fromZone:(NXZone *)zone
  179. {
  180.     return [self extractPart:n useAsDelimiter:c
  181.             caseSensitive:YES fromZone:zone];
  182. }
  183.  
  184. - extractPart:(int)n useAsDelimiter:(char)c
  185. {
  186.     return [self extractPart:n useAsDelimiter:c
  187.             caseSensitive:YES fromZone:[self zone]];
  188. }
  189.  
  190. - wordNum:(int)num
  191. {
  192.     return [self wordNum:num fromZone:[self zone]];
  193. }
  194.  
  195. - wordNum:(int) num fromZone:(NXZone *)zone
  196. // returns a new String containing the numth word in buffer
  197. // if numth word does not exist, returns nil.
  198. {
  199.     int i = 0;
  200.     int currword = 0;
  201.     int spot = 0; 
  202.     int spot2 = 0;
  203.  
  204.     if (!buffer) return nil;
  205.     while ((currword <= num) && (i <= length)) {
  206.         while ((NXIsSpace(buffer[i])) && (i <= length)) i++;
  207.         spot2 = i;
  208.         while ((!NXIsSpace(buffer[i])) && (i <= length)) i++;
  209.         spot = i;
  210.         currword++;
  211.     }
  212.     if (spot == spot2) return nil;
  213.     return [self midFrom:spot2 length:spot-spot2 fromZone:zone];
  214. }
  215.  
  216. - (int)numWords
  217. { //counts the number of words in buffer.
  218.     int i=0;
  219.     int currword = 0;
  220.  
  221.     if (!buffer) return 0;  
  222.     while (i <= length) {
  223.         while ((NXIsSpace(buffer[i])) && (i <= length)) i++;
  224.         while ((!NXIsSpace(buffer[i])) && (i <= length)) i++;
  225.         currword++;
  226.     }
  227.     if (NXIsSpace(buffer[length-1])) currword--;
  228.     return currword;
  229. }
  230.  
  231. - tokenize:(const char *)breakChars into:aList
  232. // Uses strtok() on a COPY of the strings contents to break the string
  233. // into tokens.  Fills aList with MiscString objects containing (in order) 
  234. // the tokens from strtok.  If aList is nil a list is allocated, but the 
  235. // caller is still responsible for freeing it.  This is a "safe" usage of 
  236. // strtok() (although not "thread-safe").  strtok() relies on static 
  237. // variables to keep track of where it is in between calls.  This is
  238. // "safe" in that we parse the whole thing before returning, so no one
  239. // in this thread will start parsing another string with strtok() while
  240. // we're in the middle of ours.
  241. {
  242.     char *toker, *token;
  243.  
  244.     // Allocate the list if we need to.
  245.     if (aList == nil)  {
  246.         aList = [[List allocFromZone:[self zone]] init];
  247.     }
  248.  
  249.     // Copy our contents into a scratch buffer.
  250.     NX_MALLOC(toker, char, length+1);
  251.     strcpy(toker, buffer);
  252.     
  253.     // Start toking on it.
  254.     token = strtok(toker, breakChars);
  255.     while (token)  {
  256.         [aList addObject:[[[self class] allocFromZone:[self zone]] 
  257.                     initString:token]];
  258.         token = strtok(NULL, breakChars);
  259.     }
  260.     
  261.     // As you allocate, so shall ye free.
  262.     NX_FREE(toker);    
  263.     return aList;
  264. }
  265.  
  266. @end
  267.