xref: /freebsd/contrib/tzcode/asctime.c (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
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 ** the output of strftime is supposed to be locale specific
11 ** whereas the output of asctime is supposed to be constant.
12 */
13 
14 /*LINTLIBRARY*/
15 
16 #include "namespace.h"
17 #include "private.h"
18 #include "un-namespace.h"
19 #include <stdio.h>
20 
21 /*
22 ** All years associated with 32-bit time_t values are exactly four digits long;
23 ** some years associated with 64-bit time_t values are not.
24 ** Vintage programs are coded for years that are always four digits long
25 ** and may assume that the newline always lands in the same place.
26 ** For years that are less than four digits, we pad the output with
27 ** leading zeroes to get the newline in the traditional place.
28 ** The -4 ensures that we get four characters of output even if
29 ** we call a strftime variant that produces fewer characters for some years.
30 ** This conforms to recent ISO C and POSIX standards, which say behavior
31 ** is undefined when the year is less than 1000 or greater than 9999.
32 */
33 static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
34 /*
35 ** For years that are more than four digits we put extra spaces before the year
36 ** so that code trying to overwrite the newline won't end up overwriting
37 ** a digit within a year and truncating the year (operating on the assumption
38 ** that no output is better than wrong output).
39 */
40 static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d     %s\n";
41 
42 enum { STD_ASCTIME_BUF_SIZE = 26 };
43 /*
44 ** Big enough for something such as
45 ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648     -2147483648\n
46 ** (two three-character abbreviations, five strings denoting integers,
47 ** seven explicit spaces, two explicit colons, a newline,
48 ** and a trailing NUL byte).
49 ** The values above are for systems where an int is 32 bits and are provided
50 ** as an example; the size expression below is a bound for the system at
51 ** hand.
52 */
53 static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
54 
55 /* A similar buffer for ctime.
56    C89 requires that they be the same buffer.
57    This requirement was removed in C99, so support it only if requested,
58    as support is more likely to lead to bugs in badly written programs.  */
59 #if SUPPORT_C89
60 # define buf_ctime buf_asctime
61 #else
62 static char buf_ctime[sizeof buf_asctime];
63 #endif
64 
65 /* Publish asctime_r and ctime_r only when supporting older POSIX.  */
66 #if SUPPORT_POSIX2008
67 # define asctime_static
68 #else
69 # define asctime_static static
70 # undef asctime_r
71 # undef ctime_r
72 # define asctime_r static_asctime_r
73 # define ctime_r static_ctime_r
74 #endif
75 
76 asctime_static
77 char *
78 asctime_r(struct tm const *restrict timeptr, char *restrict buf)
79 {
80 	static const char	wday_name[][4] = {
81 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
82 	};
83 	static const char	mon_name[][4] = {
84 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
85 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
86 	};
87 	const char *	wn;
88 	const char *	mn;
89 	char			year[INT_STRLEN_MAXIMUM(int) + 2];
90 	char result[sizeof buf_asctime];
91 
92 	if (timeptr == NULL) {
93 		errno = EINVAL;
94 		return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
95 	}
96 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
97 		wn = "???";
98 	else	wn = wday_name[timeptr->tm_wday];
99 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
100 		mn = "???";
101 	else	mn = mon_name[timeptr->tm_mon];
102 	/*
103 	** Use strftime's %Y to generate the year, to avoid overflow problems
104 	** when computing timeptr->tm_year + TM_YEAR_BASE.
105 	** Assume that strftime is unaffected by other out-of-range members
106 	** (e.g., timeptr->tm_mday) when processing "%Y".
107 	*/
108 	strftime(year, sizeof year, "%Y", timeptr);
109 	/*
110 	** We avoid using snprintf since it's not available on all systems.
111 	*/
112 	sprintf(result,
113 		((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
114 		wn, mn,
115 		timeptr->tm_mday, timeptr->tm_hour,
116 		timeptr->tm_min, timeptr->tm_sec,
117 		year);
118 	if (strlen(result) < STD_ASCTIME_BUF_SIZE
119 	    || buf == buf_ctime || buf == buf_asctime)
120 		return strcpy(buf, result);
121 	else {
122 		errno = EOVERFLOW;
123 		return NULL;
124 	}
125 }
126 
127 char *
128 asctime(register const struct tm *timeptr)
129 {
130 	return asctime_r(timeptr, buf_asctime);
131 }
132 
133 asctime_static
134 char *
135 ctime_r(const time_t *timep, char *buf)
136 {
137   struct tm mytm;
138   struct tm *tmp = localtime_r(timep, &mytm);
139   return tmp ? asctime_r(tmp, buf) : NULL;
140 }
141 
142 char *
143 ctime(const time_t *timep)
144 {
145   return ctime_r(timep, buf_ctime);
146 }
147