asctime.c
Paul Eggert
eggert at CS.UCLA.EDU
Tue Jul 27 07:37:12 UTC 2004
"Olson, Arthur David (NIH/NCI)" <olsona at dc37a.nci.nih.gov> writes:
> snprintf's behavior is to return the number of characters that would have
> been generated had the output buffer not been limited.
Good catch. Sorry, I misused snprintf. (Wouldn't be the first time. :-)
> Does anyone see problems with using this in the next bundle?
Here are the problems I see:
* There's a regression for out-of-range inputs. The current asctime
always returns a valid, well-defined string. With the proposed
change, asctime will fail and set errno = EOVERFLOW in some (but not
in all) cases when the inputs are outside their traditional ranges.
This problem is not limited to 64-bit hosts: it can also occur with
32-bit hosts, e.g, if tm_mday is out of range then the proposed
asctime might fail (but it might not, depending on tm_year's value).
The standard doesn't require asctime to succeed in all these cases,
but I'd rather keep the current behavior: it's simpler to explain,
is more useful in practice, and is less likely to crash (admittedly
poorly-written) user programs.
* If snprintf returns a negative value (this shouldn't happen --
perhaps if memory exhausted though?), asctime should probably leave
errno set to whatever snprintf set it to, rather than setting it to
EOVERFLOW.
* A few portability assumptions, which aren't true on older hosts.
These aren't that big a deal, I suppose....
- The code assumes that snprintf works.
- The code assumes <errno.h> defines EOVERFLOW.
Here's a proposed version to address the above issues. It merges the
changes that you circulated, and it assumes the private.h and
Makefile (comment) changes I proposed previously.
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson (arthur_david_olson at nih.gov).
*/
#ifndef lint
#ifndef NOID
static char elsieid[] = "@(#)asctime.c 7.13";
#endif /* !defined NOID */
#endif /* !defined lint */
/*LINTLIBRARY*/
#include "private.h"
#include "tzfile.h"
#define STANDARD_BUFFER_SIZE 26
#ifndef EOVERFLOW
# define EOVERFLOW EINVAL
#endif
/*
** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
*/
/*
** 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);
} else {
(void) memcpy(buf, tbuf, size - 1);
buf[size - 1] = '\0';
}
return len;
}
#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"
};
static const char mon_name[][3] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
register const char * wn;
register const char * mn;
register int result;
if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
wn = "???";
else wn = wday_name[timeptr->tm_wday];
if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
mn = "???";
else mn = mon_name[timeptr->tm_mon];
/*
** The format used in the (2004) standard is
** "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
** Use "%02d", as it is a bit more portable than "%.2d".
*/
result = snprintf(buf, STANDARD_BUFFER_SIZE,
"%.3s %.3s%3d %02d:%02d:%02d %ld\n",
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
timeptr->tm_year + (long) TM_YEAR_BASE);
if (result < 0)
return NULL;
if (result >= size) {
errno = EOVERFLOW;
return NULL;
}
return buf;
}
char *
asctime_r(timeptr, buf)
register const struct tm * timeptr;
char * buf;
{
return asctime_rn(timeptr, buf, STANDARD_BUFFER_SIZE);
}
/*
** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition,
** with core dump avoidance.
*/
char *
asctime(timeptr)
register const struct tm * timeptr;
{
static char result[MAX_ASCTIME_SIZE];
return asctime_rn(timeptr, result, sizeof result);
}
More information about the tz
mailing list