summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--dir.c22
2 files changed, 27 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 1144dc2..edf30c3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2013-04-28 Eli Zaretskii <eliz@gnu.org>
+
+ * dir.c (local_stat) [WINDOWS32]: Use the wrapper on MS-Windows.
+ If the argument ends in "dir/.", make sure the parent dir exists
+ and is indeed a directory. Fixes Savannah bug #37065.
+
2013-04-28 Paul Smith <psmith@gnu.org>
* makeint.h (message_s, error_s): Functions that print to strings
diff --git a/dir.c b/dir.c
index 59b2a8c..dc0ef76 100644
--- a/dir.c
+++ b/dir.c
@@ -1185,8 +1185,11 @@ ansi_free (void *p)
/* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
* macro for stat64(). If stat is a macro, make a local wrapper function to
* invoke it.
+ *
+ * On MS-Windows, stat() "succeeds" for foo/bar/. where foo/bar is a
+ * regular file; fix that here.
*/
-#ifndef stat
+#if !defined(stat) && !defined(WINDOWS32)
# ifndef VMS
int stat (const char *path, struct stat *sbuf);
# endif
@@ -1196,6 +1199,23 @@ static int
local_stat (const char *path, struct stat *buf)
{
int e;
+#ifdef WINDOWS32
+ size_t plen = strlen (path);
+
+ /* Make sure the parent of "." exists and is a directory, not a
+ file. This is because 'stat' on Windows normalizes the argument
+ foo/. => foo without checking first that foo is a directory. */
+ if (plen > 1 && path[plen - 1] == '.'
+ && (path[plen - 2] == '/' || path[plen - 2] == '\\'))
+ {
+ char parent[MAXPATHLEN];
+
+ strncpy (parent, path, plen - 2);
+ parent[plen - 2] = '\0';
+ if (stat (parent, buf) < 0 || !_S_ISDIR (buf->st_mode))
+ return -1;
+ }
+#endif
EINTRLOOP (e, stat (path, buf));
return e;