diff --git a/ChangeLog b/ChangeLog index 00c847aab..0f01dc62c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,10 @@ * Source/Source/GSFFCallInvocation.m: gs_objc_msg_forward() trust the selector we are given. Rather than trying to find a better one. * Source/GSFFIInvocation.m: ditto + * Source/NSScanner.m: Add a couple of internal utility functions. + * Source/NSString.m: Fix floatValue and doubelValue to use non + localised conversions. + * Source/GSString.m: ditto. 2003-07-06 Adam Fedor diff --git a/Source/GSFormat.m b/Source/GSFormat.m index 1d1a4c371..b33d944d6 100644 --- a/Source/GSFormat.m +++ b/Source/GSFormat.m @@ -2002,3 +2002,4 @@ group_number (unichar *w, unichar *rear_ptr, const char *grouping, } return w; } + diff --git a/Source/GSString.m b/Source/GSString.m index ef6e46e07..ed5fe6b60 100644 --- a/Source/GSString.m +++ b/Source/GSString.m @@ -1004,21 +1004,25 @@ dataUsingEncoding_u(ivars self, NSStringEncoding encoding, BOOL flag) } } +extern BOOL GSScanDouble(unichar*, unsigned, double*); + static inline double doubleValue_c(ivars self) { if (self->_count == 0) { - return 0; + return 0.0; } else { - unsigned len = self->_count < 32 ? self->_count : 31; - unsigned char buf[len+1]; + unsigned l = self->_count < 32 ? self->_count : 32; + unichar buf[l]; + unichar *b = buf; + double d = 0.0; - memcpy(buf, self->_contents.c, len); - buf[len] = '\0'; - return atof(buf); + GSToUnicode(&b, &l, self->_contents.c, l, intEnc, 0, 0); + GSScanDouble(b, l, &d); + return d; } } @@ -1027,16 +1031,14 @@ doubleValue_u(ivars self) { if (self->_count == 0) { - return 0; + return 0.0; } else { - unsigned int l = self->_count < 10 ? self->_count : 9; - unsigned char buf[l+1]; - unsigned char *b = buf; + double d = 0.0; - GSFromUnicode(&b, &l, self->_contents.u, l, intEnc, 0, GSUniTerminate); - return atof(buf); + GSScanDouble(self->_contents.u, self->_count, &d); + return d; } } diff --git a/Source/NSNumber.m b/Source/NSNumber.m index 057de811d..d93351728 100644 --- a/Source/NSNumber.m +++ b/Source/NSNumber.m @@ -646,6 +646,10 @@ static Class doubleNumberClass; return NSCopyObject(self, 0, zone); } +/** + * Returns the string representation of this number using a non-localised + * conversion (decimal point is '.' irrespective of the locale). + */ - (NSString*) description { return [self descriptionWithLocale: nil]; diff --git a/Source/NSScanner.m b/Source/NSScanner.m index bf283c450..272b8506e 100644 --- a/Source/NSScanner.m +++ b/Source/NSScanner.m @@ -1113,3 +1113,181 @@ typedef struct { } @end + +/* + * 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]) + { + case '+': + pos++; + break; + case '-': + 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).
+ * Return YES on success, NO on failure.
+ * The value pointed to by result is unmodified on failure.
+ * 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]) + { + case '+': + pos++; + break; + case '-': + 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; +} diff --git a/Source/NSString.m b/Source/NSString.m index c84659de5..52f55bcd9 100644 --- a/Source/NSString.m +++ b/Source/NSString.m @@ -2425,14 +2425,40 @@ handle_printf_atsign (FILE *stream, return [self intValue] != 0 ? YES : NO; } +extern BOOL GSScanDouble(unichar*, unsigned, double*); + +/** + * Returns the strings content as a double. Skips leading whitespace.
+ * Conversion is not localised (ie uses '.' as the decimal separator).
+ * Returns 0.0 on underflow or if the string does not contain a number. + */ - (double) doubleValue { - return atof([self lossyCString]); + unichar buf[32]; + unsigned len = [self length]; + double d = 0.0; + + if (len > 32) len = 32; + [self getCharacters: buf range: NSMakeRange(0, len)]; + GSScanDouble(buf, len, &d); + return d; } +/** + * Returns the strings content as a double. Skips leading whitespace.
+ * Conversion is not localised (ie uses '.' as the decimal separator).
+ * Returns 0.0 on underflow or if the string does not contain a number. + */ - (float) floatValue { - return (float) atof([self lossyCString]); + unichar buf[32]; + unsigned len = [self length]; + double d = 0.0; + + if (len > 32) len = 32; + [self getCharacters: buf range: NSMakeRange(0, len)]; + GSScanDouble(buf, len, &d); + return (float)d; } - (int) intValue