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