Use new macros to make sure LC_NUMERIC is correctly set
authorKarl Williamson <public@khwilliamson.com>
Tue, 17 Dec 2013 05:34:19 +0000 (22:34 -0700)
committerKarl Williamson <public@khwilliamson.com>
Sat, 4 Jan 2014 20:33:05 +0000 (13:33 -0700)
This uses the macros added in the previous commit to make sure the
current LC_NUMERIC locale is correct during the operation being done;
restoring it to its prior condition afterwards.  Outside of 'use locale'
the locale should be C; inside it should be the underlying default
locale.  The macros handle the whole thing.  In most of the places here,
the code was trying to do what the macros do more elegantly, but there
are some additional places where we set the locale correctly around an
operation that is affected by it.

numeric.c
pp_ctl.c
sv.c

index b5144f4..d8e85d9 100644 (file)
--- a/numeric.c
+++ b/numeric.c
@@ -853,6 +853,8 @@ Perl_my_atof(pTHX_ const char* s)
 
     PERL_ARGS_ASSERT_MY_ATOF;
 
+    {
+        DECLARE_STORE_LC_NUMERIC_SET_TO_NEEDED();
     if (PL_numeric_local && PL_numeric_radix_sv && IN_SOME_LOCALE_FORM) {
         const char *standard = NULL, *local = NULL;
         bool use_standard_radix;
@@ -878,6 +880,8 @@ Perl_my_atof(pTHX_ const char* s)
     }
     else
        Perl_atof2(s, x);
+        RESTORE_LC_NUMERIC();
+    }
 #else
     Perl_atof2(s, x);
 #endif
index 7236921..d47e983 100644 (file)
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -836,13 +836,13 @@ PP(pp_formline)
            }
            /* Formats aren't yet marked for locales, so assume "yes". */
            {
-               STORE_NUMERIC_STANDARD_SET_LOCAL();
+                DECLARE_STORE_LC_NUMERIC_SET_TO_NEEDED();
                arg &= ~(FORM_NUM_POINT|FORM_NUM_BLANK);
                 /* we generate fmt ourselves so it is safe */
                 GCC_DIAG_IGNORE(-Wformat-nonliteral);
                my_snprintf(t, SvLEN(PL_formtarget) - (t - SvPVX(PL_formtarget)), fmt, (int) fieldsize, (int) arg, value);
                 GCC_DIAG_RESTORE;
-               RESTORE_NUMERIC_STANDARD();
+                RESTORE_LC_NUMERIC();
            }
            t += fieldsize;
            break;
diff --git a/sv.c b/sv.c
index 93d6a1a..676340b 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -2957,31 +2957,19 @@ Perl_sv_2pv_flags(pTHX_ SV *const sv, STRLEN *const lp, const I32 flags)
             V_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_SOME_LOCALE_FORM_RUNTIME) {
+            {
+                DECLARE_STORE_LC_NUMERIC_SET_TO_NEEDED();
                 V_Gconvert(SvNVX(sv), NV_DIG, 0, s);
 
                 /* If the radix character is UTF-8, and actually is in the
                  * output, turn on the UTF-8 flag for the scalar */
-                if (! PL_numeric_standard
+                if (PL_numeric_local
                     && PL_numeric_radix_sv && SvUTF8(PL_numeric_radix_sv)
                     && instr(s, SvPVX_const(PL_numeric_radix_sv)))
                 {
                     SvUTF8_on(sv);
                 }
-            }
-            else {
-                char *loc = savepv(setlocale(LC_NUMERIC, NULL));
-                setlocale(LC_NUMERIC, "C");
-                V_Gconvert(SvNVX(sv), NV_DIG, 0, s);
-                setlocale(LC_NUMERIC, loc);
-                Safefree(loc);
-
+                RESTORE_LC_NUMERIC();
             }
 
             /* We don't call SvPOK_on(), because it may come to pass that the
@@ -10423,9 +10411,8 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
     char ebuf[IV_DIG * 4 + NV_DIG + 32];
     /* large enough for "%#.#f" --chip */
     /* what about long double NVs? --jhi */
-#ifdef USE_LOCALE_NUMERIC
-    SV* oldlocale = NULL;
-#endif
+
+    DECLARATION_FOR_STORE_LC_NUMERIC_SET_TO_NEEDED;
 
     PERL_ARGS_ASSERT_SV_VCATPVFN_FLAGS;
     PERL_UNUSED_ARG(maybe_tainted);
@@ -10478,6 +10465,7 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
                   a Configure test for this.  */
                if (digits && digits < sizeof(ebuf) - NV_DIG - 10) {
                     /* 0, point, slack */
+                    STORE_LC_NUMERIC_SET_TO_NEEDED();
                    V_Gconvert(nv, (int)digits, 0, ebuf);
                    sv_catpv_nomg(sv, ebuf);
                    if (*ebuf)  /* May return an empty string for digits==0 */
@@ -11338,6 +11326,7 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
                /* See earlier comment about buggy Gconvert when digits,
                   aka precis is 0  */
                if ( c == 'g' && precis) {
+                    STORE_LC_NUMERIC_SET_TO_NEEDED();
                    V_Gconvert((NV)nv, (int)precis, 0, PL_efloatbuf);
                    /* May return an empty string for digits==0 */
                    if (*PL_efloatbuf) {
@@ -11387,19 +11376,7 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
                 * where printf() taints but print($float) doesn't.
                 * --jhi */
 
-#ifdef USE_LOCALE_NUMERIC
-                if (! PL_numeric_standard && ! IN_SOME_LOCALE_FORM) {
-
-                    /* We use a mortal SV, so that any failures (such as if
-                     * warnings are made fatal) won't leak */
-                    char *oldlocale_string = setlocale(LC_NUMERIC, NULL);
-                    oldlocale = newSVpvn_flags(oldlocale_string,
-                                               strlen(oldlocale_string),
-                                               SVs_TEMP);
-                    PL_numeric_standard = TRUE;
-                    setlocale(LC_NUMERIC, "C");
-                }
-#endif
+                STORE_LC_NUMERIC_SET_TO_NEEDED();
 
                 /* hopefully the above makes ptr a very constrained format
                  * that is safe to use, even though it's not literal */
@@ -11581,13 +11558,8 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
     }
     SvTAINT(sv);
 
-#ifdef USE_LOCALE_NUMERIC   /* Done outside loop, so don't have to save/restore
+    RESTORE_LC_NUMERIC();   /* Done outside loop, so don't have to save/restore
                                each iteration. */
-    if (oldlocale) {
-        setlocale(LC_NUMERIC, SvPVX(oldlocale));
-        PL_numeric_standard = FALSE;
-    }
-#endif
 }
 
 /* =========================================================================