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