1995-03-12 19:54:14 +00:00
|
|
|
/* Implementation of GNUSTEP string class
|
1998-10-15 05:03:16 +00:00
|
|
|
Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
|
1995-03-12 19:54:14 +00:00
|
|
|
|
1996-04-17 20:17:45 +00:00
|
|
|
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
1995-03-12 19:54:14 +00:00
|
|
|
Date: January 1995
|
1997-05-03 18:05:21 +00:00
|
|
|
|
1998-01-08 15:25:59 +00:00
|
|
|
Unicode implementation by Stevo Crvenkovski <stevo@btinternet.com>
|
1997-05-03 18:05:21 +00:00
|
|
|
Date: February 1997
|
|
|
|
|
1998-10-15 05:03:16 +00:00
|
|
|
Optimisations by Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
2000-10-29 14:52:33 +00:00
|
|
|
Date: October 1998 - 2000
|
1998-10-15 05:03:16 +00:00
|
|
|
|
1996-05-12 00:56:10 +00:00
|
|
|
This file is part of the GNUstep Base Library.
|
1995-03-12 19:54:14 +00:00
|
|
|
|
|
|
|
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.
|
1997-05-03 18:05:21 +00:00
|
|
|
|
1995-03-12 19:54:14 +00:00
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; if not, write to the Free
|
1999-08-25 14:47:19 +00:00
|
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
1997-05-03 18:05:21 +00:00
|
|
|
*/
|
1995-03-12 19:54:14 +00:00
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
/* Caveats:
|
1995-04-03 01:35:42 +00:00
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
Some implementations will need to be changed.
|
1996-09-02 13:20:20 +00:00
|
|
|
Does not support all justification directives for `%@' in format strings
|
|
|
|
on non-GNU-libc systems.
|
1995-04-03 01:35:42 +00:00
|
|
|
*/
|
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
/* Initial implementation of Unicode. Version 0.0.0 : )
|
1997-09-01 21:59:51 +00:00
|
|
|
Locales not yet supported.
|
1997-05-03 18:05:21 +00:00
|
|
|
Limited choice of default encodings.
|
|
|
|
*/
|
|
|
|
|
1997-11-06 00:51:23 +00:00
|
|
|
#include <config.h>
|
2000-06-14 04:03:56 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
1998-12-20 21:27:47 +00:00
|
|
|
#include <base/preface.h>
|
1995-04-17 21:13:20 +00:00
|
|
|
#include <Foundation/NSString.h>
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
#include <Foundation/NSArray.h>
|
|
|
|
#include <Foundation/NSCharacterSet.h>
|
1996-03-26 19:35:47 +00:00
|
|
|
#include <Foundation/NSException.h>
|
1996-02-22 15:18:57 +00:00
|
|
|
#include <Foundation/NSValue.h>
|
1996-10-31 17:03:44 +00:00
|
|
|
#include <Foundation/NSDictionary.h>
|
1996-11-24 21:04:24 +00:00
|
|
|
#include <Foundation/NSUserDefaults.h>
|
1998-10-15 18:46:27 +00:00
|
|
|
#include <Foundation/NSFileManager.h>
|
1998-12-18 17:05:44 +00:00
|
|
|
#include <Foundation/NSPortCoder.h>
|
|
|
|
#include <Foundation/NSPathUtilities.h>
|
1999-06-21 08:30:26 +00:00
|
|
|
#include <Foundation/NSRange.h>
|
2000-06-16 11:06:06 +00:00
|
|
|
#include <Foundation/NSException.h>
|
1997-09-01 21:59:51 +00:00
|
|
|
#include <Foundation/NSData.h>
|
1998-01-08 15:25:59 +00:00
|
|
|
#include <Foundation/NSBundle.h>
|
2000-09-22 13:45:58 +00:00
|
|
|
#include <Foundation/NSURL.h>
|
2000-11-03 10:11:56 +00:00
|
|
|
#include <Foundation/NSMapTable.h>
|
|
|
|
#include <Foundation/NSLock.h>
|
1995-04-03 01:35:42 +00:00
|
|
|
#include <limits.h>
|
1996-07-15 18:41:44 +00:00
|
|
|
#include <string.h> // for strstr()
|
1996-10-31 17:03:44 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
1998-12-20 21:27:47 +00:00
|
|
|
#include <base/behavior.h>
|
1997-05-03 18:05:21 +00:00
|
|
|
|
1998-12-20 21:27:47 +00:00
|
|
|
#include <base/Unicode.h>
|
1997-05-03 18:05:21 +00:00
|
|
|
|
2000-10-23 06:18:03 +00:00
|
|
|
@class GSString;
|
2000-11-03 10:11:56 +00:00
|
|
|
@class GSMutableString;
|
|
|
|
@class GSPlaceholderString;
|
2000-10-23 11:44:34 +00:00
|
|
|
@class NSGMutableArray;
|
|
|
|
@class NSGMutableDictionary;
|
|
|
|
|
1998-10-09 04:24:56 +00:00
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
/*
|
2000-10-23 11:44:34 +00:00
|
|
|
* Cache classes and method implementations for speed.
|
2000-08-07 22:00:31 +00:00
|
|
|
*/
|
2000-10-23 11:44:34 +00:00
|
|
|
static Class NSDataClass;
|
2000-10-23 06:18:03 +00:00
|
|
|
static Class NSStringClass;
|
|
|
|
static Class NSMutableStringClass;
|
|
|
|
|
|
|
|
static Class GSStringClass;
|
2000-11-03 10:11:56 +00:00
|
|
|
static Class GSMutableStringClass;
|
|
|
|
static Class GSPlaceholderStringClass;
|
|
|
|
|
|
|
|
static GSPlaceholderString *defaultPlaceholderString;
|
|
|
|
static NSMapTable *placeholderMap;
|
|
|
|
static NSLock *placeholderLock;
|
1999-05-10 11:02:28 +00:00
|
|
|
|
2000-10-23 11:44:34 +00:00
|
|
|
static Class plArray;
|
|
|
|
static id (*plAdd)(id, SEL, id) = 0;
|
|
|
|
|
|
|
|
static Class plDictionary;
|
|
|
|
static id (*plSet)(id, SEL, id, id) = 0;
|
|
|
|
|
|
|
|
static id (*plAlloc)(Class, SEL, NSZone*) = 0;
|
|
|
|
static id (*plInit)(id, SEL, unichar*, unsigned) = 0;
|
|
|
|
|
2000-10-30 18:00:27 +00:00
|
|
|
static SEL plSel;
|
2000-11-01 12:31:21 +00:00
|
|
|
static SEL cMemberSel = 0;
|
2000-10-23 11:44:34 +00:00
|
|
|
|
|
|
|
static NSCharacterSet *hexdigits = nil;
|
|
|
|
static BOOL (*hexdigitsImp)(id, SEL, unichar) = 0;
|
|
|
|
static void setupHexdigits()
|
|
|
|
{
|
|
|
|
if (hexdigits == nil)
|
|
|
|
{
|
|
|
|
hexdigits = [NSCharacterSet characterSetWithCharactersInString:
|
|
|
|
@"0123456789abcdefABCDEF"];
|
|
|
|
IF_NO_GC(RETAIN(hexdigits));
|
2000-11-01 12:31:21 +00:00
|
|
|
if (cMemberSel == 0)
|
|
|
|
cMemberSel = @selector(characterIsMember:);
|
2000-10-23 11:44:34 +00:00
|
|
|
hexdigitsImp =
|
|
|
|
(BOOL(*)(id,SEL,unichar)) [hexdigits methodForSelector: cMemberSel];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSCharacterSet *quotables = nil;
|
|
|
|
static BOOL (*quotablesImp)(id, SEL, unichar) = 0;
|
|
|
|
static void setupQuotables()
|
|
|
|
{
|
|
|
|
if (quotables == nil)
|
|
|
|
{
|
|
|
|
NSMutableCharacterSet *s;
|
|
|
|
|
|
|
|
s = [[NSCharacterSet characterSetWithCharactersInString:
|
|
|
|
@"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz$./_"]
|
|
|
|
mutableCopy];
|
|
|
|
[s invert];
|
|
|
|
quotables = [s copy];
|
|
|
|
RELEASE(s);
|
2000-11-01 12:31:21 +00:00
|
|
|
if (cMemberSel == 0)
|
|
|
|
cMemberSel = @selector(characterIsMember:);
|
2000-10-23 11:44:34 +00:00
|
|
|
quotablesImp =
|
|
|
|
(BOOL(*)(id,SEL,unichar)) [quotables methodForSelector: cMemberSel];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSCharacterSet *whitespce = nil;
|
|
|
|
static BOOL (*whitespceImp)(id, SEL, unichar) = 0;
|
|
|
|
static void setupWhitespce()
|
|
|
|
{
|
|
|
|
if (whitespce == nil)
|
|
|
|
{
|
|
|
|
whitespce = [NSCharacterSet characterSetWithCharactersInString:
|
|
|
|
@" \t\r\n\f\b"];
|
|
|
|
IF_NO_GC(RETAIN(whitespce));
|
2000-11-01 12:31:21 +00:00
|
|
|
if (cMemberSel == 0)
|
|
|
|
cMemberSel = @selector(characterIsMember:);
|
2000-10-23 11:44:34 +00:00
|
|
|
whitespceImp =
|
|
|
|
(BOOL(*)(id,SEL,unichar)) [whitespce methodForSelector: cMemberSel];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-05-20 09:20:46 +00:00
|
|
|
/*
|
|
|
|
* Include sequence handling code with instructions to generate search
|
|
|
|
* and compare functions for NSString objects.
|
|
|
|
*/
|
|
|
|
#define GSEQ_STRCOMP strCompNsNs
|
|
|
|
#define GSEQ_STRRANGE strRangeNsNs
|
|
|
|
#define GSEQ_O GSEQ_NS
|
|
|
|
#define GSEQ_S GSEQ_NS
|
|
|
|
#include <GSeq.h>
|
1997-05-03 18:05:21 +00:00
|
|
|
|
2000-10-23 11:44:34 +00:00
|
|
|
|
|
|
|
static id GSPropertyList(NSString *string);
|
|
|
|
static id GSPropertyListFromStringsFormat(NSString *string);
|
1999-02-04 13:49:27 +00:00
|
|
|
|
2000-06-12 05:17:41 +00:00
|
|
|
#if defined(__MINGW__)
|
1999-06-03 10:59:25 +00:00
|
|
|
static unichar pathSepChar = (unichar)'\\';
|
|
|
|
static NSString *pathSepString = @"\\";
|
|
|
|
static NSString *rootPath = @"C:\\";
|
|
|
|
#else
|
1999-01-20 13:28:28 +00:00
|
|
|
static unichar pathSepChar = (unichar)'/';
|
|
|
|
static NSString *pathSepString = @"/";
|
1999-06-03 10:59:25 +00:00
|
|
|
static NSString *rootPath = @"/";
|
|
|
|
#endif
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1999-09-03 11:05:33 +00:00
|
|
|
static BOOL (*sepMember)(NSCharacterSet*, SEL, unichar) = 0;
|
|
|
|
static NSCharacterSet *myPathSeps = nil;
|
1999-01-20 13:28:28 +00:00
|
|
|
/*
|
2000-11-03 10:11:56 +00:00
|
|
|
* We can't have a 'pathSeps' variable initialized in the +initialize
|
1999-01-20 13:28:28 +00:00
|
|
|
* method 'cos that would cause recursion.
|
|
|
|
*/
|
|
|
|
static NSCharacterSet*
|
|
|
|
pathSeps()
|
|
|
|
{
|
1999-09-03 11:05:33 +00:00
|
|
|
if (myPathSeps == nil)
|
1999-01-20 13:28:28 +00:00
|
|
|
{
|
2000-06-12 05:17:41 +00:00
|
|
|
#if defined(__MINGW__)
|
1999-09-03 11:05:33 +00:00
|
|
|
myPathSeps = [NSCharacterSet characterSetWithCharactersInString: @"/\\"];
|
1997-09-13 17:52:31 +00:00
|
|
|
#else
|
1999-09-03 11:05:33 +00:00
|
|
|
myPathSeps = [NSCharacterSet characterSetWithCharactersInString: @"/"];
|
1999-01-20 13:28:28 +00:00
|
|
|
#endif
|
1999-09-28 19:35:09 +00:00
|
|
|
IF_NO_GC(RETAIN(myPathSeps));
|
1999-09-13 04:11:39 +00:00
|
|
|
sepMember = (BOOL (*)(NSCharacterSet*, SEL, unichar))
|
|
|
|
[myPathSeps methodForSelector: @selector(characterIsMember:)];
|
1999-01-20 13:28:28 +00:00
|
|
|
}
|
1999-09-03 11:05:33 +00:00
|
|
|
return myPathSeps;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL
|
|
|
|
pathSepMember(unichar c)
|
|
|
|
{
|
|
|
|
if (sepMember == 0)
|
|
|
|
pathSeps();
|
|
|
|
|
|
|
|
return (*sepMember)(myPathSeps, @selector(characterIsMember:), c);
|
1999-01-20 13:28:28 +00:00
|
|
|
}
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1995-03-12 19:54:14 +00:00
|
|
|
|
1999-09-03 11:05:33 +00:00
|
|
|
|
1995-03-12 19:54:14 +00:00
|
|
|
@implementation NSString
|
1995-04-03 01:35:42 +00:00
|
|
|
|
1998-01-08 15:25:59 +00:00
|
|
|
static NSStringEncoding _DefaultStringEncoding;
|
|
|
|
|
1996-01-23 23:30:05 +00:00
|
|
|
#if HAVE_REGISTER_PRINTF_FUNCTION
|
1996-01-23 23:41:02 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <printf.h>
|
|
|
|
#include <stdarg.h>
|
1996-05-06 18:59:01 +00:00
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
/* <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,
|
1997-09-01 21:59:51 +00:00
|
|
|
it crashes. -mccallum
|
|
|
|
|
|
|
|
Apparently GNU libc 2.xx needs this to be 0 also, along with Linux
|
|
|
|
libc versions 5.2.xx and higher (including libc6, which is just GNU
|
|
|
|
libc). -chung */
|
1996-07-15 18:41:44 +00:00
|
|
|
#define PRINTF_ATSIGN_VA_LIST \
|
|
|
|
(defined(_LINUX_C_LIB_VERSION_MINOR) \
|
|
|
|
&& _LINUX_C_LIB_VERSION_MAJOR <= 5 \
|
|
|
|
&& _LINUX_C_LIB_VERSION_MINOR < 2)
|
1996-05-07 01:14:54 +00:00
|
|
|
|
|
|
|
#if ! PRINTF_ATSIGN_VA_LIST
|
1996-05-06 18:59:01 +00:00
|
|
|
static int
|
1997-09-01 21:59:51 +00:00
|
|
|
arginfo_func (const struct printf_info *info, size_t n, int *argtypes)
|
|
|
|
{
|
1996-05-06 18:59:01 +00:00
|
|
|
*argtypes = PA_POINTER;
|
|
|
|
return 1;
|
|
|
|
}
|
1996-07-15 18:41:44 +00:00
|
|
|
#endif /* !PRINTF_ATSIGN_VA_LIST */
|
1996-05-06 18:59:01 +00:00
|
|
|
|
|
|
|
static int
|
1996-01-23 23:41:02 +00:00
|
|
|
handle_printf_atsign (FILE *stream,
|
|
|
|
const struct printf_info *info,
|
1996-05-07 01:14:54 +00:00
|
|
|
#if PRINTF_ATSIGN_VA_LIST
|
|
|
|
va_list *ap_pointer)
|
1997-09-01 21:59:51 +00:00
|
|
|
#elif defined(_LINUX_C_LIB_VERSION_MAJOR) \
|
|
|
|
&& _LINUX_C_LIB_VERSION_MAJOR < 6
|
1996-05-07 01:14:54 +00:00
|
|
|
const void **const args)
|
1997-09-01 21:59:51 +00:00
|
|
|
#else /* GNU libc needs the following. */
|
|
|
|
const void *const *args)
|
1996-05-07 01:14:54 +00:00
|
|
|
#endif
|
1996-01-23 23:30:05 +00:00
|
|
|
{
|
1996-05-07 01:14:54 +00:00
|
|
|
#if ! PRINTF_ATSIGN_VA_LIST
|
1996-05-06 18:59:01 +00:00
|
|
|
const void *ptr = *args;
|
1996-05-07 01:14:54 +00:00
|
|
|
#endif
|
1996-01-23 23:30:05 +00:00
|
|
|
id string_object;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
/* xxx This implementation may not pay pay attention to as much
|
|
|
|
of printf_info as it should. */
|
|
|
|
|
1996-05-07 01:14:54 +00:00
|
|
|
#if PRINTF_ATSIGN_VA_LIST
|
|
|
|
string_object = va_arg (*ap_pointer, id);
|
|
|
|
#else
|
1996-07-15 18:41:44 +00:00
|
|
|
string_object = *((id*) ptr);
|
1996-05-07 01:14:54 +00:00
|
|
|
#endif
|
1996-01-23 23:30:05 +00:00
|
|
|
len = fprintf(stream, "%*s",
|
|
|
|
(info->left ? - info->width : info->width),
|
2000-11-06 16:44:54 +00:00
|
|
|
[[string_object description] lossyCString]);
|
1996-01-23 23:30:05 +00:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_REGISTER_PRINTF_FUNCTION */
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
1996-11-24 17:20:45 +00:00
|
|
|
if (self == [NSString class])
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-10-30 18:00:27 +00:00
|
|
|
plSel = @selector(initWithCharacters:length:);
|
|
|
|
cMemberSel = @selector(characterIsMember:);
|
1998-01-08 15:25:59 +00:00
|
|
|
_DefaultStringEncoding = GetDefEncoding();
|
2000-10-23 06:18:03 +00:00
|
|
|
NSStringClass = self;
|
2000-10-30 18:00:27 +00:00
|
|
|
[self setVersion: 1];
|
2000-10-23 06:18:03 +00:00
|
|
|
NSMutableStringClass = [NSMutableString class];
|
2000-10-23 11:44:34 +00:00
|
|
|
NSDataClass = [NSData class];
|
2000-11-03 10:11:56 +00:00
|
|
|
GSPlaceholderStringClass = [GSPlaceholderString class];
|
2000-10-23 06:18:03 +00:00
|
|
|
GSStringClass = [GSString class];
|
2000-11-03 10:11:56 +00:00
|
|
|
GSMutableStringClass = [GSMutableString class];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up infrastructure for placeholder strings.
|
|
|
|
*/
|
|
|
|
defaultPlaceholderString = (GSPlaceholderString*)
|
|
|
|
NSAllocateObject(GSPlaceholderStringClass, 0, NSDefaultMallocZone());
|
|
|
|
placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
|
|
|
NSNonRetainedObjectMapValueCallBacks, 0);
|
|
|
|
placeholderLock = [NSLock new];
|
1996-01-23 23:30:05 +00:00
|
|
|
|
|
|
|
#if HAVE_REGISTER_PRINTF_FUNCTION
|
1996-01-23 23:41:02 +00:00
|
|
|
if (register_printf_function ('@',
|
1997-09-01 21:59:51 +00:00
|
|
|
handle_printf_atsign,
|
1996-05-07 01:14:54 +00:00
|
|
|
#if PRINTF_ATSIGN_VA_LIST
|
|
|
|
0))
|
|
|
|
#else
|
1997-09-01 21:59:51 +00:00
|
|
|
arginfo_func))
|
1996-05-07 01:14:54 +00:00
|
|
|
#endif
|
1996-03-26 19:35:47 +00:00
|
|
|
[NSException raise: NSGenericException
|
|
|
|
format: @"register printf handling of %%@ failed"];
|
1996-01-23 23:30:05 +00:00
|
|
|
#endif /* HAVE_REGISTER_PRINTF_FUNCTION */
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) allocWithZone: (NSZone*)z
|
1995-11-19 20:29:39 +00:00
|
|
|
{
|
2000-10-23 06:18:03 +00:00
|
|
|
if (self == NSStringClass)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
/*
|
|
|
|
* For a constant string, we return a placeholder object that can
|
|
|
|
* be converted to a real object when its initialisation method
|
|
|
|
* is called.
|
|
|
|
*/
|
|
|
|
if (z == NSDefaultMallocZone() || z == 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* As a special case, we can return a placeholder for a string
|
|
|
|
* in the default malloc zone extremely efficiently.
|
|
|
|
*/
|
|
|
|
return defaultPlaceholderString;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
id obj;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For anything other than the default zone, we need to
|
|
|
|
* locate the correct placeholder in the (lock protected)
|
|
|
|
* table of placeholders.
|
|
|
|
*/
|
|
|
|
[placeholderLock lock];
|
|
|
|
obj = (id)NSMapGet(placeholderMap, (void*)z);
|
|
|
|
if (obj == nil)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* There is no placeholder object for this zone, so we
|
|
|
|
* create a new one and use that.
|
|
|
|
*/
|
|
|
|
obj = (id)NSAllocateObject(GSPlaceholderStringClass, 0, z);
|
|
|
|
NSMapInsert(placeholderMap, (void*)z, (void*)obj);
|
|
|
|
}
|
|
|
|
[placeholderLock unlock];
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (GSObjCIsKindOf(self, GSStringClass) == YES)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Called +allocWithZone: on private string class"];
|
|
|
|
return nil; /* NOT REACHED */
|
2000-08-07 22:00:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
/*
|
|
|
|
* For user provided strings, we simply allocate an object of
|
|
|
|
* the given class.
|
|
|
|
*/
|
2000-08-07 22:00:31 +00:00
|
|
|
return NSAllocateObject (self, 0, z);
|
|
|
|
}
|
1995-11-19 20:29:39 +00:00
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
// Creating Temporary Strings
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) string
|
1997-05-03 17:05:57 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()] init]);
|
1997-05-03 17:05:57 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) stringWithString: (NSString*)aString
|
1997-12-08 20:04:16 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
NSString *obj;
|
|
|
|
|
|
|
|
obj = [self allocWithZone: NSDefaultMallocZone()];
|
|
|
|
obj = [obj initWithString: aString];
|
|
|
|
return AUTORELEASE(obj);
|
1997-12-08 20:04:16 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) stringWithCharacters: (const unichar*)chars
|
|
|
|
length: (unsigned)length
|
1997-05-03 18:05:21 +00:00
|
|
|
{
|
2000-10-29 14:52:33 +00:00
|
|
|
NSString *obj;
|
|
|
|
|
2000-11-03 10:11:56 +00:00
|
|
|
obj = [self allocWithZone: NSDefaultMallocZone()];
|
2000-10-29 14:52:33 +00:00
|
|
|
obj = [obj initWithCharacters: chars length: length];
|
|
|
|
return AUTORELEASE(obj);
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) stringWithCString: (const char*) byteString
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-10-29 14:52:33 +00:00
|
|
|
NSString *obj;
|
|
|
|
unsigned length = strlen(byteString);
|
|
|
|
|
2000-11-03 10:11:56 +00:00
|
|
|
obj = [self allocWithZone: NSDefaultMallocZone()];
|
2000-10-29 14:52:33 +00:00
|
|
|
obj = [obj initWithCString: byteString length: length];
|
|
|
|
return AUTORELEASE(obj);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) stringWithCString: (const char*)byteString
|
|
|
|
length: (unsigned)length
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-10-29 14:52:33 +00:00
|
|
|
NSString *obj;
|
|
|
|
|
2000-11-03 10:11:56 +00:00
|
|
|
obj = [self allocWithZone: NSDefaultMallocZone()];
|
2000-10-29 14:52:33 +00:00
|
|
|
obj = [obj initWithCString: byteString length: length];
|
|
|
|
return AUTORELEASE(obj);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
+ (id) stringWithUTF8String: (const char *)bytes
|
2000-09-12 23:09:50 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
NSString *obj;
|
|
|
|
|
|
|
|
obj = [self allocWithZone: NSDefaultMallocZone()];
|
|
|
|
obj = [obj initWithUTF8String: bytes];
|
|
|
|
return AUTORELEASE(obj);
|
2000-09-12 23:09:50 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) stringWithContentsOfFile: (NSString *)path
|
1997-03-03 20:07:35 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
NSString *obj;
|
|
|
|
|
|
|
|
obj = [self allocWithZone: NSDefaultMallocZone()];
|
|
|
|
obj = [obj initWithContentsOfFile: path];
|
|
|
|
return AUTORELEASE(obj);
|
1997-03-03 20:07:35 +00:00
|
|
|
}
|
|
|
|
|
2000-09-12 23:09:50 +00:00
|
|
|
+ (id) stringWithContentsOfURL: (NSURL *)url
|
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
NSString *obj;
|
|
|
|
|
|
|
|
obj = [self allocWithZone: NSDefaultMallocZone()];
|
|
|
|
obj = [obj initWithContentsOfURL: url];
|
|
|
|
return AUTORELEASE(obj);
|
2000-09-12 23:09:50 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) stringWithFormat: (NSString*)format,...
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
id ret;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
1999-05-06 14:42:26 +00:00
|
|
|
if (format == nil)
|
|
|
|
ret = nil;
|
|
|
|
else
|
1999-06-03 10:59:25 +00:00
|
|
|
ret = AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithFormat: format arguments: ap]);
|
1995-04-03 01:35:42 +00:00
|
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (id) stringWithFormat: (NSString*)format
|
|
|
|
arguments: (va_list)argList
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithFormat: format arguments: argList]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
// Initializing Newly Allocated Strings
|
1995-04-03 01:35:42 +00:00
|
|
|
|
2000-10-31 19:07:35 +00:00
|
|
|
/* This is the designated initializer. */
|
1997-05-03 18:05:21 +00:00
|
|
|
- (id) initWithCharactersNoCopy: (unichar*)chars
|
1999-11-26 19:43:43 +00:00
|
|
|
length: (unsigned)length
|
|
|
|
freeWhenDone: (BOOL)flag
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
[self subclassResponsibility: _cmd];
|
1995-04-03 01:35:42 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCharacters: (const unichar*)chars
|
1999-06-21 08:30:26 +00:00
|
|
|
length: (unsigned)length
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
if (length > 0)
|
|
|
|
{
|
2000-10-20 10:30:51 +00:00
|
|
|
int i;
|
|
|
|
BOOL isAscii = YES;
|
2000-09-27 15:26:16 +00:00
|
|
|
|
2000-10-20 10:30:51 +00:00
|
|
|
if (chars == 0)
|
2000-09-27 15:26:16 +00:00
|
|
|
{
|
2000-10-20 10:30:51 +00:00
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
format: @"nul pointer but non-zero length"];
|
|
|
|
}
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
if (chars[i] >= 128)
|
|
|
|
{
|
|
|
|
isAscii = NO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isAscii == YES)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
s = NSZoneMalloc(GSObjCZone(self), length);
|
2000-10-20 10:30:51 +00:00
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
s[i] = (unsigned char)chars[i];
|
|
|
|
}
|
|
|
|
self = [self initWithCStringNoCopy: s
|
|
|
|
length: length
|
|
|
|
freeWhenDone: YES];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unichar *s;
|
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
s = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*length);
|
2000-10-20 10:30:51 +00:00
|
|
|
|
2000-09-27 15:26:16 +00:00
|
|
|
memcpy(s, chars, sizeof(unichar)*length);
|
2000-10-20 10:30:51 +00:00
|
|
|
self = [self initWithCharactersNoCopy: s
|
|
|
|
length: length
|
|
|
|
freeWhenDone: YES];
|
2000-09-27 15:26:16 +00:00
|
|
|
}
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCharactersNoCopy: 0 length: 0 freeWhenDone: NO];
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
1998-10-15 05:03:16 +00:00
|
|
|
|
2000-09-27 15:26:16 +00:00
|
|
|
return self;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
- (id) initWithCStringNoCopy: (char*)byteString
|
1999-06-21 08:30:26 +00:00
|
|
|
length: (unsigned)length
|
2000-09-27 15:26:16 +00:00
|
|
|
freeWhenDone: (BOOL)flag
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-10-31 19:07:35 +00:00
|
|
|
unichar *buf;
|
|
|
|
|
|
|
|
buf = (unichar*)NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*length);
|
|
|
|
length = encode_strtoustr(buf, byteString, length, _DefaultStringEncoding);
|
|
|
|
if (flag == YES && byteString != 0)
|
|
|
|
{
|
|
|
|
NSZoneFree(NSZoneFromPointer(byteString), byteString);
|
|
|
|
}
|
|
|
|
self = [self initWithCharactersNoCopy: buf length: length freeWhenDone: YES];
|
1995-04-03 01:35:42 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
- (id) initWithCString: (const char*)byteString length: (unsigned)length
|
1997-05-03 18:05:21 +00:00
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
if (length > 0)
|
|
|
|
{
|
2000-10-31 16:17:33 +00:00
|
|
|
char *s = NSZoneMalloc(GSObjCZone(self), length);
|
2000-09-27 15:26:16 +00:00
|
|
|
|
|
|
|
if (byteString != 0)
|
1999-07-02 13:26:37 +00:00
|
|
|
{
|
|
|
|
memcpy(s, byteString, length);
|
|
|
|
}
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCStringNoCopy: s length: length freeWhenDone: YES];
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCStringNoCopy: 0 length: 0 freeWhenDone: NO];
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
1998-10-15 05:03:16 +00:00
|
|
|
|
2000-09-27 15:26:16 +00:00
|
|
|
return self;
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
1997-01-09 16:24:07 +00:00
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
- (id) initWithCString: (const char*)byteString
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self initWithCString: byteString
|
2000-09-27 15:26:16 +00:00
|
|
|
length: (byteString ? strlen(byteString) : 0)];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
- (id) initWithString: (NSString*)string
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
unsigned length = [string length];
|
1998-10-15 05:03:16 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (length > 0)
|
|
|
|
{
|
2000-10-31 16:17:33 +00:00
|
|
|
unichar *s = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*length);
|
2000-09-27 15:26:16 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
[string getCharacters: s];
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCharactersNoCopy: s
|
|
|
|
length: length
|
|
|
|
freeWhenDone: YES];
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCharactersNoCopy: 0
|
|
|
|
length: 0
|
|
|
|
freeWhenDone: NO];
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
2000-09-27 15:26:16 +00:00
|
|
|
return self;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
- (id) initWithUTF8String: (const char *)bytes
|
2000-09-12 23:09:50 +00:00
|
|
|
{
|
2000-09-18 23:28:00 +00:00
|
|
|
unsigned length = strlen(bytes);
|
|
|
|
|
|
|
|
if (length > 0)
|
|
|
|
{
|
2000-09-30 04:54:43 +00:00
|
|
|
unsigned i;
|
2000-09-27 15:26:16 +00:00
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
/*
|
|
|
|
* Check to see if we have in fact got an ascii string
|
|
|
|
*/
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
if (((unsigned char*)bytes)[i] > 127)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == length)
|
|
|
|
{
|
|
|
|
self = [self initWithCString: bytes length: length];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unichar *s;
|
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
s = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*length);
|
2000-09-30 04:54:43 +00:00
|
|
|
length = encode_strtoustr(s, bytes, length+1, NSUTF8StringEncoding);
|
|
|
|
self = [self initWithCharactersNoCopy: s
|
|
|
|
length: length
|
|
|
|
freeWhenDone: YES];
|
|
|
|
}
|
2000-09-18 23:28:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCharactersNoCopy: 0
|
|
|
|
length: 0
|
|
|
|
freeWhenDone: NO];
|
2000-09-18 23:28:00 +00:00
|
|
|
}
|
2000-09-27 15:26:16 +00:00
|
|
|
return self;
|
2000-09-12 23:09:50 +00:00
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
- (id) initWithFormat: (NSString*)format,...
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
2000-07-07 19:19:26 +00:00
|
|
|
self = [self initWithFormat: format locale: nil arguments: ap];
|
1995-04-03 01:35:42 +00:00
|
|
|
va_end(ap);
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2000-07-07 19:19:26 +00:00
|
|
|
- (id) initWithFormat: (NSString*)format
|
|
|
|
locale: (NSDictionary*)locale
|
|
|
|
{
|
|
|
|
return [self initWithFormat: format locale: locale arguments: NULL];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithFormat: (NSString*)format
|
|
|
|
arguments: (va_list)arg_list
|
|
|
|
{
|
|
|
|
return [self initWithFormat: format locale: nil arguments: arg_list];
|
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
/* xxx Change this when we have non-CString classes */
|
|
|
|
- (id) initWithFormat: (NSString*)format
|
2000-07-07 19:19:26 +00:00
|
|
|
locale: (NSDictionary*)locale
|
|
|
|
arguments: (va_list)arg_list
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-03-24 00:25:42 +00:00
|
|
|
#if defined(HAVE_VSPRINTF) || defined(HAVE_VASPRINTF)
|
2000-12-14 09:47:02 +00:00
|
|
|
const char *format_cp = [format lossyCString];
|
1996-05-31 15:10:53 +00:00
|
|
|
int format_len = strlen (format_cp);
|
2000-03-24 00:25:42 +00:00
|
|
|
#if HAVE_VASPRINTF
|
|
|
|
char *buf;
|
|
|
|
int printed_len = 0;
|
|
|
|
NSString *ret;
|
|
|
|
|
|
|
|
#if ! HAVE_REGISTER_PRINTF_FUNCTION
|
2000-10-31 16:17:33 +00:00
|
|
|
NSZone *z = GSObjCZone(self);
|
2000-03-24 00:25:42 +00:00
|
|
|
|
|
|
|
/* 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; /* points to a location inside format_cp_copy */
|
|
|
|
char *format_to_go = format_cp_copy;
|
|
|
|
char *buf_l;
|
|
|
|
#define _PRINTF_BUF_LEN 256
|
|
|
|
int printed_local_len, avail_len = _PRINTF_BUF_LEN;
|
|
|
|
int cstring_len;
|
|
|
|
|
|
|
|
buf = NSZoneMalloc(z, _PRINTF_BUF_LEN);
|
|
|
|
strcpy (format_cp_copy, format_cp);
|
|
|
|
/* Loop once for each `%@' in the format string. */
|
|
|
|
while ((atsign_pos = strstr (format_to_go, "%@")))
|
|
|
|
{
|
|
|
|
const char *cstring;
|
|
|
|
char *formatter_pos; // Position for formatter.
|
|
|
|
|
|
|
|
/* 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_local_len = VASPRINTF_LENGTH (vasprintf (&buf_l,
|
2000-09-08 18:04:51 +00:00
|
|
|
format_to_go, arg_list));
|
2000-03-24 00:25:42 +00:00
|
|
|
if(buf_l)
|
|
|
|
{
|
|
|
|
if(avail_len < printed_local_len+1)
|
|
|
|
{
|
|
|
|
NS_DURING
|
|
|
|
{
|
|
|
|
buf = NSZoneRealloc(z, buf,
|
2000-09-08 18:04:51 +00:00
|
|
|
printed_len+printed_local_len+_PRINTF_BUF_LEN);
|
2000-03-24 00:25:42 +00:00
|
|
|
avail_len += _PRINTF_BUF_LEN;
|
|
|
|
}
|
|
|
|
NS_HANDLER
|
|
|
|
{
|
|
|
|
free(buf_l);
|
|
|
|
[localException raise];
|
|
|
|
}
|
|
|
|
NS_ENDHANDLER
|
|
|
|
}
|
|
|
|
memcpy(&buf[printed_len], buf_l, printed_local_len+1);
|
|
|
|
avail_len -= printed_local_len;
|
|
|
|
printed_len += printed_local_len;
|
|
|
|
free(buf_l);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[NSException raise: NSMallocException
|
2000-09-08 18:04:51 +00:00
|
|
|
format: @"No available memory"];
|
2000-03-24 00:25:42 +00:00
|
|
|
}
|
|
|
|
/* Skip arguments used in last vsprintf(). */
|
|
|
|
while ((formatter_pos = strchr(format_to_go, '%')))
|
|
|
|
{
|
|
|
|
char *spec_pos; // Position of conversion specifier.
|
|
|
|
|
|
|
|
if (*(formatter_pos+1) == '%')
|
|
|
|
{
|
|
|
|
format_to_go = formatter_pos+2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
spec_pos = strpbrk(formatter_pos+1, "dioxXucsfeEgGpn\0");
|
|
|
|
switch (*spec_pos)
|
|
|
|
{
|
|
|
|
#ifndef powerpc
|
|
|
|
/* FIXME: vsprintf on powerpc apparently advances the arg list
|
|
|
|
so this doesn't need to be done. Make a more general check
|
|
|
|
for this */
|
|
|
|
case 'd': case 'i': case 'o':
|
|
|
|
case 'x': case 'X': case 'u': case 'c':
|
|
|
|
va_arg(arg_list, int);
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if (*(spec_pos - 1) == '*')
|
|
|
|
va_arg(arg_list, int*);
|
|
|
|
va_arg(arg_list, char*);
|
|
|
|
break;
|
|
|
|
case 'f': case 'e': case 'E': case 'g': case 'G':
|
|
|
|
va_arg(arg_list, double);
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
va_arg(arg_list, void*);
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
va_arg(arg_list, int*);
|
|
|
|
break;
|
|
|
|
#endif /* NOT powerpc */
|
|
|
|
case '\0':
|
|
|
|
spec_pos--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
format_to_go = spec_pos+1;
|
|
|
|
}
|
|
|
|
/* Get a C-string (char*) from the String object, and print it. */
|
2000-12-14 09:47:02 +00:00
|
|
|
cstring = [[(id) va_arg (arg_list, id) description] lossyCString];
|
2000-03-24 00:25:42 +00:00
|
|
|
if (!cstring)
|
|
|
|
cstring = "<null string>";
|
|
|
|
cstring_len = strlen(cstring);
|
|
|
|
|
|
|
|
if(cstring_len)
|
|
|
|
{
|
|
|
|
if(avail_len < cstring_len+1)
|
|
|
|
{
|
|
|
|
buf = NSZoneRealloc(z, buf,
|
|
|
|
printed_len+cstring_len+_PRINTF_BUF_LEN);
|
|
|
|
avail_len += _PRINTF_BUF_LEN;
|
|
|
|
}
|
|
|
|
memcpy(&buf[printed_len], cstring, cstring_len+1);
|
|
|
|
avail_len -= cstring_len;
|
|
|
|
printed_len += cstring_len;
|
|
|
|
}
|
|
|
|
/* Skip over this `%@', and look for another one. */
|
|
|
|
format_to_go = atsign_pos + 2;
|
|
|
|
}
|
|
|
|
/* Print the rest of the string after the last `%@'. */
|
|
|
|
printed_local_len = VASPRINTF_LENGTH (vasprintf (&buf_l,
|
2000-09-08 18:04:51 +00:00
|
|
|
format_to_go, arg_list));
|
2000-03-24 00:25:42 +00:00
|
|
|
if(buf_l)
|
|
|
|
{
|
|
|
|
if(avail_len < printed_local_len+1)
|
|
|
|
{
|
|
|
|
NS_DURING
|
|
|
|
{
|
|
|
|
buf = NSZoneRealloc(z, buf,
|
2000-09-08 18:04:51 +00:00
|
|
|
printed_len+printed_local_len+_PRINTF_BUF_LEN);
|
2000-03-24 00:25:42 +00:00
|
|
|
avail_len += _PRINTF_BUF_LEN;
|
|
|
|
}
|
|
|
|
NS_HANDLER
|
|
|
|
{
|
|
|
|
free(buf_l);
|
|
|
|
[localException raise];
|
|
|
|
}
|
|
|
|
NS_ENDHANDLER
|
|
|
|
}
|
|
|
|
memcpy(&buf[printed_len], buf_l, printed_local_len+1);
|
|
|
|
avail_len -= printed_local_len;
|
|
|
|
printed_len += printed_local_len;
|
|
|
|
free(buf_l);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[NSException raise: NSMallocException
|
|
|
|
format: @"No available memory"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else /* HAVE_VSPRINTF */
|
|
|
|
/* The available libc has `register_printf_function()', so the `%@'
|
|
|
|
printf directive is handled by printf and friends. */
|
|
|
|
printed_len = VASPRINTF_LENGTH (vasprintf (&buf, format_cp, arg_list));
|
|
|
|
|
|
|
|
if(!buf)
|
|
|
|
{
|
|
|
|
[NSException raise: NSMallocException
|
|
|
|
format: @"No available memory"];
|
|
|
|
}
|
|
|
|
#endif /* !HAVE_REGISTER_PRINTF_FUNCTION */
|
|
|
|
|
|
|
|
ret = [self initWithCString: buf];
|
|
|
|
#if ! HAVE_REGISTER_PRINTF_FUNCTION
|
|
|
|
NSZoneFree(z, buf);
|
|
|
|
#else
|
|
|
|
free(buf);
|
|
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
#else
|
1996-07-15 18:41:44 +00:00
|
|
|
/* xxx horrible disgusting BUFFER_EXTRA arbitrary limit; fix this! */
|
1997-01-09 16:01:52 +00:00
|
|
|
#define BUFFER_EXTRA 1024*500
|
1996-07-15 18:41:44 +00:00
|
|
|
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
|
1996-09-02 13:20:20 +00:00
|
|
|
writing to them will cause a segfault.) */
|
1996-07-15 18:41:44 +00:00
|
|
|
char format_cp_copy[format_len+1];
|
1996-09-25 13:45:31 +00:00
|
|
|
char *atsign_pos; /* points to a location inside format_cp_copy */
|
1996-07-15 18:41:44 +00:00
|
|
|
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;
|
1997-09-01 21:59:51 +00:00
|
|
|
char *formatter_pos; // Position for formatter.
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
/* 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 '%@' */
|
1996-10-31 18:27:04 +00:00
|
|
|
printed_len += VSPRINTF_LENGTH (vsprintf (buf+printed_len,
|
|
|
|
format_to_go, arg_list));
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Skip arguments used in last vsprintf(). */
|
|
|
|
while ((formatter_pos = strchr(format_to_go, '%')))
|
|
|
|
{
|
|
|
|
char *spec_pos; // Position of conversion specifier.
|
|
|
|
|
|
|
|
if (*(formatter_pos+1) == '%')
|
|
|
|
{
|
|
|
|
format_to_go = formatter_pos+2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
spec_pos = strpbrk(formatter_pos+1, "dioxXucsfeEgGpn\0");
|
|
|
|
switch (*spec_pos)
|
|
|
|
{
|
1999-08-25 14:47:19 +00:00
|
|
|
#ifndef powerpc
|
|
|
|
/* FIXME: vsprintf on powerpc apparently advances the arg list
|
|
|
|
so this doesn't need to be done. Make a more general check
|
|
|
|
for this */
|
1999-05-06 14:42:26 +00:00
|
|
|
case 'd': case 'i': case 'o':
|
|
|
|
case 'x': case 'X': case 'u': case 'c':
|
2000-06-06 16:50:52 +00:00
|
|
|
(void)va_arg(arg_list, int);
|
1997-09-01 21:59:51 +00:00
|
|
|
break;
|
1999-05-06 14:42:26 +00:00
|
|
|
case 's':
|
1997-09-18 14:56:47 +00:00
|
|
|
if (*(spec_pos - 1) == '*')
|
2000-06-06 16:50:52 +00:00
|
|
|
(void)va_arg(arg_list, int*);
|
|
|
|
(void)va_arg(arg_list, char*);
|
1997-09-01 21:59:51 +00:00
|
|
|
break;
|
1999-05-06 14:42:26 +00:00
|
|
|
case 'f': case 'e': case 'E': case 'g': case 'G':
|
2000-06-06 16:50:52 +00:00
|
|
|
(void)va_arg(arg_list, double);
|
1997-09-01 21:59:51 +00:00
|
|
|
break;
|
1999-05-06 14:42:26 +00:00
|
|
|
case 'p':
|
2000-06-06 16:50:52 +00:00
|
|
|
(void)va_arg(arg_list, void*);
|
1997-09-01 21:59:51 +00:00
|
|
|
break;
|
1999-05-06 14:42:26 +00:00
|
|
|
case 'n':
|
2000-06-06 16:50:52 +00:00
|
|
|
(void)va_arg(arg_list, int*);
|
1997-09-01 21:59:51 +00:00
|
|
|
break;
|
1999-08-25 14:47:19 +00:00
|
|
|
#endif /* NOT powerpc */
|
1999-05-06 14:42:26 +00:00
|
|
|
case '\0':
|
1997-09-01 21:59:51 +00:00
|
|
|
spec_pos--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
format_to_go = spec_pos+1;
|
|
|
|
}
|
1996-07-15 18:41:44 +00:00
|
|
|
/* Get a C-string (char*) from the String object, and print it. */
|
2000-12-14 09:47:02 +00:00
|
|
|
cstring = [[(id) va_arg (arg_list, id) description] lossyCString];
|
1997-11-12 15:37:27 +00:00
|
|
|
if (!cstring)
|
|
|
|
cstring = "<null string>";
|
1996-07-15 18:41:44 +00:00
|
|
|
strcat (buf+printed_len, cstring);
|
|
|
|
printed_len += strlen (cstring);
|
|
|
|
/* Skip over this `%@', and look for another one. */
|
|
|
|
format_to_go = atsign_pos + 2;
|
|
|
|
}
|
|
|
|
/* Print the rest of the string after the last `%@'. */
|
1996-10-31 18:27:04 +00:00
|
|
|
printed_len += VSPRINTF_LENGTH (vsprintf (buf+printed_len,
|
|
|
|
format_to_go, arg_list));
|
1996-07-15 18:41:44 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* The available libc has `register_printf_function()', so the `%@'
|
|
|
|
printf directive is handled by printf and friends. */
|
1996-10-31 18:27:04 +00:00
|
|
|
printed_len = VSPRINTF_LENGTH (vsprintf (buf, format_cp, arg_list));
|
1996-07-15 18:41:44 +00:00
|
|
|
#endif /* !HAVE_REGISTER_PRINTF_FUNCTION */
|
1996-05-31 15:10:53 +00:00
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
/* Raise an exception if we overran our buffer. */
|
1996-05-31 15:10:53 +00:00
|
|
|
NSParameterAssert (printed_len < format_len + BUFFER_EXTRA - 1);
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self initWithCString: buf];
|
2000-03-24 00:25:42 +00:00
|
|
|
#endif /* HAVE_VASPRINTF */
|
|
|
|
#else /* HAVE_VSPRINTF || HAVE_VASPRINTF */
|
1996-07-15 18:41:44 +00:00
|
|
|
[self notImplemented: _cmd];
|
1995-04-03 01:35:42 +00:00
|
|
|
return self;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
- (id) initWithData: (NSData*)data
|
1999-07-02 13:26:37 +00:00
|
|
|
encoding: (NSStringEncoding)encoding
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
if ((encoding == [NSString defaultCStringEncoding])
|
|
|
|
|| (encoding == NSASCIIStringEncoding))
|
1998-10-15 05:03:16 +00:00
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
unsigned len = [data length];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (len > 0)
|
|
|
|
{
|
2000-10-31 16:17:33 +00:00
|
|
|
char *s = NSZoneMalloc(GSObjCZone(self), len);
|
2000-09-27 15:26:16 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
[data getBytes: s];
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCStringNoCopy: s length: len freeWhenDone: YES];
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCStringNoCopy: 0 length: 0 freeWhenDone: NO];
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
2000-09-27 15:26:16 +00:00
|
|
|
return self;
|
2000-09-30 04:54:43 +00:00
|
|
|
}
|
|
|
|
else if (encoding == NSUTF8StringEncoding)
|
|
|
|
{
|
|
|
|
unsigned length = [data length];
|
|
|
|
const char *bytes = [data bytes];
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if we have in fact got an ascii string
|
|
|
|
*/
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
if (((unsigned char*)bytes)[i] > 127)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == length)
|
|
|
|
{
|
|
|
|
self = [self initWithCString: bytes length: length];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unichar *u;
|
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
u = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*length);
|
2000-09-30 04:54:43 +00:00
|
|
|
length = encode_strtoustr(u, bytes, length+1, NSUTF8StringEncoding);
|
|
|
|
self = [self initWithCharactersNoCopy: u
|
|
|
|
length: length
|
|
|
|
freeWhenDone: YES];
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
|
|
|
else
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
unsigned len = [data length];
|
|
|
|
unichar *u;
|
|
|
|
unsigned count;
|
|
|
|
const unsigned char *b;
|
|
|
|
|
|
|
|
if (len < 2)
|
2000-09-27 15:26:16 +00:00
|
|
|
return [self initWithCStringNoCopy: 0 length: 0 freeWhenDone: NO];
|
1999-07-02 13:26:37 +00:00
|
|
|
|
2000-09-27 15:26:16 +00:00
|
|
|
b = [data bytes];
|
2000-10-31 16:17:33 +00:00
|
|
|
u = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*(len+1));
|
2000-09-27 15:26:16 +00:00
|
|
|
if (encoding == NSUnicodeStringEncoding)
|
1998-10-15 05:03:16 +00:00
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
if ((b[0]==0xFE) & (b[1]==0xFF))
|
|
|
|
{
|
|
|
|
for (count = 2; count < (len - 1); count += 2)
|
|
|
|
{
|
|
|
|
u[count/2 - 1] = 256*b[count] + b[count + 1];
|
|
|
|
}
|
|
|
|
}
|
1998-10-15 05:03:16 +00:00
|
|
|
else
|
2000-09-27 15:26:16 +00:00
|
|
|
{
|
|
|
|
for (count = 2; count < (len - 1); count += 2)
|
|
|
|
{
|
|
|
|
u[count/2 - 1] = 256*b[count + 1] + b[count];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
count = count/2 - 1;
|
1998-10-15 05:03:16 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
else
|
2000-09-27 15:26:16 +00:00
|
|
|
{
|
|
|
|
count = encode_strtoustr(u, b, len, encoding);
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
2000-09-27 15:26:16 +00:00
|
|
|
self = [self initWithCharactersNoCopy: u length: count freeWhenDone: YES];
|
1998-10-15 05:03:16 +00:00
|
|
|
}
|
1997-05-03 18:05:21 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithContentsOfFile: (NSString*)path
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
NSStringEncoding enc;
|
2000-10-23 11:44:34 +00:00
|
|
|
NSData *d = [NSDataClass dataWithContentsOfFile: path];
|
2000-08-07 22:00:31 +00:00
|
|
|
const unsigned char *test;
|
1997-05-03 18:05:21 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (d == nil)
|
|
|
|
return nil;
|
|
|
|
if ([d length] < 2)
|
|
|
|
return @"";
|
|
|
|
test = [d bytes];
|
2000-09-27 15:26:16 +00:00
|
|
|
if (test != 0 &&
|
|
|
|
(((test[0]==0xFF) && (test[1]==0xFE))
|
|
|
|
|| ((test[1]==0xFF) && (test[0]==0xFE))))
|
1998-01-08 15:25:59 +00:00
|
|
|
enc = NSUnicodeStringEncoding;
|
|
|
|
else
|
|
|
|
enc = [NSString defaultCStringEncoding];
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self initWithData: d encoding: enc];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
2000-09-12 23:09:50 +00:00
|
|
|
- (id) initWithContentsOfURL: (NSURL*)url
|
|
|
|
{
|
|
|
|
NSStringEncoding enc;
|
2000-10-23 11:44:34 +00:00
|
|
|
NSData *d = [NSDataClass dataWithContentsOfURL: url];
|
2000-09-12 23:09:50 +00:00
|
|
|
const unsigned char *test;
|
|
|
|
|
|
|
|
if (d == nil)
|
|
|
|
return nil;
|
|
|
|
if ([d length] < 2)
|
|
|
|
return @"";
|
|
|
|
test = [d bytes];
|
2000-09-27 15:26:16 +00:00
|
|
|
if (test != 0
|
|
|
|
&& (((test[0]==0xFF) && (test[1]==0xFE))
|
|
|
|
|| ((test[1]==0xFF) && (test[0]==0xFE))))
|
2000-09-12 23:09:50 +00:00
|
|
|
enc = NSUnicodeStringEncoding;
|
|
|
|
else
|
|
|
|
enc = [NSString defaultCStringEncoding];
|
|
|
|
return [self initWithData: d encoding: enc];
|
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
- (id) init
|
|
|
|
{
|
2000-10-05 15:17:18 +00:00
|
|
|
self = [self initWithCharactersNoCopy: 0 length: 0 freeWhenDone: 0];
|
1999-07-02 13:26:37 +00:00
|
|
|
return self;
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
1995-04-03 01:35:42 +00:00
|
|
|
|
|
|
|
// Getting a String's Length
|
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
- (unsigned) length
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self subclassResponsibility: _cmd];
|
1997-05-03 18:05:21 +00:00
|
|
|
return 0;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Accessing Characters
|
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
- (unichar) characterAtIndex: (unsigned)index
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self subclassResponsibility: _cmd];
|
1997-05-03 18:05:21 +00:00
|
|
|
return (unichar)0;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Inefficient. Should be overridden */
|
|
|
|
- (void) getCharacters: (unichar*)buffer
|
|
|
|
{
|
2000-10-05 15:17:18 +00:00
|
|
|
[self getCharacters: buffer range: ((NSRange){0, [self length]})];
|
1995-04-03 01:35:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Inefficient. Should be overridden */
|
|
|
|
- (void) getCharacters: (unichar*)buffer
|
1999-05-20 09:20:46 +00:00
|
|
|
range: (NSRange)aRange
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-06-04 15:15:59 +00:00
|
|
|
unsigned l = [self length];
|
|
|
|
unsigned i;
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1999-06-04 15:15:59 +00:00
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
GS_RANGE_CHECK(aRange, l);
|
1999-06-04 15:15:59 +00:00
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
2000-10-05 15:17:18 +00:00
|
|
|
|
1996-09-02 13:38:19 +00:00
|
|
|
for (i = 0; i < aRange.length; i++)
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
buffer[i] = (*caiImp)(self, caiSel, aRange.location + i);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combining Strings
|
|
|
|
|
|
|
|
- (NSString*) stringByAppendingFormat: (NSString*)format,...
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
id ret;
|
|
|
|
va_start(ap, format);
|
1999-05-06 14:42:26 +00:00
|
|
|
ret = [self stringByAppendingString:
|
|
|
|
[NSString stringWithFormat: format arguments: ap]];
|
1995-04-03 01:35:42 +00:00
|
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) stringByAppendingString: (NSString*)aString
|
|
|
|
{
|
2000-09-27 15:26:16 +00:00
|
|
|
unsigned len = [self length];
|
|
|
|
unsigned otherLength = [aString length];
|
2000-10-31 16:17:33 +00:00
|
|
|
NSZone *z = GSObjCZone(self);
|
2000-09-27 15:26:16 +00:00
|
|
|
unichar *s = NSZoneMalloc(z, (len+otherLength)*sizeof(unichar));
|
|
|
|
NSString *tmp;
|
1998-10-15 05:03:16 +00:00
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
[self getCharacters: s];
|
2000-09-27 15:26:16 +00:00
|
|
|
[aString getCharacters: s + len];
|
2000-11-03 10:11:56 +00:00
|
|
|
tmp = [[NSStringClass allocWithZone: z] initWithCharactersNoCopy: s
|
2000-10-23 06:18:03 +00:00
|
|
|
length: len + otherLength freeWhenDone: YES];
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE(tmp);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Dividing Strings into Substrings
|
|
|
|
|
|
|
|
- (NSArray*) componentsSeparatedByString: (NSString*)separator
|
|
|
|
{
|
1997-09-13 17:52:31 +00:00
|
|
|
NSRange search, complete;
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange found;
|
1996-03-19 01:44:01 +00:00
|
|
|
NSMutableArray *array = [NSMutableArray array];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
|
1997-03-03 20:07:35 +00:00
|
|
|
search = NSMakeRange (0, [self length]);
|
1997-09-13 17:52:31 +00:00
|
|
|
complete = search;
|
1996-03-19 01:44:01 +00:00
|
|
|
found = [self rangeOfString: separator];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
while (found.length)
|
|
|
|
{
|
|
|
|
NSRange current;
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1996-03-19 01:44:01 +00:00
|
|
|
current = NSMakeRange (search.location,
|
|
|
|
found.location - search.location);
|
1999-06-21 08:30:26 +00:00
|
|
|
[array addObject: [self substringWithRange: current]];
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
search = NSMakeRange (found.location + found.length,
|
1997-09-13 17:52:31 +00:00
|
|
|
complete.length - found.location - found.length);
|
1996-03-19 01:44:01 +00:00
|
|
|
found = [self rangeOfString: separator
|
|
|
|
options: 0
|
|
|
|
range: search];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
}
|
1997-03-03 20:07:35 +00:00
|
|
|
// Add the last search string range
|
1999-06-21 08:30:26 +00:00
|
|
|
[array addObject: [self substringWithRange: search]];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
|
|
|
|
// FIXME: Need to make mutable array into non-mutable array?
|
|
|
|
return array;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
- (NSString*) substringFromIndex: (unsigned)index
|
|
|
|
{
|
|
|
|
return [self substringWithRange: ((NSRange){index, [self length]-index})];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) substringToIndex: (unsigned)index
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
return [self substringWithRange: ((NSRange){0,index})];;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) substringFromRange: (NSRange)aRange
|
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
return [self substringWithRange: aRange];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) substringWithRange: (NSRange)aRange
|
|
|
|
{
|
|
|
|
unichar *buf;
|
|
|
|
id ret;
|
|
|
|
unsigned len = [self length];
|
|
|
|
|
|
|
|
GS_RANGE_CHECK(aRange, len);
|
1997-12-08 20:04:16 +00:00
|
|
|
|
|
|
|
if (aRange.length == 0)
|
|
|
|
return @"";
|
2000-10-31 16:17:33 +00:00
|
|
|
buf = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*aRange.length);
|
1999-05-06 14:42:26 +00:00
|
|
|
[self getCharacters: buf range: aRange];
|
2000-11-03 10:11:56 +00:00
|
|
|
ret = [[NSStringClass allocWithZone: NSDefaultMallocZone()]
|
2000-09-27 15:26:16 +00:00
|
|
|
initWithCharactersNoCopy: buf length: aRange.length freeWhenDone: YES];
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE(ret);
|
1997-12-08 20:04:16 +00:00
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
// Finding Ranges of Characters and Substrings
|
|
|
|
|
|
|
|
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange all = NSMakeRange(0, [self length]);
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self rangeOfCharacterFromSet: aSet
|
|
|
|
options: 0
|
|
|
|
range: all];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
1999-06-21 08:30:26 +00:00
|
|
|
options: (unsigned)mask
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange all = NSMakeRange(0, [self length]);
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self rangeOfCharacterFromSet: aSet
|
|
|
|
options: mask
|
|
|
|
range: all];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
/* xxx FIXME */
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
1999-06-21 08:30:26 +00:00
|
|
|
options: (unsigned)mask
|
1998-11-19 20:42:06 +00:00
|
|
|
range: (NSRange)aRange
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
int i, start, stop, step;
|
|
|
|
NSRange range;
|
1999-02-04 13:49:27 +00:00
|
|
|
unichar (*cImp)(id, SEL, unsigned);
|
|
|
|
BOOL (*mImp)(id, SEL, unichar);
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
|
1998-11-19 20:42:06 +00:00
|
|
|
i = [self length];
|
1999-06-21 08:30:26 +00:00
|
|
|
GS_RANGE_CHECK(aRange, i);
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
|
|
|
|
if ((mask & NSBackwardsSearch) == NSBackwardsSearch)
|
|
|
|
{
|
1998-07-15 12:48:57 +00:00
|
|
|
start = NSMaxRange(aRange)-1; stop = aRange.location-1; step = -1;
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
start = aRange.location; stop = NSMaxRange(aRange); step = 1;
|
|
|
|
}
|
1999-01-20 18:26:46 +00:00
|
|
|
range.location = 0;
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
range.length = 0;
|
1999-02-04 13:49:27 +00:00
|
|
|
|
1999-05-19 20:05:49 +00:00
|
|
|
cImp = (unichar(*)(id,SEL,unsigned)) [self methodForSelector: caiSel];
|
1999-02-04 13:49:27 +00:00
|
|
|
mImp = (BOOL(*)(id,SEL,unichar))
|
|
|
|
[aSet methodForSelector: cMemberSel];
|
|
|
|
|
|
|
|
for (i = start; i != stop; i += step)
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
{
|
1999-05-19 20:05:49 +00:00
|
|
|
unichar letter = (unichar)(*cImp)(self, caiSel, i);
|
1999-02-04 13:49:27 +00:00
|
|
|
if ((*mImp)(aSet, cMemberSel, letter))
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
{
|
|
|
|
range = NSMakeRange(i, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return range;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSRange) rangeOfString: (NSString*)string
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange all = NSMakeRange(0, [self length]);
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self rangeOfString: string
|
|
|
|
options: 0
|
|
|
|
range: all];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSRange) rangeOfString: (NSString*)string
|
1999-06-21 08:30:26 +00:00
|
|
|
options: (unsigned)mask
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange all = NSMakeRange(0, [self length]);
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self rangeOfString: string
|
|
|
|
options: mask
|
|
|
|
range: all];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1999-05-19 20:05:49 +00:00
|
|
|
- (NSRange) rangeOfString: (NSString *) aString
|
1999-06-21 08:30:26 +00:00
|
|
|
options: (unsigned) mask
|
1999-05-20 09:20:46 +00:00
|
|
|
range: (NSRange) aRange
|
|
|
|
{
|
1999-05-25 14:27:42 +00:00
|
|
|
if (aString == nil)
|
|
|
|
[NSException raise: NSInvalidArgumentException format: @"range of nil"];
|
1999-05-20 09:20:46 +00:00
|
|
|
return strRangeNsNs(self, aString, mask, aRange);
|
1996-09-02 13:38:19 +00:00
|
|
|
}
|
1995-04-03 01:35:42 +00:00
|
|
|
|
2000-06-06 16:50:52 +00:00
|
|
|
- (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;
|
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
// Determining Composed Character Sequences
|
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
- (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned)anIndex
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-10 06:35:41 +00:00
|
|
|
unsigned start;
|
|
|
|
unsigned end;
|
|
|
|
unsigned length = [self length];
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1997-05-03 18:05:21 +00:00
|
|
|
|
1999-05-10 06:35:41 +00:00
|
|
|
if (anIndex >= length)
|
|
|
|
[NSException raise: NSRangeException format:@"Invalid location."];
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
1999-05-10 06:35:41 +00:00
|
|
|
start = anIndex;
|
2000-08-07 22:00:31 +00:00
|
|
|
while (uni_isnonsp((*caiImp)(self, caiSel, start)) && start > 0)
|
1999-01-11 17:28:51 +00:00
|
|
|
start--;
|
1997-05-03 18:05:21 +00:00
|
|
|
end=start+1;
|
1999-05-10 06:35:41 +00:00
|
|
|
if (end < length)
|
2000-08-07 22:00:31 +00:00
|
|
|
while ((end < length) && (uni_isnonsp((*caiImp)(self, caiSel, end))) )
|
1997-05-03 18:05:21 +00:00
|
|
|
end++;
|
|
|
|
return NSMakeRange(start, end-start);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
// Identifying and Comparing Strings
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSComparisonResult) compare: (NSString*)aString
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self compare: aString options: 0];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSComparisonResult) compare: (NSString*)aString
|
1999-06-21 08:30:26 +00:00
|
|
|
options: (unsigned)mask
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self compare: aString options: mask
|
1999-05-19 20:05:49 +00:00
|
|
|
range: ((NSRange){0, [self length]})];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
// xxx Should implement full POSIX.2 collate
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSComparisonResult) compare: (NSString*)aString
|
1999-06-21 08:30:26 +00:00
|
|
|
options: (unsigned)mask
|
1999-05-19 20:05:49 +00:00
|
|
|
range: (NSRange)aRange
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-25 14:27:42 +00:00
|
|
|
if (aString == nil)
|
|
|
|
[NSException raise: NSInvalidArgumentException format: @"compare with nil"];
|
1999-05-20 09:20:46 +00:00
|
|
|
return strCompNsNs(self, aString, mask, aRange);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasPrefix: (NSString*)aString
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange range;
|
1999-05-06 14:42:26 +00:00
|
|
|
range = [self rangeOfString: aString];
|
1997-01-06 22:36:21 +00:00
|
|
|
return ((range.location == 0) && (range.length != 0)) ? YES : NO;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasSuffix: (NSString*)aString
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange range;
|
1999-05-06 14:42:26 +00:00
|
|
|
range = [self rangeOfString: aString options: NSBackwardsSearch];
|
1998-08-13 10:01:43 +00:00
|
|
|
return (range.length > 0 && range.location == ([self length] - [aString length])) ? YES : NO;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
- (BOOL) isEqual: (id)anObject
|
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
if (anObject == self)
|
|
|
|
{
|
|
|
|
return YES;
|
1998-10-09 04:24:56 +00:00
|
|
|
}
|
2000-10-31 16:17:33 +00:00
|
|
|
if (anObject != nil && GSObjCIsInstance(anObject) == YES)
|
1999-07-02 13:26:37 +00:00
|
|
|
{
|
2000-10-31 16:17:33 +00:00
|
|
|
Class c = GSObjCClass(anObject);
|
1998-10-09 04:24:56 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (c != nil)
|
|
|
|
{
|
2000-10-31 16:17:33 +00:00
|
|
|
if (GSObjCIsKindOf(c, NSStringClass))
|
1999-07-02 13:26:37 +00:00
|
|
|
{
|
|
|
|
return [self isEqualToString: anObject];
|
1998-10-09 04:24:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-07-02 13:26:37 +00:00
|
|
|
return NO;
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) isEqualToString: (NSString*)aString
|
|
|
|
{
|
1998-08-03 15:31:33 +00:00
|
|
|
if ([self hash] != [aString hash])
|
|
|
|
return NO;
|
1999-05-20 09:20:46 +00:00
|
|
|
if (strCompNsNs(self, aString, 0, (NSRange){0, [self length]})
|
|
|
|
== NSOrderedSame)
|
1997-09-01 21:59:51 +00:00
|
|
|
return YES;
|
1999-05-20 09:20:46 +00:00
|
|
|
return NO;
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
2000-10-05 15:17:18 +00:00
|
|
|
/*
|
|
|
|
* Return 28-bit hash value (in 32-bit integer). The top few bits are used
|
|
|
|
* for other purposes in a bitfield in the concrete string subclasses, so we
|
|
|
|
* must not use the full unsigned integer.
|
|
|
|
*/
|
1999-06-21 08:30:26 +00:00
|
|
|
- (unsigned) hash
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
|
|
|
unsigned ret = 0;
|
1998-01-08 15:25:59 +00:00
|
|
|
|
|
|
|
int len = [self length];
|
|
|
|
|
1999-05-19 20:05:49 +00:00
|
|
|
if (len > NSHashStringLength)
|
|
|
|
len = NSHashStringLength;
|
1998-01-08 15:25:59 +00:00
|
|
|
if (len)
|
|
|
|
{
|
1999-05-19 20:05:49 +00:00
|
|
|
unichar buf[len * MAXDEC + 1];
|
|
|
|
GSeqStruct s = { buf, len, len * MAXDEC, 0 };
|
|
|
|
unichar *p;
|
|
|
|
unsigned char_count = 0;
|
1998-01-08 15:25:59 +00:00
|
|
|
|
1999-05-19 20:05:49 +00:00
|
|
|
[self getCharacters: buf range: NSMakeRange(0,len)];
|
|
|
|
GSeq_normalize(&s);
|
1998-10-06 15:11:27 +00:00
|
|
|
|
1999-05-19 20:05:49 +00:00
|
|
|
p = buf;
|
1998-10-06 15:11:27 +00:00
|
|
|
|
1999-05-19 20:05:49 +00:00
|
|
|
while (*p && char_count++ < NSHashStringLength)
|
|
|
|
{
|
|
|
|
ret = (ret << 5) + ret + *p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The hash caching in our concrete string classes uses zero to denote
|
|
|
|
* an empty cache value, so we MUST NOT return a hash of zero.
|
|
|
|
*/
|
|
|
|
if (ret == 0)
|
2000-10-05 15:17:18 +00:00
|
|
|
ret = 0x0fffffff;
|
|
|
|
else
|
|
|
|
ret &= 0x0fffffff;
|
1999-05-19 20:05:49 +00:00
|
|
|
return ret;
|
|
|
|
}
|
1998-01-08 15:25:59 +00:00
|
|
|
else
|
2000-10-05 15:17:18 +00:00
|
|
|
return 0x0ffffffe; /* Hash for an empty string. */
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1995-04-03 01:35:42 +00:00
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
// Getting a Shared Prefix
|
|
|
|
|
|
|
|
- (NSString*) commonPrefixWithString: (NSString*)aString
|
1999-06-21 08:30:26 +00:00
|
|
|
options: (unsigned)mask
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-19 20:05:49 +00:00
|
|
|
if (mask & NSLiteralSearch)
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
1999-05-19 20:05:49 +00:00
|
|
|
int prefix_len = 0;
|
|
|
|
unichar *u,*w;
|
|
|
|
unichar a1[[self length]+1];
|
|
|
|
unichar *s1 = a1;
|
|
|
|
unichar a2[[aString length]+1];
|
|
|
|
unichar *s2 = a2;
|
|
|
|
|
|
|
|
u = s1;
|
|
|
|
[self getCharacters: s1];
|
|
|
|
s1[[self length]] = (unichar)0;
|
|
|
|
[aString getCharacters: s2];
|
|
|
|
s2[[aString length]] = (unichar)0;
|
|
|
|
u = s1;
|
|
|
|
w = s2;
|
|
|
|
|
|
|
|
if (mask & NSCaseInsensitiveSearch)
|
|
|
|
{
|
|
|
|
while (*s1 && *s2 && (uni_tolower(*s1) == uni_tolower(*s2)))
|
|
|
|
{
|
|
|
|
s1++;
|
|
|
|
s2++;
|
|
|
|
prefix_len++;
|
|
|
|
}
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
else
|
1999-05-19 20:05:49 +00:00
|
|
|
{
|
|
|
|
while (*s1 && *s2 && (*s1 == *s2))
|
|
|
|
{
|
|
|
|
s1++;
|
|
|
|
s2++;
|
|
|
|
prefix_len++;
|
|
|
|
}
|
|
|
|
}
|
2000-10-29 14:52:33 +00:00
|
|
|
return [NSStringClass stringWithCharacters: u length: prefix_len];
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1999-05-19 20:05:49 +00:00
|
|
|
else
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
1999-05-19 20:05:49 +00:00
|
|
|
unichar (*scImp)(NSString*, SEL, unsigned);
|
|
|
|
unichar (*ocImp)(NSString*, SEL, unsigned);
|
1999-07-12 04:27:18 +00:00
|
|
|
void (*sgImp)(NSString*, SEL, unichar*, NSRange) = 0;
|
|
|
|
void (*ogImp)(NSString*, SEL, unichar*, NSRange) = 0;
|
|
|
|
NSRange (*srImp)(NSString*, SEL, unsigned) = 0;
|
|
|
|
NSRange (*orImp)(NSString*, SEL, unsigned) = 0;
|
1999-05-19 20:05:49 +00:00
|
|
|
BOOL gotRangeImps = NO;
|
|
|
|
BOOL gotFetchImps = NO;
|
|
|
|
NSRange sRange;
|
|
|
|
NSRange oRange;
|
|
|
|
unsigned sLength = [self length];
|
|
|
|
unsigned oLength = [aString length];
|
|
|
|
unsigned sIndex = 0;
|
|
|
|
unsigned oIndex = 0;
|
|
|
|
|
|
|
|
if (!sLength)
|
|
|
|
return self;
|
|
|
|
if (!oLength)
|
|
|
|
return aString;
|
|
|
|
|
|
|
|
scImp = (unichar (*)())[self methodForSelector: caiSel];
|
|
|
|
ocImp = (unichar (*)())[aString methodForSelector: caiSel];
|
|
|
|
|
|
|
|
while ((sIndex < sLength) && (oIndex < oLength))
|
|
|
|
{
|
|
|
|
unichar sc = (*scImp)(self, caiSel, sIndex);
|
|
|
|
unichar oc = (*ocImp)(aString, caiSel, oIndex);
|
|
|
|
|
|
|
|
if (sc == oc)
|
|
|
|
{
|
|
|
|
sIndex++;
|
|
|
|
oIndex++;
|
|
|
|
}
|
|
|
|
else if ((mask & NSCaseInsensitiveSearch)
|
|
|
|
&& (uni_tolower(sc) == uni_tolower(oc)))
|
|
|
|
{
|
|
|
|
sIndex++;
|
|
|
|
oIndex++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (gotRangeImps == NO)
|
|
|
|
{
|
|
|
|
gotRangeImps = YES;
|
|
|
|
srImp=(NSRange (*)())[self methodForSelector: ranSel];
|
|
|
|
orImp=(NSRange (*)())[aString methodForSelector: ranSel];
|
|
|
|
}
|
|
|
|
sRange = (*srImp)(self, ranSel, sIndex);
|
|
|
|
oRange = (*orImp)(aString, ranSel, oIndex);
|
|
|
|
|
|
|
|
if ((sRange.length < 2) || (oRange.length < 2))
|
1999-06-21 08:30:26 +00:00
|
|
|
return [self substringWithRange: NSMakeRange(0, sIndex)];
|
1999-05-19 20:05:49 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
GSEQ_MAKE(sBuf, sSeq, sRange.length);
|
|
|
|
GSEQ_MAKE(oBuf, oSeq, oRange.length);
|
|
|
|
|
|
|
|
if (gotFetchImps == NO)
|
|
|
|
{
|
|
|
|
gotFetchImps = YES;
|
|
|
|
sgImp=(void (*)())[self methodForSelector: gcrSel];
|
|
|
|
ogImp=(void (*)())[aString methodForSelector: gcrSel];
|
|
|
|
}
|
|
|
|
(*sgImp)(self, gcrSel, sBuf, sRange);
|
|
|
|
(*ogImp)(aString, gcrSel, oBuf, oRange);
|
|
|
|
|
|
|
|
if (GSeq_compare(&sSeq, &oSeq) == NSOrderedSame)
|
|
|
|
{
|
|
|
|
sIndex += sRange.length;
|
|
|
|
oIndex += oRange.length;
|
|
|
|
}
|
|
|
|
else if (mask & NSCaseInsensitiveSearch)
|
|
|
|
{
|
|
|
|
GSeq_lowercase(&sSeq);
|
|
|
|
GSeq_lowercase(&oSeq);
|
|
|
|
if (GSeq_compare(&sSeq, &oSeq) == NSOrderedSame)
|
|
|
|
{
|
|
|
|
sIndex += sRange.length;
|
|
|
|
oIndex += oRange.length;
|
|
|
|
}
|
|
|
|
else
|
1999-06-21 08:30:26 +00:00
|
|
|
return [self substringWithRange: NSMakeRange(0,sIndex)];
|
1999-05-19 20:05:49 +00:00
|
|
|
}
|
|
|
|
else
|
1999-06-21 08:30:26 +00:00
|
|
|
return [self substringWithRange: NSMakeRange(0,sIndex)];
|
1999-05-19 20:05:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-06-21 08:30:26 +00:00
|
|
|
return [self substringWithRange: NSMakeRange(0, sIndex)];
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1999-05-19 20:05:49 +00:00
|
|
|
- (NSRange) lineRangeForRange: (NSRange)aRange
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
unsigned startIndex;
|
|
|
|
unsigned lineEndIndex;
|
1998-01-08 15:25:59 +00:00
|
|
|
|
|
|
|
[self getLineStart: &startIndex
|
|
|
|
end: &lineEndIndex
|
|
|
|
contentsEnd: NULL
|
1999-05-06 14:42:26 +00:00
|
|
|
forRange: aRange];
|
1998-01-08 15:25:59 +00:00
|
|
|
return NSMakeRange(startIndex, lineEndIndex - startIndex);
|
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
- (void) getLineStart: (unsigned *)startIndex
|
|
|
|
end: (unsigned *)lineEndIndex
|
|
|
|
contentsEnd: (unsigned *)contentsEndIndex
|
|
|
|
forRange: (NSRange)aRange
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
unichar thischar;
|
|
|
|
unsigned start, end, len;
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1998-01-08 15:25:59 +00:00
|
|
|
|
|
|
|
len = [self length];
|
1999-06-21 08:30:26 +00:00
|
|
|
GS_RANGE_CHECK(aRange, len);
|
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
1999-06-21 08:30:26 +00:00
|
|
|
start = aRange.location;
|
1998-01-08 15:25:59 +00:00
|
|
|
|
1999-02-04 13:49:27 +00:00
|
|
|
if (startIndex)
|
1999-06-24 19:30:29 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
if (start == 0)
|
1999-06-24 19:30:29 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
*startIndex = 0;
|
1999-06-24 19:30:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
start--;
|
|
|
|
while (start > 0)
|
|
|
|
{
|
|
|
|
BOOL done = NO;
|
1999-06-21 08:30:26 +00:00
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
thischar = (*caiImp)(self, caiSel, start);
|
|
|
|
switch (thischar)
|
1999-06-24 19:30:29 +00:00
|
|
|
{
|
|
|
|
case (unichar)0x000A:
|
|
|
|
case (unichar)0x000D:
|
|
|
|
case (unichar)0x2028:
|
|
|
|
case (unichar)0x2029:
|
|
|
|
done = YES;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
start--;
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
if (start == 0)
|
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
thischar = (*caiImp)(self, caiSel, start);
|
|
|
|
switch (thischar)
|
1999-06-24 19:30:29 +00:00
|
|
|
{
|
|
|
|
case (unichar)0x000A:
|
|
|
|
case (unichar)0x000D:
|
|
|
|
case (unichar)0x2028:
|
|
|
|
case (unichar)0x2029:
|
|
|
|
start++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
start++;
|
|
|
|
*startIndex = start;
|
|
|
|
}
|
|
|
|
}
|
1998-01-08 15:25:59 +00:00
|
|
|
|
1999-02-04 13:49:27 +00:00
|
|
|
if (lineEndIndex || contentsEndIndex)
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
end = aRange.location + aRange.length;
|
|
|
|
while (end < len)
|
1999-06-21 08:30:26 +00:00
|
|
|
{
|
|
|
|
BOOL done = NO;
|
2000-08-07 22:00:31 +00:00
|
|
|
thischar = (*caiImp)(self, caiSel, end);
|
|
|
|
switch (thischar)
|
1999-06-21 08:30:26 +00:00
|
|
|
{
|
|
|
|
case (unichar)0x000A:
|
|
|
|
case (unichar)0x000D:
|
|
|
|
case (unichar)0x2028:
|
|
|
|
case (unichar)0x2029:
|
|
|
|
done = YES;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
end++;
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
};
|
2000-08-07 22:00:31 +00:00
|
|
|
if (end < len)
|
1999-06-21 08:30:26 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
if ((*caiImp)(self, caiSel, end) == (unichar)0x000D)
|
1999-06-21 08:30:26 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
if ((*caiImp)(self, caiSel, end+1) == (unichar)0x000A)
|
1999-06-21 08:30:26 +00:00
|
|
|
*lineEndIndex = end+1;
|
|
|
|
else
|
|
|
|
*lineEndIndex = end;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*lineEndIndex = end;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*lineEndIndex = end;
|
1998-01-08 15:25:59 +00:00
|
|
|
}
|
|
|
|
|
1999-02-04 13:49:27 +00:00
|
|
|
if (contentsEndIndex)
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
if (end < len)
|
1999-06-21 08:30:26 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
*contentsEndIndex = end-1;
|
1999-06-21 08:30:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* xxx OPENSTEP documentation does not say what to do if last
|
|
|
|
line is not terminated. Assume this */
|
2000-08-07 22:00:31 +00:00
|
|
|
*contentsEndIndex = end;
|
1999-06-21 08:30:26 +00:00
|
|
|
}
|
1998-01-08 15:25:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
// Changing Case
|
|
|
|
|
|
|
|
// xxx There is more than this in word capitalization in Unicode,
|
|
|
|
// but this will work in most cases
|
|
|
|
- (NSString*) capitalizedString
|
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
unichar *s;
|
|
|
|
unsigned count = 0;
|
|
|
|
BOOL found = YES;
|
|
|
|
unsigned len = [self length];
|
1999-02-04 13:49:27 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (len == 0)
|
|
|
|
return self;
|
1999-02-04 13:49:27 +00:00
|
|
|
if (whitespce == nil)
|
|
|
|
setupWhitespce();
|
1998-01-08 15:25:59 +00:00
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
s = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*len);
|
1999-05-06 14:42:26 +00:00
|
|
|
[self getCharacters: s];
|
1999-07-17 14:41:31 +00:00
|
|
|
while (count < len)
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
if ((*whitespceImp)(whitespce, cMemberSel, s[count]))
|
|
|
|
{
|
|
|
|
count++;
|
1999-07-17 14:41:31 +00:00
|
|
|
found = YES;
|
|
|
|
while (count < len
|
|
|
|
&& (*whitespceImp)(whitespce, cMemberSel, s[count]))
|
1999-07-02 13:26:37 +00:00
|
|
|
{
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
1999-07-17 14:41:31 +00:00
|
|
|
if (count < len)
|
1999-07-02 13:26:37 +00:00
|
|
|
{
|
1999-07-17 14:41:31 +00:00
|
|
|
if (found)
|
1999-07-02 13:26:37 +00:00
|
|
|
{
|
1999-07-17 14:41:31 +00:00
|
|
|
s[count] = uni_toupper(s[count]);
|
1999-07-02 13:26:37 +00:00
|
|
|
count++;
|
|
|
|
}
|
1999-07-17 14:41:31 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
while (count < len
|
|
|
|
&& !(*whitespceImp)(whitespce, cMemberSel, s[count]))
|
|
|
|
{
|
|
|
|
s[count] = uni_tolower(s[count]);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
1999-07-02 13:26:37 +00:00
|
|
|
}
|
1999-07-17 14:41:31 +00:00
|
|
|
found = NO;
|
1998-01-08 15:25:59 +00:00
|
|
|
}
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE([[NSString allocWithZone: NSDefaultMallocZone()]
|
2000-09-27 15:26:16 +00:00
|
|
|
initWithCharactersNoCopy: s length: len freeWhenDone: YES]);
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) lowercaseString
|
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
unichar *s;
|
|
|
|
unsigned count;
|
|
|
|
unsigned len = [self length];
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1999-06-03 10:59:25 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (len == 0)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
2000-10-31 16:17:33 +00:00
|
|
|
s = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*len);
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
1999-06-03 10:59:25 +00:00
|
|
|
for (count = 0; count < len; count++)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
s[count] = uni_tolower((*caiImp)(self, caiSel, count));
|
|
|
|
}
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[NSStringClass allocWithZone: NSDefaultMallocZone()]
|
2000-09-27 15:26:16 +00:00
|
|
|
initWithCharactersNoCopy: s length: len freeWhenDone: YES]);
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) uppercaseString;
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
unichar *s;
|
|
|
|
unsigned count;
|
|
|
|
unsigned len = [self length];
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1999-06-03 10:59:25 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (len == 0)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
2000-10-31 16:17:33 +00:00
|
|
|
s = NSZoneMalloc(GSObjCZone(self), sizeof(unichar)*len);
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
1999-06-03 10:59:25 +00:00
|
|
|
for (count = 0; count < len; count++)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
s[count] = uni_toupper((*caiImp)(self, caiSel, count));
|
|
|
|
}
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[NSStringClass allocWithZone: NSDefaultMallocZone()]
|
2000-09-27 15:26:16 +00:00
|
|
|
initWithCharactersNoCopy: s length: len freeWhenDone: YES]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Storing the String
|
|
|
|
|
|
|
|
- (NSString*) description
|
|
|
|
{
|
1997-03-03 20:07:35 +00:00
|
|
|
return self;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Getting C Strings
|
|
|
|
|
|
|
|
- (const char*) cString
|
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
NSData *d;
|
2000-10-23 11:44:34 +00:00
|
|
|
NSMutableData *m;
|
2000-08-07 22:00:31 +00:00
|
|
|
|
|
|
|
d = [self dataUsingEncoding: _DefaultStringEncoding
|
|
|
|
allowLossyConversion: NO];
|
1999-08-25 14:47:19 +00:00
|
|
|
if (d == nil)
|
|
|
|
{
|
|
|
|
[NSException raise: NSCharacterConversionException
|
|
|
|
format: @"unable to convert to cString"];
|
|
|
|
}
|
2000-10-23 11:44:34 +00:00
|
|
|
m = [d mutableCopy];
|
|
|
|
[m appendBytes: "" length: 1];
|
|
|
|
AUTORELEASE(m);
|
|
|
|
return (const char*)[m bytes];
|
1999-08-25 14:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (const char*) lossyCString
|
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
NSData *d;
|
2000-10-23 11:44:34 +00:00
|
|
|
NSMutableData *m;
|
2000-08-07 22:00:31 +00:00
|
|
|
|
|
|
|
d = [self dataUsingEncoding: _DefaultStringEncoding
|
|
|
|
allowLossyConversion: YES];
|
2000-10-23 11:44:34 +00:00
|
|
|
m = [d mutableCopy];
|
|
|
|
[m appendBytes: "" length: 1];
|
|
|
|
AUTORELEASE(m);
|
|
|
|
return (const char*)[m bytes];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
- (const char *) UTF8String
|
2000-09-12 23:09:50 +00:00
|
|
|
{
|
|
|
|
NSData *d;
|
2000-10-23 11:44:34 +00:00
|
|
|
NSMutableData *m;
|
2000-09-12 23:09:50 +00:00
|
|
|
|
|
|
|
d = [self dataUsingEncoding: NSUTF8StringEncoding
|
|
|
|
allowLossyConversion: NO];
|
2000-10-23 11:44:34 +00:00
|
|
|
m = [d mutableCopy];
|
|
|
|
[m appendBytes: "" length: 1];
|
|
|
|
AUTORELEASE(m);
|
|
|
|
return (const char*)[m bytes];
|
2000-09-12 23:09:50 +00:00
|
|
|
}
|
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
- (unsigned) cStringLength
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
NSData *d;
|
|
|
|
|
|
|
|
d = [self dataUsingEncoding: _DefaultStringEncoding
|
|
|
|
allowLossyConversion: YES];
|
|
|
|
return [d length];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) getCString: (char*)buffer
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self getCString: buffer maxLength: NSMaximumStringLength
|
2000-08-07 22:00:31 +00:00
|
|
|
range: ((NSRange){0, [self length]})
|
|
|
|
remainingRange: NULL];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) getCString: (char*)buffer
|
1999-11-26 19:43:43 +00:00
|
|
|
maxLength: (unsigned)maxLength
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self getCString: buffer maxLength: maxLength
|
2000-08-07 22:00:31 +00:00
|
|
|
range: ((NSRange){0, [self length]})
|
|
|
|
remainingRange: NULL];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) getCString: (char*)buffer
|
1999-07-02 13:26:37 +00:00
|
|
|
maxLength: (unsigned)maxLength
|
|
|
|
range: (NSRange)aRange
|
|
|
|
remainingRange: (NSRange*)leftoverRange
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
unsigned len;
|
|
|
|
unsigned count;
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1995-04-03 01:35:42 +00:00
|
|
|
|
1998-11-19 20:42:06 +00:00
|
|
|
len = [self cStringLength];
|
1999-06-21 08:30:26 +00:00
|
|
|
GS_RANGE_CHECK(aRange, len);
|
1998-11-19 20:42:06 +00:00
|
|
|
|
2000-10-05 15:17:18 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
1999-07-02 13:26:37 +00:00
|
|
|
count = 0;
|
|
|
|
while (count < len)
|
|
|
|
{
|
2000-12-14 09:47:02 +00:00
|
|
|
buffer[count] = encode_unitochar(
|
|
|
|
(*caiImp)(self, caiSel, aRange.location + count),
|
|
|
|
_DefaultStringEncoding);
|
|
|
|
if (buffer[count] == 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSCharacterConversionException
|
|
|
|
format: @"unable to convert to cString"];
|
|
|
|
}
|
1999-07-02 13:26:37 +00:00
|
|
|
count++;
|
|
|
|
}
|
1997-05-03 18:05:21 +00:00
|
|
|
buffer[len] = '\0';
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Getting Numeric Values
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
// xxx Sould we use NSScanner here ?
|
|
|
|
|
1998-10-21 11:56:58 +00:00
|
|
|
- (BOOL) boolValue
|
|
|
|
{
|
1999-07-02 13:26:37 +00:00
|
|
|
if ([self caseInsensitiveCompare: @"YES"] == NSOrderedSame)
|
|
|
|
return YES;
|
|
|
|
return [self intValue] != 0 ? YES : NO;
|
1998-10-21 11:56:58 +00:00
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
- (double) doubleValue
|
|
|
|
{
|
2000-12-14 09:47:02 +00:00
|
|
|
return atof([self lossyCString]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (float) floatValue
|
|
|
|
{
|
2000-12-14 09:47:02 +00:00
|
|
|
return (float) atof([self lossyCString]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (int) intValue
|
|
|
|
{
|
2000-12-14 09:47:02 +00:00
|
|
|
return atoi([self lossyCString]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Working With Encodings
|
|
|
|
|
|
|
|
+ (NSStringEncoding) defaultCStringEncoding
|
|
|
|
{
|
1998-01-08 15:25:59 +00:00
|
|
|
return _DefaultStringEncoding;
|
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (NSStringEncoding*) availableStringEncodings
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
2000-09-30 18:55:15 +00:00
|
|
|
return GetAvailableEncodings();
|
1998-01-08 15:25:59 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
+ (NSString*) localizedNameOfStringEncoding: (NSStringEncoding)encoding
|
1998-01-08 15:25:59 +00:00
|
|
|
{
|
|
|
|
id ourbundle;
|
|
|
|
id ourname;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Should be path to localizable.strings file.
|
|
|
|
Until we have it, just make shure that bundle
|
|
|
|
is initialized.
|
|
|
|
*/
|
1999-06-03 10:59:25 +00:00
|
|
|
ourbundle = [NSBundle bundleWithPath: rootPath];
|
1998-01-08 15:25:59 +00:00
|
|
|
|
|
|
|
ourname = GetEncodingName(encoding);
|
2000-08-07 22:00:31 +00:00
|
|
|
return [ourbundle localizedStringForKey: ourname
|
|
|
|
value: ourname
|
|
|
|
table: nil];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) canBeConvertedToEncoding: (NSStringEncoding)encoding
|
|
|
|
{
|
2000-09-30 04:54:43 +00:00
|
|
|
id d = [self dataUsingEncoding: encoding allowLossyConversion: NO];
|
1997-09-01 21:59:51 +00:00
|
|
|
return d ? YES : NO;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData*) dataUsingEncoding: (NSStringEncoding)encoding
|
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return [self dataUsingEncoding: encoding allowLossyConversion: NO];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData*) dataUsingEncoding: (NSStringEncoding)encoding
|
1999-02-04 22:06:59 +00:00
|
|
|
allowLossyConversion: (BOOL)flag
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
unsigned int count = 0;
|
|
|
|
unsigned int len = [self length];
|
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
if (len == 0)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
2000-10-23 11:44:34 +00:00
|
|
|
return [NSDataClass data];
|
2000-08-07 22:00:31 +00:00
|
|
|
}
|
1999-07-02 13:26:37 +00:00
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
|
|
|
if ((encoding == NSASCIIStringEncoding)
|
|
|
|
|| (encoding == NSISOLatin1StringEncoding)
|
|
|
|
|| (encoding == NSISOLatin2StringEncoding)
|
|
|
|
|| (encoding == NSNEXTSTEPStringEncoding)
|
|
|
|
|| (encoding == NSNonLossyASCIIStringEncoding)
|
|
|
|
|| (encoding == NSSymbolStringEncoding)
|
|
|
|
|| (encoding == NSCyrillicStringEncoding))
|
1999-02-04 22:06:59 +00:00
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
char t;
|
|
|
|
unsigned char *buff;
|
1999-02-04 22:06:59 +00:00
|
|
|
|
1999-08-25 14:47:19 +00:00
|
|
|
buff = (unsigned char*)NSZoneMalloc(NSDefaultMallocZone(), len+1);
|
1999-02-04 22:06:59 +00:00
|
|
|
if (!flag)
|
|
|
|
{
|
|
|
|
for (count = 0; count < len; count++)
|
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
t = encode_unitochar((*caiImp)(self, caiSel, count), encoding);
|
1999-02-04 22:06:59 +00:00
|
|
|
if (t)
|
|
|
|
{
|
|
|
|
buff[count] = t;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), buff);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* lossy */
|
|
|
|
{
|
|
|
|
for (count = 0; count < len; count++)
|
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
t = encode_unitochar((*caiImp)(self, caiSel, count), encoding);
|
1999-02-04 22:06:59 +00:00
|
|
|
if (t)
|
|
|
|
{
|
|
|
|
buff[count] = t;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* xxx should handle decomposed characters */
|
|
|
|
/* OpenStep documentation is unclear on what to do
|
|
|
|
* if there is no simple replacement for character
|
|
|
|
*/
|
|
|
|
buff[count] = '*';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-08-25 14:47:19 +00:00
|
|
|
buff[count] = '\0';
|
2000-10-23 11:44:34 +00:00
|
|
|
return [NSDataClass dataWithBytesNoCopy: buff length: count];
|
1999-02-04 22:06:59 +00:00
|
|
|
}
|
|
|
|
else if (encoding == NSUnicodeStringEncoding)
|
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar *buff;
|
1999-02-04 22:06:59 +00:00
|
|
|
|
2000-10-23 11:44:34 +00:00
|
|
|
buff = (unichar*)NSZoneMalloc(NSDefaultMallocZone(),
|
|
|
|
sizeof(unichar)*(len+1));
|
2000-08-07 22:00:31 +00:00
|
|
|
buff[0] = 0xFEFF;
|
1999-02-04 22:06:59 +00:00
|
|
|
for (count = 0; count < len; count++)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
buff[count+1] = (*caiImp)(self, caiSel, count);
|
|
|
|
}
|
2000-10-23 11:44:34 +00:00
|
|
|
return [NSDataClass dataWithBytesNoCopy: buff
|
|
|
|
length: sizeof(unichar)*(len+1)];
|
1999-02-04 22:06:59 +00:00
|
|
|
}
|
2000-10-23 11:44:34 +00:00
|
|
|
else
|
1999-02-04 22:06:59 +00:00
|
|
|
{
|
2000-10-23 11:44:34 +00:00
|
|
|
int t;
|
|
|
|
unichar *u;
|
|
|
|
unsigned char *buff;
|
|
|
|
|
|
|
|
u = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), len*sizeof(unichar));
|
|
|
|
[self getCharacters: u];
|
|
|
|
buff = (unsigned char*)NSZoneMalloc(NSDefaultMallocZone(), len);
|
|
|
|
if (flag == YES)
|
|
|
|
t = encode_ustrtostr(buff, u, len, encoding);
|
|
|
|
else
|
|
|
|
t = encode_ustrtostr_strict(buff, u, len, encoding);
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), u);
|
|
|
|
if (t == 0)
|
|
|
|
{
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), buff);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
return [NSDataClass dataWithBytesNoCopy: buff length: t];
|
1997-09-01 21:59:51 +00:00
|
|
|
}
|
1998-01-08 15:25:59 +00:00
|
|
|
return nil;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSStringEncoding) fastestEncoding
|
|
|
|
{
|
2000-10-23 06:18:03 +00:00
|
|
|
return NSUnicodeStringEncoding;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSStringEncoding) smallestEncoding
|
|
|
|
{
|
2000-10-23 06:18:03 +00:00
|
|
|
return NSUnicodeStringEncoding;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Manipulating File System Paths
|
|
|
|
|
1999-06-21 08:30:26 +00:00
|
|
|
- (unsigned) completePathIntoString: (NSString**)outputName
|
1999-11-26 19:43:43 +00:00
|
|
|
caseSensitive: (BOOL)flag
|
|
|
|
matchesIntoArray: (NSArray**)outputArray
|
|
|
|
filterTypes: (NSArray*)filterTypes
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-07-12 04:27:18 +00:00
|
|
|
NSString *base_path = [self stringByDeletingLastPathComponent];
|
|
|
|
NSString *last_compo = [self lastPathComponent];
|
|
|
|
NSString *tmp_path;
|
|
|
|
NSDirectoryEnumerator *e;
|
|
|
|
NSMutableArray *op = nil;
|
|
|
|
unsigned match_count = 0;
|
1998-10-15 18:46:27 +00:00
|
|
|
|
1998-11-19 20:42:06 +00:00
|
|
|
if (outputArray != 0)
|
|
|
|
op = (NSMutableArray*)[NSMutableArray array];
|
|
|
|
|
1999-07-12 04:27:18 +00:00
|
|
|
if (outputName != NULL)
|
|
|
|
*outputName = nil;
|
1998-10-15 18:46:27 +00:00
|
|
|
|
1999-07-12 04:27:18 +00:00
|
|
|
if ([base_path length] == 0)
|
|
|
|
base_path = @".";
|
1998-10-15 18:46:27 +00:00
|
|
|
|
|
|
|
e = [[NSFileManager defaultManager] enumeratorAtPath: base_path];
|
|
|
|
while (tmp_path = [e nextObject], tmp_path)
|
|
|
|
{
|
|
|
|
/* Prefix matching */
|
|
|
|
if (YES == flag)
|
|
|
|
{ /* Case sensitive */
|
|
|
|
if (NO == [tmp_path hasPrefix: last_compo])
|
|
|
|
continue ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (NO == [[tmp_path uppercaseString]
|
|
|
|
hasPrefix: [last_compo uppercaseString]])
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extensions filtering */
|
|
|
|
if (filterTypes &&
|
|
|
|
(NO == [filterTypes containsObject: [tmp_path pathExtension]]))
|
|
|
|
continue ;
|
|
|
|
|
|
|
|
/* Found a completion */
|
|
|
|
match_count++;
|
|
|
|
if (outputArray != NULL)
|
1998-11-19 20:42:06 +00:00
|
|
|
[*op addObject: tmp_path];
|
1998-10-15 18:46:27 +00:00
|
|
|
|
|
|
|
if ((outputName != NULL) &&
|
1998-11-19 20:42:06 +00:00
|
|
|
((*outputName == nil) || (([*outputName length] < [tmp_path length]))))
|
1998-10-15 18:46:27 +00:00
|
|
|
*outputName = tmp_path;
|
|
|
|
}
|
1998-11-19 20:42:06 +00:00
|
|
|
if (outputArray != NULL)
|
1999-06-03 10:59:25 +00:00
|
|
|
*outputArray = AUTORELEASE([op copy]);
|
1998-10-15 18:46:27 +00:00
|
|
|
return match_count;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-11-03 01:40:03 +00:00
|
|
|
/* Return a string for passing to OS calls to handle file system objects. */
|
1999-11-26 19:43:43 +00:00
|
|
|
- (const char*) fileSystemRepresentation
|
1997-11-03 01:40:03 +00:00
|
|
|
{
|
2000-09-22 04:20:52 +00:00
|
|
|
return [[NSFileManager defaultManager] fileSystemRepresentationWithPath: self];
|
1997-11-03 01:40:03 +00:00
|
|
|
}
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
- (BOOL) getFileSystemRepresentation: (char*)buffer maxLength: (unsigned)size
|
1997-11-03 01:40:03 +00:00
|
|
|
{
|
2000-09-22 04:20:52 +00:00
|
|
|
const char* ptr = [self fileSystemRepresentation];
|
1997-11-03 01:40:03 +00:00
|
|
|
if (strlen(ptr) > size)
|
|
|
|
return NO;
|
|
|
|
strcpy(buffer, ptr);
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
/* 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. */
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSString*) lastPathComponent
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange range;
|
|
|
|
NSString *substring = nil;
|
|
|
|
|
1999-01-20 13:28:28 +00:00
|
|
|
range = [self rangeOfCharacterFromSet: pathSeps() options: NSBackwardsSearch];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
if (range.length == 0)
|
1999-06-03 10:59:25 +00:00
|
|
|
substring = AUTORELEASE([self copy]);
|
1999-01-20 13:28:28 +00:00
|
|
|
else if (range.location == ([self length] - 1))
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
{
|
|
|
|
if (range.location == 0)
|
1999-06-03 10:59:25 +00:00
|
|
|
substring = @"";
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
else
|
1999-05-06 14:42:26 +00:00
|
|
|
substring = [[self substringToIndex: range.location]
|
1997-09-13 17:52:31 +00:00
|
|
|
lastPathComponent];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
}
|
|
|
|
else
|
1999-05-06 14:42:26 +00:00
|
|
|
substring = [self substringFromIndex: range.location + 1];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
|
|
|
|
return substring;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
1997-09-13 17:52:31 +00:00
|
|
|
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
/* 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. */
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSString*) pathExtension
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange range;
|
|
|
|
NSString *substring = nil;
|
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
range = [self rangeOfString: @"." options: NSBackwardsSearch];
|
1999-01-20 18:26:46 +00:00
|
|
|
if (range.length == 0)
|
1997-09-13 17:52:31 +00:00
|
|
|
substring = nil;
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
else
|
1999-01-20 18:26:46 +00:00
|
|
|
{
|
|
|
|
NSRange range2 = [self rangeOfCharacterFromSet: pathSeps()
|
2000-08-07 22:00:31 +00:00
|
|
|
options: NSBackwardsSearch];
|
1999-01-20 18:26:46 +00:00
|
|
|
if (range2.length > 0 && range.location < range2.location)
|
|
|
|
substring = nil;
|
|
|
|
else
|
1999-05-06 14:42:26 +00:00
|
|
|
substring = [self substringFromIndex: range.location + 1];
|
1999-01-20 18:26:46 +00:00
|
|
|
}
|
1997-09-13 17:52:31 +00:00
|
|
|
|
|
|
|
if (!substring)
|
1999-06-03 10:59:25 +00:00
|
|
|
substring = @"";
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
return substring;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
/* Returns a new string with the path component given in aString
|
2000-03-23 18:57:43 +00:00
|
|
|
* appended to the receiver.
|
|
|
|
* Removes trailing separators and multiple separators.
|
|
|
|
*/
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSString*) stringByAppendingPathComponent: (NSString*)aString
|
|
|
|
{
|
2000-03-23 18:57:43 +00:00
|
|
|
unsigned length = [self length];
|
|
|
|
unsigned aLength = [aString length];
|
|
|
|
unichar buf[length+aLength+1];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
2000-03-23 18:57:43 +00:00
|
|
|
[self getCharacters: buf];
|
|
|
|
if (aLength > 0)
|
|
|
|
{
|
2000-03-24 11:50:18 +00:00
|
|
|
if (length > 0)
|
|
|
|
{
|
|
|
|
buf[length++] = pathSepChar;
|
|
|
|
}
|
2000-03-23 18:57:43 +00:00
|
|
|
[aString getCharacters: &buf[length]];
|
|
|
|
}
|
|
|
|
length += aLength;
|
|
|
|
while (length > 1 && pathSepMember(buf[length-1]) == YES)
|
|
|
|
{
|
|
|
|
length--;
|
|
|
|
}
|
2000-04-04 18:36:46 +00:00
|
|
|
if (length > 0)
|
2000-03-23 18:57:43 +00:00
|
|
|
{
|
2000-04-04 18:36:46 +00:00
|
|
|
aLength = length - 1;
|
|
|
|
while (aLength > 1)
|
2000-03-23 18:57:43 +00:00
|
|
|
{
|
2000-04-04 18:36:46 +00:00
|
|
|
if (pathSepMember(buf[aLength]) == YES)
|
2000-03-23 18:57:43 +00:00
|
|
|
{
|
2000-04-04 18:36:46 +00:00
|
|
|
if (pathSepMember(buf[aLength-1]) == YES)
|
2000-03-23 18:57:43 +00:00
|
|
|
{
|
2000-04-04 18:36:46 +00:00
|
|
|
unsigned pos;
|
|
|
|
|
|
|
|
for (pos = aLength+1; pos < length; pos++)
|
|
|
|
{
|
|
|
|
buf[pos-1] = buf[pos];
|
|
|
|
}
|
|
|
|
length--;
|
2000-03-23 18:57:43 +00:00
|
|
|
}
|
|
|
|
}
|
2000-04-04 18:36:46 +00:00
|
|
|
aLength--;
|
2000-03-23 18:57:43 +00:00
|
|
|
}
|
|
|
|
}
|
2000-10-23 06:18:03 +00:00
|
|
|
return [NSStringClass stringWithCharacters: buf length: length];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
/* Returns a new string with the path extension given in aString
|
1999-09-03 11:05:33 +00:00
|
|
|
appended to the receiver.
|
|
|
|
A '.' is appended before appending aString */
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSString*) stringByAppendingPathExtension: (NSString*)aString
|
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
if ([aString length] == 0)
|
1999-09-03 11:05:33 +00:00
|
|
|
return [self stringByAppendingString: @"."];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
else
|
1999-09-03 11:05:33 +00:00
|
|
|
return [self stringByAppendingFormat: @".%@", aString];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
/* Returns a new string with the last path component removed from the
|
|
|
|
receiver. See lastPathComponent for a definition of a path component */
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSString*) stringByDeletingLastPathComponent
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange range;
|
|
|
|
NSString *substring;
|
|
|
|
|
1999-01-20 13:28:28 +00:00
|
|
|
range = [self rangeOfString: [self lastPathComponent]
|
|
|
|
options: NSBackwardsSearch];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
if (range.length == 0)
|
1999-06-03 10:59:25 +00:00
|
|
|
substring = AUTORELEASE([self copy]);
|
1997-09-01 21:59:51 +00:00
|
|
|
else if (range.location == 0)
|
1999-06-03 10:59:25 +00:00
|
|
|
substring = @"";
|
1997-09-01 21:59:51 +00:00
|
|
|
else if (range.location > 1)
|
1999-05-06 14:42:26 +00:00
|
|
|
substring = [self substringToIndex: range.location-1];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
else
|
1999-01-20 13:28:28 +00:00
|
|
|
substring = pathSepString;
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
return substring;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
/* Returns a new string with the path extension removed from the receiver.
|
|
|
|
See pathExtension for a definition of the path extension */
|
1995-04-03 01:35:42 +00:00
|
|
|
- (NSString*) stringByDeletingPathExtension
|
|
|
|
{
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
NSRange range;
|
|
|
|
NSString *substring;
|
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
range = [self rangeOfString: [self pathExtension] options: NSBackwardsSearch];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
if (range.length != 0)
|
1999-05-06 14:42:26 +00:00
|
|
|
substring = [self substringToIndex: range.location-1];
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
else
|
1999-06-03 10:59:25 +00:00
|
|
|
substring = AUTORELEASE([self copy]);
|
(componentsSeparatedByString:, substringFromRange:,
rangeOfCharacterFromSet:, rangeOfCharacterFromSet:options:,
rangeOfCharacterFromSet:options:range:, rangeOfString:,
rangeOfString:options:, caseInsensitiveCompare:, hasPrefix:,
hasSuffix:, lastPathComponent, pathExtension,
stringByAppendingPathComponent:, stringByAppendingPathExtension:,
stringByDeletingLastPathComponent, stringByDeletingPathExtension):
Implemented methods.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@505 72102866-910b-0410-8b05-ffd578937521
1995-08-02 15:10:41 +00:00
|
|
|
return substring;
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) stringByExpandingTildeInPath
|
|
|
|
{
|
2000-08-07 22:00:31 +00:00
|
|
|
NSString *homedir;
|
|
|
|
NSRange first_slash_range;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
1998-02-03 14:20:00 +00:00
|
|
|
if ([self length] == 0)
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE([self copy]);
|
1998-02-03 14:20:00 +00:00
|
|
|
if ([self characterAtIndex: 0] != 0x007E)
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE([self copy]);
|
1996-11-24 21:04:24 +00:00
|
|
|
|
1999-01-20 13:28:28 +00:00
|
|
|
first_slash_range = [self rangeOfString: pathSepString];
|
1996-11-24 21:04:24 +00:00
|
|
|
|
|
|
|
if (first_slash_range.location != 1)
|
|
|
|
{
|
|
|
|
/* It is of the form `~username/blah/...' */
|
|
|
|
int uname_len;
|
|
|
|
NSString *uname;
|
|
|
|
|
|
|
|
if (first_slash_range.length != 0)
|
|
|
|
uname_len = first_slash_range.length - 1;
|
|
|
|
else
|
|
|
|
/* It is actually of the form `~username' */
|
|
|
|
uname_len = [self length] - 1;
|
1999-06-21 08:30:26 +00:00
|
|
|
uname = [self substringWithRange: ((NSRange){1, uname_len})];
|
1996-11-24 21:04:24 +00:00
|
|
|
homedir = NSHomeDirectoryForUser (uname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* It is of the form `~/blah/...' */
|
|
|
|
homedir = NSHomeDirectory ();
|
|
|
|
}
|
|
|
|
|
2000-10-23 06:18:03 +00:00
|
|
|
return [NSStringClass stringWithFormat: @"%@%@", homedir,
|
2000-08-07 22:00:31 +00:00
|
|
|
[self substringFromIndex: first_slash_range.location]];
|
1996-11-24 21:04:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) stringByAbbreviatingWithTildeInPath
|
|
|
|
{
|
|
|
|
NSString *homedir = NSHomeDirectory ();
|
|
|
|
|
|
|
|
if (![self hasPrefix: homedir])
|
1999-06-03 10:59:25 +00:00
|
|
|
return AUTORELEASE([self copy]);
|
1996-11-24 21:04:24 +00:00
|
|
|
|
2000-10-23 06:18:03 +00:00
|
|
|
return [NSStringClass stringWithFormat: @"~%c%@", (char)pathSepChar,
|
2000-08-07 22:00:31 +00:00
|
|
|
[self substringFromIndex: [homedir length] + 1]];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) stringByResolvingSymlinksInPath
|
|
|
|
{
|
2000-06-12 05:17:41 +00:00
|
|
|
#if defined(__MINGW__)
|
1995-04-03 01:35:42 +00:00
|
|
|
return self;
|
1998-10-15 13:46:35 +00:00
|
|
|
#else
|
2000-06-13 14:50:40 +00:00
|
|
|
#ifndef MAX_PATH
|
|
|
|
#define MAX_PATH 1024
|
|
|
|
#endif
|
1999-07-20 09:03:50 +00:00
|
|
|
char new_buf[MAX_PATH];
|
1999-07-12 04:21:05 +00:00
|
|
|
#if HAVE_REALPATH
|
1998-10-15 13:46:35 +00:00
|
|
|
|
1999-07-20 09:03:50 +00:00
|
|
|
if (realpath([self cString], new_buf) == 0)
|
1999-07-12 04:21:05 +00:00
|
|
|
return self;
|
|
|
|
#else
|
1999-07-20 09:03:50 +00:00
|
|
|
char extra[MAX_PATH];
|
|
|
|
char *dest;
|
|
|
|
const char *name = [self cString];
|
|
|
|
const char *start;
|
|
|
|
const char *end;
|
|
|
|
unsigned num_links = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (name[0] != '/')
|
|
|
|
{
|
|
|
|
if (!getcwd(new_buf, MAX_PATH))
|
|
|
|
return self; /* Couldn't get directory. */
|
|
|
|
dest = strchr(new_buf, '\0');
|
|
|
|
}
|
|
|
|
else
|
1998-10-15 13:46:35 +00:00
|
|
|
{
|
1999-07-20 09:03:50 +00:00
|
|
|
new_buf[0] = '/';
|
|
|
|
dest = &new_buf[1];
|
|
|
|
}
|
1998-10-15 13:46:35 +00:00
|
|
|
|
1999-07-20 09:03:50 +00:00
|
|
|
for (start = end = name; *start; start = end)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
int n;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
/* Elide repeated path separators */
|
|
|
|
while (*start == '/')
|
|
|
|
start++;
|
|
|
|
|
|
|
|
/* Locate end of path component */
|
|
|
|
end = start;
|
|
|
|
while (*end && *end != '/')
|
|
|
|
end++;
|
|
|
|
|
|
|
|
len = end - start;
|
|
|
|
if (len == 0)
|
1998-10-15 13:46:35 +00:00
|
|
|
{
|
1999-07-20 09:03:50 +00:00
|
|
|
break; /* End of path. */
|
1998-10-15 13:46:35 +00:00
|
|
|
}
|
1999-07-20 09:03:50 +00:00
|
|
|
else if (len == 1 && *start == '.')
|
1998-10-15 13:46:35 +00:00
|
|
|
{
|
1999-07-20 09:03:50 +00:00
|
|
|
/* Elide '/./' sequence by ignoring it. */
|
1998-10-15 13:46:35 +00:00
|
|
|
}
|
1999-07-20 09:03:50 +00:00
|
|
|
else if (len == 2 && strncmp(start, "..", len) == 0)
|
1998-10-15 13:46:35 +00:00
|
|
|
{
|
1999-07-20 09:03:50 +00:00
|
|
|
/*
|
|
|
|
* Backup - if we are not at the root, remove the last component.
|
|
|
|
*/
|
|
|
|
if (dest > &new_buf[1])
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
dest--;
|
|
|
|
}
|
|
|
|
while (dest[-1] != '/');
|
|
|
|
}
|
1998-10-15 13:46:35 +00:00
|
|
|
}
|
1999-07-20 09:03:50 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (dest[-1] != '/')
|
|
|
|
*dest++ = '/';
|
|
|
|
|
|
|
|
if (&dest[len] >= &new_buf[MAX_PATH])
|
|
|
|
return self; /* Resolved name would be too long. */
|
|
|
|
|
|
|
|
memcpy(dest, start, len);
|
|
|
|
dest += len;
|
|
|
|
*dest = '\0';
|
|
|
|
|
|
|
|
if (lstat(new_buf, &st) < 0)
|
|
|
|
return self; /* Unable to stat file. */
|
|
|
|
|
|
|
|
if (S_ISLNK(st.st_mode))
|
|
|
|
{
|
|
|
|
char buf[MAX_PATH];
|
|
|
|
|
|
|
|
if (++num_links > MAXSYMLINKS)
|
|
|
|
return self; /* Too many symbolic links. */
|
|
|
|
|
|
|
|
n = readlink(new_buf, buf, MAX_PATH);
|
|
|
|
if (n < 0)
|
|
|
|
return self; /* Couldn't resolve links. */
|
|
|
|
|
|
|
|
buf[n] = '\0';
|
|
|
|
|
|
|
|
if ((n + strlen(end)) >= MAX_PATH)
|
|
|
|
return self; /* Path would be too long. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Concatenate the resolved name with the string still to
|
|
|
|
* be processed, and start using the result as input.
|
|
|
|
*/
|
|
|
|
strcat(buf, end);
|
|
|
|
strcpy(extra, buf);
|
|
|
|
name = end = extra;
|
|
|
|
|
|
|
|
if (buf[0] == '/')
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* For an absolute link, we start at root again.
|
|
|
|
*/
|
|
|
|
dest = new_buf + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Backup - remove the last component.
|
|
|
|
*/
|
|
|
|
if (dest > new_buf + 1)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
dest--;
|
|
|
|
}
|
|
|
|
while (dest[-1] != '/');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
num_links = 0;
|
|
|
|
}
|
|
|
|
}
|
1998-10-15 13:46:35 +00:00
|
|
|
}
|
1999-07-20 09:03:50 +00:00
|
|
|
if (dest > new_buf + 1 && dest[-1] == '/')
|
|
|
|
--dest;
|
|
|
|
*dest = '\0';
|
1999-07-12 04:21:05 +00:00
|
|
|
#endif
|
1999-07-20 09:03:50 +00:00
|
|
|
if (strncmp(new_buf, "/private/", 9) == 0)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (lstat(&new_buf[8], &st) == 0)
|
|
|
|
strcpy(new_buf, &new_buf[8]);
|
|
|
|
}
|
2000-10-29 14:52:33 +00:00
|
|
|
return [NSStringClass stringWithCString: new_buf];
|
2000-06-12 05:17:41 +00:00
|
|
|
#endif /* (__MINGW__) */
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) stringByStandardizingPath
|
|
|
|
{
|
1999-07-12 04:21:05 +00:00
|
|
|
NSMutableString *s;
|
|
|
|
NSRange r;
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1996-11-24 21:04:24 +00:00
|
|
|
|
|
|
|
/* Expand `~' in the path */
|
|
|
|
s = [[self stringByExpandingTildeInPath] mutableCopy];
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[s methodForSelector: caiSel];
|
1996-11-24 21:04:24 +00:00
|
|
|
|
|
|
|
/* Remove `/private' */
|
|
|
|
if ([s hasPrefix: @"/private"])
|
|
|
|
[s deleteCharactersInRange: ((NSRange){0,7})];
|
|
|
|
|
1999-07-20 09:03:50 +00:00
|
|
|
/* Condense `//' and '/./' */
|
1999-07-12 11:23:06 +00:00
|
|
|
r = NSMakeRange(0, [s length]);
|
1999-07-12 04:21:05 +00:00
|
|
|
while ((r = [s rangeOfCharacterFromSet: pathSeps()
|
|
|
|
options: 0
|
|
|
|
range: r]).length)
|
|
|
|
{
|
1999-07-20 09:03:50 +00:00
|
|
|
unsigned length = [s length];
|
1996-11-24 21:04:24 +00:00
|
|
|
|
1999-07-20 09:03:50 +00:00
|
|
|
if (r.location + r.length + 1 <= length
|
2000-08-07 22:00:31 +00:00
|
|
|
&& pathSepMember((*caiImp)(s, caiSel, r.location + 1)) == YES)
|
1999-07-20 09:03:50 +00:00
|
|
|
{
|
|
|
|
[s deleteCharactersInRange: r];
|
|
|
|
}
|
|
|
|
else if (r.location + r.length + 2 <= length
|
2000-08-07 22:00:31 +00:00
|
|
|
&& (*caiImp)(s, caiSel, r.location + 1) == (unichar)'.'
|
|
|
|
&& pathSepMember((*caiImp)(s, caiSel, r.location + 2)) == YES)
|
1999-07-12 04:21:05 +00:00
|
|
|
{
|
|
|
|
r.length++;
|
|
|
|
[s deleteCharactersInRange: r];
|
|
|
|
}
|
|
|
|
else
|
1999-07-20 09:03:50 +00:00
|
|
|
{
|
|
|
|
r.location++;
|
|
|
|
}
|
1999-07-12 04:21:05 +00:00
|
|
|
if ((r.length = [s length]) > r.location)
|
|
|
|
r.length -= r.location;
|
|
|
|
else
|
|
|
|
break;
|
1996-11-24 21:04:24 +00:00
|
|
|
}
|
|
|
|
|
1999-07-12 04:21:05 +00:00
|
|
|
if ([s isAbsolutePath] == NO)
|
|
|
|
return s;
|
|
|
|
|
1999-07-20 09:03:50 +00:00
|
|
|
/*
|
2000-06-12 05:17:41 +00:00
|
|
|
* For absolute paths, we must resolve symbolic links or (on MINGW)
|
1999-07-20 09:03:50 +00:00
|
|
|
* remove '/../' sequences and their matching parent directories.
|
|
|
|
*/
|
2000-06-12 05:17:41 +00:00
|
|
|
#if defined(__MINGW__)
|
1996-11-24 21:04:24 +00:00
|
|
|
/* Condense `/../' */
|
1999-07-12 11:23:06 +00:00
|
|
|
r = NSMakeRange(0, [s length]);
|
1999-07-12 04:21:05 +00:00
|
|
|
while ((r = [s rangeOfCharacterFromSet: pathSeps()
|
|
|
|
options: 0
|
|
|
|
range: r]).length)
|
1996-11-24 21:04:24 +00:00
|
|
|
{
|
1999-07-12 04:21:05 +00:00
|
|
|
if (r.location + r.length + 3 <= [s length]
|
2000-08-07 22:00:31 +00:00
|
|
|
&& (*caiImp)(s, caiSel, r.location + 1) == (unichar)'.'
|
|
|
|
&& (*caiImp)(s, caiSel, r.location + 2) == (unichar)'.'
|
|
|
|
&& pathSepMember((*caiImp)(s, caiSel, r.location + 3)) == YES)
|
1999-01-20 13:28:28 +00:00
|
|
|
{
|
1999-07-12 04:21:05 +00:00
|
|
|
if (r.location > 0)
|
|
|
|
{
|
|
|
|
NSRange r2 = {0, r.location};
|
|
|
|
r = [s rangeOfCharacterFromSet: pathSeps()
|
|
|
|
options: NSBackwardsSearch
|
|
|
|
range: r2];
|
|
|
|
if (r.length == 0)
|
|
|
|
r = r2;
|
|
|
|
r.length += 4; /* Add the `/../' */
|
|
|
|
}
|
|
|
|
[s deleteCharactersInRange: r];
|
1999-01-20 13:28:28 +00:00
|
|
|
}
|
1999-07-12 04:21:05 +00:00
|
|
|
else
|
|
|
|
r.location++;
|
|
|
|
if ((r.length = [s length]) > r.location)
|
|
|
|
r.length -= r.location;
|
|
|
|
else
|
|
|
|
break;
|
1996-11-24 21:04:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
1999-07-20 09:03:50 +00:00
|
|
|
#else
|
|
|
|
return [s stringByResolvingSymlinksInPath];
|
|
|
|
#endif
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
// private methods for Unicode level 3 implementation
|
1997-05-03 18:05:21 +00:00
|
|
|
- (int) _baseLength
|
|
|
|
{
|
1999-04-09 17:07:21 +00:00
|
|
|
int blen = 0;
|
|
|
|
unsigned len = [self length];
|
|
|
|
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
int count = 0;
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1999-04-09 17:07:21 +00:00
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
1999-04-09 17:07:21 +00:00
|
|
|
while (count < len)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
if (!uni_isnonsp((*caiImp)(self, caiSel, count++)))
|
|
|
|
{
|
|
|
|
blen++;
|
|
|
|
}
|
|
|
|
}
|
1999-04-09 17:07:21 +00:00
|
|
|
}
|
1997-05-03 18:05:21 +00:00
|
|
|
return blen;
|
|
|
|
}
|
|
|
|
|
1998-01-21 15:09:22 +00:00
|
|
|
+ (NSString*) pathWithComponents: (NSArray*)components
|
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSString *s;
|
|
|
|
unsigned c;
|
|
|
|
unsigned i;
|
1998-01-21 15:09:22 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
c = [components count];
|
|
|
|
if (c == 0)
|
|
|
|
return @"";
|
|
|
|
s = [components objectAtIndex: 0];
|
|
|
|
if ([s length] == 0 || [s isEqualToString: pathSepString] == YES)
|
|
|
|
s = rootPath;
|
|
|
|
for (i = 1; i < c; i++)
|
|
|
|
{
|
|
|
|
s = [s stringByAppendingPathComponent: [components objectAtIndex: i]];
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
1999-06-03 10:59:25 +00:00
|
|
|
return s;
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) isAbsolutePath
|
|
|
|
{
|
1999-05-06 05:53:51 +00:00
|
|
|
if ([self length] == 0)
|
1998-01-21 15:09:22 +00:00
|
|
|
return NO;
|
1999-05-06 05:53:51 +00:00
|
|
|
|
2000-06-12 05:17:41 +00:00
|
|
|
#if defined(__MINGW__)
|
2000-09-22 04:20:52 +00:00
|
|
|
if ([self indexOfString: @":"] == NSNotFound)
|
|
|
|
{
|
|
|
|
const char *cpath = [self fileSystemRepresentation];
|
|
|
|
if (isalpha(cpath[0]) && cpath[1] == ':')
|
|
|
|
return YES;
|
|
|
|
else
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
else
|
1999-05-06 05:53:51 +00:00
|
|
|
return YES;
|
|
|
|
#else
|
1999-06-03 10:59:25 +00:00
|
|
|
{
|
|
|
|
unichar c = [self characterAtIndex: 0];
|
|
|
|
|
|
|
|
if (c == (unichar)'/' || c == (unichar)'~')
|
|
|
|
return YES;
|
|
|
|
}
|
1999-05-06 05:53:51 +00:00
|
|
|
#endif
|
|
|
|
return NO;
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*) pathComponents
|
|
|
|
{
|
2000-03-23 18:57:43 +00:00
|
|
|
NSMutableArray *a;
|
|
|
|
NSArray *r;
|
1998-01-21 15:09:22 +00:00
|
|
|
|
2000-03-23 18:57:43 +00:00
|
|
|
a = [[self componentsSeparatedByString: pathSepString] mutableCopy];
|
|
|
|
if ([a count] > 0)
|
|
|
|
{
|
|
|
|
int i;
|
1998-01-21 15:09:22 +00:00
|
|
|
|
2000-03-23 18:57:43 +00:00
|
|
|
/* If the path began with a '/' then the first path component must
|
|
|
|
* be a '/' rather than an empty string so that our output could be
|
|
|
|
* fed into [+pathWithComponents: ]
|
|
|
|
*/
|
|
|
|
if ([[a objectAtIndex: 0] length] == 0)
|
|
|
|
{
|
|
|
|
[a replaceObjectAtIndex: 0 withObject: pathSepString];
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
2000-03-23 18:57:43 +00:00
|
|
|
/* Any empty path components (except a trailing one) must be removed. */
|
|
|
|
for (i = [a count] - 2; i > 0; i--)
|
|
|
|
{
|
|
|
|
if ([[a objectAtIndex: i] length] == 0)
|
|
|
|
{
|
|
|
|
[a removeObjectAtIndex: i];
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-03-23 18:57:43 +00:00
|
|
|
r = [a copy];
|
|
|
|
RELEASE(a);
|
|
|
|
return AUTORELEASE(r);
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*) stringsByAppendingPaths: (NSArray*)paths
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
NSMutableArray *a;
|
|
|
|
NSArray *r;
|
|
|
|
int i;
|
1998-01-21 15:09:22 +00:00
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
a = [[NSMutableArray allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithCapacity: [paths count]];
|
|
|
|
for (i = 0; i < [paths count]; i++)
|
|
|
|
{
|
|
|
|
NSString *s = [paths objectAtIndex: i];
|
1998-01-21 15:09:22 +00:00
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
while ([s isAbsolutePath])
|
|
|
|
{
|
|
|
|
s = [s substringFromIndex: 1];
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
1999-05-06 14:42:26 +00:00
|
|
|
s = [self stringByAppendingPathComponent: s];
|
|
|
|
[a addObject: s];
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
1999-05-06 14:42:26 +00:00
|
|
|
r = [a copy];
|
1999-06-03 10:59:25 +00:00
|
|
|
RELEASE(a);
|
|
|
|
return AUTORELEASE(r);
|
1998-01-21 15:09:22 +00:00
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
+ (NSString*) localizedStringWithFormat: (NSString*) format, ...
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self notImplemented: _cmd];
|
1997-05-03 18:05:21 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSComparisonResult) caseInsensitiveCompare: (NSString*)aString
|
|
|
|
{
|
1999-11-26 19:43:43 +00:00
|
|
|
return [self compare: aString
|
|
|
|
options: NSCaseInsensitiveSearch
|
|
|
|
range: ((NSRange){0, [self length]})];
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
- (NSComparisonResult) compare: (NSString *)string
|
|
|
|
options: (unsigned)mask
|
|
|
|
range: (NSRange)compareRange
|
|
|
|
locale: (NSDictionary *)dict
|
2000-09-12 23:09:50 +00:00
|
|
|
{
|
|
|
|
// FIXME: This does only a normal compare
|
|
|
|
return [self compare: string
|
|
|
|
options: mask
|
|
|
|
range: compareRange];
|
|
|
|
}
|
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
- (NSComparisonResult) localizedCompare: (NSString *)string
|
2000-09-12 23:09:50 +00:00
|
|
|
{
|
|
|
|
// FIXME: This does only a normal compare
|
|
|
|
return [self compare: string
|
|
|
|
options: 0
|
|
|
|
range: ((NSRange){0, [self length]})];
|
|
|
|
}
|
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
- (NSComparisonResult) localizedCaseInsensitiveCompare: (NSString *)string
|
2000-09-12 23:09:50 +00:00
|
|
|
{
|
|
|
|
// FIXME: This does only a normal compare
|
|
|
|
return [self compare: string
|
|
|
|
options: NSCaseInsensitiveSearch
|
|
|
|
range: ((NSRange){0, [self length]})];
|
|
|
|
}
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
- (BOOL) writeToFile: (NSString*)filename
|
1999-11-26 19:43:43 +00:00
|
|
|
atomically: (BOOL)useAuxiliaryFile
|
1997-05-03 18:05:21 +00:00
|
|
|
{
|
1998-01-08 15:25:59 +00:00
|
|
|
id d;
|
1999-05-06 14:42:26 +00:00
|
|
|
if (!(d = [self dataUsingEncoding: [NSString defaultCStringEncoding]]))
|
1998-01-08 15:25:59 +00:00
|
|
|
d = [self dataUsingEncoding: NSUnicodeStringEncoding];
|
|
|
|
return [d writeToFile: filename atomically: useAuxiliaryFile];
|
1997-05-03 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
2000-09-30 04:54:43 +00:00
|
|
|
- (BOOL) writeToURL: (NSURL*)anURL atomically: (BOOL)atomically
|
2000-09-12 23:09:50 +00:00
|
|
|
{
|
|
|
|
id d;
|
|
|
|
if (!(d = [self dataUsingEncoding: [NSString defaultCStringEncoding]]))
|
|
|
|
d = [self dataUsingEncoding: NSUnicodeStringEncoding];
|
|
|
|
return [d writeToURL: anURL atomically: atomically];
|
|
|
|
}
|
|
|
|
|
2000-03-17 13:13:08 +00:00
|
|
|
- (void) descriptionWithLocale: (NSDictionary*)aLocale
|
|
|
|
indent: (unsigned)level
|
|
|
|
to: (id<GNUDescriptionDestination>)output
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1998-12-18 17:05:44 +00:00
|
|
|
if ([self length] == 0)
|
|
|
|
{
|
|
|
|
[output appendString: @"\"\""];
|
|
|
|
return;
|
1997-10-31 16:26:44 +00:00
|
|
|
}
|
|
|
|
|
1998-12-18 17:05:44 +00:00
|
|
|
if (quotables == nil)
|
2000-03-17 13:13:08 +00:00
|
|
|
{
|
|
|
|
setupQuotables();
|
|
|
|
}
|
1998-12-18 17:05:44 +00:00
|
|
|
if ([self rangeOfCharacterFromSet: quotables].length > 0)
|
|
|
|
{
|
|
|
|
const char *cstring = [self cString];
|
|
|
|
const char *from;
|
|
|
|
int len = 0;
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-12-18 17:05:44 +00:00
|
|
|
for (from = cstring; *from; from++)
|
|
|
|
{
|
|
|
|
switch (*from)
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
case '\a':
|
|
|
|
case '\b':
|
|
|
|
case '\t':
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
case '\v':
|
|
|
|
case '\f':
|
|
|
|
case '\\':
|
|
|
|
case '\'' :
|
|
|
|
case '"' :
|
1998-12-18 17:05:44 +00:00
|
|
|
len += 2;
|
|
|
|
break;
|
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
default:
|
1998-12-18 17:05:44 +00:00
|
|
|
if (isprint(*from) || *from == ' ')
|
|
|
|
{
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
len += 4;
|
|
|
|
}
|
|
|
|
break;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1997-10-31 16:26:44 +00:00
|
|
|
}
|
1998-12-18 17:05:44 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
char buf[len+3];
|
|
|
|
char *ptr = buf;
|
|
|
|
|
|
|
|
*ptr++ = '"';
|
|
|
|
for (from = cstring; *from; from++)
|
|
|
|
{
|
|
|
|
switch (*from)
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
case '\a': *ptr++ = '\\'; *ptr++ = 'a'; break;
|
|
|
|
case '\b': *ptr++ = '\\'; *ptr++ = 'b'; break;
|
|
|
|
case '\t': *ptr++ = '\\'; *ptr++ = 't'; break;
|
|
|
|
case '\r': *ptr++ = '\\'; *ptr++ = 'r'; break;
|
|
|
|
case '\n': *ptr++ = '\\'; *ptr++ = 'n'; break;
|
|
|
|
case '\v': *ptr++ = '\\'; *ptr++ = 'v'; break;
|
|
|
|
case '\f': *ptr++ = '\\'; *ptr++ = 'f'; break;
|
|
|
|
case '\\': *ptr++ = '\\'; *ptr++ = '\\'; break;
|
|
|
|
case '\'': *ptr++ = '\\'; *ptr++ = '\''; break;
|
|
|
|
case '"' : *ptr++ = '\\'; *ptr++ = '"'; break;
|
|
|
|
|
|
|
|
default:
|
1998-12-18 17:05:44 +00:00
|
|
|
if (isprint(*from) || *from == ' ')
|
|
|
|
{
|
|
|
|
*ptr++ = *from;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sprintf(ptr, "\\%03o", *(unsigned char*)from);
|
|
|
|
ptr = &ptr[4];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*ptr++ = '"';
|
|
|
|
*ptr = '\0';
|
2000-10-23 06:18:03 +00:00
|
|
|
[output appendString: [NSStringClass stringWithCString: buf]];
|
1998-12-18 17:05:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[output appendString: self];
|
1997-10-31 16:26:44 +00:00
|
|
|
}
|
|
|
|
}
|
1997-05-03 18:05:21 +00:00
|
|
|
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
/* NSCopying Protocol */
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1998-03-12 14:21:20 +00:00
|
|
|
if ([self isKindOfClass: [NSMutableString class]] ||
|
2000-10-23 06:18:03 +00:00
|
|
|
NSShouldRetainWithZone(self, zone) == NO)
|
2000-11-03 10:11:56 +00:00
|
|
|
return [[NSStringClass allocWithZone: zone] initWithString: self];
|
1998-03-12 14:21:20 +00:00
|
|
|
else
|
1999-06-03 10:59:25 +00:00
|
|
|
return RETAIN(self);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
- (id) mutableCopyWithZone: (NSZone*)zone
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return [[GSMutableStringClass allocWithZone: zone] initWithString: self];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1995-04-09 01:53:53 +00:00
|
|
|
/* NSCoding Protocol */
|
|
|
|
|
2000-10-09 04:41:18 +00:00
|
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
1995-04-09 01:53:53 +00:00
|
|
|
{
|
2000-10-09 04:41:18 +00:00
|
|
|
unsigned count = [self length];
|
|
|
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned) at: &count];
|
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
NSStringEncoding enc = NSUnicodeStringEncoding;
|
|
|
|
unichar *chars;
|
|
|
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(NSStringEncoding) at: &enc];
|
|
|
|
|
|
|
|
chars = NSZoneMalloc(NSDefaultMallocZone(), count*sizeof(unichar));
|
|
|
|
[self getCharacters: chars];
|
|
|
|
[aCoder encodeArrayOfObjCType: @encode(unichar)
|
|
|
|
count: count
|
|
|
|
at: chars];
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), chars);
|
|
|
|
}
|
1995-04-09 01:53:53 +00:00
|
|
|
}
|
|
|
|
|
2000-10-09 04:41:18 +00:00
|
|
|
- (id) initWithCoder: (NSCoder*)aCoder
|
1995-04-09 01:53:53 +00:00
|
|
|
{
|
2000-10-09 04:41:18 +00:00
|
|
|
unsigned count;
|
|
|
|
|
|
|
|
[aCoder decodeValueOfObjCType: @encode(unsigned) at: &count];
|
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
NSStringEncoding enc;
|
|
|
|
NSZone *zone;
|
|
|
|
|
|
|
|
[aCoder decodeValueOfObjCType: @encode(NSStringEncoding) at: &enc];
|
|
|
|
#if GS_WITH_GC
|
|
|
|
zone = GSAtomicMallocZone();
|
|
|
|
#else
|
2000-10-31 16:17:33 +00:00
|
|
|
zone = GSObjCZone(self);
|
2000-10-09 04:41:18 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (enc == NSUnicodeStringEncoding)
|
|
|
|
{
|
|
|
|
unichar *chars;
|
|
|
|
|
|
|
|
chars = NSZoneMalloc(zone, count*sizeof(unichar));
|
|
|
|
[aCoder decodeArrayOfObjCType: @encode(unichar)
|
|
|
|
count: count
|
|
|
|
at: chars];
|
|
|
|
self = [self initWithCharactersNoCopy: chars
|
|
|
|
length: count
|
|
|
|
freeWhenDone: YES];
|
|
|
|
}
|
|
|
|
else if (enc == NSASCIIStringEncoding || enc == _DefaultStringEncoding)
|
|
|
|
{
|
|
|
|
unsigned char *chars;
|
|
|
|
|
|
|
|
chars = NSZoneMalloc(zone, count+1);
|
|
|
|
[aCoder decodeArrayOfObjCType: @encode(unsigned char)
|
|
|
|
count: count
|
|
|
|
at: chars];
|
|
|
|
self = [self initWithCStringNoCopy: chars
|
|
|
|
length: count
|
|
|
|
freeWhenDone: YES];
|
|
|
|
}
|
|
|
|
else if (enc == NSUTF8StringEncoding)
|
|
|
|
{
|
|
|
|
unsigned char *chars;
|
|
|
|
|
|
|
|
chars = NSZoneMalloc(zone, count+1);
|
|
|
|
[aCoder decodeArrayOfObjCType: @encode(unsigned char)
|
|
|
|
count: count
|
|
|
|
at: chars];
|
|
|
|
chars[count] = '\0';
|
|
|
|
self = [self initWithUTF8String: chars];
|
|
|
|
NSZoneFree(zone, chars);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned char *chars;
|
|
|
|
NSData *data;
|
|
|
|
|
|
|
|
chars = NSZoneMalloc(zone, count);
|
|
|
|
[aCoder decodeArrayOfObjCType: @encode(unsigned char)
|
|
|
|
count: count
|
|
|
|
at: chars];
|
2000-10-23 11:44:34 +00:00
|
|
|
data = [NSDataClass allocWithZone: zone];
|
2000-10-09 04:41:18 +00:00
|
|
|
data = [data initWithBytesNoCopy: chars length: count];
|
|
|
|
self = [self initWithData: data encoding: enc];
|
|
|
|
RELEASE(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-10-09 05:30:04 +00:00
|
|
|
self = [self initWithCStringNoCopy: 0 length: 0 freeWhenDone: NO];
|
2000-10-09 04:41:18 +00:00
|
|
|
}
|
1999-07-02 13:26:37 +00:00
|
|
|
return self;
|
1995-04-09 01:53:53 +00:00
|
|
|
}
|
|
|
|
|
1998-12-18 17:05:44 +00:00
|
|
|
- (Class) classForCoder
|
|
|
|
{
|
2000-10-23 06:18:03 +00:00
|
|
|
return NSStringClass;
|
1998-12-18 17:05:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
|
|
|
|
{
|
|
|
|
if ([aCoder isByref] == NO)
|
|
|
|
return self;
|
|
|
|
return [super replacementObjectForPortCoder: aCoder];
|
|
|
|
}
|
|
|
|
|
1999-02-04 13:49:27 +00:00
|
|
|
- (id) propertyList
|
|
|
|
{
|
2000-10-23 11:44:34 +00:00
|
|
|
return GSPropertyList(self);
|
1999-02-04 13:49:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*) propertyListFromStringsFileFormat
|
|
|
|
{
|
2000-10-23 11:44:34 +00:00
|
|
|
return GSPropertyListFromStringsFormat(self);
|
1999-02-04 13:49:27 +00:00
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
@end
|
|
|
|
|
1997-05-03 18:05:21 +00:00
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
@implementation NSMutableString
|
|
|
|
|
1999-07-02 13:26:37 +00:00
|
|
|
+ (id) allocWithZone: (NSZone*)z
|
1995-11-19 20:29:39 +00:00
|
|
|
{
|
2000-10-23 06:18:03 +00:00
|
|
|
if (self == NSMutableStringClass)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return NSAllocateObject(GSMutableStringClass, 0, z);
|
2000-08-07 22:00:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return NSAllocateObject(self, 0, z);
|
|
|
|
}
|
1995-11-19 20:29:39 +00:00
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
// Creating Temporary Strings
|
|
|
|
|
1999-12-01 19:36:20 +00:00
|
|
|
+ (NSMutableString*) string
|
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[GSMutableStringClass allocWithZone:
|
1999-12-01 19:36:20 +00:00
|
|
|
NSDefaultMallocZone()] initWithCapacity: 0]);
|
|
|
|
}
|
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
+ (NSMutableString*) stringWithCapacity: (unsigned)capacity
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[GSMutableStringClass allocWithZone:
|
1999-12-01 19:36:20 +00:00
|
|
|
NSDefaultMallocZone()] initWithCapacity: capacity]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Inefficient. */
|
|
|
|
+ (NSString*) stringWithCharacters: (const unichar*)characters
|
1999-07-02 13:26:37 +00:00
|
|
|
length: (unsigned)length
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[GSMutableStringClass allocWithZone:
|
1999-12-01 19:36:20 +00:00
|
|
|
NSDefaultMallocZone()] initWithCharacters: characters length: length]);
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) stringWithContentsOfFile: (NSString *)path
|
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[GSMutableStringClass allocWithZone:
|
1999-12-01 19:36:20 +00:00
|
|
|
NSDefaultMallocZone()] initWithContentsOfFile: path]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString*) stringWithCString: (const char*)byteString
|
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[GSMutableStringClass allocWithZone:
|
1999-06-03 10:59:25 +00:00
|
|
|
NSDefaultMallocZone()] initWithCString: byteString]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1998-09-30 07:42:38 +00:00
|
|
|
+ (NSString*) stringWithCString: (const char*)byteString
|
1999-06-21 08:30:26 +00:00
|
|
|
length: (unsigned)length
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
2000-11-03 10:11:56 +00:00
|
|
|
return AUTORELEASE([[GSMutableStringClass allocWithZone:
|
1999-06-03 10:59:25 +00:00
|
|
|
NSDefaultMallocZone()] initWithCString: byteString length: length]);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* xxx Change this when we have non-CString classes */
|
|
|
|
+ (NSString*) stringWithFormat: (NSString*)format, ...
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
1999-05-06 14:42:26 +00:00
|
|
|
self = [super stringWithFormat: format arguments: ap];
|
1995-04-03 01:35:42 +00:00
|
|
|
va_end(ap);
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2000-10-31 18:11:30 +00:00
|
|
|
// Designated initialiser
|
1999-07-02 13:26:37 +00:00
|
|
|
- (id) initWithCapacity: (unsigned)capacity
|
1997-05-03 18:05:21 +00:00
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self subclassResponsibility: _cmd];
|
1997-05-03 18:05:21 +00:00
|
|
|
return self;
|
|
|
|
}
|
1995-04-03 01:35:42 +00:00
|
|
|
|
2000-10-31 18:11:30 +00:00
|
|
|
- (id) initWithCharactersNoCopy: (unichar*)chars
|
|
|
|
length: (unsigned)length
|
|
|
|
freeWhenDone: (BOOL)flag
|
|
|
|
{
|
|
|
|
if ((self = [self initWithCapacity: length]) != nil && length > 0)
|
|
|
|
{
|
|
|
|
NSString *tmp;
|
|
|
|
|
|
|
|
tmp = [NSString allocWithZone: NSDefaultMallocZone()];
|
|
|
|
tmp = [tmp initWithCharactersNoCopy: chars
|
|
|
|
length: length
|
|
|
|
freeWhenDone: flag];
|
|
|
|
[self replaceCharactersInRange: NSMakeRange(0,0) withString: tmp];
|
|
|
|
RELEASE(tmp);
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCStringNoCopy: (char*)chars
|
|
|
|
length: (unsigned)length
|
|
|
|
freeWhenDone: (BOOL)flag
|
|
|
|
{
|
|
|
|
if ((self = [self initWithCapacity: length]) != nil && length > 0)
|
|
|
|
{
|
|
|
|
NSString *tmp;
|
|
|
|
|
|
|
|
tmp = [NSString allocWithZone: NSDefaultMallocZone()];
|
|
|
|
tmp = [tmp initWithCStringNoCopy: chars
|
|
|
|
length: length
|
|
|
|
freeWhenDone: flag];
|
|
|
|
[self replaceCharactersInRange: NSMakeRange(0,0) withString: tmp];
|
|
|
|
RELEASE(tmp);
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
// Modify A String
|
|
|
|
|
|
|
|
- (void) appendString: (NSString*)aString
|
|
|
|
{
|
1998-01-26 14:18:18 +00:00
|
|
|
NSRange aRange;
|
|
|
|
|
|
|
|
aRange.location = [self length];
|
|
|
|
aRange.length = 0;
|
|
|
|
[self replaceCharactersInRange: aRange withString: aString];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Inefficient. */
|
|
|
|
- (void) appendFormat: (NSString*)format, ...
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
id tmp;
|
|
|
|
va_start(ap, format);
|
1999-05-06 14:42:26 +00:00
|
|
|
tmp = [[NSString allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithFormat: format arguments: ap];
|
1995-04-03 01:35:42 +00:00
|
|
|
va_end(ap);
|
1999-05-06 14:42:26 +00:00
|
|
|
[self appendString: tmp];
|
1999-06-03 10:59:25 +00:00
|
|
|
RELEASE(tmp);
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
2000-10-09 04:41:18 +00:00
|
|
|
- (Class) classForCoder
|
|
|
|
{
|
2000-10-23 06:18:03 +00:00
|
|
|
return NSMutableStringClass;
|
2000-10-09 04:41:18 +00:00
|
|
|
}
|
|
|
|
|
1995-04-03 01:35:42 +00:00
|
|
|
- (void) deleteCharactersInRange: (NSRange)range
|
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self replaceCharactersInRange: range withString: nil];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
1999-05-06 14:42:26 +00:00
|
|
|
- (void) insertString: (NSString*)aString atIndex: (unsigned)loc
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1997-12-08 20:04:16 +00:00
|
|
|
NSRange range = {loc, 0};
|
1999-05-06 14:42:26 +00:00
|
|
|
[self replaceCharactersInRange: range withString: aString];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) replaceCharactersInRange: (NSRange)range
|
1999-11-26 19:43:43 +00:00
|
|
|
withString: (NSString*)aString
|
1995-04-03 01:35:42 +00:00
|
|
|
{
|
1999-05-06 14:42:26 +00:00
|
|
|
[self subclassResponsibility: _cmd];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setString: (NSString*)aString
|
|
|
|
{
|
1997-12-08 20:04:16 +00:00
|
|
|
NSRange range = {0, [self length]};
|
1999-05-06 14:42:26 +00:00
|
|
|
[self replaceCharactersInRange: range withString: aString];
|
1995-04-03 01:35:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
1999-02-13 00:50:41 +00:00
|
|
|
#ifndef NO_GNUSTEP
|
1999-06-03 10:59:25 +00:00
|
|
|
|
1999-02-13 00:50:41 +00:00
|
|
|
@implementation NSString (GSTrimming)
|
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringByTrimmingLeadWhiteSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSCharacterSet *nonSPSet;
|
|
|
|
NSRange nonSPCharRange;
|
|
|
|
|
|
|
|
nonSPSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet];
|
|
|
|
nonSPCharRange = [self rangeOfCharacterFromSet: nonSPSet];
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
if (nonSPCharRange.length > 0)
|
|
|
|
return [self substringFromIndex: nonSPCharRange.location];
|
1999-02-13 00:50:41 +00:00
|
|
|
else
|
1999-06-03 10:59:25 +00:00
|
|
|
return @"";
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringByTrimmingTailWhiteSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSCharacterSet *nonSPSet;
|
|
|
|
NSRange nonSPCharRange;
|
|
|
|
|
|
|
|
nonSPSet= [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet];
|
|
|
|
nonSPCharRange = [self rangeOfCharacterFromSet: nonSPSet
|
|
|
|
options: NSBackwardsSearch];
|
|
|
|
if (nonSPCharRange.length > 0)
|
|
|
|
return [self substringToIndex: nonSPCharRange.location+1];
|
1999-02-13 00:50:41 +00:00
|
|
|
else
|
1999-06-03 10:59:25 +00:00
|
|
|
return @"";
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringByTrimmingWhiteSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
|
|
|
return [[self stringByTrimmingLeadWhiteSpaces]
|
1999-06-03 10:59:25 +00:00
|
|
|
stringByTrimmingTailWhiteSpaces];
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringByTrimmingLeadSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSMutableString *tmp = [self mutableCopy];
|
|
|
|
NSString *str;
|
|
|
|
|
1999-02-13 00:50:41 +00:00
|
|
|
[tmp trimLeadSpaces];
|
1999-06-03 10:59:25 +00:00
|
|
|
str = AUTORELEASE([tmp copy]);
|
|
|
|
RELEASE(tmp);
|
|
|
|
return str;
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringByTrimmingTailSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSMutableString *tmp = [self mutableCopy];
|
|
|
|
NSString *str;
|
|
|
|
|
1999-02-13 00:50:41 +00:00
|
|
|
[tmp trimTailSpaces];
|
1999-06-03 10:59:25 +00:00
|
|
|
str = AUTORELEASE([tmp copy]);
|
|
|
|
RELEASE(tmp);
|
|
|
|
return str;
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringByTrimmingSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSMutableString *tmp = [self mutableCopy];
|
|
|
|
NSString *str;
|
|
|
|
|
1999-02-13 00:50:41 +00:00
|
|
|
[tmp trimLeadSpaces];
|
|
|
|
[tmp trimTailSpaces];
|
1999-06-03 10:59:25 +00:00
|
|
|
str = AUTORELEASE([tmp copy]);
|
|
|
|
RELEASE(tmp);
|
|
|
|
return str;
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSMutableString (GSTrimming)
|
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (void) trimLeadSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
unsigned location = 0;
|
|
|
|
unsigned length = [self length];
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1999-06-03 10:59:25 +00:00
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
|
|
|
while (location < length && isspace((*caiImp)(self, caiSel, location)))
|
|
|
|
{
|
|
|
|
location++;
|
|
|
|
}
|
1999-06-03 10:59:25 +00:00
|
|
|
if (location > 0)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
[self deleteCharactersInRange: NSMakeRange(0,location)];
|
|
|
|
}
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (void) trimTailSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
unsigned length = [self length];
|
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
if (length > 0)
|
1999-06-03 10:59:25 +00:00
|
|
|
{
|
|
|
|
unsigned location = length;
|
2000-08-07 22:00:31 +00:00
|
|
|
unichar (*caiImp)(NSString*, SEL, unsigned);
|
1999-02-13 00:50:41 +00:00
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
caiImp = (unichar (*)())[self methodForSelector: caiSel];
|
1999-06-03 10:59:25 +00:00
|
|
|
while (location > 0)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
if (!isspace((*caiImp)(self, caiSel, --location)))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-06-03 10:59:25 +00:00
|
|
|
if (location < length-1)
|
2000-08-07 22:00:31 +00:00
|
|
|
{
|
|
|
|
[self deleteCharactersInRange: NSMakeRange((location == 0) ? 0
|
|
|
|
: location + 1, length - ((location == 0) ? 0 : location + 1))];
|
|
|
|
}
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
}
|
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (void) trimSpaces
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
|
|
|
[self trimLeadSpaces];
|
|
|
|
[self trimTailSpaces];
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
2000-10-23 06:18:03 +00:00
|
|
|
@implementation NSString (GNUstep)
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringWithoutSuffix: (NSString*)_suffix
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSCAssert2([self hasSuffix: _suffix],
|
|
|
|
@"'%@' has not the suffix '%@'",self,_suffix);
|
1999-11-26 19:43:43 +00:00
|
|
|
return [self substringToIndex: ([self length] - [_suffix length])];
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (NSString*) stringWithoutPrefix: (NSString*)_prefix
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSCAssert2([self hasPrefix: _prefix],
|
|
|
|
@"'%@' has not the prefix '%@'",self,_prefix);
|
1999-05-06 14:42:26 +00:00
|
|
|
return [self substringFromIndex: [_prefix length]];
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
- (NSString*) stringByReplacingString: (NSString*)replace
|
|
|
|
withString: (NSString*)by
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-11-26 19:43:43 +00:00
|
|
|
NSRange range = [self rangeOfString: replace];
|
1999-06-03 10:59:25 +00:00
|
|
|
|
|
|
|
if (range.length > 0)
|
|
|
|
{
|
|
|
|
NSMutableString *tmp = [self mutableCopy];
|
|
|
|
NSString *str;
|
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
[tmp replaceString: replace withString: by];
|
1999-06-03 10:59:25 +00:00
|
|
|
str = AUTORELEASE([tmp copy]);
|
|
|
|
RELEASE(tmp);
|
|
|
|
return str;
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
else
|
1999-06-03 10:59:25 +00:00
|
|
|
return self;
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
2000-10-23 06:18:03 +00:00
|
|
|
@implementation NSMutableString (GNUstep)
|
1999-06-03 10:59:25 +00:00
|
|
|
- (void) removeSuffix: (NSString*)_suffix
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSCAssert2([self hasSuffix: _suffix],
|
|
|
|
@"'%@' has not the suffix '%@'",self,_suffix);
|
|
|
|
[self deleteCharactersInRange:
|
1999-11-26 19:43:43 +00:00
|
|
|
NSMakeRange([self length] - [_suffix length], [_suffix length])];
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
- (void) removePrefix: (NSString*)_prefix;
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
NSCAssert2([self hasPrefix: _prefix],
|
|
|
|
@"'%@' has not the prefix '%@'",self,_prefix);
|
1999-11-26 19:43:43 +00:00
|
|
|
[self deleteCharactersInRange: NSMakeRange(0, [_prefix length])];
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
|
1999-11-26 19:43:43 +00:00
|
|
|
- (void) replaceString: (NSString*)replace
|
|
|
|
withString: (NSString*)by
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-11-26 19:43:43 +00:00
|
|
|
NSRange range = [self rangeOfString: replace];
|
|
|
|
|
|
|
|
if (range.length > 0)
|
1999-06-03 10:59:25 +00:00
|
|
|
{
|
1999-11-26 19:43:43 +00:00
|
|
|
unsigned byLen = [by length];
|
|
|
|
|
1999-06-03 10:59:25 +00:00
|
|
|
do
|
1999-02-13 00:50:41 +00:00
|
|
|
{
|
1999-06-03 10:59:25 +00:00
|
|
|
[self replaceCharactersInRange: range
|
1999-11-26 19:43:43 +00:00
|
|
|
withString: by];
|
|
|
|
range.location += byLen;
|
|
|
|
range.length = [self length] - range.location;
|
|
|
|
range = [self rangeOfString: replace
|
|
|
|
options: 0
|
|
|
|
range: range];
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
1999-11-26 19:43:43 +00:00
|
|
|
while (range.length > 0);
|
1999-06-03 10:59:25 +00:00
|
|
|
}
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
@end
|
|
|
|
|
2000-10-23 11:44:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
#if HAVE_LIBXML
|
|
|
|
#include <Foundation/GSXML.h>
|
|
|
|
|
|
|
|
static void
|
|
|
|
decodeBase64Unit(const char* ptr, unsigned char *out)
|
|
|
|
{
|
|
|
|
out[0] = (ptr[0] << 2) | ((ptr[1] & 0x30) >> 4);
|
|
|
|
out[1] = ((ptr[1] & 0x0F) << 4) | ((ptr[2] & 0x3C) >> 2);
|
|
|
|
out[2] = ((ptr[2] & 0x03) << 6) | (ptr[3] & 0x3F);
|
|
|
|
out[3] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSData*
|
|
|
|
decodeBase64(const char *source)
|
|
|
|
{
|
|
|
|
int length = strlen(source);
|
|
|
|
char *sourceBuffer = objc_malloc(length+1);
|
|
|
|
NSMutableData *data = [NSMutableData dataWithCapacity:0];
|
|
|
|
int i, j;
|
|
|
|
unsigned char tmp[4];
|
|
|
|
|
|
|
|
strcpy(sourceBuffer, source);
|
|
|
|
j = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
if (!isspace(source[i]))
|
|
|
|
{
|
|
|
|
sourceBuffer[j++] = source[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sourceBuffer[j] = '\0';
|
|
|
|
length = strlen(sourceBuffer);
|
|
|
|
while (length > 0 && sourceBuffer[length-1] == '=')
|
|
|
|
{
|
|
|
|
sourceBuffer[--length] = '\0';
|
|
|
|
}
|
|
|
|
for (i = 0; i < length; i += 4)
|
|
|
|
{
|
|
|
|
decodeBase64Unit(&sourceBuffer[i], tmp);
|
|
|
|
[data appendBytes: tmp length: strlen(tmp)];
|
|
|
|
}
|
|
|
|
|
|
|
|
objc_free(sourceBuffer);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define inrange(ch,min,max) ((ch)>=(min) && (ch)<=(max))
|
|
|
|
#define char2num(ch) \
|
|
|
|
inrange(ch,'0','9') \
|
|
|
|
? ((ch)-0x30) \
|
|
|
|
: (inrange(ch,'a','f') \
|
|
|
|
? ((ch)-0x57) : ((ch)-0x37))
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
const unichar *ptr;
|
|
|
|
unsigned end;
|
|
|
|
unsigned pos;
|
|
|
|
unsigned lin;
|
|
|
|
NSString *err;
|
|
|
|
} pldata;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Property list parsing - skip whitespace keeping count of lines and
|
|
|
|
* regarding objective-c style comments as whitespace.
|
|
|
|
* Returns YES if there is any non-whitespace text remaining.
|
|
|
|
*/
|
|
|
|
static BOOL skipSpace(pldata *pld)
|
|
|
|
{
|
|
|
|
unichar c;
|
|
|
|
|
|
|
|
while (pld->pos < pld->end)
|
|
|
|
{
|
|
|
|
c = pld->ptr[pld->pos];
|
|
|
|
|
|
|
|
if ((*whitespceImp)(whitespce, cMemberSel, c) == NO)
|
|
|
|
{
|
|
|
|
if (c == '/' && pld->pos < pld->end - 1)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Check for comments beginning '/' followed by '/' or '*'
|
|
|
|
*/
|
|
|
|
if (pld->ptr[pld->pos + 1] == '/')
|
|
|
|
{
|
|
|
|
pld->pos += 2;
|
|
|
|
while (pld->pos < pld->end)
|
|
|
|
{
|
|
|
|
c = pld->ptr[pld->pos];
|
|
|
|
if (c == '\n')
|
|
|
|
break;
|
|
|
|
pld->pos++;
|
|
|
|
}
|
|
|
|
if (pld->pos >= pld->end)
|
|
|
|
{
|
|
|
|
pld->err = @"reached end of string in comment";
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pld->ptr[pld->pos + 1] == '*')
|
|
|
|
{
|
|
|
|
pld->pos += 2;
|
|
|
|
while (pld->pos < pld->end)
|
|
|
|
{
|
|
|
|
c = pld->ptr[pld->pos];
|
|
|
|
if (c == '\n')
|
|
|
|
pld->lin++;
|
|
|
|
else if (c == '*' && pld->pos < pld->end - 1
|
|
|
|
&& pld->ptr[pld->pos+1] == '/')
|
|
|
|
{
|
|
|
|
pld->pos++; /* Skip past '*' */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pld->pos++;
|
|
|
|
}
|
|
|
|
if (pld->pos >= pld->end)
|
|
|
|
{
|
|
|
|
pld->err = @"reached end of string in comment";
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
if (c == '\n')
|
|
|
|
pld->lin++;
|
|
|
|
pld->pos++;
|
|
|
|
}
|
|
|
|
pld->err = @"reached end of string";
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline id parseQuotedString(pldata* pld)
|
|
|
|
{
|
|
|
|
unsigned start = ++pld->pos;
|
|
|
|
unsigned escaped = 0;
|
|
|
|
unsigned shrink = 0;
|
|
|
|
BOOL hex = NO;
|
|
|
|
NSString *obj;
|
|
|
|
|
|
|
|
while (pld->pos < pld->end)
|
|
|
|
{
|
|
|
|
unichar c = pld->ptr[pld->pos];
|
|
|
|
|
|
|
|
if (escaped)
|
|
|
|
{
|
|
|
|
if (escaped == 1 && c == '0')
|
|
|
|
{
|
|
|
|
escaped = 2;
|
|
|
|
hex = NO;
|
|
|
|
}
|
|
|
|
else if (escaped > 1)
|
|
|
|
{
|
|
|
|
if (escaped == 2 && c == 'x')
|
|
|
|
{
|
|
|
|
hex = YES;
|
|
|
|
shrink++;
|
|
|
|
escaped++;
|
|
|
|
}
|
|
|
|
else if (hex && (*hexdigitsImp)(hexdigits, cMemberSel, c))
|
|
|
|
{
|
|
|
|
shrink++;
|
|
|
|
escaped++;
|
|
|
|
}
|
|
|
|
else if (c >= '0' && c <= '7')
|
|
|
|
{
|
|
|
|
shrink++;
|
|
|
|
escaped++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pld->pos--;
|
|
|
|
escaped = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
escaped = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (c == '\\')
|
|
|
|
{
|
|
|
|
escaped = 1;
|
|
|
|
shrink++;
|
|
|
|
}
|
|
|
|
else if (c == '"')
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (c == '\n')
|
|
|
|
pld->lin++;
|
|
|
|
pld->pos++;
|
|
|
|
}
|
|
|
|
if (pld->pos >= pld->end)
|
|
|
|
{
|
|
|
|
pld->err = @"reached end of string while parsing quoted string";
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (pld->pos - start - shrink == 0)
|
|
|
|
{
|
|
|
|
obj = @"";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unichar chars[pld->pos - start - shrink];
|
|
|
|
unsigned j;
|
|
|
|
unsigned k;
|
|
|
|
|
|
|
|
escaped = 0;
|
|
|
|
hex = NO;
|
|
|
|
for (j = start, k = 0; j < pld->pos; j++)
|
|
|
|
{
|
|
|
|
unichar c = pld->ptr[j];
|
|
|
|
|
|
|
|
if (escaped)
|
|
|
|
{
|
|
|
|
if (escaped == 1 && c == '0')
|
|
|
|
{
|
|
|
|
chars[k] = 0;
|
|
|
|
hex = NO;
|
|
|
|
escaped++;
|
|
|
|
}
|
|
|
|
else if (escaped > 1)
|
|
|
|
{
|
|
|
|
if (escaped == 2 && c == 'x')
|
|
|
|
{
|
|
|
|
hex = YES;
|
|
|
|
escaped++;
|
|
|
|
}
|
|
|
|
else if (hex && (*hexdigitsImp)(hexdigits, cMemberSel, c))
|
|
|
|
{
|
|
|
|
chars[k] <<= 4;
|
|
|
|
chars[k] |= char2num(c);
|
|
|
|
escaped++;
|
|
|
|
}
|
|
|
|
else if (c >= '0' && c <= '7')
|
|
|
|
{
|
|
|
|
chars[k] <<= 3;
|
|
|
|
chars[k] |= (c - '0');
|
|
|
|
escaped++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
escaped = 0;
|
|
|
|
j--;
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
escaped = 0;
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case 'a' : chars[k] = '\a'; break;
|
|
|
|
case 'b' : chars[k] = '\b'; break;
|
|
|
|
case 't' : chars[k] = '\t'; break;
|
|
|
|
case 'r' : chars[k] = '\r'; break;
|
|
|
|
case 'n' : chars[k] = '\n'; break;
|
|
|
|
case 'v' : chars[k] = '\v'; break;
|
|
|
|
case 'f' : chars[k] = '\f'; break;
|
|
|
|
default : chars[k] = c; break;
|
|
|
|
}
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
chars[k] = c;
|
|
|
|
if (c == '\\')
|
|
|
|
{
|
|
|
|
escaped = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
obj = (*plAlloc)(NSStringClass, @selector(allocWithZone:),
|
|
|
|
NSDefaultMallocZone());
|
|
|
|
obj = (*plInit)(obj, plSel, (void*)chars, pld->pos - start - shrink);
|
|
|
|
}
|
|
|
|
pld->pos++;
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline id parseUnquotedString(pldata *pld)
|
|
|
|
{
|
|
|
|
unsigned start = pld->pos;
|
|
|
|
id obj;
|
|
|
|
|
|
|
|
while (pld->pos < pld->end)
|
|
|
|
{
|
|
|
|
if ((*quotablesImp)(quotables, cMemberSel, pld->ptr[pld->pos]) == YES)
|
|
|
|
break;
|
|
|
|
pld->pos++;
|
|
|
|
}
|
|
|
|
obj = (*plAlloc)(NSStringClass, @selector(allocWithZone:),
|
|
|
|
NSDefaultMallocZone());
|
|
|
|
obj = (*plInit)(obj, plSel, (void*)&pld->ptr[start], pld->pos-start);
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
static id parsePlItem(pldata* pld)
|
|
|
|
{
|
|
|
|
if (skipSpace(pld) == NO)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
switch (pld->ptr[pld->pos])
|
|
|
|
{
|
|
|
|
case '{':
|
|
|
|
{
|
|
|
|
NSMutableDictionary *dict;
|
|
|
|
|
|
|
|
dict = [[plDictionary allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithCapacity: 0];
|
|
|
|
pld->pos++;
|
|
|
|
while (skipSpace(pld) == YES && pld->ptr[pld->pos] != '}')
|
|
|
|
{
|
|
|
|
id key;
|
|
|
|
id val;
|
|
|
|
|
|
|
|
key = parsePlItem(pld);
|
|
|
|
if (key == nil)
|
|
|
|
return nil;
|
|
|
|
if (skipSpace(pld) == NO)
|
|
|
|
{
|
|
|
|
RELEASE(key);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (pld->ptr[pld->pos] != '=')
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected character (wanted '=')";
|
|
|
|
RELEASE(key);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
pld->pos++;
|
|
|
|
val = parsePlItem(pld);
|
|
|
|
if (val == nil)
|
|
|
|
{
|
|
|
|
RELEASE(key);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (skipSpace(pld) == NO)
|
|
|
|
{
|
|
|
|
RELEASE(key);
|
|
|
|
RELEASE(val);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (pld->ptr[pld->pos] == ';')
|
|
|
|
{
|
|
|
|
pld->pos++;
|
|
|
|
}
|
|
|
|
else if (pld->ptr[pld->pos] != '}')
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected character (wanted ';' or '}')";
|
|
|
|
RELEASE(key);
|
|
|
|
RELEASE(val);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
(*plSet)(dict, @selector(setObject:forKey:), val, key);
|
|
|
|
RELEASE(key);
|
|
|
|
RELEASE(val);
|
|
|
|
}
|
|
|
|
if (pld->pos >= pld->end)
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected end of string when parsing dictionary";
|
|
|
|
RELEASE(dict);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
pld->pos++;
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '(':
|
|
|
|
{
|
|
|
|
NSMutableArray *array;
|
|
|
|
|
|
|
|
array = [[plArray allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithCapacity: 0];
|
|
|
|
pld->pos++;
|
|
|
|
while (skipSpace(pld) == YES && pld->ptr[pld->pos] != ')')
|
|
|
|
{
|
|
|
|
id val;
|
|
|
|
|
|
|
|
val = parsePlItem(pld);
|
|
|
|
if (val == nil)
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (skipSpace(pld) == NO)
|
|
|
|
{
|
|
|
|
RELEASE(val);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (pld->ptr[pld->pos] == ',')
|
|
|
|
{
|
|
|
|
pld->pos++;
|
|
|
|
}
|
|
|
|
else if (pld->ptr[pld->pos] != ')')
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected character (wanted ',' or ')')";
|
|
|
|
RELEASE(val);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
(*plAdd)(array, @selector(addObject:), val);
|
|
|
|
RELEASE(val);
|
|
|
|
}
|
|
|
|
if (pld->pos >= pld->end)
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected end of string when parsing array";
|
|
|
|
RELEASE(array);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
pld->pos++;
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
{
|
|
|
|
NSMutableData *data;
|
|
|
|
unsigned max = pld->end - 1;
|
|
|
|
unsigned char buf[BUFSIZ];
|
|
|
|
unsigned len = 0;
|
|
|
|
|
|
|
|
data = [[NSMutableData alloc] initWithCapacity: 0];
|
|
|
|
pld->pos++;
|
2000-11-01 12:31:21 +00:00
|
|
|
skipSpace(pld);
|
|
|
|
while (pld->pos < max
|
|
|
|
&& (*hexdigitsImp)(hexdigits, cMemberSel, pld->ptr[pld->pos])
|
|
|
|
&& (*hexdigitsImp)(hexdigits, cMemberSel, pld->ptr[pld->pos+1]))
|
2000-10-23 11:44:34 +00:00
|
|
|
{
|
2000-11-01 12:31:21 +00:00
|
|
|
unsigned char byte;
|
2000-10-23 11:44:34 +00:00
|
|
|
|
2000-11-01 12:31:21 +00:00
|
|
|
byte = (char2num(pld->ptr[pld->pos])) << 4;
|
|
|
|
pld->pos++;
|
|
|
|
byte |= char2num(pld->ptr[pld->pos]);
|
|
|
|
pld->pos++;
|
|
|
|
buf[len++] = byte;
|
|
|
|
if (len == sizeof(buf))
|
|
|
|
{
|
|
|
|
[data appendBytes: buf length: len];
|
|
|
|
len = 0;
|
2000-10-23 11:44:34 +00:00
|
|
|
}
|
2000-11-01 12:31:21 +00:00
|
|
|
skipSpace(pld);
|
2000-10-23 11:44:34 +00:00
|
|
|
}
|
|
|
|
if (pld->pos >= pld->end)
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected end of string when parsing data";
|
|
|
|
RELEASE(data);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (pld->ptr[pld->pos] != '>')
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected character in string";
|
|
|
|
RELEASE(data);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
[data appendBytes: buf length: len];
|
|
|
|
}
|
|
|
|
pld->pos++;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '"':
|
|
|
|
return parseQuotedString(pld);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return parseUnquotedString(pld);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_LIBXML
|
|
|
|
static GSXMLNode*
|
|
|
|
elementNode(GSXMLNode* node)
|
|
|
|
{
|
|
|
|
while (node != nil)
|
|
|
|
{
|
|
|
|
if ([node type] == XML_ELEMENT_NODE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = [node next];
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
static id
|
|
|
|
nodeToObject(GSXMLNode* node)
|
|
|
|
{
|
|
|
|
NSString *name;
|
|
|
|
NSString *content;
|
|
|
|
GSXMLNode *children;
|
|
|
|
|
|
|
|
node = elementNode(node);
|
|
|
|
if (node == nil)
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
name = [node name];
|
|
|
|
children = [node children];
|
|
|
|
content = [children content];
|
|
|
|
children = elementNode(children);
|
|
|
|
|
|
|
|
if ([name isEqualToString: @"string"])
|
|
|
|
{
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"key"])
|
|
|
|
{
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"true"])
|
|
|
|
{
|
|
|
|
return [NSNumber numberWithBool: YES];
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"false"])
|
|
|
|
{
|
|
|
|
return [NSNumber numberWithBool: NO];
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"integer"])
|
|
|
|
{
|
|
|
|
return [NSNumber numberWithInt: [content intValue]];
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"real"])
|
|
|
|
{
|
|
|
|
return [NSNumber numberWithDouble: [content doubleValue]];
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"date"])
|
|
|
|
{
|
|
|
|
return [NSCalendarDate dateWithString: content
|
|
|
|
calendarFormat: @"%Y-%m-%d %H:%M:%S %z"];
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"data"])
|
|
|
|
{
|
|
|
|
return decodeBase64([content cString]);
|
|
|
|
}
|
|
|
|
// container class
|
|
|
|
else if ([name isEqualToString: @"array"])
|
|
|
|
{
|
|
|
|
NSMutableArray *container = [NSMutableArray array];
|
|
|
|
|
|
|
|
while (children != nil)
|
|
|
|
{
|
|
|
|
id val;
|
|
|
|
|
|
|
|
val = nodeToObject(children);
|
|
|
|
[container addObject: val];
|
|
|
|
children = elementNode([children next]);
|
|
|
|
}
|
|
|
|
return container;
|
|
|
|
}
|
|
|
|
else if ([name isEqualToString: @"dict"])
|
|
|
|
{
|
|
|
|
NSMutableDictionary *container = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
while (children != nil)
|
|
|
|
{
|
|
|
|
NSString *key;
|
|
|
|
id val;
|
|
|
|
|
|
|
|
key = nodeToObject(children);
|
|
|
|
children = elementNode([children next]);
|
|
|
|
val = nodeToObject(children);
|
|
|
|
children = elementNode([children next]);
|
|
|
|
[container setObject: val forKey: key];
|
|
|
|
}
|
|
|
|
return container;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
setupPl()
|
|
|
|
{
|
|
|
|
plAlloc = (id (*)(id, SEL, NSZone*))
|
|
|
|
[NSStringClass methodForSelector: @selector(allocWithZone:)];
|
|
|
|
plInit = (id (*)(id, SEL, unichar*, unsigned))
|
|
|
|
[NSStringClass instanceMethodForSelector: plSel];
|
|
|
|
|
|
|
|
plArray = [NSGMutableArray class];
|
|
|
|
plAdd = (id (*)(id, SEL, id))
|
|
|
|
[plArray instanceMethodForSelector: @selector(addObject:)];
|
|
|
|
|
|
|
|
plDictionary = [NSGMutableDictionary class];
|
|
|
|
plSet = (id (*)(id, SEL, id, id))
|
|
|
|
[plDictionary instanceMethodForSelector: @selector(setObject:forKey:)];
|
|
|
|
|
|
|
|
setupHexdigits();
|
|
|
|
setupQuotables();
|
|
|
|
setupWhitespce();
|
|
|
|
}
|
|
|
|
|
|
|
|
static id
|
|
|
|
GSPropertyList(NSString *string)
|
|
|
|
{
|
|
|
|
pldata _pld;
|
|
|
|
pldata *pld = &_pld;
|
|
|
|
unsigned length = [string length];
|
|
|
|
NSData *d;
|
|
|
|
#if HAVE_LIBXML
|
|
|
|
unsigned index = 0;
|
|
|
|
|
|
|
|
if (whitespce == nil)
|
|
|
|
setupWhitespce();
|
|
|
|
while (index < length)
|
|
|
|
{
|
|
|
|
unsigned c = [string characterAtIndex: index];
|
|
|
|
|
2000-11-01 12:31:21 +00:00
|
|
|
if ((*whitespceImp)(whitespce, cMemberSel, c) == NO)
|
2000-10-23 11:44:34 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* A string beginning with a '<?' must be an XML file
|
|
|
|
*/
|
|
|
|
if (index + 1 < length && [string characterAtIndex: index] == '<'
|
|
|
|
&& [string characterAtIndex: index+1] == '?')
|
|
|
|
{
|
|
|
|
NSData *data;
|
|
|
|
GSXMLParser *parser;
|
|
|
|
|
|
|
|
data = [string dataUsingEncoding: NSUTF8StringEncoding];
|
2000-11-01 12:31:21 +00:00
|
|
|
parser = [GSXMLParser parser];
|
|
|
|
[parser substituteEntities: YES];
|
|
|
|
[parser doValidityChecking: YES];
|
2000-10-23 11:44:34 +00:00
|
|
|
if ([parser parse: data] == NO || [parser parse: nil] == NO)
|
|
|
|
{
|
|
|
|
NSLog(@"not a property list - failed to parse as XML");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (![[[[parser doc] root] name] isEqualToString: @"plist"])
|
|
|
|
{
|
|
|
|
NSLog(@"not a property list - because name node is %@",
|
|
|
|
[[[parser doc] root] name]);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
return RETAIN(nodeToObject([[[parser doc] root] children]));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
d = [string dataUsingEncoding: NSUnicodeStringEncoding];
|
|
|
|
_pld.ptr = (unichar*)[d bytes];
|
|
|
|
_pld.pos = 1;
|
|
|
|
_pld.end = length + 1;
|
|
|
|
_pld.err = nil;
|
|
|
|
_pld.lin = 1;
|
|
|
|
if (plAlloc == 0)
|
|
|
|
setupPl();
|
|
|
|
return parsePlItem(pld);
|
|
|
|
}
|
|
|
|
|
|
|
|
static id
|
|
|
|
GSPropertyListFromStringsFormat(NSString *string)
|
|
|
|
{
|
|
|
|
NSMutableDictionary *dict;
|
|
|
|
pldata _pld;
|
|
|
|
pldata *pld = &_pld;
|
|
|
|
unsigned length = [string length];
|
|
|
|
NSData *d;
|
|
|
|
|
|
|
|
d = [string dataUsingEncoding: NSUnicodeStringEncoding];
|
|
|
|
_pld.ptr = (unichar*)[d bytes];
|
|
|
|
_pld.pos = 1;
|
|
|
|
_pld.end = length + 1;
|
|
|
|
_pld.err = nil;
|
|
|
|
_pld.lin = 1;
|
|
|
|
if (plAlloc == 0)
|
|
|
|
setupPl();
|
|
|
|
|
|
|
|
dict = [[plDictionary allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithCapacity: 0];
|
|
|
|
while (skipSpace(pld) == YES)
|
|
|
|
{
|
|
|
|
id key;
|
|
|
|
id val;
|
|
|
|
|
|
|
|
if (pld->ptr[pld->pos] == '"')
|
|
|
|
key = parseQuotedString(pld);
|
|
|
|
else
|
|
|
|
key = parseUnquotedString(pld);
|
|
|
|
if (key == nil)
|
|
|
|
return nil;
|
|
|
|
if (skipSpace(pld) == NO)
|
|
|
|
{
|
|
|
|
pld->err = @"incomplete final entry (no semicolon?)";
|
|
|
|
RELEASE(key);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (pld->ptr[pld->pos] == ';')
|
|
|
|
{
|
|
|
|
pld->pos++;
|
|
|
|
(*plSet)(dict, @selector(setObject:forKey:), @"", key);
|
|
|
|
RELEASE(key);
|
|
|
|
}
|
|
|
|
else if (pld->ptr[pld->pos] == '=')
|
|
|
|
{
|
|
|
|
pld->pos++;
|
|
|
|
if (skipSpace(pld) == NO)
|
|
|
|
{
|
|
|
|
RELEASE(key);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (pld->ptr[pld->pos] == '"')
|
|
|
|
val = parseQuotedString(pld);
|
|
|
|
else
|
|
|
|
val = parseUnquotedString(pld);
|
|
|
|
if (val == nil)
|
|
|
|
{
|
|
|
|
RELEASE(key);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (skipSpace(pld) == NO)
|
|
|
|
{
|
|
|
|
pld->err = @"missing final semicolon";
|
|
|
|
RELEASE(key);
|
|
|
|
RELEASE(val);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
(*plSet)(dict, @selector(setObject:forKey:), val, key);
|
|
|
|
RELEASE(key);
|
|
|
|
RELEASE(val);
|
|
|
|
if (pld->ptr[pld->pos] == ';')
|
|
|
|
pld->pos++;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pld->err = @"unexpected character (wanted ';')";
|
|
|
|
RELEASE(dict);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RELEASE(key);
|
|
|
|
RELEASE(dict);
|
|
|
|
pld->err = @"unexpected character (wanted '=' or ';')";
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dict;
|
|
|
|
}
|
1999-02-13 00:50:41 +00:00
|
|
|
#endif /* NO_GNUSTEP */
|