Don't use locale definitions unless within scope
authorKarl Williamson <public@khwilliamson.com>
Tue, 18 Jun 2013 04:04:47 +0000 (22:04 -0600)
committerKarl Williamson <public@khwilliamson.com>
Tue, 18 Jun 2013 05:25:21 +0000 (23:25 -0600)
Prior to this patch, stringification of an NV used the current locale's
decimal point character, even outside the scope of a 'use locale'.  This
is contrary to the documentation (though one example in perllocale
omitted the 'use locale') and can lead to unexpected results.

There was one test in the core that relied on the old behavior, and
maybe more in CPAN.  This patch is being made early in 5.19 to see what
breaks.  I do believe though that any breakage is trumped by the
principal that locale rules should only be used if locales are
explicitly requested.

lib/version/t/07locale.t
pod/perllocale.pod
sv.c

index 2628c46..784bc11 100644 (file)
@@ -20,6 +20,8 @@ SKIP: {
        # test locale handling
        my $warning;
 
+        use locale;
+
        local $SIG{__WARN__} = sub { $warning = $_[0] };
 
        my $ver = 1.23;  # has to be floating point number
@@ -61,6 +63,7 @@ SKIP: {
        (my $package = basename($filename)) =~ s/\.pm$//;
        print $fh <<"EOF";
 package $package;
+use locale;
 use POSIX qw(locale_h);
 \$^W = 1;
 use version;
index 1b59fa2..3bd74a1 100644 (file)
@@ -709,6 +709,7 @@ same is true for Perl's internal conversions between numeric and
 string formats:
 
         use POSIX qw(strtod setlocale LC_NUMERIC);
+        use locale;
 
        setlocale LC_NUMERIC, "";
 
diff --git a/sv.c b/sv.c
index a8f15ce..05c6536 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -2918,13 +2918,31 @@ Perl_sv_2pv_flags(pTHX_ SV *const sv, STRLEN *const lp, const I32 flags)
            s = SvGROW_mutable(sv, NV_DIG + 20);
            /* some Xenix systems wipe out errno here */
 
-            Gconvert(SvNVX(sv), NV_DIG, 0, s);
 #ifndef USE_LOCALE_NUMERIC
-            /* We don't call SvPOK_on() if there are locales, because it may
-             * come to pass that the locale changes so that the stringification
-             * we just did is no longer correct.  We will have to re-stringify
-             * every time it is needed */
+            Gconvert(SvNVX(sv), NV_DIG, 0, s);
             SvPOK_on(sv);
+#else
+            /* Gconvert always uses the current locale.  That's the right thing
+             * to do if we're supposed to be using locales.  But otherwise, we
+             * want the result to be based on the C locale, so we need to
+             * change to the C locale during the Gconvert and then change back.
+             * But if we're already in the C locale (PL_numeric_standard is
+             * TRUE in that case), no need to do any changing */
+            if (PL_numeric_standard || IN_LOCALE_RUNTIME) {
+                Gconvert(SvNVX(sv), NV_DIG, 0, s);
+            }
+            else {
+                char *loc = savepv(setlocale(LC_NUMERIC, NULL));
+                setlocale(LC_NUMERIC, "C");
+                Gconvert(SvNVX(sv), NV_DIG, 0, s);
+                setlocale(LC_NUMERIC, loc);
+                Safefree(loc);
+            }
+
+            /* We don't call SvPOK_on(), because it may come to pass that the
+             * locale changes so that the stringification we just did is no
+             * longer correct.  We will have to re-stringify every time it is
+             * needed */
 #endif
            RESTORE_ERRNO;
            while (*s) s++;