From patchwork Tue Feb 22 16:17:26 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Janne Blomqvist X-Patchwork-Id: 83982 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 1330AB70CB for ; Wed, 23 Feb 2011 03:17:52 +1100 (EST) Received: (qmail 6159 invoked by alias); 22 Feb 2011 16:17:45 -0000 Received: (qmail 6124 invoked by uid 22791); 22 Feb 2011 16:17:36 -0000 X-SWARE-Spam-Status: No, hits=-2.2 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, TW_CP, TW_PW, TW_TP, TW_WU X-Spam-Check-By: sourceware.org Received: from mail-iy0-f175.google.com (HELO mail-iy0-f175.google.com) (209.85.210.175) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 22 Feb 2011 16:17:28 +0000 Received: by iyb26 with SMTP id 26so1974184iyb.20 for ; Tue, 22 Feb 2011 08:17:26 -0800 (PST) MIME-Version: 1.0 Received: by 10.231.12.68 with SMTP id w4mr2233161ibw.50.1298391446040; Tue, 22 Feb 2011 08:17:26 -0800 (PST) Received: by 10.231.160.16 with HTTP; Tue, 22 Feb 2011 08:17:26 -0800 (PST) Date: Tue, 22 Feb 2011 18:17:26 +0200 Message-ID: Subject: [Patch, libfortran] PR 47802 Implementation of CTIME intrinsic From: Janne Blomqvist To: Fortran List , GCC Patches Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Hi, the implementation of the CTIME and FDATE intrinsics was recently changed to use ctime_r() if available (see PR 47431). However, this caused problems on HP-UX 10.2 which provides a function ctime_r() which has another interface than the POSIX one (PR 47802). Also, the autoconf manual recommends not to use ctime/ctime_r due to many implementations suffering from buffer overflows for bad input (http://www.gnu.org/software/hello/manual/autoconf/Buffer-Overruns.html). For partly the same reasons, POSIX 2008 marks these functions as obsolescent, and recommends strftime() instead. From POSIX 2008: "These functions are included only for compatibility with older implementations. They have undefined behavior if the resulting string would be too long, so the use of these functions should be discouraged. On implementations that do not detect output string length overflow, it is possible to overflow the output buffers in such a way as to cause applications to fail, or possible system security violations. Also, these functions do not support localized date and time formats. To avoid these problems, applications should use strftime() to generate strings from broken-down times." The attached patch changes the implementation of CTIME and FDATE to use strftime instead. A caveat is that as strftime supports i18n, if the program sets the LC_ALL/LC_TIME locale the output will not be the same as with ctime(). However, few if any Fortran programs call setlocale(), and for those that do, a localized time and date string might even be seen as a feature. Regtested on x86_64-unknown-linux-gnu, Ok for trunk? 2011-02-22 Janne Blomqvist PR libfortran/47802 * config.h.in: Regenerated. * configure: Regenerated. * configure.ac: Remove checks for ctime and ctime_r, add check for strftime. * intrinsics/date_and_time.c (localtime_r): Move fallback implementation to time_1.h. * intrinsics/time_1.h (localtime_r): Fallback implementation. * intrinsics/ctime.c: Include time_1.h. (ctime_r): Remove fallback implementation. (fctime): New function. (fdate): Use fctime instead of ctime_r. (fdate_sub): Likewise. (ctime): Likewise. (ctime_sub): Likewise. diff --git a/libgfortran/config.h.in b/libgfortran/config.h.in index fdf502b..7ba68ab 100644 --- a/libgfortran/config.h.in +++ b/libgfortran/config.h.in @@ -318,12 +318,6 @@ /* libm includes ctanl */ #undef HAVE_CTANL -/* Define to 1 if you have the `ctime' function. */ -#undef HAVE_CTIME - -/* Define to 1 if you have the `ctime_r' function. */ -#undef HAVE_CTIME_R - /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H @@ -720,6 +714,9 @@ /* Define to 1 if you have the `strerror_r' function. */ #undef HAVE_STRERROR_R +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H diff --git a/libgfortran/configure b/libgfortran/configure index 9d3c891..63aec4e 100755 --- a/libgfortran/configure +++ b/libgfortran/configure @@ -16312,7 +16312,7 @@ _ACEOF fi done -for ac_func in sleep time ttyname signal alarm ctime clock access fork execl +for ac_func in sleep time ttyname signal alarm clock access fork execl do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -16351,7 +16351,7 @@ _ACEOF fi done -for ac_func in localtime_r gmtime_r strerror_r getpwuid_r ttyname_r ctime_r +for ac_func in localtime_r gmtime_r strerror_r getpwuid_r ttyname_r do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -16364,12 +16364,14 @@ _ACEOF fi done -for ac_func in clock_gettime +for ac_func in clock_gettime strftime do : - ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" -if test "x$ac_cv_func_clock_gettime" = x""yes; then : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF -#define HAVE_CLOCK_GETTIME 1 +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi diff --git a/libgfortran/configure.ac b/libgfortran/configure.ac index 8161659..ddd2617 100644 --- a/libgfortran/configure.ac +++ b/libgfortran/configure.ac @@ -248,11 +248,11 @@ AC_CHECK_MEMBERS([struct stat.st_rdev]) # Check for library functions. AC_CHECK_FUNCS(getrusage times mkstemp strtof strtold snprintf ftruncate chsize) AC_CHECK_FUNCS(chdir strerror getlogin gethostname kill link symlink perror) -AC_CHECK_FUNCS(sleep time ttyname signal alarm ctime clock access fork execl) +AC_CHECK_FUNCS(sleep time ttyname signal alarm clock access fork execl) AC_CHECK_FUNCS(wait setmode execvp pipe dup2 close fdopen strcasestr getrlimit) AC_CHECK_FUNCS(gettimeofday stat fstat lstat getpwuid vsnprintf dup getcwd) -AC_CHECK_FUNCS(localtime_r gmtime_r strerror_r getpwuid_r ttyname_r ctime_r) -AC_CHECK_FUNCS(clock_gettime) +AC_CHECK_FUNCS(localtime_r gmtime_r strerror_r getpwuid_r ttyname_r) +AC_CHECK_FUNCS(clock_gettime strftime) # Check for glibc backtrace functions AC_CHECK_FUNCS(backtrace backtrace_symbols) diff --git a/libgfortran/intrinsics/ctime.c b/libgfortran/intrinsics/ctime.c index b7b463c..c30389d 100644 --- a/libgfortran/intrinsics/ctime.c +++ b/libgfortran/intrinsics/ctime.c @@ -25,42 +25,35 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #include "libgfortran.h" -#ifdef TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# ifdef HAVE_TIME_H -# include -# endif -# endif -#endif +#include "time_1.h" #include -#ifndef HAVE_CTIME_R -/* Make sure we don't see here a macro. */ -#undef ctime_r +/* strftime-like function that fills a Fortran string with %c format + which is identical to ctime in the default locale. As ctime and + ctime_r are poorly specified and their usage not recommended, the + implementation instead uses strftime. */ -static char * -ctime_r (const time_t * timep, char * buf __attribute__((unused))) +static size_t +fctime (char *s, size_t max, const time_t *timep) { -#ifdef HAVE_CTIME - char *tmp = ctime (timep); - if (tmp) - tmp = strcpy (buf, tmp); - return tmp; +#ifdef HAVE_STRFTIME + struct tm res; + struct tm *ltm = localtime_r (timep, &res); + size_t n = strftime (s, max, "%c", ltm); + if (n < max) + memset (s + n, ' ', max - n); + return n; #else - return NULL; + memset (s, ' ', max); + return 0; #endif } -#endif -/* ctime_r() buffer size needs to be at least 26 bytes. */ -#define CSZ 26 +/* In the default locale, the date and time representation fits in 26 + bytes. However, other locales might need more space. */ +#define CSZ 100 extern void fdate (char **, gfc_charlen_type *); export_proto(fdate); @@ -68,29 +61,15 @@ export_proto(fdate); void fdate (char ** date, gfc_charlen_type * date_len) { -#if defined(HAVE_TIME) && defined(HAVE_CTIME) - char cbuf[CSZ]; - int i; +#if defined(HAVE_TIME) time_t now = time(NULL); - *date = ctime_r (&now, cbuf); - if (*date != NULL) - { - *date = strdup (*date); - *date_len = strlen (*date); - - i = 0; - while ((*date)[i]) - { - if ((*date)[i] == '\n') - (*date)[i] = ' '; - i++; - } - return; - } -#endif + *date = get_mem (CSZ); + *date_len = fctime (*date, CSZ, &now); +#else *date = NULL; *date_len = 0; +#endif } @@ -100,22 +79,11 @@ export_proto(fdate_sub); void fdate_sub (char * date, gfc_charlen_type date_len) { -#if defined(HAVE_TIME) && defined(HAVE_CTIME) - char cbuf[CSZ]; - int i; - char *d; +#if defined(HAVE_TIME) time_t now = time(NULL); -#endif - + fctime (date, date_len, &now); +#else memset (date, ' ', date_len); -#if defined(HAVE_TIME) && defined(HAVE_CTIME) - d = ctime_r (&now, cbuf); - if (d != NULL) - { - i = 0; - while (*d && *d != '\n' && i < date_len) - date[i++] = *(d++); - } #endif } @@ -127,29 +95,15 @@ export_proto_np(PREFIX(ctime)); void PREFIX(ctime) (char ** date, gfc_charlen_type * date_len, GFC_INTEGER_8 t) { -#if defined(HAVE_CTIME) - char cbuf[CSZ]; +#if defined(HAVE_TIME) time_t now = t; - int i; - *date = ctime_r (&now, cbuf); - if (*date != NULL) - { - *date = strdup (*date); - *date_len = strlen (*date); - - i = 0; - while ((*date)[i]) - { - if ((*date)[i] == '\n') - (*date)[i] = ' '; - i++; - } - return; - } -#endif + *date = get_mem (CSZ); + *date_len = fctime (*date, CSZ, &now); +#else *date = NULL; *date_len = 0; +#endif } @@ -159,21 +113,6 @@ export_proto(ctime_sub); void ctime_sub (GFC_INTEGER_8 * t, char * date, gfc_charlen_type date_len) { -#if defined(HAVE_CTIME) - char cbuf[CSZ]; - int i; - char *d; time_t now = *t; -#endif - - memset (date, ' ', date_len); -#if defined(HAVE_CTIME) - d = ctime_r (&now, cbuf); - if (d != NULL) - { - i = 0; - while (*d && *d != '\n' && i < date_len) - date[i++] = *(d++); - } -#endif + fctime (date, date_len, &now); } diff --git a/libgfortran/intrinsics/date_and_time.c b/libgfortran/intrinsics/date_and_time.c index c58d114..7a1bd3b 100644 --- a/libgfortran/intrinsics/date_and_time.c +++ b/libgfortran/intrinsics/date_and_time.c @@ -36,24 +36,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #endif -/* If the re-entrant versions of localtime and gmtime are not - available, provide fallback implementations. On some targets where - the _r versions are not available, localtime and gmtime use - thread-local storage so they are threadsafe. */ - -#ifndef HAVE_LOCALTIME_R -/* If _POSIX is defined localtime_r gets defined by mingw-w64 headers. */ -#ifdef localtime_r -#undef localtime_r -#endif - -static struct tm * -localtime_r (const time_t * timep, struct tm * result) -{ - *result = *localtime (timep); - return result; -} -#endif +/* If the re-entrant version of gmtime is not available, provide a + fallback implementation. On some targets where the _r version are + not available, gmtime use thread-local storage so it's + threadsafe. */ #ifndef HAVE_GMTIME_R /* If _POSIX is defined gmtime_r gets defined by mingw-w64 headers. */ diff --git a/libgfortran/intrinsics/time_1.h b/libgfortran/intrinsics/time_1.h index 073595a..06b8e4b 100644 --- a/libgfortran/intrinsics/time_1.h +++ b/libgfortran/intrinsics/time_1.h @@ -84,6 +84,26 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #endif /* !HAVE_GETRUSAGE || !HAVE_SYS_RESOURCE_H */ +/* If the re-entrant version of localtime is not available, provide a + fallback implementation. On some targets where the _r version are + not available, localtime use thread-local storage so it's + threadsafe. */ + +#ifndef HAVE_LOCALTIME_R +/* If _POSIX is defined localtime_r gets defined by mingw-w64 headers. */ +#ifdef localtime_r +#undef localtime_r +#endif + +static struct tm * +localtime_r (const time_t * timep, struct tm * result) +{ + *result = *localtime (timep); + return result; +} +#endif + + #if defined (__GNUC__) && (__GNUC__ >= 3) # define ATTRIBUTE_ALWAYS_INLINE __attribute__ ((__always_inline__)) #else