summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2013-09-25 19:13:38 +0200
committerBram Moolenaar <Bram@vim.org>2013-09-25 19:13:38 +0200
commit2ee95f7d13566ba393ed70bd4401e7164b0cc2f9 (patch)
tree9ccfd8c005d3a370675290c759f4dad4ffa4afb3
parent134bf07ca0e28addeeb67edc4fceeba00388d7fc (diff)
downloadvim-git-2ee95f7d13566ba393ed70bd4401e7164b0cc2f9.tar.gz
updated for version 7.4.039v7.4.039
Problem: MS-Windows: MSCV10 and earlier can't handle symlinks to a directory properly. Solution: Add stat_symlink_aware() and wstat_symlink_aware(). (Ken Takata)
-rw-r--r--src/os_mswin.c96
-rw-r--r--src/os_win32.c10
-rw-r--r--src/os_win32.h13
-rw-r--r--src/version.c2
4 files changed, 109 insertions, 12 deletions
diff --git a/src/os_mswin.c b/src/os_mswin.c
index 67b796097..8b507f6fb 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -498,6 +498,98 @@ slash_adjust(p)
}
}
+ static int
+stat_symlink_aware(const char *name, struct stat *stp)
+{
+#if defined(_MSC_VER) && _MSC_VER < 1700
+ /* Work around for VC10 or earlier. stat() can't handle symlinks properly.
+ * VC9 or earlier: stat() doesn't support a symlink at all. It retrieves
+ * status of a symlink itself.
+ * VC10: stat() supports a symlink to a normal file, but it doesn't support
+ * a symlink to a directory (always returns an error). */
+ WIN32_FIND_DATA findData;
+ HANDLE hFind, h;
+ DWORD attr = 0;
+ BOOL is_symlink = FALSE;
+
+ hFind = FindFirstFile(name, &findData);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ attr = findData.dwFileAttributes;
+ if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
+ && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
+ is_symlink = TRUE;
+ FindClose(hFind);
+ }
+ if (is_symlink)
+ {
+ h = CreateFile(name, FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING,
+ (attr & FILE_ATTRIBUTE_DIRECTORY)
+ ? FILE_FLAG_BACKUP_SEMANTICS : 0,
+ NULL);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ int fd, n;
+
+ fd = _open_osfhandle((intptr_t)h, _O_RDONLY);
+ n = _fstat(fd, (struct _stat*)stp);
+ _close(fd);
+ return n;
+ }
+ }
+#endif
+ return stat(name, stp);
+}
+
+#ifdef FEAT_MBYTE
+ static int
+wstat_symlink_aware(const WCHAR *name, struct _stat *stp)
+{
+# if defined(_MSC_VER) && _MSC_VER < 1700
+ /* Work around for VC10 or earlier. _wstat() can't handle symlinks properly.
+ * VC9 or earlier: _wstat() doesn't support a symlink at all. It retrieves
+ * status of a symlink itself.
+ * VC10: _wstat() supports a symlink to a normal file, but it doesn't
+ * support a symlink to a directory (always returns an error). */
+ int n;
+ BOOL is_symlink = FALSE;
+ HANDLE hFind, h;
+ DWORD attr = 0;
+ WIN32_FIND_DATAW findDataW;
+
+ hFind = FindFirstFileW(name, &findDataW);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ attr = findDataW.dwFileAttributes;
+ if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
+ && (findDataW.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
+ is_symlink = TRUE;
+ FindClose(hFind);
+ }
+ if (is_symlink)
+ {
+ h = CreateFileW(name, FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING,
+ (attr & FILE_ATTRIBUTE_DIRECTORY)
+ ? FILE_FLAG_BACKUP_SEMANTICS : 0,
+ NULL);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ int fd;
+
+ fd = _open_osfhandle((intptr_t)h, _O_RDONLY);
+ n = _fstat(fd, stp);
+ _close(fd);
+ return n;
+ }
+ }
+# endif
+ return _wstat(name, stp);
+}
+#endif
/*
* stat() can't handle a trailing '/' or '\', remove it first.
@@ -534,7 +626,7 @@ vim_stat(const char *name, struct stat *stp)
if (wp != NULL)
{
- n = _wstat(wp, (struct _stat *)stp);
+ n = wstat_symlink_aware(wp, (struct _stat *)stp);
vim_free(wp);
if (n >= 0)
return n;
@@ -544,7 +636,7 @@ vim_stat(const char *name, struct stat *stp)
}
}
#endif
- return stat(buf, stp);
+ return stat_symlink_aware(buf, stp);
}
#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
diff --git a/src/os_win32.c b/src/os_win32.c
index f36dfb3db..cd29b8738 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -78,16 +78,6 @@
# endif
#endif
-/*
- * Reparse Point
- */
-#ifndef FILE_ATTRIBUTE_REPARSE_POINT
-# define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
-#endif
-#ifndef IO_REPARSE_TAG_SYMLINK
-# define IO_REPARSE_TAG_SYMLINK 0xA000000C
-#endif
-
/* Record all output and all keyboard & mouse input */
/* #define MCH_WRITE_DUMP */
diff --git a/src/os_win32.h b/src/os_win32.h
index 58b179ff8..29fe5e4fb 100644
--- a/src/os_win32.h
+++ b/src/os_win32.h
@@ -130,6 +130,19 @@
# define DFLT_MAXMEMTOT (5*1024) /* use up to 5 Mbyte for Vim */
#endif
+/*
+ * Reparse Point
+ */
+#ifndef FILE_ATTRIBUTE_REPARSE_POINT
+# define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
+#endif
+#ifndef IO_REPARSE_TAG_MOUNT_POINT
+# define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
+#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+# define IO_REPARSE_TAG_SYMLINK 0xA000000C
+#endif
+
#if defined(_MSC_VER) || defined(__BORLANDC__)
/* Support for __try / __except. All versions of MSVC and Borland C are
* expected to have this. Any other compilers that support it? */
diff --git a/src/version.c b/src/version.c
index c7b73329a..3495b0ee8 100644
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 39,
+/**/
38,
/**/
37,