aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/misc/texinfo.tex37
-rw-r--r--lib/gnulib.mk.in5
-rw-r--r--lib/limits.in.h2
-rw-r--r--lib/nstrftime.c1501
-rw-r--r--lib/strftime.c2051
-rw-r--r--lib/strftime.h73
-rw-r--r--lib/time.in.h6
-rw-r--r--lib/time_r.c5
-rw-r--r--lib/warn-on-use.h4
-rw-r--r--lib/xalloc-oversized.h3
-rw-r--r--m4/gnulib-common.m476
-rw-r--r--m4/gnulib-comp.m43
-rw-r--r--m4/nanosleep.m46
-rw-r--r--m4/nstrftime.m45
-rw-r--r--m4/utimens.m415
-rw-r--r--m4/utimensat.m45
16 files changed, 2211 insertions, 1586 deletions
diff --git a/doc/misc/texinfo.tex b/doc/misc/texinfo.tex
index e8c382f5967..93d592193a0 100644
--- a/doc/misc/texinfo.tex
+++ b/doc/misc/texinfo.tex
@@ -3,9 +3,9 @@
3% Load plain if necessary, i.e., if running under initex. 3% Load plain if necessary, i.e., if running under initex.
4\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi 4\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
5% 5%
6\def\texinfoversion{2023-09-19.19} 6\def\texinfoversion{2024-02-10.22}
7% 7%
8% Copyright 1985, 1986, 1988, 1990-2023 Free Software Foundation, Inc. 8% Copyright 1985, 1986, 1988, 1990-2024 Free Software Foundation, Inc.
9% 9%
10% This texinfo.tex file is free software: you can redistribute it and/or 10% This texinfo.tex file is free software: you can redistribute it and/or
11% modify it under the terms of the GNU General Public License as 11% modify it under the terms of the GNU General Public License as
@@ -5238,14 +5238,14 @@ $$%
5238% the current value of \escapechar. 5238% the current value of \escapechar.
5239\def\escapeisbackslash{\escapechar=`\\} 5239\def\escapeisbackslash{\escapechar=`\\}
5240 5240
5241% Use \ in index files by default. texi2dvi didn't support @ as the escape 5241% Uncomment to use \ in index files by default. Old texi2dvi (before 2019)
5242% character (as it checked for "\entry" in the files, and not "@entry"). When 5242% didn't support @ as the escape character (as it checked for "\entry" in
5243% the new version of texi2dvi has had a chance to become more prevalent, then 5243% the files, and not "@entry").
5244% the escape character can change back to @ again. This should be an easy 5244% In the future we can remove this flag and simplify the code for
5245% change to make now because both @ and \ are only used as escape characters in 5245% index files and backslashes, once the support is no longer likely to be
5246% index files, never standing for themselves. 5246% useful.
5247% 5247%
5248\set txiindexescapeisbackslash 5248% \set txiindexescapeisbackslash
5249 5249
5250% Write the entry in \indextext to the index file. 5250% Write the entry in \indextext to the index file.
5251% 5251%
@@ -6137,8 +6137,7 @@ might help (with 'rm \jobname.?? \jobname.??s')%
6137% normally unnmhead0 calls unnumberedzzz: 6137% normally unnmhead0 calls unnumberedzzz:
6138\outer\parseargdef\unnumbered{\unnmhead0{#1}} 6138\outer\parseargdef\unnumbered{\unnmhead0{#1}}
6139\def\unnumberedzzz#1{% 6139\def\unnumberedzzz#1{%
6140 \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 6140 \global\advance\unnumberedno by 1
6141 \global\advance\unnumberedno by 1
6142 % 6141 %
6143 % Since an unnumbered has no number, no prefix for figures. 6142 % Since an unnumbered has no number, no prefix for figures.
6144 \global\let\chaplevelprefix = \empty 6143 \global\let\chaplevelprefix = \empty
@@ -6194,8 +6193,8 @@ might help (with 'rm \jobname.?? \jobname.??s')%
6194% normally calls unnumberedseczzz: 6193% normally calls unnumberedseczzz:
6195\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} 6194\outer\parseargdef\unnumberedsec{\unnmhead1{#1}}
6196\def\unnumberedseczzz#1{% 6195\def\unnumberedseczzz#1{%
6197 \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 6196 \global\advance\unnumberedno by 1
6198 \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% 6197 \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno}%
6199} 6198}
6200 6199
6201% Subsections. 6200% Subsections.
@@ -6218,9 +6217,8 @@ might help (with 'rm \jobname.?? \jobname.??s')%
6218% normally calls unnumberedsubseczzz: 6217% normally calls unnumberedsubseczzz:
6219\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} 6218\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}}
6220\def\unnumberedsubseczzz#1{% 6219\def\unnumberedsubseczzz#1{%
6221 \global\subsubsecno=0 \global\advance\subsecno by 1 6220 \global\advance\unnumberedno by 1
6222 \sectionheading{#1}{subsec}{Ynothing}% 6221 \sectionheading{#1}{subsec}{Ynothing}{\the\unnumberedno}%
6223 {\the\unnumberedno.\the\secno.\the\subsecno}%
6224} 6222}
6225 6223
6226% Subsubsections. 6224% Subsubsections.
@@ -6244,9 +6242,8 @@ might help (with 'rm \jobname.?? \jobname.??s')%
6244% normally unnumberedsubsubseczzz: 6242% normally unnumberedsubsubseczzz:
6245\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} 6243\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}}
6246\def\unnumberedsubsubseczzz#1{% 6244\def\unnumberedsubsubseczzz#1{%
6247 \global\advance\subsubsecno by 1 6245 \global\advance\unnumberedno by 1
6248 \sectionheading{#1}{subsubsec}{Ynothing}% 6246 \sectionheading{#1}{subsubsec}{Ynothing}{\the\unnumberedno}%
6249 {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
6250} 6247}
6251 6248
6252% These macros control what the section commands do, according 6249% These macros control what the section commands do, according
@@ -8205,8 +8202,6 @@ might help (with 'rm \jobname.?? \jobname.??s')%
8205 \let\commondummyword\unmacrodo 8202 \let\commondummyword\unmacrodo
8206 \xdef\macrolist{\macrolist}% 8203 \xdef\macrolist{\macrolist}%
8207 \endgroup 8204 \endgroup
8208 \else
8209 \errmessage{Macro #1 not defined}%
8210 \fi 8205 \fi
8211} 8206}
8212 8207
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index e10aab5fc8d..9970f7810e2 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -47,6 +47,7 @@
47# --avoid=iswdigit \ 47# --avoid=iswdigit \
48# --avoid=iswxdigit \ 48# --avoid=iswxdigit \
49# --avoid=langinfo \ 49# --avoid=langinfo \
50# --avoid=localename \
50# --avoid=lock \ 51# --avoid=lock \
51# --avoid=mbrtowc \ 52# --avoid=mbrtowc \
52# --avoid=mbsinit \ 53# --avoid=mbsinit \
@@ -2745,7 +2746,9 @@ ifeq (,$(OMIT_GNULIB_MODULE_nstrftime))
2745 2746
2746libgnu_a_SOURCES += nstrftime.c 2747libgnu_a_SOURCES += nstrftime.c
2747 2748
2748EXTRA_DIST += strftime.h 2749EXTRA_DIST += strftime.c strftime.h
2750
2751EXTRA_libgnu_a_SOURCES += strftime.c
2749 2752
2750endif 2753endif
2751## end gnulib module nstrftime 2754## end gnulib module nstrftime
diff --git a/lib/limits.in.h b/lib/limits.in.h
index 236fc58e525..c65eb4c1cfe 100644
--- a/lib/limits.in.h
+++ b/lib/limits.in.h
@@ -130,7 +130,7 @@
130# define BOOL_WIDTH 1 130# define BOOL_WIDTH 1
131# define BOOL_MAX 1 131# define BOOL_MAX 1
132# elif ! defined BOOL_MAX 132# elif ! defined BOOL_MAX
133# define BOOL_MAX ((((1U << (BOOL_WIDTH - 1)) - 1) << 1) + 1) 133# define BOOL_MAX 1
134# endif 134# endif
135#endif 135#endif
136 136
diff --git a/lib/nstrftime.c b/lib/nstrftime.c
index 69e4164dc0c..88490064297 100644
--- a/lib/nstrftime.c
+++ b/lib/nstrftime.c
@@ -1,5 +1,6 @@
1/* Copyright (C) 1991-2024 Free Software Foundation, Inc. 1/* Generate time strings.
2 This file is part of the GNU C Library. 2
3 Copyright (C) 2024 Free Software Foundation, Inc.
3 4
4 This file is free software: you can redistribute it and/or modify 5 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as 6 it under the terms of the GNU Lesser General Public License as
@@ -14,1497 +15,5 @@
14 You should have received a copy of the GNU Lesser General Public License 15 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16 17
17#ifdef _LIBC 18#define my_strftime nstrftime
18# define USE_IN_EXTENDED_LOCALE_MODEL 1 19#include "strftime.c"
19# define HAVE_STRUCT_ERA_ENTRY 1
20# define HAVE_TM_GMTOFF 1
21# define HAVE_STRUCT_TM_TM_ZONE 1
22# define HAVE_TZNAME 1
23# include "../locale/localeinfo.h"
24#else
25# include <libc-config.h>
26# if FPRINTFTIME
27# include "fprintftime.h"
28# else
29# include "strftime.h"
30# endif
31# include "time-internal.h"
32#endif
33
34#include <ctype.h>
35#include <errno.h>
36#include <time.h>
37
38#if HAVE_TZNAME && !HAVE_DECL_TZNAME
39extern char *tzname[];
40#endif
41
42/* Do multibyte processing if multibyte encodings are supported, unless
43 multibyte sequences are safe in formats. Multibyte sequences are
44 safe if they cannot contain byte sequences that look like format
45 conversion specifications. The multibyte encodings used by the
46 C library on the various platforms (UTF-8, GB2312, GBK, CP936,
47 GB18030, EUC-TW, BIG5, BIG5-HKSCS, CP950, EUC-JP, EUC-KR, CP949,
48 SHIFT_JIS, CP932, JOHAB) are safe for formats, because the byte '%'
49 cannot occur in a multibyte character except in the first byte.
50
51 The DEC-HANYU encoding used on OSF/1 is not safe for formats, but
52 this encoding has never been seen in real-life use, so we ignore
53 it. */
54#if !(defined __osf__ && 0)
55# define MULTIBYTE_IS_FORMAT_SAFE 1
56#endif
57#define DO_MULTIBYTE (! MULTIBYTE_IS_FORMAT_SAFE)
58
59#if DO_MULTIBYTE
60# include <wchar.h>
61 static const mbstate_t mbstate_zero;
62#endif
63
64#include <limits.h>
65#include <stdckdint.h>
66#include <stddef.h>
67#include <stdlib.h>
68#include <string.h>
69
70#include "attribute.h"
71#include <intprops.h>
72
73#ifdef COMPILE_WIDE
74# include <endian.h>
75# define CHAR_T wchar_t
76# define UCHAR_T unsigned int
77# define L_(Str) L##Str
78# define NLW(Sym) _NL_W##Sym
79
80# define MEMCPY(d, s, n) __wmemcpy (d, s, n)
81# define STRLEN(s) __wcslen (s)
82
83#else
84# define CHAR_T char
85# define UCHAR_T unsigned char
86# define L_(Str) Str
87# define NLW(Sym) Sym
88# define ABALTMON_1 _NL_ABALTMON_1
89
90# define MEMCPY(d, s, n) memcpy (d, s, n)
91# define STRLEN(s) strlen (s)
92
93#endif
94
95/* Shift A right by B bits portably, by dividing A by 2**B and
96 truncating towards minus infinity. A and B should be free of side
97 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
98 INT_BITS is the number of useful bits in an int. GNU code can
99 assume that INT_BITS is at least 32.
100
101 ISO C99 says that A >> B is implementation-defined if A < 0. Some
102 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
103 right in the usual way when A < 0, so SHR falls back on division if
104 ordinary A >> B doesn't seem to be the usual signed shift. */
105#define SHR(a, b) \
106 (-1 >> 1 == -1 \
107 ? (a) >> (b) \
108 : ((a) + ((a) < 0)) / (1 << (b)) - ((a) < 0))
109
110#define TM_YEAR_BASE 1900
111
112#ifndef __isleap
113/* Nonzero if YEAR is a leap year (every 4 years,
114 except every 100th isn't, and every 400th is). */
115# define __isleap(year) \
116 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
117#endif
118
119
120#ifdef _LIBC
121# define mktime_z(tz, tm) mktime (tm)
122# define tzname __tzname
123# define tzset __tzset
124#endif
125
126#ifndef FPRINTFTIME
127# define FPRINTFTIME 0
128#endif
129
130#if FPRINTFTIME
131# define STREAM_OR_CHAR_T FILE
132# define STRFTIME_ARG(x) /* empty */
133#else
134# define STREAM_OR_CHAR_T CHAR_T
135# define STRFTIME_ARG(x) x,
136#endif
137
138#if FPRINTFTIME
139# define memset_byte(P, Len, Byte) \
140 do { size_t _i; for (_i = 0; _i < Len; _i++) fputc (Byte, P); } while (0)
141# define memset_space(P, Len) memset_byte (P, Len, ' ')
142# define memset_zero(P, Len) memset_byte (P, Len, '0')
143#elif defined COMPILE_WIDE
144# define memset_space(P, Len) (wmemset (P, L' ', Len), (P) += (Len))
145# define memset_zero(P, Len) (wmemset (P, L'0', Len), (P) += (Len))
146#else
147# define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
148# define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
149#endif
150
151#if FPRINTFTIME
152# define advance(P, N)
153#else
154# define advance(P, N) ((P) += (N))
155#endif
156
157#define add(n, f) width_add (width, n, f)
158#define width_add(width, n, f) \
159 do \
160 { \
161 size_t _n = (n); \
162 size_t _w = pad == L_('-') || width < 0 ? 0 : width; \
163 size_t _incr = _n < _w ? _w : _n; \
164 if (_incr >= maxsize - i) \
165 { \
166 errno = ERANGE; \
167 return 0; \
168 } \
169 if (p) \
170 { \
171 if (_n < _w) \
172 { \
173 size_t _delta = _w - _n; \
174 if (pad == L_('0') || pad == L_('+')) \
175 memset_zero (p, _delta); \
176 else \
177 memset_space (p, _delta); \
178 } \
179 f; \
180 advance (p, _n); \
181 } \
182 i += _incr; \
183 } while (0)
184
185#define add1(c) width_add1 (width, c)
186#if FPRINTFTIME
187# define width_add1(width, c) width_add (width, 1, fputc (c, p))
188#else
189# define width_add1(width, c) width_add (width, 1, *p = c)
190#endif
191
192#define cpy(n, s) width_cpy (width, n, s)
193#if FPRINTFTIME
194# define width_cpy(width, n, s) \
195 width_add (width, n, \
196 do \
197 { \
198 if (to_lowcase) \
199 fwrite_lowcase (p, (s), _n); \
200 else if (to_uppcase) \
201 fwrite_uppcase (p, (s), _n); \
202 else \
203 { \
204 /* Ignore the value of fwrite. The caller can determine whether \
205 an error occurred by inspecting ferror (P). All known fwrite \
206 implementations set the stream's error indicator when they \
207 fail due to ENOMEM etc., even though C11 and POSIX.1-2008 do \
208 not require this. */ \
209 fwrite (s, _n, 1, p); \
210 } \
211 } \
212 while (0) \
213 )
214#else
215# define width_cpy(width, n, s) \
216 width_add (width, n, \
217 if (to_lowcase) \
218 memcpy_lowcase (p, (s), _n LOCALE_ARG); \
219 else if (to_uppcase) \
220 memcpy_uppcase (p, (s), _n LOCALE_ARG); \
221 else \
222 MEMCPY ((void *) p, (void const *) (s), _n))
223#endif
224
225#ifdef COMPILE_WIDE
226# ifndef USE_IN_EXTENDED_LOCALE_MODEL
227# undef __mbsrtowcs_l
228# define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
229# endif
230#endif
231
232
233#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
234/* We use this code also for the extended locale handling where the
235 function gets as an additional argument the locale which has to be
236 used. To access the values we have to redefine the _NL_CURRENT
237 macro. */
238# define strftime __strftime_l
239# define wcsftime __wcsftime_l
240# undef _NL_CURRENT
241# define _NL_CURRENT(category, item) \
242 (current->values[_NL_ITEM_INDEX (item)].string)
243# define LOCALE_PARAM , locale_t loc
244# define LOCALE_ARG , loc
245# define HELPER_LOCALE_ARG , current
246#else
247# define LOCALE_PARAM
248# define LOCALE_ARG
249# ifdef _LIBC
250# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
251# else
252# define HELPER_LOCALE_ARG
253# endif
254#endif
255
256#ifdef COMPILE_WIDE
257# ifdef USE_IN_EXTENDED_LOCALE_MODEL
258# define TOUPPER(Ch, L) __towupper_l (Ch, L)
259# define TOLOWER(Ch, L) __towlower_l (Ch, L)
260# else
261# define TOUPPER(Ch, L) towupper (Ch)
262# define TOLOWER(Ch, L) towlower (Ch)
263# endif
264#else
265# ifdef USE_IN_EXTENDED_LOCALE_MODEL
266# define TOUPPER(Ch, L) __toupper_l (Ch, L)
267# define TOLOWER(Ch, L) __tolower_l (Ch, L)
268# else
269# define TOUPPER(Ch, L) toupper (Ch)
270# define TOLOWER(Ch, L) tolower (Ch)
271# endif
272#endif
273/* We don't use 'isdigit' here since the locale dependent
274 interpretation is not what we want here. We only need to accept
275 the arabic digits in the ASCII range. One day there is perhaps a
276 more reliable way to accept other sets of digits. */
277#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
278
279/* Avoid false GCC warning "'memset' specified size 18446744073709551615 exceeds
280 maximum object size 9223372036854775807", caused by insufficient data flow
281 analysis and value propagation of the 'width_add' expansion when GCC is not
282 optimizing. Cf. <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443>. */
283#if __GNUC__ >= 7 && !__OPTIMIZE__
284# pragma GCC diagnostic ignored "-Wstringop-overflow"
285#endif
286
287#if FPRINTFTIME
288static void
289fwrite_lowcase (FILE *fp, const CHAR_T *src, size_t len)
290{
291 while (len-- > 0)
292 {
293 fputc (TOLOWER ((UCHAR_T) *src, loc), fp);
294 ++src;
295 }
296}
297
298static void
299fwrite_uppcase (FILE *fp, const CHAR_T *src, size_t len)
300{
301 while (len-- > 0)
302 {
303 fputc (TOUPPER ((UCHAR_T) *src, loc), fp);
304 ++src;
305 }
306}
307#else
308static CHAR_T *memcpy_lowcase (CHAR_T *dest, const CHAR_T *src,
309 size_t len LOCALE_PARAM);
310
311static CHAR_T *
312memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
313{
314 while (len-- > 0)
315 dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
316 return dest;
317}
318
319static CHAR_T *memcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
320 size_t len LOCALE_PARAM);
321
322static CHAR_T *
323memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
324{
325 while (len-- > 0)
326 dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
327 return dest;
328}
329#endif
330
331
332#if ! HAVE_TM_GMTOFF
333/* Yield the difference between *A and *B,
334 measured in seconds, ignoring leap seconds. */
335# define tm_diff ftime_tm_diff
336static int tm_diff (const struct tm *, const struct tm *);
337static int
338tm_diff (const struct tm *a, const struct tm *b)
339{
340 /* Compute intervening leap days correctly even if year is negative.
341 Take care to avoid int overflow in leap day calculations,
342 but it's OK to assume that A and B are close to each other. */
343 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
344 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
345 int a100 = (a4 + (a4 < 0)) / 25 - (a4 < 0);
346 int b100 = (b4 + (b4 < 0)) / 25 - (b4 < 0);
347 int a400 = SHR (a100, 2);
348 int b400 = SHR (b100, 2);
349 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
350 int years = a->tm_year - b->tm_year;
351 int days = (365 * years + intervening_leap_days
352 + (a->tm_yday - b->tm_yday));
353 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
354 + (a->tm_min - b->tm_min))
355 + (a->tm_sec - b->tm_sec));
356}
357#endif /* ! HAVE_TM_GMTOFF */
358
359
360
361/* The number of days from the first day of the first ISO week of this
362 year to the year day YDAY with week day WDAY. ISO weeks start on
363 Monday; the first ISO week has the year's first Thursday. YDAY may
364 be as small as YDAY_MINIMUM. */
365#define ISO_WEEK_START_WDAY 1 /* Monday */
366#define ISO_WEEK1_WDAY 4 /* Thursday */
367#define YDAY_MINIMUM (-366)
368static int iso_week_days (int, int);
369static __inline int
370iso_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 TZ and NS. */
383
384#if FPRINTFTIME
385# undef my_strftime
386# define my_strftime fprintftime
387#endif
388
389#ifdef my_strftime
390# define extra_args , tz, ns
391# define extra_args_spec , timezone_t tz, int ns
392#else
393# if defined COMPILE_WIDE
394# define my_strftime wcsftime
395# define nl_get_alt_digit _nl_get_walt_digit
396# else
397# define my_strftime strftime
398# define nl_get_alt_digit _nl_get_alt_digit
399# endif
400# define extra_args
401# define extra_args_spec
402/* We don't have this information in general. */
403# define tz 1
404# define ns 0
405#endif
406
407static size_t __strftime_internal (STREAM_OR_CHAR_T *, STRFTIME_ARG (size_t)
408 const CHAR_T *, const struct tm *,
409 bool, int, int, bool *
410 extra_args_spec LOCALE_PARAM);
411
412/* Write information from TP into S according to the format
413 string FORMAT, writing no more that MAXSIZE characters
414 (including the terminating '\0') and returning number of
415 characters written. If S is NULL, nothing will be written
416 anywhere, so to determine how many characters would be
417 written, use NULL for S and (size_t) -1 for MAXSIZE. */
418size_t
419my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
420 const CHAR_T *format,
421 const struct tm *tp extra_args_spec LOCALE_PARAM)
422{
423 bool tzset_called = false;
424 return __strftime_internal (s, STRFTIME_ARG (maxsize) format, tp, false,
425 0, -1, &tzset_called extra_args LOCALE_ARG);
426}
427libc_hidden_def (my_strftime)
428
429/* Just like my_strftime, above, but with more parameters.
430 UPCASE indicates that the result should be converted to upper case.
431 YR_SPEC and WIDTH specify the padding and width for the year.
432 *TZSET_CALLED indicates whether tzset has been called here. */
433static size_t
434__strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
435 const CHAR_T *format,
436 const struct tm *tp, bool upcase,
437 int yr_spec, int width, bool *tzset_called
438 extra_args_spec LOCALE_PARAM)
439{
440#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
441 struct __locale_data *const current = loc->__locales[LC_TIME];
442#endif
443#if FPRINTFTIME
444 size_t maxsize = (size_t) -1;
445#endif
446
447 int saved_errno = errno;
448 int hour12 = tp->tm_hour;
449#ifdef _NL_CURRENT
450 /* We cannot make the following values variables since we must delay
451 the evaluation of these values until really needed since some
452 expressions might not be valid in every situation. The 'struct tm'
453 might be generated by a strptime() call that initialized
454 only a few elements. Dereference the pointers only if the format
455 requires this. Then it is ok to fail if the pointers are invalid. */
456# define a_wkday \
457 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
458 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)))
459# define f_wkday \
460 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
461 ? "?" : _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)))
462# define a_month \
463 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
464 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)))
465# define f_month \
466 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
467 ? "?" : _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)))
468# define a_altmonth \
469 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
470 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABALTMON_1) + tp->tm_mon)))
471# define f_altmonth \
472 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
473 ? "?" : _NL_CURRENT (LC_TIME, NLW(ALTMON_1) + tp->tm_mon)))
474# define ampm \
475 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \
476 ? NLW(PM_STR) : NLW(AM_STR)))
477
478# define aw_len STRLEN (a_wkday)
479# define am_len STRLEN (a_month)
480# define aam_len STRLEN (a_altmonth)
481# define ap_len STRLEN (ampm)
482#endif
483#if HAVE_TZNAME
484 char **tzname_vec = tzname;
485#endif
486 const char *zone;
487 size_t i = 0;
488 STREAM_OR_CHAR_T *p = s;
489 const CHAR_T *f;
490#if DO_MULTIBYTE && !defined COMPILE_WIDE
491 const char *format_end = NULL;
492#endif
493
494 zone = NULL;
495#if HAVE_STRUCT_TM_TM_ZONE
496 /* The POSIX test suite assumes that setting
497 the environment variable TZ to a new value before calling strftime()
498 will influence the result (the %Z format) even if the information in
499 TP is computed with a totally different time zone.
500 This is bogus: though POSIX allows bad behavior like this,
501 POSIX does not require it. Do the right thing instead. */
502 zone = (const char *) tp->tm_zone;
503#endif
504#if HAVE_TZNAME
505 if (!tz)
506 {
507 if (! (zone && *zone))
508 zone = "GMT";
509 }
510 else
511 {
512# if !HAVE_STRUCT_TM_TM_ZONE
513 /* Infer the zone name from *TZ instead of from TZNAME. */
514 tzname_vec = tz->tzname_copy;
515# endif
516 }
517 /* The tzset() call might have changed the value. */
518 if (!(zone && *zone) && tp->tm_isdst >= 0)
519 {
520 /* POSIX.1 requires that local time zone information be used as
521 though strftime called tzset. */
522# ifndef my_strftime
523 if (!*tzset_called)
524 {
525 tzset ();
526 *tzset_called = true;
527 }
528# endif
529 zone = tzname_vec[tp->tm_isdst != 0];
530 }
531#endif
532 if (! zone)
533 zone = "";
534
535 if (hour12 > 12)
536 hour12 -= 12;
537 else
538 if (hour12 == 0)
539 hour12 = 12;
540
541 for (f = format; *f != '\0'; width = -1, f++)
542 {
543 int pad = 0; /* Padding for number ('_', '-', '+', '0', or 0). */
544 int modifier; /* Field modifier ('E', 'O', or 0). */
545 int digits = 0; /* Max digits for numeric format. */
546 int number_value; /* Numeric value to be printed. */
547 unsigned int u_number_value; /* (unsigned int) number_value. */
548 bool negative_number; /* The number is negative. */
549 bool always_output_a_sign; /* +/- should always be output. */
550 int tz_colon_mask; /* Bitmask of where ':' should appear. */
551 const CHAR_T *subfmt;
552 CHAR_T *bufp;
553 CHAR_T buf[1
554 + 2 /* for the two colons in a %::z or %:::z time zone */
555 + (sizeof (int) < sizeof (time_t)
556 ? INT_STRLEN_BOUND (time_t)
557 : INT_STRLEN_BOUND (int))];
558 bool to_lowcase = false;
559 bool to_uppcase = upcase;
560 size_t colons;
561 bool change_case = false;
562 int format_char;
563 int subwidth;
564
565#if DO_MULTIBYTE && !defined COMPILE_WIDE
566 switch (*f)
567 {
568 case L_('%'):
569 break;
570
571 case L_('\b'): case L_('\t'): case L_('\n'):
572 case L_('\v'): case L_('\f'): case L_('\r'):
573 case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
574 case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
575 case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
576 case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
577 case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
578 case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
579 case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
580 case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
581 case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
582 case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
583 case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
584 case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
585 case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
586 case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
587 case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
588 case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
589 case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
590 case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
591 case L_('~'):
592 /* The C Standard requires these 98 characters (plus '%') to
593 be in the basic execution character set. None of these
594 characters can start a multibyte sequence, so they need
595 not be analyzed further. */
596 add1 (*f);
597 continue;
598
599 default:
600 /* Copy this multibyte sequence until we reach its end, find
601 an error, or come back to the initial shift state. */
602 {
603 mbstate_t mbstate = mbstate_zero;
604 size_t len = 0;
605 size_t fsize;
606
607 if (! format_end)
608 format_end = f + strlen (f) + 1;
609 fsize = format_end - f;
610
611 do
612 {
613 size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
614
615 if (bytes == 0)
616 break;
617
618 if (bytes == (size_t) -2)
619 {
620 len += strlen (f + len);
621 break;
622 }
623
624 if (bytes == (size_t) -1)
625 {
626 len++;
627 break;
628 }
629
630 len += bytes;
631 }
632 while (! mbsinit (&mbstate));
633
634 cpy (len, f);
635 f += len - 1;
636 continue;
637 }
638 }
639
640#else /* ! DO_MULTIBYTE */
641
642 /* Either multibyte encodings are not supported, they are
643 safe for formats, so any non-'%' byte can be copied through,
644 or this is the wide character version. */
645 if (*f != L_('%'))
646 {
647 add1 (*f);
648 continue;
649 }
650
651#endif /* ! DO_MULTIBYTE */
652
653 char const *percent = f;
654
655 /* Check for flags that can modify a format. */
656 while (1)
657 {
658 switch (*++f)
659 {
660 /* This influences the number formats. */
661 case L_('_'):
662 case L_('-'):
663 case L_('+'):
664 case L_('0'):
665 pad = *f;
666 continue;
667
668 /* This changes textual output. */
669 case L_('^'):
670 to_uppcase = true;
671 continue;
672 case L_('#'):
673 change_case = true;
674 continue;
675
676 default:
677 break;
678 }
679 break;
680 }
681
682 if (ISDIGIT (*f))
683 {
684 width = 0;
685 do
686 {
687 if (ckd_mul (&width, width, 10)
688 || ckd_add (&width, width, *f - L_('0')))
689 width = INT_MAX;
690 ++f;
691 }
692 while (ISDIGIT (*f));
693 }
694
695 /* Check for modifiers. */
696 switch (*f)
697 {
698 case L_('E'):
699 case L_('O'):
700 modifier = *f++;
701 break;
702
703 default:
704 modifier = 0;
705 break;
706 }
707
708 /* Now do the specified format. */
709 format_char = *f;
710 switch (format_char)
711 {
712#define DO_NUMBER(d, v) \
713 do \
714 { \
715 digits = d; \
716 number_value = v; \
717 goto do_number; \
718 } \
719 while (0)
720#define DO_SIGNED_NUMBER(d, negative, v) \
721 DO_MAYBE_SIGNED_NUMBER (d, negative, v, do_signed_number)
722#define DO_YEARISH(d, negative, v) \
723 DO_MAYBE_SIGNED_NUMBER (d, negative, v, do_yearish)
724#define DO_MAYBE_SIGNED_NUMBER(d, negative, v, label) \
725 do \
726 { \
727 digits = d; \
728 negative_number = negative; \
729 u_number_value = v; \
730 goto label; \
731 } \
732 while (0)
733
734 /* The mask is not what you might think.
735 When the ordinal i'th bit is set, insert a colon
736 before the i'th digit of the time zone representation. */
737#define DO_TZ_OFFSET(d, mask, v) \
738 do \
739 { \
740 digits = d; \
741 tz_colon_mask = mask; \
742 u_number_value = v; \
743 goto do_tz_offset; \
744 } \
745 while (0)
746#define DO_NUMBER_SPACEPAD(d, v) \
747 do \
748 { \
749 digits = d; \
750 number_value = v; \
751 goto do_number_spacepad; \
752 } \
753 while (0)
754
755 case L_('%'):
756 if (f - 1 != percent)
757 goto bad_percent;
758 add1 (*f);
759 break;
760
761 case L_('a'):
762 if (modifier != 0)
763 goto bad_format;
764 if (change_case)
765 {
766 to_uppcase = true;
767 to_lowcase = false;
768 }
769#ifdef _NL_CURRENT
770 cpy (aw_len, a_wkday);
771 break;
772#else
773 goto underlying_strftime;
774#endif
775
776 case 'A':
777 if (modifier != 0)
778 goto bad_format;
779 if (change_case)
780 {
781 to_uppcase = true;
782 to_lowcase = false;
783 }
784#ifdef _NL_CURRENT
785 cpy (STRLEN (f_wkday), f_wkday);
786 break;
787#else
788 goto underlying_strftime;
789#endif
790
791 case L_('b'):
792 case L_('h'):
793 if (change_case)
794 {
795 to_uppcase = true;
796 to_lowcase = false;
797 }
798 if (modifier == L_('E'))
799 goto bad_format;
800#ifdef _NL_CURRENT
801 if (modifier == L_('O'))
802 cpy (aam_len, a_altmonth);
803 else
804 cpy (am_len, a_month);
805 break;
806#else
807 goto underlying_strftime;
808#endif
809
810 case L_('B'):
811 if (modifier == L_('E'))
812 goto bad_format;
813 if (change_case)
814 {
815 to_uppcase = true;
816 to_lowcase = false;
817 }
818#ifdef _NL_CURRENT
819 if (modifier == L_('O'))
820 cpy (STRLEN (f_altmonth), f_altmonth);
821 else
822 cpy (STRLEN (f_month), f_month);
823 break;
824#else
825 goto underlying_strftime;
826#endif
827
828 case L_('c'):
829 if (modifier == L_('O'))
830 goto bad_format;
831#ifdef _NL_CURRENT
832 if (! (modifier == L_('E')
833 && (*(subfmt =
834 (const CHAR_T *) _NL_CURRENT (LC_TIME,
835 NLW(ERA_D_T_FMT)))
836 != '\0')))
837 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
838#else
839 goto underlying_strftime;
840#endif
841
842 subformat:
843 subwidth = -1;
844 subformat_width:
845 {
846 size_t len = __strftime_internal (NULL, STRFTIME_ARG ((size_t) -1)
847 subfmt, tp, to_uppcase,
848 pad, subwidth, tzset_called
849 extra_args LOCALE_ARG);
850 add (len, __strftime_internal (p,
851 STRFTIME_ARG (maxsize - i)
852 subfmt, tp, to_uppcase,
853 pad, subwidth, tzset_called
854 extra_args LOCALE_ARG));
855 }
856 break;
857
858#if !(defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
859 underlying_strftime:
860 {
861 /* The relevant information is available only via the
862 underlying strftime implementation, so use that. */
863 char ufmt[5];
864 char *u = ufmt;
865 char ubuf[1024]; /* enough for any single format in practice */
866 size_t len;
867 /* Make sure we're calling the actual underlying strftime.
868 In some cases, config.h contains something like
869 "#define strftime rpl_strftime". */
870# ifdef strftime
871# undef strftime
872 size_t strftime ();
873# endif
874
875 /* The space helps distinguish strftime failure from empty
876 output. */
877 *u++ = ' ';
878 *u++ = '%';
879 if (modifier != 0)
880 *u++ = modifier;
881 *u++ = format_char;
882 *u = '\0';
883 len = strftime (ubuf, sizeof ubuf, ufmt, tp);
884 if (len != 0)
885 cpy (len - 1, ubuf + 1);
886 }
887 break;
888#endif
889
890 case L_('C'):
891 if (modifier == L_('E'))
892 {
893#if HAVE_STRUCT_ERA_ENTRY
894 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
895 if (era)
896 {
897# ifdef COMPILE_WIDE
898 size_t len = __wcslen (era->era_wname);
899 cpy (len, era->era_wname);
900# else
901 size_t len = strlen (era->era_name);
902 cpy (len, era->era_name);
903# endif
904 break;
905 }
906#else
907 goto underlying_strftime;
908#endif
909 }
910
911 {
912 bool negative_year = tp->tm_year < - TM_YEAR_BASE;
913 bool zero_thru_1899 = !negative_year & (tp->tm_year < 0);
914 int century = ((tp->tm_year - 99 * zero_thru_1899) / 100
915 + TM_YEAR_BASE / 100);
916 DO_YEARISH (2, negative_year, century);
917 }
918
919 case L_('x'):
920 if (modifier == L_('O'))
921 goto bad_format;
922#ifdef _NL_CURRENT
923 if (! (modifier == L_('E')
924 && (*(subfmt =
925 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
926 != L_('\0'))))
927 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
928 goto subformat;
929#else
930 goto underlying_strftime;
931#endif
932 case L_('D'):
933 if (modifier != 0)
934 goto bad_format;
935 subfmt = L_("%m/%d/%y");
936 goto subformat;
937
938 case L_('d'):
939 if (modifier == L_('E'))
940 goto bad_format;
941
942 DO_NUMBER (2, tp->tm_mday);
943
944 case L_('e'):
945 if (modifier == L_('E'))
946 goto bad_format;
947
948 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
949
950 /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE)
951 and then jump to one of these labels. */
952
953 do_tz_offset:
954 always_output_a_sign = true;
955 goto do_number_body;
956
957 do_yearish:
958 if (pad == 0)
959 pad = yr_spec;
960 always_output_a_sign
961 = (pad == L_('+')
962 && ((digits == 2 ? 99 : 9999) < u_number_value
963 || digits < width));
964 goto do_maybe_signed_number;
965
966 do_number_spacepad:
967 if (pad == 0)
968 pad = L_('_');
969
970 do_number:
971 /* Format NUMBER_VALUE according to the MODIFIER flag. */
972 negative_number = number_value < 0;
973 u_number_value = number_value;
974
975 do_signed_number:
976 always_output_a_sign = false;
977
978 do_maybe_signed_number:
979 tz_colon_mask = 0;
980
981 do_number_body:
982 /* Format U_NUMBER_VALUE according to the MODIFIER flag.
983 NEGATIVE_NUMBER is nonzero if the original number was
984 negative; in this case it was converted directly to
985 unsigned int (i.e., modulo (UINT_MAX + 1)) without
986 negating it. */
987 if (modifier == L_('O') && !negative_number)
988 {
989#ifdef _NL_CURRENT
990 /* Get the locale specific alternate representation of
991 the number. If none exist NULL is returned. */
992 const CHAR_T *cp = nl_get_alt_digit (u_number_value
993 HELPER_LOCALE_ARG);
994
995 if (cp != NULL)
996 {
997 size_t digitlen = STRLEN (cp);
998 if (digitlen != 0)
999 {
1000 cpy (digitlen, cp);
1001 break;
1002 }
1003 }
1004#else
1005 goto underlying_strftime;
1006#endif
1007 }
1008
1009 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1010
1011 if (negative_number)
1012 u_number_value = - u_number_value;
1013
1014 do
1015 {
1016 if (tz_colon_mask & 1)
1017 *--bufp = ':';
1018 tz_colon_mask >>= 1;
1019 *--bufp = u_number_value % 10 + L_('0');
1020 u_number_value /= 10;
1021 }
1022 while (u_number_value != 0 || tz_colon_mask != 0);
1023
1024 do_number_sign_and_padding:
1025 if (pad == 0)
1026 pad = L_('0');
1027 if (width < 0)
1028 width = digits;
1029
1030 {
1031 CHAR_T sign_char = (negative_number ? L_('-')
1032 : always_output_a_sign ? L_('+')
1033 : 0);
1034 int numlen = buf + sizeof buf / sizeof buf[0] - bufp;
1035 int shortage = width - !!sign_char - numlen;
1036 int padding = pad == L_('-') || shortage <= 0 ? 0 : shortage;
1037
1038 if (sign_char)
1039 {
1040 if (pad == L_('_'))
1041 {
1042 if (p)
1043 memset_space (p, padding);
1044 i += padding;
1045 width -= padding;
1046 }
1047 width_add1 (0, sign_char);
1048 width--;
1049 }
1050
1051 cpy (numlen, bufp);
1052 }
1053 break;
1054
1055 case L_('F'):
1056 if (modifier != 0)
1057 goto bad_format;
1058 if (pad == 0 && width < 0)
1059 {
1060 pad = L_('+');
1061 subwidth = 4;
1062 }
1063 else
1064 {
1065 subwidth = width - 6;
1066 if (subwidth < 0)
1067 subwidth = 0;
1068 }
1069 subfmt = L_("%Y-%m-%d");
1070 goto subformat_width;
1071
1072 case L_('H'):
1073 if (modifier == L_('E'))
1074 goto bad_format;
1075
1076 DO_NUMBER (2, tp->tm_hour);
1077
1078 case L_('I'):
1079 if (modifier == L_('E'))
1080 goto bad_format;
1081
1082 DO_NUMBER (2, hour12);
1083
1084 case L_('k'): /* GNU extension. */
1085 if (modifier == L_('E'))
1086 goto bad_format;
1087
1088 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
1089
1090 case L_('l'): /* GNU extension. */
1091 if (modifier == L_('E'))
1092 goto bad_format;
1093
1094 DO_NUMBER_SPACEPAD (2, hour12);
1095
1096 case L_('j'):
1097 if (modifier == L_('E'))
1098 goto bad_format;
1099
1100 DO_SIGNED_NUMBER (3, tp->tm_yday < -1, tp->tm_yday + 1U);
1101
1102 case L_('M'):
1103 if (modifier == L_('E'))
1104 goto bad_format;
1105
1106 DO_NUMBER (2, tp->tm_min);
1107
1108 case L_('m'):
1109 if (modifier == L_('E'))
1110 goto bad_format;
1111
1112 DO_SIGNED_NUMBER (2, tp->tm_mon < -1, tp->tm_mon + 1U);
1113
1114#ifndef _LIBC
1115 case L_('N'): /* GNU extension. */
1116 if (modifier == L_('E'))
1117 goto bad_format;
1118 {
1119 int n = ns, ns_digits = 9;
1120 if (width <= 0)
1121 width = ns_digits;
1122 int ndigs = ns_digits;
1123 while (width < ndigs || (1 < ndigs && n % 10 == 0))
1124 ndigs--, n /= 10;
1125 for (int j = ndigs; 0 < j; j--)
1126 buf[j - 1] = n % 10 + L_('0'), n /= 10;
1127 if (!pad)
1128 pad = L_('0');
1129 width_cpy (0, ndigs, buf);
1130 width_add (width - ndigs, 0, (void) 0);
1131 }
1132 break;
1133#endif
1134
1135 case L_('n'):
1136 add1 (L_('\n'));
1137 break;
1138
1139 case L_('P'):
1140 to_lowcase = true;
1141#ifndef _NL_CURRENT
1142 format_char = L_('p');
1143#endif
1144 FALLTHROUGH;
1145 case L_('p'):
1146 if (change_case)
1147 {
1148 to_uppcase = false;
1149 to_lowcase = true;
1150 }
1151#ifdef _NL_CURRENT
1152 cpy (ap_len, ampm);
1153 break;
1154#else
1155 goto underlying_strftime;
1156#endif
1157
1158 case L_('q'): /* GNU extension. */
1159 DO_SIGNED_NUMBER (1, false, ((tp->tm_mon * 11) >> 5) + 1);
1160
1161 case L_('R'):
1162 subfmt = L_("%H:%M");
1163 goto subformat;
1164
1165 case L_('r'):
1166#ifdef _NL_CURRENT
1167 if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
1168 NLW(T_FMT_AMPM)))
1169 == L_('\0'))
1170 subfmt = L_("%I:%M:%S %p");
1171 goto subformat;
1172#else
1173 goto underlying_strftime;
1174#endif
1175
1176 case L_('S'):
1177 if (modifier == L_('E'))
1178 goto bad_format;
1179
1180 DO_NUMBER (2, tp->tm_sec);
1181
1182 case L_('s'): /* GNU extension. */
1183 {
1184 struct tm ltm;
1185 time_t t;
1186
1187 ltm = *tp;
1188 ltm.tm_yday = -1;
1189 t = mktime_z (tz, &ltm);
1190 if (ltm.tm_yday < 0)
1191 {
1192 errno = EOVERFLOW;
1193 return 0;
1194 }
1195
1196 /* Generate string value for T using time_t arithmetic;
1197 this works even if sizeof (long) < sizeof (time_t). */
1198
1199 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1200 negative_number = t < 0;
1201
1202 do
1203 {
1204 int d = t % 10;
1205 t /= 10;
1206 *--bufp = (negative_number ? -d : d) + L_('0');
1207 }
1208 while (t != 0);
1209
1210 digits = 1;
1211 always_output_a_sign = false;
1212 goto do_number_sign_and_padding;
1213 }
1214
1215 case L_('X'):
1216 if (modifier == L_('O'))
1217 goto bad_format;
1218#ifdef _NL_CURRENT
1219 if (! (modifier == L_('E')
1220 && (*(subfmt =
1221 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
1222 != L_('\0'))))
1223 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
1224 goto subformat;
1225#else
1226 goto underlying_strftime;
1227#endif
1228 case L_('T'):
1229 subfmt = L_("%H:%M:%S");
1230 goto subformat;
1231
1232 case L_('t'):
1233 add1 (L_('\t'));
1234 break;
1235
1236 case L_('u'):
1237 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1238
1239 case L_('U'):
1240 if (modifier == L_('E'))
1241 goto bad_format;
1242
1243 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1244
1245 case L_('V'):
1246 case L_('g'):
1247 case L_('G'):
1248 if (modifier == L_('E'))
1249 goto bad_format;
1250 {
1251 /* YEAR is a leap year if and only if (tp->tm_year + TM_YEAR_BASE)
1252 is a leap year, except that YEAR and YEAR - 1 both work
1253 correctly even when (tp->tm_year + TM_YEAR_BASE) would
1254 overflow. */
1255 int year = (tp->tm_year
1256 + (tp->tm_year < 0
1257 ? TM_YEAR_BASE % 400
1258 : TM_YEAR_BASE % 400 - 400));
1259 int year_adjust = 0;
1260 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1261
1262 if (days < 0)
1263 {
1264 /* This ISO week belongs to the previous year. */
1265 year_adjust = -1;
1266 days = iso_week_days (tp->tm_yday + (365 + __isleap (year - 1)),
1267 tp->tm_wday);
1268 }
1269 else
1270 {
1271 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1272 tp->tm_wday);
1273 if (0 <= d)
1274 {
1275 /* This ISO week belongs to the next year. */
1276 year_adjust = 1;
1277 days = d;
1278 }
1279 }
1280
1281 switch (*f)
1282 {
1283 case L_('g'):
1284 {
1285 int yy = (tp->tm_year % 100 + year_adjust) % 100;
1286 DO_YEARISH (2, false,
1287 (0 <= yy
1288 ? yy
1289 : tp->tm_year < -TM_YEAR_BASE - year_adjust
1290 ? -yy
1291 : yy + 100));
1292 }
1293
1294 case L_('G'):
1295 DO_YEARISH (4, tp->tm_year < -TM_YEAR_BASE - year_adjust,
1296 (tp->tm_year + (unsigned int) TM_YEAR_BASE
1297 + year_adjust));
1298
1299 default:
1300 DO_NUMBER (2, days / 7 + 1);
1301 }
1302 }
1303
1304 case L_('W'):
1305 if (modifier == L_('E'))
1306 goto bad_format;
1307
1308 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1309
1310 case L_('w'):
1311 if (modifier == L_('E'))
1312 goto bad_format;
1313
1314 DO_NUMBER (1, tp->tm_wday);
1315
1316 case L_('Y'):
1317 if (modifier == L_('E'))
1318 {
1319#if HAVE_STRUCT_ERA_ENTRY
1320 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1321 if (era)
1322 {
1323# ifdef COMPILE_WIDE
1324 subfmt = era->era_wformat;
1325# else
1326 subfmt = era->era_format;
1327# endif
1328 if (pad == 0)
1329 pad = yr_spec;
1330 goto subformat;
1331 }
1332#else
1333 goto underlying_strftime;
1334#endif
1335 }
1336 if (modifier == L_('O'))
1337 goto bad_format;
1338
1339 DO_YEARISH (4, tp->tm_year < -TM_YEAR_BASE,
1340 tp->tm_year + (unsigned int) TM_YEAR_BASE);
1341
1342 case L_('y'):
1343 if (modifier == L_('E'))
1344 {
1345#if HAVE_STRUCT_ERA_ENTRY
1346 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1347 if (era)
1348 {
1349 int delta = tp->tm_year - era->start_date[0];
1350 if (pad == 0)
1351 pad = yr_spec;
1352 DO_NUMBER (2, (era->offset
1353 + delta * era->absolute_direction));
1354 }
1355#else
1356 goto underlying_strftime;
1357#endif
1358 }
1359
1360 {
1361 int yy = tp->tm_year % 100;
1362 if (yy < 0)
1363 yy = tp->tm_year < - TM_YEAR_BASE ? -yy : yy + 100;
1364 DO_YEARISH (2, false, yy);
1365 }
1366
1367 case L_('Z'):
1368 if (change_case)
1369 {
1370 to_uppcase = false;
1371 to_lowcase = true;
1372 }
1373
1374#ifdef COMPILE_WIDE
1375 {
1376 /* The zone string is always given in multibyte form. We have
1377 to convert it to wide character. */
1378 size_t w = pad == L_('-') || width < 0 ? 0 : width;
1379 char const *z = zone;
1380 mbstate_t st = {0};
1381 size_t len = __mbsrtowcs_l (p, &z, maxsize - i, &st, loc);
1382 if (len == (size_t) -1)
1383 return 0;
1384 size_t incr = len < w ? w : len;
1385 if (incr >= maxsize - i)
1386 {
1387 errno = ERANGE;
1388 return 0;
1389 }
1390 if (p)
1391 {
1392 if (len < w)
1393 {
1394 size_t delta = w - len;
1395 __wmemmove (p + delta, p, len);
1396 wchar_t wc = pad == L_('0') || pad == L_('+') ? L'0' : L' ';
1397 wmemset (p, wc, delta);
1398 }
1399 p += incr;
1400 }
1401 i += incr;
1402 }
1403#else
1404 cpy (strlen (zone), zone);
1405#endif
1406 break;
1407
1408 case L_(':'):
1409 /* :, ::, and ::: are valid only just before 'z'.
1410 :::: etc. are rejected later. */
1411 for (colons = 1; f[colons] == L_(':'); colons++)
1412 continue;
1413 if (f[colons] != L_('z'))
1414 goto bad_format;
1415 f += colons;
1416 goto do_z_conversion;
1417
1418 case L_('z'):
1419 colons = 0;
1420
1421 do_z_conversion:
1422 if (tp->tm_isdst < 0)
1423 break;
1424
1425 {
1426 int diff;
1427 int hour_diff;
1428 int min_diff;
1429 int sec_diff;
1430#if HAVE_TM_GMTOFF
1431 diff = tp->tm_gmtoff;
1432#else
1433 if (!tz)
1434 diff = 0;
1435 else
1436 {
1437 struct tm gtm;
1438 struct tm ltm;
1439 time_t lt;
1440
1441 /* POSIX.1 requires that local time zone information be used as
1442 though strftime called tzset. */
1443# ifndef my_strftime
1444 if (!*tzset_called)
1445 {
1446 tzset ();
1447 *tzset_called = true;
1448 }
1449# endif
1450
1451 ltm = *tp;
1452 ltm.tm_wday = -1;
1453 lt = mktime_z (tz, &ltm);
1454 if (ltm.tm_wday < 0 || ! localtime_rz (0, &lt, &gtm))
1455 break;
1456 diff = tm_diff (&ltm, &gtm);
1457 }
1458#endif
1459
1460 negative_number = diff < 0 || (diff == 0 && *zone == '-');
1461 hour_diff = diff / 60 / 60;
1462 min_diff = diff / 60 % 60;
1463 sec_diff = diff % 60;
1464
1465 switch (colons)
1466 {
1467 case 0: /* +hhmm */
1468 DO_TZ_OFFSET (5, 0, hour_diff * 100 + min_diff);
1469
1470 case 1: tz_hh_mm: /* +hh:mm */
1471 DO_TZ_OFFSET (6, 04, hour_diff * 100 + min_diff);
1472
1473 case 2: tz_hh_mm_ss: /* +hh:mm:ss */
1474 DO_TZ_OFFSET (9, 024,
1475 hour_diff * 10000 + min_diff * 100 + sec_diff);
1476
1477 case 3: /* +hh if possible, else +hh:mm, else +hh:mm:ss */
1478 if (sec_diff != 0)
1479 goto tz_hh_mm_ss;
1480 if (min_diff != 0)
1481 goto tz_hh_mm;
1482 DO_TZ_OFFSET (3, 0, hour_diff);
1483
1484 default:
1485 goto bad_format;
1486 }
1487 }
1488
1489 case L_('\0'): /* GNU extension: % at end of format. */
1490 bad_percent:
1491 --f;
1492 FALLTHROUGH;
1493 default:
1494 /* Unknown format; output the format, including the '%',
1495 since this is most likely the right thing to do if a
1496 multibyte string has been misparsed. */
1497 bad_format:
1498 cpy (f - percent + 1, percent);
1499 break;
1500 }
1501 }
1502
1503#if ! FPRINTFTIME
1504 if (p && maxsize != 0)
1505 *p = L_('\0');
1506#endif
1507
1508 errno = saved_errno;
1509 return i;
1510}
diff --git a/lib/strftime.c b/lib/strftime.c
new file mode 100644
index 00000000000..c7256c3d354
--- /dev/null
+++ b/lib/strftime.c
@@ -0,0 +1,2051 @@
1/* Copyright (C) 1991-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation, either version 3 of the
7 License, or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17#ifndef FPRINTFTIME
18# define FPRINTFTIME 0
19#endif
20
21#ifndef USE_C_LOCALE
22# define USE_C_LOCALE 0
23#endif
24
25#ifdef _LIBC
26# define USE_IN_EXTENDED_LOCALE_MODEL 1
27# define HAVE_STRUCT_ERA_ENTRY 1
28# define HAVE_TM_GMTOFF 1
29# define HAVE_STRUCT_TM_TM_ZONE 1
30# define HAVE_TZNAME 1
31# include "../locale/localeinfo.h"
32#else
33# include <libc-config.h>
34# if FPRINTFTIME
35# include "fprintftime.h"
36# else
37# include "strftime.h"
38# endif
39# include "time-internal.h"
40#endif
41
42/* Whether to require GNU behavior for AM and PM indicators, even on
43 other platforms. This matters only in non-C locales.
44 The default is to require it; you can override this via
45 AC_DEFINE([REQUIRE_GNUISH_STRFTIME_AM_PM], 1) and if you do that
46 you may be able to omit Gnulib's localename module and its dependencies. */
47#ifndef REQUIRE_GNUISH_STRFTIME_AM_PM
48# define REQUIRE_GNUISH_STRFTIME_AM_PM true
49#endif
50#if USE_C_LOCALE
51# undef REQUIRE_GNUISH_STRFTIME_AM_PM
52# define REQUIRE_GNUISH_STRFTIME_AM_PM false
53#endif
54
55#if USE_C_LOCALE
56# include "c-ctype.h"
57#else
58# include <ctype.h>
59#endif
60#include <errno.h>
61#include <time.h>
62
63#if HAVE_TZNAME && !HAVE_DECL_TZNAME
64extern char *tzname[];
65#endif
66
67/* Do multibyte processing if multibyte encodings are supported, unless
68 multibyte sequences are safe in formats. Multibyte sequences are
69 safe if they cannot contain byte sequences that look like format
70 conversion specifications. The multibyte encodings used by the
71 C library on the various platforms (UTF-8, GB2312, GBK, CP936,
72 GB18030, EUC-TW, BIG5, BIG5-HKSCS, CP950, EUC-JP, EUC-KR, CP949,
73 SHIFT_JIS, CP932, JOHAB) are safe for formats, because the byte '%'
74 cannot occur in a multibyte character except in the first byte.
75
76 The DEC-HANYU encoding used on OSF/1 is not safe for formats, but
77 this encoding has never been seen in real-life use, so we ignore
78 it. */
79#if !(defined __osf__ && 0)
80# define MULTIBYTE_IS_FORMAT_SAFE 1
81#endif
82#define DO_MULTIBYTE (! MULTIBYTE_IS_FORMAT_SAFE)
83
84#if DO_MULTIBYTE
85# include <wchar.h>
86 static const mbstate_t mbstate_zero;
87#endif
88
89#include <limits.h>
90#include <stdckdint.h>
91#include <stddef.h>
92#include <stdlib.h>
93#include <string.h>
94
95#if USE_C_LOCALE && HAVE_STRFTIME_L
96# include <locale.h>
97#endif
98
99#if (defined __NetBSD__ || defined __sun) && REQUIRE_GNUISH_STRFTIME_AM_PM
100# include <locale.h>
101# include "localename.h"
102#endif
103
104#include "attribute.h"
105#include <intprops.h>
106
107#ifdef COMPILE_WIDE
108# include <endian.h>
109# define CHAR_T wchar_t
110# define UCHAR_T unsigned int
111# define L_(Str) L##Str
112# define NLW(Sym) _NL_W##Sym
113
114# define MEMCPY(d, s, n) __wmemcpy (d, s, n)
115# define STRLEN(s) __wcslen (s)
116
117#else
118# define CHAR_T char
119# define UCHAR_T unsigned char
120# define L_(Str) Str
121# define NLW(Sym) Sym
122# define ABALTMON_1 _NL_ABALTMON_1
123
124# define MEMCPY(d, s, n) memcpy (d, s, n)
125# define STRLEN(s) strlen (s)
126
127#endif
128
129/* Shift A right by B bits portably, by dividing A by 2**B and
130 truncating towards minus infinity. A and B should be free of side
131 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
132 INT_BITS is the number of useful bits in an int. GNU code can
133 assume that INT_BITS is at least 32.
134
135 ISO C99 says that A >> B is implementation-defined if A < 0. Some
136 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
137 right in the usual way when A < 0, so SHR falls back on division if
138 ordinary A >> B doesn't seem to be the usual signed shift. */
139#define SHR(a, b) \
140 (-1 >> 1 == -1 \
141 ? (a) >> (b) \
142 : ((a) + ((a) < 0)) / (1 << (b)) - ((a) < 0))
143
144#define TM_YEAR_BASE 1900
145
146#ifndef __isleap
147/* Nonzero if YEAR is a leap year (every 4 years,
148 except every 100th isn't, and every 400th is). */
149# define __isleap(year) \
150 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
151#endif
152
153
154#ifdef _LIBC
155# define mktime_z(tz, tm) mktime (tm)
156# define tzname __tzname
157# define tzset __tzset
158
159# define time_t __time64_t
160# define __gmtime_r(t, tp) __gmtime64_r (t, tp)
161# define mktime(tp) __mktime64 (tp)
162#endif
163
164#if FPRINTFTIME
165# define STREAM_OR_CHAR_T FILE
166# define STRFTIME_ARG(x) /* empty */
167#else
168# define STREAM_OR_CHAR_T CHAR_T
169# define STRFTIME_ARG(x) x,
170#endif
171
172#if FPRINTFTIME
173# define memset_byte(P, Len, Byte) \
174 do { size_t _i; for (_i = 0; _i < Len; _i++) fputc (Byte, P); } while (0)
175# define memset_space(P, Len) memset_byte (P, Len, ' ')
176# define memset_zero(P, Len) memset_byte (P, Len, '0')
177#elif defined COMPILE_WIDE
178# define memset_space(P, Len) (wmemset (P, L' ', Len), (P) += (Len))
179# define memset_zero(P, Len) (wmemset (P, L'0', Len), (P) += (Len))
180#else
181# define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
182# define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
183#endif
184
185#if FPRINTFTIME
186# define advance(P, N)
187#else
188# define advance(P, N) ((P) += (N))
189#endif
190
191#define add(n, f) width_add (width, n, f)
192#define width_add(width, n, f) \
193 do \
194 { \
195 size_t _n = (n); \
196 size_t _w = pad == L_('-') || width < 0 ? 0 : width; \
197 size_t _incr = _n < _w ? _w : _n; \
198 if (_incr >= maxsize - i) \
199 { \
200 errno = ERANGE; \
201 return 0; \
202 } \
203 if (p) \
204 { \
205 if (_n < _w) \
206 { \
207 size_t _delta = _w - _n; \
208 if (pad == L_('0') || pad == L_('+')) \
209 memset_zero (p, _delta); \
210 else \
211 memset_space (p, _delta); \
212 } \
213 f; \
214 advance (p, _n); \
215 } \
216 i += _incr; \
217 } while (0)
218
219#define add1(c) width_add1 (width, c)
220#if FPRINTFTIME
221# define width_add1(width, c) width_add (width, 1, fputc (c, p))
222#else
223# define width_add1(width, c) width_add (width, 1, *p = c)
224#endif
225
226#define cpy(n, s) width_cpy (width, n, s)
227#if FPRINTFTIME
228# define width_cpy(width, n, s) \
229 width_add (width, n, \
230 do \
231 { \
232 if (to_lowcase) \
233 fwrite_lowcase (p, (s), _n); \
234 else if (to_uppcase) \
235 fwrite_uppcase (p, (s), _n); \
236 else \
237 { \
238 /* Ignore the value of fwrite. The caller can determine whether \
239 an error occurred by inspecting ferror (P). All known fwrite \
240 implementations set the stream's error indicator when they \
241 fail due to ENOMEM etc., even though C11 and POSIX.1-2008 do \
242 not require this. */ \
243 fwrite (s, _n, 1, p); \
244 } \
245 } \
246 while (0) \
247 )
248#else
249# define width_cpy(width, n, s) \
250 width_add (width, n, \
251 if (to_lowcase) \
252 memcpy_lowcase (p, (s), _n LOCALE_ARG); \
253 else if (to_uppcase) \
254 memcpy_uppcase (p, (s), _n LOCALE_ARG); \
255 else \
256 MEMCPY ((void *) p, (void const *) (s), _n))
257#endif
258
259#ifdef COMPILE_WIDE
260# ifndef USE_IN_EXTENDED_LOCALE_MODEL
261# undef __mbsrtowcs_l
262# define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
263# endif
264#endif
265
266
267#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
268/* We use this code also for the extended locale handling where the
269 function gets as an additional argument the locale which has to be
270 used. To access the values we have to redefine the _NL_CURRENT
271 macro. */
272# define strftime __strftime_l
273# define wcsftime __wcsftime_l
274# undef _NL_CURRENT
275# define _NL_CURRENT(category, item) \
276 (current->values[_NL_ITEM_INDEX (item)].string)
277# define LOCALE_PARAM , locale_t loc
278# define LOCALE_ARG , loc
279# define HELPER_LOCALE_ARG , current
280#else
281# define LOCALE_PARAM
282# define LOCALE_ARG
283# ifdef _LIBC
284# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
285# else
286# define HELPER_LOCALE_ARG
287# endif
288#endif
289
290#ifdef COMPILE_WIDE
291# ifdef USE_IN_EXTENDED_LOCALE_MODEL
292# define TOUPPER(Ch, L) __towupper_l (Ch, L)
293# define TOLOWER(Ch, L) __towlower_l (Ch, L)
294# else
295# define TOUPPER(Ch, L) towupper (Ch)
296# define TOLOWER(Ch, L) towlower (Ch)
297# endif
298#else
299# ifdef USE_IN_EXTENDED_LOCALE_MODEL
300# define TOUPPER(Ch, L) __toupper_l (Ch, L)
301# define TOLOWER(Ch, L) __tolower_l (Ch, L)
302# else
303# if USE_C_LOCALE
304# define TOUPPER(Ch, L) c_toupper (Ch)
305# define TOLOWER(Ch, L) c_tolower (Ch)
306# else
307# define TOUPPER(Ch, L) toupper (Ch)
308# define TOLOWER(Ch, L) tolower (Ch)
309# endif
310# endif
311#endif
312/* We don't use 'isdigit' here since the locale dependent
313 interpretation is not what we want here. We only need to accept
314 the arabic digits in the ASCII range. One day there is perhaps a
315 more reliable way to accept other sets of digits. */
316#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
317
318/* Avoid false GCC warning "'memset' specified size 18446744073709551615 exceeds
319 maximum object size 9223372036854775807", caused by insufficient data flow
320 analysis and value propagation of the 'width_add' expansion when GCC is not
321 optimizing. Cf. <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443>. */
322#if __GNUC__ >= 7 && !__OPTIMIZE__
323# pragma GCC diagnostic ignored "-Wstringop-overflow"
324#endif
325
326#if FPRINTFTIME
327static void
328fwrite_lowcase (FILE *fp, const CHAR_T *src, size_t len)
329{
330 while (len-- > 0)
331 {
332 fputc (TOLOWER ((UCHAR_T) *src, loc), fp);
333 ++src;
334 }
335}
336
337static void
338fwrite_uppcase (FILE *fp, const CHAR_T *src, size_t len)
339{
340 while (len-- > 0)
341 {
342 fputc (TOUPPER ((UCHAR_T) *src, loc), fp);
343 ++src;
344 }
345}
346#else
347static CHAR_T *memcpy_lowcase (CHAR_T *dest, const CHAR_T *src,
348 size_t len LOCALE_PARAM);
349
350static CHAR_T *
351memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
352{
353 while (len-- > 0)
354 dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
355 return dest;
356}
357
358static CHAR_T *memcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
359 size_t len LOCALE_PARAM);
360
361static CHAR_T *
362memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
363{
364 while (len-- > 0)
365 dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
366 return dest;
367}
368#endif
369
370
371#if USE_C_LOCALE && HAVE_STRFTIME_L
372
373/* Cache for the C locale object.
374 Marked volatile so that different threads see the same value
375 (avoids locking). */
376static volatile locale_t c_locale_cache;
377
378/* Return the C locale object, or (locale_t) 0 with errno set
379 if it cannot be created. */
380static locale_t
381c_locale (void)
382{
383 if (!c_locale_cache)
384 c_locale_cache = newlocale (LC_ALL_MASK, "C", (locale_t) 0);
385 return c_locale_cache;
386}
387
388#endif
389
390
391#if (defined __NetBSD__ || defined __sun) && REQUIRE_GNUISH_STRFTIME_AM_PM
392
393/* Return true if an AM/PM indicator should be removed. */
394static bool
395should_remove_ampm (void)
396{
397 /* According to glibc's 'am_pm' attribute in the locale database, an AM/PM
398 indicator should be absent in the locales for the following languages:
399 ab an ast az be ber bg br bs ce cs csb cv da de dsb eo et eu fa fi fo fr
400 fur fy ga gl gv hr hsb ht hu hy it ka kk kl ku kv kw ky lb lg li lij ln
401 lt lv mg mhr mi mk mn ms mt nb nds nhn nl nn nr nso oc os pap pl pt ro
402 ru rw sah sc se sgs sk sl sm sr ss st su sv szl tg tk tn ts tt ug uk unm
403 uz ve wae wo xh zu */
404 const char *loc = gl_locale_name (LC_TIME, "LC_TIME");
405 bool remove_ampm = false;
406 switch (loc[0])
407 {
408 case 'a':
409 switch (loc[1])
410 {
411 case 'b': case 'n': case 'z':
412 if (loc[2] == '\0' || loc[2] == '_')
413 remove_ampm = true;
414 break;
415 case 's':
416 if (loc[2] == 't' && (loc[3] == '\0' || loc[3] == '_'))
417 remove_ampm = true;
418 break;
419 default:
420 break;
421 }
422 break;
423 case 'b':
424 switch (loc[1])
425 {
426 case 'e':
427 if (loc[2] == '\0' || loc[2] == '_'
428 || (loc[2] == 'r' && (loc[3] == '\0' || loc[3] == '_')))
429 remove_ampm = true;
430 break;
431 case 'g': case 'r': case 's':
432 if (loc[2] == '\0' || loc[2] == '_')
433 remove_ampm = true;
434 break;
435 default:
436 break;
437 }
438 break;
439 case 'c':
440 switch (loc[1])
441 {
442 case 'e': case 'v':
443 if (loc[2] == '\0' || loc[2] == '_')
444 remove_ampm = true;
445 break;
446 case 's':
447 if (loc[2] == '\0' || loc[2] == '_'
448 || (loc[2] == 'b' && (loc[3] == '\0' || loc[3] == '_')))
449 remove_ampm = true;
450 break;
451 default:
452 break;
453 }
454 break;
455 case 'd':
456 switch (loc[1])
457 {
458 case 'a': case 'e':
459 if (loc[2] == '\0' || loc[2] == '_')
460 remove_ampm = true;
461 break;
462 case 's':
463 if (loc[2] == 'b' && (loc[3] == '\0' || loc[3] == '_'))
464 remove_ampm = true;
465 break;
466 default:
467 break;
468 }
469 break;
470 case 'e':
471 switch (loc[1])
472 {
473 case 'o': case 't': case 'u':
474 if (loc[2] == '\0' || loc[2] == '_')
475 remove_ampm = true;
476 break;
477 default:
478 break;
479 }
480 break;
481 case 'f':
482 switch (loc[1])
483 {
484 case 'a': case 'i': case 'o': case 'r': case 'y':
485 if (loc[2] == '\0' || loc[2] == '_')
486 remove_ampm = true;
487 break;
488 case 'u':
489 if (loc[2] == 'r' && (loc[3] == '\0' || loc[3] == '_'))
490 remove_ampm = true;
491 break;
492 default:
493 break;
494 }
495 break;
496 case 'g':
497 switch (loc[1])
498 {
499 case 'a': case 'l': case 'v':
500 if (loc[2] == '\0' || loc[2] == '_')
501 remove_ampm = true;
502 break;
503 default:
504 break;
505 }
506 break;
507 case 'h':
508 switch (loc[1])
509 {
510 case 'r': case 't': case 'u': case 'y':
511 if (loc[2] == '\0' || loc[2] == '_')
512 remove_ampm = true;
513 break;
514 case 's':
515 if (loc[2] == 'b' && (loc[3] == '\0' || loc[3] == '_'))
516 remove_ampm = true;
517 break;
518 default:
519 break;
520 }
521 break;
522 case 'i':
523 switch (loc[1])
524 {
525 case 't':
526 if (loc[2] == '\0' || loc[2] == '_')
527 remove_ampm = true;
528 break;
529 default:
530 break;
531 }
532 break;
533 case 'k':
534 switch (loc[1])
535 {
536 case 'a': case 'k': case 'l': case 'u': case 'v': case 'w': case 'y':
537 if (loc[2] == '\0' || loc[2] == '_')
538 remove_ampm = true;
539 break;
540 default:
541 break;
542 }
543 break;
544 case 'l':
545 switch (loc[1])
546 {
547 case 'b': case 'g': case 'n': case 't': case 'v':
548 if (loc[2] == '\0' || loc[2] == '_')
549 remove_ampm = true;
550 break;
551 case 'i':
552 if (loc[2] == 'j' && (loc[3] == '\0' || loc[3] == '_'))
553 remove_ampm = true;
554 break;
555 default:
556 break;
557 }
558 break;
559 case 'm':
560 switch (loc[1])
561 {
562 case 'g': case 'i': case 'k': case 'n': case 's': case 't':
563 if (loc[2] == '\0' || loc[2] == '_')
564 remove_ampm = true;
565 break;
566 case 'h':
567 if (loc[2] == 'r' && (loc[3] == '\0' || loc[3] == '_'))
568 remove_ampm = true;
569 break;
570 default:
571 break;
572 }
573 break;
574 case 'n':
575 switch (loc[1])
576 {
577 case 'b': case 'l': case 'n': case 'r':
578 if (loc[2] == '\0' || loc[2] == '_')
579 remove_ampm = true;
580 break;
581 case 'd':
582 if (loc[2] == 's' && (loc[3] == '\0' || loc[3] == '_'))
583 remove_ampm = true;
584 break;
585 case 'h':
586 if (loc[2] == 'n' && (loc[3] == '\0' || loc[3] == '_'))
587 remove_ampm = true;
588 break;
589 case 's':
590 if (loc[2] == 'o' && (loc[3] == '\0' || loc[3] == '_'))
591 remove_ampm = true;
592 break;
593 default:
594 break;
595 }
596 break;
597 case 'o':
598 switch (loc[1])
599 {
600 case 'c': case 's':
601 if (loc[2] == '\0' || loc[2] == '_')
602 remove_ampm = true;
603 break;
604 default:
605 break;
606 }
607 break;
608 case 'p':
609 switch (loc[1])
610 {
611 case 'l': case 't':
612 if (loc[2] == '\0' || loc[2] == '_')
613 remove_ampm = true;
614 break;
615 case 'a':
616 if (loc[2] == 'p' && (loc[3] == '\0' || loc[3] == '_'))
617 remove_ampm = true;
618 break;
619 default:
620 break;
621 }
622 break;
623 case 'r':
624 switch (loc[1])
625 {
626 case 'o': case 'u': case 'w':
627 if (loc[2] == '\0' || loc[2] == '_')
628 remove_ampm = true;
629 break;
630 default:
631 break;
632 }
633 break;
634 case 's':
635 switch (loc[1])
636 {
637 case 'c': case 'e': case 'k': case 'l': case 'm': case 'r': case 's':
638 case 't': case 'u': case 'v':
639 if (loc[2] == '\0' || loc[2] == '_')
640 remove_ampm = true;
641 break;
642 case 'a':
643 if (loc[2] == 'h' && (loc[3] == '\0' || loc[3] == '_'))
644 remove_ampm = true;
645 break;
646 case 'g':
647 if (loc[2] == 's' && (loc[3] == '\0' || loc[3] == '_'))
648 remove_ampm = true;
649 break;
650 case 'z':
651 if (loc[2] == 'l' && (loc[3] == '\0' || loc[3] == '_'))
652 remove_ampm = true;
653 break;
654 default:
655 break;
656 }
657 break;
658 case 't':
659 switch (loc[1])
660 {
661 case 'g': case 'k': case 'n': case 's': case 't':
662 if (loc[2] == '\0' || loc[2] == '_')
663 remove_ampm = true;
664 break;
665 default:
666 break;
667 }
668 break;
669 case 'u':
670 switch (loc[1])
671 {
672 case 'g': case 'k': case 'z':
673 if (loc[2] == '\0' || loc[2] == '_')
674 remove_ampm = true;
675 break;
676 case 'n':
677 if (loc[2] == 'm'&& (loc[3] == '\0' || loc[3] == '_'))
678 remove_ampm = true;
679 break;
680 default:
681 break;
682 }
683 break;
684 case 'v':
685 switch (loc[1])
686 {
687 case 'e':
688 if (loc[2] == '\0' || loc[2] == '_')
689 remove_ampm = true;
690 break;
691 default:
692 break;
693 }
694 break;
695 case 'w':
696 switch (loc[1])
697 {
698 case 'a':
699 if (loc[2] == 'e' && (loc[3] == '\0' || loc[3] == '_'))
700 remove_ampm = true;
701 break;
702 case 'o':
703 if (loc[2] == '\0' || loc[2] == '_')
704 remove_ampm = true;
705 break;
706 default:
707 break;
708 }
709 break;
710 case 'x':
711 switch (loc[1])
712 {
713 case 'h':
714 if (loc[2] == '\0' || loc[2] == '_')
715 remove_ampm = true;
716 break;
717 default:
718 break;
719 }
720 break;
721 case 'z':
722 switch (loc[1])
723 {
724 case 'u':
725 if (loc[2] == '\0' || loc[2] == '_')
726 remove_ampm = true;
727 break;
728 default:
729 break;
730 }
731 break;
732 default:
733 break;
734 }
735 return remove_ampm;
736}
737
738#endif
739
740
741#if ! HAVE_TM_GMTOFF
742/* Yield the difference between *A and *B,
743 measured in seconds, ignoring leap seconds. */
744# define tm_diff ftime_tm_diff
745static int tm_diff (const struct tm *, const struct tm *);
746static int
747tm_diff (const struct tm *a, const struct tm *b)
748{
749 /* Compute intervening leap days correctly even if year is negative.
750 Take care to avoid int overflow in leap day calculations,
751 but it's OK to assume that A and B are close to each other. */
752 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
753 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
754 int a100 = (a4 + (a4 < 0)) / 25 - (a4 < 0);
755 int b100 = (b4 + (b4 < 0)) / 25 - (b4 < 0);
756 int a400 = SHR (a100, 2);
757 int b400 = SHR (b100, 2);
758 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
759 int years = a->tm_year - b->tm_year;
760 int days = (365 * years + intervening_leap_days
761 + (a->tm_yday - b->tm_yday));
762 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
763 + (a->tm_min - b->tm_min))
764 + (a->tm_sec - b->tm_sec));
765}
766#endif /* ! HAVE_TM_GMTOFF */
767
768
769
770/* The number of days from the first day of the first ISO week of this
771 year to the year day YDAY with week day WDAY. ISO weeks start on
772 Monday; the first ISO week has the year's first Thursday. YDAY may
773 be as small as YDAY_MINIMUM. */
774#define ISO_WEEK_START_WDAY 1 /* Monday */
775#define ISO_WEEK1_WDAY 4 /* Thursday */
776#define YDAY_MINIMUM (-366)
777static int iso_week_days (int, int);
778static __inline int
779iso_week_days (int yday, int wday)
780{
781 /* Add enough to the first operand of % to make it nonnegative. */
782 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
783 return (yday
784 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
785 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
786}
787
788
789#if !defined _NL_CURRENT && (USE_C_LOCALE && !HAVE_STRFTIME_L)
790static CHAR_T const c_weekday_names[][sizeof "Wednesday"] =
791 {
792 L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"),
793 L_("Thursday"), L_("Friday"), L_("Saturday")
794 };
795static CHAR_T const c_month_names[][sizeof "September"] =
796 {
797 L_("January"), L_("February"), L_("March"), L_("April"), L_("May"),
798 L_("June"), L_("July"), L_("August"), L_("September"), L_("October"),
799 L_("November"), L_("December")
800 };
801#endif
802
803
804/* When compiling this file, GNU applications can #define my_strftime
805 to a symbol (typically nstrftime) to get an extended strftime with
806 extra arguments TZ and NS. */
807
808#ifdef my_strftime
809# define extra_args , tz, ns
810# define extra_args_spec , timezone_t tz, int ns
811#else
812# if defined COMPILE_WIDE
813# define my_strftime wcsftime
814# define nl_get_alt_digit _nl_get_walt_digit
815# else
816# define my_strftime strftime
817# define nl_get_alt_digit _nl_get_alt_digit
818# endif
819# define extra_args
820# define extra_args_spec
821/* We don't have this information in general. */
822# define tz 1
823# define ns 0
824#endif
825
826static size_t __strftime_internal (STREAM_OR_CHAR_T *, STRFTIME_ARG (size_t)
827 const CHAR_T *, const struct tm *,
828 bool, int, int, bool *
829 extra_args_spec LOCALE_PARAM);
830
831/* Write information from TP into S according to the format
832 string FORMAT, writing no more that MAXSIZE characters
833 (including the terminating '\0') and returning number of
834 characters written. If S is NULL, nothing will be written
835 anywhere, so to determine how many characters would be
836 written, use NULL for S and (size_t) -1 for MAXSIZE. */
837size_t
838my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
839 const CHAR_T *format,
840 const struct tm *tp extra_args_spec LOCALE_PARAM)
841{
842 bool tzset_called = false;
843 return __strftime_internal (s, STRFTIME_ARG (maxsize) format, tp, false,
844 0, -1, &tzset_called extra_args LOCALE_ARG);
845}
846libc_hidden_def (my_strftime)
847
848/* Just like my_strftime, above, but with more parameters.
849 UPCASE indicates that the result should be converted to upper case.
850 YR_SPEC and WIDTH specify the padding and width for the year.
851 *TZSET_CALLED indicates whether tzset has been called here. */
852static size_t
853__strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
854 const CHAR_T *format,
855 const struct tm *tp, bool upcase,
856 int yr_spec, int width, bool *tzset_called
857 extra_args_spec LOCALE_PARAM)
858{
859#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
860 struct __locale_data *const current = loc->__locales[LC_TIME];
861#endif
862#if FPRINTFTIME
863 size_t maxsize = (size_t) -1;
864#endif
865
866 int saved_errno = errno;
867 int hour12 = tp->tm_hour;
868#ifdef _NL_CURRENT
869 /* We cannot make the following values variables since we must delay
870 the evaluation of these values until really needed since some
871 expressions might not be valid in every situation. The 'struct tm'
872 might be generated by a strptime() call that initialized
873 only a few elements. Dereference the pointers only if the format
874 requires this. Then it is ok to fail if the pointers are invalid. */
875# define a_wkday \
876 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
877 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)))
878# define f_wkday \
879 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
880 ? "?" : _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)))
881# define a_month \
882 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
883 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)))
884# define f_month \
885 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
886 ? "?" : _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)))
887# define a_altmonth \
888 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
889 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABALTMON_1) + tp->tm_mon)))
890# define f_altmonth \
891 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
892 ? "?" : _NL_CURRENT (LC_TIME, NLW(ALTMON_1) + tp->tm_mon)))
893# define ampm \
894 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \
895 ? NLW(PM_STR) : NLW(AM_STR)))
896
897# define aw_len STRLEN (a_wkday)
898# define am_len STRLEN (a_month)
899# define aam_len STRLEN (a_altmonth)
900# define ap_len STRLEN (ampm)
901#elif USE_C_LOCALE && !HAVE_STRFTIME_L
902/* The English abbreviated weekday names are just the first 3 characters of the
903 English full weekday names. */
904# define a_wkday \
905 (tp->tm_wday < 0 || tp->tm_wday > 6 ? L_("?") : c_weekday_names[tp->tm_wday])
906# define aw_len 3
907# define f_wkday \
908 (tp->tm_wday < 0 || tp->tm_wday > 6 ? L_("?") : c_weekday_names[tp->tm_wday])
909/* The English abbreviated month names are just the first 3 characters of the
910 English full month names. */
911# define a_month \
912 (tp->tm_mon < 0 || tp->tm_mon > 11 ? L_("?") : c_month_names[tp->tm_mon])
913# define am_len 3
914# define f_month \
915 (tp->tm_mon < 0 || tp->tm_mon > 11 ? L_("?") : c_month_names[tp->tm_mon])
916/* The English AM/PM strings happen to have the same length, namely 2. */
917# define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
918# define ap_len 2
919#endif
920#if HAVE_TZNAME
921 char **tzname_vec = tzname;
922#endif
923 const char *zone;
924 size_t i = 0;
925 STREAM_OR_CHAR_T *p = s;
926 const CHAR_T *f;
927#if DO_MULTIBYTE && !defined COMPILE_WIDE
928 const char *format_end = NULL;
929#endif
930
931 zone = NULL;
932#if HAVE_STRUCT_TM_TM_ZONE
933 /* The POSIX test suite assumes that setting
934 the environment variable TZ to a new value before calling strftime()
935 will influence the result (the %Z format) even if the information in
936 TP is computed with a totally different time zone.
937 This is bogus: though POSIX allows bad behavior like this,
938 POSIX does not require it. Do the right thing instead. */
939 zone = (const char *) tp->tm_zone;
940#endif
941#if HAVE_TZNAME
942 if (!tz)
943 {
944 if (! (zone && *zone))
945 zone = "GMT";
946 }
947 else
948 {
949# if !HAVE_STRUCT_TM_TM_ZONE
950 /* Infer the zone name from *TZ instead of from TZNAME. */
951 tzname_vec = tz->tzname_copy;
952# endif
953 }
954 /* The tzset() call might have changed the value. */
955 if (!(zone && *zone) && tp->tm_isdst >= 0)
956 {
957 /* POSIX.1 requires that local time zone information be used as
958 though strftime called tzset. */
959# ifndef my_strftime
960 if (!*tzset_called)
961 {
962 tzset ();
963 *tzset_called = true;
964 }
965# endif
966 zone = tzname_vec[tp->tm_isdst != 0];
967 }
968#endif
969 if (! zone)
970 zone = "";
971
972 if (hour12 > 12)
973 hour12 -= 12;
974 else
975 if (hour12 == 0)
976 hour12 = 12;
977
978 for (f = format; *f != '\0'; width = -1, f++)
979 {
980 int pad = 0; /* Padding for number ('_', '-', '+', '0', or 0). */
981 int modifier; /* Field modifier ('E', 'O', or 0). */
982 int digits = 0; /* Max digits for numeric format. */
983 int number_value; /* Numeric value to be printed. */
984 unsigned int u_number_value; /* (unsigned int) number_value. */
985 bool negative_number; /* The number is negative. */
986 bool always_output_a_sign; /* +/- should always be output. */
987 int tz_colon_mask; /* Bitmask of where ':' should appear. */
988 const CHAR_T *subfmt;
989 CHAR_T *bufp;
990 CHAR_T buf[1
991 + 2 /* for the two colons in a %::z or %:::z time zone */
992 + (sizeof (int) < sizeof (time_t)
993 ? INT_STRLEN_BOUND (time_t)
994 : INT_STRLEN_BOUND (int))];
995 bool to_lowcase = false;
996 bool to_uppcase = upcase;
997 size_t colons;
998 bool change_case = false;
999 int format_char;
1000 int subwidth;
1001
1002#if DO_MULTIBYTE && !defined COMPILE_WIDE
1003 switch (*f)
1004 {
1005 case L_('%'):
1006 break;
1007
1008 case L_('\b'): case L_('\t'): case L_('\n'):
1009 case L_('\v'): case L_('\f'): case L_('\r'):
1010 case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
1011 case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
1012 case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
1013 case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
1014 case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
1015 case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
1016 case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
1017 case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
1018 case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
1019 case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
1020 case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
1021 case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
1022 case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
1023 case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
1024 case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
1025 case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
1026 case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
1027 case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
1028 case L_('~'):
1029 /* The C Standard requires these 98 characters (plus '%') to
1030 be in the basic execution character set. None of these
1031 characters can start a multibyte sequence, so they need
1032 not be analyzed further. */
1033 add1 (*f);
1034 continue;
1035
1036 default:
1037 /* Copy this multibyte sequence until we reach its end, find
1038 an error, or come back to the initial shift state. */
1039 {
1040 mbstate_t mbstate = mbstate_zero;
1041 size_t len = 0;
1042 size_t fsize;
1043
1044 if (! format_end)
1045 format_end = f + strlen (f) + 1;
1046 fsize = format_end - f;
1047
1048 do
1049 {
1050 size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
1051
1052 if (bytes == 0)
1053 break;
1054
1055 if (bytes == (size_t) -2)
1056 {
1057 len += strlen (f + len);
1058 break;
1059 }
1060
1061 if (bytes == (size_t) -1)
1062 {
1063 len++;
1064 break;
1065 }
1066
1067 len += bytes;
1068 }
1069 while (! mbsinit (&mbstate));
1070
1071 cpy (len, f);
1072 f += len - 1;
1073 continue;
1074 }
1075 }
1076
1077#else /* ! DO_MULTIBYTE */
1078
1079 /* Either multibyte encodings are not supported, they are
1080 safe for formats, so any non-'%' byte can be copied through,
1081 or this is the wide character version. */
1082 if (*f != L_('%'))
1083 {
1084 add1 (*f);
1085 continue;
1086 }
1087
1088#endif /* ! DO_MULTIBYTE */
1089
1090 char const *percent = f;
1091
1092 /* Check for flags that can modify a format. */
1093 while (1)
1094 {
1095 switch (*++f)
1096 {
1097 /* This influences the number formats. */
1098 case L_('_'):
1099 case L_('-'):
1100 case L_('+'):
1101 case L_('0'):
1102 pad = *f;
1103 continue;
1104
1105 /* This changes textual output. */
1106 case L_('^'):
1107 to_uppcase = true;
1108 continue;
1109 case L_('#'):
1110 change_case = true;
1111 continue;
1112
1113 default:
1114 break;
1115 }
1116 break;
1117 }
1118
1119 if (ISDIGIT (*f))
1120 {
1121 width = 0;
1122 do
1123 {
1124 if (ckd_mul (&width, width, 10)
1125 || ckd_add (&width, width, *f - L_('0')))
1126 width = INT_MAX;
1127 ++f;
1128 }
1129 while (ISDIGIT (*f));
1130 }
1131
1132 /* Check for modifiers. */
1133 switch (*f)
1134 {
1135 case L_('E'):
1136 case L_('O'):
1137 modifier = *f++;
1138 break;
1139
1140 default:
1141 modifier = 0;
1142 break;
1143 }
1144
1145 /* Now do the specified format. */
1146 format_char = *f;
1147 switch (format_char)
1148 {
1149#define DO_NUMBER(d, v) \
1150 do \
1151 { \
1152 digits = d; \
1153 number_value = v; \
1154 goto do_number; \
1155 } \
1156 while (0)
1157#define DO_SIGNED_NUMBER(d, negative, v) \
1158 DO_MAYBE_SIGNED_NUMBER (d, negative, v, do_signed_number)
1159#define DO_YEARISH(d, negative, v) \
1160 DO_MAYBE_SIGNED_NUMBER (d, negative, v, do_yearish)
1161#define DO_MAYBE_SIGNED_NUMBER(d, negative, v, label) \
1162 do \
1163 { \
1164 digits = d; \
1165 negative_number = negative; \
1166 u_number_value = v; \
1167 goto label; \
1168 } \
1169 while (0)
1170
1171 /* The mask is not what you might think.
1172 When the ordinal i'th bit is set, insert a colon
1173 before the i'th digit of the time zone representation. */
1174#define DO_TZ_OFFSET(d, mask, v) \
1175 do \
1176 { \
1177 digits = d; \
1178 tz_colon_mask = mask; \
1179 u_number_value = v; \
1180 goto do_tz_offset; \
1181 } \
1182 while (0)
1183#define DO_NUMBER_SPACEPAD(d, v) \
1184 do \
1185 { \
1186 digits = d; \
1187 number_value = v; \
1188 goto do_number_spacepad; \
1189 } \
1190 while (0)
1191
1192 case L_('%'):
1193 if (f - 1 != percent)
1194 goto bad_percent;
1195 add1 (*f);
1196 break;
1197
1198 case L_('a'):
1199 if (modifier != 0)
1200 goto bad_format;
1201 if (change_case)
1202 {
1203 to_uppcase = true;
1204 to_lowcase = false;
1205 }
1206#if defined _NL_CURRENT || (USE_C_LOCALE && !HAVE_STRFTIME_L)
1207 cpy (aw_len, a_wkday);
1208 break;
1209#else
1210 goto underlying_strftime;
1211#endif
1212
1213 case 'A':
1214 if (modifier != 0)
1215 goto bad_format;
1216 if (change_case)
1217 {
1218 to_uppcase = true;
1219 to_lowcase = false;
1220 }
1221#if defined _NL_CURRENT || (USE_C_LOCALE && !HAVE_STRFTIME_L)
1222 cpy (STRLEN (f_wkday), f_wkday);
1223 break;
1224#else
1225 goto underlying_strftime;
1226#endif
1227
1228 case L_('b'):
1229 case L_('h'):
1230 if (change_case)
1231 {
1232 to_uppcase = true;
1233 to_lowcase = false;
1234 }
1235 if (modifier == L_('E'))
1236 goto bad_format;
1237#ifdef _NL_CURRENT
1238 if (modifier == L_('O'))
1239 cpy (aam_len, a_altmonth);
1240 else
1241 cpy (am_len, a_month);
1242 break;
1243#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1244 cpy (am_len, a_month);
1245 break;
1246#else
1247 goto underlying_strftime;
1248#endif
1249
1250 case L_('B'):
1251 if (modifier == L_('E'))
1252 goto bad_format;
1253 if (change_case)
1254 {
1255 to_uppcase = true;
1256 to_lowcase = false;
1257 }
1258#ifdef _NL_CURRENT
1259 if (modifier == L_('O'))
1260 cpy (STRLEN (f_altmonth), f_altmonth);
1261 else
1262 cpy (STRLEN (f_month), f_month);
1263 break;
1264#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1265 cpy (STRLEN (f_month), f_month);
1266 break;
1267#else
1268 goto underlying_strftime;
1269#endif
1270
1271 case L_('c'):
1272 if (modifier == L_('O'))
1273 goto bad_format;
1274#ifdef _NL_CURRENT
1275 if (! (modifier == L_('E')
1276 && (*(subfmt =
1277 (const CHAR_T *) _NL_CURRENT (LC_TIME,
1278 NLW(ERA_D_T_FMT)))
1279 != '\0')))
1280 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
1281#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1282 subfmt = L_("%a %b %e %H:%M:%S %Y");
1283#else
1284 goto underlying_strftime;
1285#endif
1286
1287 subformat:
1288 subwidth = -1;
1289 subformat_width:
1290 {
1291 size_t len = __strftime_internal (NULL, STRFTIME_ARG ((size_t) -1)
1292 subfmt, tp, to_uppcase,
1293 pad, subwidth, tzset_called
1294 extra_args LOCALE_ARG);
1295 add (len, __strftime_internal (p,
1296 STRFTIME_ARG (maxsize - i)
1297 subfmt, tp, to_uppcase,
1298 pad, subwidth, tzset_called
1299 extra_args LOCALE_ARG));
1300 }
1301 break;
1302
1303#if !((defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY) || (USE_C_LOCALE && !HAVE_STRFTIME_L))
1304 underlying_strftime:
1305 {
1306 /* The relevant information is available only via the
1307 underlying strftime implementation, so use that. */
1308 char ufmt[5];
1309 char *u = ufmt;
1310 char ubuf[1024]; /* enough for any single format in practice */
1311 size_t len;
1312 /* Make sure we're calling the actual underlying strftime.
1313 In some cases, config.h contains something like
1314 "#define strftime rpl_strftime". */
1315# ifdef strftime
1316# undef strftime
1317 size_t strftime (char *, size_t, const char *, struct tm const *);
1318# endif
1319
1320 /* The space helps distinguish strftime failure from empty
1321 output. */
1322 *u++ = ' ';
1323 *u++ = '%';
1324 if (modifier != 0)
1325 *u++ = modifier;
1326 *u++ = format_char;
1327 *u = '\0';
1328
1329# if USE_C_LOCALE /* implies HAVE_STRFTIME_L */
1330 locale_t locale = c_locale ();
1331 if (!locale)
1332 return 0; /* errno is set here */
1333 len = strftime_l (ubuf, sizeof ubuf, ufmt, tp, locale);
1334# else
1335 len = strftime (ubuf, sizeof ubuf, ufmt, tp);
1336# endif
1337 if (len != 0)
1338 {
1339# if defined __NetBSD__ || defined __sun /* NetBSD, Solaris */
1340 if (format_char == L_('c'))
1341 {
1342 /* The output of the strftime %c directive consists of the
1343 date, the time, and the time zone. But the time zone is
1344 wrong, since neither TZ nor ZONE was passed as argument.
1345 Therefore, remove the the last space-delimited word.
1346 In order not to accidentally remove a date or a year
1347 (that contains no letter) or an AM/PM indicator (that has
1348 length 2), remove that last word only if it contains a
1349 letter and has length >= 3. */
1350 char *space;
1351 for (space = ubuf + len - 1; *space != ' '; space--)
1352 ;
1353 if (space > ubuf)
1354 {
1355 /* Found a space. */
1356 if (strlen (space + 1) >= 3)
1357 {
1358 /* The last word has length >= 3. */
1359 bool found_letter = false;
1360 const char *p;
1361 for (p = space + 1; *p != '\0'; p++)
1362 if ((*p >= 'A' && *p <= 'Z')
1363 || (*p >= 'a' && *p <= 'z'))
1364 {
1365 found_letter = true;
1366 break;
1367 }
1368 if (found_letter)
1369 {
1370 /* The last word contains a letter. */
1371 *space = '\0';
1372 len = space - ubuf;
1373 }
1374 }
1375 }
1376 }
1377# if REQUIRE_GNUISH_STRFTIME_AM_PM
1378 /* The output of the strftime %p and %r directives contains
1379 an AM/PM indicator even for locales where it is not
1380 suitable, such as French. Remove this indicator. */
1381 else if (format_char == L_('p'))
1382 {
1383 bool found_ampm = (len > 1);
1384 if (found_ampm && should_remove_ampm ())
1385 {
1386 ubuf[1] = '\0';
1387 len = 1;
1388 }
1389 }
1390 else if (format_char == L_('r'))
1391 {
1392 char last_char = ubuf[len - 1];
1393 bool found_ampm = !(last_char >= '0' && last_char <= '9');
1394 if (found_ampm && should_remove_ampm ())
1395 {
1396 char *space;
1397 for (space = ubuf + len - 1; *space != ' '; space--)
1398 ;
1399 if (space > ubuf)
1400 {
1401 *space = '\0';
1402 len = space - ubuf;
1403 }
1404 }
1405 }
1406# endif
1407# endif
1408 cpy (len - 1, ubuf + 1);
1409 }
1410 }
1411 break;
1412#endif
1413
1414 case L_('C'):
1415 if (modifier == L_('E'))
1416 {
1417#if HAVE_STRUCT_ERA_ENTRY
1418 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1419 if (era)
1420 {
1421# ifdef COMPILE_WIDE
1422 size_t len = __wcslen (era->era_wname);
1423 cpy (len, era->era_wname);
1424# else
1425 size_t len = strlen (era->era_name);
1426 cpy (len, era->era_name);
1427# endif
1428 break;
1429 }
1430#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1431#else
1432 goto underlying_strftime;
1433#endif
1434 }
1435
1436 {
1437 bool negative_year = tp->tm_year < - TM_YEAR_BASE;
1438 bool zero_thru_1899 = !negative_year & (tp->tm_year < 0);
1439 int century = ((tp->tm_year - 99 * zero_thru_1899) / 100
1440 + TM_YEAR_BASE / 100);
1441 DO_YEARISH (2, negative_year, century);
1442 }
1443
1444 case L_('x'):
1445 if (modifier == L_('O'))
1446 goto bad_format;
1447#ifdef _NL_CURRENT
1448 if (! (modifier == L_('E')
1449 && (*(subfmt =
1450 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
1451 != L_('\0'))))
1452 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
1453 goto subformat;
1454#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1455 subfmt = L_("%m/%d/%y");
1456 goto subformat;
1457#else
1458 goto underlying_strftime;
1459#endif
1460 case L_('D'):
1461 if (modifier != 0)
1462 goto bad_format;
1463 subfmt = L_("%m/%d/%y");
1464 goto subformat;
1465
1466 case L_('d'):
1467 if (modifier == L_('E'))
1468 goto bad_format;
1469
1470 DO_NUMBER (2, tp->tm_mday);
1471
1472 case L_('e'):
1473 if (modifier == L_('E'))
1474 goto bad_format;
1475
1476 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
1477
1478 /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE)
1479 and then jump to one of these labels. */
1480
1481 do_tz_offset:
1482 always_output_a_sign = true;
1483 goto do_number_body;
1484
1485 do_yearish:
1486 if (pad == 0)
1487 pad = yr_spec;
1488 always_output_a_sign
1489 = (pad == L_('+')
1490 && ((digits == 2 ? 99 : 9999) < u_number_value
1491 || digits < width));
1492 goto do_maybe_signed_number;
1493
1494 do_number_spacepad:
1495 if (pad == 0)
1496 pad = L_('_');
1497
1498 do_number:
1499 /* Format NUMBER_VALUE according to the MODIFIER flag. */
1500 negative_number = number_value < 0;
1501 u_number_value = number_value;
1502
1503 do_signed_number:
1504 always_output_a_sign = false;
1505
1506 do_maybe_signed_number:
1507 tz_colon_mask = 0;
1508
1509 do_number_body:
1510 /* Format U_NUMBER_VALUE according to the MODIFIER flag.
1511 NEGATIVE_NUMBER is nonzero if the original number was
1512 negative; in this case it was converted directly to
1513 unsigned int (i.e., modulo (UINT_MAX + 1)) without
1514 negating it. */
1515 if (modifier == L_('O') && !negative_number)
1516 {
1517#ifdef _NL_CURRENT
1518 /* Get the locale specific alternate representation of
1519 the number. If none exist NULL is returned. */
1520 const CHAR_T *cp = nl_get_alt_digit (u_number_value
1521 HELPER_LOCALE_ARG);
1522
1523 if (cp != NULL)
1524 {
1525 size_t digitlen = STRLEN (cp);
1526 if (digitlen != 0)
1527 {
1528 cpy (digitlen, cp);
1529 break;
1530 }
1531 }
1532#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1533#else
1534 goto underlying_strftime;
1535#endif
1536 }
1537
1538 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1539
1540 if (negative_number)
1541 u_number_value = - u_number_value;
1542
1543 do
1544 {
1545 if (tz_colon_mask & 1)
1546 *--bufp = ':';
1547 tz_colon_mask >>= 1;
1548 *--bufp = u_number_value % 10 + L_('0');
1549 u_number_value /= 10;
1550 }
1551 while (u_number_value != 0 || tz_colon_mask != 0);
1552
1553 do_number_sign_and_padding:
1554 if (pad == 0)
1555 pad = L_('0');
1556 if (width < 0)
1557 width = digits;
1558
1559 {
1560 CHAR_T sign_char = (negative_number ? L_('-')
1561 : always_output_a_sign ? L_('+')
1562 : 0);
1563 int numlen = buf + sizeof buf / sizeof buf[0] - bufp;
1564 int shortage = width - !!sign_char - numlen;
1565 int padding = pad == L_('-') || shortage <= 0 ? 0 : shortage;
1566
1567 if (sign_char)
1568 {
1569 if (pad == L_('_'))
1570 {
1571 if (p)
1572 memset_space (p, padding);
1573 i += padding;
1574 width -= padding;
1575 }
1576 width_add1 (0, sign_char);
1577 width--;
1578 }
1579
1580 cpy (numlen, bufp);
1581 }
1582 break;
1583
1584 case L_('F'):
1585 if (modifier != 0)
1586 goto bad_format;
1587 if (pad == 0 && width < 0)
1588 {
1589 pad = L_('+');
1590 subwidth = 4;
1591 }
1592 else
1593 {
1594 subwidth = width - 6;
1595 if (subwidth < 0)
1596 subwidth = 0;
1597 }
1598 subfmt = L_("%Y-%m-%d");
1599 goto subformat_width;
1600
1601 case L_('H'):
1602 if (modifier == L_('E'))
1603 goto bad_format;
1604
1605 DO_NUMBER (2, tp->tm_hour);
1606
1607 case L_('I'):
1608 if (modifier == L_('E'))
1609 goto bad_format;
1610
1611 DO_NUMBER (2, hour12);
1612
1613 case L_('k'): /* GNU extension. */
1614 if (modifier == L_('E'))
1615 goto bad_format;
1616
1617 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
1618
1619 case L_('l'): /* GNU extension. */
1620 if (modifier == L_('E'))
1621 goto bad_format;
1622
1623 DO_NUMBER_SPACEPAD (2, hour12);
1624
1625 case L_('j'):
1626 if (modifier == L_('E'))
1627 goto bad_format;
1628
1629 DO_SIGNED_NUMBER (3, tp->tm_yday < -1, tp->tm_yday + 1U);
1630
1631 case L_('M'):
1632 if (modifier == L_('E'))
1633 goto bad_format;
1634
1635 DO_NUMBER (2, tp->tm_min);
1636
1637 case L_('m'):
1638 if (modifier == L_('E'))
1639 goto bad_format;
1640
1641 DO_SIGNED_NUMBER (2, tp->tm_mon < -1, tp->tm_mon + 1U);
1642
1643#ifndef _LIBC
1644 case L_('N'): /* GNU extension. */
1645 if (modifier == L_('E'))
1646 goto bad_format;
1647 {
1648 int n = ns, ns_digits = 9;
1649 if (width <= 0)
1650 width = ns_digits;
1651 int ndigs = ns_digits;
1652 while (width < ndigs || (1 < ndigs && n % 10 == 0))
1653 ndigs--, n /= 10;
1654 for (int j = ndigs; 0 < j; j--)
1655 buf[j - 1] = n % 10 + L_('0'), n /= 10;
1656 if (!pad)
1657 pad = L_('0');
1658 width_cpy (0, ndigs, buf);
1659 width_add (width - ndigs, 0, (void) 0);
1660 }
1661 break;
1662#endif
1663
1664 case L_('n'):
1665 add1 (L_('\n'));
1666 break;
1667
1668 case L_('P'):
1669 to_lowcase = true;
1670#ifndef _NL_CURRENT
1671 format_char = L_('p');
1672#endif
1673 FALLTHROUGH;
1674 case L_('p'):
1675 if (change_case)
1676 {
1677 to_uppcase = false;
1678 to_lowcase = true;
1679 }
1680#if defined _NL_CURRENT || (USE_C_LOCALE && !HAVE_STRFTIME_L)
1681 cpy (ap_len, ampm);
1682 break;
1683#else
1684 goto underlying_strftime;
1685#endif
1686
1687 case L_('q'): /* GNU extension. */
1688 DO_SIGNED_NUMBER (1, false, ((tp->tm_mon * 11) >> 5) + 1);
1689
1690 case L_('R'):
1691 subfmt = L_("%H:%M");
1692 goto subformat;
1693
1694 case L_('r'):
1695#ifdef _NL_CURRENT
1696 if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
1697 NLW(T_FMT_AMPM)))
1698 == L_('\0'))
1699 subfmt = L_("%I:%M:%S %p");
1700 goto subformat;
1701#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1702 subfmt = L_("%I:%M:%S %p");
1703 goto subformat;
1704#elif (defined __APPLE__ && defined __MACH__) || defined __FreeBSD__
1705 /* macOS, FreeBSD strftime() may produce empty output for "%r". */
1706 subfmt = L_("%I:%M:%S %p");
1707 goto subformat;
1708#else
1709 goto underlying_strftime;
1710#endif
1711
1712 case L_('S'):
1713 if (modifier == L_('E'))
1714 goto bad_format;
1715
1716 DO_NUMBER (2, tp->tm_sec);
1717
1718 case L_('s'): /* GNU extension. */
1719 {
1720 struct tm ltm;
1721 time_t t;
1722
1723 ltm = *tp;
1724 ltm.tm_yday = -1;
1725 t = mktime_z (tz, &ltm);
1726 if (ltm.tm_yday < 0)
1727 {
1728 errno = EOVERFLOW;
1729 return 0;
1730 }
1731
1732 /* Generate string value for T using time_t arithmetic;
1733 this works even if sizeof (long) < sizeof (time_t). */
1734
1735 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1736 negative_number = t < 0;
1737
1738 do
1739 {
1740 int d = t % 10;
1741 t /= 10;
1742 *--bufp = (negative_number ? -d : d) + L_('0');
1743 }
1744 while (t != 0);
1745
1746 digits = 1;
1747 always_output_a_sign = false;
1748 goto do_number_sign_and_padding;
1749 }
1750
1751 case L_('X'):
1752 if (modifier == L_('O'))
1753 goto bad_format;
1754#ifdef _NL_CURRENT
1755 if (! (modifier == L_('E')
1756 && (*(subfmt =
1757 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
1758 != L_('\0'))))
1759 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
1760 goto subformat;
1761#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1762 subfmt = L_("%H:%M:%S");
1763 goto subformat;
1764#else
1765 goto underlying_strftime;
1766#endif
1767 case L_('T'):
1768 subfmt = L_("%H:%M:%S");
1769 goto subformat;
1770
1771 case L_('t'):
1772 add1 (L_('\t'));
1773 break;
1774
1775 case L_('u'):
1776 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1777
1778 case L_('U'):
1779 if (modifier == L_('E'))
1780 goto bad_format;
1781
1782 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1783
1784 case L_('V'):
1785 case L_('g'):
1786 case L_('G'):
1787 if (modifier == L_('E'))
1788 goto bad_format;
1789 {
1790 /* YEAR is a leap year if and only if (tp->tm_year + TM_YEAR_BASE)
1791 is a leap year, except that YEAR and YEAR - 1 both work
1792 correctly even when (tp->tm_year + TM_YEAR_BASE) would
1793 overflow. */
1794 int year = (tp->tm_year
1795 + (tp->tm_year < 0
1796 ? TM_YEAR_BASE % 400
1797 : TM_YEAR_BASE % 400 - 400));
1798 int year_adjust = 0;
1799 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1800
1801 if (days < 0)
1802 {
1803 /* This ISO week belongs to the previous year. */
1804 year_adjust = -1;
1805 days = iso_week_days (tp->tm_yday + (365 + __isleap (year - 1)),
1806 tp->tm_wday);
1807 }
1808 else
1809 {
1810 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1811 tp->tm_wday);
1812 if (0 <= d)
1813 {
1814 /* This ISO week belongs to the next year. */
1815 year_adjust = 1;
1816 days = d;
1817 }
1818 }
1819
1820 switch (*f)
1821 {
1822 case L_('g'):
1823 {
1824 int yy = (tp->tm_year % 100 + year_adjust) % 100;
1825 DO_YEARISH (2, false,
1826 (0 <= yy
1827 ? yy
1828 : tp->tm_year < -TM_YEAR_BASE - year_adjust
1829 ? -yy
1830 : yy + 100));
1831 }
1832
1833 case L_('G'):
1834 DO_YEARISH (4, tp->tm_year < -TM_YEAR_BASE - year_adjust,
1835 (tp->tm_year + (unsigned int) TM_YEAR_BASE
1836 + year_adjust));
1837
1838 default:
1839 DO_NUMBER (2, days / 7 + 1);
1840 }
1841 }
1842
1843 case L_('W'):
1844 if (modifier == L_('E'))
1845 goto bad_format;
1846
1847 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1848
1849 case L_('w'):
1850 if (modifier == L_('E'))
1851 goto bad_format;
1852
1853 DO_NUMBER (1, tp->tm_wday);
1854
1855 case L_('Y'):
1856 if (modifier == L_('E'))
1857 {
1858#if HAVE_STRUCT_ERA_ENTRY
1859 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1860 if (era)
1861 {
1862# ifdef COMPILE_WIDE
1863 subfmt = era->era_wformat;
1864# else
1865 subfmt = era->era_format;
1866# endif
1867 if (pad == 0)
1868 pad = yr_spec;
1869 goto subformat;
1870 }
1871#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1872#else
1873 goto underlying_strftime;
1874#endif
1875 }
1876 if (modifier == L_('O'))
1877 goto bad_format;
1878
1879 DO_YEARISH (4, tp->tm_year < -TM_YEAR_BASE,
1880 tp->tm_year + (unsigned int) TM_YEAR_BASE);
1881
1882 case L_('y'):
1883 if (modifier == L_('E'))
1884 {
1885#if HAVE_STRUCT_ERA_ENTRY
1886 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1887 if (era)
1888 {
1889 int delta = tp->tm_year - era->start_date[0];
1890 if (pad == 0)
1891 pad = yr_spec;
1892 DO_NUMBER (2, (era->offset
1893 + delta * era->absolute_direction));
1894 }
1895#elif USE_C_LOCALE && !HAVE_STRFTIME_L
1896#else
1897 goto underlying_strftime;
1898#endif
1899 }
1900
1901 {
1902 int yy = tp->tm_year % 100;
1903 if (yy < 0)
1904 yy = tp->tm_year < - TM_YEAR_BASE ? -yy : yy + 100;
1905 DO_YEARISH (2, false, yy);
1906 }
1907
1908 case L_('Z'):
1909 if (change_case)
1910 {
1911 to_uppcase = false;
1912 to_lowcase = true;
1913 }
1914
1915#ifdef COMPILE_WIDE
1916 {
1917 /* The zone string is always given in multibyte form. We have
1918 to convert it to wide character. */
1919 size_t w = pad == L_('-') || width < 0 ? 0 : width;
1920 char const *z = zone;
1921 mbstate_t st = {0};
1922 size_t len = __mbsrtowcs_l (p, &z, maxsize - i, &st, loc);
1923 if (len == (size_t) -1)
1924 return 0;
1925 size_t incr = len < w ? w : len;
1926 if (incr >= maxsize - i)
1927 {
1928 errno = ERANGE;
1929 return 0;
1930 }
1931 if (p)
1932 {
1933 if (len < w)
1934 {
1935 size_t delta = w - len;
1936 __wmemmove (p + delta, p, len);
1937 wchar_t wc = pad == L_('0') || pad == L_('+') ? L'0' : L' ';
1938 wmemset (p, wc, delta);
1939 }
1940 p += incr;
1941 }
1942 i += incr;
1943 }
1944#else
1945 cpy (strlen (zone), zone);
1946#endif
1947 break;
1948
1949 case L_(':'):
1950 /* :, ::, and ::: are valid only just before 'z'.
1951 :::: etc. are rejected later. */
1952 for (colons = 1; f[colons] == L_(':'); colons++)
1953 continue;
1954 if (f[colons] != L_('z'))
1955 goto bad_format;
1956 f += colons;
1957 goto do_z_conversion;
1958
1959 case L_('z'):
1960 colons = 0;
1961
1962 do_z_conversion:
1963 if (tp->tm_isdst < 0)
1964 break;
1965
1966 {
1967 int diff;
1968 int hour_diff;
1969 int min_diff;
1970 int sec_diff;
1971#if HAVE_TM_GMTOFF
1972 diff = tp->tm_gmtoff;
1973#else
1974 if (!tz)
1975 diff = 0;
1976 else
1977 {
1978 struct tm gtm;
1979 struct tm ltm;
1980 time_t lt;
1981
1982 /* POSIX.1 requires that local time zone information be used as
1983 though strftime called tzset. */
1984# ifndef my_strftime
1985 if (!*tzset_called)
1986 {
1987 tzset ();
1988 *tzset_called = true;
1989 }
1990# endif
1991
1992 ltm = *tp;
1993 ltm.tm_wday = -1;
1994 lt = mktime_z (tz, &ltm);
1995 if (ltm.tm_wday < 0 || ! localtime_rz (0, &lt, &gtm))
1996 break;
1997 diff = tm_diff (&ltm, &gtm);
1998 }
1999#endif
2000
2001 negative_number = diff < 0 || (diff == 0 && *zone == '-');
2002 hour_diff = diff / 60 / 60;
2003 min_diff = diff / 60 % 60;
2004 sec_diff = diff % 60;
2005
2006 switch (colons)
2007 {
2008 case 0: /* +hhmm */
2009 DO_TZ_OFFSET (5, 0, hour_diff * 100 + min_diff);
2010
2011 case 1: tz_hh_mm: /* +hh:mm */
2012 DO_TZ_OFFSET (6, 04, hour_diff * 100 + min_diff);
2013
2014 case 2: tz_hh_mm_ss: /* +hh:mm:ss */
2015 DO_TZ_OFFSET (9, 024,
2016 hour_diff * 10000 + min_diff * 100 + sec_diff);
2017
2018 case 3: /* +hh if possible, else +hh:mm, else +hh:mm:ss */
2019 if (sec_diff != 0)
2020 goto tz_hh_mm_ss;
2021 if (min_diff != 0)
2022 goto tz_hh_mm;
2023 DO_TZ_OFFSET (3, 0, hour_diff);
2024
2025 default:
2026 goto bad_format;
2027 }
2028 }
2029
2030 case L_('\0'): /* GNU extension: % at end of format. */
2031 bad_percent:
2032 --f;
2033 FALLTHROUGH;
2034 default:
2035 /* Unknown format; output the format, including the '%',
2036 since this is most likely the right thing to do if a
2037 multibyte string has been misparsed. */
2038 bad_format:
2039 cpy (f - percent + 1, percent);
2040 break;
2041 }
2042 }
2043
2044#if ! FPRINTFTIME
2045 if (p && maxsize != 0)
2046 *p = L_('\0');
2047#endif
2048
2049 errno = saved_errno;
2050 return i;
2051}
diff --git a/lib/strftime.h b/lib/strftime.h
index d6efdb848a3..8ce62cdb6d7 100644
--- a/lib/strftime.h
+++ b/lib/strftime.h
@@ -21,17 +21,68 @@
21extern "C" { 21extern "C" {
22#endif 22#endif
23 23
24/* Just like strftime, but with two more arguments: 24/* Formats the broken-down time *__TP, with additional __NS nanoseconds,
25 POSIX requires that strftime use the local timezone information. 25 into the buffer __S of size __MAXSIZE, according to the rules of the
26 Use the timezone __TZ instead. Use __NS as the number of 26 LC_TIME category of the current locale.
27 nanoseconds in the %N directive. 27
28 28 Uses the time zone __TZ.
29 On error, set errno and return 0. Otherwise, return the number of 29 If *__TP represents local time, __TZ should be set to
30 bytes generated (not counting the trailing NUL), preserving errno 30 tzalloc (getenv ("TZ")).
31 if the number is 0. This errno behavior is in draft POSIX 202x 31 If *__TP represents universal time (a.k.a. GMT), __TZ should be set to
32 plus some requested changes to POSIX. */ 32 (timezone_t) 0.
33size_t nstrftime (char *restrict, size_t, char const *, struct tm const *, 33
34 timezone_t __tz, int __ns); 34 The format string __FORMAT, including GNU extensions, is described in
35 the GNU libc's strftime() documentation:
36 <https://www.gnu.org/software/libc/manual/html_node/Formatting-Calendar-Time.html>
37 Additionally, the following conversion is supported:
38 %N The number of nanoseconds, passed as __NS argument.
39 Here's a summary of the available conversions (= format directives):
40 literal characters %n %t %%
41 date:
42 century %C
43 year %Y %y
44 week-based year %G %g
45 month (in year) %m %B %b %h
46 week in year %U %W %V
47 day in year %j
48 day (in month) %d %e
49 day in week %u %w %A %a
50 year, month, day %x %F %D
51 time:
52 half-day %p %P
53 hour %H %k %I %l
54 minute (in hour) %M
55 hour, minute %R
56 second (in minute) %S
57 hour, minute, second %r %T %X
58 second (since epoch) %s
59 date and time: %c
60 time zone: %z %Z
61 nanosecond %N
62
63 Stores the result, as a string with a trailing NUL character, at the
64 beginning of the array __S[0..__MAXSIZE-1], if it fits, and returns
65 the length of that string, not counting the trailing NUL. In this case,
66 errno is preserved if the return value is 0.
67 If it does not fit, this function sets errno to ERANGE and returns 0.
68 Upon other errors, this function sets errno and returns 0 as well.
69
70 Note: The errno behavior is in draft POSIX 202x plus some requested
71 changes to POSIX.
72
73 This function is like strftime, but with two more arguments:
74 * __TZ instead of the local timezone information,
75 * __NS as the number of nanoseconds in the %N directive.
76 */
77size_t nstrftime (char *restrict __s, size_t __maxsize,
78 char const *__format,
79 struct tm const *__tp, timezone_t __tz, int __ns);
80
81/* Like nstrftime, except that it uses the "C" locale instead of the
82 current locale. */
83size_t c_nstrftime (char *restrict __s, size_t __maxsize,
84 char const *__format,
85 struct tm const *__tp, timezone_t __tz, int __ns);
35 86
36#ifdef __cplusplus 87#ifdef __cplusplus
37} 88}
diff --git a/lib/time.in.h b/lib/time.in.h
index ce28f1af25d..df99c8abca9 100644
--- a/lib/time.in.h
+++ b/lib/time.in.h
@@ -438,11 +438,7 @@ _GL_CXXALIAS_SYS (ctime, char *, (time_t const *__tp));
438_GL_CXXALIASWARN (ctime); 438_GL_CXXALIASWARN (ctime);
439# endif 439# endif
440# elif defined GNULIB_POSIXCHECK 440# elif defined GNULIB_POSIXCHECK
441# undef ctime 441/* No need to warn about portability, as a more serious warning is below. */
442# if HAVE_RAW_DECL_CTIME
443_GL_WARN_ON_USE (ctime, "ctime has portability problems - "
444 "use gnulib module ctime for portability");
445# endif
446# endif 442# endif
447 443
448/* Convert *TP to a date and time string. See 444/* Convert *TP to a date and time string. See
diff --git a/lib/time_r.c b/lib/time_r.c
index 3ef0b36802c..b724f3b38de 100644
--- a/lib/time_r.c
+++ b/lib/time_r.c
@@ -21,6 +21,11 @@
21 21
22#include <time.h> 22#include <time.h>
23 23
24/* The replacement functions in this file are only used on native Windows.
25 They are multithread-safe, because the gmtime() and localtime() functions
26 on native Windows — both in the ucrt and in the older MSVCRT — return a
27 pointer to a 'struct tm' in thread-local memory. */
28
24static struct tm * 29static struct tm *
25copy_tm_result (struct tm *dest, struct tm const *src) 30copy_tm_result (struct tm *dest, struct tm const *src)
26{ 31{
diff --git a/lib/warn-on-use.h b/lib/warn-on-use.h
index 8f4d40dcbeb..701013a07f4 100644
--- a/lib/warn-on-use.h
+++ b/lib/warn-on-use.h
@@ -32,6 +32,10 @@
32 _GL_WARN_ON_USE_ATTRIBUTE is for functions with 'static' or 'inline' 32 _GL_WARN_ON_USE_ATTRIBUTE is for functions with 'static' or 'inline'
33 linkage. 33 linkage.
34 34
35 _GL_WARN_ON_USE should not be used more than once for a given function
36 in a given compilation unit (because this may generate a warning even
37 if the function is never called).
38
35 However, one of the reasons that a function is a portability trap is 39 However, one of the reasons that a function is a portability trap is
36 if it has the wrong signature. Declaring FUNCTION with a different 40 if it has the wrong signature. Declaring FUNCTION with a different
37 signature in C is a compilation error, so this macro must use the 41 signature in C is a compilation error, so this macro must use the
diff --git a/lib/xalloc-oversized.h b/lib/xalloc-oversized.h
index 0b7bb2cee85..7f30f83e769 100644
--- a/lib/xalloc-oversized.h
+++ b/lib/xalloc-oversized.h
@@ -29,8 +29,7 @@
29 is SIZE_MAX - 1. */ 29 is SIZE_MAX - 1. */
30#define __xalloc_oversized(n, s) \ 30#define __xalloc_oversized(n, s) \
31 ((s) != 0 \ 31 ((s) != 0 \
32 && ((size_t) (PTRDIFF_MAX < SIZE_MAX ? PTRDIFF_MAX : SIZE_MAX - 1) / (s) \ 32 && (PTRDIFF_MAX < SIZE_MAX ? PTRDIFF_MAX : SIZE_MAX - 1) / (s) < (n))
33 < (n)))
34 33
35/* Return 1 if and only if an array of N objects, each of size S, 34/* Return 1 if and only if an array of N objects, each of size S,
36 cannot exist reliably because its total size in bytes would exceed 35 cannot exist reliably because its total size in bytes would exceed
diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index 00691c0d6c3..d8d0904f787 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -1,4 +1,4 @@
1# gnulib-common.m4 serial 91 1# gnulib-common.m4 serial 92
2dnl Copyright (C) 2007-2024 Free Software Foundation, Inc. 2dnl Copyright (C) 2007-2024 Free Software Foundation, Inc.
3dnl This file is free software; the Free Software Foundation 3dnl This file is free software; the Free Software Foundation
4dnl gives unlimited permission to copy and/or distribute it, 4dnl gives unlimited permission to copy and/or distribute it,
@@ -76,42 +76,48 @@ AC_DEFUN([gl_COMMON_BODY], [
76#endif]) 76#endif])
77 AH_VERBATIM([attribute], 77 AH_VERBATIM([attribute],
78[/* Attributes. */ 78[/* Attributes. */
79#if (defined __has_attribute \ 79/* Define _GL_HAS_ATTRIBUTE only once, because on FreeBSD, with gcc < 5, if
80 && (!defined __clang_minor__ \ 80 <config.h> gets included once again after <sys/cdefs.h>, __has_attribute(x)
81 || (defined __apple_build_version__ \ 81 expands to 0 always, and redefining _GL_HAS_ATTRIBUTE would turn off all
82 ? 7000000 <= __apple_build_version__ \ 82 attributes. */
83 : 5 <= __clang_major__))) 83#ifndef _GL_HAS_ATTRIBUTE
84# define _GL_HAS_ATTRIBUTE(attr) __has_attribute (__##attr##__) 84# if (defined __has_attribute \
85#else 85 && (!defined __clang_minor__ \
86# define _GL_HAS_ATTRIBUTE(attr) _GL_ATTR_##attr 86 || (defined __apple_build_version__ \
87# define _GL_ATTR_alloc_size _GL_GNUC_PREREQ (4, 3) 87 ? 7000000 <= __apple_build_version__ \
88# define _GL_ATTR_always_inline _GL_GNUC_PREREQ (3, 2) 88 : 5 <= __clang_major__)))
89# define _GL_ATTR_artificial _GL_GNUC_PREREQ (4, 3) 89# define _GL_HAS_ATTRIBUTE(attr) __has_attribute (__##attr##__)
90# define _GL_ATTR_cold _GL_GNUC_PREREQ (4, 3)
91# define _GL_ATTR_const _GL_GNUC_PREREQ (2, 95)
92# define _GL_ATTR_deprecated _GL_GNUC_PREREQ (3, 1)
93# define _GL_ATTR_diagnose_if 0
94# define _GL_ATTR_error _GL_GNUC_PREREQ (4, 3)
95# define _GL_ATTR_externally_visible _GL_GNUC_PREREQ (4, 1)
96# define _GL_ATTR_fallthrough _GL_GNUC_PREREQ (7, 0)
97# define _GL_ATTR_format _GL_GNUC_PREREQ (2, 7)
98# define _GL_ATTR_leaf _GL_GNUC_PREREQ (4, 6)
99# define _GL_ATTR_malloc _GL_GNUC_PREREQ (3, 0)
100# ifdef _ICC
101# define _GL_ATTR_may_alias 0
102# else 90# else
103# define _GL_ATTR_may_alias _GL_GNUC_PREREQ (3, 3) 91# define _GL_HAS_ATTRIBUTE(attr) _GL_ATTR_##attr
92# define _GL_ATTR_alloc_size _GL_GNUC_PREREQ (4, 3)
93# define _GL_ATTR_always_inline _GL_GNUC_PREREQ (3, 2)
94# define _GL_ATTR_artificial _GL_GNUC_PREREQ (4, 3)
95# define _GL_ATTR_cold _GL_GNUC_PREREQ (4, 3)
96# define _GL_ATTR_const _GL_GNUC_PREREQ (2, 95)
97# define _GL_ATTR_deprecated _GL_GNUC_PREREQ (3, 1)
98# define _GL_ATTR_diagnose_if 0
99# define _GL_ATTR_error _GL_GNUC_PREREQ (4, 3)
100# define _GL_ATTR_externally_visible _GL_GNUC_PREREQ (4, 1)
101# define _GL_ATTR_fallthrough _GL_GNUC_PREREQ (7, 0)
102# define _GL_ATTR_format _GL_GNUC_PREREQ (2, 7)
103# define _GL_ATTR_leaf _GL_GNUC_PREREQ (4, 6)
104# define _GL_ATTR_malloc _GL_GNUC_PREREQ (3, 0)
105# ifdef _ICC
106# define _GL_ATTR_may_alias 0
107# else
108# define _GL_ATTR_may_alias _GL_GNUC_PREREQ (3, 3)
109# endif
110# define _GL_ATTR_noinline _GL_GNUC_PREREQ (3, 1)
111# define _GL_ATTR_nonnull _GL_GNUC_PREREQ (3, 3)
112# define _GL_ATTR_nonstring _GL_GNUC_PREREQ (8, 0)
113# define _GL_ATTR_nothrow _GL_GNUC_PREREQ (3, 3)
114# define _GL_ATTR_packed _GL_GNUC_PREREQ (2, 7)
115# define _GL_ATTR_pure _GL_GNUC_PREREQ (2, 96)
116# define _GL_ATTR_returns_nonnull _GL_GNUC_PREREQ (4, 9)
117# define _GL_ATTR_sentinel _GL_GNUC_PREREQ (4, 0)
118# define _GL_ATTR_unused _GL_GNUC_PREREQ (2, 7)
119# define _GL_ATTR_warn_unused_result _GL_GNUC_PREREQ (3, 4)
104# endif 120# endif
105# define _GL_ATTR_noinline _GL_GNUC_PREREQ (3, 1)
106# define _GL_ATTR_nonnull _GL_GNUC_PREREQ (3, 3)
107# define _GL_ATTR_nonstring _GL_GNUC_PREREQ (8, 0)
108# define _GL_ATTR_nothrow _GL_GNUC_PREREQ (3, 3)
109# define _GL_ATTR_packed _GL_GNUC_PREREQ (2, 7)
110# define _GL_ATTR_pure _GL_GNUC_PREREQ (2, 96)
111# define _GL_ATTR_returns_nonnull _GL_GNUC_PREREQ (4, 9)
112# define _GL_ATTR_sentinel _GL_GNUC_PREREQ (4, 0)
113# define _GL_ATTR_unused _GL_GNUC_PREREQ (2, 7)
114# define _GL_ATTR_warn_unused_result _GL_GNUC_PREREQ (3, 4)
115#endif 121#endif
116 122
117/* Use __has_c_attribute if available. However, do not use with 123/* Use __has_c_attribute if available. However, do not use with
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 7a7ebb0f34e..d8b92e7b122 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -1024,7 +1024,7 @@ AC_DEFUN([gl_INIT],
1024 if test $ac_use_included_regex = yes; then 1024 if test $ac_use_included_regex = yes; then
1025 func_gl_gnulib_m4code_fd38c7e463b54744b77b98aeafb4fa7c 1025 func_gl_gnulib_m4code_fd38c7e463b54744b77b98aeafb4fa7c
1026 fi 1026 fi
1027 if { test $HAVE_DECL_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then 1027 if test $HAVE_DECL_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; then
1028 func_gl_gnulib_m4code_strtoll 1028 func_gl_gnulib_m4code_strtoll
1029 fi 1029 fi
1030 if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then 1030 if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then
@@ -1422,6 +1422,7 @@ AC_DEFUN([gl_FILE_LIST], [
1422 lib/stdlib.in.h 1422 lib/stdlib.in.h
1423 lib/stpcpy.c 1423 lib/stpcpy.c
1424 lib/str-two-way.h 1424 lib/str-two-way.h
1425 lib/strftime.c
1425 lib/strftime.h 1426 lib/strftime.h
1426 lib/string.in.h 1427 lib/string.in.h
1427 lib/strnlen.c 1428 lib/strnlen.c
diff --git a/m4/nanosleep.m4 b/m4/nanosleep.m4
index c51f590402f..ff730b676cd 100644
--- a/m4/nanosleep.m4
+++ b/m4/nanosleep.m4
@@ -1,4 +1,4 @@
1# serial 46 1# serial 47
2 2
3dnl From Jim Meyering. 3dnl From Jim Meyering.
4dnl Check for the nanosleep function. 4dnl Check for the nanosleep function.
@@ -119,6 +119,10 @@ AC_DEFUN([gl_FUNC_NANOSLEEP],
119 # Guess it halfway works when the kernel is Linux. 119 # Guess it halfway works when the kernel is Linux.
120 linux*) 120 linux*)
121 gl_cv_func_nanosleep='guessing no (mishandles large arguments)' ;; 121 gl_cv_func_nanosleep='guessing no (mishandles large arguments)' ;;
122 # Midipix generally emulates the Linux system calls,
123 # but here it handles large arguments correctly.
124 midipix*)
125 gl_cv_func_nanosleep='guessing yes' ;;
122 # Guess no on native Windows. 126 # Guess no on native Windows.
123 mingw* | windows*) 127 mingw* | windows*)
124 gl_cv_func_nanosleep='guessing no' ;; 128 gl_cv_func_nanosleep='guessing no' ;;
diff --git a/m4/nstrftime.m4 b/m4/nstrftime.m4
index 67250dc9455..aa5d63a54b5 100644
--- a/m4/nstrftime.m4
+++ b/m4/nstrftime.m4
@@ -1,4 +1,4 @@
1# serial 37 1# serial 38
2 2
3# Copyright (C) 1996-1997, 1999-2007, 2009-2024 Free Software Foundation, Inc. 3# Copyright (C) 1996-1997, 1999-2007, 2009-2024 Free Software Foundation, Inc.
4# 4#
@@ -16,7 +16,4 @@ AC_DEFUN([gl_FUNC_GNU_STRFTIME],
16 AC_REQUIRE([AC_STRUCT_TIMEZONE]) 16 AC_REQUIRE([AC_STRUCT_TIMEZONE])
17 17
18 AC_REQUIRE([gl_TM_GMTOFF]) 18 AC_REQUIRE([gl_TM_GMTOFF])
19
20 AC_DEFINE([my_strftime], [nstrftime],
21 [Define to the name of the strftime replacement function.])
22]) 19])
diff --git a/m4/utimens.m4 b/m4/utimens.m4
index af03e6b52be..0f5bfd4c843 100644
--- a/m4/utimens.m4
+++ b/m4/utimens.m4
@@ -3,7 +3,7 @@ dnl This file is free software; the Free Software Foundation
3dnl gives unlimited permission to copy and/or distribute it, 3dnl gives unlimited permission to copy and/or distribute it,
4dnl with or without modifications, as long as this notice is preserved. 4dnl with or without modifications, as long as this notice is preserved.
5 5
6dnl serial 15 6dnl serial 16
7 7
8AC_DEFUN([gl_UTIMENS], 8AC_DEFUN([gl_UTIMENS],
9[ 9[
@@ -36,12 +36,13 @@ AC_DEFUN([gl_UTIMENS],
36 [gl_cv_func_futimesat_works=yes], 36 [gl_cv_func_futimesat_works=yes],
37 [gl_cv_func_futimesat_works=no], 37 [gl_cv_func_futimesat_works=no],
38 [case "$host_os" in 38 [case "$host_os" in
39 # Guess yes on Linux systems. 39 # Guess yes on Linux systems
40 linux-* | linux) gl_cv_func_futimesat_works="guessing yes" ;; 40 # and on systems that emulate the Linux system calls.
41 # Guess yes on glibc systems. 41 linux* | midipix*) gl_cv_func_futimesat_works="guessing yes" ;;
42 *-gnu*) gl_cv_func_futimesat_works="guessing yes" ;; 42 # Guess yes on glibc systems.
43 # If we don't know, obey --enable-cross-guesses. 43 *-gnu*) gl_cv_func_futimesat_works="guessing yes" ;;
44 *) gl_cv_func_futimesat_works="$gl_cross_guess_normal" ;; 44 # If we don't know, obey --enable-cross-guesses.
45 *) gl_cv_func_futimesat_works="$gl_cross_guess_normal" ;;
45 esac 46 esac
46 ]) 47 ])
47 rm -f conftest.file]) 48 rm -f conftest.file])
diff --git a/m4/utimensat.m4 b/m4/utimensat.m4
index e595b333d17..4af7f6f81c8 100644
--- a/m4/utimensat.m4
+++ b/m4/utimensat.m4
@@ -1,4 +1,4 @@
1# serial 11 1# serial 12
2# See if we need to provide utimensat replacement. 2# See if we need to provide utimensat replacement.
3 3
4dnl Copyright (C) 2009-2024 Free Software Foundation, Inc. 4dnl Copyright (C) 2009-2024 Free Software Foundation, Inc.
@@ -83,6 +83,9 @@ AC_DEFUN([gl_FUNC_UTIMENSAT],
83 # Guess yes on Linux or glibc systems. 83 # Guess yes on Linux or glibc systems.
84 linux-* | linux | *-gnu* | gnu*) 84 linux-* | linux | *-gnu* | gnu*)
85 gl_cv_func_utimensat_works="guessing yes" ;; 85 gl_cv_func_utimensat_works="guessing yes" ;;
86 # Guess yes on systems that emulate the Linux system calls.
87 midipix*)
88 gl_cv_func_utimensat_works="guessing yes" ;;
86 # Guess 'nearly' on AIX. 89 # Guess 'nearly' on AIX.
87 aix*) 90 aix*)
88 gl_cv_func_utimensat_works="guessing nearly" ;; 91 gl_cv_func_utimensat_works="guessing nearly" ;;