// MOStringParsing.m
//
// by Mike Ferris
// Part of MOKit
// Copyright 1993, all rights reserved.

// ABOUT MOKit
// by Mike Ferris (mike@lorax.com)
//
// MOKit is a collection of useful and general objects.  Permission is 
// granted by the author to use MOKit in your own programs in any way 
// you see fit.  All other rights pertaining to the kit are reserved by the 
// author including the right to sell these objects as objects,  as part 
// of a LIBRARY, or as SOURCE CODE.  In plain English, I wish to retain 
// rights to these objects as objects, but allow the use of the objects 
// as pieces in a fully functional program.  Permission is also granted to 
// redistribute the source code of MOKit for FREE as long as this copyright 
// notice is left intact and unchanged.  NO WARRANTY is expressed or implied.  
// The author will under no circumstances be held responsible for ANY 
// consequences from the use of these objects.  Since you don't have to pay 
// for them, and full source is provided, I think this is perfectly fair.

#import "MOKit/MOString.h"

extern char *MOBuildStringFromFormatV(const char *formatStr, 
			va_list param_list);

@implementation MOString(Parsing)

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-= Parsing MOStrings =-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

- (char)charAt:(int)index
// Returns the character at index.  Returns '\0' if str is NULL or index
// is beyond the end of the str.
{
	if ((index<0) || (isUnique?!uStr:!str) || (index>=len))  {
		return '\0';
	}
	return (isUnique?uStr[index]:str[index]);
}

- substringFrom:(int)start to:(int)end
// Returns a new String object containing the specified substring of the
// receiver's contents.  Returns nil if the indexes are bad or the receiving
// String is empty.  The new string is NOT unique regardless of whether the
// receiver is or not.
//
// Note that the string object returned is always the receiving object's 
// class even if the receiving object is a subclass!
{
	id newStr;
	char *buf;
	int i,j;
	
	if ((start<0) || (end<0) || (start>end) || (isUnique?!uStr:!str) || 
				(end>len))  {
		return nil;
	}
	NX_MALLOC(buf, char, end-start+2);
	for (i=start, j=0; i<=end; i++, j++)  {
		buf[j]=(isUnique?uStr[i]:str[i]);
	}
	buf[j]='\0';
	newStr = [[[self class] allocFromZone:[self zone]] 
				initStringValueNoCopy:buf shouldFree:YES];
	return newStr;
}

- (int)positionOf:(char)aChar nthOccurrence:(int)n
// Returns the index of the nth occurrence of aChar in the string.
// If n is negative it counts backwards from the end of the string.
// If there is no nth occurrence of aChar or the string is NULL this
// returns -1.  This won't work for aChar=='\0' unless n==1.
{
	int numFound=0, i;
	BOOL fromBack = (n<0);
	
	if ((isUnique?!uStr:!str) || (n==0))  {
		return -1;
	}
	
	n = abs(n);
	for (i=(fromBack?len:0); (fromBack?i>=0:i<=len); (fromBack?i--:i++))  {
		if ((isUnique?uStr[i]:str[i])==aChar)  {
			numFound++;
			if (numFound==n)  {
				return i;
			}
		}
	}
	return -1;
}

- (int)countOccurrencesOf:(char)aChar
{
	int i;
	int count = 0;
	for (i=0; i<len; i++)  {
		if ((isUnique?uStr:str)[i] == aChar)  {
			count++;
		}
	}
	return count;
}

- tokenize:(const char *)breakChars into:aList
// Uses strtok() on a COPY of the strings contents to break the string
// into tokens.  Fills aList with MOString 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 damn thing before returning, so no one
// in this thread will start parsing another string with strtok() while
// we're in the middle of ours.
//
// Note that the string object returned is always the receiving object's 
// class even if the receiving object is a subclass!
{
	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, len+1);
	strcpy(toker, (isUnique?uStr:str));
	
	// Start toking on it.
	token = strtok(toker, breakChars);
	while (token)  {
		[aList addObject:[[[self class] allocFromZone:[self zone]] 
					initStringValue:token]];
		token = strtok(NULL, breakChars);
	}
	
	// As you allocate, so shall ye free.
	NX_FREE(toker);	
	return aList;
}

@end

