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