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