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