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