xref: /freebsd/contrib/diff/lib/strftime.c (revision 18fd37a7)
118fd37a7SXin LI /* Copyright (C) 1991-1999, 2000, 2001, 2003 Free Software Foundation, Inc.
218fd37a7SXin LI 
318fd37a7SXin LI    NOTE: The canonical source of this file is maintained with the GNU C Library.
418fd37a7SXin LI    Bugs can be reported to bug-glibc@prep.ai.mit.edu.
518fd37a7SXin LI 
618fd37a7SXin LI    This program is free software; you can redistribute it and/or modify
718fd37a7SXin LI    it under the terms of the GNU General Public License as published by
818fd37a7SXin LI    the Free Software Foundation; either version 2, or (at your option)
918fd37a7SXin LI    any later version.
1018fd37a7SXin LI 
1118fd37a7SXin LI    This program is distributed in the hope that it will be useful,
1218fd37a7SXin LI    but WITHOUT ANY WARRANTY; without even the implied warranty of
1318fd37a7SXin LI    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1418fd37a7SXin LI    GNU General Public License for more details.
1518fd37a7SXin LI 
1618fd37a7SXin LI    You should have received a copy of the GNU General Public License along
1718fd37a7SXin LI    with this program; if not, write to the Free Software Foundation,
1818fd37a7SXin LI    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
1918fd37a7SXin LI 
2018fd37a7SXin LI #ifdef HAVE_CONFIG_H
2118fd37a7SXin LI # include <config.h>
2218fd37a7SXin LI #endif
2318fd37a7SXin LI 
2418fd37a7SXin LI #ifdef _LIBC
2518fd37a7SXin LI # define HAVE_MBLEN 1
2618fd37a7SXin LI # define HAVE_MBRLEN 1
2718fd37a7SXin LI # define HAVE_STRUCT_ERA_ENTRY 1
2818fd37a7SXin LI # define HAVE_TM_GMTOFF 1
2918fd37a7SXin LI # define HAVE_TM_ZONE 1
3018fd37a7SXin LI # define HAVE_TZNAME 1
3118fd37a7SXin LI # define HAVE_TZSET 1
3218fd37a7SXin LI # define MULTIBYTE_IS_FORMAT_SAFE 1
3318fd37a7SXin LI # include "../locale/localeinfo.h"
3418fd37a7SXin LI #endif
3518fd37a7SXin LI 
3618fd37a7SXin LI #include <ctype.h>
3718fd37a7SXin LI #include <sys/types.h>		/* Some systems define `time_t' here.  */
3818fd37a7SXin LI 
3918fd37a7SXin LI #ifdef TIME_WITH_SYS_TIME
4018fd37a7SXin LI # include <sys/time.h>
4118fd37a7SXin LI # include <time.h>
4218fd37a7SXin LI #else
4318fd37a7SXin LI # ifdef HAVE_SYS_TIME_H
4418fd37a7SXin LI #  include <sys/time.h>
4518fd37a7SXin LI # else
4618fd37a7SXin LI #  include <time.h>
4718fd37a7SXin LI # endif
4818fd37a7SXin LI #endif
4918fd37a7SXin LI #if HAVE_TZNAME
5018fd37a7SXin LI extern char *tzname[];
5118fd37a7SXin LI #endif
5218fd37a7SXin LI 
5318fd37a7SXin LI /* Do multibyte processing if multibytes are supported, unless
5418fd37a7SXin LI    multibyte sequences are safe in formats.  Multibyte sequences are
5518fd37a7SXin LI    safe if they cannot contain byte sequences that look like format
5618fd37a7SXin LI    conversion specifications.  The GNU C Library uses UTF8 multibyte
5718fd37a7SXin LI    encoding, which is safe for formats, but strftime.c can be used
5818fd37a7SXin LI    with other C libraries that use unsafe encodings.  */
5918fd37a7SXin LI #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
6018fd37a7SXin LI 
6118fd37a7SXin LI #if DO_MULTIBYTE
6218fd37a7SXin LI # if HAVE_MBRLEN
6318fd37a7SXin LI #  include <wchar.h>
6418fd37a7SXin LI # else
6518fd37a7SXin LI    /* Simulate mbrlen with mblen as best we can.  */
6618fd37a7SXin LI #  define mbstate_t int
6718fd37a7SXin LI #  define mbrlen(s, n, ps) mblen (s, n)
6818fd37a7SXin LI #  define mbsinit(ps) (*(ps) == 0)
6918fd37a7SXin LI # endif
7018fd37a7SXin LI   static const mbstate_t mbstate_zero;
7118fd37a7SXin LI #endif
7218fd37a7SXin LI 
7318fd37a7SXin LI #include <limits.h>
7418fd37a7SXin LI #include <stddef.h>
7518fd37a7SXin LI #include <stdlib.h>
7618fd37a7SXin LI #include <string.h>
7718fd37a7SXin LI 
7818fd37a7SXin LI #ifdef COMPILE_WIDE
7918fd37a7SXin LI # include <endian.h>
8018fd37a7SXin LI # define CHAR_T wchar_t
8118fd37a7SXin LI # define UCHAR_T unsigned int
8218fd37a7SXin LI # define L_(Str) L##Str
8318fd37a7SXin LI # define NLW(Sym) _NL_W##Sym
8418fd37a7SXin LI 
8518fd37a7SXin LI # define MEMCPY(d, s, n) __wmemcpy (d, s, n)
8618fd37a7SXin LI # define STRLEN(s) __wcslen (s)
8718fd37a7SXin LI 
8818fd37a7SXin LI #else
8918fd37a7SXin LI # define CHAR_T char
9018fd37a7SXin LI # define UCHAR_T unsigned char
9118fd37a7SXin LI # define L_(Str) Str
9218fd37a7SXin LI # define NLW(Sym) Sym
9318fd37a7SXin LI 
9418fd37a7SXin LI # define MEMCPY(d, s, n) memcpy (d, s, n)
9518fd37a7SXin LI # define STRLEN(s) strlen (s)
9618fd37a7SXin LI 
9718fd37a7SXin LI # ifdef _LIBC
9818fd37a7SXin LI #  define MEMPCPY(d, s, n) __mempcpy (d, s, n)
9918fd37a7SXin LI # else
10018fd37a7SXin LI #  ifndef HAVE_MEMPCPY
10118fd37a7SXin LI #   define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
10218fd37a7SXin LI #  endif
10318fd37a7SXin LI # endif
10418fd37a7SXin LI #endif
10518fd37a7SXin LI 
10618fd37a7SXin LI #define TYPE_SIGNED(t) ((t) -1 < 0)
10718fd37a7SXin LI 
10818fd37a7SXin LI /* Bound on length of the string representing an integer value of type t.
10918fd37a7SXin LI    Subtract one for the sign bit if t is signed;
11018fd37a7SXin LI    302 / 1000 is log10 (2) rounded up;
11118fd37a7SXin LI    add one for integer division truncation;
11218fd37a7SXin LI    add one more for a minus sign if t is signed.  */
11318fd37a7SXin LI #define INT_STRLEN_BOUND(t) \
11418fd37a7SXin LI  ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
11518fd37a7SXin LI 
11618fd37a7SXin LI #define TM_YEAR_BASE 1900
11718fd37a7SXin LI 
11818fd37a7SXin LI #ifndef __isleap
11918fd37a7SXin LI /* Nonzero if YEAR is a leap year (every 4 years,
12018fd37a7SXin LI    except every 100th isn't, and every 400th is).  */
12118fd37a7SXin LI # define __isleap(year)	\
12218fd37a7SXin LI   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
12318fd37a7SXin LI #endif
12418fd37a7SXin LI 
12518fd37a7SXin LI 
12618fd37a7SXin LI #ifdef _LIBC
12718fd37a7SXin LI # define tzname __tzname
12818fd37a7SXin LI # define tzset __tzset
12918fd37a7SXin LI #endif
13018fd37a7SXin LI 
13118fd37a7SXin LI #if !HAVE_TM_GMTOFF
13218fd37a7SXin LI /* Portable standalone applications should supply a "time_r.h" that
13318fd37a7SXin LI    declares a POSIX-compliant localtime_r, for the benefit of older
13418fd37a7SXin LI    implementations that lack localtime_r or have a nonstandard one.
13518fd37a7SXin LI    See the gnulib time_r module for one way to implement this.  */
13618fd37a7SXin LI # include "time_r.h"
13718fd37a7SXin LI # undef __gmtime_r
13818fd37a7SXin LI # undef __localtime_r
13918fd37a7SXin LI # define __gmtime_r gmtime_r
14018fd37a7SXin LI # define __localtime_r localtime_r
14118fd37a7SXin LI #endif
14218fd37a7SXin LI 
14318fd37a7SXin LI 
14418fd37a7SXin LI #ifdef COMPILE_WIDE
14518fd37a7SXin LI # define memset_space(P, Len) (wmemset (P, L' ', Len), (P) += (Len))
14618fd37a7SXin LI # define memset_zero(P, Len) (wmemset (P, L'0', Len), (P) += (Len))
14718fd37a7SXin LI #else
14818fd37a7SXin LI # define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
14918fd37a7SXin LI # define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
15018fd37a7SXin LI #endif
15118fd37a7SXin LI 
15218fd37a7SXin LI #define add(n, f)							      \
15318fd37a7SXin LI   do									      \
15418fd37a7SXin LI     {									      \
15518fd37a7SXin LI       int _n = (n);							      \
15618fd37a7SXin LI       int _delta = width - _n;						      \
15718fd37a7SXin LI       int _incr = _n + (_delta > 0 ? _delta : 0);			      \
15818fd37a7SXin LI       if ((size_t) _incr >= maxsize - i)				      \
15918fd37a7SXin LI 	return 0;							      \
16018fd37a7SXin LI       if (p)								      \
16118fd37a7SXin LI 	{								      \
16218fd37a7SXin LI 	  if (_delta > 0)						      \
16318fd37a7SXin LI 	    {								      \
16418fd37a7SXin LI 	      if (pad == L_('0'))					      \
16518fd37a7SXin LI 		memset_zero (p, _delta);				      \
16618fd37a7SXin LI 	      else							      \
16718fd37a7SXin LI 		memset_space (p, _delta);				      \
16818fd37a7SXin LI 	    }								      \
16918fd37a7SXin LI 	  f;								      \
17018fd37a7SXin LI 	  p += _n;							      \
17118fd37a7SXin LI 	}								      \
17218fd37a7SXin LI       i += _incr;							      \
17318fd37a7SXin LI     } while (0)
17418fd37a7SXin LI 
17518fd37a7SXin LI #define cpy(n, s) \
17618fd37a7SXin LI     add ((n),								      \
17718fd37a7SXin LI 	 if (to_lowcase)						      \
17818fd37a7SXin LI 	   memcpy_lowcase (p, (s), _n LOCALE_ARG);			      \
17918fd37a7SXin LI 	 else if (to_uppcase)						      \
18018fd37a7SXin LI 	   memcpy_uppcase (p, (s), _n LOCALE_ARG);			      \
18118fd37a7SXin LI 	 else								      \
18218fd37a7SXin LI 	   MEMCPY ((void *) p, (void const *) (s), _n))
18318fd37a7SXin LI 
18418fd37a7SXin LI #ifdef COMPILE_WIDE
18518fd37a7SXin LI # ifndef USE_IN_EXTENDED_LOCALE_MODEL
18618fd37a7SXin LI #  undef __mbsrtowcs_l
18718fd37a7SXin LI #  define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
18818fd37a7SXin LI # endif
18918fd37a7SXin LI # define widen(os, ws, l) \
19018fd37a7SXin LI   {									      \
19118fd37a7SXin LI     mbstate_t __st;							      \
19218fd37a7SXin LI     const char *__s = os;						      \
19318fd37a7SXin LI     memset (&__st, '\0', sizeof (__st));				      \
19418fd37a7SXin LI     l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc);			      \
19518fd37a7SXin LI     ws = (wchar_t *) alloca ((l + 1) * sizeof (wchar_t));		      \
19618fd37a7SXin LI     (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc);			      \
19718fd37a7SXin LI   }
19818fd37a7SXin LI #endif
19918fd37a7SXin LI 
20018fd37a7SXin LI 
20118fd37a7SXin LI #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
20218fd37a7SXin LI /* We use this code also for the extended locale handling where the
20318fd37a7SXin LI    function gets as an additional argument the locale which has to be
20418fd37a7SXin LI    used.  To access the values we have to redefine the _NL_CURRENT
20518fd37a7SXin LI    macro.  */
20618fd37a7SXin LI # define strftime		__strftime_l
20718fd37a7SXin LI # define wcsftime		__wcsftime_l
20818fd37a7SXin LI # undef _NL_CURRENT
20918fd37a7SXin LI # define _NL_CURRENT(category, item) \
21018fd37a7SXin LI   (current->values[_NL_ITEM_INDEX (item)].string)
21118fd37a7SXin LI # define LOCALE_ARG , loc
21218fd37a7SXin LI # define LOCALE_PARAM_PROTO , __locale_t loc
21318fd37a7SXin LI # define HELPER_LOCALE_ARG  , current
21418fd37a7SXin LI #else
21518fd37a7SXin LI # define LOCALE_PARAM_PROTO
21618fd37a7SXin LI # define LOCALE_ARG
21718fd37a7SXin LI # ifdef _LIBC
21818fd37a7SXin LI #  define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
21918fd37a7SXin LI # else
22018fd37a7SXin LI #  define HELPER_LOCALE_ARG
22118fd37a7SXin LI # endif
22218fd37a7SXin LI #endif
22318fd37a7SXin LI 
22418fd37a7SXin LI #ifdef COMPILE_WIDE
22518fd37a7SXin LI # ifdef USE_IN_EXTENDED_LOCALE_MODEL
22618fd37a7SXin LI #  define TOUPPER(Ch, L) __towupper_l (Ch, L)
22718fd37a7SXin LI #  define TOLOWER(Ch, L) __towlower_l (Ch, L)
22818fd37a7SXin LI # else
22918fd37a7SXin LI #  define TOUPPER(Ch, L) towupper (Ch)
23018fd37a7SXin LI #  define TOLOWER(Ch, L) towlower (Ch)
23118fd37a7SXin LI # endif
23218fd37a7SXin LI #else
23318fd37a7SXin LI # ifdef _LIBC
23418fd37a7SXin LI #  ifdef USE_IN_EXTENDED_LOCALE_MODEL
23518fd37a7SXin LI #   define TOUPPER(Ch, L) __toupper_l (Ch, L)
23618fd37a7SXin LI #   define TOLOWER(Ch, L) __tolower_l (Ch, L)
23718fd37a7SXin LI #  else
23818fd37a7SXin LI #   define TOUPPER(Ch, L) toupper (Ch)
23918fd37a7SXin LI #   define TOLOWER(Ch, L) tolower (Ch)
24018fd37a7SXin LI #  endif
24118fd37a7SXin LI # else
24218fd37a7SXin LI #  define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
24318fd37a7SXin LI #  define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
24418fd37a7SXin LI # endif
24518fd37a7SXin LI #endif
24618fd37a7SXin LI /* We don't use `isdigit' here since the locale dependent
24718fd37a7SXin LI    interpretation is not what we want here.  We only need to accept
24818fd37a7SXin LI    the arabic digits in the ASCII range.  One day there is perhaps a
24918fd37a7SXin LI    more reliable way to accept other sets of digits.  */
25018fd37a7SXin LI #define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
25118fd37a7SXin LI 
25218fd37a7SXin LI static CHAR_T *
memcpy_lowcase(CHAR_T * dest,const CHAR_T * src,size_t len LOCALE_PARAM_PROTO)25318fd37a7SXin LI memcpy_lowcase (CHAR_T *dest, const CHAR_T *src,
25418fd37a7SXin LI 		size_t len LOCALE_PARAM_PROTO)
25518fd37a7SXin LI {
25618fd37a7SXin LI   while (len-- > 0)
25718fd37a7SXin LI     dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
25818fd37a7SXin LI   return dest;
25918fd37a7SXin LI }
26018fd37a7SXin LI 
26118fd37a7SXin LI static CHAR_T *
memcpy_uppcase(CHAR_T * dest,const CHAR_T * src,size_t len LOCALE_PARAM_PROTO)26218fd37a7SXin LI memcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
26318fd37a7SXin LI 		size_t len LOCALE_PARAM_PROTO)
26418fd37a7SXin LI {
26518fd37a7SXin LI   while (len-- > 0)
26618fd37a7SXin LI     dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
26718fd37a7SXin LI   return dest;
26818fd37a7SXin LI }
26918fd37a7SXin LI 
27018fd37a7SXin LI 
27118fd37a7SXin LI #if ! HAVE_TM_GMTOFF
27218fd37a7SXin LI /* Yield the difference between *A and *B,
27318fd37a7SXin LI    measured in seconds, ignoring leap seconds.  */
27418fd37a7SXin LI # define tm_diff ftime_tm_diff
27518fd37a7SXin LI static int
tm_diff(const struct tm * a,const struct tm * b)27618fd37a7SXin LI tm_diff (const struct tm *a, const struct tm *b)
27718fd37a7SXin LI {
27818fd37a7SXin LI   /* Compute intervening leap days correctly even if year is negative.
27918fd37a7SXin LI      Take care to avoid int overflow in leap day calculations,
28018fd37a7SXin LI      but it's OK to assume that A and B are close to each other.  */
28118fd37a7SXin LI   int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
28218fd37a7SXin LI   int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
28318fd37a7SXin LI   int a100 = a4 / 25 - (a4 % 25 < 0);
28418fd37a7SXin LI   int b100 = b4 / 25 - (b4 % 25 < 0);
28518fd37a7SXin LI   int a400 = a100 >> 2;
28618fd37a7SXin LI   int b400 = b100 >> 2;
28718fd37a7SXin LI   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
28818fd37a7SXin LI   int years = a->tm_year - b->tm_year;
28918fd37a7SXin LI   int days = (365 * years + intervening_leap_days
29018fd37a7SXin LI 	      + (a->tm_yday - b->tm_yday));
29118fd37a7SXin LI   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
29218fd37a7SXin LI 		+ (a->tm_min - b->tm_min))
29318fd37a7SXin LI 	  + (a->tm_sec - b->tm_sec));
29418fd37a7SXin LI }
29518fd37a7SXin LI #endif /* ! HAVE_TM_GMTOFF */
29618fd37a7SXin LI 
29718fd37a7SXin LI 
29818fd37a7SXin LI 
29918fd37a7SXin LI /* The number of days from the first day of the first ISO week of this
30018fd37a7SXin LI    year to the year day YDAY with week day WDAY.  ISO weeks start on
30118fd37a7SXin LI    Monday; the first ISO week has the year's first Thursday.  YDAY may
30218fd37a7SXin LI    be as small as YDAY_MINIMUM.  */
30318fd37a7SXin LI #define ISO_WEEK_START_WDAY 1 /* Monday */
30418fd37a7SXin LI #define ISO_WEEK1_WDAY 4 /* Thursday */
30518fd37a7SXin LI #define YDAY_MINIMUM (-366)
30618fd37a7SXin LI #ifdef __GNUC__
30718fd37a7SXin LI __inline__
30818fd37a7SXin LI #endif
30918fd37a7SXin LI static int
iso_week_days(int yday,int wday)31018fd37a7SXin LI iso_week_days (int yday, int wday)
31118fd37a7SXin LI {
31218fd37a7SXin LI   /* Add enough to the first operand of % to make it nonnegative.  */
31318fd37a7SXin LI   int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
31418fd37a7SXin LI   return (yday
31518fd37a7SXin LI 	  - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
31618fd37a7SXin LI 	  + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
31718fd37a7SXin LI }
31818fd37a7SXin LI 
31918fd37a7SXin LI 
32018fd37a7SXin LI #if !(defined _NL_CURRENT || HAVE_STRFTIME)
32118fd37a7SXin LI static CHAR_T const weekday_name[][10] =
32218fd37a7SXin LI   {
32318fd37a7SXin LI     L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"),
32418fd37a7SXin LI     L_("Thursday"), L_("Friday"), L_("Saturday")
32518fd37a7SXin LI   };
32618fd37a7SXin LI static CHAR_T const month_name[][10] =
32718fd37a7SXin LI   {
32818fd37a7SXin LI     L_("January"), L_("February"), L_("March"), L_("April"), L_("May"),
32918fd37a7SXin LI     L_("June"), L_("July"), L_("August"), L_("September"), L_("October"),
33018fd37a7SXin LI     L_("November"), L_("December")
33118fd37a7SXin LI   };
33218fd37a7SXin LI #endif
33318fd37a7SXin LI 
33418fd37a7SXin LI 
33518fd37a7SXin LI /* When compiling this file, GNU applications can #define my_strftime
33618fd37a7SXin LI    to a symbol (typically nstrftime) to get an extended strftime with
33718fd37a7SXin LI    extra arguments UT and NS.  Emacs is a special case for now, but
33818fd37a7SXin LI    this Emacs-specific code can be removed once Emacs's config.h
33918fd37a7SXin LI    defines my_strftime.  */
34018fd37a7SXin LI #if defined emacs && !defined my_strftime
34118fd37a7SXin LI # define my_strftime nstrftime
34218fd37a7SXin LI #endif
34318fd37a7SXin LI 
34418fd37a7SXin LI #ifdef my_strftime
34518fd37a7SXin LI # define extra_args , ut, ns
34618fd37a7SXin LI # define extra_args_spec , int ut, int ns
34718fd37a7SXin LI #else
34818fd37a7SXin LI # ifdef COMPILE_WIDE
34918fd37a7SXin LI #  define my_strftime wcsftime
35018fd37a7SXin LI #  define nl_get_alt_digit _nl_get_walt_digit
35118fd37a7SXin LI # else
35218fd37a7SXin LI #  define my_strftime strftime
35318fd37a7SXin LI #  define nl_get_alt_digit _nl_get_alt_digit
35418fd37a7SXin LI # endif
35518fd37a7SXin LI # define extra_args
35618fd37a7SXin LI # define extra_args_spec
35718fd37a7SXin LI /* We don't have this information in general.  */
35818fd37a7SXin LI # define ut 0
35918fd37a7SXin LI # define ns 0
36018fd37a7SXin LI #endif
36118fd37a7SXin LI 
36218fd37a7SXin LI #if ! defined _LIBC && ! HAVE_RUN_TZSET_TEST
36318fd37a7SXin LI /* Solaris 2.5.x and 2.6 tzset sometimes modify the storage returned
36418fd37a7SXin LI    by localtime.  On such systems, we must use the tzset and localtime
36518fd37a7SXin LI    wrappers to work around the bug.  */
36618fd37a7SXin LI "you must run the autoconf test for a working tzset function"
36718fd37a7SXin LI #endif
36818fd37a7SXin LI 
36918fd37a7SXin LI 
37018fd37a7SXin LI /* Write information from TP into S according to the format
37118fd37a7SXin LI    string FORMAT, writing no more that MAXSIZE characters
37218fd37a7SXin LI    (including the terminating '\0') and returning number of
37318fd37a7SXin LI    characters written.  If S is NULL, nothing will be written
37418fd37a7SXin LI    anywhere, so to determine how many characters would be
37518fd37a7SXin LI    written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
37618fd37a7SXin LI size_t
37718fd37a7SXin LI my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
37818fd37a7SXin LI 	     const struct tm *tp extra_args_spec LOCALE_PARAM_PROTO)
37918fd37a7SXin LI {
38018fd37a7SXin LI #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
38118fd37a7SXin LI   struct locale_data *const current = loc->__locales[LC_TIME];
38218fd37a7SXin LI #endif
38318fd37a7SXin LI 
38418fd37a7SXin LI   int hour12 = tp->tm_hour;
38518fd37a7SXin LI #ifdef _NL_CURRENT
38618fd37a7SXin LI   /* We cannot make the following values variables since we must delay
38718fd37a7SXin LI      the evaluation of these values until really needed since some
38818fd37a7SXin LI      expressions might not be valid in every situation.  The `struct tm'
38918fd37a7SXin LI      might be generated by a strptime() call that initialized
39018fd37a7SXin LI      only a few elements.  Dereference the pointers only if the format
39118fd37a7SXin LI      requires this.  Then it is ok to fail if the pointers are invalid.  */
39218fd37a7SXin LI # define a_wkday \
39318fd37a7SXin LI   ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday))
39418fd37a7SXin LI # define f_wkday \
39518fd37a7SXin LI   ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday))
39618fd37a7SXin LI # define a_month \
39718fd37a7SXin LI   ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon))
39818fd37a7SXin LI # define f_month \
39918fd37a7SXin LI   ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon))
40018fd37a7SXin LI # define ampm \
40118fd37a7SXin LI   ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11		      \
40218fd37a7SXin LI 				 ? NLW(PM_STR) : NLW(AM_STR)))
40318fd37a7SXin LI 
40418fd37a7SXin LI # define aw_len STRLEN (a_wkday)
40518fd37a7SXin LI # define am_len STRLEN (a_month)
40618fd37a7SXin LI # define ap_len STRLEN (ampm)
40718fd37a7SXin LI #else
40818fd37a7SXin LI # if !HAVE_STRFTIME
40918fd37a7SXin LI #  define f_wkday (weekday_name[tp->tm_wday])
41018fd37a7SXin LI #  define f_month (month_name[tp->tm_mon])
41118fd37a7SXin LI #  define a_wkday f_wkday
41218fd37a7SXin LI #  define a_month f_month
41318fd37a7SXin LI #  define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
41418fd37a7SXin LI 
41518fd37a7SXin LI   size_t aw_len = 3;
41618fd37a7SXin LI   size_t am_len = 3;
41718fd37a7SXin LI   size_t ap_len = 2;
41818fd37a7SXin LI # endif
41918fd37a7SXin LI #endif
42018fd37a7SXin LI   const char *zone;
42118fd37a7SXin LI   size_t i = 0;
42218fd37a7SXin LI   CHAR_T *p = s;
42318fd37a7SXin LI   const CHAR_T *f;
42418fd37a7SXin LI #if DO_MULTIBYTE && !defined COMPILE_WIDE
42518fd37a7SXin LI   const char *format_end = NULL;
42618fd37a7SXin LI #endif
42718fd37a7SXin LI 
42818fd37a7SXin LI   zone = NULL;
42918fd37a7SXin LI #if HAVE_TM_ZONE
43018fd37a7SXin LI   /* The POSIX test suite assumes that setting
43118fd37a7SXin LI      the environment variable TZ to a new value before calling strftime()
43218fd37a7SXin LI      will influence the result (the %Z format) even if the information in
43318fd37a7SXin LI      TP is computed with a totally different time zone.
43418fd37a7SXin LI      This is bogus: though POSIX allows bad behavior like this,
43518fd37a7SXin LI      POSIX does not require it.  Do the right thing instead.  */
43618fd37a7SXin LI   zone = (const char *) tp->tm_zone;
43718fd37a7SXin LI #endif
43818fd37a7SXin LI #if HAVE_TZNAME
43918fd37a7SXin LI   if (ut)
44018fd37a7SXin LI     {
44118fd37a7SXin LI       if (! (zone && *zone))
44218fd37a7SXin LI 	zone = "GMT";
44318fd37a7SXin LI     }
44418fd37a7SXin LI   else
44518fd37a7SXin LI     {
44618fd37a7SXin LI       /* POSIX.1 requires that local time zone information be used as
44718fd37a7SXin LI 	 though strftime called tzset.  */
44818fd37a7SXin LI # if HAVE_TZSET
44918fd37a7SXin LI       tzset ();
45018fd37a7SXin LI # endif
45118fd37a7SXin LI     }
45218fd37a7SXin LI #endif
45318fd37a7SXin LI 
45418fd37a7SXin LI   if (hour12 > 12)
45518fd37a7SXin LI     hour12 -= 12;
45618fd37a7SXin LI   else
45718fd37a7SXin LI     if (hour12 == 0)
45818fd37a7SXin LI       hour12 = 12;
45918fd37a7SXin LI 
46018fd37a7SXin LI   for (f = format; *f != '\0'; ++f)
46118fd37a7SXin LI     {
46218fd37a7SXin LI       int pad = 0;		/* Padding for number ('-', '_', or 0).  */
46318fd37a7SXin LI       int modifier;		/* Field modifier ('E', 'O', or 0).  */
46418fd37a7SXin LI       int digits;		/* Max digits for numeric format.  */
46518fd37a7SXin LI       int number_value;		/* Numeric value to be printed.  */
46618fd37a7SXin LI       int negative_number;	/* 1 if the number is negative.  */
46718fd37a7SXin LI       const CHAR_T *subfmt;
46818fd37a7SXin LI       CHAR_T *bufp;
46918fd37a7SXin LI       CHAR_T buf[1 + (sizeof (int) < sizeof (time_t)
47018fd37a7SXin LI 		      ? INT_STRLEN_BOUND (time_t)
47118fd37a7SXin LI 		      : INT_STRLEN_BOUND (int))];
47218fd37a7SXin LI       int width = -1;
47318fd37a7SXin LI       int to_lowcase = 0;
47418fd37a7SXin LI       int to_uppcase = 0;
47518fd37a7SXin LI       int change_case = 0;
47618fd37a7SXin LI       int format_char;
47718fd37a7SXin LI 
47818fd37a7SXin LI #if DO_MULTIBYTE && !defined COMPILE_WIDE
47918fd37a7SXin LI       switch (*f)
48018fd37a7SXin LI 	{
48118fd37a7SXin LI 	case L_('%'):
48218fd37a7SXin LI 	  break;
48318fd37a7SXin LI 
48418fd37a7SXin LI 	case L_('\b'): case L_('\t'): case L_('\n'):
48518fd37a7SXin LI 	case L_('\v'): case L_('\f'): case L_('\r'):
48618fd37a7SXin LI 	case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
48718fd37a7SXin LI 	case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
48818fd37a7SXin LI 	case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
48918fd37a7SXin LI 	case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
49018fd37a7SXin LI 	case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
49118fd37a7SXin LI 	case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
49218fd37a7SXin LI 	case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
49318fd37a7SXin LI 	case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
49418fd37a7SXin LI 	case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
49518fd37a7SXin LI 	case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
49618fd37a7SXin LI 	case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
49718fd37a7SXin LI 	case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
49818fd37a7SXin LI 	case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
49918fd37a7SXin LI 	case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
50018fd37a7SXin LI 	case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
50118fd37a7SXin LI 	case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
50218fd37a7SXin LI 	case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
50318fd37a7SXin LI 	case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
50418fd37a7SXin LI 	case L_('~'):
50518fd37a7SXin LI 	  /* The C Standard requires these 98 characters (plus '%') to
50618fd37a7SXin LI 	     be in the basic execution character set.  None of these
50718fd37a7SXin LI 	     characters can start a multibyte sequence, so they need
50818fd37a7SXin LI 	     not be analyzed further.  */
50918fd37a7SXin LI 	  add (1, *p = *f);
51018fd37a7SXin LI 	  continue;
51118fd37a7SXin LI 
51218fd37a7SXin LI 	default:
51318fd37a7SXin LI 	  /* Copy this multibyte sequence until we reach its end, find
51418fd37a7SXin LI 	     an error, or come back to the initial shift state.  */
51518fd37a7SXin LI 	  {
51618fd37a7SXin LI 	    mbstate_t mbstate = mbstate_zero;
51718fd37a7SXin LI 	    size_t len = 0;
51818fd37a7SXin LI 	    size_t fsize;
51918fd37a7SXin LI 
52018fd37a7SXin LI 	    if (! format_end)
52118fd37a7SXin LI 	      format_end = f + strlen (f) + 1;
52218fd37a7SXin LI 	    fsize = format_end - f;
52318fd37a7SXin LI 
52418fd37a7SXin LI 	    do
52518fd37a7SXin LI 	      {
52618fd37a7SXin LI 		size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
52718fd37a7SXin LI 
52818fd37a7SXin LI 		if (bytes == 0)
52918fd37a7SXin LI 		  break;
53018fd37a7SXin LI 
53118fd37a7SXin LI 		if (bytes == (size_t) -2)
53218fd37a7SXin LI 		  {
53318fd37a7SXin LI 		    len += strlen (f + len);
53418fd37a7SXin LI 		    break;
53518fd37a7SXin LI 		  }
53618fd37a7SXin LI 
53718fd37a7SXin LI 		if (bytes == (size_t) -1)
53818fd37a7SXin LI 		  {
53918fd37a7SXin LI 		    len++;
54018fd37a7SXin LI 		    break;
54118fd37a7SXin LI 		  }
54218fd37a7SXin LI 
54318fd37a7SXin LI 		len += bytes;
54418fd37a7SXin LI 	      }
54518fd37a7SXin LI 	    while (! mbsinit (&mbstate));
54618fd37a7SXin LI 
54718fd37a7SXin LI 	    cpy (len, f);
54818fd37a7SXin LI 	    f += len - 1;
54918fd37a7SXin LI 	    continue;
55018fd37a7SXin LI 	  }
55118fd37a7SXin LI 	}
55218fd37a7SXin LI 
55318fd37a7SXin LI #else /* ! DO_MULTIBYTE */
55418fd37a7SXin LI 
55518fd37a7SXin LI       /* Either multibyte encodings are not supported, they are
55618fd37a7SXin LI 	 safe for formats, so any non-'%' byte can be copied through,
55718fd37a7SXin LI 	 or this is the wide character version.  */
55818fd37a7SXin LI       if (*f != L_('%'))
55918fd37a7SXin LI 	{
56018fd37a7SXin LI 	  add (1, *p = *f);
56118fd37a7SXin LI 	  continue;
56218fd37a7SXin LI 	}
56318fd37a7SXin LI 
56418fd37a7SXin LI #endif /* ! DO_MULTIBYTE */
56518fd37a7SXin LI 
56618fd37a7SXin LI       /* Check for flags that can modify a format.  */
56718fd37a7SXin LI       while (1)
56818fd37a7SXin LI 	{
56918fd37a7SXin LI 	  switch (*++f)
57018fd37a7SXin LI 	    {
57118fd37a7SXin LI 	      /* This influences the number formats.  */
57218fd37a7SXin LI 	    case L_('_'):
57318fd37a7SXin LI 	    case L_('-'):
57418fd37a7SXin LI 	    case L_('0'):
57518fd37a7SXin LI 	      pad = *f;
57618fd37a7SXin LI 	      continue;
57718fd37a7SXin LI 
57818fd37a7SXin LI 	      /* This changes textual output.  */
57918fd37a7SXin LI 	    case L_('^'):
58018fd37a7SXin LI 	      to_uppcase = 1;
58118fd37a7SXin LI 	      continue;
58218fd37a7SXin LI 	    case L_('#'):
58318fd37a7SXin LI 	      change_case = 1;
58418fd37a7SXin LI 	      continue;
58518fd37a7SXin LI 
58618fd37a7SXin LI 	    default:
58718fd37a7SXin LI 	      break;
58818fd37a7SXin LI 	    }
58918fd37a7SXin LI 	  break;
59018fd37a7SXin LI 	}
59118fd37a7SXin LI 
59218fd37a7SXin LI       /* As a GNU extension we allow to specify the field width.  */
59318fd37a7SXin LI       if (ISDIGIT (*f))
59418fd37a7SXin LI 	{
59518fd37a7SXin LI 	  width = 0;
59618fd37a7SXin LI 	  do
59718fd37a7SXin LI 	    {
59818fd37a7SXin LI 	      if (width > INT_MAX / 10
59918fd37a7SXin LI 		  || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10))
60018fd37a7SXin LI 		/* Avoid overflow.  */
60118fd37a7SXin LI 		width = INT_MAX;
60218fd37a7SXin LI 	      else
60318fd37a7SXin LI 		{
60418fd37a7SXin LI 		  width *= 10;
60518fd37a7SXin LI 		  width += *f - L_('0');
60618fd37a7SXin LI 		}
60718fd37a7SXin LI 	      ++f;
60818fd37a7SXin LI 	    }
60918fd37a7SXin LI 	  while (ISDIGIT (*f));
61018fd37a7SXin LI 	}
61118fd37a7SXin LI 
61218fd37a7SXin LI       /* Check for modifiers.  */
61318fd37a7SXin LI       switch (*f)
61418fd37a7SXin LI 	{
61518fd37a7SXin LI 	case L_('E'):
61618fd37a7SXin LI 	case L_('O'):
61718fd37a7SXin LI 	  modifier = *f++;
61818fd37a7SXin LI 	  break;
61918fd37a7SXin LI 
62018fd37a7SXin LI 	default:
62118fd37a7SXin LI 	  modifier = 0;
62218fd37a7SXin LI 	  break;
62318fd37a7SXin LI 	}
62418fd37a7SXin LI 
62518fd37a7SXin LI       /* Now do the specified format.  */
62618fd37a7SXin LI       format_char = *f;
62718fd37a7SXin LI       switch (format_char)
62818fd37a7SXin LI 	{
62918fd37a7SXin LI #define DO_NUMBER(d, v) \
63018fd37a7SXin LI 	  digits = d > width ? d : width;				      \
63118fd37a7SXin LI 	  number_value = v; goto do_number
63218fd37a7SXin LI #define DO_NUMBER_SPACEPAD(d, v) \
63318fd37a7SXin LI 	  digits = d > width ? d : width;				      \
63418fd37a7SXin LI 	  number_value = v; goto do_number_spacepad
63518fd37a7SXin LI 
63618fd37a7SXin LI 	case L_('%'):
63718fd37a7SXin LI 	  if (modifier != 0)
63818fd37a7SXin LI 	    goto bad_format;
63918fd37a7SXin LI 	  add (1, *p = *f);
64018fd37a7SXin LI 	  break;
64118fd37a7SXin LI 
64218fd37a7SXin LI 	case L_('a'):
64318fd37a7SXin LI 	  if (modifier != 0)
64418fd37a7SXin LI 	    goto bad_format;
64518fd37a7SXin LI 	  if (change_case)
64618fd37a7SXin LI 	    {
64718fd37a7SXin LI 	      to_uppcase = 1;
64818fd37a7SXin LI 	      to_lowcase = 0;
64918fd37a7SXin LI 	    }
65018fd37a7SXin LI #if defined _NL_CURRENT || !HAVE_STRFTIME
65118fd37a7SXin LI 	  cpy (aw_len, a_wkday);
65218fd37a7SXin LI 	  break;
65318fd37a7SXin LI #else
65418fd37a7SXin LI 	  goto underlying_strftime;
65518fd37a7SXin LI #endif
65618fd37a7SXin LI 
65718fd37a7SXin LI 	case 'A':
65818fd37a7SXin LI 	  if (modifier != 0)
65918fd37a7SXin LI 	    goto bad_format;
66018fd37a7SXin LI 	  if (change_case)
66118fd37a7SXin LI 	    {
66218fd37a7SXin LI 	      to_uppcase = 1;
66318fd37a7SXin LI 	      to_lowcase = 0;
66418fd37a7SXin LI 	    }
66518fd37a7SXin LI #if defined _NL_CURRENT || !HAVE_STRFTIME
66618fd37a7SXin LI 	  cpy (STRLEN (f_wkday), f_wkday);
66718fd37a7SXin LI 	  break;
66818fd37a7SXin LI #else
66918fd37a7SXin LI 	  goto underlying_strftime;
67018fd37a7SXin LI #endif
67118fd37a7SXin LI 
67218fd37a7SXin LI 	case L_('b'):
67318fd37a7SXin LI 	case L_('h'):
67418fd37a7SXin LI 	  if (change_case)
67518fd37a7SXin LI 	    {
67618fd37a7SXin LI 	      to_uppcase = 1;
67718fd37a7SXin LI 	      to_lowcase = 0;
67818fd37a7SXin LI 	    }
67918fd37a7SXin LI 	  if (modifier != 0)
68018fd37a7SXin LI 	    goto bad_format;
68118fd37a7SXin LI #if defined _NL_CURRENT || !HAVE_STRFTIME
68218fd37a7SXin LI 	  cpy (am_len, a_month);
68318fd37a7SXin LI 	  break;
68418fd37a7SXin LI #else
68518fd37a7SXin LI 	  goto underlying_strftime;
68618fd37a7SXin LI #endif
68718fd37a7SXin LI 
68818fd37a7SXin LI 	case L_('B'):
68918fd37a7SXin LI 	  if (modifier != 0)
69018fd37a7SXin LI 	    goto bad_format;
69118fd37a7SXin LI 	  if (change_case)
69218fd37a7SXin LI 	    {
69318fd37a7SXin LI 	      to_uppcase = 1;
69418fd37a7SXin LI 	      to_lowcase = 0;
69518fd37a7SXin LI 	    }
69618fd37a7SXin LI #if defined _NL_CURRENT || !HAVE_STRFTIME
69718fd37a7SXin LI 	  cpy (STRLEN (f_month), f_month);
69818fd37a7SXin LI 	  break;
69918fd37a7SXin LI #else
70018fd37a7SXin LI 	  goto underlying_strftime;
70118fd37a7SXin LI #endif
70218fd37a7SXin LI 
70318fd37a7SXin LI 	case L_('c'):
70418fd37a7SXin LI 	  if (modifier == L_('O'))
70518fd37a7SXin LI 	    goto bad_format;
70618fd37a7SXin LI #ifdef _NL_CURRENT
70718fd37a7SXin LI 	  if (! (modifier == 'E'
70818fd37a7SXin LI 		 && (*(subfmt =
70918fd37a7SXin LI 		       (const CHAR_T *) _NL_CURRENT (LC_TIME,
71018fd37a7SXin LI 						     NLW(ERA_D_T_FMT)))
71118fd37a7SXin LI 		     != '\0')))
71218fd37a7SXin LI 	    subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
71318fd37a7SXin LI #else
71418fd37a7SXin LI # if HAVE_STRFTIME
71518fd37a7SXin LI 	  goto underlying_strftime;
71618fd37a7SXin LI # else
71718fd37a7SXin LI 	  subfmt = L_("%a %b %e %H:%M:%S %Y");
71818fd37a7SXin LI # endif
71918fd37a7SXin LI #endif
72018fd37a7SXin LI 
72118fd37a7SXin LI 	subformat:
72218fd37a7SXin LI 	  {
72318fd37a7SXin LI 	    CHAR_T *old_start = p;
72418fd37a7SXin LI 	    size_t len = my_strftime (NULL, (size_t) -1, subfmt,
72518fd37a7SXin LI 				      tp extra_args LOCALE_ARG);
72618fd37a7SXin LI 	    add (len, my_strftime (p, maxsize - i, subfmt,
72718fd37a7SXin LI 				   tp extra_args LOCALE_ARG));
72818fd37a7SXin LI 
72918fd37a7SXin LI 	    if (to_uppcase)
73018fd37a7SXin LI 	      while (old_start < p)
73118fd37a7SXin LI 		{
73218fd37a7SXin LI 		  *old_start = TOUPPER ((UCHAR_T) *old_start, loc);
73318fd37a7SXin LI 		  ++old_start;
73418fd37a7SXin LI 		}
73518fd37a7SXin LI 	  }
73618fd37a7SXin LI 	  break;
73718fd37a7SXin LI 
73818fd37a7SXin LI #if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
73918fd37a7SXin LI 	underlying_strftime:
74018fd37a7SXin LI 	  {
74118fd37a7SXin LI 	    /* The relevant information is available only via the
74218fd37a7SXin LI 	       underlying strftime implementation, so use that.  */
74318fd37a7SXin LI 	    char ufmt[4];
74418fd37a7SXin LI 	    char *u = ufmt;
74518fd37a7SXin LI 	    char ubuf[1024]; /* enough for any single format in practice */
74618fd37a7SXin LI 	    size_t len;
74718fd37a7SXin LI 	    /* Make sure we're calling the actual underlying strftime.
74818fd37a7SXin LI 	       In some cases, config.h contains something like
74918fd37a7SXin LI 	       "#define strftime rpl_strftime".  */
75018fd37a7SXin LI # ifdef strftime
75118fd37a7SXin LI #  undef strftime
75218fd37a7SXin LI 	    size_t strftime ();
75318fd37a7SXin LI # endif
75418fd37a7SXin LI 
75518fd37a7SXin LI 	    *u++ = '%';
75618fd37a7SXin LI 	    if (modifier != 0)
75718fd37a7SXin LI 	      *u++ = modifier;
75818fd37a7SXin LI 	    *u++ = format_char;
75918fd37a7SXin LI 	    *u = '\0';
76018fd37a7SXin LI 	    len = strftime (ubuf, sizeof ubuf, ufmt, tp);
76118fd37a7SXin LI 	    if (len == 0 && ubuf[0] != '\0')
76218fd37a7SXin LI 	      return 0;
76318fd37a7SXin LI 	    cpy (len, ubuf);
76418fd37a7SXin LI 	  }
76518fd37a7SXin LI 	  break;
76618fd37a7SXin LI #endif
76718fd37a7SXin LI 
76818fd37a7SXin LI 	case L_('C'):
76918fd37a7SXin LI 	  if (modifier == L_('O'))
77018fd37a7SXin LI 	    goto bad_format;
77118fd37a7SXin LI 	  if (modifier == L_('E'))
77218fd37a7SXin LI 	    {
77318fd37a7SXin LI #if HAVE_STRUCT_ERA_ENTRY
77418fd37a7SXin LI 	      struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
77518fd37a7SXin LI 	      if (era)
77618fd37a7SXin LI 		{
77718fd37a7SXin LI # ifdef COMPILE_WIDE
77818fd37a7SXin LI 		  size_t len = __wcslen (era->era_wname);
77918fd37a7SXin LI 		  cpy (len, era->era_wname);
78018fd37a7SXin LI # else
78118fd37a7SXin LI 		  size_t len = strlen (era->era_name);
78218fd37a7SXin LI 		  cpy (len, era->era_name);
78318fd37a7SXin LI # endif
78418fd37a7SXin LI 		  break;
78518fd37a7SXin LI 		}
78618fd37a7SXin LI #else
78718fd37a7SXin LI # if HAVE_STRFTIME
78818fd37a7SXin LI 	      goto underlying_strftime;
78918fd37a7SXin LI # endif
79018fd37a7SXin LI #endif
79118fd37a7SXin LI 	    }
79218fd37a7SXin LI 
79318fd37a7SXin LI 	  {
79418fd37a7SXin LI 	    int year = tp->tm_year + TM_YEAR_BASE;
79518fd37a7SXin LI 	    DO_NUMBER (1, year / 100 - (year % 100 < 0));
79618fd37a7SXin LI 	  }
79718fd37a7SXin LI 
79818fd37a7SXin LI 	case L_('x'):
79918fd37a7SXin LI 	  if (modifier == L_('O'))
80018fd37a7SXin LI 	    goto bad_format;
80118fd37a7SXin LI #ifdef _NL_CURRENT
80218fd37a7SXin LI 	  if (! (modifier == L_('E')
80318fd37a7SXin LI 		 && (*(subfmt =
80418fd37a7SXin LI 		       (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
80518fd37a7SXin LI 		     != L_('\0'))))
80618fd37a7SXin LI 	    subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
80718fd37a7SXin LI 	  goto subformat;
80818fd37a7SXin LI #else
80918fd37a7SXin LI # if HAVE_STRFTIME
81018fd37a7SXin LI 	  goto underlying_strftime;
81118fd37a7SXin LI # else
81218fd37a7SXin LI 	  /* Fall through.  */
81318fd37a7SXin LI # endif
81418fd37a7SXin LI #endif
81518fd37a7SXin LI 	case L_('D'):
81618fd37a7SXin LI 	  if (modifier != 0)
81718fd37a7SXin LI 	    goto bad_format;
81818fd37a7SXin LI 	  subfmt = L_("%m/%d/%y");
81918fd37a7SXin LI 	  goto subformat;
82018fd37a7SXin LI 
82118fd37a7SXin LI 	case L_('d'):
82218fd37a7SXin LI 	  if (modifier == L_('E'))
82318fd37a7SXin LI 	    goto bad_format;
82418fd37a7SXin LI 
82518fd37a7SXin LI 	  DO_NUMBER (2, tp->tm_mday);
82618fd37a7SXin LI 
82718fd37a7SXin LI 	case L_('e'):
82818fd37a7SXin LI 	  if (modifier == L_('E'))
82918fd37a7SXin LI 	    goto bad_format;
83018fd37a7SXin LI 
83118fd37a7SXin LI 	  DO_NUMBER_SPACEPAD (2, tp->tm_mday);
83218fd37a7SXin LI 
83318fd37a7SXin LI 	  /* All numeric formats set DIGITS and NUMBER_VALUE and then
83418fd37a7SXin LI 	     jump to one of these two labels.  */
83518fd37a7SXin LI 
83618fd37a7SXin LI 	do_number_spacepad:
83718fd37a7SXin LI 	  /* Force `_' flag unless overridden by `0' or `-' flag.  */
83818fd37a7SXin LI 	  if (pad != L_('0') && pad != L_('-'))
83918fd37a7SXin LI 	    pad = L_('_');
84018fd37a7SXin LI 
84118fd37a7SXin LI 	do_number:
84218fd37a7SXin LI 	  /* Format the number according to the MODIFIER flag.  */
84318fd37a7SXin LI 
84418fd37a7SXin LI 	  if (modifier == L_('O') && 0 <= number_value)
84518fd37a7SXin LI 	    {
84618fd37a7SXin LI #ifdef _NL_CURRENT
84718fd37a7SXin LI 	      /* Get the locale specific alternate representation of
84818fd37a7SXin LI 		 the number NUMBER_VALUE.  If none exist NULL is returned.  */
84918fd37a7SXin LI 	      const CHAR_T *cp = nl_get_alt_digit (number_value
85018fd37a7SXin LI 						   HELPER_LOCALE_ARG);
85118fd37a7SXin LI 
85218fd37a7SXin LI 	      if (cp != NULL)
85318fd37a7SXin LI 		{
85418fd37a7SXin LI 		  size_t digitlen = STRLEN (cp);
85518fd37a7SXin LI 		  if (digitlen != 0)
85618fd37a7SXin LI 		    {
85718fd37a7SXin LI 		      cpy (digitlen, cp);
85818fd37a7SXin LI 		      break;
85918fd37a7SXin LI 		    }
86018fd37a7SXin LI 		}
86118fd37a7SXin LI #else
86218fd37a7SXin LI # if HAVE_STRFTIME
86318fd37a7SXin LI 	      goto underlying_strftime;
86418fd37a7SXin LI # endif
86518fd37a7SXin LI #endif
86618fd37a7SXin LI 	    }
86718fd37a7SXin LI 	  {
86818fd37a7SXin LI 	    unsigned int u = number_value;
86918fd37a7SXin LI 
87018fd37a7SXin LI 	    bufp = buf + sizeof (buf) / sizeof (buf[0]);
87118fd37a7SXin LI 	    negative_number = number_value < 0;
87218fd37a7SXin LI 
87318fd37a7SXin LI 	    if (negative_number)
87418fd37a7SXin LI 	      u = -u;
87518fd37a7SXin LI 
87618fd37a7SXin LI 	    do
87718fd37a7SXin LI 	      *--bufp = u % 10 + L_('0');
87818fd37a7SXin LI 	    while ((u /= 10) != 0);
87918fd37a7SXin LI 	  }
88018fd37a7SXin LI 
88118fd37a7SXin LI 	do_number_sign_and_padding:
88218fd37a7SXin LI 	  if (negative_number)
88318fd37a7SXin LI 	    *--bufp = L_('-');
88418fd37a7SXin LI 
88518fd37a7SXin LI 	  if (pad != L_('-'))
88618fd37a7SXin LI 	    {
88718fd37a7SXin LI 	      int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0]))
88818fd37a7SXin LI 				      - bufp);
88918fd37a7SXin LI 
89018fd37a7SXin LI 	      if (padding > 0)
89118fd37a7SXin LI 		{
89218fd37a7SXin LI 		  if (pad == L_('_'))
89318fd37a7SXin LI 		    {
89418fd37a7SXin LI 		      if ((size_t) padding >= maxsize - i)
89518fd37a7SXin LI 			return 0;
89618fd37a7SXin LI 
89718fd37a7SXin LI 		      if (p)
89818fd37a7SXin LI 			memset_space (p, padding);
89918fd37a7SXin LI 		      i += padding;
90018fd37a7SXin LI 		      width = width > padding ? width - padding : 0;
90118fd37a7SXin LI 		    }
90218fd37a7SXin LI 		  else
90318fd37a7SXin LI 		    {
90418fd37a7SXin LI 		      if ((size_t) digits >= maxsize - i)
90518fd37a7SXin LI 			return 0;
90618fd37a7SXin LI 
90718fd37a7SXin LI 		      if (negative_number)
90818fd37a7SXin LI 			{
90918fd37a7SXin LI 			  ++bufp;
91018fd37a7SXin LI 
91118fd37a7SXin LI 			  if (p)
91218fd37a7SXin LI 			    *p++ = L_('-');
91318fd37a7SXin LI 			  ++i;
91418fd37a7SXin LI 			}
91518fd37a7SXin LI 
91618fd37a7SXin LI 		      if (p)
91718fd37a7SXin LI 			memset_zero (p, padding);
91818fd37a7SXin LI 		      i += padding;
91918fd37a7SXin LI 		      width = 0;
92018fd37a7SXin LI 		    }
92118fd37a7SXin LI 		}
92218fd37a7SXin LI 	    }
92318fd37a7SXin LI 
92418fd37a7SXin LI 	  cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
92518fd37a7SXin LI 	  break;
92618fd37a7SXin LI 
92718fd37a7SXin LI 	case L_('F'):
92818fd37a7SXin LI 	  if (modifier != 0)
92918fd37a7SXin LI 	    goto bad_format;
93018fd37a7SXin LI 	  subfmt = L_("%Y-%m-%d");
93118fd37a7SXin LI 	  goto subformat;
93218fd37a7SXin LI 
93318fd37a7SXin LI 	case L_('H'):
93418fd37a7SXin LI 	  if (modifier == L_('E'))
93518fd37a7SXin LI 	    goto bad_format;
93618fd37a7SXin LI 
93718fd37a7SXin LI 	  DO_NUMBER (2, tp->tm_hour);
93818fd37a7SXin LI 
93918fd37a7SXin LI 	case L_('I'):
94018fd37a7SXin LI 	  if (modifier == L_('E'))
94118fd37a7SXin LI 	    goto bad_format;
94218fd37a7SXin LI 
94318fd37a7SXin LI 	  DO_NUMBER (2, hour12);
94418fd37a7SXin LI 
94518fd37a7SXin LI 	case L_('k'):		/* GNU extension.  */
94618fd37a7SXin LI 	  if (modifier == L_('E'))
94718fd37a7SXin LI 	    goto bad_format;
94818fd37a7SXin LI 
94918fd37a7SXin LI 	  DO_NUMBER_SPACEPAD (2, tp->tm_hour);
95018fd37a7SXin LI 
95118fd37a7SXin LI 	case L_('l'):		/* GNU extension.  */
95218fd37a7SXin LI 	  if (modifier == L_('E'))
95318fd37a7SXin LI 	    goto bad_format;
95418fd37a7SXin LI 
95518fd37a7SXin LI 	  DO_NUMBER_SPACEPAD (2, hour12);
95618fd37a7SXin LI 
95718fd37a7SXin LI 	case L_('j'):
95818fd37a7SXin LI 	  if (modifier == L_('E'))
95918fd37a7SXin LI 	    goto bad_format;
96018fd37a7SXin LI 
96118fd37a7SXin LI 	  DO_NUMBER (3, 1 + tp->tm_yday);
96218fd37a7SXin LI 
96318fd37a7SXin LI 	case L_('M'):
96418fd37a7SXin LI 	  if (modifier == L_('E'))
96518fd37a7SXin LI 	    goto bad_format;
96618fd37a7SXin LI 
96718fd37a7SXin LI 	  DO_NUMBER (2, tp->tm_min);
96818fd37a7SXin LI 
96918fd37a7SXin LI 	case L_('m'):
97018fd37a7SXin LI 	  if (modifier == L_('E'))
97118fd37a7SXin LI 	    goto bad_format;
97218fd37a7SXin LI 
97318fd37a7SXin LI 	  DO_NUMBER (2, tp->tm_mon + 1);
97418fd37a7SXin LI 
97518fd37a7SXin LI #ifndef _LIBC
97618fd37a7SXin LI 	case L_('N'):		/* GNU extension.  */
97718fd37a7SXin LI 	  if (modifier == L_('E'))
97818fd37a7SXin LI 	    goto bad_format;
97918fd37a7SXin LI 
98018fd37a7SXin LI 	  number_value = ns;
98118fd37a7SXin LI 	  if (width != -1)
98218fd37a7SXin LI 	    {
98318fd37a7SXin LI 	      /* Take an explicit width less than 9 as a precision.  */
98418fd37a7SXin LI 	      int j;
98518fd37a7SXin LI 	      for (j = width; j < 9; j++)
98618fd37a7SXin LI 		number_value /= 10;
98718fd37a7SXin LI 	    }
98818fd37a7SXin LI 
98918fd37a7SXin LI 	  DO_NUMBER (9, number_value);
99018fd37a7SXin LI #endif
99118fd37a7SXin LI 
99218fd37a7SXin LI 	case L_('n'):
99318fd37a7SXin LI 	  add (1, *p = L_('\n'));
99418fd37a7SXin LI 	  break;
99518fd37a7SXin LI 
99618fd37a7SXin LI 	case L_('P'):
99718fd37a7SXin LI 	  to_lowcase = 1;
99818fd37a7SXin LI #if !defined _NL_CURRENT && HAVE_STRFTIME
99918fd37a7SXin LI 	  format_char = L_('p');
100018fd37a7SXin LI #endif
100118fd37a7SXin LI 	  /* FALLTHROUGH */
100218fd37a7SXin LI 
100318fd37a7SXin LI 	case L_('p'):
100418fd37a7SXin LI 	  if (change_case)
100518fd37a7SXin LI 	    {
100618fd37a7SXin LI 	      to_uppcase = 0;
100718fd37a7SXin LI 	      to_lowcase = 1;
100818fd37a7SXin LI 	    }
100918fd37a7SXin LI #if defined _NL_CURRENT || !HAVE_STRFTIME
101018fd37a7SXin LI 	  cpy (ap_len, ampm);
101118fd37a7SXin LI 	  break;
101218fd37a7SXin LI #else
101318fd37a7SXin LI 	  goto underlying_strftime;
101418fd37a7SXin LI #endif
101518fd37a7SXin LI 
101618fd37a7SXin LI 	case L_('R'):
101718fd37a7SXin LI 	  subfmt = L_("%H:%M");
101818fd37a7SXin LI 	  goto subformat;
101918fd37a7SXin LI 
102018fd37a7SXin LI 	case L_('r'):
102118fd37a7SXin LI #if !defined _NL_CURRENT && HAVE_STRFTIME
102218fd37a7SXin LI 	  goto underlying_strftime;
102318fd37a7SXin LI #else
102418fd37a7SXin LI # ifdef _NL_CURRENT
102518fd37a7SXin LI 	  if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
102618fd37a7SXin LI 						       NLW(T_FMT_AMPM)))
102718fd37a7SXin LI 	      == L_('\0'))
102818fd37a7SXin LI # endif
102918fd37a7SXin LI 	    subfmt = L_("%I:%M:%S %p");
103018fd37a7SXin LI 	  goto subformat;
103118fd37a7SXin LI #endif
103218fd37a7SXin LI 
103318fd37a7SXin LI 	case L_('S'):
103418fd37a7SXin LI 	  if (modifier == L_('E'))
103518fd37a7SXin LI 	    goto bad_format;
103618fd37a7SXin LI 
103718fd37a7SXin LI 	  DO_NUMBER (2, tp->tm_sec);
103818fd37a7SXin LI 
103918fd37a7SXin LI 	case L_('s'):		/* GNU extension.  */
104018fd37a7SXin LI 	  {
104118fd37a7SXin LI 	    struct tm ltm;
104218fd37a7SXin LI 	    time_t t;
104318fd37a7SXin LI 
104418fd37a7SXin LI 	    ltm = *tp;
104518fd37a7SXin LI 	    t = mktime (&ltm);
104618fd37a7SXin LI 
104718fd37a7SXin LI 	    /* Generate string value for T using time_t arithmetic;
104818fd37a7SXin LI 	       this works even if sizeof (long) < sizeof (time_t).  */
104918fd37a7SXin LI 
105018fd37a7SXin LI 	    bufp = buf + sizeof (buf) / sizeof (buf[0]);
105118fd37a7SXin LI 	    negative_number = t < 0;
105218fd37a7SXin LI 
105318fd37a7SXin LI 	    do
105418fd37a7SXin LI 	      {
105518fd37a7SXin LI 		int d = t % 10;
105618fd37a7SXin LI 		t /= 10;
105718fd37a7SXin LI 
105818fd37a7SXin LI 		if (negative_number)
105918fd37a7SXin LI 		  {
106018fd37a7SXin LI 		    d = -d;
106118fd37a7SXin LI 
106218fd37a7SXin LI 		    /* Adjust if division truncates to minus infinity.  */
106318fd37a7SXin LI 		    if (0 < -1 % 10 && d < 0)
106418fd37a7SXin LI 		      {
106518fd37a7SXin LI 			t++;
106618fd37a7SXin LI 			d += 10;
106718fd37a7SXin LI 		      }
106818fd37a7SXin LI 		  }
106918fd37a7SXin LI 
107018fd37a7SXin LI 		*--bufp = d + L_('0');
107118fd37a7SXin LI 	      }
107218fd37a7SXin LI 	    while (t != 0);
107318fd37a7SXin LI 
107418fd37a7SXin LI 	    digits = 1;
107518fd37a7SXin LI 	    goto do_number_sign_and_padding;
107618fd37a7SXin LI 	  }
107718fd37a7SXin LI 
107818fd37a7SXin LI 	case L_('X'):
107918fd37a7SXin LI 	  if (modifier == L_('O'))
108018fd37a7SXin LI 	    goto bad_format;
108118fd37a7SXin LI #ifdef _NL_CURRENT
108218fd37a7SXin LI 	  if (! (modifier == L_('E')
108318fd37a7SXin LI 		 && (*(subfmt =
108418fd37a7SXin LI 		       (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
108518fd37a7SXin LI 		     != L_('\0'))))
108618fd37a7SXin LI 	    subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
108718fd37a7SXin LI 	  goto subformat;
108818fd37a7SXin LI #else
108918fd37a7SXin LI # if HAVE_STRFTIME
109018fd37a7SXin LI 	  goto underlying_strftime;
109118fd37a7SXin LI # else
109218fd37a7SXin LI 	  /* Fall through.  */
109318fd37a7SXin LI # endif
109418fd37a7SXin LI #endif
109518fd37a7SXin LI 	case L_('T'):
109618fd37a7SXin LI 	  subfmt = L_("%H:%M:%S");
109718fd37a7SXin LI 	  goto subformat;
109818fd37a7SXin LI 
109918fd37a7SXin LI 	case L_('t'):
110018fd37a7SXin LI 	  add (1, *p = L_('\t'));
110118fd37a7SXin LI 	  break;
110218fd37a7SXin LI 
110318fd37a7SXin LI 	case L_('u'):
110418fd37a7SXin LI 	  DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
110518fd37a7SXin LI 
110618fd37a7SXin LI 	case L_('U'):
110718fd37a7SXin LI 	  if (modifier == L_('E'))
110818fd37a7SXin LI 	    goto bad_format;
110918fd37a7SXin LI 
111018fd37a7SXin LI 	  DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
111118fd37a7SXin LI 
111218fd37a7SXin LI 	case L_('V'):
111318fd37a7SXin LI 	case L_('g'):
111418fd37a7SXin LI 	case L_('G'):
111518fd37a7SXin LI 	  if (modifier == L_('E'))
111618fd37a7SXin LI 	    goto bad_format;
111718fd37a7SXin LI 	  {
111818fd37a7SXin LI 	    int year = tp->tm_year + TM_YEAR_BASE;
111918fd37a7SXin LI 	    int days = iso_week_days (tp->tm_yday, tp->tm_wday);
112018fd37a7SXin LI 
112118fd37a7SXin LI 	    if (days < 0)
112218fd37a7SXin LI 	      {
112318fd37a7SXin LI 		/* This ISO week belongs to the previous year.  */
112418fd37a7SXin LI 		year--;
112518fd37a7SXin LI 		days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
112618fd37a7SXin LI 				      tp->tm_wday);
112718fd37a7SXin LI 	      }
112818fd37a7SXin LI 	    else
112918fd37a7SXin LI 	      {
113018fd37a7SXin LI 		int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
113118fd37a7SXin LI 				       tp->tm_wday);
113218fd37a7SXin LI 		if (0 <= d)
113318fd37a7SXin LI 		  {
113418fd37a7SXin LI 		    /* This ISO week belongs to the next year.  */
113518fd37a7SXin LI 		    year++;
113618fd37a7SXin LI 		    days = d;
113718fd37a7SXin LI 		  }
113818fd37a7SXin LI 	      }
113918fd37a7SXin LI 
114018fd37a7SXin LI 	    switch (*f)
114118fd37a7SXin LI 	      {
114218fd37a7SXin LI 	      case L_('g'):
114318fd37a7SXin LI 		DO_NUMBER (2, (year % 100 + 100) % 100);
114418fd37a7SXin LI 
114518fd37a7SXin LI 	      case L_('G'):
114618fd37a7SXin LI 		DO_NUMBER (1, year);
114718fd37a7SXin LI 
114818fd37a7SXin LI 	      default:
114918fd37a7SXin LI 		DO_NUMBER (2, days / 7 + 1);
115018fd37a7SXin LI 	      }
115118fd37a7SXin LI 	  }
115218fd37a7SXin LI 
115318fd37a7SXin LI 	case L_('W'):
115418fd37a7SXin LI 	  if (modifier == L_('E'))
115518fd37a7SXin LI 	    goto bad_format;
115618fd37a7SXin LI 
115718fd37a7SXin LI 	  DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
115818fd37a7SXin LI 
115918fd37a7SXin LI 	case L_('w'):
116018fd37a7SXin LI 	  if (modifier == L_('E'))
116118fd37a7SXin LI 	    goto bad_format;
116218fd37a7SXin LI 
116318fd37a7SXin LI 	  DO_NUMBER (1, tp->tm_wday);
116418fd37a7SXin LI 
116518fd37a7SXin LI 	case L_('Y'):
116618fd37a7SXin LI 	  if (modifier == 'E')
116718fd37a7SXin LI 	    {
116818fd37a7SXin LI #if HAVE_STRUCT_ERA_ENTRY
116918fd37a7SXin LI 	      struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
117018fd37a7SXin LI 	      if (era)
117118fd37a7SXin LI 		{
117218fd37a7SXin LI # ifdef COMPILE_WIDE
117318fd37a7SXin LI 		  subfmt = era->era_wformat;
117418fd37a7SXin LI # else
117518fd37a7SXin LI 		  subfmt = era->era_format;
117618fd37a7SXin LI # endif
117718fd37a7SXin LI 		  goto subformat;
117818fd37a7SXin LI 		}
117918fd37a7SXin LI #else
118018fd37a7SXin LI # if HAVE_STRFTIME
118118fd37a7SXin LI 	      goto underlying_strftime;
118218fd37a7SXin LI # endif
118318fd37a7SXin LI #endif
118418fd37a7SXin LI 	    }
118518fd37a7SXin LI 	  if (modifier == L_('O'))
118618fd37a7SXin LI 	    goto bad_format;
118718fd37a7SXin LI 	  else
118818fd37a7SXin LI 	    DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
118918fd37a7SXin LI 
119018fd37a7SXin LI 	case L_('y'):
119118fd37a7SXin LI 	  if (modifier == L_('E'))
119218fd37a7SXin LI 	    {
119318fd37a7SXin LI #if HAVE_STRUCT_ERA_ENTRY
119418fd37a7SXin LI 	      struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
119518fd37a7SXin LI 	      if (era)
119618fd37a7SXin LI 		{
119718fd37a7SXin LI 		  int delta = tp->tm_year - era->start_date[0];
119818fd37a7SXin LI 		  DO_NUMBER (1, (era->offset
119918fd37a7SXin LI 				 + delta * era->absolute_direction));
120018fd37a7SXin LI 		}
120118fd37a7SXin LI #else
120218fd37a7SXin LI # if HAVE_STRFTIME
120318fd37a7SXin LI 	      goto underlying_strftime;
120418fd37a7SXin LI # endif
120518fd37a7SXin LI #endif
120618fd37a7SXin LI 	    }
120718fd37a7SXin LI 	  DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
120818fd37a7SXin LI 
120918fd37a7SXin LI 	case L_('Z'):
121018fd37a7SXin LI 	  if (change_case)
121118fd37a7SXin LI 	    {
121218fd37a7SXin LI 	      to_uppcase = 0;
121318fd37a7SXin LI 	      to_lowcase = 1;
121418fd37a7SXin LI 	    }
121518fd37a7SXin LI 
121618fd37a7SXin LI #if HAVE_TZNAME
121718fd37a7SXin LI 	  /* The tzset() call might have changed the value.  */
121818fd37a7SXin LI 	  if (!(zone && *zone) && tp->tm_isdst >= 0)
121918fd37a7SXin LI 	    zone = tzname[tp->tm_isdst];
122018fd37a7SXin LI #endif
122118fd37a7SXin LI 	  if (! zone)
122218fd37a7SXin LI 	    zone = "";
122318fd37a7SXin LI 
122418fd37a7SXin LI #ifdef COMPILE_WIDE
122518fd37a7SXin LI 	  {
122618fd37a7SXin LI 	    /* The zone string is always given in multibyte form.  We have
122718fd37a7SXin LI 	       to transform it first.  */
122818fd37a7SXin LI 	    wchar_t *wczone;
122918fd37a7SXin LI 	    size_t len;
123018fd37a7SXin LI 	    widen (zone, wczone, len);
123118fd37a7SXin LI 	    cpy (len, wczone);
123218fd37a7SXin LI 	  }
123318fd37a7SXin LI #else
123418fd37a7SXin LI 	  cpy (strlen (zone), zone);
123518fd37a7SXin LI #endif
123618fd37a7SXin LI 	  break;
123718fd37a7SXin LI 
123818fd37a7SXin LI 	case L_('z'):
123918fd37a7SXin LI 	  if (tp->tm_isdst < 0)
124018fd37a7SXin LI 	    break;
124118fd37a7SXin LI 
124218fd37a7SXin LI 	  {
124318fd37a7SXin LI 	    int diff;
124418fd37a7SXin LI #if HAVE_TM_GMTOFF
124518fd37a7SXin LI 	    diff = tp->tm_gmtoff;
124618fd37a7SXin LI #else
124718fd37a7SXin LI 	    if (ut)
124818fd37a7SXin LI 	      diff = 0;
124918fd37a7SXin LI 	    else
125018fd37a7SXin LI 	      {
125118fd37a7SXin LI 		struct tm gtm;
125218fd37a7SXin LI 		struct tm ltm;
125318fd37a7SXin LI 		time_t lt;
125418fd37a7SXin LI 
125518fd37a7SXin LI 		ltm = *tp;
125618fd37a7SXin LI 		lt = mktime (&ltm);
125718fd37a7SXin LI 
125818fd37a7SXin LI 		if (lt == (time_t) -1)
125918fd37a7SXin LI 		  {
126018fd37a7SXin LI 		    /* mktime returns -1 for errors, but -1 is also a
126118fd37a7SXin LI 		       valid time_t value.  Check whether an error really
126218fd37a7SXin LI 		       occurred.  */
126318fd37a7SXin LI 		    struct tm tm;
126418fd37a7SXin LI 
126518fd37a7SXin LI 		    if (! __localtime_r (&lt, &tm)
126618fd37a7SXin LI 			|| ((ltm.tm_sec ^ tm.tm_sec)
126718fd37a7SXin LI 			    | (ltm.tm_min ^ tm.tm_min)
126818fd37a7SXin LI 			    | (ltm.tm_hour ^ tm.tm_hour)
126918fd37a7SXin LI 			    | (ltm.tm_mday ^ tm.tm_mday)
127018fd37a7SXin LI 			    | (ltm.tm_mon ^ tm.tm_mon)
127118fd37a7SXin LI 			    | (ltm.tm_year ^ tm.tm_year)))
127218fd37a7SXin LI 		      break;
127318fd37a7SXin LI 		  }
127418fd37a7SXin LI 
127518fd37a7SXin LI 		if (! __gmtime_r (&lt, &gtm))
127618fd37a7SXin LI 		  break;
127718fd37a7SXin LI 
127818fd37a7SXin LI 		diff = tm_diff (&ltm, &gtm);
127918fd37a7SXin LI 	      }
128018fd37a7SXin LI #endif
128118fd37a7SXin LI 
128218fd37a7SXin LI 	    if (diff < 0)
128318fd37a7SXin LI 	      {
128418fd37a7SXin LI 		add (1, *p = L_('-'));
128518fd37a7SXin LI 		diff = -diff;
128618fd37a7SXin LI 	      }
128718fd37a7SXin LI 	    else
128818fd37a7SXin LI 	      add (1, *p = L_('+'));
128918fd37a7SXin LI 
129018fd37a7SXin LI 	    diff /= 60;
129118fd37a7SXin LI 	    DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
129218fd37a7SXin LI 	  }
129318fd37a7SXin LI 
129418fd37a7SXin LI 	case L_('\0'):		/* GNU extension: % at end of format.  */
129518fd37a7SXin LI 	    --f;
129618fd37a7SXin LI 	    /* Fall through.  */
129718fd37a7SXin LI 	default:
129818fd37a7SXin LI 	  /* Unknown format; output the format, including the '%',
129918fd37a7SXin LI 	     since this is most likely the right thing to do if a
130018fd37a7SXin LI 	     multibyte string has been misparsed.  */
130118fd37a7SXin LI 	bad_format:
130218fd37a7SXin LI 	  {
130318fd37a7SXin LI 	    int flen;
130418fd37a7SXin LI 	    for (flen = 1; f[1 - flen] != L_('%'); flen++)
130518fd37a7SXin LI 	      continue;
130618fd37a7SXin LI 	    cpy (flen, &f[1 - flen]);
130718fd37a7SXin LI 	  }
130818fd37a7SXin LI 	  break;
130918fd37a7SXin LI 	}
131018fd37a7SXin LI     }
131118fd37a7SXin LI 
131218fd37a7SXin LI   if (p && maxsize != 0)
131318fd37a7SXin LI     *p = L_('\0');
131418fd37a7SXin LI   return i;
131518fd37a7SXin LI }
131618fd37a7SXin LI #ifdef _LIBC
libc_hidden_def(my_strftime)131718fd37a7SXin LI libc_hidden_def (my_strftime)
131818fd37a7SXin LI #endif
131918fd37a7SXin LI 
132018fd37a7SXin LI 
132118fd37a7SXin LI #ifdef emacs
132218fd37a7SXin LI /* For Emacs we have a separate interface which corresponds to the normal
132318fd37a7SXin LI    strftime function plus the ut argument, but without the ns argument.  */
132418fd37a7SXin LI size_t
132518fd37a7SXin LI emacs_strftimeu (char *s, size_t maxsize, const char *format,
132618fd37a7SXin LI 		 const struct tm *tp, int ut)
132718fd37a7SXin LI {
132818fd37a7SXin LI   return my_strftime (s, maxsize, format, tp, ut, 0);
132918fd37a7SXin LI }
133018fd37a7SXin LI #endif
1331