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