This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Keep LC_NUMERIC in C locale, except for brief periods
authorKarl Williamson <khw@cpan.org>
Sun, 1 Jun 2014 20:05:48 +0000 (14:05 -0600)
committerKarl Williamson <khw@cpan.org>
Thu, 5 Jun 2014 17:23:00 +0000 (11:23 -0600)
This is for XS modules, so they don't have to worry about the radix
being a non-dot.  When the locale needs to be in the underlying one, the
operation should be wrapped using macros for the purpose.  That API may
change as we gain experience in 5.21, so I'm not including it now.

MANIFEST
ext/XS-APItest/APItest.xs
ext/XS-APItest/t/locale.t [new file with mode: 0644]
locale.c
perl.c
pod/perldelta.pod
pod/perllocale.pod

index 0ecc5db..baf405f 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -3808,6 +3808,7 @@ ext/XS-APItest/t/labelconst.aux   auxiliary file for label test
 ext/XS-APItest/t/labelconst.t  test recursive descent label parsing
 ext/XS-APItest/t/labelconst_utf8.aux   auxiliary file for label test in UTF-8
 ext/XS-APItest/t/lexsub.t      Test XS registration of lexical subs
+ext/XS-APItest/t/locale.t      test locale-related things
 ext/XS-APItest/t/loopblock.t   test recursive descent block parsing
 ext/XS-APItest/t/looprest.t    test recursive descent statement-sequence parsing
 ext/XS-APItest/t/lvalue.t      Test XS lvalue functions
index 31724f9..a692c51 100644 (file)
@@ -4796,3 +4796,18 @@ test_toTITLE_utf8(SV * p)
         RETVAL = av;
     OUTPUT:
         RETVAL
+
+SV *
+test_Gconvert(SV * number, SV * num_digits)
+    PREINIT:
+        char buffer[100];
+        int len;
+    CODE:
+        len = (int) SvIV(num_digits);
+        if (len > 99) croak("Too long a number for test_Gconvert");
+        PERL_UNUSED_RESULT(Gconvert(SvNV(number), len,
+                 0,    /* No trailing zeroes */
+                 buffer));
+        RETVAL = newSVpv(buffer, 0);
+    OUTPUT:
+        RETVAL
diff --git a/ext/XS-APItest/t/locale.t b/ext/XS-APItest/t/locale.t
new file mode 100644 (file)
index 0000000..900fe74
--- /dev/null
@@ -0,0 +1,35 @@
+BEGIN {
+    require '../../t/test.pl';
+    require '../../t/loc_tools.pl'; # to find locales
+}
+
+use XS::APItest;
+
+BEGIN {
+    eval { require POSIX; POSIX->import("locale_h") };
+    if ($@) {
+       skip_all("could not load the POSIX module"); # running minitest?
+    }
+}
+
+my @locales = eval { find_locales( &LC_NUMERIC ) };
+skip_all("no locales available") unless @locales;
+
+my $non_dot_locale;
+for (@locales) {
+    use locale;
+    setlocale(LC_NUMERIC, $_) or next;
+    my $in = 4.2; # avoid any constant folding bugs
+    if (sprintf("%g", $in) ne "4.2") {
+        $non_dot_locale = $_;
+        last;
+    }
+}
+
+skip_all("no non-dot radix locales available") unless $non_dot_locale;
+
+plan tests => 2;
+
+is(test_Gconvert(4.179, 2), "4.2", "Gconvert doesn't recognize underlying locale outside 'use locale'");
+use locale;
+is(test_Gconvert(4.179, 2), "4.2", "Gconvert doesn't recognize underlying locale inside 'use locale'");
index b93a821..efd46fd 100644 (file)
--- a/locale.c
+++ b/locale.c
@@ -180,6 +180,12 @@ Perl_new_numeric(pTHX_ const char *newnum)
     PL_numeric_standard = ((*save_newnum == 'C' && save_newnum[1] == '\0')
                             || strEQ(save_newnum, "POSIX"));
     PL_numeric_local = TRUE;
+
+    /* Keep LC_NUMERIC in the C locale.  This is for XS modules, so they don't
+     * have to worry about the radix being a non-dot.  (Core operations that
+     * need the underlying locale change to it temporarily). */
+    set_numeric_standard();
+
     set_numeric_radix();
 
 #endif /* USE_LOCALE_NUMERIC */
diff --git a/perl.c b/perl.c
index 18bcf8c..24ef155 100644 (file)
--- a/perl.c
+++ b/perl.c
@@ -256,13 +256,6 @@ perl_construct(pTHXx)
 
     init_i18nl10n(1);
 
-    /* Keep LC_NUMERIC in the C locale for backwards compatibility for XS
-     * modules.  (Core operations that need the underlying locale change to it
-     * temporarily).  An explicit call to POSIX::setlocale() still will cause
-     * XS module failures, but this is how it has been for a long time [perl
-     * #121317] */
-    SET_NUMERIC_STANDARD();
-
 #if defined(LOCAL_PATCH_COUNT)
     PL_localpatches = local_patches;   /* For possible -v */
 #endif
index cce9793..21c59b6 100644 (file)
@@ -469,6 +469,21 @@ well.
 
 The deprecated variable C<PL_sv_objcount> has been removed.
 
+=item *
+
+Perl now tries to keep the locale category C<LC_NUMERIC> set to "C"
+except around operations that need it to be set to the program's
+underlying locale.  This protects the many XS modules that cannot cope
+with the decimal radix character not being a dot.  Prior to this
+release, Perl initialized this category to "C", but a call to
+C<POSIX::setlocale()> would change it.  Now such a call will change the
+underlying locale of the C<LC_NUMERIC> category for the program, but the
+locale exposed to XS code will remain "C".  There is an API under
+development for those relatively few modules that need to use the
+underlying locale.  This API will be nailed down during the course of
+developing v5.21.  Send email to L<mailto:perl5-porters@perl.org> for
+guidance.
+
 =back
 
 =head1 Selected Bug Fixes
index 13bfeec..19ec397 100644 (file)
@@ -230,19 +230,17 @@ locale isn't exposed to Perl space.
 
 XS modules for all categories but C<LC_NUMERIC> get the underlying
 locale, and hence any C library functions they call will use that
-underlying locale.  Perl always initializes C<LC_NUMERIC> to C<"C">
+underlying locale.
+
+Perl tries to keep C<LC_NUMERIC> set to C<"C">
 because too many modules are unable to cope with the decimal point in a
 floating point number not being a dot (it's a comma in many locales).
-But note that these modules are vulnerable because C<LC_NUMERIC>
-currently can be changed at any time by a call to the C C<set_locale()>
-by XS code or by something XS code calls, or by C<POSIX::setlocale()> by
-Perl code.  This is true also for the Perl-provided lite wrappers for XS
-modules to use some C library C<printf> functions:
-C<Gconvert>,
-L<my_sprintf|perlapi/my_sprintf>,
-L<my_snprintf|perlapi/my_snprintf>,
-and
-L<my_vsnprintf|perlapi/my_vsnprintf>.
+Macros are provided for XS code to temporarily change to use the
+underlying locale when necessary; however buggy code that fails to
+restore when done can break other XS code (but not Perl code) in this
+regard.  The API for these macros has not yet been nailed down, but will be
+during the course of v5.21.  Send email to
+L<mailto:perl5-porters@perl.org> for guidance.
 
 =back