This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
locale.c: Fix bug in getting langinfo(CRNCYSTR)
authorKarl Williamson <khw@cpan.org>
Thu, 28 Dec 2023 19:51:34 +0000 (12:51 -0700)
committerKarl Williamson <khw@cpan.org>
Tue, 2 Jan 2024 00:38:30 +0000 (17:38 -0700)
This does not affect platforms that have libc nl_langinfo().  Thus,
mainly Windows machines are affected.  The code was passing to
sv__setpvf() a pointer to the existing string of the passed in  SV.
This could lead to reallocating the SV with the pointer invalidated
before being used.

Tony Cook spotted the problem, and the fix used here, using sv_insert()
instead.

ext/I18N-Langinfo/t/Langinfo.t
locale.c

index 6db2a7a..ba17eae 100644 (file)
@@ -16,6 +16,11 @@ push @constants, @times;
 
 my %want = (    RADIXCHAR => qr/ ^ \. $ /x,
                 THOUSEP          => qr/ ^$ /x,
+
+                # Can be empty; otherwise first character must be one of
+                # these.  In the C locale, there is nothing after the first
+                # character.
+                CRNCYSTR  => qr/ ^ [+-.]? $ /x,
            );
 
 # Abbreviated and full are swapped in many locales in early netbsd.  Skip
index 9eb4da9..bf381d9 100644 (file)
--- a/locale.c
+++ b/locale.c
@@ -6235,15 +6235,13 @@ S_my_langinfo_i(pTHX_
 
             /* The modification is to prefix the localeconv() return with a
              * single byte, calculated as follows: */
-            char prefix = (LIKELY(SvIV(precedes) != -1))
-                          ? ((precedes != 0) ?  '-' : '+')
-
-                            /* khw couldn't find any documentation that
-                             * CHAR_MAX (which we modify to -1) is the signal,
-                             * but cygwin uses it thusly, and it makes sense
-                             * given that CHAR_MAX indicates the value isn't
-                             * used, so it neither precedes nor succeeds */
-                          : '.';
+            const char * prefix = (LIKELY(SvIV(precedes) != -1))
+                                   ? ((precedes != 0) ?  "-" : "+")
+                                   : ".";
+            /* (khw couldn't find any documentation that the dot is signalled
+             * by CHAR_MAX (which we modify to -1), but cygwin uses it thusly,
+             * and it makes sense given that CHAR_MAX indicates the value isn't
+             * used, so it neither precedes nor succeeds) */
 
             /* Now get CRNCYSTR */
             (void) hv_iterinit(result_hv);
@@ -6251,7 +6249,7 @@ S_my_langinfo_i(pTHX_
             string = hv_iterval(result_hv, entry);
 
             /* And perform the modification */
-            Perl_sv_setpvf(aTHX_ string, "%c%s", prefix, SvPV_nolen(string));
+            sv_insert(string, 0, 0, prefix, 1);
         }
 
         /* Here, 'string' contains the value we want to return */