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 (<m); 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 (<m); 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 (<, &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 (<, >m)) 1276170754Sdelphij break; 1277170754Sdelphij 1278170754Sdelphij diff = tm_diff (<m, >m); 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