/* 
   NSString.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <ctype.h>

#include <Foundation/common.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSCoder.h>
#include <Foundation/NSException.h>
#include <Foundation/exceptions/StringExceptions.h>
#include <Foundation/exceptions/GeneralExceptions.h>

#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSString.h>
#include <Foundation/NSConcreteString.h>

/* 

General ideas in NSString class cluster implementation

The NSString class is an abstract class that uses only unicode representation
and handles in an inefficient manner since it operates on abstract strings
accessing characters one by one using characterAtIndex: method.

Currently there is only one abstract subclass of classes designed to implement
ANSI C strings in the default encoding provided by the operating system 
(assumed ASCII or ASCII extension like NEXTSTEP). This type of strings are called NS8BitString since they uses characters (usually represented on 8 bits)
for immutable classes and NSMutable8BitString for mutable ones respectively. 
This tree of classes is implemented based on `__compact8BitBytes' method
that returns a pointer to the internal representation of the string as char[],
with all characters compact, with no gap.

Unicode and encodings will be supported in the future when I will know
how to handle the following (If you know about these please send me a message)
	- convert between different encodings
	- convert characters to upper/lower cases
	- handle composed character sequences

So for the time being only NS8BitString/NSMutable8BitString are used but their
use in the future will be limited to ...WithCString... methods and the strings
generated by the compiler with @"" construct (which should generate unicode 
ones some day in the future).

There is another problem: methods like initWithCharacters... and 
initWithCString assume that the underlying support is 16Bit respectively 8Bit
but it is sent to an instance allocated with [NSString alloc]. So we make 
NSString alloc method to return a temporary string that will allocate and 
initialize the concrete class that will hold the character in initWith... 
methods. This class is NSTemporaryString. Thus one must use constructs like
	var = [[NSString alloc] initWith...]
	var = [NSString stringWith...]
but not
	var = [NSString alloc]
	[var initWith...]
since the second line will return an instance different from the one in var.

*/

/***************************
 * NSString abstract class
 ***************************/

@implementation NSString

+ (void)initialize
{
    /* Force linking of NSString categories (path utilities) */
    NSUserName();
}

/* Getting a string's length */

- (unsigned int)length
{
    [self subclassResponsibility:_cmd];
    return 0;
}

/* Accessing characters	*/

- (unichar)characterAtIndex:(unsigned int)index
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (void)getCharacters:(unichar*)buffer
{
    NSRange range = {0, [self length]};
    [self getCharacters:buffer range:range];
}

- (void)getCharacters:(unichar*)buffer range:(NSRange)aRange
{
    int i = 0;
    IMP imp = [self methodForSelector:@selector(characterAtIndex:)];

    if (aRange.location + aRange.length > [self length])
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, [self length]]);
    
    for (i = 0; i < aRange.length; i++)
	buffer[i] = (unichar)(int)(*imp)(self, @selector(characterAtIndex:), 
	    aRange.location + i);
}

/* Combining strings */	

- (NSString*)stringByAppendingString:(NSString*)aString
{
    NSMutableString *string = [self mutableCopy];

    [string appendString:aString];
    return [string autorelease];
}

- (NSString*)stringByAppendingFormat:(NSString*)format,...
{
    NSMutableString *string = [self mutableCopy];
    va_list va;
    
    va_start(va, format);
    [string appendFormat:format arguments:va];
    va_end(va);
    return string;
}

- (NSString*)stringByAppendingFormat:(NSString*)format 
  arguments:(va_list)argList
{
    NSMutableString *string = [self mutableCopy];

    [string appendFormat:format arguments:argList];
    return [string autorelease];
}

- (NSString*)stringByPrependingString:(NSString*)aString
{
    return [aString stringByAppendingString:self];
}

- (NSString*)stringByPrependingFormat:(NSString*)format,...
{
    NSString *string;
    va_list va;

    va_start(va, format);
    string = Avsprintf(format, va);
    va_end(va);

    return [string stringByAppendingString:self];
}

- (NSString*)stringByPrependingFormat:(NSString*)format 
  arguments:(va_list)argList
{
    NSString *string = Avsprintf(format, argList);

    return [string stringByAppendingString:self];
}

/* Dividing strings */

- (NSArray*)componentsSeparatedByString:(NSString*)separator
{
    int first = 0, last = 0;
    int slen = [separator length];
    NSMutableArray* components = [NSMutableArray new];

    while((first = [self indexOfString:separator fromIndex:last])
	    != NSNotFound) {
	NSRange range = {last, first - last};

	[components addObject:[self substringWithRange:range]];
	last = first + slen;
    }
    
    if([self length] >= last) {
	NSString* lastComponent = [self substringFromIndex:last];
	[components addObject:lastComponent];
    }
    return [components autorelease];
}

- (NSString*)substringWithRange:(NSRange)aRange
{
    // UNICODE
    [self subclassResponsibility:_cmd];
    return nil;
}

- (NSString*)substringFromIndex:(unsigned int)index
{
    NSRange range = {index, [self length] - index};

    return [self substringWithRange:range];
}

- (NSString*)substringToIndex:(unsigned int)index
{
    NSRange range = {0, index};

    return [self substringWithRange:range];
}

/* Finding characters and substrings */

- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet
{
    NSRange range = {0, [self length]};

    return [self rangeOfCharacterFromSet:aSet options:0 range:range];
}

- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet
  options:(unsigned int)mask
{
    NSRange range = {0, [self length]};

    return [self rangeOfCharacterFromSet:aSet options:mask range:range];
}

- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet
  options:(unsigned int)mask range:(NSRange)aRange
{
    // UNICODE
    NSRange range = {0, 0};
    [self subclassResponsibility:_cmd];
    return range;
}

- (NSRange)rangeOfString:(NSString*)string
{
    NSRange range = {0, [self length]};

    return [self rangeOfString:string options:0 range:range];
}

- (NSRange)rangeOfString:(NSString*)string options:(unsigned int)mask
{
    NSRange range = {0, [self length]};

    return [self rangeOfString:string options:mask range:range];
}

- (NSRange)rangeOfString:(NSString*)aString
  options:(unsigned int)mask range:(NSRange)aRange
{
    // UNICODE 
    // ENCODINGS
    NSRange range = {0, 0};
    [self subclassResponsibility:_cmd];
    return range;
}

- (unsigned int)indexOfString:(NSString*)substring
{
    NSRange range = {0, [self length]};
    
    range = [self rangeOfString:substring options:0 range:range];
    return range.length ? range.location : NSNotFound;
}

- (unsigned int)indexOfString:(NSString*)substring fromIndex:(unsigned)index
{
    NSRange range = {index, [self length]-index};
    
    range = [self rangeOfString:substring options:0 range:range];
    return range.length ? range.location : NSNotFound;
}

- (unsigned int)indexOfString:(NSString*)substring range:(NSRange)range
{
    range = [self rangeOfString:substring options:0 range:range];
    return range.length ? range.location : NSNotFound;
}

/* Determining composed character sequences */

- (NSRange)rangeOfComposedCharacterSequenceAtIndex:(unsigned int)anIndex
{
    NSRange range;
    int length = [self length];
    
    if (anIndex >= length)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"index %d out of range in string %x of length %d",
	    	anIndex, self, length]);

    // UNICODE
    range.location = anIndex;
    range.length = 1;
    return range;
}

/* Converting string contents into a property list */

- propertyList
{
    extern id readPropertyListFrom(NSString* string);
    return readPropertyListFrom(self);
}

- (NSMutableDictionary*)propertyListFromStringsFileFormat
{
    extern id readDictionary(NSString *_string);
    return readDictionary(self);
}

/* Identifying and comparing strings */

- (NSComparisonResult)caseInsensitiveCompare:(NSString*)aString
{
    NSRange range = {0, [self length]};
    
    return [self compare:aString options:NSCaseInsensitiveSearch range:range];
}

- (NSComparisonResult)compare:(id)aString
{
    NSRange range = {0, [self length]};
    
    return [self compare:aString options:0 range:range];
}

- (NSComparisonResult)compare:(NSString*)aString options:(unsigned int)mask
{
    NSRange range = {0, [self length]};
    
    return [self compare:aString options:mask range:range];
}

- (NSComparisonResult)compare:(NSString*)aString
  options:(unsigned int)mask range:(NSRange)aRange
{
    // UNICODE 
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return NSOrderedSame;
}

- (BOOL)hasPrefix:(NSString*)aString
{
    int mLen = [self length];
    int aLen = [aString length];
    NSRange range = {0, aLen};
    
    if (aLen > mLen)
	return NO;
    
    return [self compare:aString options:0 range:range] == NSOrderedSame;
}

- (BOOL)hasSuffix:(NSString*)aString
{
    int mLen = [self length];
    int aLen = [aString length];
    NSRange range = {mLen-aLen, aLen};

    if (aLen > mLen)
	return NO;
    
    return [self compare:aString options:0 range:range] == NSOrderedSame;
}

- (BOOL)isEqual:(id)anObject
{
    if (self == anObject)
	return YES;
    
    if ([anObject isKindOfClass:[NSString class]]) {
	NSRange range = {0, [self length]};
	return [self compare:anObject options:0 range:range] == NSOrderedSame;
    }
    
    return NO;
}

- (BOOL)isEqualToString:(NSString*)aString
{
    NSRange range = {0, [self length]};
    
    if (self == aString)
	return YES;

    return [self compare:aString options:0 range:range] == NSOrderedSame;
}

- (unsigned int)hash
{
    // UNICODE 
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return 0;
}

/* Getting a shared prefix */

- (NSString*)commonPrefixWithString:(NSString*)aString
  options:(unsigned int)mask
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return nil;
}

/* Changing case */

- (NSString*)capitalizedString
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return nil;
}

- (NSString*)lowercaseString
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return nil;
}

- (NSString*)uppercaseString
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return nil;
}

/* Getting C strings */

- (const char*)cString
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return NULL;
}

- (unsigned int)cStringLength
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return 0;
}

- (void)getCString:(char*)buffer
{
    NSRange range = {0, [self length]};
    
    [self getCString:buffer maxLength:NSMaximumStringLength
	range:range remainingRange:NULL];
}

- (void)getCString:(char*)buffer maxLength:(unsigned int)maxLength
{
    NSRange range = {0, [self length]};
    
    [self getCString:buffer maxLength:maxLength
	range:range remainingRange:NULL];
}

- (void)getCString:(char*)buffer maxLength:(unsigned int)maxLength
  range:(NSRange)aRange remainingRange:(NSRange*)leftoverRange
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
}

/* Getting numeric values */

- (double)doubleValue
{
    // UNICODE
    // ENCODINGS
    double val = 0;
    const char *str = [self cString];
    sscanf(str ? str : "", " %lf ", &val);
    return val;
}

- (float)floatValue
{
    // UNICODE
    // ENCODINGS
    float val = 0;
    const char *str = [self cString];
    sscanf(str ? str : "", " %f ", &val);
    return val;
}

- (int)intValue
{
    // UNICODE
    // ENCODINGS
    int val = 0;
    const char *str = [self cString];
    sscanf(str ? str : "", " %d ", &val);
    return val;
}

/* Working with encodings */

+ (NSStringEncoding*)availableStringEncodings
{
    // UNICODE
    // ENCODINGS
    static NSStringEncoding availableEncodings[] = {
	NSASCIIStringEncoding,
	0
    };
    
    return availableEncodings;
}

+ (NSStringEncoding)defaultCStringEncoding
{
    // UNICODE
    // ENCODINGS
    return NSASCIIStringEncoding;
}

+ (NSString*)localizedNameOfStringEncoding:(NSStringEncoding)encoding
{
    switch(encoding) {
	case NSASCIIStringEncoding:
	    return @"NSASCIIStringEncoding";
	case NSNEXTSTEPStringEncoding:
	    return @"NSNEXTSTEPStringEncoding";
	case NSJapaneseEUCStringEncoding:
	    return @"NSJapaneseEUCStringEncoding";
	case NSUTF8StringEncoding:
	    return @"NSUTF8StringEncoding";
	case NSISOLatin1StringEncoding:
	    return @"NSISOLatin1StringEncoding";
	case NSSymbolStringEncoding:
	    return @"NSSymbolStringEncoding";
	case NSNonLossyASCIIStringEncoding:
	    return @"NSNonLossyASCIIStringEncoding";
	case NSShiftJISStringEncoding:
	    return @"NSShiftJISStringEncoding";
	case NSISOLatin2StringEncoding:
	    return @"NSISOLatin2StringEncoding";
	case NSUnicodeStringEncoding:
	    return @"NSUnicodeStringEncoding";
	case NSAdobeStandardCyrillicStringEncoding:
	    return @"NSAdobeStandardCyrillicStringEncoding";
	case NSWinLatin1StringEncoding:
	    return @"NSWinLatin1StringEncoding";
	default: 
	    return @"Invalid encoding";
    }
}

- (BOOL)canBeConvertedToEncoding:(NSStringEncoding)encoding
{
    id data = [self dataUsingEncoding:encoding allowLossyConversion:NO];
    return data ? YES : NO;
}

- (NSData*)dataUsingEncoding:(NSStringEncoding)encoding
{
    return [self dataUsingEncoding:encoding allowLossyConversion:NO];
}

- (NSData*)dataUsingEncoding:(NSStringEncoding)encoding
  allowLossyConversion:(BOOL)flag
{
    // UNICODE
    // ENCODINGS
    [self notImplemented:_cmd];
    return nil;
}

- (NSStringEncoding)fastestEncoding
{
    // UNICODE
    // ENCODINGS
    return NSASCIIStringEncoding;
}

- (NSStringEncoding)smallestEncoding
{
    // UNICODE
    // ENCODINGS
    return NSASCIIStringEncoding;
}

- (BOOL)getBytes:(void*)bytes maxLength:(unsigned int)maxLength
  inEncoding:(NSStringEncoding)encoding
  allowLossesInConversion:(BOOL)allowLossesInConversion
  fromRange:(NSRange)fromRange
  usedRange:(NSRange*)usedRange
  remainingRange:(NSRange*)remainingRange
{
    // UNICODE
    // ENCODINGS
    [self notImplemented:_cmd];
    return NO;
}

/* Writing to a file */

- (BOOL)writeToFile:(NSString*)path atomically:(BOOL)flag
{
    // UNICODE
    [self subclassResponsibility:_cmd];
    return NO;
}

/* Encoding methods */

- (void)encodeWithCoder:(NSCoder*)aCoder
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
}

- initWithCoder:(NSCoder*)aDecoder
{
    // UNICODE
    // ENCODINGS
    [self subclassResponsibility:_cmd];
    return nil;
}

/* NSCopying methods */

- (id)copyWithZone:(NSZone*)zone
{
    return [[NSString allocWithZone:zone] initWithString:self];
}

/* NSMutableCopying methods */

- (id)mutableCopyWithZone:(NSZone*)zone
{
    return [[NSMutableString allocWithZone:zone] initWithString:self];
}

/* NSObject protocol */

- (NSString*)description
{
    return self;
}

@end /* NSString */

/*********************************
 * NSMutableString abstract class
 *********************************/

@implementation NSMutableString

/*
 * Modifying a string 
 */

- (void)appendFormat:(NSString*)format,...
{
    va_list va;

    va_start(va, format);
    [self appendFormat:format arguments:va];
    va_end(va);
}

- (void)appendFormat:(NSString*)format arguments:(va_list)argList;
{
    // FIX : Vsprinf(self, format, argList)
    [self appendString:Avsprintf(format, argList)];
}

- (void)appendString:(NSString*)aString
{
    NSRange range = {[self length], 0};

    [self replaceCharactersInRange:range withString:aString];
}

- (void)prependFormat:(NSString*)format,...
{
    NSRange range = {0, 0};
    va_list va;

    va_start(va, format);
    [self replaceCharactersInRange:range
	withString:Avsprintf(format, va)];
    va_end(va);
}

- (void)prependFormat:(NSString*)format arguments:(va_list)argList
{
    NSRange range = {0, 0};

    [self replaceCharactersInRange:range
	withString:Avsprintf(format, argList)];
}

- (void)prependString:(NSString*)aString
{
    NSRange range = {0, 0};

    [self replaceCharactersInRange:range withString:aString];
}

- (void)deleteCharactersInRange:(NSRange)range
{
    [self replaceCharactersInRange:range withString:nil];
}

- (void)insertString:(NSString*)aString atIndex:(unsigned)index
{
    NSRange range = {index, 0};

    [self replaceCharactersInRange:range withString:aString];
}

- (void)setString:(NSString*)aString
{
    NSRange range = {0, [self length]};

    [self replaceCharactersInRange:range withString:aString];
}

- (void)replaceCharactersInRange:(NSRange)aRange
  withString:(NSString*)aString
{
    [self subclassResponsibility:_cmd];
}

- (id)copyWithZone:(NSZone*)zone
{
    return [[NSString allocWithZone:zone] initWithString:self];
}

@end /* NSMutableString */

/*********************************
 * NSString creation methods
 *********************************/

@implementation NSString(NSStringCreation)

+ (id)allocWithZone:(NSZone *)zone
{
    return (self == [NSString class]) ? 
	[NSTemporaryString allocWithZone:zone] 
	: NSAllocateObject(self, 0, zone);
}

+ (id)localizedStringWithFormat:(NSString*)format,...
{
    va_list va;
    NSString* string;
    
    va_start(va, format);
    string = [[[self alloc] initWithFormat:format arguments:va] autorelease];
    va_end(va);
    return string;
}

+ (id)stringWithCharacters:(const unichar*)chars
  length:(unsigned int)length
{
    return [[[self alloc] initWithCharacters:chars length:length] autorelease];
}

+ (id)stringWithCharactersNoCopy:(unichar*)chars	
  length:(unsigned int)length freeWhenDone:(BOOL)flag
{
    return [[[self alloc] initWithCharactersNoCopy:chars 
	length:length freeWhenDone:flag] autorelease];
}

+ (id)stringWithCString:(const char*)byteString
{
    return [[[self alloc] initWithCString:byteString] autorelease];
}

+ (id)stringWithCString:(const char*)byteString
  length:(unsigned int)length
{
    return [[[self alloc] initWithCString:byteString
	length:length] autorelease];
}

+ (id)stringWithCStringNoCopy:(char*)byteString
  length:(unsigned int)length freeWhenDone:(BOOL)flag
{
    return [[[self alloc] initWithCStringNoCopy:byteString
	length:length freeWhenDone:flag] autorelease];
}

+ (NSString*)stringWithCStringNoCopy:(char*)byteString
  freeWhenDone:(BOOL)flag
{
    return [[[self alloc] initWithCStringNoCopy:byteString
	freeWhenDone:flag] autorelease];
}

+ (id)stringWithFormat:(NSString*)format,...
{
    va_list va;
    NSString* string;
    
    va_start(va, format);
    string = [[[self alloc] initWithFormat:format arguments:va] autorelease];
    va_end(va);
    return string;
}

+ (id)stringWithFormat:(NSString*)format arguments:(va_list)argList
{
    return [[[self alloc] initWithFormat:format
	arguments:argList] autorelease];
}

+ (id)stringWithContentsOfFile:(NSString*)path
{
    return [[[self alloc] initWithContentsOfFile:path] autorelease];
}

@end /* NSString(NSStringCreation) */

@implementation NSMutableString(NSStringCreation)

+ (id)allocWithZone:(NSZone *)zone
{
    return (self == [NSMutableString class]) ? 
	[NSMutableTemporaryString allocWithZone:zone] 
	: NSAllocateObject(self, 0, zone);
}

+ (id)stringWithCapacity:(unsigned int)capacity
{
    return [[[self alloc] initWithCapacity:capacity] autorelease];
}

@end /* NSMutableString(NSStringCreation) */

/****************************
 * Initializing strings
 ****************************/

@implementation NSMutableString(NSStringInitialization)

- initWithCapacity:(unsigned int)capacity
{
    [self subclassResponsibility:_cmd];
    return nil;
}

- initWithCharacters:(const unichar*)chars length:(unsigned int)length
{
    [self notImplemented:_cmd];
    return nil;
}

- initWithCharactersNoCopy:(unichar*)chars length:(unsigned int)length 
  freeWhenDone:(BOOL)flag
{
    [self notImplemented:_cmd];
    return nil;
}

- initWithCString:(const char*)byteString
{
    return[self initWithString:[[[NSString alloc] 
	initWithCStringNoCopy:(char*)byteString freeWhenDone:NO]
	autorelease]];
}

- initWithCString:(const char*)byteString length:(unsigned int)length
{
    return [self initWithString:[[[NSString alloc] 
	initWithCStringNoCopy:(char*)byteString length:length freeWhenDone:NO] 
	autorelease]];
}

- initWithCStringNoCopy:(char*)byteString freeWhenDone:(BOOL)flag
{
    return [self initWithString:[[[NSString alloc] 
	initWithCStringNoCopy:byteString freeWhenDone:flag] 
	autorelease]];
}

- initWithCStringNoCopy:(char*)byteString length:(unsigned int)length 
  freeWhenDone:(BOOL)flag
{
    return[self initWithString:[[[NSString alloc] 
	initWithCStringNoCopy:byteString length:length freeWhenDone:flag] 
	autorelease]];
}

- initWithString:(NSString*)aString
{
    [self subclassResponsibility:_cmd];
    return nil;
}

- initWithFormat:(NSString*)format, ...
{
    id str;
    va_list va;
    
    va_start(va, format);
    str = [self initWithFormat:format arguments:va];
    va_end(va);
    return str;
}

- initWithFormat:(NSString*)format arguments:(va_list)argList
{
    return [self initWithString:Avsprintf(format, argList)];
}

- initWithFormat:(NSString*)format
  locale:(NSDictionary*)dictionary, ...
{
    id str;
    va_list va;
    
    va_start(va, dictionary);
    str = [self initWithFormat:format arguments:va];
    va_end(va);
    return str;
}

- initWithFormat:(NSString*)format 
  locale:(NSDictionary*)dictionary arguments:(va_list)argList
{
    return [self initWithFormat:format arguments:argList];
}

- initWithData:(NSData*)data encoding:(NSStringEncoding)encoding
{
    // UNICODE
    // ENCODINGS
    [self notImplemented:_cmd];
    return nil;
}

- initWithContentsOfFile:(NSString*)path
{
    // UNICODE
    // ENCODINGS
    [self notImplemented:_cmd];
    return nil;
}

@end

/****************************
 * Allocate concrete strings
 ****************************/
/*
 * Classes used for allocation of NSString concrete instances
 */

@implementation NSTemporaryString

+ allocWithZone:(NSZone*)zone
{
    // OPT - keep a list of deallocated but not freed instances here
    NSTemporaryString* obj = NSAllocateObject(self, 0, zone);
    obj->_zone = zone;
    return obj;
}

- (void)collect
{
    [self dealloc];
}

- (NSZone*)zone
{
    return _zone;
}

/*
 * Methods that return strings
 */

- init
{
    id str = @"";

    [self collect];
    return str;
}

- initWithCharacters:(const unichar*)chars length:(unsigned int)length
{
    // UNICODE
    [self notImplemented:_cmd];
    [self collect];
    return nil;
}

- initWithCharactersNoCopy:(unichar*)chars
  length:(unsigned int)length freeWhenDone:(BOOL)flag
{
    // UNICODE
    [self notImplemented:_cmd];
    [self collect];
    return nil;
}

- initWithCString:(const char*)byteString
{
    int length = Strlen(byteString);
    id str = [[NSInline8BitString allocForCapacity:length zone:_zone]
	    initWithCString:byteString length:length];

    [self collect];
    return str;
}

- initWithCString:(const char*)byteString length:(unsigned int)length
{
    id str = [[NSInline8BitString allocForCapacity:length zone:_zone]
	    initWithCString:byteString length:length];

    [self collect];
    return str;
}

- initWithCStringNoCopy:(char*)byteString freeWhenDone:(BOOL)flag
{
    id str = flag ? 
	[NSOwned8BitString allocWithZone:_zone]
	: [NSNonOwned8BitString allocWithZone:_zone];

    str = [str initWithCString:byteString length:Strlen(byteString) copy:NO];
    [self collect];
    return str;
}

- initWithCStringNoCopy:(char*)byteString
  length:(unsigned int)length freeWhenDone:(BOOL)flag
{
    id str = flag ? 
	[NSOwnedOpen8BitString allocWithZone:_zone]
	: [NSNonOwnedOpen8BitString allocWithZone:_zone];

    str = [str initWithCString:byteString length:length copy:NO];
    [self collect];
    return str;
}

- initWithString:(NSString*)aString
{
    id str = [aString copyWithZone:_zone];
    [self collect];
    return str;
}

- initWithFormat:(NSString*)format, ...
{
    id str;
    va_list va;
    
    va_start(va, format);
    str = [self initWithFormat:format arguments:va];
    va_end(va);
    return str;
}

- initWithFormat:(NSString*)format arguments:(va_list)argList
{
    id str = Avsprintf(format, argList);
    [self collect];
    return [str copyWithZone:_zone];
}

- initWithFormat:(NSString*)format
  locale:(NSDictionary*)dictionary, ...
{
    id str;
    va_list va;
    
    va_start(va, dictionary);
    str = [self initWithFormat:format arguments:va];
    va_end(va);
    return str;
}

- initWithFormat:(NSString*)format 
  locale:(NSDictionary*)dictionary arguments:(va_list)argList
{
    return [self initWithFormat:format arguments:argList];
}

- initWithData:(NSData*)data encoding:(NSStringEncoding)encoding
{
    // UNICODE
    [self notImplemented:_cmd];
    [self collect];
    return nil;
}

- initWithContentsOfFile:(NSString*)path
{
    id str;
    int fd = open([path cString], O_RDONLY);
    struct stat fstat_buf;
    char* bytes = NULL;

    if((fd == -1) || (fstat(fd, &fstat_buf) == -1))
	return nil;

    bytes = NSZoneMalloc(_zone, fstat_buf.st_size + 1);
    if (read(fd, bytes, fstat_buf.st_size) != fstat_buf.st_size) {
	Free(bytes);
	return nil;
    }
    close(fd);
    bytes[fstat_buf.st_size]=0;
    str = [[NSOwned8BitString allocWithZone:_zone] 
	initWithCString:bytes length:fstat_buf.st_size copy:NO];
    [self collect];
    return str;
}

@end /* NSTemporaryString */


@implementation NSMutableTemporaryString

+ allocWithZone:(NSZone*)zone
{
    // OPT - keep a list of deallocated but not freed instances here
    NSMutableTemporaryString* obj = NSAllocateObject(self, 0, zone);
    obj->_zone = zone;
    return obj;
}

- (void)collect
{
    [self release];
}

/*
 * Methods that return strings
 */

- initWithCapacity:(unsigned int)capacity
{
    id str = [[NSMutableSimple8BitString allocWithZone:_zone] 
	initWithCapacity:capacity];
    
    [self collect];
    return str;
}

- init
{
    id str = [[NSMutableSimple8BitString allocWithZone:_zone] init];
    
    [self collect];
    return str;
}

- initWithCString:(const char*)byteString
{
    int length = Strlen(byteString);
    id str = [[NSMutableSimple8BitString allocWithZone:_zone]
	    initWithCString:(char*)byteString length:length copy:YES];

    [self collect];
    return str;
}

- initWithCString:(const char*)byteString length:(unsigned int)length
{
    id str = [[NSMutableSimple8BitString allocWithZone:_zone]
	    initWithCString:(char*)byteString length:length copy:YES];
    
    [self collect];
    return str;
}

- initWithCStringNoCopy:(char*)byteString freeWhenDone:(BOOL)flag
{
    id str = [NSMutableSimple8BitString allocWithZone:_zone];
    
    str = [str initWithCString:byteString
	length:Strlen(byteString) copy:!flag];
    
    [self collect];
    return str;
}

- initWithCStringNoCopy:(char*)byteString
  length:(unsigned int)length freeWhenDone:(BOOL)flag
{
    id str = [NSMutableSimple8BitString allocWithZone:_zone];
    
    str = [str initWithCString:byteString
	length:Strlen(byteString) copy:!flag];

    [self collect];
    return str;
}

- initWithString:(NSString*)aString
{
    id str = [[NSMutableSimple8BitString allocWithZone:_zone] 
	initWithString:aString];
    
    [self collect];
    return str;
}

- initWithFormat:(NSString*)format, ...
{
    id str;
    va_list va;
    
    va_start(va, format);
    str = [self initWithFormat:format arguments:va];
    va_end(va);
    return str;
}

- initWithFormat:(NSString*)format arguments:(va_list)argList
{
    id str = [[NSMutableSimple8BitString allocWithZone:_zone] init];
    
    [str appendFormat:format arguments:argList];
    [self collect];
    return str;
}

- initWithFormat:(NSString*)format
  locale:(NSDictionary*)dictionary, ...
{
    id str;
    va_list va;
    
    va_start(va, dictionary);
    str = [self initWithFormat:format arguments:va];
    va_end(va);
    return str;
}

- initWithFormat:(NSString*)format 
  locale:(NSDictionary*)dictionary arguments:(va_list)argList
{
    return [self initWithFormat:format arguments:argList];
}

- initWithData:(NSData*)data encoding:(NSStringEncoding)encoding
{
    // UNICODE
    [self notImplemented:_cmd];
    [self collect];
    return nil;
}

- initWithContentsOfFile:(NSString*)path
{
    id str;
    int fd = open([path cString], O_RDONLY);
    struct stat fstat_buf;
    char* bytes = NULL;

    if((fd == -1) || (fstat(fd, &fstat_buf) == -1))
	return nil;

    bytes = NSZoneMalloc(_zone, fstat_buf.st_size + 1);
    if (read(fd, bytes, fstat_buf.st_size) != fstat_buf.st_size) {
	Free(bytes);
	return nil;
    }
    close(fd);
    bytes[fstat_buf.st_size]=0;
    str = [[NSMutableSimple8BitString allocWithZone:_zone] 
	initWithCString:bytes length:fstat_buf.st_size copy:NO];
    [self collect];
    return str;
}

@end /* NSMutableTemporaryString */

/*
 * Used for forcing linking of this category
 */

static void __dummyNSStringfile ()
{
    extern void __dummyNSStringFilePathfile();
    __dummyNSStringFilePathfile();
    __dummyNSStringfile();
}
