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