aboutsummaryrefslogtreecommitdiffstats
path: root/lib/strftime.c
diff options
context:
space:
mode:
authorPaul Eggert2016-11-19 16:00:57 -0800
committerPaul Eggert2016-11-19 16:01:32 -0800
commitbbd84f86bce9c04ae08d9bccbad19c48e74f3f8b (patch)
tree19d511f0e9094149fc61dfe19af992bff0cb03d8 /lib/strftime.c
parent493a8f33ba916403d1fab620e08146632b826101 (diff)
downloademacs-bbd84f86bce9c04ae08d9bccbad19c48e74f3f8b.tar.gz
emacs-bbd84f86bce9c04ae08d9bccbad19c48e74f3f8b.zip
Merge from gnulib
This incorporates: 2016-11-15 sys_time: add gnulib::timeval for C++ 2016-11-14 snippet/c++defs: fix real-floating arg functions in C++ mode 2016-11-13 strftime: don't use __THROW 2016-11-12 strftime: tune %q 2016-11-12 Merge strftime.c changes from glibc 2016-11-09 manywarnings: fix -Wno-missing-field-initializers detection 2016-11-05 strftime,strptime: support %q to represent the quarter The glibc changes in turn incorporate the following strftime.c changes: 2015-10-20 Convert misc function definitions to prototype style 2015-09-26 [BZ #18985] out of range data to strftime() causes segfault 2010-01-09 Add support for XPG7 testing 2009-10-30 Implement Burmese language locale for Myanmar 2008-06-13 [BZ #6612] pass reference to tzset_called around 2007-10-16 [BZ #5184] Add tzset_called argument * build-aux/snippet/c++defs.h, lib/strftime.c, lib/sys_time.in.h: * m4/manywarnings.m4: Copy from gnulib.
Diffstat (limited to 'lib/strftime.c')
-rw-r--r--lib/strftime.c162
1 files changed, 98 insertions, 64 deletions
diff --git a/lib/strftime.c b/lib/strftime.c
index 4e65190386d..2e010830f7a 100644
--- a/lib/strftime.c
+++ b/lib/strftime.c
@@ -1,22 +1,22 @@
1/* Copyright (C) 1991-2001, 2003-2007, 2009-2016 Free Software Foundation, Inc. 1/* Copyright (C) 1991-2016 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
2 3
3 NOTE: The canonical source of this file is maintained with the GNU C Library. 4 The GNU C Library is free software; you can redistribute it and/or
4 Bugs can be reported to bug-glibc@prep.ai.mit.edu. 5 modify it under the terms of the GNU General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
5 8
6 This program is free software: you can redistribute it and/or modify 9 The GNU C Library is distributed in the hope that it will be useful,
7 it under the terms of the GNU 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 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 GNU General Public License for more details. 12 General Public License for more details.
15 13
16 You should have received a copy of the GNU General Public License 14 You should have received a copy of the GNU General Public
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 License along with the GNU C Library; if not, see
16 <http://www.gnu.org/licenses/>. */
18 17
19#ifdef _LIBC 18#ifdef _LIBC
19# define USE_IN_EXTENDED_LOCALE_MODEL 1
20# define HAVE_STRUCT_ERA_ENTRY 1 20# define HAVE_STRUCT_ERA_ENTRY 1
21# define HAVE_TM_GMTOFF 1 21# define HAVE_TM_GMTOFF 1
22# define HAVE_TM_ZONE 1 22# define HAVE_TM_ZONE 1
@@ -63,10 +63,10 @@ extern char *tzname[];
63#endif 63#endif
64 64
65#include <limits.h> 65#include <limits.h>
66#include <stdbool.h>
67#include <stddef.h> 66#include <stddef.h>
68#include <stdlib.h> 67#include <stdlib.h>
69#include <string.h> 68#include <string.h>
69#include <stdbool.h>
70 70
71#ifdef COMPILE_WIDE 71#ifdef COMPILE_WIDE
72# include <endian.h> 72# include <endian.h>
@@ -247,11 +247,11 @@ extern char *tzname[];
247# undef _NL_CURRENT 247# undef _NL_CURRENT
248# define _NL_CURRENT(category, item) \ 248# define _NL_CURRENT(category, item) \
249 (current->values[_NL_ITEM_INDEX (item)].string) 249 (current->values[_NL_ITEM_INDEX (item)].string)
250# define LOCALE_PARAM , __locale_t loc
250# define LOCALE_ARG , loc 251# define LOCALE_ARG , loc
251# define LOCALE_PARAM_PROTO , __locale_t loc
252# define HELPER_LOCALE_ARG , current 252# define HELPER_LOCALE_ARG , current
253#else 253#else
254# define LOCALE_PARAM_PROTO 254# define LOCALE_PARAM
255# define LOCALE_ARG 255# define LOCALE_ARG
256# ifdef _LIBC 256# ifdef _LIBC
257# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME) 257# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
@@ -304,18 +304,22 @@ fwrite_uppcase (FILE *fp, const CHAR_T *src, size_t len)
304 } 304 }
305} 305}
306#else 306#else
307static CHAR_T *memcpy_lowcase (CHAR_T *dest, const CHAR_T *src,
308 size_t len LOCALE_PARAM);
309
307static CHAR_T * 310static CHAR_T *
308memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, 311memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
309 size_t len LOCALE_PARAM_PROTO)
310{ 312{
311 while (len-- > 0) 313 while (len-- > 0)
312 dest[len] = TOLOWER ((UCHAR_T) src[len], loc); 314 dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
313 return dest; 315 return dest;
314} 316}
315 317
318static CHAR_T *memcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
319 size_t len LOCALE_PARAM);
320
316static CHAR_T * 321static CHAR_T *
317memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, 322memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
318 size_t len LOCALE_PARAM_PROTO)
319{ 323{
320 while (len-- > 0) 324 while (len-- > 0)
321 dest[len] = TOUPPER ((UCHAR_T) src[len], loc); 325 dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
@@ -328,6 +332,7 @@ memcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
328/* Yield the difference between *A and *B, 332/* Yield the difference between *A and *B,
329 measured in seconds, ignoring leap seconds. */ 333 measured in seconds, ignoring leap seconds. */
330# define tm_diff ftime_tm_diff 334# define tm_diff ftime_tm_diff
335static int tm_diff (const struct tm *, const struct tm *);
331static int 336static int
332tm_diff (const struct tm *a, const struct tm *b) 337tm_diff (const struct tm *a, const struct tm *b)
333{ 338{
@@ -359,6 +364,7 @@ tm_diff (const struct tm *a, const struct tm *b)
359#define ISO_WEEK_START_WDAY 1 /* Monday */ 364#define ISO_WEEK_START_WDAY 1 /* Monday */
360#define ISO_WEEK1_WDAY 4 /* Thursday */ 365#define ISO_WEEK1_WDAY 4 /* Thursday */
361#define YDAY_MINIMUM (-366) 366#define YDAY_MINIMUM (-366)
367static int iso_week_days (int, int);
362#ifdef __GNUC__ 368#ifdef __GNUC__
363__inline__ 369__inline__
364#endif 370#endif
@@ -401,17 +407,41 @@ iso_week_days (int yday, int wday)
401# define ns 0 407# define ns 0
402#endif 408#endif
403 409
410static size_t __strftime_internal (STREAM_OR_CHAR_T *, STRFTIME_ARG (size_t)
411 const CHAR_T *, const struct tm *,
412 bool, bool *
413 extra_args_spec LOCALE_PARAM);
404 414
405/* Just like my_strftime, below, but with one more parameter, UPCASE, 415/* Write information from TP into S according to the format
406 to indicate that the result should be converted to upper case. */ 416 string FORMAT, writing no more that MAXSIZE characters
417 (including the terminating '\0') and returning number of
418 characters written. If S is NULL, nothing will be written
419 anywhere, so to determine how many characters would be
420 written, use NULL for S and (size_t) -1 for MAXSIZE. */
421size_t
422my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
423 const CHAR_T *format,
424 const struct tm *tp extra_args_spec LOCALE_PARAM)
425{
426 bool tzset_called = false;
427 return __strftime_internal (s, STRFTIME_ARG (maxsize) format, tp,
428 false, &tzset_called extra_args LOCALE_ARG);
429}
430#if defined _LIBC && ! FPRINTFTIME
431libc_hidden_def (my_strftime)
432#endif
433
434/* Just like my_strftime, above, but with two more parameters.
435 UPCASE indicate that the result should be converted to upper case,
436 and *TZSET_CALLED indicates whether tzset has been called here. */
407static size_t 437static size_t
408strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s, 438__strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
409 STRFTIME_ARG (size_t maxsize) 439 const CHAR_T *format,
410 const CHAR_T *format, 440 const struct tm *tp, bool upcase, bool *tzset_called
411 const struct tm *tp extra_args_spec LOCALE_PARAM_PROTO) 441 extra_args_spec LOCALE_PARAM)
412{ 442{
413#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL 443#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
414 struct locale_data *const current = loc->__locales[LC_TIME]; 444 struct __locale_data *const current = loc->__locales[LC_TIME];
415#endif 445#endif
416#if FPRINTFTIME 446#if FPRINTFTIME
417 size_t maxsize = (size_t) -1; 447 size_t maxsize = (size_t) -1;
@@ -426,13 +456,17 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
426 only a few elements. Dereference the pointers only if the format 456 only a few elements. Dereference the pointers only if the format
427 requires this. Then it is ok to fail if the pointers are invalid. */ 457 requires this. Then it is ok to fail if the pointers are invalid. */
428# define a_wkday \ 458# define a_wkday \
429 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)) 459 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
460 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)))
430# define f_wkday \ 461# define f_wkday \
431 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)) 462 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
463 ? "?" : _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)))
432# define a_month \ 464# define a_month \
433 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)) 465 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
466 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)))
434# define f_month \ 467# define f_month \
435 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)) 468 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
469 ? "?" : _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)))
436# define ampm \ 470# define ampm \
437 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \ 471 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \
438 ? NLW(PM_STR) : NLW(AM_STR))) 472 ? NLW(PM_STR) : NLW(AM_STR)))
@@ -483,15 +517,21 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
483 /* Infer the zone name from *TZ instead of from TZNAME. */ 517 /* Infer the zone name from *TZ instead of from TZNAME. */
484 tzname_vec = tz->tzname_copy; 518 tzname_vec = tz->tzname_copy;
485# endif 519# endif
520 }
521 /* The tzset() call might have changed the value. */
522 if (!(zone && *zone) && tp->tm_isdst >= 0)
523 {
486 /* POSIX.1 requires that local time zone information be used as 524 /* POSIX.1 requires that local time zone information be used as
487 though strftime called tzset. */ 525 though strftime called tzset. */
488# if HAVE_TZSET 526# if HAVE_TZSET
489 tzset (); 527 if (!*tzset_called)
528 {
529 tzset ();
530 *tzset_called = true;
531 }
490# endif 532# endif
533 zone = tzname_vec[tp->tm_isdst != 0];
491 } 534 }
492 /* The tzset() call might have changed the value. */
493 if (!(zone && *zone) && tp->tm_isdst >= 0)
494 zone = tzname_vec[tp->tm_isdst != 0];
495#endif 535#endif
496 if (! zone) 536 if (! zone)
497 zone = ""; 537 zone = "";
@@ -801,14 +841,15 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
801 841
802 subformat: 842 subformat:
803 { 843 {
804 size_t len = strftime_case_ (to_uppcase, 844 size_t len = __strftime_internal (NULL, STRFTIME_ARG ((size_t) -1)
805 NULL, STRFTIME_ARG ((size_t) -1) 845 subfmt,
806 subfmt, 846 tp, to_uppcase, tzset_called
807 tp extra_args LOCALE_ARG); 847 extra_args LOCALE_ARG);
808 add (len, strftime_case_ (to_uppcase, p, 848 add (len, __strftime_internal (p,
809 STRFTIME_ARG (maxsize - i) 849 STRFTIME_ARG (maxsize - i)
810 subfmt, 850 subfmt,
811 tp extra_args LOCALE_ARG)); 851 tp, to_uppcase, tzset_called
852 extra_args LOCALE_ARG));
812 } 853 }
813 break; 854 break;
814 855
@@ -845,8 +886,6 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
845#endif 886#endif
846 887
847 case L_('C'): 888 case L_('C'):
848 if (modifier == L_('O'))
849 goto bad_format;
850 if (modifier == L_('E')) 889 if (modifier == L_('E'))
851 { 890 {
852#if HAVE_STRUCT_ERA_ENTRY 891#if HAVE_STRUCT_ERA_ENTRY
@@ -1115,6 +1154,10 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1115 goto underlying_strftime; 1154 goto underlying_strftime;
1116#endif 1155#endif
1117 1156
1157 case L_('q'): /* GNU extension. */
1158 DO_SIGNED_NUMBER (1, false, ((tp->tm_mon * 11) >> 5) + 1);
1159 break;
1160
1118 case L_('R'): 1161 case L_('R'):
1119 subfmt = L_("%H:%M"); 1162 subfmt = L_("%H:%M");
1120 goto subformat; 1163 goto subformat;
@@ -1364,6 +1407,16 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1364 struct tm ltm; 1407 struct tm ltm;
1365 time_t lt; 1408 time_t lt;
1366 1409
1410 /* POSIX.1 requires that local time zone information be used as
1411 though strftime called tzset. */
1412# if HAVE_TZSET
1413 if (!*tzset_called)
1414 {
1415 tzset ();
1416 *tzset_called = true;
1417 }
1418# endif
1419
1367 ltm = *tp; 1420 ltm = *tp;
1368 lt = mktime_z (tz, &ltm); 1421 lt = mktime_z (tz, &ltm);
1369 1422
@@ -1444,22 +1497,3 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1444 1497
1445 return i; 1498 return i;
1446} 1499}
1447
1448/* Write information from TP into S according to the format
1449 string FORMAT, writing no more that MAXSIZE characters
1450 (including the terminating '\0') and returning number of
1451 characters written. If S is NULL, nothing will be written
1452 anywhere, so to determine how many characters would be
1453 written, use NULL for S and (size_t) -1 for MAXSIZE. */
1454size_t
1455my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
1456 const CHAR_T *format,
1457 const struct tm *tp extra_args_spec LOCALE_PARAM_PROTO)
1458{
1459 return strftime_case_ (false, s, STRFTIME_ARG (maxsize)
1460 format, tp extra_args LOCALE_ARG);
1461}
1462
1463#if defined _LIBC && ! FPRINTFTIME
1464libc_hidden_def (my_strftime)
1465#endif