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