]> git.ipfire.org Git - thirdparty/glibc.git/blame - time/strftime_l.c
Implement alternative month names (bug 10871).
[thirdparty/glibc.git] / time / strftime_l.c
CommitLineData
688903eb 1/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
c4d6f155
RM
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
59ba27a6
PE
15 License along with the GNU C Library; if not, see
16 <http://www.gnu.org/licenses/>. */
c4d6f155 17
ccadf7b5
UD
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
c4d6f155 21
ccadf7b5
UD
22#ifdef _LIBC
23# define USE_IN_EXTENDED_LOCALE_MODEL 1
24# define HAVE_LIMITS_H 1
25# define HAVE_MBLEN 1
26# define HAVE_MBRLEN 1
27# define HAVE_STRUCT_ERA_ENTRY 1
28# define HAVE_TM_GMTOFF 1
29# define HAVE_TM_ZONE 1
30# define HAVE_TZNAME 1
31# define HAVE_TZSET 1
d22f1fe2 32# define HAVE_STRFTIME 0
ccadf7b5
UD
33# define MULTIBYTE_IS_FORMAT_SAFE 1
34# define STDC_HEADERS 1
35# include "../locale/localeinfo.h"
36#endif
37
38#if defined emacs && !defined HAVE_BCOPY
39# define HAVE_MEMCPY 1
40#endif
41
42#include <ctype.h>
43#include <sys/types.h> /* Some systems define `time_t' here. */
44
45#ifdef TIME_WITH_SYS_TIME
46# include <sys/time.h>
47# include <time.h>
48#else
49# ifdef HAVE_SYS_TIME_H
50# include <sys/time.h>
51# else
52# include <time.h>
53# endif
54#endif
55#if HAVE_TZNAME
56extern char *tzname[];
57#endif
58
59/* Do multibyte processing if multibytes are supported, unless
60 multibyte sequences are safe in formats. Multibyte sequences are
61 safe if they cannot contain byte sequences that look like format
62 conversion specifications. The GNU C Library uses UTF8 multibyte
63 encoding, which is safe for formats, but strftime.c can be used
64 with other C libraries that use unsafe encodings. */
65#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
66
67#if DO_MULTIBYTE
68# if HAVE_MBRLEN
69# include <wchar.h>
70# else
71 /* Simulate mbrlen with mblen as best we can. */
72# define mbstate_t int
73# define mbrlen(s, n, ps) mblen (s, n)
74# define mbsinit(ps) (*(ps) == 0)
75# endif
76 static const mbstate_t mbstate_zero;
77#endif
78
79#if HAVE_LIMITS_H
80# include <limits.h>
81#endif
82
83#if STDC_HEADERS
84# include <stddef.h>
85# include <stdlib.h>
86# include <string.h>
800d6f9e 87# include <stdbool.h>
ccadf7b5
UD
88#else
89# ifndef HAVE_MEMCPY
90# define memcpy(d, s, n) bcopy ((s), (d), (n))
91# endif
92#endif
93
94#ifdef COMPILE_WIDE
95# include <endian.h>
96# define CHAR_T wchar_t
97# define UCHAR_T unsigned int
98# define L_(Str) L##Str
99# define NLW(Sym) _NL_W##Sym
100
101# define MEMCPY(d, s, n) __wmemcpy (d, s, n)
102# define STRLEN(s) __wcslen (s)
103
104#else
105# define CHAR_T char
106# define UCHAR_T unsigned char
107# define L_(Str) Str
108# define NLW(Sym) Sym
109
110# if !defined STDC_HEADERS && !defined HAVE_MEMCPY
111# define MEMCPY(d, s, n) bcopy ((s), (d), (n))
112# else
113# define MEMCPY(d, s, n) memcpy ((d), (s), (n))
114# endif
115# define STRLEN(s) strlen (s)
116
117# ifdef _LIBC
118# define MEMPCPY(d, s, n) __mempcpy (d, s, n)
119# else
120# ifndef HAVE_MEMPCPY
121# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
122# endif
123# endif
124#endif
125
ccadf7b5 126#ifndef PTR
c8a89e7d 127# define PTR void *
ccadf7b5
UD
128#endif
129
130#ifndef CHAR_BIT
131# define CHAR_BIT 8
132#endif
133
134#ifndef NULL
135# define NULL 0
136#endif
137
138#define TYPE_SIGNED(t) ((t) -1 < 0)
139
140/* Bound on length of the string representing an integer value of type t.
141 Subtract one for the sign bit if t is signed;
142 302 / 1000 is log10 (2) rounded up;
143 add one for integer division truncation;
144 add one more for a minus sign if t is signed. */
145#define INT_STRLEN_BOUND(t) \
146 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
147
148#define TM_YEAR_BASE 1900
149
150#ifndef __isleap
151/* Nonzero if YEAR is a leap year (every 4 years,
152 except every 100th isn't, and every 400th is). */
153# define __isleap(year) \
154 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
155#endif
156
157
158#ifdef _LIBC
159# define tzname __tzname
160# define tzset __tzset
161#endif
162
163#if !HAVE_TM_GMTOFF
164/* Portable standalone applications should supply a "time_r.h" that
165 declares a POSIX-compliant localtime_r, for the benefit of older
166 implementations that lack localtime_r or have a nonstandard one.
167 Similarly for gmtime_r. See the gnulib time_r module for one way
168 to implement this. */
169# include "time_r.h"
170# undef __gmtime_r
171# undef __localtime_r
172# define __gmtime_r gmtime_r
173# define __localtime_r localtime_r
174#endif
175
176
177#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
178/* Some systems lack the `memset' function and we don't want to
179 introduce additional dependencies. */
180/* The SGI compiler reportedly barfs on the trailing null
181 if we use a string constant as the initializer. 28 June 1997, rms. */
182static const CHAR_T spaces[16] = /* " " */
183{
184 L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),
185 L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' ')
186};
187static const CHAR_T zeroes[16] = /* "0000000000000000" */
188{
189 L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),
190 L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0')
191};
192
193# define memset_space(P, Len) \
194 do { \
195 int _len = (Len); \
196 \
197 do \
198 { \
199 int _this = _len > 16 ? 16 : _len; \
200 (P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T)); \
201 _len -= _this; \
202 } \
203 while (_len > 0); \
204 } while (0)
205
206# define memset_zero(P, Len) \
207 do { \
208 int _len = (Len); \
209 \
210 do \
211 { \
212 int _this = _len > 16 ? 16 : _len; \
213 (P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T)); \
214 _len -= _this; \
215 } \
216 while (_len > 0); \
217 } while (0)
218#else
219# ifdef COMPILE_WIDE
220# define memset_space(P, Len) (wmemset ((P), L' ', (Len)), (P) += (Len))
221# define memset_zero(P, Len) (wmemset ((P), L'0', (Len)), (P) += (Len))
222# else
223# define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
224# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
225# endif
226#endif
227
228#define add(n, f) \
229 do \
230 { \
231 int _n = (n); \
232 int _delta = width - _n; \
233 int _incr = _n + (_delta > 0 ? _delta : 0); \
234 if ((size_t) _incr >= maxsize - i) \
235 return 0; \
236 if (p) \
237 { \
238 if (_delta > 0) \
239 { \
240 if (pad == L_('0')) \
241 memset_zero (p, _delta); \
242 else \
243 memset_space (p, _delta); \
244 } \
245 f; \
246 p += _n; \
247 } \
248 i += _incr; \
249 } while (0)
250
251#define cpy(n, s) \
252 add ((n), \
253 if (to_lowcase) \
254 memcpy_lowcase (p, (s), _n LOCALE_ARG); \
255 else if (to_uppcase) \
256 memcpy_uppcase (p, (s), _n LOCALE_ARG); \
257 else \
258 MEMCPY ((PTR) p, (const PTR) (s), _n))
259
260#ifdef COMPILE_WIDE
261# ifndef USE_IN_EXTENDED_LOCALE_MODEL
262# undef __mbsrtowcs_l
263# define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
264# endif
265# define widen(os, ws, l) \
266 { \
267 mbstate_t __st; \
268 const char *__s = os; \
269 memset (&__st, '\0', sizeof (__st)); \
270 l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc); \
271 ws = alloca ((l + 1) * sizeof (wchar_t)); \
272 (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc); \
273 }
274#endif
275
276
277#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
278/* We use this code also for the extended locale handling where the
279 function gets as an additional argument the locale which has to be
280 used. To access the values we have to redefine the _NL_CURRENT
281 macro. */
282# define strftime __strftime_l
283# define wcsftime __wcsftime_l
284# undef _NL_CURRENT
285# define _NL_CURRENT(category, item) \
286 (current->values[_NL_ITEM_INDEX (item)].string)
af85385f 287# define LOCALE_PARAM , locale_t loc
ccadf7b5 288# define LOCALE_ARG , loc
ccadf7b5
UD
289# define HELPER_LOCALE_ARG , current
290#else
291# define LOCALE_PARAM
ccadf7b5 292# define LOCALE_ARG
ccadf7b5
UD
293# ifdef _LIBC
294# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
295# else
296# define HELPER_LOCALE_ARG
297# endif
298#endif
299
300#ifdef COMPILE_WIDE
301# ifdef USE_IN_EXTENDED_LOCALE_MODEL
302# define TOUPPER(Ch, L) __towupper_l (Ch, L)
303# define TOLOWER(Ch, L) __towlower_l (Ch, L)
304# else
305# define TOUPPER(Ch, L) towupper (Ch)
306# define TOLOWER(Ch, L) towlower (Ch)
307# endif
308#else
309# ifdef _LIBC
310# ifdef USE_IN_EXTENDED_LOCALE_MODEL
311# define TOUPPER(Ch, L) __toupper_l (Ch, L)
312# define TOLOWER(Ch, L) __tolower_l (Ch, L)
313# else
314# define TOUPPER(Ch, L) toupper (Ch)
315# define TOLOWER(Ch, L) tolower (Ch)
316# endif
317# else
318# define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
319# define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
320# endif
321#endif
322/* We don't use `isdigit' here since the locale dependent
323 interpretation is not what we want here. We only need to accept
324 the arabic digits in the ASCII range. One day there is perhaps a
325 more reliable way to accept other sets of digits. */
326#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
327
79937577 328static CHAR_T *memcpy_lowcase (CHAR_T *dest, const CHAR_T *src,
80d9be81 329 size_t len LOCALE_PARAM) __THROW;
ccadf7b5
UD
330
331static CHAR_T *
80d9be81 332memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
ccadf7b5
UD
333{
334 while (len-- > 0)
335 dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
336 return dest;
337}
338
79937577 339static CHAR_T *memcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
80d9be81 340 size_t len LOCALE_PARAM) __THROW;
ccadf7b5
UD
341
342static CHAR_T *
80d9be81 343memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
ccadf7b5
UD
344{
345 while (len-- > 0)
346 dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
347 return dest;
348}
349
350
351#if ! HAVE_TM_GMTOFF
352/* Yield the difference between *A and *B,
353 measured in seconds, ignoring leap seconds. */
354# define tm_diff ftime_tm_diff
79937577 355static int tm_diff (const struct tm *, const struct tm *) __THROW;
ccadf7b5 356static int
9d46370c 357tm_diff (const struct tm *a, const struct tm *b)
ccadf7b5
UD
358{
359 /* Compute intervening leap days correctly even if year is negative.
360 Take care to avoid int overflow in leap day calculations,
361 but it's OK to assume that A and B are close to each other. */
362 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
363 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
364 int a100 = a4 / 25 - (a4 % 25 < 0);
365 int b100 = b4 / 25 - (b4 % 25 < 0);
366 int a400 = a100 >> 2;
367 int b400 = b100 >> 2;
368 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
369 int years = a->tm_year - b->tm_year;
370 int days = (365 * years + intervening_leap_days
371 + (a->tm_yday - b->tm_yday));
372 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
373 + (a->tm_min - b->tm_min))
374 + (a->tm_sec - b->tm_sec));
375}
376#endif /* ! HAVE_TM_GMTOFF */
377
378
379
380/* The number of days from the first day of the first ISO week of this
381 year to the year day YDAY with week day WDAY. ISO weeks start on
382 Monday; the first ISO week has the year's first Thursday. YDAY may
383 be as small as YDAY_MINIMUM. */
384#define ISO_WEEK_START_WDAY 1 /* Monday */
385#define ISO_WEEK1_WDAY 4 /* Thursday */
386#define YDAY_MINIMUM (-366)
79937577 387static int iso_week_days (int, int) __THROW;
ccadf7b5
UD
388#ifdef __GNUC__
389__inline__
390#endif
391static int
9d46370c 392iso_week_days (int yday, int wday)
ccadf7b5
UD
393{
394 /* Add enough to the first operand of % to make it nonnegative. */
395 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
396 return (yday
397 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
398 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
399}
400
401
402#if !(defined _NL_CURRENT || HAVE_STRFTIME)
403static CHAR_T const weekday_name[][10] =
404 {
405 L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"),
406 L_("Thursday"), L_("Friday"), L_("Saturday")
407 };
408static CHAR_T const month_name[][10] =
409 {
410 L_("January"), L_("February"), L_("March"), L_("April"), L_("May"),
411 L_("June"), L_("July"), L_("August"), L_("September"), L_("October"),
412 L_("November"), L_("December")
413 };
414#endif
415
416
417#ifdef emacs
418# define my_strftime emacs_strftimeu
419# define ut_argument , ut
80d9be81 420# define ut_argument_spec , int ut
ccadf7b5
UD
421#else
422# ifdef COMPILE_WIDE
423# define my_strftime wcsftime
424# define nl_get_alt_digit _nl_get_walt_digit
425# else
426# define my_strftime strftime
427# define nl_get_alt_digit _nl_get_alt_digit
428# endif
429# define ut_argument
430# define ut_argument_spec
ccadf7b5
UD
431/* We don't have this information in general. */
432# define ut 0
433#endif
434
800d6f9e 435static size_t __strftime_internal (CHAR_T *, size_t, const CHAR_T *,
5bcc6c0f 436 const struct tm *, bool *
80d9be81
JM
437 ut_argument_spec
438 LOCALE_PARAM) __THROW;
ccadf7b5
UD
439
440/* Write information from TP into S according to the format
441 string FORMAT, writing no more that MAXSIZE characters
442 (including the terminating '\0') and returning number of
443 characters written. If S is NULL, nothing will be written
444 anywhere, so to determine how many characters would be
445 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
800d6f9e 446
ccadf7b5 447size_t
80d9be81
JM
448my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
449 const struct tm *tp ut_argument_spec LOCALE_PARAM)
800d6f9e
UD
450{
451#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
452 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
453 Work around this bug by copying *tp before it might be munged. */
454 struct tm tmcopy;
455 tmcopy = *tp;
456 tp = &tmcopy;
457#endif
5bcc6c0f
UD
458 bool tzset_called = false;
459 return __strftime_internal (s, maxsize, format, tp, &tzset_called
800d6f9e
UD
460 ut_argument LOCALE_ARG);
461}
462#ifdef _LIBC
463libc_hidden_def (my_strftime)
464#endif
465
466static size_t
80d9be81
JM
467__strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
468 const struct tm *tp, bool *tzset_called
469 ut_argument_spec LOCALE_PARAM)
ccadf7b5
UD
470{
471#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
f095bb72 472 struct __locale_data *const current = loc->__locales[LC_TIME];
ccadf7b5
UD
473#endif
474
475 int hour12 = tp->tm_hour;
476#ifdef _NL_CURRENT
477 /* We cannot make the following values variables since we must delay
478 the evaluation of these values until really needed since some
479 expressions might not be valid in every situation. The `struct tm'
480 might be generated by a strptime() call that initialized
481 only a few elements. Dereference the pointers only if the format
482 requires this. Then it is ok to fail if the pointers are invalid. */
483# define a_wkday \
d36c75fc
PP
484 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
485 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)))
ccadf7b5 486# define f_wkday \
d36c75fc
PP
487 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
488 ? "?" : _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)))
ccadf7b5 489# define a_month \
d36c75fc
PP
490 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
491 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)))
ccadf7b5 492# define f_month \
d36c75fc
PP
493 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
494 ? "?" : _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)))
95cb863a
RL
495# define f_altmonth \
496 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
497 ? "?" : _NL_CURRENT (LC_TIME, NLW(ALTMON_1) + tp->tm_mon)))
ccadf7b5
UD
498# define ampm \
499 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \
500 ? NLW(PM_STR) : NLW(AM_STR)))
501
502# define aw_len STRLEN (a_wkday)
503# define am_len STRLEN (a_month)
504# define ap_len STRLEN (ampm)
505#else
506# if !HAVE_STRFTIME
d36c75fc
PP
507# define f_wkday (tp->tm_wday < 0 || tp->tm_wday > 6 \
508 ? "?" : weekday_name[tp->tm_wday])
509# define f_month (tp->tm_mon < 0 || tp->tm_mon > 11 \
510 ? "?" : month_name[tp->tm_mon])
ccadf7b5
UD
511# define a_wkday f_wkday
512# define a_month f_month
95cb863a 513# define f_altmonth f_month
ccadf7b5
UD
514# define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
515
516 size_t aw_len = 3;
517 size_t am_len = 3;
518 size_t ap_len = 2;
519# endif
520#endif
521 const char *zone;
522 size_t i = 0;
523 CHAR_T *p = s;
524 const CHAR_T *f;
525#if DO_MULTIBYTE && !defined COMPILE_WIDE
526 const char *format_end = NULL;
527#endif
528
529 zone = NULL;
530#if HAVE_TM_ZONE
531 /* The POSIX test suite assumes that setting
532 the environment variable TZ to a new value before calling strftime()
533 will influence the result (the %Z format) even if the information in
534 TP is computed with a totally different time zone.
535 This is bogus: though POSIX allows bad behavior like this,
536 POSIX does not require it. Do the right thing instead. */
537 zone = (const char *) tp->tm_zone;
538#endif
539#if HAVE_TZNAME
540 if (ut)
541 {
542 if (! (zone && *zone))
543 zone = "GMT";
544 }
ccadf7b5
UD
545#endif
546
547 if (hour12 > 12)
548 hour12 -= 12;
549 else
550 if (hour12 == 0)
551 hour12 = 12;
552
553 for (f = format; *f != '\0'; ++f)
554 {
555 int pad = 0; /* Padding for number ('-', '_', or 0). */
556 int modifier; /* Field modifier ('E', 'O', or 0). */
557 int digits; /* Max digits for numeric format. */
558 int number_value; /* Numeric value to be printed. */
559 int negative_number; /* 1 if the number is negative. */
560 const CHAR_T *subfmt;
561 CHAR_T *bufp;
562 CHAR_T buf[1 + (sizeof (int) < sizeof (time_t)
563 ? INT_STRLEN_BOUND (time_t)
564 : INT_STRLEN_BOUND (int))];
565 int width = -1;
566 int to_lowcase = 0;
567 int to_uppcase = 0;
568 int change_case = 0;
569 int format_char;
570
571#if DO_MULTIBYTE && !defined COMPILE_WIDE
572 switch (*f)
573 {
574 case L_('%'):
575 break;
576
577 case L_('\b'): case L_('\t'): case L_('\n'):
578 case L_('\v'): case L_('\f'): case L_('\r'):
579 case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
580 case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
581 case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
582 case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
583 case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
584 case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
585 case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
586 case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
587 case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
588 case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
589 case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
590 case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
591 case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
592 case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
593 case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
594 case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
595 case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
596 case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
597 case L_('~'):
598 /* The C Standard requires these 98 characters (plus '%') to
599 be in the basic execution character set. None of these
600 characters can start a multibyte sequence, so they need
601 not be analyzed further. */
602 add (1, *p = *f);
603 continue;
604
605 default:
606 /* Copy this multibyte sequence until we reach its end, find
607 an error, or come back to the initial shift state. */
608 {
609 mbstate_t mbstate = mbstate_zero;
610 size_t len = 0;
611 size_t fsize;
612
613 if (! format_end)
614 format_end = f + strlen (f) + 1;
615 fsize = format_end - f;
616
617 do
618 {
619 size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
620
621 if (bytes == 0)
622 break;
623
624 if (bytes == (size_t) -2)
625 {
626 len += strlen (f + len);
627 break;
628 }
629
630 if (bytes == (size_t) -1)
631 {
632 len++;
633 break;
634 }
635
636 len += bytes;
637 }
638 while (! mbsinit (&mbstate));
639
640 cpy (len, f);
641 f += len - 1;
642 continue;
643 }
644 }
645
646#else /* ! DO_MULTIBYTE */
647
648 /* Either multibyte encodings are not supported, they are
649 safe for formats, so any non-'%' byte can be copied through,
650 or this is the wide character version. */
651 if (*f != L_('%'))
652 {
653 add (1, *p = *f);
654 continue;
655 }
656
657#endif /* ! DO_MULTIBYTE */
658
659 /* Check for flags that can modify a format. */
660 while (1)
661 {
662 switch (*++f)
663 {
664 /* This influences the number formats. */
665 case L_('_'):
666 case L_('-'):
667 case L_('0'):
668 pad = *f;
669 continue;
670
671 /* This changes textual output. */
672 case L_('^'):
673 to_uppcase = 1;
674 continue;
675 case L_('#'):
676 change_case = 1;
677 continue;
678
679 default:
680 break;
681 }
682 break;
683 }
684
685 /* As a GNU extension we allow to specify the field width. */
686 if (ISDIGIT (*f))
687 {
688 width = 0;
689 do
690 {
691 if (width > INT_MAX / 10
692 || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10))
693 /* Avoid overflow. */
694 width = INT_MAX;
695 else
696 {
697 width *= 10;
698 width += *f - L_('0');
699 }
700 ++f;
701 }
702 while (ISDIGIT (*f));
703 }
704
705 /* Check for modifiers. */
706 switch (*f)
707 {
708 case L_('E'):
709 case L_('O'):
710 modifier = *f++;
711 break;
712
713 default:
714 modifier = 0;
715 break;
716 }
717
718 /* Now do the specified format. */
719 format_char = *f;
720 switch (format_char)
721 {
e4043b84
JM
722#define DO_NUMBER(d, v) \
723 do \
724 { \
725 digits = d > width ? d : width; \
726 number_value = v; \
727 goto do_number; \
728 } \
729 while (0)
730#define DO_NUMBER_SPACEPAD(d, v) \
731 do \
732 { \
733 digits = d > width ? d : width; \
734 number_value = v; \
735 goto do_number_spacepad; \
736 } \
737 while (0)
ccadf7b5
UD
738
739 case L_('%'):
740 if (modifier != 0)
741 goto bad_format;
742 add (1, *p = *f);
743 break;
744
745 case L_('a'):
746 if (modifier != 0)
747 goto bad_format;
748 if (change_case)
749 {
750 to_uppcase = 1;
751 to_lowcase = 0;
752 }
753#if defined _NL_CURRENT || !HAVE_STRFTIME
754 cpy (aw_len, a_wkday);
755 break;
756#else
757 goto underlying_strftime;
758#endif
759
760 case 'A':
761 if (modifier != 0)
762 goto bad_format;
763 if (change_case)
764 {
765 to_uppcase = 1;
766 to_lowcase = 0;
767 }
768#if defined _NL_CURRENT || !HAVE_STRFTIME
769 cpy (STRLEN (f_wkday), f_wkday);
770 break;
771#else
772 goto underlying_strftime;
773#endif
774
775 case L_('b'):
776 case L_('h'):
777 if (change_case)
778 {
779 to_uppcase = 1;
780 to_lowcase = 0;
781 }
782 if (modifier != 0)
783 goto bad_format;
784#if defined _NL_CURRENT || !HAVE_STRFTIME
785 cpy (am_len, a_month);
786 break;
787#else
788 goto underlying_strftime;
789#endif
790
791 case L_('B'):
95cb863a 792 if (modifier == L_('E'))
ccadf7b5
UD
793 goto bad_format;
794 if (change_case)
795 {
796 to_uppcase = 1;
797 to_lowcase = 0;
798 }
799#if defined _NL_CURRENT || !HAVE_STRFTIME
95cb863a
RL
800 if (modifier == L_('O'))
801 cpy (STRLEN (f_altmonth), f_altmonth);
802 else
803 cpy (STRLEN (f_month), f_month);
ccadf7b5
UD
804 break;
805#else
806 goto underlying_strftime;
807#endif
808
809 case L_('c'):
810 if (modifier == L_('O'))
811 goto bad_format;
812#ifdef _NL_CURRENT
813 if (! (modifier == 'E'
814 && (*(subfmt =
815 (const CHAR_T *) _NL_CURRENT (LC_TIME,
816 NLW(ERA_D_T_FMT)))
817 != '\0')))
818 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
819#else
820# if HAVE_STRFTIME
821 goto underlying_strftime;
822# else
823 subfmt = L_("%a %b %e %H:%M:%S %Y");
824# endif
825#endif
826
827 subformat:
828 {
829 CHAR_T *old_start = p;
800d6f9e
UD
830 size_t len = __strftime_internal (NULL, (size_t) -1, subfmt,
831 tp, tzset_called ut_argument
832 LOCALE_ARG);
833 add (len, __strftime_internal (p, maxsize - i, subfmt,
834 tp, tzset_called ut_argument
835 LOCALE_ARG));
ccadf7b5
UD
836
837 if (to_uppcase)
838 while (old_start < p)
839 {
840 *old_start = TOUPPER ((UCHAR_T) *old_start, loc);
841 ++old_start;
842 }
843 }
844 break;
845
846#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
847 underlying_strftime:
848 {
849 /* The relevant information is available only via the
850 underlying strftime implementation, so use that. */
851 char ufmt[4];
852 char *u = ufmt;
853 char ubuf[1024]; /* enough for any single format in practice */
854 size_t len;
855 /* Make sure we're calling the actual underlying strftime.
856 In some cases, config.h contains something like
857 "#define strftime rpl_strftime". */
858# ifdef strftime
859# undef strftime
860 size_t strftime ();
861# endif
862
863 *u++ = '%';
864 if (modifier != 0)
865 *u++ = modifier;
866 *u++ = format_char;
867 *u = '\0';
868 len = strftime (ubuf, sizeof ubuf, ufmt, tp);
869 if (len == 0 && ubuf[0] != '\0')
870 return 0;
871 cpy (len, ubuf);
872 }
873 break;
874#endif
875
876 case L_('C'):
ccadf7b5
UD
877 if (modifier == L_('E'))
878 {
879#if HAVE_STRUCT_ERA_ENTRY
880 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
881 if (era)
882 {
883# ifdef COMPILE_WIDE
884 size_t len = __wcslen (era->era_wname);
885 cpy (len, era->era_wname);
886# else
887 size_t len = strlen (era->era_name);
888 cpy (len, era->era_name);
889# endif
890 break;
891 }
892#else
893# if HAVE_STRFTIME
894 goto underlying_strftime;
895# endif
896#endif
897 }
898
899 {
900 int year = tp->tm_year + TM_YEAR_BASE;
901 DO_NUMBER (1, year / 100 - (year % 100 < 0));
902 }
903
904 case L_('x'):
905 if (modifier == L_('O'))
906 goto bad_format;
907#ifdef _NL_CURRENT
908 if (! (modifier == L_('E')
909 && (*(subfmt =
910 (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
911 != L_('\0'))))
912 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
913 goto subformat;
914#else
915# if HAVE_STRFTIME
916 goto underlying_strftime;
917# else
918 /* Fall through. */
919# endif
920#endif
921 case L_('D'):
922 if (modifier != 0)
923 goto bad_format;
924 subfmt = L_("%m/%d/%y");
925 goto subformat;
926
927 case L_('d'):
928 if (modifier == L_('E'))
929 goto bad_format;
930
931 DO_NUMBER (2, tp->tm_mday);
932
933 case L_('e'):
934 if (modifier == L_('E'))
935 goto bad_format;
936
937 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
938
939 /* All numeric formats set DIGITS and NUMBER_VALUE and then
940 jump to one of these two labels. */
941
942 do_number_spacepad:
943 /* Force `_' flag unless overwritten by `0' or '-' flag. */
944 if (pad != L_('0') && pad != L_('-'))
945 pad = L_('_');
946
947 do_number:
948 /* Format the number according to the MODIFIER flag. */
949
950 if (modifier == L_('O') && 0 <= number_value)
951 {
952#ifdef _NL_CURRENT
953 /* Get the locale specific alternate representation of
954 the number NUMBER_VALUE. If none exist NULL is returned. */
955 const CHAR_T *cp = nl_get_alt_digit (number_value
956 HELPER_LOCALE_ARG);
957
958 if (cp != NULL)
959 {
960 size_t digitlen = STRLEN (cp);
961 if (digitlen != 0)
962 {
963 cpy (digitlen, cp);
964 break;
965 }
966 }
967#else
968# if HAVE_STRFTIME
969 goto underlying_strftime;
970# endif
971#endif
972 }
973 {
974 unsigned int u = number_value;
975
976 bufp = buf + sizeof (buf) / sizeof (buf[0]);
977 negative_number = number_value < 0;
978
979 if (negative_number)
980 u = -u;
981
982 do
983 *--bufp = u % 10 + L_('0');
984 while ((u /= 10) != 0);
985 }
986
987 do_number_sign_and_padding:
988 if (negative_number)
989 *--bufp = L_('-');
990
991 if (pad != L_('-'))
992 {
993 int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0]))
994 - bufp);
995
996 if (padding > 0)
997 {
998 if (pad == L_('_'))
999 {
1000 if ((size_t) padding >= maxsize - i)
1001 return 0;
1002
1003 if (p)
1004 memset_space (p, padding);
1005 i += padding;
1006 width = width > padding ? width - padding : 0;
1007 }
1008 else
1009 {
1010 if ((size_t) digits >= maxsize - i)
1011 return 0;
1012
1013 if (negative_number)
1014 {
1015 ++bufp;
1016
1017 if (p)
1018 *p++ = L_('-');
1019 ++i;
1020 }
1021
1022 if (p)
1023 memset_zero (p, padding);
1024 i += padding;
1025 width = 0;
1026 }
1027 }
1028 }
1029
1030 cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
1031 break;
1032
1033 case L_('F'):
1034 if (modifier != 0)
1035 goto bad_format;
1036 subfmt = L_("%Y-%m-%d");
1037 goto subformat;
1038
1039 case L_('H'):
1040 if (modifier == L_('E'))
1041 goto bad_format;
1042
1043 DO_NUMBER (2, tp->tm_hour);
1044
1045 case L_('I'):
1046 if (modifier == L_('E'))
1047 goto bad_format;
1048
1049 DO_NUMBER (2, hour12);
1050
1051 case L_('k'): /* GNU extension. */
1052 if (modifier == L_('E'))
1053 goto bad_format;
1054
1055 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
1056
1057 case L_('l'): /* GNU extension. */
1058 if (modifier == L_('E'))
1059 goto bad_format;
1060
1061 DO_NUMBER_SPACEPAD (2, hour12);
1062
1063 case L_('j'):
1064 if (modifier == L_('E'))
1065 goto bad_format;
1066
1067 DO_NUMBER (3, 1 + tp->tm_yday);
1068
1069 case L_('M'):
1070 if (modifier == L_('E'))
1071 goto bad_format;
1072
1073 DO_NUMBER (2, tp->tm_min);
1074
1075 case L_('m'):
1076 if (modifier == L_('E'))
1077 goto bad_format;
1078
1079 DO_NUMBER (2, tp->tm_mon + 1);
1080
1081 case L_('n'):
1082 add (1, *p = L_('\n'));
1083 break;
1084
1085 case L_('P'):
1086 to_lowcase = 1;
1087#if !defined _NL_CURRENT && HAVE_STRFTIME
1088 format_char = L_('p');
1089#endif
1090 /* FALLTHROUGH */
1091
1092 case L_('p'):
1093 if (change_case)
1094 {
1095 to_uppcase = 0;
1096 to_lowcase = 1;
1097 }
1098#if defined _NL_CURRENT || !HAVE_STRFTIME
1099 cpy (ap_len, ampm);
1100 break;
1101#else
1102 goto underlying_strftime;
1103#endif
1104
1105 case L_('R'):
1106 subfmt = L_("%H:%M");
1107 goto subformat;
1108
1109 case L_('r'):
1110#if !defined _NL_CURRENT && HAVE_STRFTIME
1111 goto underlying_strftime;
1112#else
1113# ifdef _NL_CURRENT
1114 if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
1115 NLW(T_FMT_AMPM)))
1116 == L_('\0'))
1117# endif
1118 subfmt = L_("%I:%M:%S %p");
1119 goto subformat;
1120#endif
1121
1122 case L_('S'):
1123 if (modifier == L_('E'))
1124 goto bad_format;
1125
1126 DO_NUMBER (2, tp->tm_sec);
1127
1128 case L_('s'): /* GNU extension. */
1129 {
1130 struct tm ltm;
1131 time_t t;
1132
1133 ltm = *tp;
1134 t = mktime (&ltm);
1135
1136 /* Generate string value for T using time_t arithmetic;
1137 this works even if sizeof (long) < sizeof (time_t). */
1138
1139 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1140 negative_number = t < 0;
1141
1142 do
1143 {
1144 int d = t % 10;
1145 t /= 10;
1146
1147 if (negative_number)
1148 {
1149 d = -d;
1150
1151 /* Adjust if division truncates to minus infinity. */
1152 if (0 < -1 % 10 && d < 0)
1153 {
1154 t++;
1155 d += 10;
1156 }
1157 }
1158
1159 *--bufp = d + L_('0');
1160 }
1161 while (t != 0);
1162
1163 digits = 1;
1164 goto do_number_sign_and_padding;
1165 }
1166
1167 case L_('X'):
1168 if (modifier == L_('O'))
1169 goto bad_format;
1170#ifdef _NL_CURRENT
1171 if (! (modifier == L_('E')
1172 && (*(subfmt =
1173 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
1174 != L_('\0'))))
1175 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
1176 goto subformat;
1177#else
1178# if HAVE_STRFTIME
1179 goto underlying_strftime;
1180# else
1181 /* Fall through. */
1182# endif
1183#endif
1184 case L_('T'):
1185 subfmt = L_("%H:%M:%S");
1186 goto subformat;
1187
1188 case L_('t'):
1189 add (1, *p = L_('\t'));
1190 break;
1191
1192 case L_('u'):
1193 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1194
1195 case L_('U'):
1196 if (modifier == L_('E'))
1197 goto bad_format;
1198
1199 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1200
1201 case L_('V'):
1202 case L_('g'):
1203 case L_('G'):
1204 if (modifier == L_('E'))
1205 goto bad_format;
1206 {
1207 int year = tp->tm_year + TM_YEAR_BASE;
1208 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1209
1210 if (days < 0)
1211 {
1212 /* This ISO week belongs to the previous year. */
1213 year--;
1214 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
1215 tp->tm_wday);
1216 }
1217 else
1218 {
1219 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1220 tp->tm_wday);
1221 if (0 <= d)
1222 {
1223 /* This ISO week belongs to the next year. */
1224 year++;
1225 days = d;
1226 }
1227 }
1228
1229 switch (*f)
1230 {
1231 case L_('g'):
1232 DO_NUMBER (2, (year % 100 + 100) % 100);
1233
1234 case L_('G'):
1235 DO_NUMBER (1, year);
1236
1237 default:
1238 DO_NUMBER (2, days / 7 + 1);
1239 }
1240 }
1241
1242 case L_('W'):
1243 if (modifier == L_('E'))
1244 goto bad_format;
1245
1246 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1247
1248 case L_('w'):
1249 if (modifier == L_('E'))
1250 goto bad_format;
1251
1252 DO_NUMBER (1, tp->tm_wday);
1253
1254 case L_('Y'):
1255 if (modifier == 'E')
1256 {
1257#if HAVE_STRUCT_ERA_ENTRY
1258 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1259 if (era)
1260 {
1261# ifdef COMPILE_WIDE
1262 subfmt = era->era_wformat;
1263# else
1264 subfmt = era->era_format;
1265# endif
1266 goto subformat;
1267 }
1268#else
1269# if HAVE_STRFTIME
1270 goto underlying_strftime;
1271# endif
1272#endif
1273 }
1274 if (modifier == L_('O'))
1275 goto bad_format;
1276 else
1277 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
1278
1279 case L_('y'):
1280 if (modifier == L_('E'))
1281 {
1282#if HAVE_STRUCT_ERA_ENTRY
1283 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1284 if (era)
1285 {
1286 int delta = tp->tm_year - era->start_date[0];
1287 DO_NUMBER (1, (era->offset
1288 + delta * era->absolute_direction));
1289 }
1290#else
1291# if HAVE_STRFTIME
1292 goto underlying_strftime;
1293# endif
1294#endif
1295 }
1296 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
1297
1298 case L_('Z'):
1299 if (change_case)
1300 {
1301 to_uppcase = 0;
1302 to_lowcase = 1;
1303 }
1304
1305#if HAVE_TZNAME
1306 /* The tzset() call might have changed the value. */
1307 if (!(zone && *zone) && tp->tm_isdst >= 0)
5bcc6c0f
UD
1308 {
1309 /* POSIX.1 requires that local time zone information is used as
1310 though strftime called tzset. */
1311# if HAVE_TZSET
1312 if (!*tzset_called)
1313 {
1314 tzset ();
1315 *tzset_called = true;
1316 }
1317# endif
d36c75fc 1318 zone = tp->tm_isdst <= 1 ? tzname[tp->tm_isdst] : "?";
5bcc6c0f 1319 }
ccadf7b5
UD
1320#endif
1321 if (! zone)
1322 zone = "";
1323
1324#ifdef COMPILE_WIDE
1325 {
1326 /* The zone string is always given in multibyte form. We have
1327 to transform it first. */
1328 wchar_t *wczone;
1329 size_t len;
1330 widen (zone, wczone, len);
1331 cpy (len, wczone);
1332 }
1333#else
1334 cpy (strlen (zone), zone);
1335#endif
1336 break;
1337
1338 case L_('z'):
1339 if (tp->tm_isdst < 0)
1340 break;
1341
1342 {
1343 int diff;
1344#if HAVE_TM_GMTOFF
1345 diff = tp->tm_gmtoff;
1346#else
1347 if (ut)
1348 diff = 0;
1349 else
1350 {
1351 struct tm gtm;
1352 struct tm ltm;
1353 time_t lt;
1354
5bcc6c0f
UD
1355 /* POSIX.1 requires that local time zone information is used as
1356 though strftime called tzset. */
1357# if HAVE_TZSET
1358 if (!*tzset_called)
1359 {
1360 tzset ();
1361 *tzset_called = true;
1362 }
1363# endif
1364
ccadf7b5
UD
1365 ltm = *tp;
1366 lt = mktime (&ltm);
1367
1368 if (lt == (time_t) -1)
1369 {
1370 /* mktime returns -1 for errors, but -1 is also a
1371 valid time_t value. Check whether an error really
1372 occurred. */
1373 struct tm tm;
1374
1375 if (! __localtime_r (&lt, &tm)
1376 || ((ltm.tm_sec ^ tm.tm_sec)
1377 | (ltm.tm_min ^ tm.tm_min)
1378 | (ltm.tm_hour ^ tm.tm_hour)
1379 | (ltm.tm_mday ^ tm.tm_mday)
1380 | (ltm.tm_mon ^ tm.tm_mon)
1381 | (ltm.tm_year ^ tm.tm_year)))
1382 break;
1383 }
1384
1385 if (! __gmtime_r (&lt, &gtm))
1386 break;
1387
1388 diff = tm_diff (&ltm, &gtm);
1389 }
1390#endif
1391
1392 if (diff < 0)
1393 {
1394 add (1, *p = L_('-'));
1395 diff = -diff;
1396 }
1397 else
1398 add (1, *p = L_('+'));
1399
1400 diff /= 60;
1401 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1402 }
1403
1404 case L_('\0'): /* GNU extension: % at end of format. */
1405 --f;
1406 /* Fall through. */
1407 default:
1408 /* Unknown format; output the format, including the '%',
1409 since this is most likely the right thing to do if a
1410 multibyte string has been misparsed. */
1411 bad_format:
1412 {
1413 int flen;
1414 for (flen = 1; f[1 - flen] != L_('%'); flen++)
1415 continue;
1416 cpy (flen, &f[1 - flen]);
1417 }
1418 break;
1419 }
1420 }
1421
1422 if (p && maxsize != 0)
1423 *p = L_('\0');
1424 return i;
1425}
ccadf7b5
UD
1426
1427
1428#ifdef emacs
1429/* For Emacs we have a separate interface which corresponds to the normal
1430 strftime function and does not have the extra information whether the
1431 TP arguments comes from a `gmtime' call or not. */
1432size_t
f63f2bfd
JM
1433emacs_strftime (char *s, size_t maxsize, const char *format,
1434 const struct tm *tp)
ccadf7b5
UD
1435{
1436 return my_strftime (s, maxsize, format, tp, 0);
1437}
1438#endif
1439
1440#if defined _LIBC && !defined COMPILE_WIDE
c4d6f155 1441weak_alias (__strftime_l, strftime_l)
ccadf7b5 1442#endif