From b211e5489b95f39f07baa769fc581b05b393c4a3 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 7 Dec 2025 15:34:42 -0800 Subject: [PATCH] Update from Gnulib by running admin/merge-gnulib * admin/merge-gnulib (AVOIDED_MODULES): Add strncpy, as Emacs is not likely to exercise the FreeBSD 15 strncpy bug that this module fixes. * lib/issymlinkat.h: New file, taken from Gnulib. --- admin/merge-gnulib | 2 +- doc/misc/texinfo.tex | 46 ++++++++++++++--------- lib/attribute.h | 8 ++-- lib/dup2.c | 43 ++++++++++++++++------ lib/fchmodat.c | 2 +- lib/fcntl.c | 87 +++++++++++++++++++++++++++++++------------ lib/file-has-acl.c | 4 +- lib/free.c | 4 +- lib/getloadavg.c | 4 +- lib/gnulib.mk.in | 7 +++- lib/issymlink.h | 46 ++--------------------- lib/issymlinkat.c | 2 +- lib/issymlinkat.h | 68 ++++++++++++++++++++++++++++++++++ lib/lchmod.c | 1 + lib/realloc.c | 2 +- lib/str-two-way.h | 88 +++++++++++++++++++++++--------------------- lib/string.in.h | 29 +++++++++++++++ lib/time_rz.c | 4 +- m4/extern-inline.m4 | 2 +- m4/gnulib-common.m4 | 8 ++-- m4/gnulib-comp.m4 | 1 + m4/malloc.m4 | 63 ++++++++++++++++--------------- m4/realloc.m4 | 47 +++++++++++++++++++++-- m4/stdalign.m4 | 4 +- m4/string_h.m4 | 6 ++- 25 files changed, 383 insertions(+), 195 deletions(-) create mode 100644 lib/issymlinkat.h diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 892daa78033..34b1dda1d58 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -61,7 +61,7 @@ AVOIDED_MODULES=' localcharset locale-h localename-unsafe-limited lock mbrtowc mbsinit memchr mkdir msvc-inval msvc-nothrow nl_langinfo openat-die opendir pthread-h raise - save-cwd select setenv sigprocmask stat std-gnu11 stdarg-h + save-cwd select setenv sigprocmask stat std-gnu11 stdarg-h strncpy threadlib tzset unsetenv utime utime-h wchar-h wcrtomb wctype wctype-h ' diff --git a/doc/misc/texinfo.tex b/doc/misc/texinfo.tex index d432325279c..20e4f78b6c9 100644 --- a/doc/misc/texinfo.tex +++ b/doc/misc/texinfo.tex @@ -3,7 +3,7 @@ % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % -\def\texinfoversion{2025-10-25.20} +\def\texinfoversion{2025-12-06.19} % % Copyright 1985, 1986, 1988, 1990-2025 Free Software Foundation, Inc. % @@ -335,7 +335,7 @@ % % Make the heading and footing. \makeheadline and \makefootline % use the contents of \headline and \footline. - \def\commonheadfootline{\let\hsize=\txipagewidth \texinfochars} + \def\commonheadfootline{\let\hsize=\txipagewidth \texinfochars}% \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi \global\setbox\headlinebox = \vbox{\commonheadfootline \makeheadline}% \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi @@ -558,7 +558,13 @@ \def\:{\spacefactor=1000 } % @* forces a line break. -\def\*{\unskip\hfil\break\hbox{}\ignorespaces} +% In internal horizontal or vertical mode, convert explicit line breaks +% from @* into spaces. For instance, if the user gives these in long +% section titles, this may occur in the table of contents, or the page +% heading line. +\def\*{% + \ifinner\unskip\space\ignorespaces + \else\unskip\hfil\break\hbox{}\ignorespaces\fi} % @/ allows a line break. \let\/=\allowbreak @@ -5681,14 +5687,16 @@ \def\indexentry#1#2{% \let\entrypagetarget\empty - \ifpdforxetex - % only link the index text to the page if no comma appears in the - % list of pages, i.e. there is only one page - \checkpagelistcomma{#2}\pagelistcomma - \expandafter\ifcase\pagelistcomma - \def\entrypagetarget{#2}% - \fi - \fi% + \ifflagclear{txilinkindextext}{}{% + \ifpdforxetex + % link the index text to the page if no comma appears in the + % list of pages, i.e. there is only one page + \checkpagelistcomma{#2}\pagelistcomma + \expandafter\ifcase\pagelistcomma + \def\entrypagetarget{#2}% + \fi + \fi% + }% \entryinternal{#1}{#2}% } @@ -5783,9 +5791,10 @@ \def\indexrbrace{\}}% \def\indexbackslash{\realbackslash}% \def\indexatchar{\@}% - \writetocentry{idxinitial}{\asis #1}{IDX\the\idxinitialno}% + \writetocentry{idxinitial}{\asis #1\empty}{IDX\the\idxinitialno}% % The @asis removes a pair of braces around e.g. {@indexatchar} that - % are output by texindex. + % are output by texindex. \empty is an argument to \asis in case #1 + % is empty. % \pdfmkdest{idx.\asis #1.IDX\the\idxinitialno}% \fi @@ -5827,10 +5836,6 @@ % No extra space above this paragraph. \parskip = 0in % - % When reading the text of entry, convert explicit line breaks - % from @* into spaces. The user might give these in long section - % titles, for instance. - \def\*{\unskip\space\ignorespaces}% \def\entrybreak{\hfil\break}% An undocumented command % % Swallow the left brace of the text (first parameter): @@ -6191,10 +6196,17 @@ % and \thissection, as is done in \startcontents. \let\pchapsepmacro\relax \chapmacro{}{Yomitfromtoc}{}% + \gdef\thispart{#1}% \chapoddpage \endgroup } +% Name of current part, if any. +% Note: unlike @thischapter etc. defined below, the name of the part +% is always set before a page with a heading or footing is shipped out, +% so there is no need to keep the value of these in "marks". +\def\thispart{}% + % \unnumberedno is an oxymoron. But we count the unnumbered % sections so that we can refer to them unambiguously in the pdf % outlines by their "section number". We avoid collisions with chapter diff --git a/lib/attribute.h b/lib/attribute.h index c85412d90af..f24f5b18f1e 100644 --- a/lib/attribute.h +++ b/lib/attribute.h @@ -257,7 +257,7 @@ calls to the function with the same arguments, so long as the state addressed by its arguments is the same. This attribute is safe for a function that is effectless, idempotent, - stateless, and independent; see ISO C 23 § 6.7.12.7 for a definition of + stateless, and independent; see ISO C 23 § 6.7.13.8 for a definition of these terms. (This attribute is stricter than REPRODUCIBLE because the function must be stateless and independent. It is looser than ATTRIBUTE_CONST @@ -266,7 +266,7 @@ See also and . ATTENTION! Efforts are underway to change the meaning of this attribute. - See . */ + See . */ /* Applies to: functions, pointer to functions, function type. */ #define UNSEQUENCED _GL_ATTRIBUTE_UNSEQUENCED @@ -287,7 +287,7 @@ addressed by its arguments is the same and is updated in time for the rest of the program. This attribute is safe for a function that is effectless and idempotent; see - ISO C 23 § 6.7.12.7 for a definition of these terms. + ISO C 23 § 6.7.13.8 for a definition of these terms. (This attribute is looser than UNSEQUENCED because the function need not be stateless and idempotent. It is looser than ATTRIBUTE_PURE because the function need not return exactly once and can affect @@ -295,7 +295,7 @@ See also and . ATTENTION! Efforts are underway to change the meaning of this attribute. - See . */ + See . */ /* Applies to: functions, pointer to functions, function type. */ #define REPRODUCIBLE _GL_ATTRIBUTE_REPRODUCIBLE diff --git a/lib/dup2.c b/lib/dup2.c index 69b37196dee..434e78c2bc6 100644 --- a/lib/dup2.c +++ b/lib/dup2.c @@ -114,10 +114,10 @@ klibc_dup2dirfd (int fd, int desired_fd) int dupfd; tempfd = open ("NUL", O_RDONLY); - if (tempfd == -1) - return -1; + if (tempfd < 0) + return tempfd; - if (tempfd == desired_fd) + if (tempfd >= desired_fd) { close (tempfd); @@ -125,7 +125,29 @@ klibc_dup2dirfd (int fd, int desired_fd) if (__libc_Back_ioFHToPath (fd, path, sizeof (path))) return -1; - return open(path, O_RDONLY); + for (;;) + { + close (desired_fd); + + dupfd = open (path, O_RDONLY); + if (dupfd < 0) + return dupfd; + + if (dupfd == desired_fd) + return dupfd; + + /* If lower FD was closed by other threads, fill again. */ + if (dupfd < desired_fd) + { + tempfd = dupfd; + break; + } + + /* desired_fd was opened by other threads. Try again. */ + /* FIXME: Closing desired_fd opened by other threads may lead to + unexpected behavior. */ + close (dupfd); + } } dupfd = klibc_dup2dirfd (fd, desired_fd); @@ -142,13 +164,9 @@ klibc_dup2 (int fd, int desired_fd) struct stat sbuf; dupfd = dup2 (fd, desired_fd); - if (dupfd == -1 && errno == ENOTSUP \ + if (dupfd < 0 && errno == ENOTSUP \ && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) - { - close (desired_fd); - - return klibc_dup2dirfd (fd, desired_fd); - } + return klibc_dup2dirfd (fd, desired_fd); return dupfd; } @@ -179,10 +197,11 @@ rpl_dup2 (int fd, int desired_fd) result = dup2 (fd, desired_fd); /* Correct an errno value on FreeBSD 6.1 and Cygwin 1.5.x. */ - if (result == -1 && errno == EMFILE) + if (result < 0 && errno == EMFILE) errno = EBADF; + #if REPLACE_FCHDIR - if (fd != desired_fd && result != -1) + if (! (result < 0 || fd == desired_fd)) result = _gl_register_dup (fd, result); #endif return result; diff --git a/lib/fchmodat.c b/lib/fchmodat.c index 9151778b8e3..4aa764f8510 100644 --- a/lib/fchmodat.c +++ b/lib/fchmodat.c @@ -46,7 +46,7 @@ orig_fchmodat (int dir, char const *file, mode_t mode, int flags) #include -#include "issymlink.h" +#include "issymlinkat.h" /* Invoke chmod or lchmod on FILE, using mode MODE, in the directory open on descriptor FD. If possible, do it without changing the diff --git a/lib/fcntl.c b/lib/fcntl.c index 29cb4f9eb84..abdd7b89890 100644 --- a/lib/fcntl.c +++ b/lib/fcntl.c @@ -29,8 +29,8 @@ #include #ifdef __KLIBC__ -# define INCL_DOS -# include +# include +# include #endif #if defined _WIN32 && ! defined __CYGWIN__ @@ -539,6 +539,41 @@ rpl_fcntl_DUPFD_CLOEXEC (int fd, int target) #undef fcntl #ifdef __KLIBC__ +static int +klibc_dupdirfd (int fd, int minfd) +{ + int tempfd; + int dupfd; + + tempfd = open ("NUL", O_RDONLY); + if (tempfd == -1) + return -1; + + if (tempfd >= minfd) + { + close (tempfd); + + char path[_MAX_PATH]; + if (__libc_Back_ioFHToPath (fd, path, sizeof (path))) + return -1; + + dupfd = open (path, O_RDONLY); + if (dupfd == -1) + return -1; + + if (dupfd >= minfd) + return dupfd; + + /* Lower FD was closed by other threads. Fill again. */ + tempfd = dupfd; + } + + dupfd = klibc_dupdirfd (fd, minfd); + + close (tempfd); + + return dupfd; +} static int klibc_fcntl (int fd, int action, /* arg */...) @@ -555,46 +590,50 @@ klibc_fcntl (int fd, int action, /* arg */...) if (result == -1 && (errno == EPERM || errno == ENOTSUP) && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) { - ULONG ulMode; + PLIBCFH pFH; + unsigned fFlags; switch (action) { case F_DUPFD: - /* Find available fd */ - while (fcntl (arg, F_GETFL) != -1 || errno != EBADF) - arg++; - - result = dup2 (fd, arg); + result = klibc_dupdirfd (fd, arg); break; - /* Using underlying APIs is right ? */ case F_GETFD: - if (DosQueryFHState (fd, &ulMode)) - break; + pFH = __libc_FH (fd); + if (!pFH) + { + errno = EBADF; + break; + } - result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0; + result = (pFH->fFlags & ((FD_CLOEXEC << __LIBC_FH_FDFLAGS_SHIFT ) + | O_NOINHERIT)) ? FD_CLOEXEC : 0; break; case F_SETFD: if (arg & ~FD_CLOEXEC) break; - if (DosQueryFHState (fd, &ulMode)) - break; + pFH = __libc_FH (fd); + if (!pFH) + { + errno = EBADF; + break; + } + fFlags = pFH->fFlags; if (arg & FD_CLOEXEC) - ulMode |= OPEN_FLAGS_NOINHERIT; + fFlags |= (FD_CLOEXEC << __LIBC_FH_FDFLAGS_SHIFT) | O_NOINHERIT; else - ulMode &= ~OPEN_FLAGS_NOINHERIT; + fFlags &= ~((FD_CLOEXEC << __LIBC_FH_FDFLAGS_SHIFT) | O_NOINHERIT); - /* Filter supported flags. */ - ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR - | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT); - - if (DosSetFHState (fd, ulMode)) - break; - - result = 0; + result = __libc_FHSetFlags (pFH, fd, fFlags); + if (result < 0) + { + errno = -result; + result = -1; + } break; case F_GETFL: diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c index b1eb1f2e8e7..9a5a18aabcf 100644 --- a/lib/file-has-acl.c +++ b/lib/file-has-acl.c @@ -399,9 +399,9 @@ acl_get_link_np (char const *name, acl_type_t type) if (fd < 0) return NULL; acl_t r = acl_get_fd (fd); - int err = errno; + int saved_errno = errno; close (fd); - errno = err; + errno = saved_errno; return r; } # define HAVE_ACL_GET_LINK_NP 1 diff --git a/lib/free.c b/lib/free.c index 394d8d13905..3a6577f13fa 100644 --- a/lib/free.c +++ b/lib/free.c @@ -44,9 +44,9 @@ rpl_free (void *p) free (p); errno = err[errno == 0]; # else - int err = errno; + int saved_errno = errno; free (p); - errno = err; + errno = saved_errno; # endif } diff --git a/lib/getloadavg.c b/lib/getloadavg.c index f9ed97b04bd..ec7305f5d48 100644 --- a/lib/getloadavg.c +++ b/lib/getloadavg.c @@ -551,11 +551,11 @@ getloadavg (double loadavg[], int nelem) if (fd < 0) return fd; int nread = read (fd, readbuf, sizeof readbuf - 1); - int err = errno; + int saved_errno = errno; close (fd); if (nread < 0) { - errno = err; + errno = saved_errno; return -1; } readbuf[nread] = '\0'; diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 96d9cd8bd44..297af181c57 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -71,6 +71,7 @@ # --avoid=stat \ # --avoid=std-gnu11 \ # --avoid=stdarg-h \ +# --avoid=strncpy \ # --avoid=threadlib \ # --avoid=tzset \ # --avoid=unsetenv \ @@ -631,6 +632,7 @@ GL_GNULIB_STRERROR_R = @GL_GNULIB_STRERROR_R@ GL_GNULIB_STRFTIME = @GL_GNULIB_STRFTIME@ GL_GNULIB_STRINGEQ = @GL_GNULIB_STRINGEQ@ GL_GNULIB_STRNCAT = @GL_GNULIB_STRNCAT@ +GL_GNULIB_STRNCPY = @GL_GNULIB_STRNCPY@ GL_GNULIB_STRNDUP = @GL_GNULIB_STRNDUP@ GL_GNULIB_STRNLEN = @GL_GNULIB_STRNLEN@ GL_GNULIB_STRPBRK = @GL_GNULIB_STRPBRK@ @@ -1327,6 +1329,7 @@ REPLACE_STRERROR_L = @REPLACE_STRERROR_L@ REPLACE_STRERROR_R = @REPLACE_STRERROR_R@ REPLACE_STRFTIME = @REPLACE_STRFTIME@ REPLACE_STRNCAT = @REPLACE_STRNCAT@ +REPLACE_STRNCPY = @REPLACE_STRNCPY@ REPLACE_STRNDUP = @REPLACE_STRNDUP@ REPLACE_STRNLEN = @REPLACE_STRNLEN@ REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@ @@ -2650,7 +2653,7 @@ ifneq (,$(gl_GNULIB_ENABLED_issymlinkat_CONDITION)) libgnu_a_SOURCES += issymlinkat.c endif -EXTRA_DIST += issymlink.h +EXTRA_DIST += issymlinkat.h endif ## end gnulib module issymlinkat @@ -3757,6 +3760,7 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's/@''GNULIB_STRDUP''@/$(GL_GNULIB_STRDUP)/g' \ -e 's/@''GNULIB_STRINGEQ''@/$(GL_GNULIB_STRINGEQ)/g' \ -e 's/@''GNULIB_STRNCAT''@/$(GL_GNULIB_STRNCAT)/g' \ + -e 's/@''GNULIB_STRNCPY''@/$(GL_GNULIB_STRNCPY)/g' \ -e 's/@''GNULIB_STRNDUP''@/$(GL_GNULIB_STRNDUP)/g' \ -e 's/@''GNULIB_STRNLEN''@/$(GL_GNULIB_STRNLEN)/g' \ -e 's/@''GNULIB_STRPBRK''@/$(GL_GNULIB_STRPBRK)/g' \ @@ -3818,6 +3822,7 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's|@''REPLACE_STRCHRNUL''@|$(REPLACE_STRCHRNUL)|g' \ -e 's|@''REPLACE_STRDUP''@|$(REPLACE_STRDUP)|g' \ -e 's|@''REPLACE_STRNCAT''@|$(REPLACE_STRNCAT)|g' \ + -e 's|@''REPLACE_STRNCPY''@|$(REPLACE_STRNCPY)|g' \ -e 's|@''REPLACE_STRNDUP''@|$(REPLACE_STRNDUP)|g' \ -e 's|@''REPLACE_STRNLEN''@|$(REPLACE_STRNLEN)|g' \ -e 's|@''REPLACE_STRSTR''@|$(REPLACE_STRSTR)|g' \ diff --git a/lib/issymlink.h b/lib/issymlink.h index af6dc965645..a3f97cb5211 100644 --- a/lib/issymlink.h +++ b/lib/issymlink.h @@ -23,7 +23,7 @@ #endif #include -#include /* for readlink, readlinkat */ +#include /* for readlink */ _GL_INLINE_HEADER_BEGIN @@ -31,11 +31,7 @@ _GL_INLINE_HEADER_BEGIN #ifndef _GL_ISSYMLINK_INLINE # define _GL_ISSYMLINK_INLINE _GL_INLINE #endif -#ifndef _GL_ISSYMLINKAT_INLINE -# define _GL_ISSYMLINKAT_INLINE _GL_INLINE -#endif -#if GNULIB_ISSYMLINK /* Tests whether FILENAME represents a symbolic link. This function is more reliable than lstat() / fstatat() followed by S_ISLNK, because it avoids possible EOVERFLOW errors. @@ -44,9 +40,9 @@ _GL_INLINE_HEADER_BEGIN 0 if FILENAME exists and is not a symbolic link, -1 with errno set if determination failed, in particular -1 with errno = ENOENT or ENOTDIR if FILENAME does not exist. */ -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif _GL_ISSYMLINK_INLINE int issymlink (const char *filename) _GL_ARG_NONNULL ((1)); _GL_ISSYMLINK_INLINE int @@ -60,42 +56,8 @@ issymlink (const char *filename) else return -1; } -# ifdef __cplusplus +#ifdef __cplusplus } -# endif -#endif - -#if GNULIB_ISSYMLINKAT -/* Tests whether FILENAME represents a symbolic link. - This function is more reliable than lstat() / fstatat() followed by S_ISLNK, - because it avoids possible EOVERFLOW errors. - If FILENAME is a relative file name, it is interpreted as relative to the - directory referred to by FD (where FD = AT_FDCWD denotes the current - directory). - Returns - 1 if FILENAME is a symbolic link, - 0 if FILENAME exists and is not a symbolic link, - -1 with errno set if determination failed, in particular - -1 with errno = ENOENT or ENOTDIR if FILENAME does not exist. */ -# ifdef __cplusplus -extern "C" { -# endif -_GL_ISSYMLINKAT_INLINE int issymlinkat (int fd, const char *filename) - _GL_ARG_NONNULL ((2)); -_GL_ISSYMLINKAT_INLINE int -issymlinkat (int fd, const char *filename) -{ - char linkbuf[1]; - if (readlinkat (fd, filename, linkbuf, sizeof (linkbuf)) >= 0) - return 1; - if (errno == EINVAL) - return 0; - else - return -1; -} -# ifdef __cplusplus -} -# endif #endif _GL_INLINE_HEADER_END diff --git a/lib/issymlinkat.c b/lib/issymlinkat.c index 8286356c8a2..ebd937e91bc 100644 --- a/lib/issymlinkat.c +++ b/lib/issymlinkat.c @@ -17,4 +17,4 @@ #include #define _GL_ISSYMLINKAT_INLINE _GL_EXTERN_INLINE -#include "issymlink.h" +#include "issymlinkat.h" diff --git a/lib/issymlinkat.h b/lib/issymlinkat.h new file mode 100644 index 00000000000..abbbbfff837 --- /dev/null +++ b/lib/issymlinkat.h @@ -0,0 +1,68 @@ +/* Test whether a file is a symbolic link. + Copyright (C) 2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +#ifndef _ISSYMLINKAT_H +#define _ISSYMLINKAT_H + +/* This file uses _GL_ARG_NONNULL, _GL_INLINE. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + +#include +#include /* for readlinkat */ + + +_GL_INLINE_HEADER_BEGIN + +#ifndef _GL_ISSYMLINKAT_INLINE +# define _GL_ISSYMLINKAT_INLINE _GL_INLINE +#endif + +/* Tests whether FILENAME represents a symbolic link. + This function is more reliable than lstat() / fstatat() followed by S_ISLNK, + because it avoids possible EOVERFLOW errors. + If FILENAME is a relative file name, it is interpreted as relative to the + directory referred to by FD (where FD = AT_FDCWD denotes the current + directory). + Returns + 1 if FILENAME is a symbolic link, + 0 if FILENAME exists and is not a symbolic link, + -1 with errno set if determination failed, in particular + -1 with errno = ENOENT or ENOTDIR if FILENAME does not exist. */ +#ifdef __cplusplus +extern "C" { +#endif +_GL_ISSYMLINKAT_INLINE int issymlinkat (int fd, const char *filename) + _GL_ARG_NONNULL ((2)); +_GL_ISSYMLINKAT_INLINE int +issymlinkat (int fd, const char *filename) +{ + char linkbuf[1]; + if (readlinkat (fd, filename, linkbuf, sizeof (linkbuf)) >= 0) + return 1; + if (errno == EINVAL) + return 0; + else + return -1; +} +#ifdef __cplusplus +} +#endif + +_GL_INLINE_HEADER_END + +#endif /* _ISSYMLINKAT_H */ diff --git a/lib/lchmod.c b/lib/lchmod.c index deba4c50f5b..acccef629cc 100644 --- a/lib/lchmod.c +++ b/lib/lchmod.c @@ -30,6 +30,7 @@ #include #include "issymlink.h" +#include "issymlinkat.h" /* Work like chmod, except when FILE is a symbolic link. In that case, on systems where permissions on symbolic links are unsupported diff --git a/lib/realloc.c b/lib/realloc.c index 62efd5a39ff..cf0bf6e9f70 100644 --- a/lib/realloc.c +++ b/lib/realloc.c @@ -94,7 +94,7 @@ rpl_realloc (void *p, size_t n) void *result = realloc (p, n1); -# if !HAVE_MALLOC_POSIX +# if !HAVE_REALLOC_POSIX if (result == NULL) errno = ENOMEM; # endif diff --git a/lib/str-two-way.h b/lib/str-two-way.h index 852e5d078f2..1da773eb70e 100644 --- a/lib/str-two-way.h +++ b/lib/str-two-way.h @@ -374,32 +374,34 @@ two_way_long_needle (const unsigned char *haystack, size_t haystack_len, } memory = 0; j += shift; - continue; - } - /* Scan for matches in right half. The last byte has - already been matched, by virtue of the shift table. */ - size_t i = MAX (suffix, memory); - while (i < needle_len - 1 && (CANON_ELEMENT (needle[i]) - == CANON_ELEMENT (haystack[i + j]))) - ++i; - if (needle_len - 1 <= i) - { - /* Scan for matches in left half. */ - i = suffix - 1; - while (memory < i + 1 && (CANON_ELEMENT (needle[i]) - == CANON_ELEMENT (haystack[i + j]))) - --i; - if (i + 1 < memory + 1) - return (RETURN_TYPE) (haystack + j); - /* No match, so remember how many repetitions of period - on the right half were scanned. */ - j += period; - memory = needle_len - period; } else { - j += i - suffix + 1; - memory = 0; + /* Scan for matches in right half. The last byte has + already been matched, by virtue of the shift table. */ + size_t i = MAX (suffix, memory); + while (i < needle_len - 1 && (CANON_ELEMENT (needle[i]) + == CANON_ELEMENT (haystack[i + j]))) + ++i; + if (needle_len - 1 <= i) + { + /* Scan for matches in left half. */ + i = suffix - 1; + while (memory < i + 1 && (CANON_ELEMENT (needle[i]) + == CANON_ELEMENT (haystack[i + j]))) + --i; + if (i + 1 < memory + 1) + return (RETURN_TYPE) (haystack + j); + /* No match, so remember how many repetitions of period + on the right half were scanned. */ + j += period; + memory = needle_len - period; + } + else + { + j += i - suffix + 1; + memory = 0; + } } } } @@ -418,27 +420,29 @@ two_way_long_needle (const unsigned char *haystack, size_t haystack_len, if (0 < shift) { j += shift; - continue; - } - /* Scan for matches in right half. The last byte has - already been matched, by virtue of the shift table. */ - size_t i = suffix; - while (i < needle_len - 1 && (CANON_ELEMENT (needle[i]) - == CANON_ELEMENT (haystack[i + j]))) - ++i; - if (needle_len - 1 <= i) - { - /* Scan for matches in left half. */ - i = suffix - 1; - while (i != SIZE_MAX && (CANON_ELEMENT (needle[i]) - == CANON_ELEMENT (haystack[i + j]))) - --i; - if (i == SIZE_MAX) - return (RETURN_TYPE) (haystack + j); - j += period; } else - j += i - suffix + 1; + { + /* Scan for matches in right half. The last byte has + already been matched, by virtue of the shift table. */ + size_t i = suffix; + while (i < needle_len - 1 && (CANON_ELEMENT (needle[i]) + == CANON_ELEMENT (haystack[i + j]))) + ++i; + if (needle_len - 1 <= i) + { + /* Scan for matches in left half. */ + i = suffix - 1; + while (i != SIZE_MAX && (CANON_ELEMENT (needle[i]) + == CANON_ELEMENT (haystack[i + j]))) + --i; + if (i == SIZE_MAX) + return (RETURN_TYPE) (haystack + j); + j += period; + } + else + j += i - suffix + 1; + } } } return NULL; diff --git a/lib/string.in.h b/lib/string.in.h index a323b1cd6da..f316878b4c9 100644 --- a/lib/string.in.h +++ b/lib/string.in.h @@ -840,6 +840,35 @@ _GL_WARN_ON_USE (strncat, "strncat is unportable - " # endif #endif +/* Copy no more than N bytes of SRC to DST, returning DST. */ +#if @GNULIB_STRNCPY@ +# if @REPLACE_STRNCPY@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef strncpy +# define strncpy rpl_strncpy +# endif +_GL_FUNCDECL_RPL (strncpy, char *, + (char *restrict __dst, char const *restrict __src, + size_t __n), + _GL_ARG_NONNULL ((1, 2))); +_GL_CXXALIAS_RPL (strncpy, char *, + (char *restrict __dst, char const *restrict __src, + size_t __n)); +# else +_GL_CXXALIAS_SYS (strncpy, char *, + (char *restrict __dst, char const *restrict __src, + size_t __n)); +# endif +# if __GLIBC__ >= 2 +_GL_CXXALIASWARN (strncpy); +# endif +#elif defined GNULIB_POSIXCHECK +# if HAVE_RAW_DECL_STRNCPY +_GL_WARN_ON_USE (strncpy, "strncpy is unportable - " + "use gnulib module strncpy for portability"); +# endif +#endif + /* Return a newly allocated copy of at most N bytes of STRING. */ #if @GNULIB_STRNDUP@ # if @REPLACE_STRNDUP@ diff --git a/lib/time_rz.c b/lib/time_rz.c index 8a8eb44c357..116ac39ad2d 100644 --- a/lib/time_rz.c +++ b/lib/time_rz.c @@ -57,9 +57,9 @@ void tzfree (timezone_t tz) # undef tzfree { - int err = errno; + int saved_errno = errno; tzfree (tz); - errno = err; + errno = saved_errno; } #else diff --git a/m4/extern-inline.m4 b/m4/extern-inline.m4 index 820fbda5ddb..7a63df0340e 100644 --- a/m4/extern-inline.m4 +++ b/m4/extern-inline.m4 @@ -44,7 +44,7 @@ AC_DEFUN([gl_EXTERN_INLINE], if isdigit is mistakenly implemented via a static inline function, a program containing an extern inline function that calls isdigit may not work since C99 through C23 prohibit extern inline functions - from calling static functions (ISO C 23 section 6.7.5.(2)). + from calling static functions (ISO C 23 § 6.7.5 ¶ 3)). Although a future C standard will likely relax this restriction , respect it for now. This bug is known to occur on: diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4 index 73f56f57fc9..5bb06bed4d3 100644 --- a/m4/gnulib-common.m4 +++ b/m4/gnulib-common.m4 @@ -768,7 +768,7 @@ AC_DEFUN([gl_COMMON_BODY], [ addressed by its arguments is the same and is updated in time for the rest of the program. This attribute is safe for a function that is effectless and idempotent; see - ISO C 23 § 6.7.12.7 for a definition of these terms. + ISO C 23 § 6.7.13.8 for a definition of these terms. (This attribute is looser than _GL_ATTRIBUTE_UNSEQUENCED because the function need not be stateless and idempotent. It is looser than _GL_ATTRIBUTE_PURE because the function need not return @@ -776,7 +776,7 @@ AC_DEFUN([gl_COMMON_BODY], [ See also and . ATTENTION! Efforts are underway to change the meaning of this attribute. - See . */ + See . */ /* Applies to: functions, pointer to functions, function types. */ #ifndef _GL_ATTRIBUTE_REPRODUCIBLE /* This may be revisited when gcc and clang support [[reproducible]] or possibly @@ -820,7 +820,7 @@ AC_DEFUN([gl_COMMON_BODY], [ calls to the function with the same arguments, so long as the state addressed by its arguments is the same. This attribute is safe for a function that is effectless, idempotent, - stateless, and independent; see ISO C 23 § 6.7.12.7 for a definition of + stateless, and independent; see ISO C 23 § 6.7.13.8 for a definition of these terms. (This attribute is stricter than _GL_ATTRIBUTE_REPRODUCIBLE because the function must be stateless and independent. It is looser than @@ -829,7 +829,7 @@ AC_DEFUN([gl_COMMON_BODY], [ See also and . ATTENTION! Efforts are underway to change the meaning of this attribute. - See . */ + See . */ /* Applies to: functions, pointer to functions, function types. */ #ifndef _GL_ATTRIBUTE_UNSEQUENCED /* This may be revisited when gcc and clang support [[unsequenced]] or possibly diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 56f1a603ebc..47391071a7c 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -1383,6 +1383,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/issymlink.c lib/issymlink.h lib/issymlinkat.c + lib/issymlinkat.h lib/lchmod.c lib/libc-config.h lib/limits.in.h diff --git a/m4/malloc.m4 b/m4/malloc.m4 index 688594fe661..c49c91e0404 100644 --- a/m4/malloc.m4 +++ b/m4/malloc.m4 @@ -1,5 +1,5 @@ # malloc.m4 -# serial 44 +# serial 46 dnl Copyright (C) 2007, 2009-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -99,7 +99,7 @@ AC_DEFUN([gl_FUNC_MALLOC_PTRDIFF], [REPLACE_MALLOC_FOR_MALLOC_POSIX=1]) ]) -# Test whether malloc, realloc, calloc refuse to create objects +# Test whether malloc, calloc refuse to create objects # larger than what can be expressed in ptrdiff_t. # Set gl_cv_func_malloc_gnu. AC_DEFUN([gl_CHECK_MALLOC_PTRDIFF], @@ -148,7 +148,7 @@ AC_DEFUN([gl_FUNC_MALLOC_POSIX], case "$gl_cv_func_malloc_posix" in *yes) AC_DEFINE([HAVE_MALLOC_POSIX], [1], - [Define if malloc, realloc, and calloc set errno on allocation failure.]) + [Define if malloc and calloc set errno on allocation failure.]) ;; *) REPLACE_MALLOC_FOR_MALLOC_POSIX=1 @@ -156,19 +156,20 @@ AC_DEFUN([gl_FUNC_MALLOC_POSIX], esac ]) -# Test whether malloc, realloc, calloc set errno to ENOMEM on failure. +# Test whether malloc, calloc set errno to ENOMEM on failure. # Set gl_cv_func_malloc_posix to *yes or *no accordingly. AC_DEFUN([gl_CHECK_MALLOC_POSIX], [ AC_REQUIRE([AC_CANONICAL_HOST]) - AC_CACHE_CHECK([whether malloc, realloc, calloc set errno on failure], + AC_CACHE_CHECK([whether malloc, calloc set errno on failure], [gl_cv_func_malloc_posix], [ dnl It is too dangerous to try to allocate a large amount of memory: dnl some systems go to their knees when you do that. So assume that dnl all Unix implementations of the function set errno on failure, dnl except on those platforms where we have seen 'test-malloc-gnu', - dnl 'test-realloc-posix', 'test-calloc-gnu' fail. + dnl 'test-realloc-posix', 'test-calloc-gnu' fail. For platforms + dnl where only 'test-realloc-posix', see realloc.m4. case "$host_os" in mingw* | windows*) dnl Old MSVCRT from 2001 did not set errno=ENOMEM when malloc failed. @@ -187,37 +188,41 @@ AC_DEFUN([gl_CHECK_MALLOC_POSIX], [gl_cv_func_malloc_posix="guessing no"]) ;; solaris*) - dnl On Solaris 11.3, the three functions return NULL with errno set + dnl On Solaris 11.3, the three functions might fail with errno set dnl to EAGAIN, not ENOMEM, when the argument is larger than - dnl PTRDIFF_MAX. + dnl PTRDIFF_MAX. See: + dnl https://lists.gnu.org/r/bug-gnulib/2021-05/msg00052.html dnl Here is a test program: + m4_divert_push([KILL]) #include +#include #include +#include #include -#define ptrdiff_t long -#ifndef PTRDIFF_MAX -# define PTRDIFF_MAX ((ptrdiff_t) ((1UL << (8 * sizeof (ptrdiff_t) - 1)) - 1)) -#endif -int main () +#define TEST_CALL(call) \ + do { \ + void *p = call; \ + if (p) \ + fprintf (stderr, "returned %p (incorrect success)\n", p); \ + else if (errno == ENOMEM) \ + perror ("correct failure"); \ + else \ + perror ("incorrect failure (wrong errno)"); \ + free (p); \ + } while (0) + +int +main () { - void *p; - - fprintf (stderr, "PTRDIFF_MAX = %lu\n", (unsigned long) PTRDIFF_MAX); - - errno = 0; - p = malloc ((unsigned long) PTRDIFF_MAX + 1); - fprintf (stderr, "p=%p errno=%d\n", p, errno); - - errno = 0; - p = calloc (PTRDIFF_MAX / 2 + 1, 2); - fprintf (stderr, "p=%p errno=%d\n", p, errno); - - errno = 0; - p = realloc (NULL, (unsigned long) PTRDIFF_MAX + 1); - fprintf (stderr, "p=%p errno=%d\n", p, errno); - + size_t big = PTRDIFF_MAX; + TEST_CALL (malloc (big + 1)); + TEST_CALL (calloc (big / 2 + 1, 2)); + TEST_CALL (realloc (NULL, big + 1)); + void *small = malloc (1); + TEST_CALL (realloc (small, big + 1)); + free (small); return 0; } m4_divert_pop([KILL]) diff --git a/m4/realloc.m4 b/m4/realloc.m4 index 0242abdc8cc..a6c2ec4c33b 100644 --- a/m4/realloc.m4 +++ b/m4/realloc.m4 @@ -1,5 +1,5 @@ # realloc.m4 -# serial 39 +# serial 40 dnl Copyright (C) 2007, 2009-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -27,13 +27,54 @@ AC_DEFUN([gl_FUNC_REALLOC_POSIX], [ AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) AC_REQUIRE([gl_FUNC_MALLOC_POSIX]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_CACHE_CHECK([whether realloc sets errno on failure], + [gl_cv_func_realloc_posix], + [ + dnl FreeBSD 15.0 realloc() does not set errno when asked for more than + dnl 0x7000000000000000 bytes. + case "$host_os" in + darwin* | freebsd* | dragonfly* | midnightbsd* | netbsd* | openbsd*) + AC_RUN_IFELSE( + [AC_LANG_SOURCE( + [[#include + #include + int main (int argc, char **argv) + { + void *p; + errno = 1729; + p = realloc (malloc (1), (size_t)(-1) / 100 * 49); + return (!p && errno == 1729); + } + ]]) + ], + [gl_cv_func_realloc_posix=yes], + [gl_cv_func_realloc_posix=no], + [case "$host_os" in + freebsd*) gl_cv_func_realloc_posix="guessing no" ;; + *) gl_cv_func_realloc_posix="guessing yes" ;; + esac + ]) + ;; + *) + gl_cv_func_realloc_posix="$gl_cv_func_malloc_posix" + ;; + esac + ]) + case "$gl_cv_func_realloc_posix" in + *yes) + AC_DEFINE([HAVE_REALLOC_POSIX], [1], + [Define if realloc sets errno on allocation failure.]) + ;; + *) + REPLACE_REALLOC_FOR_REALLOC_POSIX=1 + ;; + esac AC_REQUIRE([gl_FUNC_REALLOC_SANITIZED]) if test "$gl_cv_func_realloc_sanitize" != no; then REPLACE_REALLOC_FOR_REALLOC_POSIX=1 AC_DEFINE([NEED_SANITIZED_REALLOC], [1], [Define to 1 if realloc should abort upon undefined behaviour.]) - else - REPLACE_REALLOC_FOR_REALLOC_POSIX=$REPLACE_MALLOC_FOR_MALLOC_POSIX fi ]) diff --git a/m4/stdalign.m4 b/m4/stdalign.m4 index d22360e1075..d4a7d92961c 100644 --- a/m4/stdalign.m4 +++ b/m4/stdalign.m4 @@ -82,8 +82,8 @@ AC_DEFUN([gl_ALIGNASOF], References: ISO C23 (latest free draft - ) - sections 6.5.3.4, 6.7.5, 7.15. + ) + sections 6.2.8, 6.7.6. C++11 (latest free draft ) section 18.10. */ diff --git a/m4/string_h.m4 b/m4/string_h.m4 index b5324e3a37e..860440123ae 100644 --- a/m4/string_h.m4 +++ b/m4/string_h.m4 @@ -1,5 +1,5 @@ # string_h.m4 -# serial 45 +# serial 46 dnl Copyright (C) 2007-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -24,7 +24,7 @@ AC_DEFUN_ONCE([gl_STRING_H], ]], [explicit_bzero ffsl ffsll memmem mempcpy memrchr memset_explicit rawmemchr stpcpy stpncpy strchrnul - strdup strncat strndup strnlen strpbrk strsep strcasestr strtok_r + strdup strncat strncpy strndup strnlen strpbrk strsep strcasestr strtok_r strerror_l strerror_r strerrorname_np sigabbrev_np sigdescr_np strsignal strverscmp]) @@ -65,6 +65,7 @@ AC_DEFUN([gl_STRING_H_REQUIRE_DEFAULTS], gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRDUP]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRINGEQ]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRNCAT]) + gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRNCPY]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRNDUP]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRNLEN]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRPBRK]) @@ -149,6 +150,7 @@ AC_DEFUN([gl_STRING_H_DEFAULTS], REPLACE_STRCHRNUL=0; AC_SUBST([REPLACE_STRCHRNUL]) REPLACE_STRDUP=0; AC_SUBST([REPLACE_STRDUP]) REPLACE_STRNCAT=0; AC_SUBST([REPLACE_STRNCAT]) + REPLACE_STRNCPY=0; AC_SUBST([REPLACE_STRNCPY]) REPLACE_STRNDUP=0; AC_SUBST([REPLACE_STRNDUP]) REPLACE_STRNLEN=0; AC_SUBST([REPLACE_STRNLEN]) REPLACE_STRSTR=0; AC_SUBST([REPLACE_STRSTR])