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