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