Last week I wrote: > Since it's an alternative to lstat()/fstatat(), I put the declarations of > these functions into <sys/stat.h>, rather than creating a new .h file.
Unfortunately, this did not go well. On native Windows, this creates an include file chain wchar.h -> sys/stat.h -> unistd.h -> winsock2.h -> windows.h, and the latter defines - types such as WCHAR (that cause a compilation error in dfa.c), - macros such as DOMAIN (that cause a compilation error in GNU gettext). So, really, this include file chain needs to be cut: wchar.h -> sys/stat.h should NOT include <windows.h>, unistd.h -> winsock2.h -> windows.h is more or less unavoidable. This patch does it. 2025-08-18 Bruno Haible <[email protected]> sys_stat: Fix namespace pollution on native Windows. * lib/issymlink.h: New file, extracted from lib/sys_stat.in.h. * lib/issymlink.c: Include issymlink.h instead of <sys/stat.h>. * lib/issymlinkat.c: Likewise. * modules/issymlink (Files): Add lib/issymlink.h. (Depends-on): Add extern-inline. (configure.ac): Use gl_MODULE_INDICATOR. (Include): Set to "issymlink.h". * modules/issymlinkat (Files): Add lib/issymlink.h. (Depends-on): Add extern-inline. (configure.ac): Use gl_MODULE_INDICATOR. (Include): Set to "issymlink.h". * lib/sys_stat.in.h: Don't include <errno.h>, <unistd.h>. (_GL_ISSYMLINK_INLINE, _GL_ISSYMLINKAT_INLINE): Remove macros. (issymlink, issymlinkat): Remove functions. * m4/sys_stat_h.m4 (gl_SYS_STAT_H_REQUIRE_DEFAULTS): Don't initialize GNULIB_ISSYMLINK, GNULIB_ISSYMLINKAT. * modules/sys_stat-h (Depends-on): Remove extern-inline. (Makefile.am): Don't substitute GNULIB_ISSYMLINK, GNULIB_ISSYMLINKAT. * lib/chown.c: Include issymlink.h. * lib/lchown.c: Likewise. * lib/lchmod.c: Likewise. * lib/fchmodat.c: Likewise. * lib/rename.c: Likewise. * lib/renameatu.c: Likewise. * lib/unlink.c: Likewise. * lib/unlinkat.c: Likewise. * lib/utimens.c: Likewise. diff --git a/lib/chown.c b/lib/chown.c index 97cacb82da..d2af041e8e 100644 --- a/lib/chown.c +++ b/lib/chown.c @@ -28,6 +28,8 @@ #include <string.h> #include <sys/stat.h> +#include "issymlink.h" + #if !HAVE_CHOWN /* Simple stub that always fails with ENOSYS, for mingw. */ diff --git a/lib/fchmodat.c b/lib/fchmodat.c index 57222e58eb..06a20cc345 100644 --- a/lib/fchmodat.c +++ b/lib/fchmodat.c @@ -52,6 +52,8 @@ orig_fchmodat (int dir, char const *file, mode_t mode, int flags) #include <intprops.h> +#include "issymlink.h" + /* Invoke chmod or lchmod on FILE, using mode MODE, in the directory open on descriptor FD. If possible, do it without changing the working directory. Otherwise, resort to using save_cwd/fchdir, diff --git a/lib/issymlink.c b/lib/issymlink.c index fba06fff64..dbf56c037c 100644 --- a/lib/issymlink.c +++ b/lib/issymlink.c @@ -17,4 +17,4 @@ #include <config.h> #define _GL_ISSYMLINK_INLINE _GL_EXTERN_INLINE -#include <sys/stat.h> +#include "issymlink.h" diff --git a/lib/issymlink.h b/lib/issymlink.h new file mode 100644 index 0000000000..af6dc96564 --- /dev/null +++ b/lib/issymlink.h @@ -0,0 +1,103 @@ +/* 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 <https://www.gnu.org/licenses/>. */ + +#ifndef _ISSYMLINK_H +#define _ISSYMLINK_H + +/* This file uses _GL_ARG_NONNULL, _GL_INLINE. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + +#include <errno.h> +#include <unistd.h> /* for readlink, readlinkat */ + + +_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. + 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_ISSYMLINK_INLINE int issymlink (const char *filename) + _GL_ARG_NONNULL ((1)); +_GL_ISSYMLINK_INLINE int +issymlink (const char *filename) +{ + char linkbuf[1]; + if (readlink (filename, linkbuf, sizeof (linkbuf)) >= 0) + return 1; + if (errno == EINVAL) + return 0; + else + return -1; +} +# 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 + +#endif /* _ISSYMLINK_H */ diff --git a/lib/issymlinkat.c b/lib/issymlinkat.c index 924df455da..8286356c8a 100644 --- a/lib/issymlinkat.c +++ b/lib/issymlinkat.c @@ -17,4 +17,4 @@ #include <config.h> #define _GL_ISSYMLINKAT_INLINE _GL_EXTERN_INLINE -#include <sys/stat.h> +#include "issymlink.h" diff --git a/lib/lchmod.c b/lib/lchmod.c index 4c44ca8063..deba4c50f5 100644 --- a/lib/lchmod.c +++ b/lib/lchmod.c @@ -29,6 +29,7 @@ #include <unistd.h> #include <intprops.h> +#include "issymlink.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/lchown.c b/lib/lchown.c index e5e277101c..efcb23fe74 100644 --- a/lib/lchown.c +++ b/lib/lchown.c @@ -20,12 +20,15 @@ #include <config.h> +/* Specification. */ #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/stat.h> +#include "issymlink.h" + #if !HAVE_LCHOWN /* If the system chown does not follow symlinks, we don't want it diff --git a/lib/rename.c b/lib/rename.c index 7efd64f992..b365fc9b23 100644 --- a/lib/rename.c +++ b/lib/rename.c @@ -19,6 +19,7 @@ #include <config.h> +/* Specification. */ #include <stdio.h> #undef rename @@ -273,6 +274,7 @@ rpl_rename (char const *src, char const *dst) # include <unistd.h> # include "dirname.h" +# include "issymlink.h" # include "same-inode.h" /* Rename the file SRC to DST, fixing any trailing slash bugs. */ diff --git a/lib/renameatu.c b/lib/renameatu.c index 64a7f5285a..89653cb3e8 100644 --- a/lib/renameatu.c +++ b/lib/renameatu.c @@ -18,6 +18,7 @@ #include <config.h> +/* Specification. */ #include "renameatu.h" #include <errno.h> @@ -29,6 +30,8 @@ # include <sys/syscall.h> #endif +#include "issymlink.h" + static int errno_fail (int e) { diff --git a/lib/sys_stat.in.h b/lib/sys_stat.in.h index 1ffc255134..c3c38fd653 100644 --- a/lib/sys_stat.in.h +++ b/lib/sys_stat.in.h @@ -56,7 +56,7 @@ #define _@GUARD_PREFIX@_SYS_STAT_H /* This file uses _GL_ATTRIBUTE_NODISCARD, _GL_ATTRIBUTE_NOTHROW, - _GL_INLINE, GNULIB_POSIXCHECK, HAVE_RAW_DECL_*. */ + GNULIB_POSIXCHECK, HAVE_RAW_DECL_*. */ #if !_GL_CONFIG_H_INCLUDED #error "Please include config.h first." #endif @@ -91,16 +91,6 @@ /* The definition of _GL_WARN_ON_USE is copied here. */ -_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 - - /* Before doing "#define mknod rpl_mknod" below, we need to include all headers that may declare mknod(). OS/2 kLIBC declares mknod() in <unistd.h>, not in <sys/stat.h>. */ @@ -440,13 +430,6 @@ struct stat #endif -#if @GNULIB_ISSYMLINK@ || @GNULIB_ISSYMLINKAT@ -/* For the inline definitions of issymlink, issymlinkat below. */ -# include <errno.h> -# include <unistd.h> /* for readlink, readlinkat */ -#endif - - #if @GNULIB_CHMOD@ # if @REPLACE_CHMOD@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) @@ -643,71 +626,6 @@ _GL_WARN_ON_USE (getumask, "getumask is not portable - " #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. - 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_ISSYMLINK_INLINE int issymlink (const char *filename) - _GL_ARG_NONNULL ((1)); -_GL_ISSYMLINK_INLINE int -issymlink (const char *filename) -{ - char linkbuf[1]; - if (readlink (filename, linkbuf, sizeof (linkbuf)) >= 0) - return 1; - if (errno == EINVAL) - return 0; - else - return -1; -} -# 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 - - #if @GNULIB_LCHMOD@ /* Change the mode of FILENAME to MODE, without dereferencing it if FILENAME denotes a symbolic link. */ @@ -1089,8 +1007,6 @@ _GL_WARN_ON_USE (utimensat, "utimensat is not portable - " #endif -_GL_INLINE_HEADER_END - #endif /* _@GUARD_PREFIX@_SYS_STAT_H */ #endif /* _@GUARD_PREFIX@_SYS_STAT_H */ #endif diff --git a/lib/unlink.c b/lib/unlink.c index 9963ddf292..ae0c7fe5dc 100644 --- a/lib/unlink.c +++ b/lib/unlink.c @@ -17,6 +17,7 @@ #include <config.h> +/* Specification. */ #include <unistd.h> #include <errno.h> @@ -25,6 +26,7 @@ #include <sys/stat.h> #include "filename.h" +#include "issymlink.h" #undef unlink #if defined _WIN32 && !defined __CYGWIN__ diff --git a/lib/unlinkat.c b/lib/unlinkat.c index 847a379a91..3cf5fc5ea5 100644 --- a/lib/unlinkat.c +++ b/lib/unlinkat.c @@ -19,6 +19,7 @@ #include <config.h> +/* Specification. */ #include <unistd.h> #include <errno.h> @@ -29,6 +30,7 @@ #include <stdlib.h> #include "filename.h" +#include "issymlink.h" #include "openat.h" #if HAVE_UNLINKAT diff --git a/lib/utimens.c b/lib/utimens.c index 442a1800b9..0387e9f10e 100644 --- a/lib/utimens.c +++ b/lib/utimens.c @@ -21,6 +21,7 @@ #include <config.h> +/* Specification. */ #define _GL_UTIMENS_INLINE _GL_EXTERN_INLINE #include "utimens.h" @@ -32,6 +33,7 @@ #include <unistd.h> #include <utime.h> +#include "issymlink.h" #include "stat-time.h" #include "timespec.h" diff --git a/m4/sys_stat_h.m4 b/m4/sys_stat_h.m4 index a2dd603d63..10636923b1 100644 --- a/m4/sys_stat_h.m4 +++ b/m4/sys_stat_h.m4 @@ -1,5 +1,5 @@ # sys_stat_h.m4 -# serial 43 -*- Autoconf -*- +# serial 44 -*- Autoconf -*- dnl Copyright (C) 2006-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, @@ -80,8 +80,6 @@ AC_DEFUN([gl_SYS_STAT_H_REQUIRE_DEFAULTS] gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FSTATAT]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FUTIMENS]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_GETUMASK]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISSYMLINK]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISSYMLINKAT]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LCHMOD]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LSTAT]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MKDIR]) diff --git a/modules/issymlink b/modules/issymlink index d7977e8986..fcb98927cd 100644 --- a/modules/issymlink +++ b/modules/issymlink @@ -2,21 +2,23 @@ Description: Test whether a file is a symbolic link. Files: +lib/issymlink.h lib/issymlink.c Depends-on: +extern-inline sys_stat-h fcntl-h readlink configure.ac: -gl_SYS_STAT_MODULE_INDICATOR([issymlink]) +gl_MODULE_INDICATOR([issymlink]) Makefile.am: lib_SOURCES += issymlink.c Include: -<sys/stat.h> +"issymlink.h" License: LGPLv2+ diff --git a/modules/issymlinkat b/modules/issymlinkat index 15740ac1be..5833bafdfb 100644 --- a/modules/issymlinkat +++ b/modules/issymlinkat @@ -2,21 +2,23 @@ Description: Test whether a file is a symbolic link. Files: +lib/issymlink.h lib/issymlinkat.c Depends-on: +extern-inline sys_stat-h fcntl-h readlinkat configure.ac: -gl_SYS_STAT_MODULE_INDICATOR([issymlinkat]) +gl_MODULE_INDICATOR([issymlinkat]) Makefile.am: lib_SOURCES += issymlinkat.c Include: -<sys/stat.h> +"issymlink.h" License: GPL diff --git a/modules/sys_stat-h b/modules/sys_stat-h index 5257bad181..5552db8e00 100644 --- a/modules/sys_stat-h +++ b/modules/sys_stat-h @@ -13,7 +13,6 @@ include_next snippet/arg-nonnull snippet/c++defs snippet/warn-on-use -extern-inline sys_types-h time-h @@ -43,8 +42,6 @@ sys/stat.h: sys_stat.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNU -e 's/@''GNULIB_FSTATAT''@/$(GNULIB_FSTATAT)/g' \ -e 's/@''GNULIB_FUTIMENS''@/$(GNULIB_FUTIMENS)/g' \ -e 's/@''GNULIB_GETUMASK''@/$(GNULIB_GETUMASK)/g' \ - -e 's/@''GNULIB_ISSYMLINK''@/$(GNULIB_ISSYMLINK)/g' \ - -e 's/@''GNULIB_ISSYMLINKAT''@/$(GNULIB_ISSYMLINKAT)/g' \ -e 's/@''GNULIB_LCHMOD''@/$(GNULIB_LCHMOD)/g' \ -e 's/@''GNULIB_LSTAT''@/$(GNULIB_LSTAT)/g' \ -e 's/@''GNULIB_MKDIR''@/$(GNULIB_MKDIR)/g' \
