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));
 




Reply via email to