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