home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-07-19 | 28.3 KB | 1,080 lines |
- //
- // DAYString.m -- a generic class to simplify manipulation of (char *)'s
- // Written by Don Yacktman (c) 1993 by Don Yacktman.
- // Version 1.2 All rights reserved.
- //
- // This is a free object! Contact the author for the latest version.
- // Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604
- // e-mail: Don_Yacktman@byu.edu
- //
- // You may use and copy this class freely as long as you
- // comply with the following terms:
- // (1) Do not remove the author's name or any of the
- // copyright notices from this file.
- // (2) If you redistribute an application which uses this
- // object, you must either include the source code for
- // this object with the application or state in your
- // application's documentation that you (a) use this
- // object and (b) where to obtain the source code for
- // the object.
- // (3) In no way shall the author or his employer(s) be held
- // responsible for any damages caused by what this object
- // does or does not do.
- // (4) You have no warranty whatsoever that this object is
- // good for any purpose at all. If you find it useful
- // for something, consider yourself lucky and leave it at that.
- //
-
- //
- // And now for an implementation note... (philosophy of this class)
- // The object is to make a bulletproof class. That means that NULL
- // pointers and values that are out of range are ignored, or that
- // nil is returned, or that the object tries to do what would be the
- // intelligent thing to do in such boundary cases. Obviously, all
- // this error checking costs you something: speed. On the other hand,
- // your code shouldn't end up with anywhere near as many bus errors
- // and other silliness! (This is not a panacea; don't rely on the
- // DAYString to get everything right for you; if a boudary condition
- // might exist, do be sure to check return values as they can signal
- // problems for you.) There is no substitue for good programming,
- // but hopefully this class will help you survive silly mistakes.
- // Most of the methods send to other methods to do the real work;
- // it would run faster to implement each method separately, since
- // some methods would then not need to do certain checks, and so on.
- // On the other hand, it is a whole lot easier to maintain this code
- // since there are a handful of core methods that do all the work.
- // Someday I may attempt to make a class DAYFastString that will
- // be optimized for speed... but only if there is either (1) a *whole*
- // lot of demand for it, or (2) someone pays me to write it. :-)
- //
- // Returns: I try to follow NeXT conventions. Return self, nil, or
- // whatever makes sense in the context. Hopefully your idea of
- // what makes sense will coincide with mine. Read the docs...
- //
- // For this release (1.2), I have gone over every single method and
- // tried to make sure it behaved sanely on all possible boundary
- // cases, especially NULL pointers. If there is any way to
- // break this class, or cause a bus error in here, etc. or any
- // other bugs I WANT to know about them... be sure to send bug
- // reports to me at Don_Yacktman@byu.edu so I can fix them. I
- // want this class to be bulletproof. Note that there are still
- // a few possibilities to get memory leaks here. I want to fix
- // those, if at all possible, so remind me of the ones you find.
- // I got rid of the most obvious and common ones.
- //
- // One memory leak possibility, and this happens in the test app a
- // little bit: you call a method that returns a new object, but
- // then do something like this:
- // printf("%s", [[aString right:5] stringValue]);
- // A new instance is created, but never freed. I need a way around this,
- // like a method -stringValueAndFree that returns a const char * and
- // then free itself. The trick is that the returned char * is still
- // around. The most elegant solution I can think of is to run the
- // pointer through NXUniqueString() first, and then return it, after
- // freeing the object. This way the pointer is never lost, and you
- // won't really leak memory, but the string space sure will get cluttered.
- // If you have an idea on how to fix this problem, let me know. I like
- // the convenience of the above printf, but do not like the memory leak.
- // You could always to this:
- // temp = [aString right:5];
- // printf("%s", [temp stringValue]);
- // [temp free];
- // But that's a pain in the *ss...
- // Well, send me your suggestions.
- //
-
- #import "DAYString.h"
- #import <strings.h>
-
- @implementation DAYString
-
- - init
- {
- [super init];
- [self setStringOrderTable:NXDefaultStringOrderTable()];
- if (buffer) free(buffer);
- buffer = NULL;
- length = 0;
- _length = 0;
- return self;
- }
-
- - initString:(const char *)aString
- {
- [self init];
- return [self setStringValue:aString];
- }
-
- - setStringOrderTable:(NXStringOrderTable *)table
- {
- if (table) orderTable = table;
- else orderTable = NXDefaultStringOrderTable(); // just in case...
- return self;
- }
-
- - (NXStringOrderTable *)stringOrderTable
- {
- return orderTable;
- }
-
- - allocateBuffer:(int)size
- {
- return [self allocateBuffer:size fromZone:[self zone]];
- }
-
- - allocateBuffer:(int)size fromZone:(NXZone *)zone
- {
- if (size <= _length) return self;
- [self freeString];
- if (!size) return self;
- buffer = (char *)NXZoneMalloc(zone, size);
- buffer[0] = 0;
- _length = size;
-
- return self;
- }
-
- - setStringValue:(const char *)aString
- {
- return [self setStringValue:aString fromZone:[self zone]];
- }
-
- - setStringValue:(const char *)aString fromZone:(NXZone *)zone
- {
- if (!aString) return self; // use -freeString to set to "NULL"
- // Note that I could have used NXCopyStringBufferFromZone() here
- // instead. This works just as well, but it may be slower...
- // I haven't checked that out, though. It might be worth doing.
- [self allocateBuffer:strlen(aString)+1 fromZone:zone];
- strcpy(buffer, aString);
- length = strlen(buffer);
- return self;
- }
-
- - takeStringValue:sender
- { // if no string value, return nil; the user is expecting a changed string
- if (![sender respondsTo:@selector(stringValue)]) return nil;
- return [self setStringValue:[sender stringValue] fromZone:[self zone]];
- }
-
- - takeStringValue:sender fromZone:(NXZone *)zone
- {
- if (![sender respondsTo:@selector(stringValue)]) return nil;
- return [self setStringValue:[sender stringValue] fromZone:zone];
- }
-
- - (const char *)stringValue
- {
- return buffer;
- }
-
- - read:(NXTypedStream *)stream
- {
- int tempLength; char *tBuf;
- [super read:stream];
- NXReadType(stream, "i", &tempLength);
- [self allocateBuffer:tempLength fromZone:[self zone]];
- NXReadType(stream, "*", &tBuf);
- if (tBuf) strcpy(buffer, tBuf);
- if (!tBuf && buffer) buffer[0] = '\0'; // NULL string?
- return self;
- }
-
- - write:(NXTypedStream *)stream
- {
- [super write:stream];
- NXWriteType(stream, "i", &_length);
- NXWriteType(stream, "*", &buffer);
- return self;
- }
-
- - copyFromZone:(NXZone *)zone
- {
- DAYString *myCopy = [super copyFromZone:zone];
- // force child to have it's own copy of the string buffer
- [myCopy _unhookBuffer];
- [myCopy allocateBuffer:_length fromZone:zone];
- [myCopy setStringValue:buffer fromZone:zone];
- return myCopy;
- }
-
- - _unhookBuffer
- { // used by the copy method so that we don't free the buffer from orig.
- buffer = NULL; _length = 0;
- return self;
- }
-
- - freeString
- {
- if(buffer) free(buffer);
- buffer = NULL;
- length = 0;
- _length = 0;
- return self;
- }
-
- - free
- {
- [self freeString];
- return [super free];
- }
-
- - cat:(const char *)aString
- {
- return [self cat:aString n:strlen(aString) fromZone:[self zone]];
- }
-
- - cat:(const char *)aString n:(int)n
- {
- return [self cat:aString n:n fromZone:[self zone]];
- }
-
- - cat:(const char *)aString fromZone:(NXZone *)zone
- {
- return [self cat:aString n:strlen(aString) fromZone:zone];
- }
-
- - cat:(const char *)aString n:(int)n fromZone:(NXZone *)zone
- {
- char *newBuffer; int newSize;
- if (!(aString || buffer)) return nil;
- if (!buffer) return [self setStringValue:aString fromZone:zone];
- if (!aString) return self;
- if (n > strlen(aString)) n = strlen(aString);
- newSize = length + n + 1;
- if (newSize > _length) {
- newBuffer = (char *)NXZoneMalloc(zone, newSize);
- _length = newSize;
- newBuffer[0] = '\0';
- strcat(newBuffer, buffer);
- strncat(newBuffer, aString, n);
- free(buffer);
- buffer = newBuffer;
- } else strncat(buffer, aString, n);
- length = strlen(buffer);
- return self;
- }
-
- - concatenate:sender
- { // note return self here; assume that there's nothing to add...
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self cat:[sender stringValue]
- n:strlen([sender stringValue])
- fromZone:[self zone]];
- }
-
- - concatenate:sender n:(int)n
- {
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self cat:[sender stringValue] n:n fromZone:[self zone]];
- }
-
- - concatenate:sender fromZone:(NXZone *)zone
- {
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self cat:[sender stringValue]
- n:strlen([sender stringValue]) fromZone:zone];
- }
-
- - concatenate:sender n:(int)n fromZone:(NXZone *)zone
- {
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self cat:[sender stringValue] n:n fromZone:zone];
- }
-
- - (const char *)strstr:(const char *)subString
- {
- if (!(buffer||subString)) return NULL;
- return strstr(buffer, subString);
- }
-
- - 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 [[DAYString allocFromZone:[self zone]]
- initString:sub2];
- }
-
- - subStringLeft:subString
- {
- /* Old implementation: ugly and has potential memory leak
- const char *sub;
- char *tempString, *temp2;
- id retVal = [DAYString alloc];
-
- if ([subString respondsTo:@selector(stringValue)])
- sub = [subString stringValue];
- else return nil; // error if can't get string value
- tempString = NXCopyStringBufferFromZone(buffer, [self zone]);
- temp2 = strstr(tempString, sub);
- if (temp2) {
- temp2[0] = '\0'; // terminate it early
- [retVal initString:tempString];
- } else { // substring not found
- return [self copy];
- }
- free(tempString);
- return retVal;
- */
- 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];
- }
-
- - (int)length
- {
- return length;
- }
-
- - (BOOL)isEqual:anObject
- {
- if (anObject == self) return YES;
- // doesn't have to be a DAYString object to be equal...
- if ([anObject respondsTo:@selector(stringValue)]) {
- if (!NXOrderStrings(buffer, [anObject stringValue],
- YES, -1, orderTable)) return YES;
- }
- return NO;
- }
-
- - (int)compareTo:sender
- {
- return [self compareTo:sender n:(-1) caseSensitive:YES];
- }
-
- - (int)compareTo:sender n:(int)n
- {
- return [self compareTo:sender n:n caseSensitive:YES];
- }
-
- - (int)cmp:(const char *)aString
- {
- if (!aString && !buffer) return 0; // both NULL, so "equal"
- if (!aString || !buffer) return -1; // only one is NULL, so not equal
- return strcmp(buffer, aString);
- }
-
- - (int)cmp:(const char *)aString n:(int)n
- {
- if (!aString && !buffer) return 0; // both NULL, so "equal"
- if (!aString || !buffer) return -1; // only one is NULL, so not equal
- // we don't check n; use strncmp's behavior here.
- return strncmp(buffer, aString, n);
- }
-
- - (int)compareTo:sender caseSensitive:(BOOL)sense
- {
- return [self compareTo:sender n:(-1) caseSensitive:sense];
- }
-
- - (int)compareTo:sender n:(int)n caseSensitive:(BOOL)sense
- {
- if (![sender respondsTo:@selector(stringValue)]) return 1; // !=
- if (!buffer && ![sender stringValue]) return 0; // both are NULL
- if (!(buffer && [sender stringValue])) return 1; // one is NULL
- return NXOrderStrings(buffer, [sender stringValue], sense, n, orderTable);
- }
-
- - (int)casecmp:(const char *)aString
- {
- if (!aString && !buffer) return 0; // both NULL, so "equal"
- if (!aString || !buffer) return -1; // only one is NULL, so not equal
- return strcasecmp(buffer, aString);
- }
-
- - (int)casecmp:(const char *)aString n:(int)n
- {
- if (!aString && !buffer) return 0; // both NULL, so "equal"
- if (!aString || !buffer) return -1; // only one is NULL, so not equal
- // we don't check n; use strncasecmp's behavior here.
- return strncasecmp(buffer, aString, n);
- }
-
- - 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;
- if (!buffer) return nil;
- if (count >= length) return [self copyFromZone:zone];
- newString = [[DAYString allocFromZone:zone] init];
- buffer[count] = '\0';
- [newString setStringValue:buffer fromZone:zone];
- buffer[count] = smash;
- return newString;
- }
-
- - right:(int)count fromZone:(NXZone *)zone
- {
- id newString;
- if (!buffer) return nil;
- if (count >= length) return [self copyFromZone:zone];
- newString = [[DAYString allocFromZone:zone] init];
- [newString setStringValue:&buffer[length - count] fromZone:zone];
- return newString;
- }
-
- - midFrom:(int)start to:(int)end fromZone:(NXZone *)zone
- {
- char smash = buffer[end+1];
- 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 = [[DAYString allocFromZone:zone] init];
- buffer[end+1] = '\0'; // inclusive; end-1 is not. (well, end isn't either, anymore -- Carl)
- [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 DAYFastString.
-
- 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 = [[DAYString allocFromZone:zone] init];
- buffer[spot] = '\0';
- [newString setStringValue:&buffer[start] fromZone:zone];
- buffer[spot] = smash;
- return newString;
- */
- }
-
- - encrypt:salt
- { // encrypt as a UNIX password using the DAYString "salt" as the salt...
- // see crypt(3) for more info
- // The cast prevents a warning: -stringValue returns a const char *.
- // Assuming crypt() doesn't change the salt, the cast is OK.
- char *strv;
- if ([salt respondsTo:@selector(stringValue)])
- strv = (char *)[salt stringValue];
- else return nil;
- if (!(buffer && strv)) return nil;
- return [[DAYString alloc] initString:crypt(buffer, strv)];
- }
-
- // NXTransport protocol implementation:
- - encodeUsing:(id <NXEncoding>)portal
- {
- [portal encodeData:&_length ofType:"i"];
- [portal encodeData:&length ofType:"i"];
- [portal encodeData:&buffer ofType:"*"];
- return self;
- }
-
- - decodeUsing:(id <NXDecoding>)portal
- {
- int newLen;
- [self freeString];
- [portal decodeData:&newLen ofType:"i"];
- [self allocateBuffer:newLen];
- [portal decodeData:&length ofType:"i"];
- [portal decodeData:&buffer ofType:"*"];
- return self;
- }
-
- - encodeRemotelyFor:(NXConnection *)connection
- freeAfterEncoding:(BOOL *)flagp isBycopy:(BOOL)isByCopy
- {
- if (isByCopy) {
- *flagp = NO; // object will copy.
- return self; //encode object (copy it)
- }
- *flagp = NO; // object will copy.
- // super will encode the proxy otherwise
- return [super encodeRemotelyFor:connection
- freeAfterEncoding:flagp isBycopy:isByCopy];
- }
-
- // Interface Builder support
- - (const char *)getInspectorClassName { return "DAYStringInspector"; }
- - (NXImage *)getIBImage { return [NXImage findImageNamed:"DAYStringObj"]; }
-
- // some other new methods V1.2 -- don
-
- - extractPart:(int)n useAsDelimiter:(char)c caseSensitive:(BOOL)sense fromZone:(NXZone *)zone
- {
- int left = ((n == DAY_FIRST) ? 0 :
- ( n == DAY_LAST ?
- ([self rspotOf:c occurenceNum:1 caseSensitive:sense] + 1) :
- ([self spotOf:c occurenceNum:(n-1) caseSensitive:sense] + 1)));
- int right = ((n == DAY_FIRST) ?
- ([self spotOf:c occurenceNum:1 caseSensitive:sense] - 1) :
- ( n == DAY_LAST ? length :
- [self spotOf:c occurenceNum:n caseSensitive:sense] - 1));
- 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]];
- }
-
- - fileNameFromZone:(NXZone *)zone
- {
- return [self extractPart:DAY_LAST useAsDelimiter:'/'
- caseSensitive:YES fromZone:[self zone]];
- }
-
- - fileName
- {
- return [self fileNameFromZone:[self zone]];
- }
-
- - pathNameFromZone:(NXZone *)zone
- {
- return [self left:[self rspotOf:'/'] fromZone:zone];
- }
-
- - pathName
- {
- return [self pathNameFromZone:[self zone]];
- }
-
- //Carl's additions....
-
- + newWithString:(const char *)aString
- // I just got tired of typing [[[DAYString alloc] init] setStringValue:xxx]
- {
- id newString = [[DAYString alloc] init]; // should I use [[self alloc]... ?
- if ([newString setStringValue:aString]) return newString;
- [newString free];
- return nil;
- }
-
- - wordNum :(int) num
- // returns a new String containing the numth word in buffer
- // if numth word does not exist, returns nil.
- {
- int i = 0;
- int currword = 1;
- int spot = 0;
- int spot2 = 0;
-
- if (!buffer) return nil;
- while ((currword <= num) && (i <= length)) {
- while ((NXIsSpace(buffer[i])) && (i <= length)) i++;
- spot2 = i;
- while ((!NXIsSpace(buffer[i])) && (i <= length)) i++;
- spot = i;
- currword++;
- }
- if (spot == spot2) return nil;
- return [self midFrom :spot2 length:spot-spot2];
- }
-
- - (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;
- }
-
- - trimLeadSpaces
- { // removes any leading spaces from buffer
- int i = 0;
- id tmpStr;
-
- if (!buffer) return self;
- while (buffer[i] == ' ') i++;
- if (i==0) return self;
- tmpStr = [self right:length-i];
- [self takeStringValue:tmpStr];
- [tmpStr free];
- return self;
- }
-
- - trimTailSpaces
- // removes any trailing spaces from buffer
- {
- int i = length;
- id tmpStr;
-
- if (!buffer) return self;
- while (buffer[i-1] == ' ') i--;
- if (i==length) return self;
- tmpStr = [self left:i];
- [self takeStringValue:tmpStr];
- [tmpStr free];
- return self;
- }
-
-
- - trimSpaces
- // takes off leading and trailing spaces of the buffer
- {
- return [[self trimLeadSpaces] trimTailSpaces];
- }
-
- - reverse
- // reverses the characters in the buffer. If it's a palindrome, you won't
- // notice much of a difference :-)
- {
- char tmp[length+1];
- int j=0;
- int i;
-
- if (length <= 1) return self;
- for (i=length-1;i>=0;i--)
- {
- tmp[j] = buffer[i];
- j++;
- }
- tmp[length] = 0;
- if (length != 0) [self setStringValue:tmp];
- return self;
- }
-
-
- - toUpper
- // converts any lowercase characters in buffer to uppercase
- {
- int i;
-
- for (i=0;i<length;i++)
- {
- if (NXIsLower(buffer[i])) buffer[i] = NXToUpper(buffer[i]);
- }
-
- return self;
- }
-
- - toLower
- // converts any uppercase chars in buffer to lowercase
- {
- int i;
-
- for (i=0;i<length;i++)
- {
- if (NXIsUpper(buffer[i])) buffer[i] = NXToLower(buffer[i]);
- }
-
- return self;
- }
-
- - insert:(const char *)aString at:(int)index
- // inserts given string into buffer starting at index.
- // (the first character is position #0)
- {
- id temp1;
- id temp2;
-
- if ((aString == NULL) || (strlen(aString)<=0)) return self;
- if (index < 0) index = 0;
- if (index >= length) return [self cat:aString];
-
- temp1 = [self left:index];
- temp2 = [self right:length-index];
- [[temp1 cat:aString] concatenate:temp2];
- [self takeStringValue:temp1];
-
- [temp1 free];
- [temp2 free];
- return self;
- }
-
- - insertString:(id)sender at:(int)index
- // cover for insert:at: for a String object
- {
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self insert:[sender stringValue] at:index];
- }
-
- - insertChar:(char)aChar at:(int)index
- {
- id tempStr;
- id retval;
-
- if (aChar == 0) return self; // or should this return nil? DAY: leave it
-
- tempStr = [[[[DAYString alloc] init] allocateBuffer:2] addChar:aChar];
- retval = [self insert:[tempStr stringValue] at:index];
- [tempStr free];
- return retval;
- }
-
- - (char) charAt:(int)index
- {
- if ((index < 0) || (index >length-1)) return 0;
- return (char)buffer[index];
- }
-
- - removeFrom:(int)index length:(int)len
- { // to avoid memory leaks, this should NEVER return nil!
- id temp1,temp2;
-
- if (!buffer) return self; // everything's already gone
- if (len <= 0) return self; // noting to remove
- // DAY: should I presume to fix index<0 like so? or just index = 0?
- // or just return self?
- if (index < 0) { length += index; index = 0; if (len <= 0) return self; }
- if (index > length - 1) return self; // nothing out there
- temp1 = [self left:index];
- temp2 = [self midFrom:index+len to:length];
- [temp1 concatenate:temp2];
- [self takeStringValue:temp1];
- [temp1 free];
- [temp2 free];
- return self;
- }
-
- - removeFrom:(int)start to:(int)end
- {
- return [self removeFrom:start length:end-start+1];
- }
-
- - replaceFrom:(int)start length:(int)len with:(const char *)aString
- {
- return [[self removeFrom:start length:len] insert:aString at:start];
- }
-
- - replaceFrom:(int)start to:(int)end with:(const char *)aString
- {
- return [[self removeFrom:start to:end] insert:aString at:start];
- }
-
- - replaceFrom:(int)start length:(int)len withString:(id)sender
- {
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self replaceFrom:start length:len with:[sender stringValue]];
- }
-
- - replaceFrom:(int)start to:(int)end withString:(id)sender
- {
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self replaceFrom:start to:end with:[sender stringValue]];
- }
-
- - replace:(const char *)subString with:(const char *)newString
- {
- const char *spot;
-
- if (spot = [self strstr:subString])
- [self replaceFrom:(int)(spot-buffer) length:strlen(subString) with:newString];
- return self;
- }
-
- - replace:(const char *)subString withString:(id)sender
- {
- if (![sender respondsTo:@selector(stringValue)]) return self;
- return [self replace:subString with:[sender stringValue]];
- }
-
- - (int)spotOf:(char)aChar
- {
- return [self spotOf:aChar occurenceNum:1 caseSensitive:YES];
- }
-
- - (int)spotOf:(char)aChar caseSensitive:(BOOL)sense
- {
- return [self spotOf:aChar occurenceNum:1 caseSensitive:sense];
- }
-
- - (int)spotOf:(char)aChar occurenceNum:(int)n
- {
- return [self spotOf:aChar occurenceNum:n caseSensitive:YES];
- }
-
- - (int)rspotOf:(char)aChar
- {
- return [self rspotOf:aChar occurenceNum:1 caseSensitive:YES];
- }
-
- - (int)rspotOf:(char)aChar caseSensitive:(BOOL)sense
- {
- return [self rspotOf:aChar occurenceNum:1 caseSensitive:sense];
- }
-
- - (int)rspotOf:(char)aChar occurenceNum:(int)n
- {
- return [self rspotOf:aChar occurenceNum:n caseSensitive:YES];
- }
-
- - (int)spotOf:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
- {
- int currnum = 0;
- int count = 0;
-
- if (n<1) return -1;
-
- while ((currnum < n) && (count<length)) {
- if (!sense) {
- if (NXToUpper(buffer[count]) == NXToUpper(aChar)) currnum++;
- }
- else {
- if (buffer[count] == aChar) currnum++;
- }
- count++;
- }
- if (currnum != n) return -1;
- return (count-1);
- }
-
- - (int)rspotOf:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
- {
- int currnum = 0;
- int count = length-1;
-
- if (n<1) return -1;
-
- while ((currnum < n) && (count >= 0)) {
- if (!sense) {
- if (NXToUpper(buffer[count]) == NXToUpper(aChar)) currnum++;
- }
- else {
- if (buffer[count] == aChar) currnum++;
- }
- count--;
- }
- if (currnum != n) return -1;
- return (count+1);
- }
-
- - addChar:(char)aChar
- {
- if (aChar) [self cat:&aChar n:1];
- return self;
- }
-
- - squashSpaces
- {
- int count = 0;
- id tempStr;
-
- if (!buffer) return self;
- [self trimSpaces];
- tempStr = [[[DAYString alloc] init] allocateBuffer:length];
- while (count<length) {
- while (buffer[count]!=' ') {
- [tempStr addChar:buffer[count]];
- count++;
- }
- if ((count<length) && (buffer[count] == ' ')) {
- [tempStr addChar:buffer[count]];
- count++;
- }
- if ((count<length) && (buffer[count]==' ') &&
- ((buffer[count-2] == ':') || (buffer[count-2] =='.'))) {
- [tempStr addChar:buffer[count]];
- count++;
- }
- while (buffer[count]==' ') count++;
- }
- [self takeStringValue:tempStr];
- [tempStr free];
- return self;
- }
-
- - (const char *)rindex:(char)aChar
- {
- return [self rindex:aChar occurenceNum:1 caseSensitive:YES];
- }
-
- - (const char *)rindex:(char)aChar occurenceNum:(int)n
- {
- return [self rindex:aChar occurenceNum:n caseSensitive:YES];
- }
-
- - (const char *)rindex:(char)aChar caseSensitive:(BOOL)sense
- {
- return [self rindex:aChar occurenceNum:1 caseSensitive:sense];
- }
-
- - (const char *)rindex:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
- {
- int num;
- num = [self rspotOf:aChar occurenceNum:n caseSensitive:sense];
- if (num == -1) return NULL;
- return buffer+num;
- }
-
- - (const char *)index:(char)aChar
- {
- return [self index:aChar occurenceNum:1 caseSensitive:YES];
- }
-
- - (const char *)index:(char)aChar occurenceNum:(int)n
- {
- return [self index:aChar occurenceNum:n caseSensitive:YES];
- }
-
- - (const char *)index:(char)aChar caseSensitive:(BOOL)sense
- {
- return [self index:aChar occurenceNum:1 caseSensitive:sense];
- }
-
- - (const char *)index:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
- {
- int num;
- num = [self spotOf:aChar occurenceNum:n caseSensitive:sense];
- if (num==-1) return NULL;
- return buffer+num;
- }
-
- - (int)endcmp:(const char *)aString
- {
- return [self endcmp:aString n:-1];
- }
-
- - (int)endcmp:(const char *)aString n:(int)n
- {
- id tempStr;
- int retval;
- if (!aString) return -1;
- tempStr = [DAYString newWithString:aString];
- retval = [self endCompareTo:tempStr n:n caseSensitive:YES];
- [tempStr free];
- return retval;
- }
-
- - (int)endcasecmp:(const char *)aString
- {
- return [self endcasecmp:aString n:-1];
- }
-
- - (int)endcasecmp:(const char *)aString n:(int)n
- {
- id tempStr;
- int retval;
- if (!aString) return -1;
- tempStr = [DAYString newWithString:aString];
- retval = [self endCompareTo:tempStr n:n caseSensitive:NO];
- [tempStr free];
- return retval;
- }
-
- - (int)endCompareTo:(id)sender
- {
- return [self endCompareTo:sender n:-1 caseSensitive:YES];
- }
-
- - (int)endCompareTo:(id)sender caseSensitive:(BOOL)sense
- {
- return [self endCompareTo:sender n:-1 caseSensitive:sense];
- }
-
- - (int)endCompareTo:(id)sender n:(int)n
- {
- return [self endCompareTo:sender n:n caseSensitive:YES];
- }
-
- - (int)endCompareTo:(id)sender n:(int)n caseSensitive:(BOOL)sense
- { // DAY: fixed it: sender doesn't have to be a DAYString anymore.
- id tempStr, temp2, temp3,
- smaller = ((length >= [sender length]) ? sender : self);
- int retval, smallLen;
- if (!([sender respondsTo:@selector(stringValue)])) return -1;
- if (![sender stringValue] || !buffer) return -1;
- if ([sender respondsTo:@selector(length)]) smallLen = [smaller length];
- else smallLen = strlen([smaller stringValue]);
- if ((n == -1) || (n > smallLen)) n = smallLen;
- tempStr = [self right:n];
- if ([sender respondsTo:@selector(right:)]) temp2 = [sender right:n];
- else {
- temp3 = [DAYString newWithString:[sender stringValue]];
- temp2 = [temp3 right:n];
- [temp3 free];
- }
- retval = [tempStr compareTo:temp2 caseSensitive:sense];
- [tempStr free];
- [temp2 free];
- return retval;
- }
-
- - replaceCharAt:(int)index with:(char)aChar
- { // no change if index is out of range.
- // Other option is to limit index to be in range. which is better?
- if ((index < 0) || (index >= length)) return self;
-
- if (aChar == 0) return self; // should this return nil? DAY: leave it.
-
- buffer[index] = aChar;
- return self;
- }
-
- - replaceFrom:(int)start length:(int)len withChar:(char)aChar
- {
- id retval,tempStr;
-
- if (aChar == 0) return nil;
- tempStr = [[[DAYString alloc] init] addChar:aChar];
- retval = [self replaceFrom:start length:len
- withString:tempStr];
- [tempStr free];
- return retval;
- }
-
- - replaceFrom:(int)start to:(int)end withChar:(char)aChar
- {
- return [self replaceFrom:start length:end-start+1 withChar:aChar];
- }
-
- @end
-