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