Ken Brown wrote: > I took a quick look, and it appears that this is a Cygwin bug in which > realpath() fails with ENOENT instead of ENOTDIR.
Yes, I confirm. POSIX <https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html> says that when one of the components "names an existing file that is neither a directory nor a symbolic link to a directory", realpath() shall fail with error ENOTDIR. Failing with error ENOENT is not allowed in this case. Here's a patch that detects the issue at configure time and adds a workaround. With it, the unit tests pass. 2021-01-20 Bruno Haible <br...@clisp.org> canonicalize-lgpl: Work around a Cygwin bug. * m4/canonicalize.m4 (gl_FUNC_REALPATH_WORKS): Test for lstat. Add a test case that involves a symbolic link to an existing file. * doc/posix-functions/realpath.texi: Mention the Cygwin bug. diff --git a/doc/posix-functions/realpath.texi b/doc/posix-functions/realpath.texi index a5b7c1d..dbeed3d 100644 --- a/doc/posix-functions/realpath.texi +++ b/doc/posix-functions/realpath.texi @@ -24,9 +24,9 @@ This function fails to detect trailing slashes on non-directories on some platforms: glibc 2.3.5, Mac OS X 10.13, OpenBSD 6.0. @item -This function fails to recognize non-directories followed @samp{..} on -some platforms: -cygwin. +This function fails to recognize non-directories or symlinks to non-directories +followed by @samp{..} on some platforms: +Cygwin 2.9. @item This function misbehaves on consecutive slashes on some platforms: musl libc 1.2.2, AIX 7. diff --git a/m4/canonicalize.m4 b/m4/canonicalize.m4 index 6821c70..0dfb2da 100644 --- a/m4/canonicalize.m4 +++ b/m4/canonicalize.m4 @@ -1,4 +1,4 @@ -# canonicalize.m4 serial 36 +# canonicalize.m4 serial 37 dnl Copyright (C) 2003-2007, 2009-2021 Free Software Foundation, Inc. @@ -78,15 +78,20 @@ AC_DEFUN([gl_CANONICALIZE_LGPL_SEPARATE], # so is the latter. AC_DEFUN([gl_FUNC_REALPATH_WORKS], [ - AC_CHECK_FUNCS_ONCE([realpath]) + AC_CHECK_FUNCS_ONCE([realpath lstat]) AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles AC_CACHE_CHECK([whether realpath works], [gl_cv_func_realpath_works], [ rm -rf conftest.a conftest.d touch conftest.a + # Assume that if we have lstat, we can also check symlinks. + if test $ac_cv_func_lstat = yes; then + ln -s conftest.a conftest.l + fi mkdir conftest.d AC_RUN_IFELSE([ AC_LANG_PROGRAM([[ ]GL_NOCRASH[ + #include <errno.h> #include <stdlib.h> #include <string.h> ]], [[ @@ -98,17 +103,27 @@ AC_DEFUN([gl_FUNC_REALPATH_WORKS], result |= 1; free (name); } + /* This test fails on older versions of Cygwin. */ { char *name = realpath ("conftest.b/../conftest.a", NULL); if (name != NULL) result |= 2; free (name); } + /* This test fails on Cygwin 2.9. */ + #if HAVE_LSTAT + { + char *name = realpath ("conftest.l/../conftest.a", NULL); + if (name != NULL || errno != ENOTDIR) + result |= 4; + free (name); + } + #endif /* This test fails on Mac OS X 10.13, OpenBSD 6.0. */ { char *name = realpath ("conftest.a/", NULL); if (name != NULL) - result |= 4; + result |= 8; free (name); } /* This test fails on AIX 7, Solaris 10. */ @@ -116,7 +131,7 @@ AC_DEFUN([gl_FUNC_REALPATH_WORKS], char *name1 = realpath (".", NULL); char *name2 = realpath ("conftest.d//./..", NULL); if (! name1 || ! name2 || strcmp (name1, name2)) - result |= 8; + result |= 16; free (name1); free (name2); } @@ -127,7 +142,7 @@ AC_DEFUN([gl_FUNC_REALPATH_WORKS], { char *name = realpath ("//", NULL); if (! name || strcmp (name, "/")) - result |= 16; + result |= 32; free (name); } #endif @@ -136,7 +151,7 @@ AC_DEFUN([gl_FUNC_REALPATH_WORKS], ], [gl_cv_func_realpath_works=yes], [case $? in - 16) gl_cv_func_realpath_works=nearly ;; + 32) gl_cv_func_realpath_works=nearly ;; *) gl_cv_func_realpath_works=no ;; esac ], @@ -145,13 +160,15 @@ AC_DEFUN([gl_FUNC_REALPATH_WORKS], *-gnu* | gnu*) gl_cv_func_realpath_works="guessing yes" ;; # Guess 'nearly' on musl systems. *-musl*) gl_cv_func_realpath_works="guessing nearly" ;; + # Guess no on Cygwin. + cygwin*) gl_cv_func_realpath_works="guessing no" ;; # Guess no on native Windows. mingw*) gl_cv_func_realpath_works="guessing no" ;; # If we don't know, obey --enable-cross-guesses. *) gl_cv_func_realpath_works="$gl_cross_guess_normal" ;; esac ]) - rm -rf conftest.a conftest.d + rm -rf conftest.a conftest.l conftest.d ]) case "$gl_cv_func_realpath_works" in *yes)