This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Change some "shouldn't happen" failures into panics
[perl5.git] / locale.c
index d4f90e7..583bf12 100644 (file)
--- a/locale.c
+++ b/locale.c
@@ -388,55 +388,28 @@ S_set_numeric_radix(pTHX_ const bool use_locale)
 #if defined(USE_LOCALE_NUMERIC) && (   defined(HAS_LOCALECONV)              \
                                     || defined(HAS_NL_LANGINFO))
 
-    /* We only set up the radix SV if we are to use a locale radix ... */
-    if (use_locale) {
-        const char * radix = my_nl_langinfo(PERL_RADIXCHAR, FALSE);
-                                          /* FALSE => already in dest locale */
-        /* ... and the character being used isn't a dot */
-        if (strNE(radix, ".")) {
-            const U8 * first_variant;
-
-            if (PL_numeric_radix_sv) {
-                sv_setpv(PL_numeric_radix_sv, radix);
-            }
-            else {
-                PL_numeric_radix_sv = newSVpv(radix, 0);
-            }
-
-            /* If there is a byte variant under UTF-8, and if the remainder of
-             * the string starting there is valid UTF-8, and we are in a UTF-8
-             * locale, then mark the radix as being in UTF-8 */
-            if ( !  is_utf8_invariant_string_loc(
-                                            (U8 *) SvPVX(PL_numeric_radix_sv),
-                                            SvCUR(PL_numeric_radix_sv),
-                                            &first_variant)
-                &&  is_utf8_string(first_variant,
-                                   SvCUR(PL_numeric_radix_sv)
-                                 - ((char *) first_variant
-                                 - SvPVX(PL_numeric_radix_sv)))
-                && _is_cur_LC_category_utf8(LC_NUMERIC))
-            {
-                SvUTF8_on(PL_numeric_radix_sv);
-            }
-            goto done;
-        }
+    const char * radix = (use_locale)
+                         ? my_nl_langinfo(PERL_RADIXCHAR, FALSE)
+                                        /* FALSE => already in dest locale */
+                         : ".";
+
+        sv_setpv(PL_numeric_radix_sv, radix);
+
+    /* If this is valid UTF-8 that isn't totally ASCII, and we are in
+        * a UTF-8 locale, then mark the radix as being in UTF-8 */
+    if (is_utf8_non_invariant_string((U8 *) SvPVX(PL_numeric_radix_sv),
+                                            SvCUR(PL_numeric_radix_sv))
+        && _is_cur_LC_category_utf8(LC_NUMERIC))
+    {
+        SvUTF8_on(PL_numeric_radix_sv);
     }
 
-    SvREFCNT_dec(PL_numeric_radix_sv);
-    PL_numeric_radix_sv = NULL;
-
-  done: ;
-
 #  ifdef DEBUGGING
 
     if (DEBUG_L_TEST || debug_initialization) {
         PerlIO_printf(Perl_debug_log, "Locale radix is '%s', ?UTF-8=%d\n",
-                                          (PL_numeric_radix_sv)
-                                           ? SvPVX(PL_numeric_radix_sv)
-                                           : "NULL",
-                                          (PL_numeric_radix_sv)
-                                           ? cBOOL(SvUTF8(PL_numeric_radix_sv))
-                                           : 0);
+                                           SvPVX(PL_numeric_radix_sv),
+                                           cBOOL(SvUTF8(PL_numeric_radix_sv)));
     }
 
 #  endif
@@ -525,6 +498,14 @@ Perl_new_numeric(pTHX_ const char *newnum)
 
     PL_numeric_underlying_is_standard = PL_numeric_standard;
 
+#  if defined(HAS_NEWLOCALE) && ! defined(NO_POSIX_2008_LOCALE)
+
+    PL_underlying_numeric_obj = newlocale(LC_NUMERIC_MASK,
+                                          PL_numeric_name,
+                                          PL_underlying_numeric_obj);
+
+#endif
+
     if (DEBUG_L_TEST || debug_initialization) {
         PerlIO_printf(Perl_debug_log, "Called new_numeric with %s, PL_numeric_name=%s\n", newnum, PL_numeric_name);
     }
@@ -1450,41 +1431,53 @@ S_my_nl_langinfo(const int item, bool toggle)
      * switching back, as some systems destroy the buffer when setlocale() is
      * called */
 
-    LOCALE_LOCK;
+    {
+        DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
 
-    if (toggle) {
-            do_setlocale_c(LC_NUMERIC, PL_numeric_name);
-    }
+        if (toggle) {
+            STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
+        }
 
-    save_to_buffer(nl_langinfo(item), &PL_langinfo_buf, &PL_langinfo_bufsize, 0);
+        LOCALE_LOCK;    /* Prevent interference from another thread executing
+                           this code section (the only call to nl_langinfo in
+                           the core) */
 
-    if (toggle) {
-        do_setlocale_c(LC_NUMERIC, "C");
-    }
+        save_to_buffer(nl_langinfo(item), &PL_langinfo_buf,
+                                          &PL_langinfo_bufsize, 0);
 
-    LOCALE_UNLOCK;
+        LOCALE_UNLOCK;
+
+        if (toggle) {
+            RESTORE_LC_NUMERIC();
+        }
+    }
 
 #  else /* Use nl_langinfo_l(), avoiding both a mutex and changing the locale */
 
     {
-    bool do_free = FALSE;
-    locale_t cur = uselocale((locale_t) 0);
+        bool do_free = FALSE;
+        locale_t cur = uselocale((locale_t) 0);
 
-    if (cur == LC_GLOBAL_LOCALE) {
-        cur = duplocale(LC_GLOBAL_LOCALE);
-        do_free = TRUE;
-    }
+        if (cur == LC_GLOBAL_LOCALE) {
+            cur = duplocale(LC_GLOBAL_LOCALE);
+            do_free = TRUE;
+        }
 
-    if (toggle) {
-        cur = newlocale(LC_NUMERIC_MASK, PL_numeric_name, cur);
-        do_free = TRUE;
-    }
+        if (toggle) {
+            if (PL_underlying_numeric_obj) {
+                cur = PL_underlying_numeric_obj;
+            }
+            else {
+                cur = newlocale(LC_NUMERIC_MASK, PL_numeric_name, cur);
+                do_free = TRUE;
+            }
+        }
 
-    save_to_buffer(nl_langinfo_l(item, cur),
-                   &PL_langinfo_buf, &PL_langinfo_bufsize, 0);
-    if (do_free) {
-        freelocale(cur);
-    }
+        save_to_buffer(nl_langinfo_l(item, cur),
+                       &PL_langinfo_buf, &PL_langinfo_bufsize, 0);
+        if (do_free) {
+            freelocale(cur);
+        }
     }
 
 #  endif
@@ -1507,6 +1500,7 @@ S_my_nl_langinfo(const int item, bool toggle)
 #  ifdef HAS_LOCALECONV
 
         const struct lconv* lc;
+        DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
 
 #  endif
 #  ifdef HAS_STRFTIME
@@ -1546,11 +1540,12 @@ S_my_nl_langinfo(const int item, bool toggle)
 
             case PERL_CRNCYSTR:
 
-                LOCALE_LOCK;
-
                 /* We don't bother with localeconv_l() because any system that
                  * has it is likely to also have nl_langinfo() */
 
+                LOCALE_LOCK;    /* Prevent interference with other threads
+                                   using localeconv() */
+
                 lc = localeconv();
                 if (   ! lc
                     || ! lc->currency_symbol
@@ -1582,12 +1577,13 @@ S_my_nl_langinfo(const int item, bool toggle)
             case PERL_RADIXCHAR:
             case PERL_THOUSEP:
 
-                LOCALE_LOCK;
-
                 if (toggle) {
-                        do_setlocale_c(LC_NUMERIC, PL_numeric_name);
+                    STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
                 }
 
+                LOCALE_LOCK;    /* Prevent interference with other threads
+                                   using localeconv() */
+
                 lc = localeconv();
                 if (! lc) {
                     retval = "";
@@ -1604,12 +1600,12 @@ S_my_nl_langinfo(const int item, bool toggle)
                 save_to_buffer(retval, &PL_langinfo_buf,
                                &PL_langinfo_bufsize, 0);
 
+                LOCALE_UNLOCK;
+
                 if (toggle) {
-                    do_setlocale_c(LC_NUMERIC, "C");
+                    RESTORE_LC_NUMERIC();
                 }
 
-                LOCALE_UNLOCK;
-
                 break;
 
 #  endif
@@ -2044,6 +2040,8 @@ Perl_init_i18nl10n(pTHX_ int printwarn)
     my_strlcpy(PL_locale_utf8ness, C_and_POSIX_utf8ness,
                sizeof(PL_locale_utf8ness));
 
+    PL_numeric_radix_sv = newSVpvs(".");
+
 #  ifdef LOCALE_ENVIRON_REQUIRED
 
     /*
@@ -3100,8 +3098,8 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
     save_input_locale = do_setlocale_r(category, NULL);
     if (! save_input_locale) {
         Perl_croak(aTHX_
-             "panic: %s: %d: Could not find current locale for %s\n",
-                     __FILE__, __LINE__, category_name(category));
+             "panic: %s: %d: Could not find current %s locale, errno=%d\n",
+                     __FILE__, __LINE__, category_name(category), errno);
     }
 
     save_input_locale = stdize_locale(savepv(save_input_locale));
@@ -3171,9 +3169,9 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
             /* Get the current LC_CTYPE locale */
             save_ctype_locale = do_setlocale_c(LC_CTYPE, NULL);
             if (! save_ctype_locale) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                               "Could not find current locale for LC_CTYPE\n"));
-                goto cant_use_nllanginfo;
+                Perl_croak(aTHX_
+                     "panic: %s: %d: Could not find current LC_CTYPE locale,"
+                     " errno=%d\n", __FILE__, __LINE__, errno);
             }
             save_ctype_locale = stdize_locale(savepv(save_ctype_locale));
 
@@ -3186,11 +3184,10 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
                 save_ctype_locale = NULL;
             }
             else if (! do_setlocale_c(LC_CTYPE, save_input_locale)) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                                    "Could not change LC_CTYPE locale to %s\n",
-                                    save_input_locale));
                 Safefree(save_ctype_locale);
-                goto cant_use_nllanginfo;
+                Perl_croak(aTHX_
+                 "panic: %s: %d: Could not change LC_CTYPE locale to %s,"
+                 " errno=%d\n", __FILE__, __LINE__, save_input_locale, errno);
             }
         }
 
@@ -3291,8 +3288,6 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
 
     }
 
-  cant_use_nllanginfo:
-
 #  else   /* nl_langinfo should work if available, so don't bother compiling this
            fallback code.  The final fallback of looking at the name is
            compiled, and will be executed if nl_langinfo fails */
@@ -3319,9 +3314,9 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
 
             save_monetary_locale = do_setlocale_c(LC_MONETARY, NULL);
             if (! save_monetary_locale) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                            "Could not find current locale for LC_MONETARY\n"));
-                goto cant_use_monetary;
+                Perl_croak(aTHX_
+                 "panic: %s: %d: Could not find current LC_MONETARY locale,"
+                 " errno=%d\n", __FILE__, __LINE__, errno);
             }
             save_monetary_locale = stdize_locale(savepv(save_monetary_locale));
 
@@ -3330,11 +3325,10 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
                 save_monetary_locale = NULL;
             }
             else if (! do_setlocale_c(LC_MONETARY, save_input_locale)) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                            "Could not change LC_MONETARY locale to %s\n",
-                                                        save_input_locale));
                 Safefree(save_monetary_locale);
-                goto cant_use_monetary;
+                Perl_croak(aTHX_
+                 "panic: %s: %d: Could not change LC_MONETARY locale to %s,"
+                 " errno=%d\n", __FILE__, __LINE__, save_input_locale, errno);
             }
         }
 
@@ -3369,7 +3363,6 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
             goto finish_and_return;
         }
     }
-  cant_use_monetary:
 
 #      endif /* USE_LOCALE_MONETARY */
 #    endif /* HAS_LOCALECONV */
@@ -3395,9 +3388,9 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
 
             save_time_locale = do_setlocale_c(LC_TIME, NULL);
             if (! save_time_locale) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                            "Could not find current locale for LC_TIME\n"));
-                goto cant_use_time;
+                Perl_croak(aTHX_
+                     "panic: %s: %d: Could not find current LC_TIME locale,"
+                     " errno=%d\n", __FILE__, __LINE__, errno);
             }
             save_time_locale = stdize_locale(savepv(save_time_locale));
 
@@ -3406,11 +3399,10 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
                 save_time_locale = NULL;
             }
             else if (! do_setlocale_c(LC_TIME, save_input_locale)) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                            "Could not change LC_TIME locale to %s\n",
-                                                        save_input_locale));
                 Safefree(save_time_locale);
-                goto cant_use_time;
+                Perl_croak(aTHX_
+                 "panic: %s: %d: Could not change LC_TIME locale to %s,"
+                 " errno=%d\n", __FILE__, __LINE__, save_input_locale, errno);
             }
         }
 
@@ -3464,7 +3456,6 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
         }
         DEBUG_L(PerlIO_printf(Perl_debug_log, "All time-related words for %s contain only ASCII; can't use for determining if UTF-8 locale\n", save_input_locale));
     }
-  cant_use_time:
 
 #    endif
 
@@ -3494,9 +3485,9 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
 
             save_messages_locale = do_setlocale_c(LC_MESSAGES, NULL);
             if (! save_messages_locale) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                            "Could not find current locale for LC_MESSAGES\n"));
-                goto cant_use_messages;
+                Perl_croak(aTHX_
+                     "panic: %s: %d: Could not find current LC_MESSAGES locale,"
+                     " errno=%d\n", __FILE__, __LINE__, errno);
             }
             save_messages_locale = stdize_locale(savepv(save_messages_locale));
 
@@ -3505,11 +3496,10 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
                 save_messages_locale = NULL;
             }
             else if (! do_setlocale_c(LC_MESSAGES, save_input_locale)) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                            "Could not change LC_MESSAGES locale to %s\n",
-                                                        save_input_locale));
                 Safefree(save_messages_locale);
-                goto cant_use_messages;
+                Perl_croak(aTHX_
+                 "panic: %s: %d: Could not change LC_MESSAGES locale to %s,"
+                 " errno=%d\n", __FILE__, __LINE__, save_input_locale, errno);
             }
         }
 
@@ -3551,7 +3541,6 @@ Perl__is_cur_LC_category_utf8(pTHX_ int category)
 
         DEBUG_L(PerlIO_printf(Perl_debug_log, "All error messages for %s contain only ASCII; can't use for determining if UTF-8 locale\n", save_input_locale));
     }
-  cant_use_messages:
 
 #    endif
 #  endif /* the code that is compiled when no nl_langinfo */
@@ -3839,8 +3828,9 @@ Perl_my_strerror(pTHX_ const int errnum)
 
         save_locale = do_setlocale_c(LC_MESSAGES, NULL);
         if (! save_locale) {
-            DEBUG_L(PerlIO_printf(Perl_debug_log,
-                                  "setlocale failed, errno=%d\n", errno));
+            Perl_croak(aTHX_
+                 "panic: %s: %d: Could not find current LC_MESSAGES locale,"
+                 " errno=%d\n", __FILE__, __LINE__, errno);
         }
         else {
             locale_is_C = isNAME_C_OR_POSIX(save_locale);
@@ -3885,8 +3875,9 @@ Perl_my_strerror(pTHX_ const int errnum)
 
         if (save_locale && ! locale_is_C) {
             if (! do_setlocale_c(LC_MESSAGES, save_locale)) {
-                DEBUG_L(PerlIO_printf(Perl_debug_log,
-                      "setlocale restore failed, errno=%d\n", errno));
+                Perl_croak(aTHX_
+                     "panic: %s: %d: setlocale restore failed, errno=%d\n",
+                             __FILE__, __LINE__, errno);
             }
             Safefree(save_locale);
         }