asctime.c

Paul Eggert eggert at CS.UCLA.EDU
Thu Jul 22 20:15:33 UTC 2004


"Olson, Arthur David (NIH/NCI)" <olsona at dc37a.nci.nih.gov> writes:

> 		if (y >= -999 && y <= 9999)
> 			(void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %ld\n",
> 				wn, mn,
> 				timeptr->tm_mday, timeptr->tm_hour,
> 				timeptr->tm_min, timeptr->tm_sec,
> 				y);
> 		else	(void) sprintf(buf, "%.3s %.3s%3d %ld\n",
> 				wn, mn, timeptr->tm_mday, y);
>
> ...never returns NULL and never (when applied to a "struct tm" derived from
> a 64-bit time_t) overflows a 26-character buffer passed to asctime.

True, but it would still overflow the 26-byte buffer in other cases,
e.g., if tm_mday is out of range.  And even if you had a valid struct
tm, it would still overflow in some currently-theoretical
environments, e.g., 64-bit int and 128-bit long and time_t.  (While
we're fixing this, let's fix it permanently.  :-)

Also, I'd rather have asctime_r return NULL when the result doesn't
fit.  That's what POSIX says to do when asctime_r fails, and it's what
HP-UX asctime_r does.

If you want asctime_r to avoid overflowing a 26-byte buffer, the
simplest way is to use snprintf instead of sprintf.  Something like
the following should do the trick.


===================================================================
RCS file: RCS/Makefile,v
retrieving revision 2004.1
retrieving revision 2004.1.0.1
diff -pu -r2004.1 -r2004.1.0.1
--- Makefile	2004/03/19 19:48:35	2004.1
+++ Makefile	2004/07/22 20:05:59	2004.1.0.1
@@ -96,6 +96,7 @@ LDLIBS=
 #  -DHAVE_SETTIMEOFDAY=1 if settimeofday has just 1 arg (SVR4)
 #  -DHAVE_SETTIMEOFDAY=2 if settimeofday uses 2nd arg (4.3BSD)
 #  -DHAVE_SETTIMEOFDAY=3 if settimeofday ignores 2nd arg (4.4BSD)
+#  -DHAVE_SNPRINTF=0 if your system lacks the snprintf function
 #  -DHAVE_STRERROR=0 if your system lacks the strerror function
 #  -DHAVE_SYMLINK=0 if your system lacks the symlink function
 #  -DHAVE_SYS_STAT_H=0 if your compiler lacks a "sys/stat.h"
===================================================================
RCS file: RCS/private.h,v
retrieving revision 2003.5
retrieving revision 2003.5.0.1
diff -pu -r2003.5 -r2003.5.0.1
--- private.h	2003/12/15 14:36:35	2003.5
+++ private.h	2004/07/22 20:06:11	2003.5.0.1
@@ -46,6 +46,10 @@ static char	privatehid[] = "@(#)private.
 #define HAVE_SETTIMEOFDAY	3
 #endif /* !defined HAVE_SETTIMEOFDAY */
 
+#ifndef HAVE_SNPRINTF
+#define HAVE_SNPRINTF		1
+#endif /* !defined HAVE_STRERROR */
+
 #ifndef HAVE_STRERROR
 #define HAVE_STRERROR		1
 #endif /* !defined HAVE_STRERROR */
===================================================================
RCS file: RCS/asctime.c,v
retrieving revision 2004.1
retrieving revision 2004.1.0.2
diff -pu -r2004.1 -r2004.1.0.2
--- asctime.c	1998/05/28 13:56:06	2004.1
+++ asctime.c	2004/07/22 20:03:28	2004.1.0.2
@@ -14,14 +14,52 @@ static char	elsieid[] = "@(#)asctime.c	7
 #include "private.h"
 #include "tzfile.h"
 
+#ifndef EOVERFLOW
+# define EOVERFLOW EINVAL
+#endif
+
 /*
 ** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12.
 */
 
-char *
-asctime_r(timeptr, buf)
+/*
+** Big enough for something such as
+** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
+** (two three-character abbreviations, five strings denoting integers,
+** three explicit spaces, two explicit colons, a newline,
+** and a trailing ASCII nul).
+*/
+#define MAX_ASCTIME_SIZE (3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + 3 + 2 + 1 + 1)
+
+#if !HAVE_SNPRINTF
+/*
+** A substitute for snprintf that is good enough for asctime.
+*/
+static int
+snprintf(buf, size, format, mday, hour, min, sec, year)
+char *				buf;
+size_t				size;
+const char *			format;
+int				mday, hour, min, sec;
+long				year;
+{
+	char		tbuf[MAX_ASCTIME_SIZE];
+	size_t		len;
+	(void) sprintf(tbuf, buf, size, format, mday, hour, min, sec, year);
+	len = strlen(tbuf);
+	if (len < size) {
+		(void) strcpy(buf, tbuf);
+		return len;
+	} else
+		return -1;
+}
+#endif
+
+static char *
+asctime_rn(timeptr, buf, size)
 register const struct tm *	timeptr;
 char *				buf;
+size_t				size;
 {
 	static const char	wday_name[][3] = {
 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
@@ -41,17 +79,29 @@ char *				buf;
 	else	mn = mon_name[timeptr->tm_mon];
 	/*
 	** The X3J11-suggested format is
-	**	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
-	** Since the .2 in 02.2d is ignored, we drop it.
+	**	"%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
+	** Use "%02d", as it is a bit more portable than "%.2d".
 	*/
-	(void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
+	if (snprintf(buf, size, "%.3s %.3s%3d %02d:%02d:%02d %ld\n",
 		wn, mn,
 		timeptr->tm_mday, timeptr->tm_hour,
 		timeptr->tm_min, timeptr->tm_sec,
-		TM_YEAR_BASE + timeptr->tm_year);
+		timeptr->tm_year + (long) TM_YEAR_BASE)
+	    < 0) {
+		errno = EOVERFLOW;
+		return NULL;
+	}
 	return buf;
 }
 
+char *
+asctime_r(timeptr, buf)
+register const struct tm *	timeptr;
+char *				buf;
+{
+	return asctime_rn(timeptr, buf, 26);
+}
+
 /*
 ** A la X3J11, with core dump avoidance.
 */
@@ -60,15 +110,7 @@ char *
 asctime(timeptr)
 register const struct tm *	timeptr;
 {
-	/*
-	** Big enough for something such as
-	** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
-	** (two three-character abbreviations, five strings denoting integers,
-	** three explicit spaces, two explicit colons, a newline,
-	** and a trailing ASCII nul).
-	*/
-	static char		result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
-					3 + 2 + 1 + 1];
+	static char		result[MAX_ASCTIME_SIZE];
 
-	return asctime_r(timeptr, result);
+	return asctime_rn(timeptr, result, sizeof result);
 }



More information about the tz mailing list