Hi,

the attached patch fixes a few locale related failures in libgfortran,
in the case where the POSIX 2008 extended locale functionality and
extensions strto{f,d,ld}_l are present.

These failures typically occur when libgfortran is used from a program
which has set the locale with setlocale(), and the locale uses a
different decimal separator than the C locale. The patch fixes this by
creating a C locale which is then used by strto{f,d,ld}_l, and also is
installed as the per-thread locale when starting a formatted IO, then
reset to the previous value when the IO is done. I have chosen to not
fallback to calling setlocale() in case the POSIX 2008 locale stuff
isn't available, as that could create nasty hard to debug race
conditions in a multi-threaded program.

(I think Jerry's proposed patch which checks the locale for the
decimal separator is still useful as a fallback in case the POSIX 2008
locale stuff isn't available)

Regtested on x86_64-unknown-linux-gnu, Ok for trunk?

2014-11-06  Janne Blomqvist  <j...@gcc.gnu.org>

    PR libfortran/47007
    PR libfortran/61847
    * config.h.in: Regenerated.
    * configure: Regenerated.
    * configure.ac (AC_CHECK_HEADERS_ONCE): Check for locale.h.
    (AC_CHECK_FUNCS_ONCE): Check for newlocale, freelocale, uselocale,
    strtof_l, strtod_l, strtold_l.
    * io/io.h (locale.h): Include if present.
    (c_locale): New variable.
    (gfc_strtof): Move macro from libgfortran.h, use strtof_l if present.
    (gfc_strtod): Likewise.
    (gfc_strtold): Likewise.
    (st_parameter_dt): Add old_locale member.
    * io/transfer.c (data_transfer_init): Set thread locale to
    c_locale if doing formatted transfer.
    (finalize_transfer): Reset thread locale to previous.
    * io/unit.c (c_locale): New variable.
    (init_units): Init c_locale.
    (close_units): Free c_locale.
    * libgfortran.h (gfc_strto{f,d,ld}): Move macros to io/io.h.


-- 
Janne Blomqvist
diff --git a/libgfortran/configure.ac b/libgfortran/configure.ac
index b3150f4..5550380 100644
--- a/libgfortran/configure.ac
+++ b/libgfortran/configure.ac
@@ -255,7 +255,7 @@ AC_CHECK_TYPES([ptrdiff_t])
 # check header files (we assume C89 is available, so don't check for that)
 AC_CHECK_HEADERS_ONCE(unistd.h sys/time.h sys/times.h sys/resource.h \
 sys/types.h sys/stat.h sys/wait.h floatingpoint.h ieeefp.h fenv.h fptrap.h \
-fpxcp.h pwd.h complex.h) 
+fpxcp.h pwd.h complex.h locale.h) 
 
 GCC_HEADER_STDINT(gstdint.h)
 
@@ -290,7 +290,8 @@ else
    strcasestr getrlimit gettimeofday stat fstat lstat getpwuid vsnprintf dup \
    getcwd localtime_r gmtime_r getpwuid_r ttyname_r clock_gettime \
    readlink getgid getpid getppid getuid geteuid umask getegid \
-   secure_getenv __secure_getenv mkostemp strnlen strndup strtok_r)
+   secure_getenv __secure_getenv mkostemp strnlen strndup strtok_r newlocale \
+   freelocale uselocale strtof_l strtod_l strtold_l)
 fi
 
 # Check strerror_r, cannot be above as versions with two and three arguments 
exist
diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h
index 1e0d092..a638daf 100644
--- a/libgfortran/io/io.h
+++ b/libgfortran/io/io.h
@@ -32,6 +32,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If 
not, see
 
 #include <gthr.h>
 
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
 /* Forward declarations.  */
 struct st_parameter_dt;
 typedef struct stream stream;
@@ -40,6 +44,35 @@ struct format_data;
 typedef struct fnode fnode;
 struct gfc_unit;
 
+#ifdef HAVE_NEWLOCALE
+/* We have POSIX 2008 extended locale stuff.  */
+extern locale_t c_locale;
+#endif
+
+#ifdef __MINGW32__
+extern float __strtof (const char *, char **);
+#define gfc_strtof __strtof
+extern double __strtod (const char *, char **);
+#define gfc_strtod __strtod
+extern long double __strtold (const char *, char **);
+#define gfc_strtold __strtold
+#else
+#ifdef HAVE_STRTOF_L
+#define gfc_strtof(nptr, endptr) strtof_l(nptr, endptr, c_locale)
+#else
+#define gfc_strtof strtof
+#endif
+#ifdef HAVE_STRTOD_L
+#define gfc_strtod(nptr, endptr) strtod_l(nptr, endptr, c_locale)
+#else
+#define gfc_strtod strtod
+#endif
+#ifdef HAVE_STRTOLD_L
+#define gfc_strtold(nptr, endptr) strtold_l(nptr, endptr, c_locale)
+#else
+#define gfc_strtold strtold
+#endif
+#endif
 
 /* Macros for testing what kinds of I/O we are doing.  */
 
@@ -450,6 +483,9 @@ typedef struct st_parameter_dt
          char *line_buffer;
          struct format_data *fmt;
          namelist_info *ionml;
+#ifdef HAVE_NEWLOCALE
+         locale_t old_locale;
+#endif
          /* Current position within the look-ahead line buffer.  */
          int line_buffer_pos;
          /* Storage area for values except for strings.  Must be
diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c
index dc1b6f4..4706865 100644
--- a/libgfortran/io/transfer.c
+++ b/libgfortran/io/transfer.c
@@ -2874,7 +2874,12 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
   if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED
       && ((cf & (IOPARM_DT_LIST_FORMAT | IOPARM_DT_HAS_NAMELIST_NAME)) == 0)
       && dtp->u.p.ionml == NULL)
-    formatted_transfer (dtp, 0, NULL, 0, 0, 1);
+    {
+#ifdef HAVE_USELOCALE
+      dtp->u.p.old_locale = uselocale (c_locale);
+#endif
+      formatted_transfer (dtp, 0, NULL, 0, 0, 1);
+    }
 }
 
 /* Initialize an array_loop_spec given the array descriptor.  The function
@@ -3528,6 +3533,11 @@ finalize_transfer (st_parameter_dt *dtp)
   if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
     *dtp->size = dtp->u.p.size_used;
 
+#ifdef HAVE_USELOCALE
+  if (dtp->u.p.old_locale != (locale_t) 0)
+    uselocale (dtp->u.p.old_locale);
+#endif
+
   if (dtp->u.p.eor_condition)
     {
       generate_error (&dtp->common, LIBERROR_EOR, NULL);
diff --git a/libgfortran/io/unit.c b/libgfortran/io/unit.c
index 2a31e55..e4bc60b 100644
--- a/libgfortran/io/unit.c
+++ b/libgfortran/io/unit.c
@@ -90,6 +90,11 @@ static char stdin_name[] = "stdin";
 static char stdout_name[] = "stdout";
 static char stderr_name[] = "stderr";
 
+
+#ifdef HAVE_NEWLOCALE
+locale_t c_locale;
+#endif
+
 /* This implementation is based on Stefan Nilsson's article in the
  * July 1997 Doctor Dobb's Journal, "Treaps in Java". */
 
@@ -561,6 +566,10 @@ init_units (void)
   gfc_unit *u;
   unsigned int i;
 
+#ifdef HAVE_NEWLOCALE
+  c_locale = newlocale (0, "C", 0);
+#endif
+
 #ifndef __GTHREAD_MUTEX_INIT
   __GTHREAD_MUTEX_INIT_FUNCTION (&unit_lock);
 #endif
@@ -736,6 +745,10 @@ close_units (void)
   while (unit_root != NULL)
     close_unit_1 (unit_root, 1);
   __gthread_mutex_unlock (&unit_lock);
+
+#ifdef HAVE_FREELOCALE
+  freelocale (c_locale);
+#endif
 }
 
 
diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h
index d2de76f..8820edd 100644
--- a/libgfortran/libgfortran.h
+++ b/libgfortran/libgfortran.h
@@ -60,18 +60,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If 
not, see
 #  include "quadmath_weak.h"
 #endif
 
-#ifdef __MINGW32__
-extern float __strtof (const char *, char **);
-#define gfc_strtof __strtof
-extern double __strtod (const char *, char **);
-#define gfc_strtod __strtod
-extern long double __strtold (const char *, char **);
-#define gfc_strtold __strtold
-#else
-#define gfc_strtof strtof
-#define gfc_strtod strtod
-#define gfc_strtold strtold
-#endif
 
 #include "../gcc/fortran/libgfortran.h"
 

Reply via email to