Update from FSF.
[gnulib.git] / lib / strftime.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 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 it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    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, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19    USA.  */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #ifdef _LIBC
26 # define HAVE_LIMITS_H 1
27 # define HAVE_MBLEN 1
28 # define HAVE_MBRLEN 1
29 # define HAVE_STRUCT_ERA_ENTRY 1
30 # define HAVE_TM_GMTOFF 1
31 # define HAVE_TM_ZONE 1
32 # define HAVE_TZNAME 1
33 # define HAVE_TZSET 1
34 # define MULTIBYTE_IS_FORMAT_SAFE 1
35 # define STDC_HEADERS 1
36 # include "../locale/localeinfo.h"
37 #endif
38
39 #if defined emacs && !defined HAVE_BCOPY
40 # define HAVE_MEMCPY 1
41 #endif
42
43 #include <ctype.h>
44 #include <sys/types.h>          /* Some systems define `time_t' here.  */
45
46 #ifdef TIME_WITH_SYS_TIME
47 # include <sys/time.h>
48 # include <time.h>
49 #else
50 # ifdef HAVE_SYS_TIME_H
51 #  include <sys/time.h>
52 # else
53 #  include <time.h>
54 # endif
55 #endif
56 #if HAVE_TZNAME
57 extern char *tzname[];
58 #endif
59
60 /* Do multibyte processing if multibytes are supported, unless
61    multibyte sequences are safe in formats.  Multibyte sequences are
62    safe if they cannot contain byte sequences that look like format
63    conversion specifications.  The GNU C Library uses UTF8 multibyte
64    encoding, which is safe for formats, but strftime.c can be used
65    with other C libraries that use unsafe encodings.  */
66 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
67
68 #if DO_MULTIBYTE
69 # if HAVE_MBRLEN
70 #  include <wchar.h>
71 # else
72    /* Simulate mbrlen with mblen as best we can.  */
73 #  define mbstate_t int
74 #  define mbrlen(s, n, ps) mblen (s, n)
75 #  define mbsinit(ps) (*(ps) == 0)
76 # endif
77   static const mbstate_t mbstate_zero;
78 #endif
79
80 #if HAVE_LIMITS_H
81 # include <limits.h>
82 #endif
83
84 #if STDC_HEADERS
85 # include <stddef.h>
86 # include <stdlib.h>
87 # include <string.h>
88 #else
89 # ifndef HAVE_MEMCPY
90 #  define memcpy(d, s, n) bcopy ((s), (d), (n))
91 # endif
92 #endif
93
94 #ifdef _LIBC
95 # define MEMPCPY(d, s, n) __mempcpy (d, s, n)
96 #else
97 # ifndef HAVE_MEMPCPY
98 #  define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
99 # endif
100 #endif
101
102 #ifndef __P
103 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
104 #  define __P(args) args
105 # else
106 #  define __P(args) ()
107 # endif  /* GCC.  */
108 #endif  /* Not __P.  */
109
110 #ifndef PTR
111 # ifdef __STDC__
112 #  define PTR void *
113 # else
114 #  define PTR char *
115 # endif
116 #endif
117
118 #ifndef CHAR_BIT
119 # define CHAR_BIT 8
120 #endif
121
122 #ifndef NULL
123 # define NULL 0
124 #endif
125
126 #define TYPE_SIGNED(t) ((t) -1 < 0)
127
128 /* Bound on length of the string representing an integer value of type t.
129    Subtract one for the sign bit if t is signed;
130    302 / 1000 is log10 (2) rounded up;
131    add one for integer division truncation;
132    add one more for a minus sign if t is signed.  */
133 #define INT_STRLEN_BOUND(t) \
134   ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
135
136 #define TM_YEAR_BASE 1900
137
138 #ifndef __isleap
139 /* Nonzero if YEAR is a leap year (every 4 years,
140    except every 100th isn't, and every 400th is).  */
141 # define __isleap(year) \
142   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
143 #endif
144
145
146 #ifdef _LIBC
147 # define gmtime_r __gmtime_r
148 # define localtime_r __localtime_r
149 # define tzname __tzname
150 # define tzset __tzset
151 #else
152 # if ! HAVE_LOCALTIME_R
153 #  if ! HAVE_TM_GMTOFF
154 /* Approximate gmtime_r as best we can in its absence.  */
155 #   undef gmtime_r
156 #   define gmtime_r my_gmtime_r
157 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
158 static struct tm *
159 gmtime_r (t, tp)
160      const time_t *t;
161      struct tm *tp;
162 {
163   struct tm *l = gmtime (t);
164   if (! l)
165     return 0;
166   *tp = *l;
167   return tp;
168 }
169 #  endif /* ! HAVE_TM_GMTOFF */
170
171 /* Approximate localtime_r as best we can in its absence.  */
172 #  undef localtime_r
173 #  define localtime_r my_ftime_localtime_r
174 static struct tm *localtime_r __P ((const time_t *, struct tm *));
175 static struct tm *
176 localtime_r (t, tp)
177      const time_t *t;
178      struct tm *tp;
179 {
180   struct tm *l = localtime (t);
181   if (! l)
182     return 0;
183   *tp = *l;
184   return tp;
185 }
186 # endif /* ! HAVE_LOCALTIME_R */
187 #endif /* ! defined _LIBC */
188
189
190 #if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
191 /* Some systems lack the `memset' function and we don't want to
192    introduce additional dependencies.  */
193 /* The SGI compiler reportedly barfs on the trailing null
194    if we use a string constant as the initializer.  28 June 1997, rms.  */
195 static const char spaces[16] = /* "                " */
196   { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
197 static const char zeroes[16] = /* "0000000000000000" */
198   { '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0' };
199
200 # define memset_space(P, Len) \
201   do {                                                                        \
202     int _len = (Len);                                                         \
203                                                                               \
204     do                                                                        \
205       {                                                                       \
206         int _this = _len > 16 ? 16 : _len;                                    \
207         (P) = MEMPCPY ((P), spaces, _this);                                   \
208         _len -= _this;                                                        \
209       }                                                                       \
210     while (_len > 0);                                                         \
211   } while (0)
212
213 # define memset_zero(P, Len) \
214   do {                                                                        \
215     int _len = (Len);                                                         \
216                                                                               \
217     do                                                                        \
218       {                                                                       \
219         int _this = _len > 16 ? 16 : _len;                                    \
220         (P) = MEMPCPY ((P), zeroes, _this);                                   \
221         _len -= _this;                                                        \
222       }                                                                       \
223     while (_len > 0);                                                         \
224   } while (0)
225 #else
226 # define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
227 # define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
228 #endif
229
230 #define add(n, f)                                                             \
231   do                                                                          \
232     {                                                                         \
233       int _n = (n);                                                           \
234       int _delta = width - _n;                                                \
235       int _incr = _n + (_delta > 0 ? _delta : 0);                             \
236       if (i + _incr >= maxsize)                                               \
237         return 0;                                                             \
238       if (p)                                                                  \
239         {                                                                     \
240           if (_delta > 0)                                                     \
241             {                                                                 \
242               if (pad == '0')                                                 \
243                 memset_zero (p, _delta);                                      \
244               else                                                            \
245                 memset_space (p, _delta);                                     \
246             }                                                                 \
247           f;                                                                  \
248           p += _n;                                                            \
249         }                                                                     \
250       i += _incr;                                                             \
251     } while (0)
252
253 #define cpy(n, s) \
254     add ((n),                                                                 \
255          if (to_lowcase)                                                      \
256            memcpy_lowcase (p, (s), _n);                                       \
257          else if (to_uppcase)                                                 \
258            memcpy_uppcase (p, (s), _n);                                       \
259          else                                                                 \
260            memcpy ((PTR) p, (PTR) (s), _n))
261
262
263
264 #ifdef _LIBC
265 # define TOUPPER(Ch) toupper (Ch)
266 # define TOLOWER(Ch) tolower (Ch)
267 #else
268 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
269 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
270 #endif
271 /* We don't use `isdigit' here since the locale dependent
272    interpretation is not what we want here.  We only need to accept
273    the arabic digits in the ASCII range.  One day there is perhaps a
274    more reliable way to accept other sets of digits.  */
275 #define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
276
277 static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
278
279 static char *
280 memcpy_lowcase (dest, src, len)
281      char *dest;
282      const char *src;
283      size_t len;
284 {
285   while (len-- > 0)
286     dest[len] = TOLOWER ((unsigned char) src[len]);
287   return dest;
288 }
289
290 static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
291
292 static char *
293 memcpy_uppcase (dest, src, len)
294      char *dest;
295      const char *src;
296      size_t len;
297 {
298   while (len-- > 0)
299     dest[len] = TOUPPER ((unsigned char) src[len]);
300   return dest;
301 }
302
303
304 #if ! HAVE_TM_GMTOFF
305 /* Yield the difference between *A and *B,
306    measured in seconds, ignoring leap seconds.  */
307 # define tm_diff ftime_tm_diff
308 static int tm_diff __P ((const struct tm *, const struct tm *));
309 static int
310 tm_diff (a, b)
311      const struct tm *a;
312      const struct tm *b;
313 {
314   /* Compute intervening leap days correctly even if year is negative.
315      Take care to avoid int overflow in leap day calculations,
316      but it's OK to assume that A and B are close to each other.  */
317   int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
318   int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
319   int a100 = a4 / 25 - (a4 % 25 < 0);
320   int b100 = b4 / 25 - (b4 % 25 < 0);
321   int a400 = a100 >> 2;
322   int b400 = b100 >> 2;
323   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
324   int years = a->tm_year - b->tm_year;
325   int days = (365 * years + intervening_leap_days
326               + (a->tm_yday - b->tm_yday));
327   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
328                 + (a->tm_min - b->tm_min))
329           + (a->tm_sec - b->tm_sec));
330 }
331 #endif /* ! HAVE_TM_GMTOFF */
332
333
334
335 /* The number of days from the first day of the first ISO week of this
336    year to the year day YDAY with week day WDAY.  ISO weeks start on
337    Monday; the first ISO week has the year's first Thursday.  YDAY may
338    be as small as YDAY_MINIMUM.  */
339 #define ISO_WEEK_START_WDAY 1 /* Monday */
340 #define ISO_WEEK1_WDAY 4 /* Thursday */
341 #define YDAY_MINIMUM (-366)
342 static int iso_week_days __P ((int, int));
343 #ifdef __GNUC__
344 __inline__
345 #endif
346 static int
347 iso_week_days (yday, wday)
348      int yday;
349      int wday;
350 {
351   /* Add enough to the first operand of % to make it nonnegative.  */
352   int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
353   return (yday
354           - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
355           + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
356 }
357
358
359 #if !(defined _NL_CURRENT || HAVE_STRFTIME)
360 static char const weekday_name[][10] =
361   {
362     "Sunday", "Monday", "Tuesday", "Wednesday",
363     "Thursday", "Friday", "Saturday"
364   };
365 static char const month_name[][10] =
366   {
367     "January", "February", "March", "April", "May", "June",
368     "July", "August", "September", "October", "November", "December"
369   };
370 #endif
371
372
373 #ifdef emacs
374 # define my_strftime emacs_strftime
375  /* Emacs 20.2 uses `-Dstrftime=emacs_strftime' when compiling,
376     because that's how strftime used to be configured.
377     Undo this, since it gets in the way of accessing the underlying strftime,
378     which is needed for things like %Ec in Solaris.
379     The following two lines can be removed once Emacs stops compiling with
380     `-Dstrftime=emacs_strftime'.  */
381 # undef strftime
382 size_t strftime __P ((char *, size_t, const char *, const struct tm *));
383 #else
384 # define my_strftime strftime
385 #endif
386
387 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
388   /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
389      Work around this bug by copying *tp before it might be munged.  */
390   size_t _strftime_copytm __P ((char *, size_t, const char *,
391                                 const struct tm *));
392   size_t
393   my_strftime (s, maxsize, format, tp)
394       char *s;
395       size_t maxsize;
396       const char *format;
397       const struct tm *tp;
398   {
399     struct tm tmcopy;
400     tmcopy = *tp;
401     return _strftime_copytm (s, maxsize, format, &tmcopy);
402   }
403 # undef my_strftime
404 # define my_strftime(S, Maxsize, Format, Tp) \
405   _strftime_copytm (S, Maxsize, Format, Tp)
406 #endif
407
408
409 /* Write information from TP into S according to the format
410    string FORMAT, writing no more that MAXSIZE characters
411    (including the terminating '\0') and returning number of
412    characters written.  If S is NULL, nothing will be written
413    anywhere, so to determine how many characters would be
414    written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
415 size_t
416 my_strftime (s, maxsize, format, tp)
417       char *s;
418       size_t maxsize;
419       const char *format;
420       const struct tm *tp;
421 {
422   int hour12 = tp->tm_hour;
423 #ifdef _NL_CURRENT
424   const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
425   const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
426   const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
427   const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
428   const char *const ampm = _NL_CURRENT (LC_TIME,
429                                         hour12 > 11 ? PM_STR : AM_STR);
430   size_t aw_len = strlen (a_wkday);
431   size_t am_len = strlen (a_month);
432   size_t ap_len = strlen (ampm);
433 #else
434 # if !HAVE_STRFTIME
435   const char *const f_wkday = weekday_name[tp->tm_wday];
436   const char *const f_month = month_name[tp->tm_mon];
437   const char *const a_wkday = f_wkday;
438   const char *const a_month = f_month;
439   const char *const ampm = "AMPM" + 2 * (hour12 > 11);
440   size_t aw_len = 3;
441   size_t am_len = 3;
442   size_t ap_len = 2;
443 # endif
444 #endif
445 #if defined _NL_CURRENT || !HAVE_STRFTIME
446   size_t wkday_len = strlen (f_wkday);
447   size_t month_len = strlen (f_month);
448 #endif
449   const char *zone;
450   size_t zonelen;
451   size_t i = 0;
452   char *p = s;
453   const char *f;
454
455   zone = NULL;
456 #if HAVE_TM_ZONE
457   /* The POSIX test suite assumes that setting
458      the environment variable TZ to a new value before calling strftime()
459      will influence the result (the %Z format) even if the information in
460      TP is computed with a totally different time zone.
461      This is bogus: though POSIX allows bad behavior like this,
462      POSIX does not require it.  Do the right thing instead.  */
463   zone = (const char *) tp->tm_zone;
464 #endif
465 #if HAVE_TZNAME
466   /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
467      time zone names contained in the external variable `tzname' shall
468      be set as if the tzset() function had been called.  */
469 # if HAVE_TZSET
470   tzset ();
471 # endif
472
473   if (!(zone && *zone) && tp->tm_isdst >= 0)
474     zone = tzname[tp->tm_isdst];
475 #endif
476   if (! zone)
477     zone = "";          /* POSIX.2 requires the empty string here.  */
478
479   zonelen = strlen (zone);
480
481   if (hour12 > 12)
482     hour12 -= 12;
483   else
484     if (hour12 == 0) hour12 = 12;
485
486   for (f = format; *f != '\0'; ++f)
487     {
488       int pad;                  /* Padding for number ('-', '_', or 0).  */
489       int modifier;             /* Field modifier ('E', 'O', or 0).  */
490       int digits;               /* Max digits for numeric format.  */
491       int number_value;         /* Numeric value to be printed.  */
492       int negative_number;      /* 1 if the number is negative.  */
493       const char *subfmt;
494       char *bufp;
495       char buf[1 + (sizeof (int) < sizeof (time_t)
496                     ? INT_STRLEN_BOUND (time_t)
497                     : INT_STRLEN_BOUND (int))];
498       int width = -1;
499       int to_lowcase = 0;
500       int to_uppcase = 0;
501       int change_case = 0;
502       int format_char;
503
504 #if DO_MULTIBYTE
505
506        switch (*f)
507         {
508         case '%':
509           break;
510
511         case '\a': case '\b': case '\t': case '\n':
512         case '\v': case '\f': case '\r':
513         case ' ': case '!': case '"': case '#': case '&': case'\'':
514         case '(': case ')': case '*': case '+': case ',': case '-':
515         case '.': case '/': case '0': case '1': case '2': case '3':
516         case '4': case '5': case '6': case '7': case '8': case '9':
517         case ':': case ';': case '<': case '=': case '>': case '?':
518         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
519         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
520         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
521         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
522         case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
523         case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
524         case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
525         case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
526         case 'r': case 's': case 't': case 'u': case 'v': case 'w':
527         case 'x': case 'y': case 'z': case '{': case '|': case '}':
528         case '~':
529           /* The C Standard requires these 98 characters (plus '%') to
530              be in the basic execution character set.  None of these
531              characters can start a multibyte sequence, so they need
532              not be analyzed further.  */
533           add (1, *p = *f);
534           continue;
535
536         default:
537           /* Copy this multibyte sequence until we reach its end, find
538              an error, or come back to the initial shift state.  */
539           {
540             mbstate_t mbstate = mbstate_zero;
541             size_t len = 0;
542
543             do
544               {
545                 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
546
547                 if (bytes == 0)
548                   break;
549
550                 if (bytes == (size_t) -2 || bytes == (size_t) -1)
551                   {
552                     len++;
553                     break;
554                   }
555
556                 len += bytes;
557               }
558             while (! mbsinit (&mbstate));
559
560             cpy (len, f);
561             continue;
562           }
563         }
564
565 #else /* ! DO_MULTIBYTE */
566
567       /* Either multibyte encodings are not supported, or they are
568          safe for formats, so any non-'%' byte can be copied through.  */
569       if (*f != '%')
570         {
571           add (1, *p = *f);
572           continue;
573         }
574
575 #endif /* ! DO_MULTIBYTE */
576
577       /* Check for flags that can modify a format.  */
578       pad = 0;
579       while (1)
580         {
581           switch (*++f)
582             {
583               /* This influences the number formats.  */
584             case '_':
585             case '-':
586             case '0':
587               pad = *f;
588               continue;
589
590               /* This changes textual output.  */
591             case '^':
592               to_uppcase = 1;
593               continue;
594             case '#':
595               change_case = 1;
596               continue;
597
598             default:
599               break;
600             }
601           break;
602         }
603
604       /* As a GNU extension we allow to specify the field width.  */
605       if (ISDIGIT (*f))
606         {
607           width = 0;
608           do
609             {
610               width *= 10;
611               width += *f - '0';
612               ++f;
613             }
614           while (ISDIGIT (*f));
615         }
616
617       /* Check for modifiers.  */
618       switch (*f)
619         {
620         case 'E':
621         case 'O':
622           modifier = *f++;
623           break;
624
625         default:
626           modifier = 0;
627           break;
628         }
629
630       /* Now do the specified format.  */
631       format_char = *f;
632       switch (format_char)
633         {
634 #define DO_NUMBER(d, v) \
635           digits = width == -1 ? d : width;                                   \
636           number_value = v; goto do_number
637 #define DO_NUMBER_SPACEPAD(d, v) \
638           digits = width == -1 ? d : width;                                   \
639           number_value = v; goto do_number_spacepad
640
641         case '%':
642           if (modifier != 0)
643             goto bad_format;
644           add (1, *p = *f);
645           break;
646
647         case 'a':
648           if (modifier != 0)
649             goto bad_format;
650           if (change_case)
651             {
652               to_uppcase = 1;
653               to_lowcase = 0;
654             }
655 #if defined _NL_CURRENT || !HAVE_STRFTIME
656           cpy (aw_len, a_wkday);
657           break;
658 #else
659           goto underlying_strftime;
660 #endif
661
662         case 'A':
663           if (modifier != 0)
664             goto bad_format;
665           if (change_case)
666             {
667               to_uppcase = 1;
668               to_lowcase = 0;
669             }
670 #if defined _NL_CURRENT || !HAVE_STRFTIME
671           cpy (wkday_len, f_wkday);
672           break;
673 #else
674           goto underlying_strftime;
675 #endif
676
677         case 'b':
678         case 'h':               /* POSIX.2 extension.  */
679           if (modifier != 0)
680             goto bad_format;
681 #if defined _NL_CURRENT || !HAVE_STRFTIME
682           cpy (am_len, a_month);
683           break;
684 #else
685           goto underlying_strftime;
686 #endif
687
688         case 'B':
689           if (modifier != 0)
690             goto bad_format;
691           if (change_case)
692             {
693               to_uppcase = 1;
694               to_lowcase = 0;
695             }
696 #if defined _NL_CURRENT || !HAVE_STRFTIME
697           cpy (month_len, f_month);
698           break;
699 #else
700           goto underlying_strftime;
701 #endif
702
703         case 'c':
704           if (modifier == 'O')
705             goto bad_format;
706 #ifdef _NL_CURRENT
707           if (! (modifier == 'E'
708                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
709             subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
710 #else
711 # if HAVE_STRFTIME
712           goto underlying_strftime;
713 # else
714           subfmt = "%a %b %e %H:%M:%S %Y";
715 # endif
716 #endif
717
718         subformat:
719           {
720             char *old_start = p;
721             size_t len = my_strftime (NULL, maxsize - i, subfmt, tp);
722             if (len == 0 && *subfmt)
723               return 0;
724             add (len, my_strftime (p, maxsize - i, subfmt, tp));
725
726             if (to_uppcase)
727               while (old_start < p)
728                 {
729                   *old_start = TOUPPER ((unsigned char) *old_start);
730                   ++old_start;
731                 }
732           }
733           break;
734
735 #if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
736         underlying_strftime:
737           {
738             /* The relevant information is available only via the
739                underlying strftime implementation, so use that.  */
740             char ufmt[4];
741             char *u = ufmt;
742             char ubuf[1024]; /* enough for any single format in practice */
743             size_t len;
744             *u++ = '%';
745             if (modifier != 0)
746               *u++ = modifier;
747             *u++ = format_char;
748             *u = '\0';
749             len = strftime (ubuf, sizeof ubuf, ufmt, tp);
750             if (len == 0)
751               return 0;
752             cpy (len, ubuf);
753           }
754           break;
755 #endif
756
757         case 'C':               /* POSIX.2 extension.  */
758           if (modifier == 'O')
759             goto bad_format;
760           if (modifier == 'E')
761             {
762 #if HAVE_STRUCT_ERA_ENTRY
763               struct era_entry *era = _nl_get_era_entry (tp);
764               if (era)
765                 {
766                   size_t len = strlen (era->name_fmt);
767                   cpy (len, era->name_fmt);
768                   break;
769                 }
770 #else
771 # if HAVE_STRFTIME
772               goto underlying_strftime;
773 # endif
774 #endif
775             }
776
777           {
778             int year = tp->tm_year + TM_YEAR_BASE;
779             DO_NUMBER (1, year / 100 - (year % 100 < 0));
780           }
781
782         case 'x':
783           if (modifier == 'O')
784             goto bad_format;
785 #ifdef _NL_CURRENT
786           if (! (modifier == 'E'
787                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
788             subfmt = _NL_CURRENT (LC_TIME, D_FMT);
789           goto subformat;
790 #else
791 # if HAVE_STRFTIME
792           goto underlying_strftime;
793 # else
794           /* Fall through.  */
795 # endif
796 #endif
797         case 'D':               /* POSIX.2 extension.  */
798           if (modifier != 0)
799             goto bad_format;
800           subfmt = "%m/%d/%y";
801           goto subformat;
802
803         case 'd':
804           if (modifier == 'E')
805             goto bad_format;
806
807           DO_NUMBER (2, tp->tm_mday);
808
809         case 'e':               /* POSIX.2 extension.  */
810           if (modifier == 'E')
811             goto bad_format;
812
813           DO_NUMBER_SPACEPAD (2, tp->tm_mday);
814
815           /* All numeric formats set DIGITS and NUMBER_VALUE and then
816              jump to one of these two labels.  */
817
818         do_number_spacepad:
819           /* Force `_' flag unless overwritten by `0' flag.  */
820           if (pad != '0')
821             pad = '_';
822
823         do_number:
824           /* Format the number according to the MODIFIER flag.  */
825
826           if (modifier == 'O' && 0 <= number_value)
827             {
828 #ifdef _NL_CURRENT
829               /* Get the locale specific alternate representation of
830                  the number NUMBER_VALUE.  If none exist NULL is returned.  */
831               const char *cp = _nl_get_alt_digit (number_value);
832
833               if (cp != NULL)
834                 {
835                   size_t digitlen = strlen (cp);
836                   if (digitlen != 0)
837                     {
838                       cpy (digitlen, cp);
839                       break;
840                     }
841                 }
842 #else
843 # if HAVE_STRFTIME
844               goto underlying_strftime;
845 # endif
846 #endif
847             }
848           {
849             unsigned int u = number_value;
850
851             bufp = buf + sizeof (buf);
852             negative_number = number_value < 0;
853
854             if (negative_number)
855               u = -u;
856
857             do
858               *--bufp = u % 10 + '0';
859             while ((u /= 10) != 0);
860           }
861
862         do_number_sign_and_padding:
863           if (negative_number)
864             *--bufp = '-';
865
866           if (pad != '-')
867             {
868               int padding = digits - (buf + sizeof (buf) - bufp);
869
870               if (pad == '_')
871                 {
872                   while (0 < padding--)
873                     *--bufp = ' ';
874                 }
875               else
876                 {
877                   bufp += negative_number;
878                   while (0 < padding--)
879                     *--bufp = '0';
880                   if (negative_number)
881                     *--bufp = '-';
882                 }
883             }
884
885           cpy (buf + sizeof (buf) - bufp, bufp);
886           break;
887
888         case 'F':
889           if (modifier != 0)
890             goto bad_format;
891           subfmt = "%Y-%m-%d";
892           goto subformat;
893
894         case 'H':
895           if (modifier == 'E')
896             goto bad_format;
897
898           DO_NUMBER (2, tp->tm_hour);
899
900         case 'I':
901           if (modifier == 'E')
902             goto bad_format;
903
904           DO_NUMBER (2, hour12);
905
906         case 'k':               /* GNU extension.  */
907           if (modifier == 'E')
908             goto bad_format;
909
910           DO_NUMBER_SPACEPAD (2, tp->tm_hour);
911
912         case 'l':               /* GNU extension.  */
913           if (modifier == 'E')
914             goto bad_format;
915
916           DO_NUMBER_SPACEPAD (2, hour12);
917
918         case 'j':
919           if (modifier == 'E')
920             goto bad_format;
921
922           DO_NUMBER (3, 1 + tp->tm_yday);
923
924         case 'M':
925           if (modifier == 'E')
926             goto bad_format;
927
928           DO_NUMBER (2, tp->tm_min);
929
930         case 'm':
931           if (modifier == 'E')
932             goto bad_format;
933
934           DO_NUMBER (2, tp->tm_mon + 1);
935
936         case 'n':               /* POSIX.2 extension.  */
937           add (1, *p = '\n');
938           break;
939
940         case 'P':
941           to_lowcase = 1;
942 #if !defined _NL_CURRENT && HAVE_STRFTIME
943           format_char = 'p';
944 #endif
945           /* FALLTHROUGH */
946
947         case 'p':
948           if (change_case)
949             {
950               to_uppcase = 0;
951               to_lowcase = 1;
952             }
953 #if defined _NL_CURRENT || !HAVE_STRFTIME
954           cpy (ap_len, ampm);
955           break;
956 #else
957           goto underlying_strftime;
958 #endif
959
960         case 'R':               /* GNU extension.  */
961           subfmt = "%H:%M";
962           goto subformat;
963
964         case 'r':               /* POSIX.2 extension.  */
965 #ifdef _NL_CURRENT
966           if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
967 #endif
968             subfmt = "%I:%M:%S %p";
969           goto subformat;
970
971         case 'S':
972           if (modifier == 'E')
973             goto bad_format;
974
975           DO_NUMBER (2, tp->tm_sec);
976
977         case 's':               /* GNU extension.  */
978           {
979             struct tm ltm;
980             time_t t;
981
982             ltm = *tp;
983             t = mktime (&ltm);
984
985             /* Generate string value for T using time_t arithmetic;
986                this works even if sizeof (long) < sizeof (time_t).  */
987
988             bufp = buf + sizeof (buf);
989             negative_number = t < 0;
990
991             do
992               {
993                 int d = t % 10;
994                 t /= 10;
995
996                 if (negative_number)
997                   {
998                     d = -d;
999
1000                     /* Adjust if division truncates to minus infinity.  */
1001                     if (0 < -1 % 10 && d < 0)
1002                       {
1003                         t++;
1004                         d += 10;
1005                       }
1006                   }
1007
1008                 *--bufp = d + '0';
1009               }
1010             while (t != 0);
1011
1012             digits = 1;
1013             goto do_number_sign_and_padding;
1014           }
1015
1016         case 'X':
1017           if (modifier == 'O')
1018             goto bad_format;
1019 #ifdef _NL_CURRENT
1020           if (! (modifier == 'E'
1021                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
1022             subfmt = _NL_CURRENT (LC_TIME, T_FMT);
1023           goto subformat;
1024 #else
1025 # if HAVE_STRFTIME
1026           goto underlying_strftime;
1027 # else
1028           /* Fall through.  */
1029 # endif
1030 #endif
1031         case 'T':               /* POSIX.2 extension.  */
1032           subfmt = "%H:%M:%S";
1033           goto subformat;
1034
1035         case 't':               /* POSIX.2 extension.  */
1036           add (1, *p = '\t');
1037           break;
1038
1039         case 'f':
1040         case 'u':               /* POSIX.2 extension.  */
1041           DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1042
1043         case 'U':
1044           if (modifier == 'E')
1045             goto bad_format;
1046
1047           DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1048
1049         case 'V':
1050         case 'g':               /* GNU extension.  */
1051         case 'G':               /* GNU extension.  */
1052           if (modifier == 'E')
1053             goto bad_format;
1054           {
1055             int year = tp->tm_year + TM_YEAR_BASE;
1056             int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1057
1058             if (days < 0)
1059               {
1060                 /* This ISO week belongs to the previous year.  */
1061                 year--;
1062                 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
1063                                       tp->tm_wday);
1064               }
1065             else
1066               {
1067                 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1068                                        tp->tm_wday);
1069                 if (0 <= d)
1070                   {
1071                     /* This ISO week belongs to the next year.  */
1072                     year++;
1073                     days = d;
1074                   }
1075               }
1076
1077             switch (*f)
1078               {
1079               case 'g':
1080                 DO_NUMBER (2, (year % 100 + 100) % 100);
1081
1082               case 'G':
1083                 DO_NUMBER (1, year);
1084
1085               default:
1086                 DO_NUMBER (2, days / 7 + 1);
1087               }
1088           }
1089
1090         case 'W':
1091           if (modifier == 'E')
1092             goto bad_format;
1093
1094           DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1095
1096         case 'w':
1097           if (modifier == 'E')
1098             goto bad_format;
1099
1100           DO_NUMBER (1, tp->tm_wday);
1101
1102         case 'Y':
1103           if (modifier == 'E')
1104             {
1105 #if HAVE_STRUCT_ERA_ENTRY
1106               struct era_entry *era = _nl_get_era_entry (tp);
1107               if (era)
1108                 {
1109                   subfmt = strchr (era->name_fmt, '\0') + 1;
1110                   goto subformat;
1111                 }
1112 #else
1113 # if HAVE_STRFTIME
1114               goto underlying_strftime;
1115 # endif
1116 #endif
1117             }
1118           if (modifier == 'O')
1119             goto bad_format;
1120           else
1121             DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
1122
1123         case 'y':
1124           if (modifier == 'E')
1125             {
1126 #if HAVE_STRUCT_ERA_ENTRY
1127               struct era_entry *era = _nl_get_era_entry (tp);
1128               if (era)
1129                 {
1130                   int delta = tp->tm_year - era->start_date[0];
1131                   DO_NUMBER (1, (era->offset
1132                                  + (era->direction == '-' ? -delta : delta)));
1133                 }
1134 #else
1135 # if HAVE_STRFTIME
1136               goto underlying_strftime;
1137 # endif
1138 #endif
1139             }
1140           DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
1141
1142         case 'Z':
1143           if (change_case)
1144             {
1145               to_uppcase = 0;
1146               to_lowcase = 1;
1147             }
1148           cpy (zonelen, zone);
1149           break;
1150
1151         case 'z':               /* GNU extension.  */
1152           if (tp->tm_isdst < 0)
1153             break;
1154
1155           {
1156             int diff;
1157 #if HAVE_TM_GMTOFF
1158             diff = tp->tm_gmtoff;
1159 #else
1160             struct tm gtm;
1161             struct tm ltm;
1162             time_t lt;
1163
1164             ltm = *tp;
1165             lt = mktime (&ltm);
1166
1167             if (lt == (time_t) -1)
1168               {
1169                 /* mktime returns -1 for errors, but -1 is also a
1170                    valid time_t value.  Check whether an error really
1171                    occurred.  */
1172                 struct tm tm;
1173                 localtime_r (&lt, &tm);
1174
1175                 if ((ltm.tm_sec ^ tm.tm_sec)
1176                     | (ltm.tm_min ^ tm.tm_min)
1177                     | (ltm.tm_hour ^ tm.tm_hour)
1178                     | (ltm.tm_mday ^ tm.tm_mday)
1179                     | (ltm.tm_mon ^ tm.tm_mon)
1180                     | (ltm.tm_year ^ tm.tm_year))
1181                   break;
1182               }
1183
1184             if (! gmtime_r (&lt, &gtm))
1185               break;
1186
1187             diff = tm_diff (&ltm, &gtm);
1188 #endif
1189
1190             if (diff < 0)
1191               {
1192                 add (1, *p = '-');
1193                 diff = -diff;
1194               }
1195             else
1196               add (1, *p = '+');
1197
1198             diff /= 60;
1199             DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1200           }
1201
1202         case '\0':              /* GNU extension: % at end of format.  */
1203             --f;
1204             /* Fall through.  */
1205         default:
1206           /* Unknown format; output the format, including the '%',
1207              since this is most likely the right thing to do if a
1208              multibyte string has been misparsed.  */
1209         bad_format:
1210           {
1211             int flen;
1212             for (flen = 1; f[1 - flen] != '%'; flen++)
1213               continue;
1214             cpy (flen, &f[1 - flen]);
1215           }
1216           break;
1217         }
1218     }
1219
1220   if (p)
1221     *p = '\0';
1222   return i;
1223 }