Home | History | Annotate | Line # | Download | only in time
asctime.c revision 1.31
      1 /*	$NetBSD: asctime.c,v 1.31 2024/09/11 13:50:34 christos Exp $	*/
      2 
      3 /* asctime a la ISO C.  */
      4 
      5 /*
      6 ** This file is in the public domain, so clarified as of
      7 ** 1996-06-05 by Arthur David Olson.
      8 */
      9 
     10 /*
     11 ** Avoid the temptation to punt entirely to strftime;
     12 ** the output of strftime is supposed to be locale specific
     13 ** whereas the output of asctime is supposed to be constant.
     14 */
     15 
     16 #include <sys/cdefs.h>
     17 #if defined(LIBC_SCCS) && !defined(lint)
     18 #if 0
     19 static char	elsieid[] = "@(#)asctime.c	8.5";
     20 #else
     21 __RCSID("$NetBSD: asctime.c,v 1.31 2024/09/11 13:50:34 christos Exp $");
     22 #endif
     23 #endif /* LIBC_SCCS and not lint */
     24 
     25 /*LINTLIBRARY*/
     26 
     27 #include "namespace.h"
     28 #include "private.h"
     29 #include <stdio.h>
     30 
     31 #ifndef __LIBC12_SOURCE__
     32 
     33 #ifdef __weak_alias
     34 __weak_alias(asctime_r,_asctime_r)
     35 #endif
     36 
     37 /*
     38 ** All years associated with 32-bit time_t values are exactly four digits long;
     39 ** some years associated with 64-bit time_t values are not.
     40 ** Vintage programs are coded for years that are always four digits long
     41 ** and may assume that the newline always lands in the same place.
     42 ** For years that are less than four digits, we pad the output with
     43 ** leading zeroes to get the newline in the traditional place.
     44 ** The -4 ensures that we get four characters of output even if
     45 ** we call a strftime variant that produces fewer characters for some years.
     46 ** This conforms to recent ISO C and POSIX standards, which say behavior
     47 ** is undefined when the year is less than 1000 or greater than 9999.
     48 */
     49 static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
     50 /*
     51 ** For years that are more than four digits we put extra spaces before the year
     52 ** so that code trying to overwrite the newline won't end up overwriting
     53 ** a digit within a year and truncating the year (operating on the assumption
     54 ** that no output is better than wrong output).
     55 */
     56 static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d     %s\n";
     57 
     58 enum { STD_ASCTIME_BUF_SIZE = 26 };
     59 #endif
     60 
     61 /* Publish asctime_r and ctime_r only when supporting older POSIX.  */
     62 #if SUPPORT_POSIX2008
     63 # define asctime_static
     64 #else
     65 # define asctime_static static
     66 # undef asctime_r
     67 # undef ctime_r
     68 # define asctime_r static_asctime_r
     69 # define ctime_r static_ctime_r
     70 #endif
     71 
     72 /*
     73 ** Big enough for something such as
     74 ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648     -2147483648\n
     75 ** (two three-character abbreviations, five strings denoting integers,
     76 ** seven explicit spaces, two explicit colons, a newline,
     77 ** and a trailing NUL byte).
     78 ** The values above are for systems where an int is 32 bits and are provided
     79 ** as an example; the size expression below is a bound for the system at
     80 ** hand.
     81 */
     82 static char buf_ctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
     83 
     84 #ifndef __LIBC12_SOURCE__
     85 /* A similar buffer for ctime.
     86    C89 requires that they be the same buffer.
     87    This requirement was removed in C99, so support it only if requested,
     88    as support is more likely to lead to bugs in badly written programs.  */
     89 #if SUPPORT_C89
     90 # define buf_asctime buf_ctime
     91 #else
     92 static char buf_asctime[sizeof buf_ctime];
     93 #endif
     94 
     95 
     96 asctime_static
     97 char *
     98 asctime_r(struct tm const *restrict timeptr, char *restrict buf)
     99 {
    100 	static const char	wday_name[][4] = {
    101 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    102 	};
    103 	static const char	mon_name[][4] = {
    104 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
    105 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    106 	};
    107 	const char *	wn;
    108 	const char *	mn;
    109 	char			year[INT_STRLEN_MAXIMUM(int) + 2];
    110 	char result[sizeof buf_ctime];
    111 
    112 	if (timeptr == NULL) {
    113 		errno = EINVAL;
    114 		return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
    115 	}
    116 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
    117 		wn = "???";
    118 	else	wn = wday_name[timeptr->tm_wday];
    119 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
    120 		mn = "???";
    121 	else	mn = mon_name[timeptr->tm_mon];
    122 	/*
    123 	** Use strftime's %Y to generate the year, to avoid overflow problems
    124 	** when computing timeptr->tm_year + TM_YEAR_BASE.
    125 	** Assume that strftime is unaffected by other out-of-range members
    126 	** (e.g., timeptr->tm_mday) when processing "%Y".
    127 	*/
    128 	(void) strftime(year, sizeof year, "%Y", timeptr);
    129 	(void) snprintf(result,
    130 		sizeof(result),
    131 		((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
    132 		wn, mn,
    133 		timeptr->tm_mday, timeptr->tm_hour,
    134 		timeptr->tm_min, timeptr->tm_sec,
    135 		year);
    136 	if (strlen(result) < STD_ASCTIME_BUF_SIZE
    137 	    || buf == buf_ctime || buf == buf_asctime)
    138 		return strcpy(buf, result);
    139 	else {
    140 		errno = EOVERFLOW;
    141 		return NULL;
    142 	}
    143 }
    144 
    145 char *
    146 asctime(const struct tm *timeptr)
    147 {
    148 	return asctime_r(timeptr, buf_asctime);
    149 }
    150 #endif /* !__LIBC12_SOURCE__ */
    151 
    152 
    153 char *
    154 ctime_rz(timezone_t sp, const time_t *timep, char *buf)
    155 {
    156   struct tm mytm;
    157   struct tm *tmp = localtime_rz(sp, timep, &mytm);
    158   return tmp ? asctime_r(tmp, buf) : NULL;
    159 }
    160 
    161 asctime_static
    162 char *
    163 ctime_r(const time_t *timep, char *buf)
    164 {
    165   struct tm mytm;
    166   struct tm *tmp = localtime_r(timep, &mytm);
    167   return tmp ? asctime_r(tmp, buf) : NULL;
    168 }
    169 
    170 char *
    171 ctime(const time_t *timep)
    172 {
    173   return ctime_r(timep, buf_ctime);
    174 }
    175