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