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