Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -869,10 +869,13 @@ OBJCFLAGS="$old_OBJCFLAGS" ]) AC_CHECK_FUNCS([sysconf gmtime_r localtime_r nanosleep fcntl]) +AC_CHECK_HEADERS(xlocale.h) +AC_CHECK_FUNCS([strtod_l strtof_l vasprintf_l]) + AC_CHECK_FUNC(pipe, [ AC_DEFINE(OF_HAVE_PIPE, 1, [Whether we have pipe()]) ]) AC_ARG_ENABLE(sockets, Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -19,10 +19,17 @@ #include #include #include #include #include + +#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) +# include +#endif +#ifdef HAVE_XLOCALE_H +# include +#endif #include #import "OFString.h" #import "OFString_UTF8.h" @@ -64,10 +71,14 @@ * However, the MinGW version __strtod seems to be ok. */ #ifdef __MINGW32__ # define strtod __strtod #endif + +#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) +static locale_t cLocale; +#endif @interface OFString () - (size_t)OF_getCString: (char*)cString maxLength: (size_t)maxLength encoding: (of_string_encoding_t)encoding @@ -499,12 +510,19 @@ @end @implementation OFString + (void)initialize { - if (self == [OFString class]) - placeholder.isa = [OFString_placeholder class]; + if (self != [OFString class]) + return; + + placeholder.isa = [OFString_placeholder class]; + +#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) + if ((cLocale = newlocale(LC_ALL_MASK, "C", NULL)) == NULL) + @throw [OFInitializationFailedException exception]; +#endif } + alloc { if (self == [OFString class]) @@ -2334,22 +2352,34 @@ } - (float)floatValue { void *pool = objc_autoreleasePoolPush(); +#ifdef HAVE_STRTOF_L + const char *UTF8String = [self UTF8String]; +#else + /* + * If we have no strtof_l, we have no other choice but to replace "." + * with the locale's decimal point. + */ OFString *decimalPoint = [OFSystemInfo decimalPoint]; const char *UTF8String = [[self stringByReplacingOccurrencesOfString: @"." withString: decimalPoint] UTF8String]; +#endif char *endPointer = NULL; float value; while (*UTF8String == ' ' || *UTF8String == '\t' || *UTF8String == '\n' || *UTF8String == '\r' || *UTF8String == '\f') UTF8String++; +#ifdef HAVE_STRTOF_L + value = strtof_l(UTF8String, &endPointer, cLocale); +#else value = strtof(UTF8String, &endPointer); +#endif /* Check if there are any invalid chars left */ if (endPointer != NULL) for (; *endPointer != '\0'; endPointer++) if (*endPointer != ' ' && *endPointer != '\t' && @@ -2363,22 +2393,34 @@ } - (double)doubleValue { void *pool = objc_autoreleasePoolPush(); +#ifdef HAVE_STRTOD_L + const char *UTF8String = [self UTF8String]; +#else + /* + * If we have no strtod_l, we have no other choice but to replace "." + * with the locale's decimal point. + */ OFString *decimalPoint = [OFSystemInfo decimalPoint]; const char *UTF8String = [[self stringByReplacingOccurrencesOfString: @"." withString: decimalPoint] UTF8String]; +#endif char *endPointer = NULL; double value; while (*UTF8String == ' ' || *UTF8String == '\t' || *UTF8String == '\n' || *UTF8String == '\r' || *UTF8String == '\f') UTF8String++; +#ifdef HAVE_STRTOD_L + value = strtod_l(UTF8String, &endPointer, cLocale); +#else value = strtod(UTF8String, &endPointer); +#endif /* Check if there are any invalid chars left */ if (endPointer != NULL) for (; *endPointer != '\0'; endPointer++) if (*endPointer != ' ' && *endPointer != '\t' && Index: src/of_asprintf.m ================================================================== --- src/of_asprintf.m +++ src/of_asprintf.m @@ -20,15 +20,24 @@ #include #include #include #include #include + +#ifdef HAVE_ASPRINTF_L +# include +#endif +#ifdef HAVE_XLOCALE_H +# include +#endif #include #import "OFString.h" #import "OFSystemInfo.h" + +#import "OFInitializationFailedException.h" #define MAX_SUBFORMAT_LEN 64 #ifndef HAVE_ASPRINTF /* @@ -67,10 +76,21 @@ LENGTH_MODIFIER_T, LENGTH_MODIFIER_CAPITAL_L } lengthModifier; bool useLocale; }; + +#ifdef HAVE_ASPRINTF_L +static locale_t cLocale; + +static void __attribute__((init)) +init(void) +{ + if ((cLocale = newlocale(LC_ALL_MASK, "C", NULL)) == NULL) + @throw [OFInitializationFailedException exception]; +} +#endif #ifndef HAVE_ASPRINTF static int vasprintf(char **string, const char *format, va_list arguments) { @@ -492,10 +512,30 @@ case 'E': case 'g': case 'G': case 'a': case 'A': +#ifdef HAVE_ASPRINTF_L + { + locale_t locale = (ctx->useLocale ? NULL : cLocale); + + switch (ctx->lengthModifier) { + case LENGTH_MODIFIER_NONE: + case LENGTH_MODIFIER_L: + tmpLen = asprintf(&tmp, ctx->subformat, + va_arg(ctx->arguments, double), locale); + break; + case LENGTH_MODIFIER_CAPITAL_L: + tmpLen = asprintf(&tmp, ctx->subformat, + va_arg(ctx->arguments, long double), + locale); + break; + default: + return false; + } + } +#else switch (ctx->lengthModifier) { case LENGTH_MODIFIER_NONE: case LENGTH_MODIFIER_L: tmpLen = asprintf(&tmp, ctx->subformat, va_arg(ctx->arguments, double)); @@ -507,12 +547,13 @@ default: return false; } /* - * Ugly hack to undo locale, as there is nothing such as - * asprintf_l in POSIX. + * If there's no asprintf_l, we have no other choice than to + * use this ugly hack to replace the locale's decimal point + * back to ".". */ if (!ctx->useLocale) { void *pool = objc_autoreleasePoolPush(); char *tmp2; @@ -534,10 +575,11 @@ objc_autoreleasePoolPop(pool); } tmp = tmp2; } +#endif break; case 'c': switch (ctx->lengthModifier) { case LENGTH_MODIFIER_NONE: Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -525,15 +525,31 @@ EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #1", OFInvalidFormatException, [@"0.0a" floatValue]) EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #2", OFInvalidFormatException, [@"0 0" floatValue]) +#ifdef HAVE_STRTOF_L + /* + * Only do this if we have strtof_l, as the locale might allow the + * comma. + */ + EXPECT_EXCEPTION(@"Detect invalid chars in -[floatValue] #3", + OFInvalidFormatException, [@"0,0" floatValue]) +#endif EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #1", - OFInvalidFormatException, [@"0.0a" floatValue]) + OFInvalidFormatException, [@"0.0a" doubleValue]) EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #2", - OFInvalidFormatException, [@"0 0" floatValue]) + OFInvalidFormatException, [@"0 0" doubleValue]) +#ifdef HAVE_STRTOD_L + /* + * Only do this if we have strtod_l, as the locale might allow the + * comma. + */ + EXPECT_EXCEPTION(@"Detect invalid chars in -[doubleValue] #3", + OFInvalidFormatException, [@"0,0" doubleValue]) +#endif EXPECT_EXCEPTION(@"Detect out of range in -[decimalValue]", OFOutOfRangeException, [@"12345678901234567890123456789012345678901234567890" @"12345678901234567890123456789012345678901234567890"