Use POSIX locale functions if setlocale not available
authorKarl Williamson <khw@cpan.org>
Mon, 4 Mar 2019 21:00:41 +0000 (14:00 -0700)
committerKarl Williamson <khw@cpan.org>
Wed, 6 Mar 2019 19:55:44 +0000 (12:55 -0700)
POSIX 2008 added an independent set of locale handling functions beyond
the venerable setlocale.  This commit changes so they can be used rather
than have no locale handling if there is, say, a problem with setlocale.

makedef.pl
perl.h
t/loc_tools.pl

index 10c7d1e..42436c7 100644 (file)
@@ -129,32 +129,43 @@ if ($define{USE_ITHREADS} && $ARGS{PLATFORM} ne 'win32' && $ARGS{PLATFORM} ne 'n
     $define{USE_REENTRANT_API} = 1;
 }
 
-if (    ($define{USE_ITHREADS} || $define{USE_THREAD_SAFE_LOCALE})
-    &&   $define{HAS_SETLOCALE}
-    && ! $define{NO_LOCALE}
-    && ! $define{NO_POSIX_2008_LOCALE})
-{
-    $define{HAS_POSIX_2008_LOCALE} = 1 if $define{HAS_NEWLOCALE}
-                                       && $define{HAS_FREELOCALE}
-                                       && $define{HAS_USELOCALE};
-    my $cctype = $ARGS{CCTYPE} =~ s/MSVC//r;
-    if (    ! $define{NO_THREAD_SAFE_LOCALE}
-        && (  $define{HAS_POSIX_2008_LOCALE}
-            || ($ARGS{PLATFORM} eq 'win32' && (   $cctype !~ /\D/
-                                               && $cctype >= 80))))
+if (! $define{NO_LOCALE}) {
+    if ( ! $define{NO_POSIX_2008_LOCALE}
+        && $define{HAS_NEWLOCALE}
+        && $define{HAS_USELOCALE}
+        && $define{HAS_DUPLOCALE}
+        && $define{HAS_FREELOCALE})
     {
-        $define{USE_THREAD_SAFE_LOCALE} = 1;
-        $define{USE_POSIX_2008_LOCALE} = 1 if $define{HAS_POSIX_2008_LOCALE};
+        $define{HAS_POSIX_2008_LOCALE} = 1;
+        $define{USE_LOCALE} = 1;
     }
-
-    if (   $ARGS{PLATFORM} eq 'win32'
-        && $define{USE_THREAD_SAFE_LOCALE}
-        && $cctype < 140)
-    {
-        $define{TS_W32_BROKEN_LOCALECONV} = 1;
+    elsif ($define{HAS_SETLOCALE}) {
+        $define{USE_LOCALE} = 1;
     }
 }
 
+my $cctype = $ARGS{CCTYPE} =~ s/MSVC//r;
+if (! $define{HAS_SETLOCALE} && $define{HAS_POSIX_2008_LOCALE}) {
+    $define{USE_POSIX_2008_LOCALE} = 1;
+    $define{USE_THREAD_SAFE_LOCALE} = 1;
+}
+elsif (   ($define{USE_ITHREADS} || $define{USE_THREAD_SAFE_LOCALE})
+       && (    $define{HAS_POSIX_2008_LOCALE}
+           || ($ARGS{PLATFORM} eq 'win32' && (   $cctype !~ /\D/
+                                              && $cctype >= 80)))
+       && ! $define{NO_THREAD_SAFE_LOCALE})
+{
+    $define{USE_THREAD_SAFE_LOCALE} = 1 unless $define{USE_THREAD_SAFE_LOCALE};
+    $define{USE_POSIX_2008_LOCALE} = 1 if $define{HAS_POSIX_2008_LOCALE};
+}
+
+if (   $ARGS{PLATFORM} eq 'win32'
+    && $define{USE_THREAD_SAFE_LOCALE}
+    && $cctype < 140)
+{
+    $define{TS_W32_BROKEN_LOCALECONV} = 1;
+}
+
 # perl.h logic duplication ends
 
 print STDERR "Defines: (" . join(' ', sort keys %define) . ")\n"
diff --git a/perl.h b/perl.h
index 098e1d7..6c3002a 100644 (file)
--- a/perl.h
+++ b/perl.h
 #   include <xlocale.h>
 #endif
 
-#if !defined(NO_LOCALE) && defined(HAS_SETLOCALE)
-#   define USE_LOCALE
+/* If not forbidden, we enable locale handling if either 1) the POSIX 2008
+ * functions are available, or 2) just the setlocale() function.  This logic is
+ * repeated in t/loc_tools.pl and makedef.pl;  The three should be kept in
+ * sync. */
+#if   ! defined(NO_LOCALE)
+
+#  if ! defined(NO_POSIX_2008_LOCALE)           \
+   &&   defined(HAS_NEWLOCALE)                  \
+   &&   defined(HAS_USELOCALE)                  \
+   &&   defined(HAS_DUPLOCALE)                  \
+   &&   defined(HAS_FREELOCALE)                 \
+   &&   defined(LC_ALL_MASK)
+
+    /* For simplicity, the code is written to assume that any platform advanced
+     * enough to have the Posix 2008 locale functions has LC_ALL.  The final
+     * test above makes sure that assumption is valid */
+
+#    define HAS_POSIX_2008_LOCALE
+#    define USE_LOCALE
+#  elif defined(HAS_SETLOCALE)
+#    define USE_LOCALE
+#  endif
+#endif
+
+#ifdef USE_LOCALE
 #   define HAS_SKIP_LOCALE_INIT /* Solely for XS code to test for this
                                    #define */
 #   if !defined(NO_LOCALE_COLLATE) && defined(LC_COLLATE) \
 #   if !defined(NO_LOCALE_TELEPHONE) && defined(LC_TELEPHONE)
 #      define USE_LOCALE_TELEPHONE
 #   endif
-#endif /* !NO_LOCALE && HAS_SETLOCALE */
 
 /* XXX The next few defines are unfortunately duplicated in makedef.pl, and
  * changes here MUST also be made there */
 
-#ifdef USE_LOCALE /* These locale things are all subject to change */
-#  if      defined(HAS_NEWLOCALE)               \
-      &&   defined(LC_ALL_MASK)                 \
-      &&   defined(HAS_FREELOCALE)              \
-      &&   defined(HAS_USELOCALE)               \
-      && ! defined(NO_POSIX_2008_LOCALE)
-
-    /* For simplicity, the code is written to assume that any platform advanced
-     * enough to have the Posix 2008 locale functions has LC_ALL.  The test
-     * above makes sure that assumption is valid */
-
-#    define HAS_POSIX_2008_LOCALE
-#  endif
+#  if ! defined(HAS_SETLOCALE) && defined(HAS_POSIX_2008_LOCALE)
+#      define USE_POSIX_2008_LOCALE
+#      ifndef USE_THREAD_SAFE_LOCALE
+#        define USE_THREAD_SAFE_LOCALE
+#      endif
                                    /* If compiled with
                                     * -DUSE_THREAD_SAFE_LOCALE, will do so even
                                     * on unthreaded builds */
-#  if    (defined(USE_ITHREADS) || defined(USE_THREAD_SAFE_LOCALE))         \
-      && (    defined(HAS_POSIX_2008_LOCALE)                                \
-          || (defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400))     \
-      && ! defined(NO_THREAD_SAFE_LOCALE)
+#  elif   (defined(USE_ITHREADS) || defined(USE_THREAD_SAFE_LOCALE))         \
+       && (    defined(HAS_POSIX_2008_LOCALE)                                \
+           || (defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400))     \
+       && ! defined(NO_THREAD_SAFE_LOCALE)
 #    ifndef USE_THREAD_SAFE_LOCALE
 #      define USE_THREAD_SAFE_LOCALE
 #    endif
index 310eb2c..01426c8 100644 (file)
@@ -207,11 +207,16 @@ sub locales_enabled(;$) {
     # denoting a single category (either name or number).  No conversion into
     # a number is done in this case.
 
-    return 0 unless    $Config{d_setlocale}
-                        # I (khw) cargo-culted the '?' in the pattern on the
-                        # next line.
-                    && $Config{ccflags} !~ /\bD?NO_LOCALE\b/
-                    && $has_locale_h;
+    # khw cargo-culted the '?' in the pattern on the next line.
+    return 0 if $Config{ccflags} =~ /\bD?NO_LOCALE\b/;
+
+    if (! $Config{d_setlocale}) {
+        return 0 if $Config{ccflags} =~ /\bD?NO_POSIX_2008_LOCALE\b/;
+        return 0 unless $Config{d_newlocale};
+        return 0 unless $Config{d_uselocale};
+        return 0 unless $Config{d_duplocale};
+        return 0 unless $Config{d_freelocale};
+    }
 
     # Done with the global possibilities.  Now check if any passed in category
     # is disabled.