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