Collin Funk wrote: > Unrelated to the original patch, but something I noticed... > > > +#if defined _WIN32 && !defined __CYGWIN__ > > + fd = open ("NUL", O_RDWR); > > +#else > > + fd = open ("/dev/null", O_RDWR); > > +#endif > > Perhaps it is worth adding a macro for this file?
Good point. In fact, it's better to support "/dev/null" also on Windows: * The main principle behind Gnulib is to present a POSIX/GNU façade to applications on all platforms. * File names are special, because on native Windows, the syntax of 'C:\' is not compatible with POSIX. But "/dev/null" is used as an idiom, not really as a file name. (No one cares about which directory "/dev/null" sits in.) * It is useful if scripts can use the command 'msgfmt -c -o /dev/null foo.po' also on native Windows. No need to change the scripts. (Similar to what bash does with /dev/fd/N, also on systems which don't have /dev/fd/ natively.) * Gnulib's open() function already supports "/dev/null" on native Windows. For consistency, stat() should support it as well. 2025-06-13 Bruno Haible <br...@clisp.org> stat: Support the file name "/dev/null" on native Windows. Reported by Collin Funk in <https://lists.gnu.org/archive/html/bug-gnulib/2025-06/msg00119.html>. * lib/stat.c (rpl_stat): On native Windows, map "/dev/null" to "NUL". * tests/test-fstat.c (main): Test /dev/null also on native Windows. * tests/test-stat.h (test_stat_func): Likewise. * tests/test-lstat.h (test_lstat_func): Likewise. * doc/posix-functions/stat.texi: Mention problem of null device name. * doc/posix-functions/lstat.texi: Likewise. diff --git a/doc/posix-functions/lstat.texi b/doc/posix-functions/lstat.texi index 43728a5343..7bbc32a81e 100644 --- a/doc/posix-functions/lstat.texi +++ b/doc/posix-functions/lstat.texi @@ -33,6 +33,9 @@ offset from @code{tv_sec}. Solaris 11.4 is similar, except that @code{tv_sec} might also be @minus{}1000000000. @item +On Windows platforms (excluding Cygwin), a different name has to be used +for the null device, namely @code{"NUL"} instead of @code{"/dev/null"}. +@item On Windows platforms (excluding Cygwin), symlinks are not supported, so @code{lstat} does not exist. @end itemize diff --git a/doc/posix-functions/stat.texi b/doc/posix-functions/stat.texi index 6e0df396a5..80762977a8 100644 --- a/doc/posix-functions/stat.texi +++ b/doc/posix-functions/stat.texi @@ -45,6 +45,9 @@ @minus{}999999999..@minus{}1, representing a negative nanoseconds offset from @code{tv_sec}. Solaris 11.4 is similar, except that @code{tv_sec} might also be @minus{}1000000000. +@item +On Windows platforms (excluding Cygwin), a different name has to be used +for the null device, namely @code{"NUL"} instead of @code{"/dev/null"}. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/stat.c b/lib/stat.c index ebed63e55a..6663783703 100644 --- a/lib/stat.c +++ b/lib/stat.c @@ -118,6 +118,10 @@ rpl_stat (char const *name, struct stat *buf) around length limitations <https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file> ? */ + /* To ease portability. Like in open.c. */ + if (strcmp (name, "/dev/null") == 0) + name = "NUL"; + /* POSIX <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13> specifies: "More than two leading <slash> characters shall be treated as a single <slash> character." */ diff --git a/tests/test-fstat.c b/tests/test-fstat.c index 7e65ee2f23..2fda69630a 100644 --- a/tests/test-fstat.c +++ b/tests/test-fstat.c @@ -52,11 +52,7 @@ main () int fd; struct stat statbuf; -#if defined _WIN32 && !defined __CYGWIN__ - fd = open ("NUL", O_RDWR); -#else fd = open ("/dev/null", O_RDWR); -#endif ASSERT (fstat (fd, &statbuf) == 0); close (fd); ASSERT (!S_ISREG (statbuf.st_mode)); diff --git a/tests/test-lstat.h b/tests/test-lstat.h index bfd5b5526b..899cb26755 100644 --- a/tests/test-lstat.h +++ b/tests/test-lstat.h @@ -66,11 +66,7 @@ test_lstat_func (int (*func) (char const *, struct stat *), bool print) /* /dev/null is a character device. Except on Solaris, where it is a symlink. */ -#if defined _WIN32 && !defined __CYGWIN__ - ASSERT (func ("NUL", &st1) == 0); -#else ASSERT (func ("/dev/null", &st1) == 0); -#endif ASSERT (!S_ISREG (st1.st_mode)); #if !defined __sun ASSERT (S_ISCHR (st1.st_mode)); diff --git a/tests/test-stat.h b/tests/test-stat.h index 3d2b912e8f..d055522f39 100644 --- a/tests/test-stat.h +++ b/tests/test-stat.h @@ -62,11 +62,7 @@ test_stat_func (int (*func) (char const *, struct stat *), bool print) ASSERT (errno == ENOTDIR); /* /dev/null is a character device. */ -#if defined _WIN32 && !defined __CYGWIN__ - ASSERT (func ("NUL", &st1) == 0); -#else ASSERT (func ("/dev/null", &st1) == 0); -#endif ASSERT (!S_ISREG (st1.st_mode)); ASSERT (S_ISCHR (st1.st_mode));