ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/gstep-base-0.2.7.tgz#/gstep-base-0.2.7/src/NSString.m

This is NSString.m in view mode; [Download] [Up]

/* Implementation of GNUSTEP string class
   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
   
   Written by:  Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
   Date: January 1995
   
   This file is part of the GNUstep Base Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
   
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */ 

/* Caveats:

   Many method unimplemented.

   Only supports C Strings.  Some implementations will need to be 
   changed when we get other string backing classes.

   Does not support all justification directives for `%@' in format strings 
   on non-GNU-libc systems.

*/

#include <gnustep/base/preface.h>
#include <gnustep/base/Coding.h>
#include <Foundation/NSString.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSException.h>
#include <Foundation/NSValue.h>
#include <gnustep/base/IndexedCollection.h>
#include <gnustep/base/IndexedCollectionPrivate.h>
#include <gnustep/base/String.h>
#include <gnustep/base/behavior.h>
#include <limits.h>
#include <string.h>		// for strstr()

@implementation NSString

/* For unichar strings.  (Not implemented---using cStrings) */
static Class NSString_concrete_class;
static Class NSMutableString_concrete_class;

/* For CString's */
static Class NSString_c_concrete_class;
static Class NSMutableString_c_concrete_class;

+ (void) _setConcreteClass: (Class)c
{
  NSString_concrete_class = c;
}

+ (void) _setConcreteCClass: (Class)c
{
  NSString_c_concrete_class = c;
}

+ (void) _setMutableConcreteClass: (Class)c
{
  NSMutableString_concrete_class = c;
}

+ (void) _setMutableConcreteCClass: (Class)c
{
  NSMutableString_c_concrete_class = c;
}

+ (Class) _concreteClass
{
  return NSString_concrete_class;
}

+ (Class) _concreteCClass
{
  return NSString_c_concrete_class;
}

+ (Class) _mutableConcreteClass
{
  return NSMutableString_concrete_class;
}

+ (Class) _mutableConcreteCClass
{
  return NSMutableString_c_concrete_class;
}

#if HAVE_REGISTER_PRINTF_FUNCTION
#include <stdio.h>
#include <printf.h>
#include <stdarg.h>

/* <sattler@volker.cs.Uni-Magdeburg.DE>, with libc-5.3.9 thinks this 
   flag PRINTF_ATSIGN_VA_LIST should be 0, but for me, with libc-5.0.9, 
   it crashes.  -mccallum */ 
#define PRINTF_ATSIGN_VA_LIST			\
       (defined(_LINUX_C_LIB_VERSION_MINOR)	\
	&& _LINUX_C_LIB_VERSION_MAJOR <= 5	\
	&& _LINUX_C_LIB_VERSION_MINOR < 2)

#if ! PRINTF_ATSIGN_VA_LIST
static int
arginfo_func (const struct printf_info *info,
            size_t n,
            int *argtypes) {
  *argtypes = PA_POINTER;
  return 1;
}
#endif /* !PRINTF_ATSIGN_VA_LIST */

static int
handle_printf_atsign (FILE *stream, 
		      const struct printf_info *info,
#if PRINTF_ATSIGN_VA_LIST
		      va_list *ap_pointer)
#else
                      const void **const args)
#endif
{
#if ! PRINTF_ATSIGN_VA_LIST
  const void *ptr = *args;
#endif
  id string_object;
  int len;

  /* xxx This implementation may not pay pay attention to as much 
     of printf_info as it should. */

#if PRINTF_ATSIGN_VA_LIST
  string_object = va_arg (*ap_pointer, id);
#else
  string_object = *((id*) ptr);
#endif
  len = fprintf(stream, "%*s",
		(info->left ? - info->width : info->width),
		[string_object cStringNoCopy]);
  return len;
}
#endif /* HAVE_REGISTER_PRINTF_FUNCTION */

+ (void) initialize
{
  static int done = 0;
  if (!done)
    {
      done = 1;
      class_add_behavior([NSString class], [String class]);
      NSString_concrete_class = [NSGCString class];
      NSString_c_concrete_class = [NSGCString class];
      NSMutableString_concrete_class = [NSGMutableCString class];
      NSMutableString_c_concrete_class = [NSGMutableCString class];

#if HAVE_REGISTER_PRINTF_FUNCTION
      if (register_printf_function ('@', 
				    (printf_function)handle_printf_atsign, 
#if PRINTF_ATSIGN_VA_LIST
				    0))
#else
	                           (printf_arginfo_function)arginfo_func))
#endif
	[NSException raise: NSGenericException
		     format: @"register printf handling of %%@ failed"];
#endif /* HAVE_REGISTER_PRINTF_FUNCTION */
    }
}

+ allocWithZone: (NSZone*)z
{
  return NSAllocateObject([self _concreteClass], 0, z);
}

// Creating Temporary Strings

+ (NSString*) localizedStringWithFormat: (NSString*) format, ...
{
  [self notImplemented:_cmd];
  return self;
}

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

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

+ (NSString*) stringWithCharacters: (const unichar*)chars
   length: (unsigned int)length
{
  [self notImplemented:_cmd];  
  return self;
}

+ (NSString*) stringWithFormat: (NSString*)format,...
{
  va_list ap;
  id ret;

  va_start(ap, format);
  ret = [[[self alloc] initWithFormat:format arguments:ap]
	 autorelease];
  va_end(ap);
  return ret;
}

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

// Initializing Newly Allocated Strings

- (id) init
{
  return [self initWithCString:""];
}

- (id) initWithCString: (const char*)byteString
{
  return [self initWithCString:byteString 
	       length:(byteString ? strlen(byteString) : 0)];
}

- (id) initWithCString: (const char*)byteString
   length: (unsigned int)length
{
  char *s;
  OBJC_MALLOC(s, char, length+1);
  if (byteString)
    memcpy(s, byteString, length);
  s[length] = '\0';
  return [self initWithCStringNoCopy:s length:length freeWhenDone:YES];
}

/* This is the designated initializer for CStrings. */
- (id) initWithCStringNoCopy: (char*)byteString
   length: (unsigned int)length
   freeWhenDone: (BOOL)flag
{
  [self subclassResponsibility:_cmd];
  return self;
}

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

/* This is the designated initializer for unichar Strings. */
- (id) initWithCharactersNoCopy: (unichar*)chars
   length: (unsigned int)length
   freeWhenDone: (BOOL)flag
{
  [self subclassResponsibility:_cmd];
  return self;
}

- (id) initWithContentsOfFile: (NSString*)path
{
  [self notImplemented:_cmd];
  return self;
}

- (id) initWithData: (NSData*)data
   encoding: (NSStringEncoding)encoding
{
  [self notImplemented:_cmd];
  return self;
}

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

/* xxx Change this when we have non-CString classes */
- (id) initWithFormat: (NSString*)format
   arguments: (va_list)arg_list
{
#if HAVE_VSPRINTF
  const char *format_cp = [format cStringNoCopy];
  int format_len = strlen (format_cp);
  /* xxx horrible disgusting BUFFER_EXTRA arbitrary limit; fix this! */
  #define BUFFER_EXTRA 1024
  char buf[format_len + BUFFER_EXTRA];
  int printed_len = 0;

#if ! HAVE_REGISTER_PRINTF_FUNCTION
  /* If the available libc doesn't have `register_printf_function()', then
     the `%@' printf directive isn't available with printf() and friends.
     Here we make a feable attempt to handle it. */
  {
    /* We need a local copy since we change it.  (Changing and undoing
       the change doesn't work because some format strings are constant
       strings, placed in a non-writable section of the executable, and 
       writing to them will cause a segfault.) */ 
    char format_cp_copy[format_len+1];
    char *atsign_pos;
    char *format_to_go = format_cp_copy;
    strcpy (format_cp_copy, format_cp);
    /* Loop once for each `%@' in the format string. */
    while ((atsign_pos = strstr (format_to_go, "%@")))
      {
	const char *cstring;
	/* If there is a "%%@", then do the right thing: print it literally. */
	if ((*(atsign_pos-1) == '%')
	    && atsign_pos != format_cp_copy)
	  continue;
	/* Temporarily terminate the string before the `%@'. */
	*atsign_pos = '\0';
	/* Print the part before the '%@' */
	printed_len = vsprintf (buf, format_cp_copy, arg_list);
	/* Get a C-string (char*) from the String object, and print it. */
	cstring = [(id) va_arg (arg_list, id) cStringNoCopy];
	strcat (buf+printed_len, cstring);
	printed_len += strlen (cstring);
	/* Put back the `%' we removed when we terminated mid-string. */
	*atsign_pos = '%';
	/* Skip over this `%@', and look for another one. */
	format_to_go = atsign_pos + 2;
      }
    /* Print the rest of the string after the last `%@'. */
    printed_len += vsprintf (buf+printed_len, format_to_go, arg_list);
  }
#else
  /* The available libc has `register_printf_function()', so the `%@' 
     printf directive is handled by printf and friends. */
  printed_len = vsprintf (buf, format_cp, arg_list);
#endif /* !HAVE_REGISTER_PRINTF_FUNCTION */

  /* Raise an exception if we overran our buffer. */
  NSParameterAssert (printed_len < format_len + BUFFER_EXTRA - 1);
  return [self initWithCString:buf];
#else /* HAVE_VSPRINTF */
  [self notImplemented: _cmd];
  return self;
#endif
}

- (id) initWithFormat: (NSString*)format
   locale: (NSDictionary*)dictionary
{
  [self notImplemented:_cmd];
  return self;
}

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

/* xxx Change this when we have non-CString classes */
- (id) initWithString: (NSString*)string
{
  return [self initWithCString:[string _cStringContents]];
}


// Getting a String's Length

/* xxx Change this when we have non-CString classes */
- (unsigned int) length
{
  return [self cStringLength];
}


// Accessing Characters

/* xxx Change this when we have non-CString classes */
- (unichar) characterAtIndex: (unsigned int)index
{
  /* xxx raise NSException instead of assert. */
  assert(index < [self cStringLength]);
  return (unichar) [self _cStringContents][index];
}

/* Inefficient.  Should be overridden */
- (void) getCharacters: (unichar*)buffer
{
  [self getCharacters:buffer range:((NSRange){0,[self length]})];
  return;
}

/* Inefficient.  Should be overridden */
- (void) getCharacters: (unichar*)buffer
   range: (NSRange)aRange
{
  int i;
  for (i = 0; i < aRange.length; i++)
    {
      buffer[i] = [self characterAtIndex: aRange.location+i];
    }
}


// Combining Strings

- (NSString*) stringByAppendingFormat: (NSString*)format,...
{
  va_list ap;
  id ret;
  va_start(ap, format);
  ret = [self stringByAppendingString:
	      [NSString stringWithFormat:format arguments:ap]];
  va_end(ap);
  return ret;
}

/* xxx Change this when we have non-CString classes */
- (NSString*) stringByAppendingString: (NSString*)aString
{
  unsigned len = [self cStringLength];
  char *s = alloca(len + [aString cStringLength] + 1);
  s = strcpy(s, [self _cStringContents]);
  strcpy(s + len, [aString _cStringContents]);
  return [NSString stringWithCString:s];
}


// Dividing Strings into Substrings

- (NSArray*) componentsSeparatedByString: (NSString*)separator
{
  NSRange search;
  NSRange found;
  NSMutableArray *array = [NSMutableArray array];

  search = NSMakeRange (0, [self length]);
  found = [self rangeOfString: separator];
  while (found.length)
    {
      NSRange current;
      current = NSMakeRange (search.location,
			     found.location - search.location);
      [array addObject: [self substringFromRange: current]];
      search = NSMakeRange (found.location+1,
			    search.length - found.location);
      found = [self rangeOfString: separator 
		    options: 0
		    range: search];
    }

  // FIXME: Need to make mutable array into non-mutable array?
  return array;
}

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

- (NSString*) substringFromRange: (NSRange)aRange
{
  char buffer[aRange.length];
  int count = [self length];

  if (aRange.location > count)
    [NSException raise: NSRangeException format: @"Invalid location."];
  if (aRange.length > (count - aRange.location))
    [NSException raise: NSRangeException format: @"Invalid location+length."];
  /* This will only DTRT for CString's... but that's all we have right now. */
  [self getCString: buffer 
	maxLength: aRange.length
	range: aRange
	remainingRange: NULL];
  return [[self class] stringWithCString: buffer length: aRange.length];
}

- (NSString*) substringToIndex: (unsigned int)index
{
  return [self substringFromRange:((NSRange){0,index+1})];;
}


// Finding Ranges of Characters and Substrings

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

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

/* FIXME:  how do you do a case insensitive search?  what's an anchored
   search? what's a literal search? */
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
    options: (unsigned int)mask
    range: (NSRange)aRange
{
  int i, start, stop, step;
  NSRange range;

  /* xxx check to make sure aRange is within self; raise NSStringBoundsError */
  assert(NSMaxRange(aRange) < [self length]);

  if ((mask & NSBackwardsSearch) == NSBackwardsSearch)
    {
      start = NSMaxRange(aRange); stop = aRange.location; step = -1;
    }
  else
    {
      start = aRange.location; stop = NSMaxRange(aRange); step = 1;
    }
  range.length = 0;
  for (i = start; i < stop; i+=step)
    {
      unichar letter = [self characterAtIndex:i];
      if ([aSet characterIsMember:letter])
	{
	  range = NSMakeRange(i, 1);
	  break;
	}
    }

  return range;
}

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

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

- (NSRange) rangeOfString:(NSString *) aString
   options:(unsigned int) mask
   range:(NSRange) aRange
{
  int stepDirection;
  unsigned int myIndex, myLength, myEndIndex;
  unsigned int strLength;
  unichar strFirstCharacter;

  /* Check that the search range is reasonable */
  myLength = [self length];
  if (aRange.location > myLength)
    [NSException raise: NSRangeException format:@"Invalid location."];
  if (aRange.length > (myLength - aRange.location))
    [NSException raise: NSRangeException format:@"Invalid location+length."];

  /* Ensure the string can be found */
  strLength = [aString length];
  if (strLength > aRange.length)
    return (NSRange){0, 0};

  /* Decide where to start and end the search */
  if (mask & NSBackwardsSearch)
    {
      stepDirection = -1;
      myIndex = aRange.location + aRange.length - strLength;
      myEndIndex = aRange.location;
    }
  else
    {
      stepDirection = 1;
      myIndex = aRange.location;
      myEndIndex = aRange.location + aRange.length - strLength;
    }
  /* FIXME: I am guessing that this is what NSAnchoredSearch does. */
  if (mask & NSAnchoredSearch)
    myEndIndex = myIndex;

  /* Start searching.  For efficiency there are separate loops for
     case-sensitive and case-insensitive searches. */
  strFirstCharacter = [aString characterAtIndex:0];
  if (mask & NSCaseInsensitiveSearch)
    {
      for (;;)
      {
        unsigned int i = 1;
        unichar myCharacter = [self characterAtIndex:myIndex];
        unichar strCharacter = strFirstCharacter;

        for (;;)
          {
            /* FIXME: I have no idea how to make case-insensitive
	       comparisons work over the full range of Unicode characters. */
            if ((myCharacter != strCharacter) &&
                (!isascii (myCharacter)
                 || !isalpha (myCharacter)
                 || !isascii (strCharacter)
                 || !isalpha (strCharacter)
                 || (tolower (myCharacter) != tolower (strCharacter))))
              break;
            if (i == strLength)
              return (NSRange){myIndex, strLength};
            myCharacter = [self characterAtIndex:myIndex + i];
            strCharacter = [aString characterAtIndex:i];
            i++;
          }
        if (myIndex == myEndIndex)
          break;
        myIndex += stepDirection;
      }
    }
  else
    {
      for (;;)
      {
        unsigned int i = 1;
        unichar myCharacter = [self characterAtIndex:myIndex];
        unichar strCharacter = strFirstCharacter;

        for (;;)
          {
            if (myCharacter != strCharacter)
              break;
            if (i == strLength)
              return (NSRange){myIndex, strLength};
            myCharacter = [self characterAtIndex:myIndex + i];
            strCharacter = [aString characterAtIndex:i];
            i++;
          }
        if (myIndex == myEndIndex)
          break;
        myIndex += stepDirection;
      }
    }
  return (NSRange){0, 0};
}

// Determining Composed Character Sequences

- (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned int)anIndex
{
  [self notImplemented:_cmd];
  return ((NSRange){0,0});
}


// Identifying and Comparing Strings

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

- (NSComparisonResult) compare: (NSString*)aString
{
  return [self compare:aString options:0];
}

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

- (NSComparisonResult) compare: (NSString*)aString
   options: (unsigned int)mask
   range: (NSRange)aRange
{
  /* xxx ignores NSAnchoredSearch in mask.  Fix this. */
  /* xxx only handles C-string encoding */

  int i, start, end, increment;
  const char *s1 = [self _cStringContents];
  const char *s2 = [aString _cStringContents];

  if (mask & NSBackwardsSearch) 
    {
      start = aRange.location + aRange.length;
      end = aRange.location;
      increment = -1;
    }
  else
    {
      start = aRange.location;
      end = aRange.location + aRange.length;
      increment = 1;
    }

  if (mask & NSCaseInsensitiveSearch)
    {
      for (i = start; i < end; i += increment)
	{
	  int c1 = tolower(s1[i]);
	  int c2 = tolower(s2[i]);
	  if (c1 < c2) return NSOrderedAscending;
	  if (c1 > c2) return NSOrderedDescending;
	}
    }
  else
    {
      for (i = start; i < end; i += increment)
	{
	  if (s1[i] < s2[i]) return NSOrderedAscending;
	  if (s1[i] > s2[i]) return NSOrderedDescending;
	}
    }
  return NSOrderedSame;
}

- (BOOL) hasPrefix: (NSString*)aString
{
  NSRange range;
  range = [self rangeOfString:aString];
  return (range.location == 0) ? YES : NO;
}

- (BOOL) hasSuffix: (NSString*)aString
{
  NSRange range;
  range = [self rangeOfString:aString options:NSBackwardsSearch];
  return (range.location == ([self length] - [aString length])) ? YES : NO;
}

- (unsigned int) hash
{
  unsigned ret = 0;
  unsigned ctr = 0;
  unsigned char_count = 0;
  const char *s = [self _cStringContents];

  while (*s && char_count++ < NSHashStringLength)
    {
      ret ^= *s++ << ctr;
      ctr = (ctr + 1) % sizeof (void*);
    }
  return ret;
}

- (BOOL) isEqual: (id)anObject
{
  if ([anObject isKindOf:[NSString class]])
    return [self isEqualToString:anObject];
  return NO;
}

- (BOOL) isEqualToString: (NSString*)aString
{
  return ! strcmp([self _cStringContents], [aString _cStringContents]);
}


// Storing the String

- (NSString*) description
{
  const char *src = [self cString];
  char *dest;
  char *src_ptr,*dest_ptr;
  int len,quote;
  unsigned char ch;

  /* xxx Really should make this work with unichars. */

#define inrange(ch,min,max) ((ch)>=(min) && (ch)<=(max))
#define noquote(ch) (inrange(ch,'a','z') || inrange(ch,'A','Z') || inrange(ch,'0','9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
#define charesc(ch) (inrange(ch,07,014) || ((ch)=='\"') || ((ch)=='\\'))
#define numesc(ch) (((ch)<=06) || inrange(ch,015,037) || ((ch)>0176))

  for (src_ptr = (char*)src, len=0,quote=0;
       (ch=*src_ptr);
       src_ptr++, len++)
    {
      if (!noquote(ch))
	{
	  quote=1;
	  if (charesc(ch))
	    len++;
	  else if (numesc(ch))
	    len+=3;
	}
    }
  if (quote)
    len+=2;

  dest = (char*) malloc (len+1);

  src_ptr = (char*) src;
  dest_ptr = dest;
  if (quote)
    *(dest_ptr++) = '\"';
  for (; (ch=*src_ptr); src_ptr++,dest_ptr++)
    {
      if (charesc(ch))
	{
	  *(dest_ptr++) = '\\';
	  switch (ch)
	    {
	    case '\a': *dest_ptr = 'a'; break;
	    case '\b': *dest_ptr = 'b'; break;
	    case '\t': *dest_ptr = 't'; break;
	    case '\n': *dest_ptr = 'n'; break;
	    case '\v': *dest_ptr = 'v'; break;
	    case '\f': *dest_ptr = 'f'; break;
	    default: *dest_ptr = ch;  /* " or \ */
	    }
	}
      else if (numesc(ch))
	{
	  *(dest_ptr++) = '\\';
	  *(dest_ptr++) = '0' + ((ch>>6)&07);
	  *(dest_ptr++) = '0' + ((ch>>3)&07);
	  *dest_ptr = '0' + (ch&07);
	}
      else
	{  /* copy literally */
	  *dest_ptr = ch;
	}
    }
  if (quote)
    *(dest_ptr++) = '\"';
  *dest_ptr = '\0';

  return [NSString stringWithCString:dest];
}

- (BOOL) writeToFile: (NSString*)filename
   atomically: (BOOL)useAuxiliaryFile
{
  [self notImplemented:_cmd];
  return NO;
}


// Getting a Shared Prefix

- (NSString*) commonPrefixWithString: (NSString*)aString
   options: (unsigned int)mask
{
  [self notImplemented:_cmd];
  return self;
}


// Changing Case

- (NSString*) capitalizedString
{
  [self notImplemented:_cmd];
  return self;
}

- (NSString*) lowercaseString
{
  [self notImplemented:_cmd];
  return self;
}

- (NSString*) uppercaseString
{
  [self notImplemented:_cmd];
  return self;
}


// Getting C Strings

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

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

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

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

- (void) getCString: (char*)buffer
   maxLength: (unsigned int)maxLength
   range: (NSRange)aRange
   remainingRange: (NSRange*)leftoverRange
{
  int len;

  /* xxx check to make sure aRange is within self; raise NSStringBoundsError */
  assert(aRange.location + aRange.length <= [self cStringLength]);
  if (maxLength < aRange.length)
    {
      len = maxLength;
      if (leftoverRange)
	{
	  leftoverRange->location = 0;
	  leftoverRange->length = 0;
	}
    }
  else
    {
      len = aRange.length;
      if (leftoverRange)
	{
	  leftoverRange->location = aRange.location + maxLength;
	  leftoverRange->length = aRange.length - maxLength;
	}
    }
  memcpy(buffer, [self _cStringContents] + aRange.location, len);
}


// Getting Numeric Values

- (double) doubleValue
{
  return atof([self _cStringContents]);
}

- (float) floatValue
{
  return (float) atof([self _cStringContents]);
}

- (int) intValue
{
  return atoi([self _cStringContents]);
}


// Working With Encodings

+ (NSStringEncoding) defaultCStringEncoding
{
  [self notImplemented:_cmd];
  return 0;
}

- (BOOL) canBeConvertedToEncoding: (NSStringEncoding)encoding
{
  [self notImplemented:_cmd];
  return NO;
}

- (NSData*) dataUsingEncoding: (NSStringEncoding)encoding
{
  [self notImplemented:_cmd];
  return nil;
}

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

- (NSStringEncoding) fastestEncoding
{
  [self notImplemented:_cmd];
  return 0;
}

- (NSStringEncoding) smallestEncoding
{
  [self notImplemented:_cmd];
  return 0;
}


// Converting String Contents into a Property List

- (id)propertyList
{
  id obj;
  void *bufstate;
  bufstate = (void *)pl_scan_string([self cString]);
  obj = (id)plparse();
  pl_delete_buffer(bufstate);
  return obj;
}

- (NSDictionary*) propertyListFromStringsFileFormat
{
  [self notImplemented:_cmd];
  return nil;
}


// Manipulating File System Paths

- (unsigned int) completePathIntoString: (NSString**)outputName
   caseSensitive: (BOOL)flag
   matchesIntoArray: (NSArray**)outputArray
   filterTypes: (NSArray*)filterTypes
{
  [self notImplemented:_cmd];
  return 0;
}

/* Returns a new string containing the last path component of the receiver. The
   path component is any substring after the last '/' character. If the last
   character is a '/', then the substring before the last '/', but after the
   second-to-last '/' is returned. Returns the receiver if there are no '/'
   characters. Returns the null string if the receiver only contains a '/'
   character. */
- (NSString*) lastPathComponent
{
  NSRange range;
  NSString *substring = nil;

  range = [self rangeOfString:@"/" options:NSBackwardsSearch];
  if (range.length == 0)
      substring = self;
  else if (range.location == [self length] - 1)
    {
      if (range.location == 0)
	  substring = [NSString new];
      else
	  substring = [[self substringToIndex:range.location-1] 
				lastPathComponent];
    }
  else
      substring = [self substringFromIndex:range.location+1];

  return substring;
}

/* Returns a new string containing the path extension of the receiver. The
   path extension is a suffix on the last path component which starts with
   a '.' (for example .tiff is the pathExtension for /foo/bar.tiff). Returns
   a null string if no such extension exists. */
- (NSString*) pathExtension
{
  NSRange range;
  NSString *substring = nil;

  range = [self rangeOfString:@"." options:NSBackwardsSearch];
  if (range.length == 0 
	|| range.location 
	    < ([self rangeOfString:@"/" options:NSBackwardsSearch]).location)
      substring =  [NSString new];
  else
      substring = [self substringFromIndex:range.location+1];
  return substring;
}

- (NSString*) stringByAbbreviatingWithTildeInPath
{
  [self notImplemented:_cmd];
  return self;
}

/* Returns a new string with the path component given in aString
   appended to the receiver.  Assumes that aString is NOT prefixed by
   a '/'.  Checks the receiver to see if the last letter is a '/', if it
   is not, a '/' is appended before appending aString */
- (NSString*) stringByAppendingPathComponent: (NSString*)aString
{
  NSRange  range;
  NSString *newstring;

  range = [self rangeOfString:@"/" options:NSBackwardsSearch];
  if (range.length != 0 && range.location != [self length] - 1)
      newstring = [self stringByAppendingString:@"/"];
  else
      newstring = self;

  return [newstring stringByAppendingString:aString];
}

/* Returns a new string with the path extension given in aString
   appended to the receiver.  Assumes that aString is NOT prefixed by
   a '.'.  Checks the receiver to see if the last letter is a '.', if it
   is not, a '.' is appended before appending aString */
- (NSString*) stringByAppendingPathExtension: (NSString*)aString
{
  NSRange  range;
  NSString *newstring;

  range = [aString rangeOfString:@"." options:NSBackwardsSearch];
  if (range.length != 0 && range.location != [self length] - 1)
      newstring = [self stringByAppendingString:@"."];
  else
      newstring = self;

  return [newstring stringByAppendingString:aString];
}

/* Returns a new string with the last path component removed from the
  receiver.  See lastPathComponent for a definition of a path component */
- (NSString*) stringByDeletingLastPathComponent
{
  NSRange range;
  NSString *substring;

  range = [self rangeOfString:[self lastPathComponent] 
			options:NSBackwardsSearch];
  if (range.length != 0)
      substring = [self substringToIndex:range.location-2];
  else
      substring = self;
  return substring;
}

/* Returns a new string with the path extension removed from the receiver.
   See pathExtension for a definition of the path extension */
- (NSString*) stringByDeletingPathExtension
{
  NSRange range;
  NSString *substring;

  range = [self rangeOfString:[self pathExtension] options:NSBackwardsSearch];
  if (range.length != 0)
      substring = [self substringToIndex:range.location-2];
  else
      substring = self;
  return substring;
}

- (NSString*) stringByExpandingTildeInPath
{
  [self notImplemented:_cmd];
  return self;
}

- (NSString*) stringByResolvingSymlinksInPath
{
  [self notImplemented:_cmd];
  return self;
}

- (NSString*) stringByStandardizingPath
{
  [self notImplemented:_cmd];
  return self;
}

/* NSCopying Protocol */

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

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

/* NSCoding Protocol */

- (void) encodeWithCoder: anEncoder
{
  [super encodeWithCoder:anEncoder];
}

- initWithCoder: aDecoder
{
  return [super initWithCoder:aDecoder];
}

@end

@implementation NSString (NSCStringAccess)
- (const char *) _cStringContents
{
  [self subclassResponsibility:_cmd];
  return NULL;
}
@end

/* We don't actually implement (GNU) Category, in order to avoid warning
   about "unimplemented methods"; they come from the behavior. */
@implementation NSString (GNUCollection)

- objectAtIndex: (unsigned)index
{
  CHECK_INDEX_RANGE_ERROR(index, [self cStringLength]);
  return [NSNumber numberWithChar: [self _cStringContents][index]];
}

/* The rest are handled by the class_add_behavior() call in +initialize. */

@end

@implementation NSMutableString

+ allocWithZone: (NSZone*)z
{
  return NSAllocateObject([self _mutableConcreteClass], 0, z);
}

/* xxx This method may be removed in future. */
- (void) setCString: (const char *)byteString length: (unsigned)length
{
  [self subclassResponsibility:_cmd];
}


// Initializing Newly Allocated Strings

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


// Creating Temporary Strings

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

/* Inefficient. */
+ (NSString*) stringWithCharacters: (const unichar*)characters
   length: (unsigned)length
{
  id n;
  n = [self stringWithCapacity:length];
  [n setString: [NSString stringWithCharacters:characters length:length]];
  return n;
}

+ (NSString*) stringWithCString: (const char*)byteString
{
  return [self stringWithCString:byteString length:strlen(byteString)];
}

+ (NSString*) stringWithCString: (const char*)bytes
   length:(unsigned)length
{
  id n = [[self alloc] initWithCapacity:length];
  [n setCString:bytes length:length];
  return n;
}

/* xxx Change this when we have non-CString classes */
+ (NSString*) stringWithFormat: (NSString*)format, ...
{
  va_list ap;
  va_start(ap, format);
  self = [super stringWithFormat:format arguments:ap];
  va_end(ap);
  return self;
}


// Modify A String

/* Inefficient. */
- (void) appendString: (NSString*)aString
{
  id tmp = [self stringByAppendingString:aString];
  [self setString:tmp];
}

/* Inefficient. */
- (void) appendFormat: (NSString*)format, ...
{
  va_list ap;
  id tmp;
  va_start(ap, format);
  tmp = [NSString stringWithFormat:format arguments:ap];
  va_end(ap);
  [self appendString:tmp];
}

- (void) deleteCharactersInRange: (NSRange)range
{
  [self subclassResponsibility:_cmd];
}

- (void) insertString: (NSString*)aString atIndex:(unsigned)loc
{
  [self subclassResponsibility:_cmd];
}

/* Inefficient. */
- (void) replaceCharactersInRange: (NSRange)range 
   withString: (NSString*)aString
{
  [self deleteCharactersInRange:range];
  [self insertString:aString atIndex:range.location];
}

/* xxx Change this when we have non-CString classes */
- (void) setString: (NSString*)aString
{
  const char *s = [aString _cStringContents];
  [self setCString:s length:strlen(s)];
}


@end


@implementation NXConstantString

- (void)dealloc
{
}

- (const char*) cString
{
  return _contents_chars;
}

- retain
{
  return self;
}

- (oneway void) release
{
  return;
}

- autorelease
{
  return self;
}

- copyWithZone: (NSZone*)z
{
  return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.