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