diff mbox

[libfortran] PR 47802 Implementation of CTIME intrinsic

Message ID AANLkTindLsTMXjFvayHVkEh-EOMDpmuuvWSMerSpRDbk@mail.gmail.com
State New
Headers show

Commit Message

Janne Blomqvist Feb. 22, 2011, 4:17 p.m. UTC
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  <jb@gcc.gnu.org>

	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 mbox

Patch

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 <dlfcn.h> 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 <strings.h> 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 <sys/time.h>
-#  include <time.h>
-#else
-#  if HAVE_SYS_TIME_H
-#    include <sys/time.h>
-#  else
-#    ifdef HAVE_TIME_H
-#      include <time.h>
-#    endif
-#  endif
-#endif
+#include "time_1.h"
 
 #include <string.h>
 
 
-#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