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