summaryrefslogtreecommitdiff
path: root/vutil.c
diff options
context:
space:
mode:
authorKarl Williamson <khw@cpan.org>2014-08-04 16:29:12 -0600
committerKarl Williamson <khw@cpan.org>2014-08-11 08:53:25 -0600
commitf57000bc399f9b433bfb06a4302f4e773f7f50bb (patch)
treebfda99efa3e70e00d1a7e65e7ee2e2e462bafc2e /vutil.c
parent0dd816ae89aab3dba2bf1bd90a0398ae682d0b6f (diff)
downloadperl-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.c32
1 files changed, 32 insertions, 0 deletions
diff --git a/vutil.c b/vutil.c
index b687103de0..20fb522ee5 100644
--- a/vutil.c
+++ b/vutil.c
@@ -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);