1170754Sdelphij/* Copyright (C) 1991-1999, 2000, 2001, 2003 Free Software Foundation, Inc.
2170754Sdelphij
3170754Sdelphij   NOTE: The canonical source of this file is maintained with the GNU C Library.
4170754Sdelphij   Bugs can be reported to bug-glibc@prep.ai.mit.edu.
5170754Sdelphij
6170754Sdelphij   This program is free software; you can redistribute it and/or modify
7170754Sdelphij   it under the terms of the GNU General Public License as published by
8170754Sdelphij   the Free Software Foundation; either version 2, or (at your option)
9170754Sdelphij   any later version.
10170754Sdelphij
11170754Sdelphij   This program is distributed in the hope that it will be useful,
12170754Sdelphij   but WITHOUT ANY WARRANTY; without even the implied warranty of
13170754Sdelphij   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14170754Sdelphij   GNU General Public License for more details.
15170754Sdelphij
16170754Sdelphij   You should have received a copy of the GNU General Public License along
17170754Sdelphij   with this program; if not, write to the Free Software Foundation,
18170754Sdelphij   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19170754Sdelphij
20170754Sdelphij#ifdef HAVE_CONFIG_H
21170754Sdelphij# include <config.h>
22170754Sdelphij#endif
23170754Sdelphij
24170754Sdelphij#ifdef _LIBC
25170754Sdelphij# define HAVE_MBLEN 1
26170754Sdelphij# define HAVE_MBRLEN 1
27170754Sdelphij# define HAVE_STRUCT_ERA_ENTRY 1
28170754Sdelphij# define HAVE_TM_GMTOFF 1
29170754Sdelphij# define HAVE_TM_ZONE 1
30170754Sdelphij# define HAVE_TZNAME 1
31170754Sdelphij# define HAVE_TZSET 1
32170754Sdelphij# define MULTIBYTE_IS_FORMAT_SAFE 1
33170754Sdelphij# include "../locale/localeinfo.h"
34170754Sdelphij#endif
35170754Sdelphij
36170754Sdelphij#include <ctype.h>
37170754Sdelphij#include <sys/types.h>		/* Some systems define `time_t' here.  */
38170754Sdelphij
39170754Sdelphij#ifdef TIME_WITH_SYS_TIME
40170754Sdelphij# include <sys/time.h>
41170754Sdelphij# include <time.h>
42170754Sdelphij#else
43170754Sdelphij# ifdef HAVE_SYS_TIME_H
44170754Sdelphij#  include <sys/time.h>
45170754Sdelphij# else
46170754Sdelphij#  include <time.h>
47170754Sdelphij# endif
48170754Sdelphij#endif
49170754Sdelphij#if HAVE_TZNAME
50170754Sdelphijextern char *tzname[];
51170754Sdelphij#endif
52170754Sdelphij
53170754Sdelphij/* Do multibyte processing if multibytes are supported, unless
54170754Sdelphij   multibyte sequences are safe in formats.  Multibyte sequences are
55170754Sdelphij   safe if they cannot contain byte sequences that look like format
56170754Sdelphij   conversion specifications.  The GNU C Library uses UTF8 multibyte
57170754Sdelphij   encoding, which is safe for formats, but strftime.c can be used
58170754Sdelphij   with other C libraries that use unsafe encodings.  */
59170754Sdelphij#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
60170754Sdelphij
61170754Sdelphij#if DO_MULTIBYTE
62170754Sdelphij# if HAVE_MBRLEN
63170754Sdelphij#  include <wchar.h>
64170754Sdelphij# else
65170754Sdelphij   /* Simulate mbrlen with mblen as best we can.  */
66170754Sdelphij#  define mbstate_t int
67170754Sdelphij#  define mbrlen(s, n, ps) mblen (s, n)
68170754Sdelphij#  define mbsinit(ps) (*(ps) == 0)
69170754Sdelphij# endif
70170754Sdelphij  static const mbstate_t mbstate_zero;
71170754Sdelphij#endif
72170754Sdelphij
73170754Sdelphij#include <limits.h>
74170754Sdelphij#include <stddef.h>
75170754Sdelphij#include <stdlib.h>
76170754Sdelphij#include <string.h>
77170754Sdelphij
78170754Sdelphij#ifdef COMPILE_WIDE
79170754Sdelphij# include <endian.h>
80170754Sdelphij# define CHAR_T wchar_t
81170754Sdelphij# define UCHAR_T unsigned int
82170754Sdelphij# define L_(Str) L##Str
83170754Sdelphij# define NLW(Sym) _NL_W##Sym
84170754Sdelphij
85170754Sdelphij# define MEMCPY(d, s, n) __wmemcpy (d, s, n)
86170754Sdelphij# define STRLEN(s) __wcslen (s)
87170754Sdelphij
88170754Sdelphij#else
89170754Sdelphij# define CHAR_T char
90170754Sdelphij# define UCHAR_T unsigned char
91170754Sdelphij# define L_(Str) Str
92170754Sdelphij# define NLW(Sym) Sym
93170754Sdelphij
94170754Sdelphij# define MEMCPY(d, s, n) memcpy (d, s, n)
95170754Sdelphij# define STRLEN(s) strlen (s)
96170754Sdelphij
97170754Sdelphij# ifdef _LIBC
98170754Sdelphij#  define MEMPCPY(d, s, n) __mempcpy (d, s, n)
99170754Sdelphij# else
100170754Sdelphij#  ifndef HAVE_MEMPCPY
101170754Sdelphij#   define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
102170754Sdelphij#  endif
103170754Sdelphij# endif
104170754Sdelphij#endif
105170754Sdelphij
106170754Sdelphij#define TYPE_SIGNED(t) ((t) -1 < 0)
107170754Sdelphij
108170754Sdelphij/* Bound on length of the string representing an integer value of type t.
109170754Sdelphij   Subtract one for the sign bit if t is signed;
110170754Sdelphij   302 / 1000 is log10 (2) rounded up;
111170754Sdelphij   add one for integer division truncation;
112170754Sdelphij   add one more for a minus sign if t is signed.  */
113170754Sdelphij#define INT_STRLEN_BOUND(t) \
114170754Sdelphij ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
115170754Sdelphij
116170754Sdelphij#define TM_YEAR_BASE 1900
117170754Sdelphij
118170754Sdelphij#ifndef __isleap
119170754Sdelphij/* Nonzero if YEAR is a leap year (every 4 years,
120170754Sdelphij   except every 100th isn't, and every 400th is).  */
121170754Sdelphij# define __isleap(year)	\
122170754Sdelphij  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
123170754Sdelphij#endif
124170754Sdelphij
125170754Sdelphij
126170754Sdelphij#ifdef _LIBC
127170754Sdelphij# define tzname __tzname
128170754Sdelphij# define tzset __tzset
129170754Sdelphij#endif
130170754Sdelphij
131170754Sdelphij#if !HAVE_TM_GMTOFF
132170754Sdelphij/* Portable standalone applications should supply a "time_r.h" that
133170754Sdelphij   declares a POSIX-compliant localtime_r, for the benefit of older
134170754Sdelphij   implementations that lack localtime_r or have a nonstandard one.
135170754Sdelphij   See the gnulib time_r module for one way to implement this.  */
136170754Sdelphij# include "time_r.h"
137170754Sdelphij# undef __gmtime_r
138170754Sdelphij# undef __localtime_r
139170754Sdelphij# define __gmtime_r gmtime_r
140170754Sdelphij# define __localtime_r localtime_r
141170754Sdelphij#endif
142170754Sdelphij
143170754Sdelphij
144170754Sdelphij#ifdef COMPILE_WIDE
145170754Sdelphij# define memset_space(P, Len) (wmemset (P, L' ', Len), (P) += (Len))
146170754Sdelphij# define memset_zero(P, Len) (wmemset (P, L'0', Len), (P) += (Len))
147170754Sdelphij#else
148170754Sdelphij# define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
149170754Sdelphij# define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
150170754Sdelphij#endif
151170754Sdelphij
152170754Sdelphij#define add(n, f)							      \
153170754Sdelphij  do									      \
154170754Sdelphij    {									      \
155170754Sdelphij      int _n = (n);							      \
156170754Sdelphij      int _delta = width - _n;						      \
157170754Sdelphij      int _incr = _n + (_delta > 0 ? _delta : 0);			      \
158170754Sdelphij      if ((size_t) _incr >= maxsize - i)				      \
159170754Sdelphij	return 0;							      \
160170754Sdelphij      if (p)								      \
161170754Sdelphij	{								      \
162170754Sdelphij	  if (_delta > 0)						      \
163170754Sdelphij	    {								      \
164170754Sdelphij	      if (pad == L_('0'))					      \
165170754Sdelphij		memset_zero (p, _delta);				      \
166170754Sdelphij	      else							      \
167170754Sdelphij		memset_space (p, _delta);				      \
168170754Sdelphij	    }								      \
169170754Sdelphij	  f;								      \
170170754Sdelphij	  p += _n;							      \
171170754Sdelphij	}								      \
172170754Sdelphij      i += _incr;							      \
173170754Sdelphij    } while (0)
174170754Sdelphij
175170754Sdelphij#define cpy(n, s) \
176170754Sdelphij    add ((n),								      \
177170754Sdelphij	 if (to_lowcase)						      \
178170754Sdelphij	   memcpy_lowcase (p, (s), _n LOCALE_ARG);			      \
179170754Sdelphij	 else if (to_uppcase)						      \
180170754Sdelphij	   memcpy_uppcase (p, (s), _n LOCALE_ARG);			      \
181170754Sdelphij	 else								      \
182170754Sdelphij	   MEMCPY ((void *) p, (void const *) (s), _n))
183170754Sdelphij
184170754Sdelphij#ifdef COMPILE_WIDE
185170754Sdelphij# ifndef USE_IN_EXTENDED_LOCALE_MODEL
186170754Sdelphij#  undef __mbsrtowcs_l
187170754Sdelphij#  define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
188170754Sdelphij# endif
189170754Sdelphij# define widen(os, ws, l) \
190170754Sdelphij  {									      \
191170754Sdelphij    mbstate_t __st;							      \
192170754Sdelphij    const char *__s = os;						      \
193170754Sdelphij    memset (&__st, '\0', sizeof (__st));				      \
194170754Sdelphij    l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc);			      \
195170754Sdelphij    ws = (wchar_t *) alloca ((l + 1) * sizeof (wchar_t));		      \
196170754Sdelphij    (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc);			      \
197170754Sdelphij  }
198170754Sdelphij#endif
199170754Sdelphij
200170754Sdelphij
201170754Sdelphij#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
202170754Sdelphij/* We use this code also for the extended locale handling where the
203170754Sdelphij   function gets as an additional argument the locale which has to be
204170754Sdelphij   used.  To access the values we have to redefine the _NL_CURRENT
205170754Sdelphij   macro.  */
206170754Sdelphij# define strftime		__strftime_l
207170754Sdelphij# define wcsftime		__wcsftime_l
208170754Sdelphij# undef _NL_CURRENT
209170754Sdelphij# define _NL_CURRENT(category, item) \
210170754Sdelphij  (current->values[_NL_ITEM_INDEX (item)].string)
211170754Sdelphij# define LOCALE_ARG , loc
212170754Sdelphij# define LOCALE_PARAM_PROTO , __locale_t loc
213170754Sdelphij# define HELPER_LOCALE_ARG  , current
214170754Sdelphij#else
215170754Sdelphij# define LOCALE_PARAM_PROTO
216170754Sdelphij# define LOCALE_ARG
217170754Sdelphij# ifdef _LIBC
218170754Sdelphij#  define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
219170754Sdelphij# else
220170754Sdelphij#  define HELPER_LOCALE_ARG
221170754Sdelphij# endif
222170754Sdelphij#endif
223170754Sdelphij
224170754Sdelphij#ifdef COMPILE_WIDE
225170754Sdelphij# ifdef USE_IN_EXTENDED_LOCALE_MODEL
226170754Sdelphij#  define TOUPPER(Ch, L) __towupper_l (Ch, L)
227170754Sdelphij#  define TOLOWER(Ch, L) __towlower_l (Ch, L)
228170754Sdelphij# else
229170754Sdelphij#  define TOUPPER(Ch, L) towupper (Ch)
230170754Sdelphij#  define TOLOWER(Ch, L) towlower (Ch)
231170754Sdelphij# endif
232170754Sdelphij#else
233170754Sdelphij# ifdef _LIBC
234170754Sdelphij#  ifdef USE_IN_EXTENDED_LOCALE_MODEL
235170754Sdelphij#   define TOUPPER(Ch, L) __toupper_l (Ch, L)
236170754Sdelphij#   define TOLOWER(Ch, L) __tolower_l (Ch, L)
237170754Sdelphij#  else
238170754Sdelphij#   define TOUPPER(Ch, L) toupper (Ch)
239170754Sdelphij#   define TOLOWER(Ch, L) tolower (Ch)
240170754Sdelphij#  endif
241170754Sdelphij# else
242170754Sdelphij#  define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
243170754Sdelphij#  define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
244170754Sdelphij# endif
245170754Sdelphij#endif
246170754Sdelphij/* We don't use `isdigit' here since the locale dependent
247170754Sdelphij   interpretation is not what we want here.  We only need to accept
248170754Sdelphij   the arabic digits in the ASCII range.  One day there is perhaps a
249170754Sdelphij   more reliable way to accept other sets of digits.  */
250170754Sdelphij#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
251170754Sdelphij
252170754Sdelphijstatic CHAR_T *
253170754Sdelphijmemcpy_lowcase (CHAR_T *dest, const CHAR_T *src,
254170754Sdelphij		size_t len LOCALE_PARAM_PROTO)
255170754Sdelphij{
256170754Sdelphij  while (len-- > 0)
257170754Sdelphij    dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
258170754Sdelphij  return dest;
259170754Sdelphij}
260170754Sdelphij
261170754Sdelphijstatic CHAR_T *
262170754Sdelphijmemcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
263170754Sdelphij		size_t len LOCALE_PARAM_PROTO)
264170754Sdelphij{
265170754Sdelphij  while (len-- > 0)
266170754Sdelphij    dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
267170754Sdelphij  return dest;
268170754Sdelphij}
269170754Sdelphij
270170754Sdelphij
271170754Sdelphij#if ! HAVE_TM_GMTOFF
272170754Sdelphij/* Yield the difference between *A and *B,
273170754Sdelphij   measured in seconds, ignoring leap seconds.  */
274170754Sdelphij# define tm_diff ftime_tm_diff
275170754Sdelphijstatic int
276170754Sdelphijtm_diff (const struct tm *a, const struct tm *b)
277170754Sdelphij{
278170754Sdelphij  /* Compute intervening leap days correctly even if year is negative.
279170754Sdelphij     Take care to avoid int overflow in leap day calculations,
280170754Sdelphij     but it's OK to assume that A and B are close to each other.  */
281170754Sdelphij  int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
282170754Sdelphij  int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
283170754Sdelphij  int a100 = a4 / 25 - (a4 % 25 < 0);
284170754Sdelphij  int b100 = b4 / 25 - (b4 % 25 < 0);
285170754Sdelphij  int a400 = a100 >> 2;
286170754Sdelphij  int b400 = b100 >> 2;
287170754Sdelphij  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
288170754Sdelphij  int years = a->tm_year - b->tm_year;
289170754Sdelphij  int days = (365 * years + intervening_leap_days
290170754Sdelphij	      + (a->tm_yday - b->tm_yday));
291170754Sdelphij  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
292170754Sdelphij		+ (a->tm_min - b->tm_min))
293170754Sdelphij	  + (a->tm_sec - b->tm_sec));
294170754Sdelphij}
295170754Sdelphij#endif /* ! HAVE_TM_GMTOFF */
296170754Sdelphij
297170754Sdelphij
298170754Sdelphij
299170754Sdelphij/* The number of days from the first day of the first ISO week of this
300170754Sdelphij   year to the year day YDAY with week day WDAY.  ISO weeks start on
301170754Sdelphij   Monday; the first ISO week has the year's first Thursday.  YDAY may
302170754Sdelphij   be as small as YDAY_MINIMUM.  */
303170754Sdelphij#define ISO_WEEK_START_WDAY 1 /* Monday */
304170754Sdelphij#define ISO_WEEK1_WDAY 4 /* Thursday */
305170754Sdelphij#define YDAY_MINIMUM (-366)
306170754Sdelphij#ifdef __GNUC__
307170754Sdelphij__inline__
308170754Sdelphij#endif
309170754Sdelphijstatic int
310170754Sdelphijiso_week_days (int yday, int wday)
311170754Sdelphij{
312170754Sdelphij  /* Add enough to the first operand of % to make it nonnegative.  */
313170754Sdelphij  int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
314170754Sdelphij  return (yday
315170754Sdelphij	  - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
316170754Sdelphij	  + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
317170754Sdelphij}
318170754Sdelphij
319170754Sdelphij
320170754Sdelphij#if !(defined _NL_CURRENT || HAVE_STRFTIME)
321170754Sdelphijstatic CHAR_T const weekday_name[][10] =
322170754Sdelphij  {
323170754Sdelphij    L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"),
324170754Sdelphij    L_("Thursday"), L_("Friday"), L_("Saturday")
325170754Sdelphij  };
326170754Sdelphijstatic CHAR_T const month_name[][10] =
327170754Sdelphij  {
328170754Sdelphij    L_("January"), L_("February"), L_("March"), L_("April"), L_("May"),
329170754Sdelphij    L_("June"), L_("July"), L_("August"), L_("September"), L_("October"),
330170754Sdelphij    L_("November"), L_("December")
331170754Sdelphij  };
332170754Sdelphij#endif
333170754Sdelphij
334170754Sdelphij
335170754Sdelphij/* When compiling this file, GNU applications can #define my_strftime
336170754Sdelphij   to a symbol (typically nstrftime) to get an extended strftime with
337170754Sdelphij   extra arguments UT and NS.  Emacs is a special case for now, but
338170754Sdelphij   this Emacs-specific code can be removed once Emacs's config.h
339170754Sdelphij   defines my_strftime.  */
340170754Sdelphij#if defined emacs && !defined my_strftime
341170754Sdelphij# define my_strftime nstrftime
342170754Sdelphij#endif
343170754Sdelphij
344170754Sdelphij#ifdef my_strftime
345170754Sdelphij# define extra_args , ut, ns
346170754Sdelphij# define extra_args_spec , int ut, int ns
347170754Sdelphij#else
348170754Sdelphij# ifdef COMPILE_WIDE
349170754Sdelphij#  define my_strftime wcsftime
350170754Sdelphij#  define nl_get_alt_digit _nl_get_walt_digit
351170754Sdelphij# else
352170754Sdelphij#  define my_strftime strftime
353170754Sdelphij#  define nl_get_alt_digit _nl_get_alt_digit
354170754Sdelphij# endif
355170754Sdelphij# define extra_args
356170754Sdelphij# define extra_args_spec
357170754Sdelphij/* We don't have this information in general.  */
358170754Sdelphij# define ut 0
359170754Sdelphij# define ns 0
360170754Sdelphij#endif
361170754Sdelphij
362170754Sdelphij#if ! defined _LIBC && ! HAVE_RUN_TZSET_TEST
363170754Sdelphij/* Solaris 2.5.x and 2.6 tzset sometimes modify the storage returned
364170754Sdelphij   by localtime.  On such systems, we must use the tzset and localtime
365170754Sdelphij   wrappers to work around the bug.  */
366170754Sdelphij"you must run the autoconf test for a working tzset function"
367170754Sdelphij#endif
368170754Sdelphij
369170754Sdelphij
370170754Sdelphij/* Write information from TP into S according to the format
371170754Sdelphij   string FORMAT, writing no more that MAXSIZE characters
372170754Sdelphij   (including the terminating '\0') and returning number of
373170754Sdelphij   characters written.  If S is NULL, nothing will be written
374170754Sdelphij   anywhere, so to determine how many characters would be
375170754Sdelphij   written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
376170754Sdelphijsize_t
377170754Sdelphijmy_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
378170754Sdelphij	     const struct tm *tp extra_args_spec LOCALE_PARAM_PROTO)
379170754Sdelphij{
380170754Sdelphij#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
381170754Sdelphij  struct locale_data *const current = loc->__locales[LC_TIME];
382170754Sdelphij#endif
383170754Sdelphij
384170754Sdelphij  int hour12 = tp->tm_hour;
385170754Sdelphij#ifdef _NL_CURRENT
386170754Sdelphij  /* We cannot make the following values variables since we must delay
387170754Sdelphij     the evaluation of these values until really needed since some
388170754Sdelphij     expressions might not be valid in every situation.  The `struct tm'
389170754Sdelphij     might be generated by a strptime() call that initialized
390170754Sdelphij     only a few elements.  Dereference the pointers only if the format
391170754Sdelphij     requires this.  Then it is ok to fail if the pointers are invalid.  */
392170754Sdelphij# define a_wkday \
393170754Sdelphij  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday))
394170754Sdelphij# define f_wkday \
395170754Sdelphij  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday))
396170754Sdelphij# define a_month \
397170754Sdelphij  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon))
398170754Sdelphij# define f_month \
399170754Sdelphij  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon))
400170754Sdelphij# define ampm \
401170754Sdelphij  ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11		      \
402170754Sdelphij				 ? NLW(PM_STR) : NLW(AM_STR)))
403170754Sdelphij
404170754Sdelphij# define aw_len STRLEN (a_wkday)
405170754Sdelphij# define am_len STRLEN (a_month)
406170754Sdelphij# define ap_len STRLEN (ampm)
407170754Sdelphij#else
408170754Sdelphij# if !HAVE_STRFTIME
409170754Sdelphij#  define f_wkday (weekday_name[tp->tm_wday])
410170754Sdelphij#  define f_month (month_name[tp->tm_mon])
411170754Sdelphij#  define a_wkday f_wkday
412170754Sdelphij#  define a_month f_month
413170754Sdelphij#  define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
414170754Sdelphij
415170754Sdelphij  size_t aw_len = 3;
416170754Sdelphij  size_t am_len = 3;
417170754Sdelphij  size_t ap_len = 2;
418170754Sdelphij# endif
419170754Sdelphij#endif
420170754Sdelphij  const char *zone;
421170754Sdelphij  size_t i = 0;
422170754Sdelphij  CHAR_T *p = s;
423170754Sdelphij  const CHAR_T *f;
424170754Sdelphij#if DO_MULTIBYTE && !defined COMPILE_WIDE
425170754Sdelphij  const char *format_end = NULL;
426170754Sdelphij#endif
427170754Sdelphij
428170754Sdelphij  zone = NULL;
429170754Sdelphij#if HAVE_TM_ZONE
430170754Sdelphij  /* The POSIX test suite assumes that setting
431170754Sdelphij     the environment variable TZ to a new value before calling strftime()
432170754Sdelphij     will influence the result (the %Z format) even if the information in
433170754Sdelphij     TP is computed with a totally different time zone.
434170754Sdelphij     This is bogus: though POSIX allows bad behavior like this,
435170754Sdelphij     POSIX does not require it.  Do the right thing instead.  */
436170754Sdelphij  zone = (const char *) tp->tm_zone;
437170754Sdelphij#endif
438170754Sdelphij#if HAVE_TZNAME
439170754Sdelphij  if (ut)
440170754Sdelphij    {
441170754Sdelphij      if (! (zone && *zone))
442170754Sdelphij	zone = "GMT";
443170754Sdelphij    }
444170754Sdelphij  else
445170754Sdelphij    {
446170754Sdelphij      /* POSIX.1 requires that local time zone information be used as
447170754Sdelphij	 though strftime called tzset.  */
448170754Sdelphij# if HAVE_TZSET
449170754Sdelphij      tzset ();
450170754Sdelphij# endif
451170754Sdelphij    }
452170754Sdelphij#endif
453170754Sdelphij
454170754Sdelphij  if (hour12 > 12)
455170754Sdelphij    hour12 -= 12;
456170754Sdelphij  else
457170754Sdelphij    if (hour12 == 0)
458170754Sdelphij      hour12 = 12;
459170754Sdelphij
460170754Sdelphij  for (f = format; *f != '\0'; ++f)
461170754Sdelphij    {
462170754Sdelphij      int pad = 0;		/* Padding for number ('-', '_', or 0).  */
463170754Sdelphij      int modifier;		/* Field modifier ('E', 'O', or 0).  */
464170754Sdelphij      int digits;		/* Max digits for numeric format.  */
465170754Sdelphij      int number_value;		/* Numeric value to be printed.  */
466170754Sdelphij      int negative_number;	/* 1 if the number is negative.  */
467170754Sdelphij      const CHAR_T *subfmt;
468170754Sdelphij      CHAR_T *bufp;
469170754Sdelphij      CHAR_T buf[1 + (sizeof (int) < sizeof (time_t)
470170754Sdelphij		      ? INT_STRLEN_BOUND (time_t)
471170754Sdelphij		      : INT_STRLEN_BOUND (int))];
472170754Sdelphij      int width = -1;
473170754Sdelphij      int to_lowcase = 0;
474170754Sdelphij      int to_uppcase = 0;
475170754Sdelphij      int change_case = 0;
476170754Sdelphij      int format_char;
477170754Sdelphij
478170754Sdelphij#if DO_MULTIBYTE && !defined COMPILE_WIDE
479170754Sdelphij      switch (*f)
480170754Sdelphij	{
481170754Sdelphij	case L_('%'):
482170754Sdelphij	  break;
483170754Sdelphij
484170754Sdelphij	case L_('\b'): case L_('\t'): case L_('\n'):
485170754Sdelphij	case L_('\v'): case L_('\f'): case L_('\r'):
486170754Sdelphij	case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
487170754Sdelphij	case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
488170754Sdelphij	case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
489170754Sdelphij	case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
490170754Sdelphij	case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
491170754Sdelphij	case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
492170754Sdelphij	case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
493170754Sdelphij	case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
494170754Sdelphij	case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
495170754Sdelphij	case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
496170754Sdelphij	case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
497170754Sdelphij	case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
498170754Sdelphij	case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
499170754Sdelphij	case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
500170754Sdelphij	case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
501170754Sdelphij	case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
502170754Sdelphij	case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
503170754Sdelphij	case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
504170754Sdelphij	case L_('~'):
505170754Sdelphij	  /* The C Standard requires these 98 characters (plus '%') to
506170754Sdelphij	     be in the basic execution character set.  None of these
507170754Sdelphij	     characters can start a multibyte sequence, so they need
508170754Sdelphij	     not be analyzed further.  */
509170754Sdelphij	  add (1, *p = *f);
510170754Sdelphij	  continue;
511170754Sdelphij
512170754Sdelphij	default:
513170754Sdelphij	  /* Copy this multibyte sequence until we reach its end, find
514170754Sdelphij	     an error, or come back to the initial shift state.  */
515170754Sdelphij	  {
516170754Sdelphij	    mbstate_t mbstate = mbstate_zero;
517170754Sdelphij	    size_t len = 0;
518170754Sdelphij	    size_t fsize;
519170754Sdelphij
520170754Sdelphij	    if (! format_end)
521170754Sdelphij	      format_end = f + strlen (f) + 1;
522170754Sdelphij	    fsize = format_end - f;
523170754Sdelphij
524170754Sdelphij	    do
525170754Sdelphij	      {
526170754Sdelphij		size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
527170754Sdelphij
528170754Sdelphij		if (bytes == 0)
529170754Sdelphij		  break;
530170754Sdelphij
531170754Sdelphij		if (bytes == (size_t) -2)
532170754Sdelphij		  {
533170754Sdelphij		    len += strlen (f + len);
534170754Sdelphij		    break;
535170754Sdelphij		  }
536170754Sdelphij
537170754Sdelphij		if (bytes == (size_t) -1)
538170754Sdelphij		  {
539170754Sdelphij		    len++;
540170754Sdelphij		    break;
541170754Sdelphij		  }
542170754Sdelphij
543170754Sdelphij		len += bytes;
544170754Sdelphij	      }
545170754Sdelphij	    while (! mbsinit (&mbstate));
546170754Sdelphij
547170754Sdelphij	    cpy (len, f);
548170754Sdelphij	    f += len - 1;
549170754Sdelphij	    continue;
550170754Sdelphij	  }
551170754Sdelphij	}
552170754Sdelphij
553170754Sdelphij#else /* ! DO_MULTIBYTE */
554170754Sdelphij
555170754Sdelphij      /* Either multibyte encodings are not supported, they are
556170754Sdelphij	 safe for formats, so any non-'%' byte can be copied through,
557170754Sdelphij	 or this is the wide character version.  */
558170754Sdelphij      if (*f != L_('%'))
559170754Sdelphij	{
560170754Sdelphij	  add (1, *p = *f);
561170754Sdelphij	  continue;
562170754Sdelphij	}
563170754Sdelphij
564170754Sdelphij#endif /* ! DO_MULTIBYTE */
565170754Sdelphij
566170754Sdelphij      /* Check for flags that can modify a format.  */
567170754Sdelphij      while (1)
568170754Sdelphij	{
569170754Sdelphij	  switch (*++f)
570170754Sdelphij	    {
571170754Sdelphij	      /* This influences the number formats.  */
572170754Sdelphij	    case L_('_'):
573170754Sdelphij	    case L_('-'):
574170754Sdelphij	    case L_('0'):
575170754Sdelphij	      pad = *f;
576170754Sdelphij	      continue;
577170754Sdelphij
578170754Sdelphij	      /* This changes textual output.  */
579170754Sdelphij	    case L_('^'):
580170754Sdelphij	      to_uppcase = 1;
581170754Sdelphij	      continue;
582170754Sdelphij	    case L_('#'):
583170754Sdelphij	      change_case = 1;
584170754Sdelphij	      continue;
585170754Sdelphij
586170754Sdelphij	    default:
587170754Sdelphij	      break;
588170754Sdelphij	    }
589170754Sdelphij	  break;
590170754Sdelphij	}
591170754Sdelphij
592170754Sdelphij      /* As a GNU extension we allow to specify the field width.  */
593170754Sdelphij      if (ISDIGIT (*f))
594170754Sdelphij	{
595170754Sdelphij	  width = 0;
596170754Sdelphij	  do
597170754Sdelphij	    {
598170754Sdelphij	      if (width > INT_MAX / 10
599170754Sdelphij		  || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10))
600170754Sdelphij		/* Avoid overflow.  */
601170754Sdelphij		width = INT_MAX;
602170754Sdelphij	      else
603170754Sdelphij		{
604170754Sdelphij		  width *= 10;
605170754Sdelphij		  width += *f - L_('0');
606170754Sdelphij		}
607170754Sdelphij	      ++f;
608170754Sdelphij	    }
609170754Sdelphij	  while (ISDIGIT (*f));
610170754Sdelphij	}
611170754Sdelphij
612170754Sdelphij      /* Check for modifiers.  */
613170754Sdelphij      switch (*f)
614170754Sdelphij	{
615170754Sdelphij	case L_('E'):
616170754Sdelphij	case L_('O'):
617170754Sdelphij	  modifier = *f++;
618170754Sdelphij	  break;
619170754Sdelphij
620170754Sdelphij	default:
621170754Sdelphij	  modifier = 0;
622170754Sdelphij	  break;
623170754Sdelphij	}
624170754Sdelphij
625170754Sdelphij      /* Now do the specified format.  */
626170754Sdelphij      format_char = *f;
627170754Sdelphij      switch (format_char)
628170754Sdelphij	{
629170754Sdelphij#define DO_NUMBER(d, v) \
630170754Sdelphij	  digits = d > width ? d : width;				      \
631170754Sdelphij	  number_value = v; goto do_number
632170754Sdelphij#define DO_NUMBER_SPACEPAD(d, v) \
633170754Sdelphij	  digits = d > width ? d : width;				      \
634170754Sdelphij	  number_value = v; goto do_number_spacepad
635170754Sdelphij
636170754Sdelphij	case L_('%'):
637170754Sdelphij	  if (modifier != 0)
638170754Sdelphij	    goto bad_format;
639170754Sdelphij	  add (1, *p = *f);
640170754Sdelphij	  break;
641170754Sdelphij
642170754Sdelphij	case L_('a'):
643170754Sdelphij	  if (modifier != 0)
644170754Sdelphij	    goto bad_format;
645170754Sdelphij	  if (change_case)
646170754Sdelphij	    {
647170754Sdelphij	      to_uppcase = 1;
648170754Sdelphij	      to_lowcase = 0;
649170754Sdelphij	    }
650170754Sdelphij#if defined _NL_CURRENT || !HAVE_STRFTIME
651170754Sdelphij	  cpy (aw_len, a_wkday);
652170754Sdelphij	  break;
653170754Sdelphij#else
654170754Sdelphij	  goto underlying_strftime;
655170754Sdelphij#endif
656170754Sdelphij
657170754Sdelphij	case 'A':
658170754Sdelphij	  if (modifier != 0)
659170754Sdelphij	    goto bad_format;
660170754Sdelphij	  if (change_case)
661170754Sdelphij	    {
662170754Sdelphij	      to_uppcase = 1;
663170754Sdelphij	      to_lowcase = 0;
664170754Sdelphij	    }
665170754Sdelphij#if defined _NL_CURRENT || !HAVE_STRFTIME
666170754Sdelphij	  cpy (STRLEN (f_wkday), f_wkday);
667170754Sdelphij	  break;
668170754Sdelphij#else
669170754Sdelphij	  goto underlying_strftime;
670170754Sdelphij#endif
671170754Sdelphij
672170754Sdelphij	case L_('b'):
673170754Sdelphij	case L_('h'):
674170754Sdelphij	  if (change_case)
675170754Sdelphij	    {
676170754Sdelphij	      to_uppcase = 1;
677170754Sdelphij	      to_lowcase = 0;
678170754Sdelphij	    }
679170754Sdelphij	  if (modifier != 0)
680170754Sdelphij	    goto bad_format;
681170754Sdelphij#if defined _NL_CURRENT || !HAVE_STRFTIME
682170754Sdelphij	  cpy (am_len, a_month);
683170754Sdelphij	  break;
684170754Sdelphij#else
685170754Sdelphij	  goto underlying_strftime;
686170754Sdelphij#endif
687170754Sdelphij
688170754Sdelphij	case L_('B'):
689170754Sdelphij	  if (modifier != 0)
690170754Sdelphij	    goto bad_format;
691170754Sdelphij	  if (change_case)
692170754Sdelphij	    {
693170754Sdelphij	      to_uppcase = 1;
694170754Sdelphij	      to_lowcase = 0;
695170754Sdelphij	    }
696170754Sdelphij#if defined _NL_CURRENT || !HAVE_STRFTIME
697170754Sdelphij	  cpy (STRLEN (f_month), f_month);
698170754Sdelphij	  break;
699170754Sdelphij#else
700170754Sdelphij	  goto underlying_strftime;
701170754Sdelphij#endif
702170754Sdelphij
703170754Sdelphij	case L_('c'):
704170754Sdelphij	  if (modifier == L_('O'))
705170754Sdelphij	    goto bad_format;
706170754Sdelphij#ifdef _NL_CURRENT
707170754Sdelphij	  if (! (modifier == 'E'
708170754Sdelphij		 && (*(subfmt =
709170754Sdelphij		       (const CHAR_T *) _NL_CURRENT (LC_TIME,
710170754Sdelphij						     NLW(ERA_D_T_FMT)))
711170754Sdelphij		     != '\0')))
712170754Sdelphij	    subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
713170754Sdelphij#else
714170754Sdelphij# if HAVE_STRFTIME
715170754Sdelphij	  goto underlying_strftime;
716170754Sdelphij# else
717170754Sdelphij	  subfmt = L_("%a %b %e %H:%M:%S %Y");
718170754Sdelphij# endif
719170754Sdelphij#endif
720170754Sdelphij
721170754Sdelphij	subformat:
722170754Sdelphij	  {
723170754Sdelphij	    CHAR_T *old_start = p;
724170754Sdelphij	    size_t len = my_strftime (NULL, (size_t) -1, subfmt,
725170754Sdelphij				      tp extra_args LOCALE_ARG);
726170754Sdelphij	    add (len, my_strftime (p, maxsize - i, subfmt,
727170754Sdelphij				   tp extra_args LOCALE_ARG));
728170754Sdelphij
729170754Sdelphij	    if (to_uppcase)
730170754Sdelphij	      while (old_start < p)
731170754Sdelphij		{
732170754Sdelphij		  *old_start = TOUPPER ((UCHAR_T) *old_start, loc);
733170754Sdelphij		  ++old_start;
734170754Sdelphij		}
735170754Sdelphij	  }
736170754Sdelphij	  break;
737170754Sdelphij
738170754Sdelphij#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
739170754Sdelphij	underlying_strftime:
740170754Sdelphij	  {
741170754Sdelphij	    /* The relevant information is available only via the
742170754Sdelphij	       underlying strftime implementation, so use that.  */
743170754Sdelphij	    char ufmt[4];
744170754Sdelphij	    char *u = ufmt;
745170754Sdelphij	    char ubuf[1024]; /* enough for any single format in practice */
746170754Sdelphij	    size_t len;
747170754Sdelphij	    /* Make sure we're calling the actual underlying strftime.
748170754Sdelphij	       In some cases, config.h contains something like
749170754Sdelphij	       "#define strftime rpl_strftime".  */
750170754Sdelphij# ifdef strftime
751170754Sdelphij#  undef strftime
752170754Sdelphij	    size_t strftime ();
753170754Sdelphij# endif
754170754Sdelphij
755170754Sdelphij	    *u++ = '%';
756170754Sdelphij	    if (modifier != 0)
757170754Sdelphij	      *u++ = modifier;
758170754Sdelphij	    *u++ = format_char;
759170754Sdelphij	    *u = '\0';
760170754Sdelphij	    len = strftime (ubuf, sizeof ubuf, ufmt, tp);
761170754Sdelphij	    if (len == 0 && ubuf[0] != '\0')
762170754Sdelphij	      return 0;
763170754Sdelphij	    cpy (len, ubuf);
764170754Sdelphij	  }
765170754Sdelphij	  break;
766170754Sdelphij#endif
767170754Sdelphij
768170754Sdelphij	case L_('C'):
769170754Sdelphij	  if (modifier == L_('O'))
770170754Sdelphij	    goto bad_format;
771170754Sdelphij	  if (modifier == L_('E'))
772170754Sdelphij	    {
773170754Sdelphij#if HAVE_STRUCT_ERA_ENTRY
774170754Sdelphij	      struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
775170754Sdelphij	      if (era)
776170754Sdelphij		{
777170754Sdelphij# ifdef COMPILE_WIDE
778170754Sdelphij		  size_t len = __wcslen (era->era_wname);
779170754Sdelphij		  cpy (len, era->era_wname);
780170754Sdelphij# else
781170754Sdelphij		  size_t len = strlen (era->era_name);
782170754Sdelphij		  cpy (len, era->era_name);
783170754Sdelphij# endif
784170754Sdelphij		  break;
785170754Sdelphij		}
786170754Sdelphij#else
787170754Sdelphij# if HAVE_STRFTIME
788170754Sdelphij	      goto underlying_strftime;
789170754Sdelphij# endif
790170754Sdelphij#endif
791170754Sdelphij	    }
792170754Sdelphij
793170754Sdelphij	  {
794170754Sdelphij	    int year = tp->tm_year + TM_YEAR_BASE;
795170754Sdelphij	    DO_NUMBER (1, year / 100 - (year % 100 < 0));
796170754Sdelphij	  }
797170754Sdelphij
798170754Sdelphij	case L_('x'):
799170754Sdelphij	  if (modifier == L_('O'))
800170754Sdelphij	    goto bad_format;
801170754Sdelphij#ifdef _NL_CURRENT
802170754Sdelphij	  if (! (modifier == L_('E')
803170754Sdelphij		 && (*(subfmt =
804170754Sdelphij		       (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
805170754Sdelphij		     != L_('\0'))))
806170754Sdelphij	    subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
807170754Sdelphij	  goto subformat;
808170754Sdelphij#else
809170754Sdelphij# if HAVE_STRFTIME
810170754Sdelphij	  goto underlying_strftime;
811170754Sdelphij# else
812170754Sdelphij	  /* Fall through.  */
813170754Sdelphij# endif
814170754Sdelphij#endif
815170754Sdelphij	case L_('D'):
816170754Sdelphij	  if (modifier != 0)
817170754Sdelphij	    goto bad_format;
818170754Sdelphij	  subfmt = L_("%m/%d/%y");
819170754Sdelphij	  goto subformat;
820170754Sdelphij
821170754Sdelphij	case L_('d'):
822170754Sdelphij	  if (modifier == L_('E'))
823170754Sdelphij	    goto bad_format;
824170754Sdelphij
825170754Sdelphij	  DO_NUMBER (2, tp->tm_mday);
826170754Sdelphij
827170754Sdelphij	case L_('e'):
828170754Sdelphij	  if (modifier == L_('E'))
829170754Sdelphij	    goto bad_format;
830170754Sdelphij
831170754Sdelphij	  DO_NUMBER_SPACEPAD (2, tp->tm_mday);
832170754Sdelphij
833170754Sdelphij	  /* All numeric formats set DIGITS and NUMBER_VALUE and then
834170754Sdelphij	     jump to one of these two labels.  */
835170754Sdelphij
836170754Sdelphij	do_number_spacepad:
837170754Sdelphij	  /* Force `_' flag unless overridden by `0' or `-' flag.  */
838170754Sdelphij	  if (pad != L_('0') && pad != L_('-'))
839170754Sdelphij	    pad = L_('_');
840170754Sdelphij
841170754Sdelphij	do_number:
842170754Sdelphij	  /* Format the number according to the MODIFIER flag.  */
843170754Sdelphij
844170754Sdelphij	  if (modifier == L_('O') && 0 <= number_value)
845170754Sdelphij	    {
846170754Sdelphij#ifdef _NL_CURRENT
847170754Sdelphij	      /* Get the locale specific alternate representation of
848170754Sdelphij		 the number NUMBER_VALUE.  If none exist NULL is returned.  */
849170754Sdelphij	      const CHAR_T *cp = nl_get_alt_digit (number_value
850170754Sdelphij						   HELPER_LOCALE_ARG);
851170754Sdelphij
852170754Sdelphij	      if (cp != NULL)
853170754Sdelphij		{
854170754Sdelphij		  size_t digitlen = STRLEN (cp);
855170754Sdelphij		  if (digitlen != 0)
856170754Sdelphij		    {
857170754Sdelphij		      cpy (digitlen, cp);
858170754Sdelphij		      break;
859170754Sdelphij		    }
860170754Sdelphij		}
861170754Sdelphij#else
862170754Sdelphij# if HAVE_STRFTIME
863170754Sdelphij	      goto underlying_strftime;
864170754Sdelphij# endif
865170754Sdelphij#endif
866170754Sdelphij	    }
867170754Sdelphij	  {
868170754Sdelphij	    unsigned int u = number_value;
869170754Sdelphij
870170754Sdelphij	    bufp = buf + sizeof (buf) / sizeof (buf[0]);
871170754Sdelphij	    negative_number = number_value < 0;
872170754Sdelphij
873170754Sdelphij	    if (negative_number)
874170754Sdelphij	      u = -u;
875170754Sdelphij
876170754Sdelphij	    do
877170754Sdelphij	      *--bufp = u % 10 + L_('0');
878170754Sdelphij	    while ((u /= 10) != 0);
879170754Sdelphij	  }
880170754Sdelphij
881170754Sdelphij	do_number_sign_and_padding:
882170754Sdelphij	  if (negative_number)
883170754Sdelphij	    *--bufp = L_('-');
884170754Sdelphij
885170754Sdelphij	  if (pad != L_('-'))
886170754Sdelphij	    {
887170754Sdelphij	      int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0]))
888170754Sdelphij				      - bufp);
889170754Sdelphij
890170754Sdelphij	      if (padding > 0)
891170754Sdelphij		{
892170754Sdelphij		  if (pad == L_('_'))
893170754Sdelphij		    {
894170754Sdelphij		      if ((size_t) padding >= maxsize - i)
895170754Sdelphij			return 0;
896170754Sdelphij
897170754Sdelphij		      if (p)
898170754Sdelphij			memset_space (p, padding);
899170754Sdelphij		      i += padding;
900170754Sdelphij		      width = width > padding ? width - padding : 0;
901170754Sdelphij		    }
902170754Sdelphij		  else
903170754Sdelphij		    {
904170754Sdelphij		      if ((size_t) digits >= maxsize - i)
905170754Sdelphij			return 0;
906170754Sdelphij
907170754Sdelphij		      if (negative_number)
908170754Sdelphij			{
909170754Sdelphij			  ++bufp;
910170754Sdelphij
911170754Sdelphij			  if (p)
912170754Sdelphij			    *p++ = L_('-');
913170754Sdelphij			  ++i;
914170754Sdelphij			}
915170754Sdelphij
916170754Sdelphij		      if (p)
917170754Sdelphij			memset_zero (p, padding);
918170754Sdelphij		      i += padding;
919170754Sdelphij		      width = 0;
920170754Sdelphij		    }
921170754Sdelphij		}
922170754Sdelphij	    }
923170754Sdelphij
924170754Sdelphij	  cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
925170754Sdelphij	  break;
926170754Sdelphij
927170754Sdelphij	case L_('F'):
928170754Sdelphij	  if (modifier != 0)
929170754Sdelphij	    goto bad_format;
930170754Sdelphij	  subfmt = L_("%Y-%m-%d");
931170754Sdelphij	  goto subformat;
932170754Sdelphij
933170754Sdelphij	case L_('H'):
934170754Sdelphij	  if (modifier == L_('E'))
935170754Sdelphij	    goto bad_format;
936170754Sdelphij
937170754Sdelphij	  DO_NUMBER (2, tp->tm_hour);
938170754Sdelphij
939170754Sdelphij	case L_('I'):
940170754Sdelphij	  if (modifier == L_('E'))
941170754Sdelphij	    goto bad_format;
942170754Sdelphij
943170754Sdelphij	  DO_NUMBER (2, hour12);
944170754Sdelphij
945170754Sdelphij	case L_('k'):		/* GNU extension.  */
946170754Sdelphij	  if (modifier == L_('E'))
947170754Sdelphij	    goto bad_format;
948170754Sdelphij
949170754Sdelphij	  DO_NUMBER_SPACEPAD (2, tp->tm_hour);
950170754Sdelphij
951170754Sdelphij	case L_('l'):		/* GNU extension.  */
952170754Sdelphij	  if (modifier == L_('E'))
953170754Sdelphij	    goto bad_format;
954170754Sdelphij
955170754Sdelphij	  DO_NUMBER_SPACEPAD (2, hour12);
956170754Sdelphij
957170754Sdelphij	case L_('j'):
958170754Sdelphij	  if (modifier == L_('E'))
959170754Sdelphij	    goto bad_format;
960170754Sdelphij
961170754Sdelphij	  DO_NUMBER (3, 1 + tp->tm_yday);
962170754Sdelphij
963170754Sdelphij	case L_('M'):
964170754Sdelphij	  if (modifier == L_('E'))
965170754Sdelphij	    goto bad_format;
966170754Sdelphij
967170754Sdelphij	  DO_NUMBER (2, tp->tm_min);
968170754Sdelphij
969170754Sdelphij	case L_('m'):
970170754Sdelphij	  if (modifier == L_('E'))
971170754Sdelphij	    goto bad_format;
972170754Sdelphij
973170754Sdelphij	  DO_NUMBER (2, tp->tm_mon + 1);
974170754Sdelphij
975170754Sdelphij#ifndef _LIBC
976170754Sdelphij	case L_('N'):		/* GNU extension.  */
977170754Sdelphij	  if (modifier == L_('E'))
978170754Sdelphij	    goto bad_format;
979170754Sdelphij
980170754Sdelphij	  number_value = ns;
981170754Sdelphij	  if (width != -1)
982170754Sdelphij	    {
983170754Sdelphij	      /* Take an explicit width less than 9 as a precision.  */
984170754Sdelphij	      int j;
985170754Sdelphij	      for (j = width; j < 9; j++)
986170754Sdelphij		number_value /= 10;
987170754Sdelphij	    }
988170754Sdelphij
989170754Sdelphij	  DO_NUMBER (9, number_value);
990170754Sdelphij#endif
991170754Sdelphij
992170754Sdelphij	case L_('n'):
993170754Sdelphij	  add (1, *p = L_('\n'));
994170754Sdelphij	  break;
995170754Sdelphij
996170754Sdelphij	case L_('P'):
997170754Sdelphij	  to_lowcase = 1;
998170754Sdelphij#if !defined _NL_CURRENT && HAVE_STRFTIME
999170754Sdelphij	  format_char = L_('p');
1000170754Sdelphij#endif
1001170754Sdelphij	  /* FALLTHROUGH */
1002170754Sdelphij
1003170754Sdelphij	case L_('p'):
1004170754Sdelphij	  if (change_case)
1005170754Sdelphij	    {
1006170754Sdelphij	      to_uppcase = 0;
1007170754Sdelphij	      to_lowcase = 1;
1008170754Sdelphij	    }
1009170754Sdelphij#if defined _NL_CURRENT || !HAVE_STRFTIME
1010170754Sdelphij	  cpy (ap_len, ampm);
1011170754Sdelphij	  break;
1012170754Sdelphij#else
1013170754Sdelphij	  goto underlying_strftime;
1014170754Sdelphij#endif
1015170754Sdelphij
1016170754Sdelphij	case L_('R'):
1017170754Sdelphij	  subfmt = L_("%H:%M");
1018170754Sdelphij	  goto subformat;
1019170754Sdelphij
1020170754Sdelphij	case L_('r'):
1021170754Sdelphij#if !defined _NL_CURRENT && HAVE_STRFTIME
1022170754Sdelphij	  goto underlying_strftime;
1023170754Sdelphij#else
1024170754Sdelphij# ifdef _NL_CURRENT
1025170754Sdelphij	  if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
1026170754Sdelphij						       NLW(T_FMT_AMPM)))
1027170754Sdelphij	      == L_('\0'))
1028170754Sdelphij# endif
1029170754Sdelphij	    subfmt = L_("%I:%M:%S %p");
1030170754Sdelphij	  goto subformat;
1031170754Sdelphij#endif
1032170754Sdelphij
1033170754Sdelphij	case L_('S'):
1034170754Sdelphij	  if (modifier == L_('E'))
1035170754Sdelphij	    goto bad_format;
1036170754Sdelphij
1037170754Sdelphij	  DO_NUMBER (2, tp->tm_sec);
1038170754Sdelphij
1039170754Sdelphij	case L_('s'):		/* GNU extension.  */
1040170754Sdelphij	  {
1041170754Sdelphij	    struct tm ltm;
1042170754Sdelphij	    time_t t;
1043170754Sdelphij
1044170754Sdelphij	    ltm = *tp;
1045170754Sdelphij	    t = mktime (&ltm);
1046170754Sdelphij
1047170754Sdelphij	    /* Generate string value for T using time_t arithmetic;
1048170754Sdelphij	       this works even if sizeof (long) < sizeof (time_t).  */
1049170754Sdelphij
1050170754Sdelphij	    bufp = buf + sizeof (buf) / sizeof (buf[0]);
1051170754Sdelphij	    negative_number = t < 0;
1052170754Sdelphij
1053170754Sdelphij	    do
1054170754Sdelphij	      {
1055170754Sdelphij		int d = t % 10;
1056170754Sdelphij		t /= 10;
1057170754Sdelphij
1058170754Sdelphij		if (negative_number)
1059170754Sdelphij		  {
1060170754Sdelphij		    d = -d;
1061170754Sdelphij
1062170754Sdelphij		    /* Adjust if division truncates to minus infinity.  */
1063170754Sdelphij		    if (0 < -1 % 10 && d < 0)
1064170754Sdelphij		      {
1065170754Sdelphij			t++;
1066170754Sdelphij			d += 10;
1067170754Sdelphij		      }
1068170754Sdelphij		  }
1069170754Sdelphij
1070170754Sdelphij		*--bufp = d + L_('0');
1071170754Sdelphij	      }
1072170754Sdelphij	    while (t != 0);
1073170754Sdelphij
1074170754Sdelphij	    digits = 1;
1075170754Sdelphij	    goto do_number_sign_and_padding;
1076170754Sdelphij	  }
1077170754Sdelphij
1078170754Sdelphij	case L_('X'):
1079170754Sdelphij	  if (modifier == L_('O'))
1080170754Sdelphij	    goto bad_format;
1081170754Sdelphij#ifdef _NL_CURRENT
1082170754Sdelphij	  if (! (modifier == L_('E')
1083170754Sdelphij		 && (*(subfmt =
1084170754Sdelphij		       (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
1085170754Sdelphij		     != L_('\0'))))
1086170754Sdelphij	    subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
1087170754Sdelphij	  goto subformat;
1088170754Sdelphij#else
1089170754Sdelphij# if HAVE_STRFTIME
1090170754Sdelphij	  goto underlying_strftime;
1091170754Sdelphij# else
1092170754Sdelphij	  /* Fall through.  */
1093170754Sdelphij# endif
1094170754Sdelphij#endif
1095170754Sdelphij	case L_('T'):
1096170754Sdelphij	  subfmt = L_("%H:%M:%S");
1097170754Sdelphij	  goto subformat;
1098170754Sdelphij
1099170754Sdelphij	case L_('t'):
1100170754Sdelphij	  add (1, *p = L_('\t'));
1101170754Sdelphij	  break;
1102170754Sdelphij
1103170754Sdelphij	case L_('u'):
1104170754Sdelphij	  DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1105170754Sdelphij
1106170754Sdelphij	case L_('U'):
1107170754Sdelphij	  if (modifier == L_('E'))
1108170754Sdelphij	    goto bad_format;
1109170754Sdelphij
1110170754Sdelphij	  DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1111170754Sdelphij
1112170754Sdelphij	case L_('V'):
1113170754Sdelphij	case L_('g'):
1114170754Sdelphij	case L_('G'):
1115170754Sdelphij	  if (modifier == L_('E'))
1116170754Sdelphij	    goto bad_format;
1117170754Sdelphij	  {
1118170754Sdelphij	    int year = tp->tm_year + TM_YEAR_BASE;
1119170754Sdelphij	    int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1120170754Sdelphij
1121170754Sdelphij	    if (days < 0)
1122170754Sdelphij	      {
1123170754Sdelphij		/* This ISO week belongs to the previous year.  */
1124170754Sdelphij		year--;
1125170754Sdelphij		days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
1126170754Sdelphij				      tp->tm_wday);
1127170754Sdelphij	      }
1128170754Sdelphij	    else
1129170754Sdelphij	      {
1130170754Sdelphij		int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1131170754Sdelphij				       tp->tm_wday);
1132170754Sdelphij		if (0 <= d)
1133170754Sdelphij		  {
1134170754Sdelphij		    /* This ISO week belongs to the next year.  */
1135170754Sdelphij		    year++;
1136170754Sdelphij		    days = d;
1137170754Sdelphij		  }
1138170754Sdelphij	      }
1139170754Sdelphij
1140170754Sdelphij	    switch (*f)
1141170754Sdelphij	      {
1142170754Sdelphij	      case L_('g'):
1143170754Sdelphij		DO_NUMBER (2, (year % 100 + 100) % 100);
1144170754Sdelphij
1145170754Sdelphij	      case L_('G'):
1146170754Sdelphij		DO_NUMBER (1, year);
1147170754Sdelphij
1148170754Sdelphij	      default:
1149170754Sdelphij		DO_NUMBER (2, days / 7 + 1);
1150170754Sdelphij	      }
1151170754Sdelphij	  }
1152170754Sdelphij
1153170754Sdelphij	case L_('W'):
1154170754Sdelphij	  if (modifier == L_('E'))
1155170754Sdelphij	    goto bad_format;
1156170754Sdelphij
1157170754Sdelphij	  DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1158170754Sdelphij
1159170754Sdelphij	case L_('w'):
1160170754Sdelphij	  if (modifier == L_('E'))
1161170754Sdelphij	    goto bad_format;
1162170754Sdelphij
1163170754Sdelphij	  DO_NUMBER (1, tp->tm_wday);
1164170754Sdelphij
1165170754Sdelphij	case L_('Y'):
1166170754Sdelphij	  if (modifier == 'E')
1167170754Sdelphij	    {
1168170754Sdelphij#if HAVE_STRUCT_ERA_ENTRY
1169170754Sdelphij	      struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1170170754Sdelphij	      if (era)
1171170754Sdelphij		{
1172170754Sdelphij# ifdef COMPILE_WIDE
1173170754Sdelphij		  subfmt = era->era_wformat;
1174170754Sdelphij# else
1175170754Sdelphij		  subfmt = era->era_format;
1176170754Sdelphij# endif
1177170754Sdelphij		  goto subformat;
1178170754Sdelphij		}
1179170754Sdelphij#else
1180170754Sdelphij# if HAVE_STRFTIME
1181170754Sdelphij	      goto underlying_strftime;
1182170754Sdelphij# endif
1183170754Sdelphij#endif
1184170754Sdelphij	    }
1185170754Sdelphij	  if (modifier == L_('O'))
1186170754Sdelphij	    goto bad_format;
1187170754Sdelphij	  else
1188170754Sdelphij	    DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
1189170754Sdelphij
1190170754Sdelphij	case L_('y'):
1191170754Sdelphij	  if (modifier == L_('E'))
1192170754Sdelphij	    {
1193170754Sdelphij#if HAVE_STRUCT_ERA_ENTRY
1194170754Sdelphij	      struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1195170754Sdelphij	      if (era)
1196170754Sdelphij		{
1197170754Sdelphij		  int delta = tp->tm_year - era->start_date[0];
1198170754Sdelphij		  DO_NUMBER (1, (era->offset
1199170754Sdelphij				 + delta * era->absolute_direction));
1200170754Sdelphij		}
1201170754Sdelphij#else
1202170754Sdelphij# if HAVE_STRFTIME
1203170754Sdelphij	      goto underlying_strftime;
1204170754Sdelphij# endif
1205170754Sdelphij#endif
1206170754Sdelphij	    }
1207170754Sdelphij	  DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
1208170754Sdelphij
1209170754Sdelphij	case L_('Z'):
1210170754Sdelphij	  if (change_case)
1211170754Sdelphij	    {
1212170754Sdelphij	      to_uppcase = 0;
1213170754Sdelphij	      to_lowcase = 1;
1214170754Sdelphij	    }
1215170754Sdelphij
1216170754Sdelphij#if HAVE_TZNAME
1217170754Sdelphij	  /* The tzset() call might have changed the value.  */
1218170754Sdelphij	  if (!(zone && *zone) && tp->tm_isdst >= 0)
1219170754Sdelphij	    zone = tzname[tp->tm_isdst];
1220170754Sdelphij#endif
1221170754Sdelphij	  if (! zone)
1222170754Sdelphij	    zone = "";
1223170754Sdelphij
1224170754Sdelphij#ifdef COMPILE_WIDE
1225170754Sdelphij	  {
1226170754Sdelphij	    /* The zone string is always given in multibyte form.  We have
1227170754Sdelphij	       to transform it first.  */
1228170754Sdelphij	    wchar_t *wczone;
1229170754Sdelphij	    size_t len;
1230170754Sdelphij	    widen (zone, wczone, len);
1231170754Sdelphij	    cpy (len, wczone);
1232170754Sdelphij	  }
1233170754Sdelphij#else
1234170754Sdelphij	  cpy (strlen (zone), zone);
1235170754Sdelphij#endif
1236170754Sdelphij	  break;
1237170754Sdelphij
1238170754Sdelphij	case L_('z'):
1239170754Sdelphij	  if (tp->tm_isdst < 0)
1240170754Sdelphij	    break;
1241170754Sdelphij
1242170754Sdelphij	  {
1243170754Sdelphij	    int diff;
1244170754Sdelphij#if HAVE_TM_GMTOFF
1245170754Sdelphij	    diff = tp->tm_gmtoff;
1246170754Sdelphij#else
1247170754Sdelphij	    if (ut)
1248170754Sdelphij	      diff = 0;
1249170754Sdelphij	    else
1250170754Sdelphij	      {
1251170754Sdelphij		struct tm gtm;
1252170754Sdelphij		struct tm ltm;
1253170754Sdelphij		time_t lt;
1254170754Sdelphij
1255170754Sdelphij		ltm = *tp;
1256170754Sdelphij		lt = mktime (&ltm);
1257170754Sdelphij
1258170754Sdelphij		if (lt == (time_t) -1)
1259170754Sdelphij		  {
1260170754Sdelphij		    /* mktime returns -1 for errors, but -1 is also a
1261170754Sdelphij		       valid time_t value.  Check whether an error really
1262170754Sdelphij		       occurred.  */
1263170754Sdelphij		    struct tm tm;
1264170754Sdelphij
1265170754Sdelphij		    if (! __localtime_r (&lt, &tm)
1266170754Sdelphij			|| ((ltm.tm_sec ^ tm.tm_sec)
1267170754Sdelphij			    | (ltm.tm_min ^ tm.tm_min)
1268170754Sdelphij			    | (ltm.tm_hour ^ tm.tm_hour)
1269170754Sdelphij			    | (ltm.tm_mday ^ tm.tm_mday)
1270170754Sdelphij			    | (ltm.tm_mon ^ tm.tm_mon)
1271170754Sdelphij			    | (ltm.tm_year ^ tm.tm_year)))
1272170754Sdelphij		      break;
1273170754Sdelphij		  }
1274170754Sdelphij
1275170754Sdelphij		if (! __gmtime_r (&lt, &gtm))
1276170754Sdelphij		  break;
1277170754Sdelphij
1278170754Sdelphij		diff = tm_diff (&ltm, &gtm);
1279170754Sdelphij	      }
1280170754Sdelphij#endif
1281170754Sdelphij
1282170754Sdelphij	    if (diff < 0)
1283170754Sdelphij	      {
1284170754Sdelphij		add (1, *p = L_('-'));
1285170754Sdelphij		diff = -diff;
1286170754Sdelphij	      }
1287170754Sdelphij	    else
1288170754Sdelphij	      add (1, *p = L_('+'));
1289170754Sdelphij
1290170754Sdelphij	    diff /= 60;
1291170754Sdelphij	    DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1292170754Sdelphij	  }
1293170754Sdelphij
1294170754Sdelphij	case L_('\0'):		/* GNU extension: % at end of format.  */
1295170754Sdelphij	    --f;
1296170754Sdelphij	    /* Fall through.  */
1297170754Sdelphij	default:
1298170754Sdelphij	  /* Unknown format; output the format, including the '%',
1299170754Sdelphij	     since this is most likely the right thing to do if a
1300170754Sdelphij	     multibyte string has been misparsed.  */
1301170754Sdelphij	bad_format:
1302170754Sdelphij	  {
1303170754Sdelphij	    int flen;
1304170754Sdelphij	    for (flen = 1; f[1 - flen] != L_('%'); flen++)
1305170754Sdelphij	      continue;
1306170754Sdelphij	    cpy (flen, &f[1 - flen]);
1307170754Sdelphij	  }
1308170754Sdelphij	  break;
1309170754Sdelphij	}
1310170754Sdelphij    }
1311170754Sdelphij
1312170754Sdelphij  if (p && maxsize != 0)
1313170754Sdelphij    *p = L_('\0');
1314170754Sdelphij  return i;
1315170754Sdelphij}
1316170754Sdelphij#ifdef _LIBC
1317170754Sdelphijlibc_hidden_def (my_strftime)
1318170754Sdelphij#endif
1319170754Sdelphij
1320170754Sdelphij
1321170754Sdelphij#ifdef emacs
1322170754Sdelphij/* For Emacs we have a separate interface which corresponds to the normal
1323170754Sdelphij   strftime function plus the ut argument, but without the ns argument.  */
1324170754Sdelphijsize_t
1325170754Sdelphijemacs_strftimeu (char *s, size_t maxsize, const char *format,
1326170754Sdelphij		 const struct tm *tp, int ut)
1327170754Sdelphij{
1328170754Sdelphij  return my_strftime (s, maxsize, format, tp, ut, 0);
1329170754Sdelphij}
1330170754Sdelphij#endif
1331