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