diff options
author | Karl Williamson <khw@cpan.org> | 2014-08-04 16:29:12 -0600 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2014-08-11 08:53:25 -0600 |
commit | f57000bc399f9b433bfb06a4302f4e773f7f50bb (patch) | |
tree | bfda99efa3e70e00d1a7e65e7ee2e2e462bafc2e /vutil.c | |
parent | 0dd816ae89aab3dba2bf1bd90a0398ae682d0b6f (diff) | |
download | perl-f57000bc399f9b433bfb06a4302f4e773f7f50bb.tar.gz |
PATCH: [perl #121930] Bleadperl breaks MDOOTSON/Wx
The root cause of this issue is that XS code or the libraries it calls
is changing the locale behind Perl's back so that the decimal point
character is not a dot. Version number parsing relies on it being a
dot.
This patch fixes the problem by retrieving the current locale just
before version number parsing, and updating Perl's records if the locale
has changed away from what is expected. Given accurate records, the
pre-existing call to the STORE_NUMERIC_LOCAL_SET_STANDARD macro will
do what it's supposed to do, and change the locale so that the dot is
the radix character for the version number parsing.
After the parsing is done, the pre-existing call to the
RESTORE_NUMERIC_LOCAL macro will restore properly, but see below
This patch should be suitable for both 5.20.1 and 5.21 (though the SHA-1
value in the porting/customize.dat will have to be adjusted because the
files aren't otherwise identical). But there is a fundamental
difference between the releases. In 5.20.X, Perl does not attempt to
keep the radix character a dot at almost all times (though it
initializes things so it is a dot, overriding any environmental settings
to the contrary). This leads to known non-regression bugs in 5.20
because very little XS code can cope with a non-dot. To fix this, Perl
has changed the macros in 5.21 so that the result after the
RESTORE_NUMERIC_LOCAL is that the current locale will have a dot. This
will fix those long-standing bugs where XS code expecting a dot fails
should it be mashed up with modules that change it to something else.
But this will break the relatively few modules that want it the other
way. So it has been done early in 5.21 to give things a chance to
settle down.
The extra {} braces around the code that calls the macros is because
STORE_NUMERIC_LOCAL_SET_STANDARD declares a variable, and so must be
within the declarations area of a block for C89 compilers. (I myself
would not write a macro that does this without indicating so in its
name.)
Diffstat (limited to 'vutil.c')
-rw-r--r-- | vutil.c | 32 |
1 files changed, 32 insertions, 0 deletions
@@ -591,6 +591,37 @@ VER_NV: char tbuf[64]; SV *sv = SvNVX(ver) > 10e50 ? newSV(64) : 0; char *buf; +#ifdef USE_LOCALE_NUMERIC + const char * const cur_numeric = setlocale(LC_NUMERIC, NULL); + assert(cur_numeric); + + /* XS code can set the locale without us knowing. To protect the + * version number parsing, which requires the radix character to be a + * dot, update our records as to what the locale is, so that our + * existing macro mechanism can correctly change it to a dot and back + * if necessary. This code is extremely unlikely to be in a loop, so + * the extra work will have a negligible performance impact. See [perl + * #121930]. + * + * If the current locale is a standard one, but we are expecting it to + * be a different, underlying locale, update our records to make the + * underlying locale this (standard) one. If the current locale is not + * a standard one, we should be expecting a non-standard one, the same + * one that we have recorded as the underlying locale. If not, update + * our records. */ + if (strEQ(cur_numeric, "C") || strEQ(cur_numeric, "POSIX")) { + if (! PL_numeric_standard) { + new_numeric(cur_numeric); + } + } + else if (PL_numeric_standard + || ! PL_numeric_name + || strNE(PL_numeric_name, cur_numeric)) + { + new_numeric(cur_numeric); + } +#endif + { /* Braces needed because macro just below declares a variable */ STORE_NUMERIC_LOCAL_SET_STANDARD(); LOCK_NUMERIC_STANDARD(); if (sv) { @@ -604,6 +635,7 @@ VER_NV: } UNLOCK_NUMERIC_STANDARD(); RESTORE_NUMERIC_LOCAL(); + } while (buf[len-1] == '0' && len > 0) len--; if ( buf[len-1] == '.' ) len--; /* eat the trailing decimal */ version = savepvn(buf, len); |