1 1.32 christos /* $NetBSD: asctime.c,v 1.32 2025/01/23 22:44:22 christos Exp $ */ 2 1.25 christos 3 1.31 christos /* asctime a la ISO C. */ 4 1.3 jtc 5 1.3 jtc /* 6 1.3 jtc ** This file is in the public domain, so clarified as of 7 1.13 mlelstv ** 1996-06-05 by Arthur David Olson. 8 1.13 mlelstv */ 9 1.13 mlelstv 10 1.13 mlelstv /* 11 1.13 mlelstv ** Avoid the temptation to punt entirely to strftime; 12 1.32 christos ** strftime can behave badly when tm components are out of range, and 13 1.13 mlelstv ** the output of strftime is supposed to be locale specific 14 1.13 mlelstv ** whereas the output of asctime is supposed to be constant. 15 1.3 jtc */ 16 1.2 jtc 17 1.6 christos #include <sys/cdefs.h> 18 1.11 msaitoh #if defined(LIBC_SCCS) && !defined(lint) 19 1.6 christos #if 0 20 1.14 christos static char elsieid[] = "@(#)asctime.c 8.5"; 21 1.6 christos #else 22 1.32 christos __RCSID("$NetBSD: asctime.c,v 1.32 2025/01/23 22:44:22 christos Exp $"); 23 1.6 christos #endif 24 1.11 msaitoh #endif /* LIBC_SCCS and not lint */ 25 1.1 jtc 26 1.1 jtc /*LINTLIBRARY*/ 27 1.1 jtc 28 1.9 kleink #include "namespace.h" 29 1.1 jtc #include "private.h" 30 1.22 christos #include <stdio.h> 31 1.1 jtc 32 1.31 christos /* Publish asctime_r and ctime_r only when supporting older POSIX. */ 33 1.32 christos #if SUPPORT_POSIX2008 || defined(__NetBSD__) 34 1.31 christos # define asctime_static 35 1.31 christos #else 36 1.31 christos # define asctime_static static 37 1.31 christos # undef asctime_r 38 1.31 christos # undef ctime_r 39 1.31 christos # define asctime_r static_asctime_r 40 1.31 christos # define ctime_r static_ctime_r 41 1.31 christos #endif 42 1.31 christos 43 1.32 christos #ifndef __LIBC12_SOURCE__ 44 1.32 christos 45 1.32 christos #ifdef __weak_alias 46 1.32 christos __weak_alias(asctime_r,_asctime_r) 47 1.32 christos #endif 48 1.32 christos 49 1.32 christos enum { STD_ASCTIME_BUF_SIZE = 26 }; 50 1.32 christos 51 1.32 christos 52 1.13 mlelstv /* 53 1.13 mlelstv ** Big enough for something such as 54 1.13 mlelstv ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n 55 1.13 mlelstv ** (two three-character abbreviations, five strings denoting integers, 56 1.13 mlelstv ** seven explicit spaces, two explicit colons, a newline, 57 1.19 christos ** and a trailing NUL byte). 58 1.13 mlelstv ** The values above are for systems where an int is 32 bits and are provided 59 1.28 christos ** as an example; the size expression below is a bound for the system at 60 1.13 mlelstv ** hand. 61 1.13 mlelstv */ 62 1.32 christos static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1]; 63 1.13 mlelstv 64 1.32 christos /* On pre-C99 platforms, a snprintf substitute good enough for us. */ 65 1.32 christos #if !HAVE_SNPRINTF 66 1.32 christos # include <stdarg.h> 67 1.32 christos ATTRIBUTE_FORMAT((printf, 3, 4)) static int 68 1.32 christos my_snprintf(char *s, size_t size, char const *format, ...) 69 1.32 christos { 70 1.32 christos int n; 71 1.32 christos va_list args; 72 1.32 christos char stackbuf[sizeof buf_asctime]; 73 1.32 christos va_start(args, format); 74 1.32 christos n = vsprintf(stackbuf, format, args); 75 1.32 christos va_end (args); 76 1.32 christos if (0 <= n && n < size) 77 1.32 christos memcpy (s, stackbuf, n + 1); 78 1.32 christos return n; 79 1.32 christos } 80 1.32 christos # undef snprintf 81 1.32 christos # define snprintf my_snprintf 82 1.29 christos #endif 83 1.29 christos 84 1.31 christos asctime_static 85 1.1 jtc char * 86 1.29 christos asctime_r(struct tm const *restrict timeptr, char *restrict buf) 87 1.1 jtc { 88 1.22 christos static const char wday_name[][4] = { 89 1.1 jtc "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 90 1.1 jtc }; 91 1.22 christos static const char mon_name[][4] = { 92 1.1 jtc "Jan", "Feb", "Mar", "Apr", "May", "Jun", 93 1.1 jtc "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 94 1.1 jtc }; 95 1.18 christos const char * wn; 96 1.18 christos const char * mn; 97 1.32 christos int year, mday, hour, min, sec; 98 1.32 christos long long_TM_YEAR_BASE = TM_YEAR_BASE; 99 1.32 christos size_t bufsize = (buf == buf_asctime 100 1.32 christos ? sizeof buf_asctime : STD_ASCTIME_BUF_SIZE); 101 1.1 jtc 102 1.14 christos if (timeptr == NULL) { 103 1.32 christos strcpy(buf, "??? ??? ?? ??:??:?? ????\n"); 104 1.32 christos /* Set errno now, since strcpy might change it in 105 1.32 christos POSIX.1-2017 and earlier. */ 106 1.14 christos errno = EINVAL; 107 1.32 christos return buf; 108 1.14 christos } 109 1.1 jtc if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) 110 1.1 jtc wn = "???"; 111 1.1 jtc else wn = wday_name[timeptr->tm_wday]; 112 1.1 jtc if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) 113 1.1 jtc mn = "???"; 114 1.1 jtc else mn = mon_name[timeptr->tm_mon]; 115 1.32 christos 116 1.32 christos year = timeptr->tm_year; 117 1.32 christos mday = timeptr->tm_mday; 118 1.32 christos hour = timeptr->tm_hour; 119 1.32 christos min = timeptr->tm_min; 120 1.32 christos sec = timeptr->tm_sec; 121 1.32 christos 122 1.32 christos /* Vintage programs are coded for years that are always four bytes long 123 1.32 christos and may assume that the newline always lands in the same place. 124 1.32 christos For years that are less than four bytes, pad the output with 125 1.32 christos leading zeroes to get the newline in the traditional place. 126 1.32 christos For years longer than four bytes, put extra spaces before the year 127 1.32 christos so that vintage code trying to overwrite the newline 128 1.32 christos won't overwrite a digit within a year and truncate the year, 129 1.32 christos using the principle that no output is better than wrong output. 130 1.32 christos This conforms to ISO C and POSIX standards, which say behavior 131 1.32 christos is undefined when the year is less than 1000 or greater than 9999. 132 1.32 christos 133 1.32 christos Also, avoid overflow when formatting tm_year + TM_YEAR_BASE. */ 134 1.32 christos 135 1.32 christos if ((size_t)(year <= INT_MAX - TM_YEAR_BASE 136 1.32 christos ? snprintf (buf, bufsize, 137 1.32 christos ((-999 - TM_YEAR_BASE <= year 138 1.32 christos && year <= 9999 - TM_YEAR_BASE) 139 1.32 christos ? "%s %s%3d %.2d:%.2d:%.2d %04ld\n" 140 1.32 christos : "%s %s%3d %.2d:%.2d:%.2d %ld\n"), 141 1.32 christos wn, mn, mday, hour, min, sec, 142 1.32 christos year + long_TM_YEAR_BASE) 143 1.32 christos : snprintf (buf, bufsize, 144 1.32 christos "%s %s%3d %.2d:%.2d:%.2d %d%d\n", 145 1.32 christos wn, mn, mday, hour, min, sec, 146 1.32 christos year / 10 + TM_YEAR_BASE / 10, 147 1.32 christos year % 10)) 148 1.32 christos < bufsize) 149 1.32 christos return buf; 150 1.14 christos else { 151 1.13 mlelstv errno = EOVERFLOW; 152 1.13 mlelstv return NULL; 153 1.13 mlelstv } 154 1.7 kleink } 155 1.7 kleink 156 1.7 kleink char * 157 1.15 abs asctime(const struct tm *timeptr) 158 1.7 kleink { 159 1.13 mlelstv return asctime_r(timeptr, buf_asctime); 160 1.1 jtc } 161 1.31 christos #endif /* !__LIBC12_SOURCE__ */ 162 1.29 christos 163 1.29 christos 164 1.29 christos char * 165 1.29 christos ctime_rz(timezone_t sp, const time_t *timep, char *buf) 166 1.29 christos { 167 1.29 christos struct tm mytm; 168 1.29 christos struct tm *tmp = localtime_rz(sp, timep, &mytm); 169 1.29 christos return tmp ? asctime_r(tmp, buf) : NULL; 170 1.29 christos } 171 1.29 christos 172 1.31 christos asctime_static 173 1.29 christos char * 174 1.29 christos ctime_r(const time_t *timep, char *buf) 175 1.29 christos { 176 1.29 christos struct tm mytm; 177 1.29 christos struct tm *tmp = localtime_r(timep, &mytm); 178 1.29 christos return tmp ? asctime_r(tmp, buf) : NULL; 179 1.29 christos } 180 1.29 christos 181 1.29 christos char * 182 1.29 christos ctime(const time_t *timep) 183 1.29 christos { 184 1.32 christos /* Do not call localtime_r, as C23 requires ctime to initialize the 185 1.32 christos static storage that localtime updates. */ 186 1.32 christos struct tm *tmp = localtime(timep); 187 1.32 christos return tmp ? asctime(tmp) : NULL; 188 1.29 christos } 189