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 <[email protected]>
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"