asctime.c revision 1.26 1 /* $NetBSD: asctime.c,v 1.26 2019/01/27 02:08:35 pgoyette Exp $ */
2
3 /* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */
4
5 /* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */
6
7 /*
8 ** This file is in the public domain, so clarified as of
9 ** 1996-06-05 by Arthur David Olson.
10 */
11
12 /*
13 ** Avoid the temptation to punt entirely to strftime;
14 ** the output of strftime is supposed to be locale specific
15 ** whereas the output of asctime is supposed to be constant.
16 */
17
18 #include <sys/cdefs.h>
19 #if defined(LIBC_SCCS) && !defined(lint)
20 #if 0
21 static char elsieid[] = "@(#)asctime.c 8.5";
22 #else
23 __RCSID("$NetBSD: asctime.c,v 1.26 2019/01/27 02:08:35 pgoyette Exp $");
24 #endif
25 #endif /* LIBC_SCCS and not lint */
26
27 /*LINTLIBRARY*/
28
29 #include "namespace.h"
30 #include "private.h"
31 #include <stdio.h>
32
33 #ifdef __weak_alias
34 __weak_alias(asctime_r,_asctime_r)
35 #endif
36
37 /*
38 ** Some systems only handle "%.2d"; others only handle "%02d";
39 ** "%02.2d" makes (most) everybody happy.
40 ** At least some versions of gcc warn about the %02.2d;
41 ** we conditionalize below to avoid the warning.
42 */
43 /*
44 ** All years associated with 32-bit time_t values are exactly four digits long;
45 ** some years associated with 64-bit time_t values are not.
46 ** Vintage programs are coded for years that are always four digits long
47 ** and may assume that the newline always lands in the same place.
48 ** For years that are less than four digits, we pad the output with
49 ** leading zeroes to get the newline in the traditional place.
50 ** The -4 ensures that we get four characters of output even if
51 ** we call a strftime variant that produces fewer characters for some years.
52 ** The ISO C and POSIX standards prohibit padding the year,
53 ** but many implementations pad anyway; most likely the standards are buggy.
54 */
55 #ifdef __GNUC__
56 #define ASCTIME_FMT "%s %s%3d %2.2d:%2.2d:%2.2d %-4s\n"
57 #else /* !defined __GNUC__ */
58 #define ASCTIME_FMT "%s %s%3d %02.2d:%02.2d:%02.2d %-4s\n"
59 #endif /* !defined __GNUC__ */
60 /*
61 ** For years that are more than four digits we put extra spaces before the year
62 ** so that code trying to overwrite the newline won't end up overwriting
63 ** a digit within a year and truncating the year (operating on the assumption
64 ** that no output is better than wrong output).
65 */
66 #ifdef __GNUC__
67 #define ASCTIME_FMT_B "%s %s%3d %2.2d:%2.2d:%2.2d %s\n"
68 #else /* !defined __GNUC__ */
69 #define ASCTIME_FMT_B "%s %s%3d %02.2d:%02.2d:%02.2d %s\n"
70 #endif /* !defined __GNUC__ */
71
72 #define STD_ASCTIME_BUF_SIZE 26
73 /*
74 ** Big enough for something such as
75 ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
76 ** (two three-character abbreviations, five strings denoting integers,
77 ** seven explicit spaces, two explicit colons, a newline,
78 ** and a trailing NUL byte).
79 ** The values above are for systems where an int is 32 bits and are provided
80 ** as an example; the define below calculates the maximum for the system at
81 ** hand.
82 */
83 #define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
84
85 static char buf_asctime[MAX_ASCTIME_BUF_SIZE];
86
87 char *
88 asctime_r(const struct tm *timeptr, char *buf)
89 {
90 static const char wday_name[][4] = {
91 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
92 };
93 static const char mon_name[][4] = {
94 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
95 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
96 };
97 const char * wn;
98 const char * mn;
99 char year[INT_STRLEN_MAXIMUM(int) + 2];
100 char result[MAX_ASCTIME_BUF_SIZE];
101
102 if (timeptr == NULL) {
103 errno = EINVAL;
104 return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
105 }
106 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
107 wn = "???";
108 else wn = wday_name[timeptr->tm_wday];
109 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
110 mn = "???";
111 else mn = mon_name[timeptr->tm_mon];
112 /*
113 ** Use strftime's %Y to generate the year, to avoid overflow problems
114 ** when computing timeptr->tm_year + TM_YEAR_BASE.
115 ** Assume that strftime is unaffected by other out-of-range members
116 ** (e.g., timeptr->tm_mday) when processing "%Y".
117 */
118 (void) strftime(year, sizeof year, "%Y", timeptr);
119 (void) snprintf(result,
120 sizeof(result),
121 ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
122 wn, mn,
123 timeptr->tm_mday, timeptr->tm_hour,
124 timeptr->tm_min, timeptr->tm_sec,
125 year);
126 if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime)
127 return strcpy(buf, result);
128 else {
129 errno = EOVERFLOW;
130 return NULL;
131 }
132 }
133
134 char *
135 asctime(const struct tm *timeptr)
136 {
137 return asctime_r(timeptr, buf_asctime);
138 }
139