]>
Commit | Line | Data |
---|---|---|
5b2abdfb A |
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 are permitted | |
6 | * provided that the above copyright notice and this paragraph are | |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
1f2f436a | 10 | * by the University of California, Berkeley. The name of the |
5b2abdfb A |
11 | * University may not be used to endorse or promote products derived |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
16 | */ | |
17 | ||
70ad1dc8 A |
18 | #pragma clang diagnostic push |
19 | #pragma clang diagnostic ignored "-Wunreachable-code" | |
20 | ||
b061a43b | 21 | #if 0 |
1f2f436a | 22 | static const char elsieid[] = "@(#)strftime.3 8.3"; |
5b2abdfb A |
23 | /* |
24 | ** Based on the UCB version with the ID appearing below. | |
25 | ** This is ANSIish only when "multibyte character == plain character". | |
26 | */ | |
b061a43b | 27 | #endif |
5b2abdfb | 28 | |
ad3c9f2a A |
29 | #include "xlocale_private.h" |
30 | ||
9385eb3d | 31 | #include "namespace.h" |
5b2abdfb A |
32 | #include "private.h" |
33 | ||
9385eb3d | 34 | #if defined(LIBC_SCCS) && !defined(lint) |
5b2abdfb | 35 | static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; |
9385eb3d A |
36 | #endif /* LIBC_SCCS and not lint */ |
37 | #include <sys/cdefs.h> | |
1f2f436a | 38 | __FBSDID("$FreeBSD: src/lib/libc/stdtime/strftime.c,v 1.44 2009/06/09 09:02:58 delphij Exp $"); |
5b2abdfb A |
39 | |
40 | #include "tzfile.h" | |
ad3c9f2a | 41 | #include <time.h> |
5b2abdfb A |
42 | #include <fcntl.h> |
43 | #include <sys/stat.h> | |
9385eb3d | 44 | #include "un-namespace.h" |
5b2abdfb A |
45 | #include "timelocal.h" |
46 | ||
ad3c9f2a | 47 | #if !BUILDING_VARIANT |
9385eb3d | 48 | static char * _add(const char *, char *, const char *); |
ad3c9f2a A |
49 | static char * _conv(int, const char *, char *, const char *, locale_t); |
50 | static char * _yconv(int, int, int, int, char *, const char *, locale_t); | |
51 | #endif | |
52 | #define _fmt _st_fmt | |
53 | __private_extern__ char * _fmt(const char *, const struct tm *, char *, const char *, | |
54 | int *, struct lc_time_T *, locale_t); | |
5b2abdfb A |
55 | |
56 | extern char * tzname[]; | |
b061a43b | 57 | extern long __darwin_altzone; /* DST timezone offset */ |
ad3c9f2a A |
58 | #define altzone __darwin_altzone |
59 | __private_extern__ long _st_get_timezone(void); | |
5b2abdfb | 60 | |
3d9156a7 A |
61 | #ifndef YEAR_2000_NAME |
62 | #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" | |
63 | #endif /* !defined YEAR_2000_NAME */ | |
64 | ||
3d9156a7 A |
65 | #define IN_NONE 0 |
66 | #define IN_SOME 1 | |
67 | #define IN_THIS 2 | |
68 | #define IN_ALL 3 | |
69 | ||
1f2f436a A |
70 | #define PAD_DEFAULT 0 |
71 | #define PAD_LESS 1 | |
72 | #define PAD_SPACE 2 | |
73 | #define PAD_ZERO 3 | |
74 | ||
ad3c9f2a | 75 | #ifndef BUILDING_VARIANT |
6465356a | 76 | static const char * const fmt_padding[][4] = { |
1f2f436a A |
77 | /* DEFAULT, LESS, SPACE, ZERO */ |
78 | #define PAD_FMT_MONTHDAY 0 | |
79 | #define PAD_FMT_HMS 0 | |
80 | #define PAD_FMT_CENTURY 0 | |
81 | #define PAD_FMT_SHORTYEAR 0 | |
82 | #define PAD_FMT_MONTH 0 | |
83 | #define PAD_FMT_WEEKOFYEAR 0 | |
84 | #define PAD_FMT_DAYOFMONTH 0 | |
85 | { "%02d", "%d", "%2d", "%02d" }, | |
86 | #define PAD_FMT_SDAYOFMONTH 1 | |
87 | #define PAD_FMT_SHMS 1 | |
88 | { "%2d", "%d", "%2d", "%02d" }, | |
89 | #define PAD_FMT_DAYOFYEAR 2 | |
90 | { "%03d", "%d", "%3d", "%03d" }, | |
91 | #define PAD_FMT_YEAR 3 | |
92 | { "%04d", "%d", "%4d", "%04d" } | |
93 | }; | |
ad3c9f2a A |
94 | #endif |
95 | ||
96 | #define USG_COMPAT | |
97 | #define ALTZONE | |
1f2f436a | 98 | |
5b2abdfb | 99 | size_t |
ad3c9f2a A |
100 | strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format, |
101 | const struct tm * __restrict t, locale_t loc) | |
5b2abdfb | 102 | { |
3d9156a7 A |
103 | char * p; |
104 | int warn; | |
5b2abdfb | 105 | |
ad3c9f2a | 106 | NORMALIZE_LOCALE(loc); |
5b2abdfb | 107 | tzset(); |
3d9156a7 | 108 | warn = IN_NONE; |
ad3c9f2a | 109 | p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, __get_current_time_locale(loc), loc); |
3d9156a7 A |
110 | #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU |
111 | if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { | |
ad3c9f2a | 112 | (void) fputs("\n", stderr); |
3d9156a7 | 113 | if (format == NULL) |
ad3c9f2a A |
114 | (void) fputs("NULL strftime format ", stderr); |
115 | else (void) fprintf_l(stderr, loc, "strftime format \"%s\" ", | |
3d9156a7 | 116 | format); |
ad3c9f2a | 117 | (void) fputs("yields only two digits of years in ", stderr); |
3d9156a7 | 118 | if (warn == IN_SOME) |
ad3c9f2a | 119 | (void) fputs("some locales", stderr); |
3d9156a7 | 120 | else if (warn == IN_THIS) |
ad3c9f2a A |
121 | (void) fputs("the current locale", stderr); |
122 | else (void) fputs("all locales", stderr); | |
123 | (void) fputs("\n", stderr); | |
3d9156a7 A |
124 | } |
125 | #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ | |
5b2abdfb A |
126 | if (p == s + maxsize) |
127 | return 0; | |
128 | *p = '\0'; | |
129 | return p - s; | |
130 | } | |
131 | ||
ad3c9f2a A |
132 | size_t |
133 | strftime(char * __restrict s, size_t maxsize, const char * __restrict format, | |
134 | const struct tm * __restrict t) | |
135 | { | |
136 | return strftime_l(s, maxsize, format, t, __current_locale()); | |
137 | } | |
138 | ||
139 | #ifndef BUILDING_VARIANT | |
140 | __private_extern__ char * | |
141 | _fmt(format, t, pt, ptlim, warnp, tptr, loc) | |
3d9156a7 A |
142 | const char * format; |
143 | const struct tm * const t; | |
144 | char * pt; | |
145 | const char * const ptlim; | |
146 | int * warnp; | |
ad3c9f2a A |
147 | struct lc_time_T * tptr; |
148 | locale_t loc; | |
5b2abdfb | 149 | { |
1f2f436a | 150 | int Ealternative, Oalternative, PadIndex; |
5b2abdfb A |
151 | |
152 | for ( ; *format; ++format) { | |
153 | if (*format == '%') { | |
154 | Ealternative = 0; | |
155 | Oalternative = 0; | |
1f2f436a | 156 | PadIndex = PAD_DEFAULT; |
5b2abdfb A |
157 | label: |
158 | switch (*++format) { | |
159 | case '\0': | |
160 | --format; | |
161 | break; | |
162 | case 'A': | |
3d9156a7 A |
163 | pt = _add((t->tm_wday < 0 || |
164 | t->tm_wday >= DAYSPERWEEK) ? | |
9385eb3d | 165 | "?" : tptr->weekday[t->tm_wday], |
5b2abdfb A |
166 | pt, ptlim); |
167 | continue; | |
168 | case 'a': | |
3d9156a7 A |
169 | pt = _add((t->tm_wday < 0 || |
170 | t->tm_wday >= DAYSPERWEEK) ? | |
9385eb3d | 171 | "?" : tptr->wday[t->tm_wday], |
5b2abdfb A |
172 | pt, ptlim); |
173 | continue; | |
174 | case 'B': | |
3d9156a7 A |
175 | pt = _add((t->tm_mon < 0 || |
176 | t->tm_mon >= MONSPERYEAR) ? | |
9385eb3d A |
177 | "?" : (Oalternative ? tptr->alt_month : |
178 | tptr->month)[t->tm_mon], | |
5b2abdfb A |
179 | pt, ptlim); |
180 | continue; | |
181 | case 'b': | |
182 | case 'h': | |
3d9156a7 A |
183 | pt = _add((t->tm_mon < 0 || |
184 | t->tm_mon >= MONSPERYEAR) ? | |
9385eb3d | 185 | "?" : tptr->mon[t->tm_mon], |
5b2abdfb A |
186 | pt, ptlim); |
187 | continue; | |
188 | case 'C': | |
189 | /* | |
190 | ** %C used to do a... | |
ad3c9f2a | 191 | ** _fmt("%a %b %e %X %Y", t, tptr, loc); |
5b2abdfb A |
192 | ** ...whereas now POSIX 1003.2 calls for |
193 | ** something completely different. | |
3d9156a7 | 194 | ** (ado, 1993-05-24) |
5b2abdfb | 195 | */ |
1f2f436a | 196 | pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, |
ad3c9f2a | 197 | pt, ptlim, loc); |
5b2abdfb A |
198 | continue; |
199 | case 'c': | |
3d9156a7 A |
200 | { |
201 | int warn2 = IN_SOME; | |
202 | ||
ad3c9f2a | 203 | pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2, tptr, loc); |
3d9156a7 A |
204 | if (warn2 == IN_ALL) |
205 | warn2 = IN_THIS; | |
206 | if (warn2 > *warnp) | |
207 | *warnp = warn2; | |
208 | } | |
5b2abdfb A |
209 | continue; |
210 | case 'D': | |
ad3c9f2a | 211 | pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, tptr, loc); |
5b2abdfb A |
212 | continue; |
213 | case 'd': | |
1f2f436a | 214 | pt = _conv(t->tm_mday, fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex], |
ad3c9f2a | 215 | pt, ptlim, loc); |
5b2abdfb A |
216 | continue; |
217 | case 'E': | |
218 | if (Ealternative || Oalternative) | |
219 | break; | |
220 | Ealternative++; | |
221 | goto label; | |
222 | case 'O': | |
223 | /* | |
3d9156a7 | 224 | ** C99 locale modifiers. |
5b2abdfb | 225 | ** The sequences |
3d9156a7 | 226 | ** %Ec %EC %Ex %EX %Ey %EY |
5b2abdfb A |
227 | ** %Od %oe %OH %OI %Om %OM |
228 | ** %OS %Ou %OU %OV %Ow %OW %Oy | |
229 | ** are supposed to provide alternate | |
230 | ** representations. | |
5b2abdfb | 231 | ** |
3d9156a7 A |
232 | ** FreeBSD extension |
233 | ** %OB | |
5b2abdfb A |
234 | */ |
235 | if (Ealternative || Oalternative) | |
236 | break; | |
237 | Oalternative++; | |
238 | goto label; | |
239 | case 'e': | |
1f2f436a | 240 | pt = _conv(t->tm_mday, |
ad3c9f2a | 241 | fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], pt, ptlim, loc); |
5b2abdfb | 242 | continue; |
5b2abdfb | 243 | case 'F': |
ad3c9f2a | 244 | pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, tptr, loc); |
5b2abdfb A |
245 | continue; |
246 | case 'H': | |
1f2f436a | 247 | pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex], |
ad3c9f2a | 248 | pt, ptlim, loc); |
5b2abdfb A |
249 | continue; |
250 | case 'I': | |
251 | pt = _conv((t->tm_hour % 12) ? | |
252 | (t->tm_hour % 12) : 12, | |
ad3c9f2a | 253 | fmt_padding[PAD_FMT_HMS][PadIndex], pt, ptlim, loc); |
5b2abdfb A |
254 | continue; |
255 | case 'j': | |
1f2f436a | 256 | pt = _conv(t->tm_yday + 1, |
ad3c9f2a | 257 | fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], pt, ptlim, loc); |
5b2abdfb A |
258 | continue; |
259 | case 'k': | |
260 | /* | |
261 | ** This used to be... | |
262 | ** _conv(t->tm_hour % 12 ? | |
ad3c9f2a | 263 | ** t->tm_hour % 12 : 12, 2, ' ', loc); |
5b2abdfb A |
264 | ** ...and has been changed to the below to |
265 | ** match SunOS 4.1.1 and Arnold Robbins' | |
1f2f436a | 266 | ** strftime version 3.0. That is, "%k" and |
5b2abdfb | 267 | ** "%l" have been swapped. |
3d9156a7 | 268 | ** (ado, 1993-05-24) |
5b2abdfb | 269 | */ |
1f2f436a | 270 | pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex], |
ad3c9f2a | 271 | pt, ptlim, loc); |
5b2abdfb A |
272 | continue; |
273 | #ifdef KITCHEN_SINK | |
274 | case 'K': | |
275 | /* | |
276 | ** After all this time, still unclaimed! | |
277 | */ | |
278 | pt = _add("kitchen sink", pt, ptlim); | |
279 | continue; | |
280 | #endif /* defined KITCHEN_SINK */ | |
281 | case 'l': | |
282 | /* | |
283 | ** This used to be... | |
284 | ** _conv(t->tm_hour, 2, ' '); | |
285 | ** ...and has been changed to the below to | |
286 | ** match SunOS 4.1.1 and Arnold Robbin's | |
1f2f436a | 287 | ** strftime version 3.0. That is, "%k" and |
5b2abdfb | 288 | ** "%l" have been swapped. |
3d9156a7 | 289 | ** (ado, 1993-05-24) |
5b2abdfb A |
290 | */ |
291 | pt = _conv((t->tm_hour % 12) ? | |
292 | (t->tm_hour % 12) : 12, | |
ad3c9f2a | 293 | fmt_padding[PAD_FMT_SHMS][PadIndex], pt, ptlim, loc); |
5b2abdfb A |
294 | continue; |
295 | case 'M': | |
1f2f436a | 296 | pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex], |
ad3c9f2a | 297 | pt, ptlim, loc); |
5b2abdfb A |
298 | continue; |
299 | case 'm': | |
1f2f436a | 300 | pt = _conv(t->tm_mon + 1, |
ad3c9f2a | 301 | fmt_padding[PAD_FMT_MONTH][PadIndex], pt, ptlim, loc); |
5b2abdfb A |
302 | continue; |
303 | case 'n': | |
304 | pt = _add("\n", pt, ptlim); | |
305 | continue; | |
306 | case 'p': | |
3d9156a7 | 307 | pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? |
9385eb3d A |
308 | tptr->pm : |
309 | tptr->am, | |
5b2abdfb A |
310 | pt, ptlim); |
311 | continue; | |
312 | case 'R': | |
ad3c9f2a | 313 | pt = _fmt("%H:%M", t, pt, ptlim, warnp, tptr, loc); |
5b2abdfb A |
314 | continue; |
315 | case 'r': | |
3d9156a7 | 316 | pt = _fmt(tptr->ampm_fmt, t, pt, ptlim, |
ad3c9f2a | 317 | warnp, tptr, loc); |
5b2abdfb A |
318 | continue; |
319 | case 'S': | |
1f2f436a | 320 | pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex], |
ad3c9f2a | 321 | pt, ptlim, loc); |
5b2abdfb A |
322 | continue; |
323 | case 's': | |
324 | { | |
325 | struct tm tm; | |
326 | char buf[INT_STRLEN_MAXIMUM( | |
327 | time_t) + 1]; | |
328 | time_t mkt; | |
329 | ||
330 | tm = *t; | |
331 | mkt = mktime(&tm); | |
332 | if (TYPE_SIGNED(time_t)) | |
ad3c9f2a | 333 | (void) sprintf_l(buf, loc, "%ld", |
5b2abdfb | 334 | (long) mkt); |
ad3c9f2a | 335 | else (void) sprintf_l(buf, loc, "%lu", |
5b2abdfb A |
336 | (unsigned long) mkt); |
337 | pt = _add(buf, pt, ptlim); | |
338 | } | |
339 | continue; | |
340 | case 'T': | |
ad3c9f2a | 341 | pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, tptr, loc); |
5b2abdfb A |
342 | continue; |
343 | case 't': | |
344 | pt = _add("\t", pt, ptlim); | |
345 | continue; | |
346 | case 'U': | |
3d9156a7 A |
347 | pt = _conv((t->tm_yday + DAYSPERWEEK - |
348 | t->tm_wday) / DAYSPERWEEK, | |
ad3c9f2a | 349 | fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim, loc); |
5b2abdfb A |
350 | continue; |
351 | case 'u': | |
352 | /* | |
353 | ** From Arnold Robbins' strftime version 3.0: | |
354 | ** "ISO 8601: Weekday as a decimal number | |
355 | ** [1 (Monday) - 7]" | |
3d9156a7 | 356 | ** (ado, 1993-05-24) |
5b2abdfb | 357 | */ |
3d9156a7 A |
358 | pt = _conv((t->tm_wday == 0) ? |
359 | DAYSPERWEEK : t->tm_wday, | |
ad3c9f2a | 360 | "%d", pt, ptlim, loc); |
5b2abdfb A |
361 | continue; |
362 | case 'V': /* ISO 8601 week number */ | |
363 | case 'G': /* ISO 8601 year (four digits) */ | |
364 | case 'g': /* ISO 8601 year (two digits) */ | |
365 | /* | |
1f2f436a | 366 | ** From Arnold Robbins' strftime version 3.0: "the week number of the |
5b2abdfb A |
367 | ** year (the first Monday as the first day of week 1) as a decimal number |
368 | ** (01-53)." | |
369 | ** (ado, 1993-05-24) | |
370 | ** | |
371 | ** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: | |
372 | ** "Week 01 of a year is per definition the first week which has the | |
373 | ** Thursday in this year, which is equivalent to the week which contains | |
374 | ** the fourth day of January. In other words, the first week of a new year | |
375 | ** is the week which has the majority of its days in the new year. Week 01 | |
376 | ** might also contain days from the previous year and the week before week | |
377 | ** 01 of a year is the last week (52 or 53) of the previous year even if | |
378 | ** it contains days from the new year. A week starts with Monday (day 1) | |
1f2f436a | 379 | ** and ends with Sunday (day 7). For example, the first week of the year |
5b2abdfb A |
380 | ** 1997 lasts from 1996-12-30 to 1997-01-05..." |
381 | ** (ado, 1996-01-02) | |
382 | */ | |
383 | { | |
384 | int year; | |
1f2f436a | 385 | int base; |
5b2abdfb A |
386 | int yday; |
387 | int wday; | |
388 | int w; | |
389 | ||
1f2f436a A |
390 | year = t->tm_year; |
391 | base = TM_YEAR_BASE; | |
5b2abdfb A |
392 | yday = t->tm_yday; |
393 | wday = t->tm_wday; | |
394 | for ( ; ; ) { | |
395 | int len; | |
396 | int bot; | |
397 | int top; | |
398 | ||
1f2f436a | 399 | len = isleap_sum(year, base) ? |
5b2abdfb A |
400 | DAYSPERLYEAR : |
401 | DAYSPERNYEAR; | |
402 | /* | |
403 | ** What yday (-3 ... 3) does | |
404 | ** the ISO year begin on? | |
405 | */ | |
406 | bot = ((yday + 11 - wday) % | |
407 | DAYSPERWEEK) - 3; | |
408 | /* | |
409 | ** What yday does the NEXT | |
410 | ** ISO year begin on? | |
411 | */ | |
412 | top = bot - | |
413 | (len % DAYSPERWEEK); | |
414 | if (top < -3) | |
415 | top += DAYSPERWEEK; | |
416 | top += len; | |
417 | if (yday >= top) { | |
1f2f436a | 418 | ++base; |
5b2abdfb A |
419 | w = 1; |
420 | break; | |
421 | } | |
422 | if (yday >= bot) { | |
423 | w = 1 + ((yday - bot) / | |
424 | DAYSPERWEEK); | |
425 | break; | |
426 | } | |
1f2f436a A |
427 | --base; |
428 | yday += isleap_sum(year, base) ? | |
5b2abdfb A |
429 | DAYSPERLYEAR : |
430 | DAYSPERNYEAR; | |
431 | } | |
432 | #ifdef XPG4_1994_04_09 | |
1f2f436a A |
433 | if ((w == 52 && |
434 | t->tm_mon == TM_JANUARY) || | |
435 | (w == 1 && | |
436 | t->tm_mon == TM_DECEMBER)) | |
437 | w = 53; | |
5b2abdfb A |
438 | #endif /* defined XPG4_1994_04_09 */ |
439 | if (*format == 'V') | |
1f2f436a | 440 | pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], |
ad3c9f2a | 441 | pt, ptlim, loc); |
5b2abdfb | 442 | else if (*format == 'g') { |
3d9156a7 | 443 | *warnp = IN_ALL; |
1f2f436a | 444 | pt = _yconv(year, base, 0, 1, |
ad3c9f2a | 445 | pt, ptlim, loc); |
1f2f436a | 446 | } else pt = _yconv(year, base, 1, 1, |
ad3c9f2a | 447 | pt, ptlim, loc); |
5b2abdfb A |
448 | } |
449 | continue; | |
450 | case 'v': | |
451 | /* | |
452 | ** From Arnold Robbins' strftime version 3.0: | |
453 | ** "date as dd-bbb-YYYY" | |
3d9156a7 | 454 | ** (ado, 1993-05-24) |
5b2abdfb | 455 | */ |
ad3c9f2a | 456 | pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, tptr, loc); |
5b2abdfb A |
457 | continue; |
458 | case 'W': | |
3d9156a7 | 459 | pt = _conv((t->tm_yday + DAYSPERWEEK - |
5b2abdfb | 460 | (t->tm_wday ? |
3d9156a7 A |
461 | (t->tm_wday - 1) : |
462 | (DAYSPERWEEK - 1))) / DAYSPERWEEK, | |
ad3c9f2a | 463 | fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim, loc); |
5b2abdfb A |
464 | continue; |
465 | case 'w': | |
ad3c9f2a | 466 | pt = _conv(t->tm_wday, "%d", pt, ptlim, loc); |
5b2abdfb A |
467 | continue; |
468 | case 'X': | |
ad3c9f2a | 469 | pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp, tptr, loc); |
5b2abdfb A |
470 | continue; |
471 | case 'x': | |
3d9156a7 A |
472 | { |
473 | int warn2 = IN_SOME; | |
474 | ||
ad3c9f2a | 475 | pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2, tptr, loc); |
3d9156a7 A |
476 | if (warn2 == IN_ALL) |
477 | warn2 = IN_THIS; | |
478 | if (warn2 > *warnp) | |
479 | *warnp = warn2; | |
480 | } | |
5b2abdfb A |
481 | continue; |
482 | case 'y': | |
3d9156a7 | 483 | *warnp = IN_ALL; |
1f2f436a | 484 | pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, |
ad3c9f2a | 485 | pt, ptlim, loc); |
5b2abdfb A |
486 | continue; |
487 | case 'Y': | |
1f2f436a | 488 | pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, |
ad3c9f2a | 489 | pt, ptlim, loc); |
5b2abdfb A |
490 | continue; |
491 | case 'Z': | |
3d9156a7 A |
492 | #ifdef TM_ZONE |
493 | if (t->TM_ZONE != NULL) | |
494 | pt = _add(t->TM_ZONE, pt, ptlim); | |
5b2abdfb | 495 | else |
3d9156a7 A |
496 | #endif /* defined TM_ZONE */ |
497 | if (t->tm_isdst >= 0) | |
498 | pt = _add(tzname[t->tm_isdst != 0], | |
5b2abdfb | 499 | pt, ptlim); |
3d9156a7 A |
500 | /* |
501 | ** C99 says that %Z must be replaced by the | |
502 | ** empty string if the time zone is not | |
503 | ** determinable. | |
504 | */ | |
5b2abdfb A |
505 | continue; |
506 | case 'z': | |
507 | { | |
3d9156a7 A |
508 | int diff; |
509 | char const * sign; | |
510 | ||
511 | if (t->tm_isdst < 0) | |
512 | continue; | |
ad3c9f2a | 513 | #if defined(TM_GMTOFF) && !__DARWIN_UNIX03 |
3d9156a7 | 514 | diff = t->TM_GMTOFF; |
ad3c9f2a | 515 | #else /* !defined TM_GMTOFF || __DARWIN_UNIX03 */ |
3d9156a7 A |
516 | /* |
517 | ** C99 says that the UTC offset must | |
518 | ** be computed by looking only at | |
1f2f436a | 519 | ** tm_isdst. This requirement is |
3d9156a7 A |
520 | ** incorrect, since it means the code |
521 | ** must rely on magic (in this case | |
522 | ** altzone and timezone), and the | |
523 | ** magic might not have the correct | |
1f2f436a | 524 | ** offset. Doing things correctly is |
3d9156a7 A |
525 | ** tricky and requires disobeying C99; |
526 | ** see GNU C strftime for details. | |
527 | ** For now, punt and conform to the | |
528 | ** standard, even though it's incorrect. | |
529 | ** | |
530 | ** C99 says that %z must be replaced by the | |
531 | ** empty string if the time zone is not | |
532 | ** determinable, so output nothing if the | |
533 | ** appropriate variables are not available. | |
534 | */ | |
535 | if (t->tm_isdst == 0) | |
536 | #ifdef USG_COMPAT | |
ad3c9f2a | 537 | diff = -_st_get_timezone(); |
3d9156a7 A |
538 | #else /* !defined USG_COMPAT */ |
539 | continue; | |
540 | #endif /* !defined USG_COMPAT */ | |
541 | else | |
542 | #ifdef ALTZONE | |
543 | diff = -altzone; | |
544 | #else /* !defined ALTZONE */ | |
545 | continue; | |
546 | #endif /* !defined ALTZONE */ | |
ad3c9f2a | 547 | #endif /* !defined TM_GMTOFF || __DARWIN_UNIX03 */ |
3d9156a7 A |
548 | if (diff < 0) { |
549 | sign = "-"; | |
550 | diff = -diff; | |
551 | } else sign = "+"; | |
552 | pt = _add(sign, pt, ptlim); | |
1f2f436a A |
553 | diff /= SECSPERMIN; |
554 | diff = (diff / MINSPERHOUR) * 100 + | |
555 | (diff % MINSPERHOUR); | |
556 | pt = _conv(diff, | |
ad3c9f2a | 557 | fmt_padding[PAD_FMT_YEAR][PadIndex], pt, ptlim, loc); |
3d9156a7 | 558 | } |
5b2abdfb A |
559 | continue; |
560 | case '+': | |
3d9156a7 | 561 | pt = _fmt(tptr->date_fmt, t, pt, ptlim, |
ad3c9f2a | 562 | warnp, tptr, loc); |
5b2abdfb | 563 | continue; |
1f2f436a A |
564 | case '-': |
565 | if (PadIndex != PAD_DEFAULT) | |
566 | break; | |
567 | PadIndex = PAD_LESS; | |
568 | goto label; | |
569 | case '_': | |
570 | if (PadIndex != PAD_DEFAULT) | |
571 | break; | |
572 | PadIndex = PAD_SPACE; | |
573 | goto label; | |
574 | case '0': | |
575 | if (PadIndex != PAD_DEFAULT) | |
576 | break; | |
577 | PadIndex = PAD_ZERO; | |
578 | goto label; | |
5b2abdfb A |
579 | case '%': |
580 | /* | |
3d9156a7 | 581 | ** X311J/88-090 (4.12.3.5): if conversion char is |
1f2f436a | 582 | ** undefined, behavior is undefined. Print out the |
3d9156a7 A |
583 | ** character itself as printf(3) also does. |
584 | */ | |
5b2abdfb A |
585 | default: |
586 | break; | |
587 | } | |
588 | } | |
589 | if (pt == ptlim) | |
590 | break; | |
591 | *pt++ = *format; | |
592 | } | |
593 | return pt; | |
594 | } | |
595 | ||
596 | static char * | |
ad3c9f2a | 597 | _conv(n, format, pt, ptlim, loc) |
3d9156a7 A |
598 | const int n; |
599 | const char * const format; | |
600 | char * const pt; | |
601 | const char * const ptlim; | |
ad3c9f2a | 602 | locale_t loc; |
5b2abdfb A |
603 | { |
604 | char buf[INT_STRLEN_MAXIMUM(int) + 1]; | |
605 | ||
ad3c9f2a | 606 | (void) sprintf_l(buf, loc, format, n); |
5b2abdfb A |
607 | return _add(buf, pt, ptlim); |
608 | } | |
609 | ||
610 | static char * | |
611 | _add(str, pt, ptlim) | |
3d9156a7 A |
612 | const char * str; |
613 | char * pt; | |
614 | const char * const ptlim; | |
5b2abdfb A |
615 | { |
616 | while (pt < ptlim && (*pt = *str++) != '\0') | |
617 | ++pt; | |
618 | return pt; | |
619 | } | |
1f2f436a A |
620 | |
621 | /* | |
622 | ** POSIX and the C Standard are unclear or inconsistent about | |
623 | ** what %C and %y do if the year is negative or exceeds 9999. | |
624 | ** Use the convention that %C concatenated with %y yields the | |
625 | ** same output as %Y, and that %Y contains at least 4 bytes, | |
626 | ** with more only if necessary. | |
627 | */ | |
628 | ||
629 | static char * | |
ad3c9f2a | 630 | _yconv(a, b, convert_top, convert_yy, pt, ptlim, loc) |
1f2f436a A |
631 | const int a; |
632 | const int b; | |
633 | const int convert_top; | |
634 | const int convert_yy; | |
635 | char * pt; | |
636 | const char * const ptlim; | |
ad3c9f2a | 637 | locale_t loc; |
1f2f436a A |
638 | { |
639 | register int lead; | |
640 | register int trail; | |
641 | ||
642 | #define DIVISOR 100 | |
643 | trail = a % DIVISOR + b % DIVISOR; | |
644 | lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; | |
645 | trail %= DIVISOR; | |
646 | if (trail < 0 && lead > 0) { | |
647 | trail += DIVISOR; | |
648 | --lead; | |
649 | } else if (lead < 0 && trail > 0) { | |
650 | trail -= DIVISOR; | |
651 | ++lead; | |
652 | } | |
653 | if (convert_top) { | |
654 | if (lead == 0 && trail < 0) | |
655 | pt = _add("-0", pt, ptlim); | |
ad3c9f2a | 656 | else pt = _conv(lead, "%02d", pt, ptlim, loc); |
1f2f436a A |
657 | } |
658 | if (convert_yy) | |
ad3c9f2a | 659 | pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim, loc); |
1f2f436a A |
660 | return pt; |
661 | } | |
ad3c9f2a | 662 | #endif /* !BUILDING_VARIANT */ |
70ad1dc8 | 663 | #pragma clang diagnostic pop |