This is MiscStringFields.m in view mode; [Download] [Up]
//
// MiscStringFields.m
// Written by Don Yacktman Copyright (c) 1993 by Don Yacktman.
// 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(Fields)
// This category is composed of methods which locate specific fields
// within a MiscString. Mostly useful for various types of parsing.
- subStringRight:subString
{
const char *sub, *sub2;
if (!buffer) return nil;
if ([subString respondsTo:@selector(stringValue)])
sub = [subString stringValue];
else return nil; // error if can't get string value
if (!sub) return nil;
sub2 = strstr(buffer,sub);
if (!sub2) return nil;
return [[[self class] allocFromZone:[self zone]]
initString:sub2];
}
- subStringLeft:subString
{
const char *sub;
char *sub2;
int spot;
if ([subString respondsTo:@selector(stringValue)])
sub = [subString stringValue];
else return nil; // error if can't get string value
if (!sub) return nil;
if (!(sub2 = strstr(buffer, sub))) return nil;
spot = (int)sub2 - (int)buffer - 1;
if (spot < 0) return nil;
return [self midFrom:0 to:spot];
}
- left:(int)count
{
return [self left:count fromZone:[self zone]];
}
- right:(int)count
{
return [self right:count fromZone:[self zone]];
}
- midFrom:(int)start to:(int)end
{
return [self midFrom:start to:end fromZone:[self zone]];
}
- midFrom:(int)start length:(int)len
{
return [self midFrom:start length:len fromZone:[self zone]];
}
- left:(int)count fromZone:(NXZone *)zone
{
char smash = buffer[count];
id newString;
// perhaps I should return an empty MiscString here rather than nil?
if ((count <= 0) || !buffer) return nil;
if (count >= length) return [self copyFromZone:zone];
newString = [[[self class] allocFromZone:zone] init];
buffer[count] = '\0';
[newString setStringValue:buffer fromZone:zone];
buffer[count] = smash;
return newString;
}
- right:(int)count fromZone:(NXZone *)zone
{
id newString;
// perhaps I should return an empty MiscString here rather than nil?
if ((count <= 0) || !buffer) return nil;
if (count >= length) return [self copyFromZone:zone];
newString = [[[self class] allocFromZone:zone] init];
[newString setStringValue:&buffer[length - count] fromZone:zone];
return newString;
}
- midFrom:(int)start to:(int)end fromZone:(NXZone *)zone
{
char smash;
id newString;
if (!buffer) return nil;
if ((end < 0) || (start >= length)) return nil;
if (end >= length) end = length - 1;
if (start < 0) start = 0;
newString = [[[self class] allocFromZone:zone] init];
smash = buffer[end+1];
buffer[end+1] = '\0'; // inclusive; end is not.
[newString setStringValue:&buffer[start] fromZone:zone];
buffer[end+1] = smash;
return newString;
}
- midFrom:(int)start length:(int)len fromZone:(NXZone *)zone
{
return [self midFrom:start to:(start + len - 1) fromZone:zone];
/* faster to have our own code here, but rather than maintain this,
we use the cover above. I'm keeping the code around in case I
decide to do MiscFastString.
register int spot = start + len;
char smash = buffer[spot];
id newString;
if (!buffer) return nil;
if ((end < 0) || (start >= length)) return nil;
if (start < 0) start = 0;
if (start+len-1 >= length) len = length-start;
newString = [[[self class] allocFromZone:zone] init];
buffer[spot] = '\0';
[newString setStringValue:&buffer[start] fromZone:zone];
buffer[spot] = smash;
return newString;
*/
}
- extractPart:(int)n useAsDelimiter:(char)c caseSensitive:(BOOL)sense fromZone:(NXZone *)zone
{ // code has to return an empty object if field is empty, but nil if
// requested field # is > # of fields
// What we will do is use the midFrom:to: to grab a segment of the
// string, and so we need to know where the left and right bounds
// are on the field in question.
int left, right;
switch (n) {
case MISC_STRING_FIRST : {
left = 0;
right = [self spotOf:c occurrenceNum:0 caseSensitive:sense] - 1;
if (right < -1) return [self copy]; // only one field
if (right == -1) // field is empty
return [[self class] newWithString:""];
break;
}
case MISC_STRING_LAST : {
left = [self rspotOf:c occurrenceNum:0 caseSensitive:sense] + 1;
if (left < 1) return [self copy]; // only one field
right = length - 1;
if (right < left) // field is empty
return [[self class] newWithString:""];
break;
}
default : { // won't work for field #0, but MISC_STRING_FIRST == 0
left = [self spotOf:c occurrenceNum:(n-1) caseSensitive:sense] + 1;
right = [self spotOf:c occurrenceNum:n caseSensitive:sense] - 1;
if (left >= 1) { // if left == 0, then no field, so leave right
// at -1 to trigger midFrom:to:fromZone: to return nil.
if (right == -2) right = length - 1; // if it's the last field
if (right < left) // field is empty
return [[self class] newWithString:""];
}
}
}
return [self midFrom:left to:right fromZone:zone];
}
- extractPart:(int)n useAsDelimiter:(char)c caseSensitive:(BOOL)sense
{
return [self extractPart:n useAsDelimiter:c
caseSensitive:sense fromZone:[self zone]];
}
- extractPart:(int)n useAsDelimiter:(char)c fromZone:(NXZone *)zone
{
return [self extractPart:n useAsDelimiter:c
caseSensitive:YES fromZone:zone];
}
- extractPart:(int)n useAsDelimiter:(char)c
{
return [self extractPart:n useAsDelimiter:c
caseSensitive:YES fromZone:[self zone]];
}
- wordNum:(int)num
{
return [self wordNum:num fromZone:[self zone]];
}
- wordNum:(int) num fromZone:(NXZone *)zone
// returns a new String containing the numth word in buffer
// if numth word does not exist, returns nil.
{
int i = 0;
int currword = 0;
int spot = 0;
int spot2 = 0;
if (!buffer) return nil;
while ((i < length) && (currword <= num)) {
while ((i < length) && (NXIsSpace(buffer[i]))) i++;
spot2 = i;
while ((i < length) && (!NXIsSpace(buffer[i]))) i++;
spot = i;
currword++;
}
if ((spot == spot2) || (num != (currword-1))) return nil;
return [self midFrom:spot2 length:spot-spot2 fromZone:zone];
}
- (int)numWords
{ //counts the number of words in buffer.
int i=0;
int currword = 0;
if (!buffer) return 0;
while (i <= length) {
while ((NXIsSpace(buffer[i])) && (i <= length)) i++;
while ((!NXIsSpace(buffer[i])) && (i <= length)) i++;
currword++;
}
if (NXIsSpace(buffer[length-1])) currword--;
return currword;
}
- tokenize:(const char *)breakChars into:aList
// Uses strtok() on a COPY of the strings contents to break the string
// into tokens. Fills aList with MiscString objects containing (in order)
// the tokens from strtok. If aList is nil a list is allocated, but the
// caller is still responsible for freeing it. This is a "safe" usage of
// strtok() (although not "thread-safe"). strtok() relies on static
// variables to keep track of where it is in between calls. This is
// "safe" in that we parse the whole thing before returning, so no one
// in this thread will start parsing another string with strtok() while
// we're in the middle of ours.
{
char *toker, *token;
// Allocate the list if we need to.
if (aList == nil) {
aList = [[List allocFromZone:[self zone]] init];
}
// Copy our contents into a scratch buffer.
NX_MALLOC(toker, char, length+1);
strcpy(toker, buffer);
// Start toking on it.
token = strtok(toker, breakChars);
while (token) {
[aList addObject:[[[self class] allocFromZone:[self zone]]
initString:token]];
token = strtok(NULL, breakChars);
}
// As you allocate, so shall ye free.
NX_FREE(toker);
return aList;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.