2001-12-17 14:31:42 +00:00
|
|
|
/** Implemenation of NSScanner class
|
1999-09-13 08:52:57 +00:00
|
|
|
Copyright (C) 1996,1999 Free Software Foundation, Inc.
|
1996-09-02 13:16:35 +00:00
|
|
|
|
|
|
|
Author: Eric Norum <eric@skatter.usask.ca>
|
|
|
|
Date: 1996
|
2001-12-17 14:31:42 +00:00
|
|
|
Rewrite/optimisation by: Richard Frith-Macdonald <rfm@gnu.org>
|
|
|
|
Date: 1998
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
This file is part of the GNUstep Objective-C Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; if not, write to the Free
|
2005-05-22 03:32:16 +00:00
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
<title>NSScanner class reference</title>
|
|
|
|
$Date$ $Revision$
|
2005-02-22 11:22:44 +00:00
|
|
|
*/
|
1996-09-02 13:16:35 +00:00
|
|
|
|
2004-08-24 16:18:48 +00:00
|
|
|
/* We need to define _GNU_SOURCE on systems (SuSE) to get LONG_LONG_MAX. */
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#endif
|
|
|
|
|
2003-06-07 01:24:41 +00:00
|
|
|
#include "config.h"
|
2003-07-31 23:49:32 +00:00
|
|
|
#include "GNUstepBase/Unicode.h"
|
2003-06-07 01:24:41 +00:00
|
|
|
#include "Foundation/NSScanner.h"
|
|
|
|
#include "Foundation/NSException.h"
|
|
|
|
#include "Foundation/NSObjCRuntime.h"
|
|
|
|
#include "Foundation/NSUserDefaults.h"
|
2004-02-29 02:57:22 +00:00
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
#include <float.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <ctype.h> /* FIXME: May go away once I figure out Unicode */
|
2002-03-13 09:58:43 +00:00
|
|
|
#include "GSPrivate.h"
|
1996-09-02 13:16:35 +00:00
|
|
|
|
2001-10-26 02:16:26 +00:00
|
|
|
/* BSD and Solaris have this */
|
2001-10-26 17:57:58 +00:00
|
|
|
#if defined(HANDLE_LLONG_MAX) && !defined(HANDLE_LONG_LONG_MAX)
|
2001-10-26 02:16:26 +00:00
|
|
|
#define LONG_LONG_MAX LLONG_MAX
|
|
|
|
#define LONG_LONG_MIN LLONG_MIN
|
|
|
|
#define ULONG_LONG_MAX ULLONG_MAX
|
|
|
|
#endif
|
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
|
2000-10-09 04:41:18 +00:00
|
|
|
@class GSCString;
|
2005-07-08 11:48:37 +00:00
|
|
|
@interface GSCString : NSObject // Help the compiler
|
|
|
|
@end
|
2000-11-03 10:11:56 +00:00
|
|
|
@class GSUnicodeString;
|
2005-07-08 11:48:37 +00:00
|
|
|
@interface GSUnicodeString : NSObject // Help the compiler
|
|
|
|
@end
|
2000-11-03 10:11:56 +00:00
|
|
|
@class GSMutableString;
|
|
|
|
@class GSPlaceholderString;
|
2005-07-08 11:48:37 +00:00
|
|
|
@interface GSPlaceholderString : NSObject // Help the compiler
|
|
|
|
@end
|
2000-11-03 10:11:56 +00:00
|
|
|
|
|
|
|
static Class NSStringClass;
|
|
|
|
static Class GSCStringClass;
|
|
|
|
static Class GSUnicodeStringClass;
|
|
|
|
static Class GSMutableStringClass;
|
|
|
|
static Class GSPlaceholderStringClass;
|
2001-06-06 15:18:28 +00:00
|
|
|
static Class NSConstantStringClass;
|
2001-12-18 16:54:15 +00:00
|
|
|
static id _holder;
|
1999-09-13 05:24:42 +00:00
|
|
|
static NSCharacterSet *defaultSkipSet;
|
2000-10-30 18:00:27 +00:00
|
|
|
static SEL memSel;
|
1999-09-13 05:24:42 +00:00
|
|
|
|
|
|
|
/*
|
2000-10-09 04:41:18 +00:00
|
|
|
* Hack for direct access to internals of an concrete string object.
|
1999-09-13 05:24:42 +00:00
|
|
|
*/
|
|
|
|
typedef struct {
|
2000-10-09 04:41:18 +00:00
|
|
|
@defs(GSString)
|
|
|
|
} *ivars;
|
|
|
|
#define myLength() (((ivars)_string)->_count)
|
|
|
|
#define myUnicode(I) (((ivars)_string)->_contents.u[I])
|
|
|
|
#define myChar(I) chartouni((((ivars)_string)->_contents.c[I]))
|
1999-09-16 07:21:34 +00:00
|
|
|
#define myCharacter(I) (_isUnicode ? myUnicode(I) : myChar(I))
|
1999-09-13 08:52:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan characters to be skipped.
|
|
|
|
* Return YES if there are more characters to be scanned.
|
|
|
|
* Return NO if the end of the string is reached.
|
|
|
|
* For internal use only.
|
|
|
|
*/
|
1999-09-27 19:19:10 +00:00
|
|
|
#define skipToNextField() ({\
|
1999-10-19 10:15:00 +00:00
|
|
|
while (_scanLocation < myLength() && _charactersToBeSkipped != nil \
|
1999-09-27 19:19:10 +00:00
|
|
|
&& (*_skipImp)(_charactersToBeSkipped, memSel, myCharacter(_scanLocation)))\
|
|
|
|
_scanLocation++;\
|
|
|
|
(_scanLocation >= myLength()) ? NO : YES;\
|
1999-09-13 08:52:57 +00:00
|
|
|
})
|
1999-09-13 05:24:42 +00:00
|
|
|
|
2005-07-08 11:48:37 +00:00
|
|
|
/**
|
|
|
|
* <p>
|
|
|
|
* The <code>NSScanner</code> class cluster (currently a single class in
|
|
|
|
* GNUstep) provides a mechanism to parse the contents of a string into
|
|
|
|
* number and string values by making a sequence of scan operations to
|
|
|
|
* step through the string retrieving successive items.
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* You can tell the scanner whether its scanning is supposed to be
|
|
|
|
* case sensitive or not, and you can specify a set of characters
|
|
|
|
* to be skipped before each scanning operation (by default,
|
|
|
|
* whitespace and newlines).
|
|
|
|
* </p>
|
|
|
|
*/
|
|
|
|
@implementation NSScanner
|
|
|
|
|
1999-09-13 05:24:42 +00:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [NSScanner class])
|
|
|
|
{
|
2000-10-30 18:00:27 +00:00
|
|
|
memSel = @selector(characterIsMember:);
|
1999-09-13 05:24:42 +00:00
|
|
|
defaultSkipSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
1999-09-28 19:35:09 +00:00
|
|
|
IF_NO_GC(RETAIN(defaultSkipSet));
|
2000-11-03 10:11:56 +00:00
|
|
|
NSStringClass = [NSString class];
|
|
|
|
GSCStringClass = [GSCString class];
|
|
|
|
GSUnicodeStringClass = [GSUnicodeString class];
|
|
|
|
GSMutableStringClass = [GSMutableString class];
|
|
|
|
GSPlaceholderStringClass = [GSPlaceholderString class];
|
2001-06-06 15:18:28 +00:00
|
|
|
NSConstantStringClass = [NSString constantStringClass];
|
2001-11-30 07:30:37 +00:00
|
|
|
_holder = (id)NSAllocateObject(GSPlaceholderStringClass, 0, 0);
|
1999-09-13 05:24:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-04-22 15:18:44 +00:00
|
|
|
/**
|
2002-08-20 10:22:05 +00:00
|
|
|
* Create and return a scanner that scans aString.<br />
|
|
|
|
* Uses -initWithString: and with no locale set.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
+ (id) scannerWithString: (NSString *)aString
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-13 08:52:57 +00:00
|
|
|
return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithString: aString]);
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Returns an NSScanner instance set up to scan aString
|
|
|
|
* (using -initWithString: and with a locale set the default locale
|
|
|
|
* (using -setLocale:
|
|
|
|
*/
|
1999-09-13 05:24:42 +00:00
|
|
|
+ (id) localizedScannerWithString: (NSString*)aString
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-13 05:24:42 +00:00
|
|
|
NSScanner *scanner = [self scannerWithString: aString];
|
|
|
|
|
1999-09-13 08:52:57 +00:00
|
|
|
if (scanner != nil)
|
|
|
|
{
|
2001-11-10 17:31:39 +00:00
|
|
|
[scanner setLocale: GSUserDefaultsDictionaryRepresentation()];
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
1999-09-13 05:24:42 +00:00
|
|
|
return scanner;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Initialises the scanner to scan aString. The GNUstep
|
|
|
|
* implementation may make an internal copy of the original
|
|
|
|
* string - so it is not safe to assume that if you modify a
|
|
|
|
* mutable string that you initialised a scanner with, the changes
|
|
|
|
* will be visible to the scanner.
|
|
|
|
* <br/>
|
|
|
|
* Returns the scanner object.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (id) initWithString: (NSString *)aString
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
2000-10-09 04:41:18 +00:00
|
|
|
Class c;
|
|
|
|
|
1999-12-14 08:40:41 +00:00
|
|
|
if ((self = [super init]) == nil)
|
|
|
|
return nil;
|
1999-09-13 05:24:42 +00:00
|
|
|
/*
|
2000-10-09 04:41:18 +00:00
|
|
|
* Ensure that we have a known string so we can access its internals directly.
|
1999-09-13 05:24:42 +00:00
|
|
|
*/
|
1999-12-14 07:33:47 +00:00
|
|
|
if (aString == nil)
|
|
|
|
{
|
|
|
|
NSLog(@"Scanner initialised with nil string");
|
1999-12-14 08:40:41 +00:00
|
|
|
aString = @"";
|
1999-12-14 07:33:47 +00:00
|
|
|
}
|
1999-12-14 08:40:41 +00:00
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
c = GSObjCClass(aString);
|
2000-11-03 10:11:56 +00:00
|
|
|
if (GSObjCIsKindOf(c, GSUnicodeStringClass) == YES)
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_isUnicode = YES;
|
|
|
|
_string = RETAIN(aString);
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
2000-11-03 10:11:56 +00:00
|
|
|
else if (GSObjCIsKindOf(c, GSCStringClass) == YES)
|
2000-10-09 04:41:18 +00:00
|
|
|
{
|
|
|
|
_isUnicode = NO;
|
|
|
|
_string = RETAIN(aString);
|
|
|
|
}
|
2000-11-03 10:11:56 +00:00
|
|
|
else if (GSObjCIsKindOf(c, GSMutableStringClass) == YES)
|
2000-10-09 04:41:18 +00:00
|
|
|
{
|
|
|
|
if (((ivars)aString)->_flags.wide == 1)
|
|
|
|
{
|
|
|
|
_isUnicode = YES;
|
2001-11-30 07:30:37 +00:00
|
|
|
_string = [_holder initWithCharacters: ((ivars)aString)->_contents.u
|
2000-10-09 04:41:18 +00:00
|
|
|
length: ((ivars)aString)->_count];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_isUnicode = NO;
|
2005-07-08 11:48:37 +00:00
|
|
|
_string = [_holder initWithCString:
|
|
|
|
(char*)((ivars)aString)->_contents.c
|
|
|
|
length: ((ivars)aString)->_count];
|
2000-10-09 04:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
2001-06-06 15:18:28 +00:00
|
|
|
else if (c == NSConstantStringClass)
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_isUnicode = NO;
|
|
|
|
_string = RETAIN(aString);
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
2000-11-03 10:11:56 +00:00
|
|
|
else if ([aString isKindOfClass: NSStringClass])
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_isUnicode = YES;
|
2001-11-30 07:30:37 +00:00
|
|
|
_string = [_holder initWithString: aString];
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
1999-09-13 05:24:42 +00:00
|
|
|
else
|
|
|
|
{
|
1999-12-14 07:33:47 +00:00
|
|
|
RELEASE(self);
|
|
|
|
NSLog(@"Scanner initialised with something not a string");
|
|
|
|
return nil;
|
1999-09-13 05:24:42 +00:00
|
|
|
}
|
1999-09-13 08:52:57 +00:00
|
|
|
[self setCharactersToBeSkipped: defaultSkipSet];
|
1999-09-16 07:21:34 +00:00
|
|
|
_decimal = '.';
|
1996-09-02 13:16:35 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deallocate a scanner and all its associated storage.
|
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (void) dealloc
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
RELEASE(_string);
|
|
|
|
TEST_RELEASE(_locale);
|
|
|
|
RELEASE(_charactersToBeSkipped);
|
1996-09-02 13:16:35 +00:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Returns YES if no more characters remain to be scanned.<br />
|
|
|
|
* Returns YES if all characters remaining to be scanned
|
|
|
|
* are to be skipped.<br />
|
1996-09-02 13:16:35 +00:00
|
|
|
* Returns NO if there are characters left to scan.
|
|
|
|
*/
|
|
|
|
- (BOOL) isAtEnd
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int save__scanLocation;
|
1999-09-13 08:52:57 +00:00
|
|
|
BOOL ret;
|
1996-09-02 13:16:35 +00:00
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation >= myLength())
|
1996-09-02 13:16:35 +00:00
|
|
|
return YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
save__scanLocation = _scanLocation;
|
1999-09-13 08:52:57 +00:00
|
|
|
ret = !skipToNextField();
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = save__scanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal version of scanInt: method.
|
|
|
|
* Does the actual work for scanInt: except for the initial skip.
|
|
|
|
* For internal use only. This method may move the scan location
|
|
|
|
* even if a valid integer is not scanned.
|
|
|
|
* Based on the strtol code from the GNU C library. A little simpler since
|
|
|
|
* we deal only with base 10.
|
|
|
|
* FIXME: I don't use the decimalDigitCharacterSet here since it
|
|
|
|
* includes many more characters than the ASCII digits. I don't
|
|
|
|
* know how to convert those other characters, so I ignore them
|
|
|
|
* for now. For the same reason, I don't try to support all the
|
|
|
|
* possible Unicode plus and minus characters.
|
|
|
|
*/
|
|
|
|
- (BOOL) _scanInt: (int*)value
|
|
|
|
{
|
|
|
|
unsigned int num = 0;
|
|
|
|
const unsigned int limit = UINT_MAX / 10;
|
|
|
|
BOOL negative = NO;
|
|
|
|
BOOL overflow = NO;
|
|
|
|
BOOL got_digits = NO;
|
|
|
|
|
|
|
|
/* Check for sign */
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation < myLength())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
switch (myCharacter(_scanLocation))
|
1999-09-13 05:24:42 +00:00
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
case '+':
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
break;
|
2005-02-22 11:22:44 +00:00
|
|
|
case '-':
|
1999-09-13 05:24:42 +00:00
|
|
|
negative = YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
break;
|
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Process digits */
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unichar digit = myCharacter(_scanLocation);
|
1999-09-13 08:52:57 +00:00
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
if ((digit < '0') || (digit > '9'))
|
|
|
|
break;
|
1999-09-13 08:52:57 +00:00
|
|
|
if (!overflow)
|
|
|
|
{
|
|
|
|
if (num >= limit)
|
|
|
|
overflow = YES;
|
|
|
|
else
|
|
|
|
num = num * 10 + (digit - '0');
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1996-09-02 13:16:35 +00:00
|
|
|
got_digits = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save result */
|
|
|
|
if (!got_digits)
|
|
|
|
return NO;
|
|
|
|
if (value)
|
|
|
|
{
|
1999-09-13 05:24:42 +00:00
|
|
|
if (overflow
|
|
|
|
|| (num > (negative ? (unsigned int)INT_MIN : (unsigned int)INT_MAX)))
|
1996-09-17 15:02:31 +00:00
|
|
|
*value = negative ? INT_MIN: INT_MAX;
|
|
|
|
else if (negative)
|
1996-09-02 13:16:35 +00:00
|
|
|
*value = -num;
|
|
|
|
else
|
|
|
|
*value = num;
|
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans a integer value,
|
|
|
|
* placing it in <em>intValue</em> if that is not null.
|
|
|
|
* <br/>
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
|
|
|
* <br/>
|
|
|
|
* On overflow, INT_MAX or INT_MIN is put into <em>intValue</em>
|
|
|
|
* <br/>
|
|
|
|
* Scans past any excess digits
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
|
|
|
- (BOOL) scanInt: (int*)value
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
|
1999-09-13 08:52:57 +00:00
|
|
|
if (skipToNextField() && [self _scanInt: value])
|
1996-09-02 13:16:35 +00:00
|
|
|
return YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
1996-09-17 15:02:31 +00:00
|
|
|
/*
|
|
|
|
* Scan an unsigned int of the given radix into value.
|
1999-07-03 19:59:44 +00:00
|
|
|
* Internal version used by scanRadixUnsignedInt: and scanHexInt: .
|
1996-09-17 15:02:31 +00:00
|
|
|
*/
|
1999-09-13 05:24:42 +00:00
|
|
|
- (BOOL) scanUnsignedInt_: (unsigned int *)value
|
2003-01-03 20:14:47 +00:00
|
|
|
radix: (unsigned int)radix
|
1999-09-13 05:24:42 +00:00
|
|
|
gotDigits: (BOOL)gotDigits
|
1996-09-17 15:02:31 +00:00
|
|
|
{
|
1999-09-13 08:52:57 +00:00
|
|
|
unsigned int num = 0;
|
|
|
|
unsigned int numLimit, digitLimit, digitValue;
|
|
|
|
BOOL overflow = NO;
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1996-09-17 15:02:31 +00:00
|
|
|
|
1999-02-01 20:58:02 +00:00
|
|
|
/* Set limits */
|
1996-09-17 15:02:31 +00:00
|
|
|
numLimit = UINT_MAX / radix;
|
|
|
|
digitLimit = UINT_MAX % radix;
|
|
|
|
|
|
|
|
/* Process digits */
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1996-09-17 15:02:31 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unichar digit = myCharacter(_scanLocation);
|
1999-09-13 08:52:57 +00:00
|
|
|
|
1996-09-17 15:02:31 +00:00
|
|
|
switch (digit)
|
|
|
|
{
|
1999-09-13 05:24:42 +00:00
|
|
|
case '0': digitValue = 0; break;
|
|
|
|
case '1': digitValue = 1; break;
|
|
|
|
case '2': digitValue = 2; break;
|
|
|
|
case '3': digitValue = 3; break;
|
|
|
|
case '4': digitValue = 4; break;
|
|
|
|
case '5': digitValue = 5; break;
|
|
|
|
case '6': digitValue = 6; break;
|
|
|
|
case '7': digitValue = 7; break;
|
|
|
|
case '8': digitValue = 8; break;
|
|
|
|
case '9': digitValue = 9; break;
|
|
|
|
case 'a': digitValue = 0xA; break;
|
|
|
|
case 'b': digitValue = 0xB; break;
|
|
|
|
case 'c': digitValue = 0xC; break;
|
|
|
|
case 'd': digitValue = 0xD; break;
|
|
|
|
case 'e': digitValue = 0xE; break;
|
|
|
|
case 'f': digitValue = 0xF; break;
|
|
|
|
case 'A': digitValue = 0xA; break;
|
|
|
|
case 'B': digitValue = 0xB; break;
|
|
|
|
case 'C': digitValue = 0xC; break;
|
|
|
|
case 'D': digitValue = 0xD; break;
|
|
|
|
case 'E': digitValue = 0xE; break;
|
|
|
|
case 'F': digitValue = 0xF; break;
|
2005-02-22 11:22:44 +00:00
|
|
|
default:
|
1999-09-13 05:24:42 +00:00
|
|
|
digitValue = radix;
|
|
|
|
break;
|
1996-09-17 15:02:31 +00:00
|
|
|
}
|
|
|
|
if (digitValue >= radix)
|
|
|
|
break;
|
|
|
|
if (!overflow)
|
|
|
|
{
|
1999-09-13 05:24:42 +00:00
|
|
|
if ((num > numLimit)
|
|
|
|
|| ((num == numLimit) && (digitValue > digitLimit)))
|
|
|
|
overflow = YES;
|
|
|
|
else
|
|
|
|
num = num * radix + digitValue;
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-02-01 20:58:02 +00:00
|
|
|
gotDigits = YES;
|
1996-09-17 15:02:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Save result */
|
1999-02-01 20:58:02 +00:00
|
|
|
if (!gotDigits)
|
1996-09-17 15:02:31 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-17 15:02:31 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
if (overflow)
|
|
|
|
*value = UINT_MAX;
|
|
|
|
else
|
|
|
|
*value = num;
|
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans an unsigned
|
|
|
|
* integer value placing it in <em>intValue</em> if that is not null.
|
|
|
|
* If the number begins with "0x" or "0X" it is treated as hexadecimal,
|
|
|
|
* otherwise if the number begins with "0" it is treated as octal,
|
|
|
|
* otherwise the number is treated as decimal.
|
|
|
|
* <br/>
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
|
|
|
* <br/>
|
|
|
|
* On overflow, INT_MAX or INT_MIN is put into <em>intValue</em>
|
|
|
|
* <br/>
|
|
|
|
* Scans past any excess digits
|
1999-02-01 20:58:02 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (BOOL) scanRadixUnsignedInt: (unsigned int *)value
|
1999-02-01 20:58:02 +00:00
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int radix;
|
1999-09-13 08:52:57 +00:00
|
|
|
BOOL gotDigits = NO;
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1999-02-01 20:58:02 +00:00
|
|
|
|
|
|
|
/* Skip whitespace */
|
1999-09-13 08:52:57 +00:00
|
|
|
if (!skipToNextField())
|
1999-02-01 20:58:02 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1999-02-01 20:58:02 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check radix */
|
|
|
|
radix = 10;
|
1999-09-16 07:21:34 +00:00
|
|
|
if ((_scanLocation < myLength()) && (myCharacter(_scanLocation) == '0'))
|
1999-02-01 20:58:02 +00:00
|
|
|
{
|
1999-09-13 05:24:42 +00:00
|
|
|
radix = 8;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
gotDigits = YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation < myLength())
|
1999-02-01 20:58:02 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
switch (myCharacter(_scanLocation))
|
1999-09-13 05:24:42 +00:00
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
case 'x':
|
|
|
|
case 'X':
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
radix = 16;
|
|
|
|
gotDigits = NO;
|
|
|
|
break;
|
|
|
|
}
|
1999-02-01 20:58:02 +00:00
|
|
|
}
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
if ([self scanUnsignedInt_: value radix: radix gotDigits: gotDigits])
|
1999-02-01 20:58:02 +00:00
|
|
|
return YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1999-02-01 20:58:02 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans a hexadecimal
|
|
|
|
* integer value (optionally prefixed by "0x" or "0X"),
|
|
|
|
* placing it in <em>intValue</em> if that is not null.
|
|
|
|
* <br/>
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
|
|
|
* <br/>
|
|
|
|
* On overflow, INT_MAX or INT_MIN is put into <em>intValue</em>
|
|
|
|
* <br/>
|
|
|
|
* Scans past any excess digits
|
1999-02-01 20:58:02 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (BOOL) scanHexInt: (unsigned int *)value
|
1999-02-01 20:58:02 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1999-02-01 20:58:02 +00:00
|
|
|
|
|
|
|
/* Skip whitespace */
|
1999-09-13 08:52:57 +00:00
|
|
|
if (!skipToNextField())
|
1999-02-01 20:58:02 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1999-02-01 20:58:02 +00:00
|
|
|
return NO;
|
|
|
|
}
|
2000-06-21 08:27:30 +00:00
|
|
|
|
|
|
|
if ((_scanLocation < myLength()) && (myCharacter(_scanLocation) == '0'))
|
|
|
|
{
|
|
|
|
_scanLocation++;
|
|
|
|
if (_scanLocation < myLength())
|
|
|
|
{
|
|
|
|
switch (myCharacter(_scanLocation))
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
case 'x':
|
|
|
|
case 'X':
|
2001-06-06 15:36:26 +00:00
|
|
|
_scanLocation++; // Scan beyond the 0x prefix
|
2000-06-21 08:27:30 +00:00
|
|
|
break;
|
|
|
|
default:
|
2001-06-06 15:36:26 +00:00
|
|
|
_scanLocation--; // Scan from the initial digit
|
2000-06-21 08:27:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2001-06-06 15:36:26 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
_scanLocation--; // Just scan the zero.
|
|
|
|
}
|
2000-06-21 08:27:30 +00:00
|
|
|
}
|
1999-07-03 19:59:44 +00:00
|
|
|
if ([self scanUnsignedInt_: value radix: 16 gotDigits: NO])
|
1999-02-01 20:58:02 +00:00
|
|
|
return YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1999-02-01 20:58:02 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans a long
|
|
|
|
* decimal integer value placing it in <em>longLongValue</em> if that
|
|
|
|
* is not null.
|
|
|
|
* <br/>
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
|
|
|
* <br/>
|
|
|
|
* On overflow, LONG_LONG_MAX or LONG_LONG_MIN is put into
|
|
|
|
* <em>longLongValue</em>
|
|
|
|
* <br/>
|
|
|
|
* Scans past any excess digits
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (BOOL) scanLongLong: (long long *)value
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
|
|
|
#if defined(LONG_LONG_MAX)
|
1999-09-13 08:52:57 +00:00
|
|
|
unsigned long long num = 0;
|
|
|
|
const unsigned long long limit = ULONG_LONG_MAX / 10;
|
|
|
|
BOOL negative = NO;
|
|
|
|
BOOL overflow = NO;
|
|
|
|
BOOL got_digits = NO;
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
|
|
|
|
/* Skip whitespace */
|
1999-09-13 08:52:57 +00:00
|
|
|
if (!skipToNextField())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for sign */
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation < myLength())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
switch (myCharacter(_scanLocation))
|
1999-09-13 05:24:42 +00:00
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
case '+':
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
break;
|
2005-02-22 11:22:44 +00:00
|
|
|
case '-':
|
1999-09-13 05:24:42 +00:00
|
|
|
negative = YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
break;
|
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Process digits */
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unichar digit = myCharacter(_scanLocation);
|
1999-09-13 08:52:57 +00:00
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
if ((digit < '0') || (digit > '9'))
|
|
|
|
break;
|
|
|
|
if (!overflow) {
|
|
|
|
if (num >= limit)
|
|
|
|
overflow = YES;
|
|
|
|
else
|
|
|
|
num = num * 10 + (digit - '0');
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1996-09-02 13:16:35 +00:00
|
|
|
got_digits = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save result */
|
|
|
|
if (!got_digits)
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (value)
|
|
|
|
{
|
1999-09-13 08:52:57 +00:00
|
|
|
if (negative)
|
|
|
|
{
|
|
|
|
if (overflow || (num > (unsigned long long)LONG_LONG_MIN))
|
|
|
|
*value = LONG_LONG_MIN;
|
|
|
|
else
|
|
|
|
*value = -num;
|
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
else
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
|
|
|
if (overflow || (num > (unsigned long long)LONG_LONG_MAX))
|
|
|
|
*value = LONG_LONG_MAX;
|
|
|
|
else
|
|
|
|
*value = num;
|
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
#else /* defined(LONG_LONG_MAX) */
|
|
|
|
/*
|
|
|
|
* Provide compile-time warning and run-time exception.
|
|
|
|
*/
|
|
|
|
# warning "Can't use long long variables."
|
|
|
|
[NSException raise: NSGenericException
|
1999-07-03 19:59:44 +00:00
|
|
|
format: @"Can't use long long variables."];
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
#endif /* defined(LONG_LONG_MAX) */
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Not implemented.
|
|
|
|
*/
|
1999-09-13 08:52:57 +00:00
|
|
|
- (BOOL) scanDecimal: (NSDecimal*)value
|
|
|
|
{
|
|
|
|
[self notImplemented:_cmd]; /* FIXME */
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans a double value,
|
|
|
|
* placing it in <em>doubleValue</em> if that is not null.
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
|
|
|
* <br/>
|
2002-11-04 15:39:43 +00:00
|
|
|
* On overflow, HUGE_VAL or - HUGE_VAL is put into <em>doubleValue</em>
|
2002-08-20 10:22:05 +00:00
|
|
|
* <br/>
|
|
|
|
* On underflow, 0.0 is put into <em>doubleValue</em>
|
|
|
|
* <br/>
|
|
|
|
* Scans past any excess digits
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (BOOL) scanDouble: (double *)value
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-13 05:24:42 +00:00
|
|
|
unichar c = 0;
|
|
|
|
double num = 0.0;
|
|
|
|
long int exponent = 0;
|
|
|
|
BOOL negative = NO;
|
|
|
|
BOOL got_dot = NO;
|
|
|
|
BOOL got_digit = NO;
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
|
|
|
|
/* Skip whitespace */
|
1999-09-13 08:52:57 +00:00
|
|
|
if (!skipToNextField())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for sign */
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation < myLength())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
switch (myCharacter(_scanLocation))
|
1999-09-13 05:24:42 +00:00
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
case '+':
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
break;
|
2005-02-22 11:22:44 +00:00
|
|
|
case '-':
|
1999-09-13 05:24:42 +00:00
|
|
|
negative = YES;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
break;
|
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Process number */
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
c = myCharacter(_scanLocation);
|
1996-09-02 13:16:35 +00:00
|
|
|
if ((c >= '0') && (c <= '9'))
|
|
|
|
{
|
|
|
|
/* Ensure that the number being accumulated will not overflow. */
|
|
|
|
if (num >= (DBL_MAX / 10.000000001))
|
|
|
|
{
|
|
|
|
++exponent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
num = (num * 10.0) + (c - '0');
|
|
|
|
got_digit = YES;
|
|
|
|
}
|
|
|
|
/* Keep track of the number of digits after the decimal point.
|
|
|
|
If we just divided by 10 here, we would lose precision. */
|
|
|
|
if (got_dot)
|
|
|
|
--exponent;
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
else if (!got_dot && (c == _decimal))
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
|
|
|
/* Note that we have found the decimal point. */
|
|
|
|
got_dot = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Any other character terminates the number. */
|
|
|
|
break;
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
1999-09-13 05:24:42 +00:00
|
|
|
if (!got_digit)
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1999-09-13 05:24:42 +00:00
|
|
|
return NO;
|
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
|
1999-09-13 05:24:42 +00:00
|
|
|
/* Check for trailing exponent */
|
1999-09-16 07:21:34 +00:00
|
|
|
if ((_scanLocation < myLength()) && ((c == 'e') || (c == 'E')))
|
1999-09-13 05:24:42 +00:00
|
|
|
{
|
|
|
|
int expval;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 05:24:42 +00:00
|
|
|
if ([self _scanInt: &expval])
|
|
|
|
{
|
|
|
|
/* Check for exponent overflow */
|
|
|
|
if (num)
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-13 05:24:42 +00:00
|
|
|
if ((exponent > 0) && (expval > (LONG_MAX - exponent)))
|
|
|
|
exponent = LONG_MAX;
|
|
|
|
else if ((exponent < 0) && (expval < (LONG_MIN - exponent)))
|
|
|
|
exponent = LONG_MIN;
|
|
|
|
else
|
|
|
|
exponent += expval;
|
1999-02-01 20:58:02 +00:00
|
|
|
}
|
1999-09-13 05:24:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-02-01 20:58:02 +00:00
|
|
|
#ifdef _ACCEPT_BAD_EXPONENTS_
|
1999-09-13 05:24:42 +00:00
|
|
|
/* Numbers like 1.23eFOO are accepted (as 1.23). */
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = expScanLocation;
|
1999-02-01 20:58:02 +00:00
|
|
|
#else
|
1999-09-13 05:24:42 +00:00
|
|
|
/* Numbers like 1.23eFOO are rejected. */
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1999-09-13 05:24:42 +00:00
|
|
|
return NO;
|
1999-02-01 20:58:02 +00:00
|
|
|
#endif
|
1999-09-13 05:24:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
if (num && exponent)
|
|
|
|
num *= pow(10.0, (double) exponent);
|
|
|
|
if (negative)
|
|
|
|
*value = -num;
|
|
|
|
else
|
|
|
|
*value = num;
|
|
|
|
}
|
|
|
|
return YES;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans a float value,
|
|
|
|
* placing it in <em>floatValue</em> if that is not null.
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
|
|
|
* <br/>
|
2002-11-04 15:39:43 +00:00
|
|
|
* On overflow, HUGE_VAL or - HUGE_VAL is put into <em>floatValue</em>
|
2002-08-20 10:22:05 +00:00
|
|
|
* <br/>
|
|
|
|
* On underflow, 0.0 is put into <em>floatValue</em>
|
|
|
|
* <br/>
|
|
|
|
* Scans past any excess digits
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (BOOL) scanFloat: (float*)value
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
|
|
|
double num;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
if (value == NULL)
|
1999-07-03 19:59:44 +00:00
|
|
|
return [self scanDouble: NULL];
|
|
|
|
if ([self scanDouble: &num])
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
|
|
|
*value = num;
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans any characters
|
|
|
|
* from aSet, terminating when a character not in the set
|
|
|
|
* is found.<br />
|
|
|
|
* Returns YES if any character is scanned, NO otherwise.<br />
|
|
|
|
* If value is not null, any character scanned are
|
|
|
|
* stored in a string returned in this location.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
- (BOOL) scanCharactersFromSet: (NSCharacterSet *)aSet
|
2002-02-03 17:21:20 +00:00
|
|
|
intoString: (NSString **)value
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
|
1999-09-13 08:52:57 +00:00
|
|
|
if (skipToNextField())
|
|
|
|
{
|
|
|
|
unsigned int start;
|
|
|
|
BOOL (*memImp)(NSCharacterSet*, SEL, unichar);
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
if (aSet == _charactersToBeSkipped)
|
|
|
|
memImp = _skipImp;
|
1999-09-13 08:52:57 +00:00
|
|
|
else
|
|
|
|
memImp = (BOOL (*)(NSCharacterSet*, SEL, unichar))
|
|
|
|
[aSet methodForSelector: memSel];
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
start = _scanLocation;
|
|
|
|
if (_isUnicode)
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if ((*memImp)(aSet, memSel, myUnicode(_scanLocation)) == NO)
|
1999-09-13 08:52:57 +00:00
|
|
|
break;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if ((*memImp)(aSet, memSel, myChar(_scanLocation)) == NO)
|
1999-09-13 08:52:57 +00:00
|
|
|
break;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation != start)
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
|
|
|
if (value != 0)
|
|
|
|
{
|
|
|
|
NSRange range;
|
|
|
|
|
|
|
|
range.location = start;
|
1999-09-16 07:21:34 +00:00
|
|
|
range.length = _scanLocation - start;
|
2000-10-24 11:58:25 +00:00
|
|
|
*value = [_string substringWithRange: range];
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* After initial skipping (if any), this method scans characters until
|
|
|
|
* it finds one in <em>set</em>. The scanned characters are placed in
|
|
|
|
* <em>stringValue</em> if that is not null.
|
|
|
|
* <br/>
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
- (BOOL) scanUpToCharactersFromSet: (NSCharacterSet *)aSet
|
2002-02-03 17:21:20 +00:00
|
|
|
intoString: (NSString **)value
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
1999-09-13 05:24:42 +00:00
|
|
|
unsigned int start;
|
|
|
|
BOOL (*memImp)(NSCharacterSet*, SEL, unichar);
|
1996-09-02 13:16:35 +00:00
|
|
|
|
1999-09-13 08:52:57 +00:00
|
|
|
if (!skipToNextField())
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
1999-09-13 08:52:57 +00:00
|
|
|
|
2002-02-03 17:21:20 +00:00
|
|
|
if (aSet == _charactersToBeSkipped)
|
1999-09-16 07:21:34 +00:00
|
|
|
memImp = _skipImp;
|
1999-09-13 08:52:57 +00:00
|
|
|
else
|
|
|
|
memImp = (BOOL (*)(NSCharacterSet*, SEL, unichar))
|
2002-02-03 17:21:20 +00:00
|
|
|
[aSet methodForSelector: memSel];
|
1999-09-13 08:52:57 +00:00
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
start = _scanLocation;
|
|
|
|
if (_isUnicode)
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
2002-02-03 17:21:20 +00:00
|
|
|
if ((*memImp)(aSet, memSel, myUnicode(_scanLocation)) == YES)
|
1999-09-13 08:52:57 +00:00
|
|
|
break;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
while (_scanLocation < myLength())
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
2002-02-03 17:21:20 +00:00
|
|
|
if ((*memImp)(aSet, memSel, myChar(_scanLocation)) == YES)
|
1999-09-13 08:52:57 +00:00
|
|
|
break;
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation++;
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
1999-09-13 08:52:57 +00:00
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation == start)
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (value)
|
|
|
|
{
|
1999-09-13 08:52:57 +00:00
|
|
|
NSRange range;
|
|
|
|
|
1996-09-02 13:16:35 +00:00
|
|
|
range.location = start;
|
1999-09-16 07:21:34 +00:00
|
|
|
range.length = _scanLocation - start;
|
2000-10-24 11:58:25 +00:00
|
|
|
*value = [_string substringWithRange: range];
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2003-04-22 15:03:45 +00:00
|
|
|
* After initial skipping (if any), this method scans for string
|
|
|
|
* and places the characters found in value if that is not null.<br/>
|
2002-08-20 10:22:05 +00:00
|
|
|
* Returns YES if anything is scanned, NO otherwise.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
2002-02-03 17:21:20 +00:00
|
|
|
- (BOOL) scanString: (NSString *)string intoString: (NSString **)value
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-13 08:52:57 +00:00
|
|
|
NSRange range;
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1999-09-13 08:52:57 +00:00
|
|
|
skipToNextField();
|
1999-09-16 07:21:34 +00:00
|
|
|
range.location = _scanLocation;
|
2002-02-03 17:21:20 +00:00
|
|
|
range.length = [string length];
|
1999-09-13 08:52:57 +00:00
|
|
|
if (range.location + range.length > myLength())
|
1998-11-10 10:28:12 +00:00
|
|
|
return NO;
|
2002-02-03 17:21:20 +00:00
|
|
|
range = [_string rangeOfString: string
|
1999-09-16 07:21:34 +00:00
|
|
|
options: _caseSensitive ? 0 : NSCaseInsensitiveSearch
|
1999-09-13 05:24:42 +00:00
|
|
|
range: range];
|
1996-09-02 13:16:35 +00:00
|
|
|
if (range.length == 0)
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (value)
|
2000-10-24 11:58:25 +00:00
|
|
|
*value = [_string substringWithRange: range];
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation += range.length;
|
1996-09-02 13:16:35 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
2003-04-22 15:03:45 +00:00
|
|
|
* <p>After initial skipping (if any), this method scans characters until
|
|
|
|
* it finds string. The scanned characters are placed in
|
|
|
|
* value if that is not null. If string is not found, all the characters
|
|
|
|
* up to the end of the scanned string will be returned.
|
|
|
|
* </p>
|
|
|
|
* Returns YES if anything is scanned, NO otherwise.<br />
|
|
|
|
* <p>NB. If the current scanner location points to a copy of string, or
|
|
|
|
* points to skippable characters immediately before a copy of string
|
|
|
|
* then this method returns NO since it finds no characters to store
|
|
|
|
* in value before it finds string.
|
|
|
|
* </p>
|
2005-11-06 13:53:40 +00:00
|
|
|
* <p>To count the occurrences of string, this should be used in
|
2003-04-22 15:03:45 +00:00
|
|
|
* conjunction with the -scanString:intoString: method.
|
|
|
|
* </p>
|
2003-04-22 15:18:44 +00:00
|
|
|
* <example>
|
2005-02-22 11:22:44 +00:00
|
|
|
* NSString *ch = @"[";
|
|
|
|
* unsigned total = 0;
|
2003-04-22 15:18:44 +00:00
|
|
|
*
|
2005-02-22 11:22:44 +00:00
|
|
|
* [scanner scanUpToString: ch intoString: NULL];
|
2003-04-22 15:18:44 +00:00
|
|
|
* while ([scanner scanString: ch intoString: NULL] == YES)
|
2005-02-22 11:22:44 +00:00
|
|
|
* {
|
2003-04-22 15:18:44 +00:00
|
|
|
* total++;
|
2005-02-22 11:22:44 +00:00
|
|
|
* [scanner scanUpToString: ch intoString: NULL];
|
|
|
|
* }
|
|
|
|
* NSLog(@"total %d", total);
|
2003-04-22 15:18:44 +00:00
|
|
|
* </example>
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
- (BOOL) scanUpToString: (NSString *)string
|
2002-02-03 17:21:20 +00:00
|
|
|
intoString: (NSString **)value
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-13 08:52:57 +00:00
|
|
|
NSRange range;
|
|
|
|
NSRange found;
|
1999-09-16 07:21:34 +00:00
|
|
|
unsigned int saveScanLocation = _scanLocation;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1999-09-13 08:52:57 +00:00
|
|
|
skipToNextField();
|
1999-09-16 07:21:34 +00:00
|
|
|
range.location = _scanLocation;
|
|
|
|
range.length = myLength() - _scanLocation;
|
2002-02-03 17:21:20 +00:00
|
|
|
found = [_string rangeOfString: string
|
1999-09-16 07:21:34 +00:00
|
|
|
options: _caseSensitive ? 0 : NSCaseInsensitiveSearch
|
|
|
|
range: range];
|
1996-09-02 13:16:35 +00:00
|
|
|
if (found.length)
|
1999-09-16 07:21:34 +00:00
|
|
|
range.length = found.location - _scanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
if (range.length == 0)
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation = saveScanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (value)
|
2000-10-24 11:58:25 +00:00
|
|
|
*value = [_string substringWithRange: range];
|
1999-09-16 07:21:34 +00:00
|
|
|
_scanLocation += range.length;
|
1996-09-02 13:16:35 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
1996-09-02 13:16:35 +00:00
|
|
|
* Returns the string being scanned.
|
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (NSString *) string
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _string;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Returns the current position that the scanner has reached in
|
|
|
|
* scanning the string. This is the position at which the next scan
|
|
|
|
* operation will begin.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (unsigned) scanLocation
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _scanLocation;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* This method sets the location in the scanned string at which the
|
|
|
|
* next scan operation begins.
|
|
|
|
* Raises an NSRangeException if index is beyond the end of the
|
|
|
|
* scanned string.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (void) setScanLocation: (unsigned int)anIndex
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_scanLocation <= myLength())
|
|
|
|
_scanLocation = anIndex;
|
1999-09-13 08:52:57 +00:00
|
|
|
else
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
format: @"Attempt to set scan location beyond end of string"];
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* If the scanner is set to be case-sensitive in its scanning of
|
|
|
|
* the string (other than characters to be skipped), this method
|
|
|
|
* returns YES, otherwise it returns NO.
|
|
|
|
* <br/>
|
|
|
|
* The default is for a scanner to <em>not</em> be case sensitive.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (BOOL) caseSensitive
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _caseSensitive;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Sets the case sensitivity of the scanner.
|
|
|
|
* <br/>
|
2002-08-20 15:07:58 +00:00
|
|
|
* Case sensitivity governs matching of characters being scanned,
|
2002-08-20 10:22:05 +00:00
|
|
|
* but does not effect the characters in the set to be skipped.
|
|
|
|
* <br/>
|
|
|
|
* The default is for a scanner to <em>not</em> be case sensitive.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (void) setCaseSensitive: (BOOL)flag
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_caseSensitive = flag;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Returns a set of characters containing those characters that the
|
|
|
|
* scanner ignores when starting any scan operation. Once a character
|
|
|
|
* not in this set has been encountered during an operation, skipping
|
|
|
|
* is finished, and any further characters from this set that are
|
|
|
|
* found are scanned normally.
|
|
|
|
* <br/>
|
|
|
|
* The default for this is the whitespaceAndNewlineCharacterSet.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (NSCharacterSet *) charactersToBeSkipped
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _charactersToBeSkipped;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Sets the set of characters that the scanner will skip over at the
|
|
|
|
* start of each scanning operation to be <em>skipSet</em>.
|
2005-11-06 13:53:40 +00:00
|
|
|
* Skipping is performed by literal character matching - the case
|
2002-08-20 10:22:05 +00:00
|
|
|
* sensitivity of the scanner does not effect it.
|
|
|
|
* If this is set to nil, no skipping is done.
|
|
|
|
* <br/>
|
|
|
|
* The default for this is the whitespaceAndNewlineCharacterSet.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (void) setCharactersToBeSkipped: (NSCharacterSet *)aSet
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGNCOPY(_charactersToBeSkipped, aSet);
|
|
|
|
_skipImp = (BOOL (*)(NSCharacterSet*, SEL, unichar))
|
|
|
|
[_charactersToBeSkipped methodForSelector: memSel];
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* Returns the locale set for the scanner, or nil if no locale has
|
|
|
|
* been set. A scanner uses it's locale to alter the way it handles
|
|
|
|
* scanning - it uses the NSDecimalSeparator value for scanning
|
|
|
|
* numbers.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (NSDictionary *) locale
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _locale;
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
/**
|
|
|
|
* This method sets the locale used by the scanner to <em>aLocale</em>.
|
|
|
|
* The locale may be set to nil.
|
1996-09-02 13:16:35 +00:00
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (void) setLocale: (NSDictionary *)localeDictionary
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_locale, localeDictionary);
|
1999-09-13 08:52:57 +00:00
|
|
|
/*
|
|
|
|
* Get decimal point character from locale if necessary.
|
|
|
|
*/
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_locale == nil)
|
1999-09-13 08:52:57 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_decimal = '.';
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSString *pointString;
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
pointString = [_locale objectForKey: NSDecimalSeparator];
|
1999-09-13 08:52:57 +00:00
|
|
|
if ([pointString length] > 0)
|
1999-09-16 07:21:34 +00:00
|
|
|
_decimal = [pointString characterAtIndex: 0];
|
1999-09-13 08:52:57 +00:00
|
|
|
else
|
1999-09-16 07:21:34 +00:00
|
|
|
_decimal = '.';
|
1999-09-13 08:52:57 +00:00
|
|
|
}
|
1996-09-02 13:16:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NSCopying protocol
|
|
|
|
*/
|
1999-07-03 19:59:44 +00:00
|
|
|
- (id) copyWithZone: (NSZone *)zone
|
1996-09-02 13:16:35 +00:00
|
|
|
{
|
1999-09-13 08:52:57 +00:00
|
|
|
NSScanner *n = [[self class] allocWithZone: zone];
|
1996-09-02 13:16:35 +00:00
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
[n initWithString: _string];
|
|
|
|
[n setCharactersToBeSkipped: _charactersToBeSkipped];
|
|
|
|
[n setLocale: _locale];
|
|
|
|
[n setScanLocation: _scanLocation];
|
|
|
|
[n setCaseSensitive: _caseSensitive];
|
1996-09-02 13:16:35 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
2003-07-07 09:05:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Some utilities
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
GSScanInt(unichar *buf, unsigned length, int *result)
|
|
|
|
{
|
|
|
|
unsigned int num = 0;
|
|
|
|
const unsigned int limit = UINT_MAX / 10;
|
|
|
|
BOOL negative = NO;
|
|
|
|
BOOL overflow = NO;
|
|
|
|
BOOL got_digits = NO;
|
|
|
|
unsigned int pos = 0;
|
|
|
|
|
|
|
|
/* Check for sign */
|
|
|
|
if (pos < length)
|
|
|
|
{
|
|
|
|
switch (buf[pos])
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
case '+':
|
2003-07-07 09:05:53 +00:00
|
|
|
pos++;
|
|
|
|
break;
|
2005-02-22 11:22:44 +00:00
|
|
|
case '-':
|
2003-07-07 09:05:53 +00:00
|
|
|
negative = YES;
|
|
|
|
pos++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process digits */
|
|
|
|
while (pos < length)
|
|
|
|
{
|
|
|
|
unichar digit = buf[pos];
|
|
|
|
|
|
|
|
if ((digit < '0') || (digit > '9'))
|
|
|
|
break;
|
|
|
|
if (!overflow)
|
|
|
|
{
|
|
|
|
if (num >= limit)
|
|
|
|
overflow = YES;
|
|
|
|
else
|
|
|
|
num = num * 10 + (digit - '0');
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
got_digits = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save result */
|
|
|
|
if (!got_digits)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
if (overflow
|
|
|
|
|| (num > (negative ? (unsigned int)INT_MIN : (unsigned int)INT_MAX)))
|
|
|
|
*result = negative ? INT_MIN: INT_MAX;
|
|
|
|
else if (negative)
|
|
|
|
*result = -num;
|
|
|
|
else
|
|
|
|
*result = num;
|
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scan in a double value in the standard locale ('.' as decimal point).<br />
|
|
|
|
* Return YES on success, NO on failure.<br />
|
|
|
|
* The value pointed to by result is unmodified on failure.<br />
|
|
|
|
* No value is returned in result if it is a null pointer.
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
GSScanDouble(unichar *buf, unsigned length, double *result)
|
|
|
|
{
|
|
|
|
unichar c = 0;
|
|
|
|
double num = 0.0;
|
|
|
|
long int exponent = 0;
|
|
|
|
BOOL negative = NO;
|
|
|
|
BOOL got_dot = NO;
|
|
|
|
BOOL got_digit = NO;
|
|
|
|
unsigned pos = 0;
|
|
|
|
|
|
|
|
/* Skip whitespace */
|
|
|
|
while (pos < length && isspace((int)buf[pos]))
|
|
|
|
{
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for sign */
|
|
|
|
if (pos < length)
|
|
|
|
{
|
|
|
|
switch (buf[pos])
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
case '+':
|
2003-07-07 09:05:53 +00:00
|
|
|
pos++;
|
|
|
|
break;
|
2005-02-22 11:22:44 +00:00
|
|
|
case '-':
|
2003-07-07 09:05:53 +00:00
|
|
|
negative = YES;
|
|
|
|
pos++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process number */
|
|
|
|
while (pos < length)
|
|
|
|
{
|
|
|
|
c = buf[pos];
|
|
|
|
if ((c >= '0') && (c <= '9'))
|
|
|
|
{
|
|
|
|
/* Ensure that the number being accumulated will not overflow. */
|
|
|
|
if (num >= (DBL_MAX / 10.000000001))
|
|
|
|
{
|
|
|
|
++exponent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
num = (num * 10.0) + (c - '0');
|
|
|
|
got_digit = YES;
|
|
|
|
}
|
|
|
|
/* Keep track of the number of digits after the decimal point.
|
|
|
|
If we just divided by 10 here, we would lose precision. */
|
|
|
|
if (got_dot)
|
|
|
|
{
|
|
|
|
--exponent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!got_dot && (c == '.'))
|
|
|
|
{
|
|
|
|
/* Note that we have found the decimal point. */
|
|
|
|
got_dot = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Any other character terminates the number. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
if (!got_digit)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for trailing exponent */
|
|
|
|
if ((pos < length) && ((c == 'e') || (c == 'E')))
|
|
|
|
{
|
|
|
|
int expval;
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
if (GSScanInt(&buf[pos], length - pos, &expval) == YES)
|
|
|
|
{
|
|
|
|
/* Check for exponent overflow */
|
|
|
|
if (num)
|
|
|
|
{
|
|
|
|
if ((exponent > 0) && (expval > (LONG_MAX - exponent)))
|
|
|
|
exponent = LONG_MAX;
|
|
|
|
else if ((exponent < 0) && (expval < (LONG_MIN - exponent)))
|
|
|
|
exponent = LONG_MIN;
|
|
|
|
else
|
|
|
|
exponent += expval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
if (num && exponent)
|
|
|
|
num *= pow(10.0, (double) exponent);
|
|
|
|
if (negative)
|
|
|
|
*result = -num;
|
|
|
|
else
|
|
|
|
*result = num;
|
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|