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"