summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--configure.in1
-rw-r--r--include/ruby/win32.h3
-rw-r--r--win32/Makefile.sub1
-rw-r--r--win32/win32.c110
5 files changed, 119 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index b18f1e5f9a..d889505489 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Mon Mar 23 17:36:00 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * win32/win32.c (wreadlink, rb_w32_ureadlink): implement readlink().
+
Mon Mar 23 14:40:45 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* win32/win32.c (winnt_stat): stat with following symbolic links.
diff --git a/configure.in b/configure.in
index d9b4f72e7b..db37cd6644 100644
--- a/configure.in
+++ b/configure.in
@@ -1104,6 +1104,7 @@ main()
ac_cv_func_isnan=yes
ac_cv_func_finite=yes
ac_cv_func_link=yes
+ ac_cv_func_readlink=yes
ac_cv_lib_crypt_crypt=no
ac_cv_func_getpgrp_void=no
ac_cv_func_memcmp_working=yes
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index 88416e9e08..77ea0944c4 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -316,6 +316,9 @@ extern int chown(const char *, int, int);
extern int rb_w32_uchown(const char *, int, int);
extern int link(const char *, const char *);
extern int rb_w32_ulink(const char *, const char *);
+extern ssize_t readlink(const char *, char *, size_t);
+extern ssize_t rb_w32_ureadlink(const char *, char *, size_t);
+extern ssize_t rb_w32_wreadlink(const WCHAR *, WCHAR *, size_t);
extern int gettimeofday(struct timeval *, struct timezone *);
extern int clock_gettime(clockid_t, struct timespec *);
extern int clock_getres(clockid_t, struct timespec *);
diff --git a/win32/Makefile.sub b/win32/Makefile.sub
index 259193e0b2..5b4ebade95 100644
--- a/win32/Makefile.sub
+++ b/win32/Makefile.sub
@@ -712,6 +712,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define HAVE_TIMES 1
#define HAVE_FCNTL 1
#define HAVE_LINK 1
+#define HAVE_READLINK 1
#define HAVE__SETJMP 1
#define HAVE_TELLDIR 1
#define HAVE_SEEKDIR 1
diff --git a/win32/win32.c b/win32/win32.c
index 73fd477edf..b3adc11fe3 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -4679,6 +4679,116 @@ link(const char *from, const char *to)
}
/* License: Ruby's */
+ssize_t
+rb_w32_wreadlink(const WCHAR *path, WCHAR *buf, size_t bufsize)
+{
+ struct {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[MAXPATHLEN * 2];
+ } SymbolicLinkReparseBuffer;
+ } rp;
+ HANDLE f;
+ DWORD ret;
+ int e = 0;
+
+ typedef BOOL (WINAPI *device_io_control_func)(HANDLE, DWORD, LPVOID,
+ DWORD, LPVOID, DWORD,
+ LPDWORD, LPOVERLAPPED);
+ static device_io_control_func device_io_control = (device_io_control_func)-1;
+
+ if (device_io_control == (device_io_control_func)-1) {
+ device_io_control = (device_io_control_func)
+ get_proc_address("kernel32", "DeviceIoControl", NULL);
+ }
+ if (!device_io_control) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ f = CreateFileW(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+ if (f == INVALID_HANDLE_VALUE) {
+ errno = map_errno(GetLastError());
+ return -1;
+ }
+
+ if (!device_io_control(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
+ &rp, sizeof(rp), &ret, NULL)) {
+ e = map_errno(GetLastError());
+ }
+ else if (rp.ReparseTag != IO_REPARSE_TAG_SYMLINK){
+ e = EINVAL;
+ }
+ else {
+ void *name = ((char *)rp.SymbolicLinkReparseBuffer.PathBuffer +
+ rp.SymbolicLinkReparseBuffer.PrintNameOffset);
+ ret = rp.SymbolicLinkReparseBuffer.PrintNameLength;
+ ((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0';
+ translate_wchar(name, L'\\', L'/');
+ bufsize *= sizeof(WCHAR);
+ memcpy(buf, name, ret > bufsize ? bufsize : ret);
+ }
+
+ CloseHandle(f);
+ if (e) {
+ errno = e;
+ return -1;
+ }
+ return ret / sizeof(WCHAR);
+}
+
+/* License: Ruby's */
+static ssize_t
+w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize)
+{
+ WCHAR *wpath;
+ WCHAR wbuf[MAXPATHLEN];
+ ssize_t ret;
+
+ wpath = mbstr_to_wstr(cp, path, -1, NULL);
+ if (!wpath) return -1;
+ ret = rb_w32_wreadlink(wpath, wbuf, MAXPATHLEN);
+ free(wpath);
+ if (ret < 0) return ret;
+ ret = WideCharToMultiByte(cp, 0, wbuf, ret, buf, bufsize, NULL, NULL);
+ if (!ret) {
+ int e = GetLastError();
+ if (e == ERROR_INSUFFICIENT_BUFFER) {
+ ret = bufsize;
+ }
+ else {
+ errno = map_errno(e);
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+/* License: Ruby's */
+ssize_t
+rb_w32_ureadlink(const char *path, char *buf, size_t bufsize)
+{
+ return w32_readlink(CP_UTF8, path, buf, bufsize);
+}
+
+/* License: Ruby's */
+ssize_t
+readlink(const char *path, char *buf, size_t bufsize)
+{
+ return w32_readlink(filecp(), path, buf, bufsize);
+}
+
+/* License: Ruby's */
int
wait(int *status)
{