(my_strftime): Delay use of *tp values until latest possible point to
[kopensolaris-gnu/glibc.git] / time / strftime.c
1 /* Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Some hosts need this in order to declare localtime_r properly.  */
24 #ifndef __EXTENSIONS__
25 # define __EXTENSIONS__ 1
26 #endif
27
28 #ifdef _LIBC
29 # define HAVE_LIMITS_H 1
30 # define HAVE_MBLEN 1
31 # define HAVE_MBRLEN 1
32 # define HAVE_STRUCT_ERA_ENTRY 1
33 # define HAVE_TM_GMTOFF 1
34 # define HAVE_TM_ZONE 1
35 # define HAVE_TZNAME 1
36 # define HAVE_TZSET 1
37 # define MULTIBYTE_IS_FORMAT_SAFE 1
38 # define STDC_HEADERS 1
39 # include "../locale/localeinfo.h"
40 #endif
41
42 #if defined emacs && !defined HAVE_BCOPY
43 # define HAVE_MEMCPY 1
44 #endif
45
46 #include <ctype.h>
47 #include <sys/types.h>          /* Some systems define `time_t' here.  */
48
49 #ifdef TIME_WITH_SYS_TIME
50 # include <sys/time.h>
51 # include <time.h>
52 #else
53 # ifdef HAVE_SYS_TIME_H
54 #  include <sys/time.h>
55 # else
56 #  include <time.h>
57 # endif
58 #endif
59 #if HAVE_TZNAME
60 extern char *tzname[];
61 #endif
62
63 /* Do multibyte processing if multibytes are supported, unless
64    multibyte sequences are safe in formats.  Multibyte sequences are
65    safe if they cannot contain byte sequences that look like format
66    conversion specifications.  The GNU C Library uses UTF8 multibyte
67    encoding, which is safe for formats, but strftime.c can be used
68    with other C libraries that use unsafe encodings.  */
69 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
70
71 #if DO_MULTIBYTE
72 # if HAVE_MBRLEN
73 #  include <wchar.h>
74 # else
75    /* Simulate mbrlen with mblen as best we can.  */
76 #  define mbstate_t int
77 #  define mbrlen(s, n, ps) mblen (s, n)
78 #  define mbsinit(ps) (*(ps) == 0)
79 # endif
80   static const mbstate_t mbstate_zero;
81 #endif
82
83 #if HAVE_LIMITS_H
84 # include <limits.h>
85 #endif
86
87 #if STDC_HEADERS
88 # include <stddef.h>
89 # include <stdlib.h>
90 # include <string.h>
91 #else
92 # ifndef HAVE_MEMCPY
93 #  define memcpy(d, s, n) bcopy ((s), (d), (n))
94 # endif
95 #endif
96
97 #ifdef _LIBC
98 # define MEMPCPY(d, s, n) __mempcpy (d, s, n)
99 #else
100 # ifndef HAVE_MEMPCPY
101 #  define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
102 # endif
103 #endif
104
105 #ifndef __P
106 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
107 #  define __P(args) args
108 # else
109 #  define __P(args) ()
110 # endif  /* GCC.  */
111 #endif  /* Not __P.  */
112
113 #ifndef PTR
114 # ifdef __STDC__
115 #  define PTR void *
116 # else
117 #  define PTR char *
118 # endif
119 #endif
120
121 #ifndef CHAR_BIT
122 # define CHAR_BIT 8
123 #endif
124
125 #ifndef NULL
126 # define NULL 0
127 #endif
128
129 #define TYPE_SIGNED(t) ((t) -1 < 0)
130
131 /* Bound on length of the string representing an integer value of type t.
132    Subtract one for the sign bit if t is signed;
133    302 / 1000 is log10 (2) rounded up;
134    add one for integer division truncation;
135    add one more for a minus sign if t is signed.  */
136 #define INT_STRLEN_BOUND(t) \
137   ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
138
139 #define TM_YEAR_BASE 1900
140
141 #ifndef __isleap
142 /* Nonzero if YEAR is a leap year (every 4 years,
143    except every 100th isn't, and every 400th is).  */
144 # define __isleap(year) \
145   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
146 #endif
147
148
149 #ifdef _LIBC
150 # define gmtime_r __gmtime_r
151 # define localtime_r __localtime_r
152 # define tzname __tzname
153 # define tzset __tzset
154 #else
155 # if ! HAVE_LOCALTIME_R
156 #  if ! HAVE_TM_GMTOFF
157 /* Approximate gmtime_r as best we can in its absence.  */
158 #   undef gmtime_r
159 #   define gmtime_r my_gmtime_r
160 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
161 static struct tm *
162 gmtime_r (t, tp)
163      const time_t *t;
164      struct tm *tp;
165 {
166   struct tm *l = gmtime (t);
167   if (! l)
168     return 0;
169   *tp = *l;
170   return tp;
171 }
172 #  endif /* ! HAVE_TM_GMTOFF */
173
174 /* Approximate localtime_r as best we can in its absence.  */
175 #  undef localtime_r
176 #  define localtime_r my_ftime_localtime_r
177 static struct tm *localtime_r __P ((const time_t *, struct tm *));
178 static struct tm *
179 localtime_r (t, tp)
180      const time_t *t;
181      struct tm *tp;
182 {
183   struct tm *l = localtime (t);
184   if (! l)
185     return 0;
186   *tp = *l;
187   return tp;
188 }
189 # endif /* ! HAVE_LOCALTIME_R */
190 #endif /* ! defined _LIBC */
191
192
193 #if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
194 /* Some systems lack the `memset' function and we don't want to
195    introduce additional dependencies.  */
196 /* The SGI compiler reportedly barfs on the trailing null
197    if we use a string constant as the initializer.  28 June 1997, rms.  */
198 static const char spaces[16] = /* "                " */
199   { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
200 static const char zeroes[16] = /* "0000000000000000" */
201   { '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0' };
202
203 # define memset_space(P, Len) \
204   do {                                                                        \
205     int _len = (Len);                                                         \
206                                                                               \
207     do                                                                        \
208       {                                                                       \
209         int _this = _len > 16 ? 16 : _len;                                    \
210         (P) = MEMPCPY ((P), spaces, _this);                                   \
211         _len -= _this;                                                        \
212       }                                                                       \
213     while (_len > 0);                                                         \
214   } while (0)
215
216 # define memset_zero(P, Len) \
217   do {                                                                        \
218     int _len = (Len);                                                         \
219                                                                               \
220     do                                                                        \
221       {                                                                       \
222         int _this = _len > 16 ? 16 : _len;                                    \
223         (P) = MEMPCPY ((P), zeroes, _this);                                   \
224         _len -= _this;                                                        \
225       }                                                                       \
226     while (_len > 0);                                                         \
227   } while (0)
228 #else
229 # define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
230 # define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
231 #endif
232
233 #define add(n, f)                                                             \
234   do                                                                          \
235     {                                                                         \
236       int _n = (n);                                                           \
237       int _delta = width - _n;                                                \
238       int _incr = _n + (_delta > 0 ? _delta : 0);                             \
239       if (i + _incr >= maxsize)                                               \
240         return 0;                                                             \
241       if (p)                                                                  \
242         {                                                                     \
243           if (_delta > 0)                                                     \
244             {                                                                 \
245               if (pad == '0')                                                 \
246                 memset_zero (p, _delta);                                      \
247               else                                                            \
248                 memset_space (p, _delta);                                     \
249             }                                                                 \
250           f;                                                                  \
251           p += _n;                                                            \
252         }                                                                     \
253       i += _incr;                                                             \
254     } while (0)
255
256 #define cpy(n, s) \
257     add ((n),                                                                 \
258          if (to_lowcase)                                                      \
259            memcpy_lowcase (p, (s), _n);                                       \
260          else if (to_uppcase)                                                 \
261            memcpy_uppcase (p, (s), _n);                                       \
262          else                                                                 \
263            memcpy ((PTR) p, (PTR) (s), _n))
264
265
266
267 #ifdef _LIBC
268 # define TOUPPER(Ch) toupper (Ch)
269 # define TOLOWER(Ch) tolower (Ch)
270 #else
271 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
272 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
273 #endif
274 /* We don't use `isdigit' here since the locale dependent
275    interpretation is not what we want here.  We only need to accept
276    the arabic digits in the ASCII range.  One day there is perhaps a
277    more reliable way to accept other sets of digits.  */
278 #define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
279
280 static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
281
282 static char *
283 memcpy_lowcase (dest, src, len)
284      char *dest;
285      const char *src;
286      size_t len;
287 {
288   while (len-- > 0)
289     dest[len] = TOLOWER ((unsigned char) src[len]);
290   return dest;
291 }
292
293 static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
294
295 static char *
296 memcpy_uppcase (dest, src, len)
297      char *dest;
298      const char *src;
299      size_t len;
300 {
301   while (len-- > 0)
302     dest[len] = TOUPPER ((unsigned char) src[len]);
303   return dest;
304 }
305
306
307 #if ! HAVE_TM_GMTOFF
308 /* Yield the difference between *A and *B,
309    measured in seconds, ignoring leap seconds.  */
310 # define tm_diff ftime_tm_diff
311 static int tm_diff __P ((const struct tm *, const struct tm *));
312 static int
313 tm_diff (a, b)
314      const struct tm *a;
315      const struct tm *b;
316 {
317   /* Compute intervening leap days correctly even if year is negative.
318      Take care to avoid int overflow in leap day calculations,
319      but it's OK to assume that A and B are close to each other.  */
320   int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
321   int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
322   int a100 = a4 / 25 - (a4 % 25 < 0);
323   int b100 = b4 / 25 - (b4 % 25 < 0);
324   int a400 = a100 >> 2;
325   int b400 = b100 >> 2;
326   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
327   int years = a->tm_year - b->tm_year;
328   int days = (365 * years + intervening_leap_days
329               + (a->tm_yday - b->tm_yday));
330   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
331                 + (a->tm_min - b->tm_min))
332           + (a->tm_sec - b->tm_sec));
333 }
334 #endif /* ! HAVE_TM_GMTOFF */
335
336
337
338 /* The number of days from the first day of the first ISO week of this
339    year to the year day YDAY with week day WDAY.  ISO weeks start on
340    Monday; the first ISO week has the year's first Thursday.  YDAY may
341    be as small as YDAY_MINIMUM.  */
342 #define ISO_WEEK_START_WDAY 1 /* Monday */
343 #define ISO_WEEK1_WDAY 4 /* Thursday */
344 #define YDAY_MINIMUM (-366)
345 static int iso_week_days __P ((int, int));
346 #ifdef __GNUC__
347 __inline__
348 #endif
349 static int
350 iso_week_days (yday, wday)
351      int yday;
352      int wday;
353 {
354   /* Add enough to the first operand of % to make it nonnegative.  */
355   int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
356   return (yday
357           - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
358           + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
359 }
360
361
362 #if !(defined _NL_CURRENT || HAVE_STRFTIME)
363 static char const weekday_name[][10] =
364   {
365     "Sunday", "Monday", "Tuesday", "Wednesday",
366     "Thursday", "Friday", "Saturday"
367   };
368 static char const month_name[][10] =
369   {
370     "January", "February", "March", "April", "May", "June",
371     "July", "August", "September", "October", "November", "December"
372   };
373 #endif
374
375
376 #ifdef emacs
377 # define my_strftime emacs_strftime
378 #else
379 # define my_strftime strftime
380 #endif
381
382 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
383   /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
384      Work around this bug by copying *tp before it might be munged.  */
385   size_t _strftime_copytm __P ((char *, size_t, const char *,
386                                 const struct tm *));
387   size_t
388   my_strftime (s, maxsize, format, tp)
389       char *s;
390       size_t maxsize;
391       const char *format;
392       const struct tm *tp;
393   {
394     struct tm tmcopy;
395     tmcopy = *tp;
396     return _strftime_copytm (s, maxsize, format, &tmcopy);
397   }
398 # undef my_strftime
399 # define my_strftime(S, Maxsize, Format, Tp) \
400   _strftime_copytm (S, Maxsize, Format, Tp)
401 #endif
402
403
404 /* Write information from TP into S according to the format
405    string FORMAT, writing no more that MAXSIZE characters
406    (including the terminating '\0') and returning number of
407    characters written.  If S is NULL, nothing will be written
408    anywhere, so to determine how many characters would be
409    written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
410 size_t
411 my_strftime (s, maxsize, format, tp)
412       char *s;
413       size_t maxsize;
414       const char *format;
415       const struct tm *tp;
416 {
417   int hour12 = tp->tm_hour;
418 #ifdef _NL_CURRENT
419   /* We cannot make the following values variables since we must dealy
420      the evaluation of these values until really needed since some
421      expressions might not be valid in every situation.  The `struct tm'
422      might be generated by a strptime() call and therefore initialized
423      only a few elements.  Dereference the pointers only if the format
424      requires this.  Then it is ok to fail if the pointers are invalid.  */
425 # define a_wkday _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday)
426 # define f_wkday _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday)
427 # define f_wkday _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday)
428 # define a_month _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon)
429 # define f_month _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon)
430 # define ampm _NL_CURRENT (LC_TIME, hour12 > 11 ? PM_STR : AM_STR)
431
432 # define aw_len strlen (a_wkday)
433 # define am_len strlen (a_month)
434 # define ap_len strlen (ampm)
435
436 # define wkday_len strlen (f_wkday)
437 # define month_len strlen (f_month)
438 #else
439 # if !HAVE_STRFTIME
440   const char *const f_wkday = weekday_name[tp->tm_wday];
441   const char *const f_month = month_name[tp->tm_mon];
442   const char *const a_wkday = f_wkday;
443   const char *const a_month = f_month;
444   const char *const ampm = "AMPM" + 2 * (hour12 > 11);
445   size_t aw_len = 3;
446   size_t am_len = 3;
447   size_t ap_len = 2;
448
449   size_t wkday_len = strlen (f_wkday);
450   size_t month_len = strlen (f_month);
451 # endif
452 #endif
453   const char *zone;
454   size_t i = 0;
455   char *p = s;
456   const char *f;
457
458   zone = NULL;
459 #if HAVE_TM_ZONE
460   /* The POSIX test suite assumes that setting
461      the environment variable TZ to a new value before calling strftime()
462      will influence the result (the %Z format) even if the information in
463      TP is computed with a totally different time zone.
464      This is bogus: though POSIX allows bad behavior like this,
465      POSIX does not require it.  Do the right thing instead.  */
466   zone = (const char *) tp->tm_zone;
467 #endif
468 #if HAVE_TZNAME && HAVE_TZSET
469   /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
470      time zone names contained in the external variable `tzname' shall
471      be set as if the tzset() function had been called.  */
472   tzset ();
473 #endif
474
475   if (hour12 > 12)
476     hour12 -= 12;
477   else
478     if (hour12 == 0) hour12 = 12;
479
480   for (f = format; *f != '\0'; ++f)
481     {
482       int pad = 0;              /* Padding for number ('-', '_', or 0).  */
483       int modifier;             /* Field modifier ('E', 'O', or 0).  */
484       int digits;               /* Max digits for numeric format.  */
485       int number_value;         /* Numeric value to be printed.  */
486       int negative_number;      /* 1 if the number is negative.  */
487       const char *subfmt;
488       char *bufp;
489       char buf[1 + (sizeof (int) < sizeof (time_t)
490                     ? INT_STRLEN_BOUND (time_t)
491                     : INT_STRLEN_BOUND (int))];
492       int width = -1;
493       int to_lowcase = 0;
494       int to_uppcase = 0;
495       int change_case = 0;
496       int format_char;
497
498 #if DO_MULTIBYTE
499
500        switch (*f)
501         {
502         case '%':
503           break;
504
505         case '\a': case '\b': case '\t': case '\n':
506         case '\v': case '\f': case '\r':
507         case ' ': case '!': case '"': case '#': case '&': case'\'':
508         case '(': case ')': case '*': case '+': case ',': case '-':
509         case '.': case '/': case '0': case '1': case '2': case '3':
510         case '4': case '5': case '6': case '7': case '8': case '9':
511         case ':': case ';': case '<': case '=': case '>': case '?':
512         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
513         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
514         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
515         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
516         case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
517         case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
518         case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
519         case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
520         case 'r': case 's': case 't': case 'u': case 'v': case 'w':
521         case 'x': case 'y': case 'z': case '{': case '|': case '}':
522         case '~':
523           /* The C Standard requires these 98 characters (plus '%') to
524              be in the basic execution character set.  None of these
525              characters can start a multibyte sequence, so they need
526              not be analyzed further.  */
527           add (1, *p = *f);
528           continue;
529
530         default:
531           /* Copy this multibyte sequence until we reach its end, find
532              an error, or come back to the initial shift state.  */
533           {
534             mbstate_t mbstate = mbstate_zero;
535             size_t len = 0;
536
537             do
538               {
539                 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
540
541                 if (bytes == 0)
542                   break;
543
544                 if (bytes == (size_t) -2)
545                   {
546                     len += strlen (f + len);
547                     break;
548                   }
549
550                 if (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             f += len - 1;
562             continue;
563           }
564         }
565
566 #else /* ! DO_MULTIBYTE */
567
568       /* Either multibyte encodings are not supported, or they are
569          safe for formats, so any non-'%' byte can be copied through.  */
570       if (*f != '%')
571         {
572           add (1, *p = *f);
573           continue;
574         }
575
576 #endif /* ! DO_MULTIBYTE */
577
578       /* Check for flags that can modify a format.  */
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
1149 #if HAVE_TZNAME
1150           /* The tzset() call might have changed the value.  */
1151           if (!(zone && *zone) && tp->tm_isdst >= 0)
1152             zone = tzname[tp->tm_isdst];
1153 #endif
1154           if (! zone)
1155             zone = "";          /* POSIX.2 requires the empty string here.  */
1156
1157           cpy (strlen (zone), zone);
1158           break;
1159
1160         case 'z':               /* GNU extension.  */
1161           if (tp->tm_isdst < 0)
1162             break;
1163
1164           {
1165             int diff;
1166 #if HAVE_TM_GMTOFF
1167             diff = tp->tm_gmtoff;
1168 #else
1169             struct tm gtm;
1170             struct tm ltm;
1171             time_t lt;
1172
1173             ltm = *tp;
1174             lt = mktime (&ltm);
1175
1176             if (lt == (time_t) -1)
1177               {
1178                 /* mktime returns -1 for errors, but -1 is also a
1179                    valid time_t value.  Check whether an error really
1180                    occurred.  */
1181                 struct tm tm;
1182
1183                 if (! localtime_r (&lt, &tm)
1184                     || ((ltm.tm_sec ^ tm.tm_sec)
1185                         | (ltm.tm_min ^ tm.tm_min)
1186                         | (ltm.tm_hour ^ tm.tm_hour)
1187                         | (ltm.tm_mday ^ tm.tm_mday)
1188                         | (ltm.tm_mon ^ tm.tm_mon)
1189                         | (ltm.tm_year ^ tm.tm_year)))
1190                   break;
1191               }
1192
1193             if (! gmtime_r (&lt, &gtm))
1194               break;
1195
1196             diff = tm_diff (&ltm, &gtm);
1197 #endif
1198
1199             if (diff < 0)
1200               {
1201                 add (1, *p = '-');
1202                 diff = -diff;
1203               }
1204             else
1205               add (1, *p = '+');
1206
1207             diff /= 60;
1208             DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1209           }
1210
1211         case '\0':              /* GNU extension: % at end of format.  */
1212             --f;
1213             /* Fall through.  */
1214         default:
1215           /* Unknown format; output the format, including the '%',
1216              since this is most likely the right thing to do if a
1217              multibyte string has been misparsed.  */
1218         bad_format:
1219           {
1220             int flen;
1221             for (flen = 1; f[1 - flen] != '%'; flen++)
1222               continue;
1223             cpy (flen, &f[1 - flen]);
1224           }
1225           break;
1226         }
1227     }
1228
1229   if (p)
1230     *p = '\0';
1231   return i;
1232 }