Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1989 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #if defined(LIBC_SCCS) && !defined(lint) | |
35 | static char sccsid[] = "@(#)strftime.c 5.11 (Berkeley) 2/24/91"; | |
36 | #endif /* LIBC_SCCS and not lint */ | |
37 | ||
38 | #include <sys/types.h> | |
39 | #include <sys/time.h> | |
40 | #include <tzfile.h> | |
41 | #include <string.h> | |
42 | ||
78ed81a3 | 43 | static char *abday[] = { |
15637ed4 RG |
44 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", |
45 | }; | |
78ed81a3 | 46 | static char *day[] = { |
15637ed4 RG |
47 | "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", |
48 | "Saturday", | |
49 | }; | |
78ed81a3 | 50 | static char *abmon[] = { |
15637ed4 RG |
51 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", |
52 | "Oct", "Nov", "Dec", | |
53 | }; | |
78ed81a3 | 54 | static char *mon[] = { |
15637ed4 RG |
55 | "January", "February", "March", "April", "May", "June", "July", |
56 | "August", "September", "October", "November", "December", | |
57 | }; | |
78ed81a3 | 58 | static char *ampm[] = { |
59 | "AM", "PM", | |
60 | }; | |
61 | ||
62 | static const char *d_t_fmt = "%a %b %e %H:%M:%S %Y"; | |
63 | static const char *d_fmt = "%m/%d/%y"; | |
64 | static const char *t_fmt = "%H:%M:%S"; | |
65 | static const char *t_fmt_ampm = "%I:%M:%S %p"; | |
15637ed4 RG |
66 | |
67 | static size_t gsize; | |
68 | static char *pt; | |
69 | static int _add(), _conv(), _secs(); | |
78ed81a3 | 70 | static size_t _fmt(); |
15637ed4 RG |
71 | |
72 | size_t | |
73 | strftime(s, maxsize, format, t) | |
74 | char *s; | |
75 | size_t maxsize; | |
76 | const char *format; | |
77 | const struct tm *t; | |
78 | { | |
15637ed4 RG |
79 | pt = s; |
80 | if ((gsize = maxsize) < 1) | |
81 | return(0); | |
82 | if (_fmt(format, t)) { | |
83 | *pt = '\0'; | |
84 | return(maxsize - gsize); | |
85 | } | |
86 | return(0); | |
87 | } | |
88 | ||
78ed81a3 | 89 | #define SUN_WEEK(t) (((t)->tm_yday + 7 - \ |
90 | ((t)->tm_wday)) / 7) | |
91 | #define MON_WEEK(t) (((t)->tm_yday + 7 - \ | |
92 | ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7) | |
15637ed4 RG |
93 | static size_t |
94 | _fmt(format, t) | |
95 | register char *format; | |
96 | struct tm *t; | |
97 | { | |
98 | for (; *format; ++format) { | |
78ed81a3 | 99 | if (*format == '%') { |
100 | ++format; | |
101 | if (*format == 'E') { | |
102 | /* Alternate Era */ | |
103 | ++format; | |
104 | } else if (*format == 'O') { | |
105 | /* Alternate numeric symbols */ | |
106 | ++format; | |
107 | } | |
108 | switch(*format) { | |
15637ed4 RG |
109 | case '\0': |
110 | --format; | |
111 | break; | |
112 | case 'A': | |
113 | if (t->tm_wday < 0 || t->tm_wday > 6) | |
114 | return(0); | |
78ed81a3 | 115 | if (!_add(day[t->tm_wday])) |
15637ed4 RG |
116 | return(0); |
117 | continue; | |
118 | case 'a': | |
119 | if (t->tm_wday < 0 || t->tm_wday > 6) | |
120 | return(0); | |
78ed81a3 | 121 | if (!_add(abday[t->tm_wday])) |
15637ed4 RG |
122 | return(0); |
123 | continue; | |
124 | case 'B': | |
125 | if (t->tm_mon < 0 || t->tm_mon > 11) | |
126 | return(0); | |
78ed81a3 | 127 | if (!_add(mon[t->tm_mon])) |
15637ed4 RG |
128 | return(0); |
129 | continue; | |
130 | case 'b': | |
131 | case 'h': | |
132 | if (t->tm_mon < 0 || t->tm_mon > 11) | |
133 | return(0); | |
78ed81a3 | 134 | if (!_add(abmon[t->tm_mon])) |
15637ed4 RG |
135 | return(0); |
136 | continue; | |
137 | case 'C': | |
78ed81a3 | 138 | if (!_conv((t->tm_year + TM_YEAR_BASE) / 100, |
139 | 2, '0')) | |
15637ed4 RG |
140 | return(0); |
141 | continue; | |
142 | case 'c': | |
78ed81a3 | 143 | if (!_fmt(d_t_fmt, t)) |
15637ed4 RG |
144 | return(0); |
145 | continue; | |
146 | case 'D': | |
147 | if (!_fmt("%m/%d/%y", t)) | |
148 | return(0); | |
149 | continue; | |
150 | case 'd': | |
151 | if (!_conv(t->tm_mday, 2, '0')) | |
152 | return(0); | |
153 | continue; | |
154 | case 'e': | |
155 | if (!_conv(t->tm_mday, 2, ' ')) | |
156 | return(0); | |
157 | continue; | |
158 | case 'H': | |
159 | if (!_conv(t->tm_hour, 2, '0')) | |
160 | return(0); | |
161 | continue; | |
162 | case 'I': | |
163 | if (!_conv(t->tm_hour % 12 ? | |
164 | t->tm_hour % 12 : 12, 2, '0')) | |
165 | return(0); | |
166 | continue; | |
167 | case 'j': | |
168 | if (!_conv(t->tm_yday + 1, 3, '0')) | |
169 | return(0); | |
170 | continue; | |
171 | case 'k': | |
172 | if (!_conv(t->tm_hour, 2, ' ')) | |
173 | return(0); | |
174 | continue; | |
175 | case 'l': | |
176 | if (!_conv(t->tm_hour % 12 ? | |
78ed81a3 | 177 | t->tm_hour % 12: 12, 2, ' ')) |
15637ed4 RG |
178 | return(0); |
179 | continue; | |
180 | case 'M': | |
181 | if (!_conv(t->tm_min, 2, '0')) | |
182 | return(0); | |
183 | continue; | |
184 | case 'm': | |
185 | if (!_conv(t->tm_mon + 1, 2, '0')) | |
186 | return(0); | |
187 | continue; | |
188 | case 'n': | |
189 | if (!_add("\n")) | |
190 | return(0); | |
191 | continue; | |
192 | case 'p': | |
78ed81a3 | 193 | if (!_add(ampm[t->tm_hour >= 12])) |
15637ed4 RG |
194 | return(0); |
195 | continue; | |
196 | case 'R': | |
197 | if (!_fmt("%H:%M", t)) | |
198 | return(0); | |
15637ed4 | 199 | case 'r': |
78ed81a3 | 200 | if (!_fmt(t_fmt_ampm, t)) |
15637ed4 RG |
201 | return(0); |
202 | continue; | |
203 | case 'S': | |
204 | if (!_conv(t->tm_sec, 2, '0')) | |
205 | return(0); | |
206 | continue; | |
207 | case 's': | |
208 | if (!_secs(t)) | |
209 | return(0); | |
210 | continue; | |
211 | case 'T': | |
15637ed4 RG |
212 | if (!_fmt("%H:%M:%S", t)) |
213 | return(0); | |
214 | continue; | |
215 | case 't': | |
216 | if (!_add("\t")) | |
217 | return(0); | |
218 | continue; | |
219 | case 'U': | |
78ed81a3 | 220 | if (!_conv(SUN_WEEK(t), 2, '0')) |
221 | return(0); | |
222 | continue; | |
223 | case 'u': | |
224 | if (!_conv(t->tm_wday ? t->tm_wday : 7, 2, '0')) | |
15637ed4 RG |
225 | return(0); |
226 | continue; | |
78ed81a3 | 227 | case 'V': |
228 | { | |
229 | /* ISO 8601 Week Of Year: | |
230 | If the week (Monday - Sunday) containing | |
231 | January 1 has four or more days in the new | |
232 | year, then it is week 1; otherwise it is | |
233 | week 53 of the previous year and the next | |
234 | week is week one. */ | |
235 | ||
236 | int week = MON_WEEK(t); | |
237 | ||
238 | if (((t->tm_yday + 7 - (t->tm_wday + 1)) % 7) >= 4) { | |
239 | week++; | |
240 | } else if (week == 0) { | |
241 | week = 53; | |
242 | } | |
243 | ||
244 | if (!_conv(week, 2, '0')) | |
245 | return(0); | |
246 | continue; | |
247 | } | |
15637ed4 | 248 | case 'W': |
78ed81a3 | 249 | if (!_conv(MON_WEEK(t), 2, '0')) |
15637ed4 RG |
250 | return(0); |
251 | continue; | |
252 | case 'w': | |
253 | if (!_conv(t->tm_wday, 1, '0')) | |
254 | return(0); | |
255 | continue; | |
256 | case 'x': | |
78ed81a3 | 257 | if (!_fmt(d_fmt, t)) |
258 | return(0); | |
259 | continue; | |
260 | case 'X': | |
261 | if (!_fmt(t_fmt, t)) | |
15637ed4 RG |
262 | return(0); |
263 | continue; | |
264 | case 'y': | |
78ed81a3 | 265 | if (!_conv((t->tm_year + TM_YEAR_BASE) % 100, |
266 | 2, '0')) | |
15637ed4 RG |
267 | return(0); |
268 | continue; | |
269 | case 'Y': | |
78ed81a3 | 270 | if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0')) |
15637ed4 RG |
271 | return(0); |
272 | continue; | |
273 | case 'Z': | |
78ed81a3 | 274 | if (t->tm_zone && !_add(t->tm_zone)) |
15637ed4 RG |
275 | return(0); |
276 | continue; | |
277 | case '%': | |
278 | /* | |
279 | * X311J/88-090 (4.12.3.5): if conversion char is | |
280 | * undefined, behavior is undefined. Print out the | |
281 | * character itself as printf(3) does. | |
282 | */ | |
283 | default: | |
284 | break; | |
78ed81a3 | 285 | } |
15637ed4 RG |
286 | } |
287 | if (!gsize--) | |
288 | return(0); | |
289 | *pt++ = *format; | |
290 | } | |
291 | return(gsize); | |
292 | } | |
293 | ||
294 | static | |
295 | _secs(t) | |
296 | struct tm *t; | |
297 | { | |
298 | static char buf[15]; | |
299 | register time_t s; | |
300 | register char *p; | |
301 | struct tm tmp; | |
302 | ||
303 | /* Make a copy, mktime(3) modifies the tm struct. */ | |
304 | tmp = *t; | |
305 | s = mktime(&tmp); | |
306 | for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10) | |
307 | *p-- = s % 10 + '0'; | |
308 | return(_add(++p)); | |
309 | } | |
310 | ||
311 | static | |
312 | _conv(n, digits, pad) | |
313 | int n, digits; | |
314 | char pad; | |
315 | { | |
316 | static char buf[10]; | |
317 | register char *p; | |
318 | ||
319 | for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits) | |
320 | *p-- = n % 10 + '0'; | |
321 | while (p > buf && digits-- > 0) | |
322 | *p-- = pad; | |
323 | return(_add(++p)); | |
324 | } | |
325 | ||
326 | static | |
327 | _add(str) | |
328 | register char *str; | |
329 | { | |
330 | for (;; ++pt, --gsize) { | |
331 | if (!gsize) | |
332 | return(0); | |
333 | if (!(*pt = *str++)) | |
334 | return(1); | |
335 | } | |
336 | } |