1/* $NetBSD: asctime.c,v 1.30 2024/01/20 14:52:49 christos Exp $ */ 2 3/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */ 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 19static char elsieid[] = "@(#)asctime.c 8.5"; 20#else 21__RCSID("$NetBSD: asctime.c,v 1.30 2024/01/20 14:52:49 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** The ISO C and POSIX standards prohibit padding the year, 47** but many implementations pad anyway; most likely the standards are buggy. 48*/ 49static 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*/ 56static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n"; 57 58enum { STD_ASCTIME_BUF_SIZE = 26 }; 59#endif 60 61/* 62** Big enough for something such as 63** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n 64** (two three-character abbreviations, five strings denoting integers, 65** seven explicit spaces, two explicit colons, a newline, 66** and a trailing NUL byte). 67** The values above are for systems where an int is 32 bits and are provided 68** as an example; the size expression below is a bound for the system at 69** hand. 70*/ 71static char buf_ctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1]; 72 73/* A similar buffer for ctime. 74 C89 requires that they be the same buffer. 75 This requirement was removed in C99, so support it only if requested, 76 as support is more likely to lead to bugs in badly written programs. */ 77#ifndef __LIBC12_SOURCE__ 78#if SUPPORT_C89 79# define buf_asctime buf_ctime 80#else 81static char buf_asctime[sizeof buf_ctime]; 82#endif 83 84char * 85asctime_r(struct tm const *restrict timeptr, char *restrict buf) 86{ 87 static const char wday_name[][4] = { 88 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 89 }; 90 static const char mon_name[][4] = { 91 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 92 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 93 }; 94 const char * wn; 95 const char * mn; 96 char year[INT_STRLEN_MAXIMUM(int) + 2]; 97 char result[sizeof buf_ctime]; 98 99 if (timeptr == NULL) { 100 errno = EINVAL; 101 return strcpy(buf, "??? ??? ?? ??:??:?? ????\n"); 102 } 103 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) 104 wn = "???"; 105 else wn = wday_name[timeptr->tm_wday]; 106 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) 107 mn = "???"; 108 else mn = mon_name[timeptr->tm_mon]; 109 /* 110 ** Use strftime's %Y to generate the year, to avoid overflow problems 111 ** when computing timeptr->tm_year + TM_YEAR_BASE. 112 ** Assume that strftime is unaffected by other out-of-range members 113 ** (e.g., timeptr->tm_mday) when processing "%Y". 114 */ 115 (void) strftime(year, sizeof year, "%Y", timeptr); 116 (void) snprintf(result, 117 sizeof(result), 118 ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), 119 wn, mn, 120 timeptr->tm_mday, timeptr->tm_hour, 121 timeptr->tm_min, timeptr->tm_sec, 122 year); 123 if (strlen(result) < STD_ASCTIME_BUF_SIZE 124 || buf == buf_ctime || buf == buf_asctime) 125 return strcpy(buf, result); 126 else { 127 errno = EOVERFLOW; 128 return NULL; 129 } 130} 131 132char * 133asctime(const struct tm *timeptr) 134{ 135 return asctime_r(timeptr, buf_asctime); 136} 137 138#endif /* !__LIBC12_SOURCE__ */ 139 140char * 141ctime_rz(timezone_t sp, const time_t *timep, char *buf) 142{ 143 struct tm mytm; 144 struct tm *tmp = localtime_rz(sp, timep, &mytm); 145 return tmp ? asctime_r(tmp, buf) : NULL; 146} 147 148char * 149ctime_r(const time_t *timep, char *buf) 150{ 151 struct tm mytm; 152 struct tm *tmp = localtime_r(timep, &mytm); 153 return tmp ? asctime_r(tmp, buf) : NULL; 154} 155 156char * 157ctime(const time_t *timep) 158{ 159 return ctime_r(timep, buf_ctime); 160} 161