From: "H.Yamamoto" Date: 2005-11-17T16:41:31+09:00 Subject: [ruby-dev:27728] Re: [ruby-list:41557] Re:Windowsにおける共有フォルダーでのDir.globは一覧を返さない? 山本です。 >>とりあえずこんな感じでしょうか。 > >とはいっても、Win9x 上でテストしたわけではないので・・・ >どなたか確認してくださるとうれしいです。 > >Win2000でテストした結果ですが、win9x_stat でも > > Dir.chdir("//server/hoge") > > Dir.glob("*") > >が動くことは確認しました。 従来どおりというのは、「FindFirstFile を使う従来の方法」という意味ではなく、 現在の Ruby の実装という意味ではないかとようやく思い至りました。 その場合はは下のようになると思います。ただ、こうしてしまうと struct stat の置き換えが困難になるのではないかという心配はあります。 (Win9x と WinNT の区別は #ifdef でしているのではないため) Index: win32.c =================================================================== RCS file: /src/ruby/win32/win32.c,v retrieving revision 1.178 diff -u -w -b -p -r1.178 win32.c --- win32.c 5 Nov 2005 04:43:46 -0000 1.178 +++ win32.c 17 Nov 2005 06:35:49 -0000 @@ -39,10 +39,11 @@ #endif #define isdirsep(x) ((x) == '/' || (x) == '\\') -#undef stat #undef fclose #undef close #undef setsockopt +#undef fstat +#undef stat #ifdef _M_IX86 # define WIN95 1 @@ -3164,13 +3165,150 @@ isUNCRoot(const char *path) return 0; } -#ifdef __BORLANDC__ -#undef fstat -int -rb_w32_fstat(int fd, struct stat *st) +static time_t +filetime_to_unixtime(const FILETIME *ft) +{ + FILETIME loc; + SYSTEMTIME st; + struct tm tm; + time_t t; + + if (!FileTimeToLocalFileTime(ft, &loc)) { + return 0; + } + if (!FileTimeToSystemTime(&loc, &st)) { + return 0; + } + memset(&tm, 0, sizeof(tm)); + tm.tm_year = st.wYear - 1900; + tm.tm_mon = st.wMonth - 1; + tm.tm_mday = st.wDay; + tm.tm_hour = st.wHour; + tm.tm_min = st.wMinute; + tm.tm_sec = st.wSecond; + t = mktime(&tm); + return t == -1 ? 0 : t; +} + +static unsigned +fileattr_to_unixmode(DWORD attr) +{ + unsigned mode = 0; + + if (attr & FILE_ATTRIBUTE_READONLY) { + mode |= S_IREAD; + } + else { + mode |= S_IREAD | S_IWRITE | S_IWUSR; + } + + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + mode |= S_IFDIR | S_IEXEC; + } + else { + mode |= S_IFREG; + } + + mode |= (mode & 0700) >> 3; + mode |= (mode & 0700) >> 6; + + return mode; +} + +#if !defined(S_IFIFO) && defined(_S_IFIFO) +#define S_IFIFO _S_IFIFO +#endif + +static int +os_stat(HANDLE h, struct stat *st) { BY_HANDLE_FILE_INFORMATION info; + + memset(st, 0, sizeof(struct stat)); + st->st_nlink = 1; + + switch (GetFileType(h)) { + case FILE_TYPE_DISK: + if (!GetFileInformationByHandle(h, &info)) { + errno = map_errno(GetLastError()); + return -1; + } + st->st_nlink = info.nNumberOfLinks; + st->st_dev = st->st_rdev = info.dwVolumeSerialNumber; + st->st_mode = fileattr_to_unixmode(info.dwFileAttributes); + st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime); + st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime); + st->st_ctime = filetime_to_unixtime(&info.ftCreationTime); + st->st_size = info.nFileSizeLow; /* TODO: 64bit support */ + break; + case FILE_TYPE_CHAR: + st->st_mode = S_IFCHR; + break; + case FILE_TYPE_PIPE: + st->st_mode = S_IFIFO; + break; + } + return 0; +} + +static int +winnt_fstat(int fd, struct stat *st) +{ + long h = _get_osfhandle(fd); + + if (h < 0) { + errno = EBADF; + return -1; + } + return os_stat((HANDLE)h, st); +} + +static int +winnt_stat(const char *path, struct stat *st) +{ + HANDLE h; + int ret; + + h = CreateFile( + path, + 0, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, + NULL + ); + if (h == INVALID_HANDLE_VALUE) { + errno = map_errno(GetLastError()); + return -1; + } + ret = os_stat(h, st); + CloseHandle(h); + + if (ret == 0 && (st->st_mode & S_IFREG)) { + const char *end = path + strlen(path); + while (path < end) { + end = CharPrev(path, end); + if (*end == '.') { + if ((strcmpi(end, ".bat") == 0) || + (strcmpi(end, ".cmd") == 0) || + (strcmpi(end, ".com") == 0) || + (strcmpi(end, ".exe") == 0)) { + st->st_mode |= S_IEXEC; + } + break; + } + } + } + return ret; +} + +static int +win9x_fstat(int fd, struct stat *st) +{ int ret = fstat(fd, st); +#ifdef __BORLANDC__ + BY_HANDLE_FILE_INFORMATION info; if (ret) return ret; st->st_mode &= ~(S_IWGRP | S_IWOTH); @@ -3178,9 +3316,25 @@ rb_w32_fstat(int fd, struct stat *st) !(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { st->st_mode |= S_IWUSR; } +#endif return ret; } -#endif + +static int +win9x_stat(const char *path, struct stat *st) +{ + int ret = stat(path, st); + if (ret == 0) { + st->st_mode &= ~(S_IWGRP | S_IWOTH); + } + return ret; +} + +int +rb_w32_fstat(int fd, struct stat *st) +{ + return (IsWinNT() ? winnt_fstat : win9x_fstat)(fd, st); +} int rb_w32_stat(const char *path, struct stat *st) @@ -3217,11 +3371,7 @@ rb_w32_stat(const char *path, struct sta } else if (*end == '\\' || (buf1 + 1 == end && *end == ':')) strcat(buf1, "."); - ret = stat(buf1, st); - if (ret == 0) { - st->st_mode &= ~(S_IWGRP | S_IWOTH); - } - return ret; + return (IsWinNT() ? winnt_stat : win9x_stat)(buf1, st); } static long Index: win32.h =================================================================== RCS file: /src/ruby/win32/win32.h,v retrieving revision 1.74 diff -u -w -b -p -r1.74 win32.h --- win32.h 5 Nov 2005 04:43:46 -0000 1.74 +++ win32.h 17 Nov 2005 04:48:06 -0000 @@ -105,8 +105,6 @@ extern "C++" { #define write(h, b, l) _write(h, b, l) #define _open _sopen #define sopen _sopen -#undef fstat -#define fstat(fd,st) rb_w32_fstat(fd,st) #undef fopen #define fopen(p, m) rb_w32_fopen(p, m) #undef fdopen @@ -117,6 +115,8 @@ extern "C++" { #undef stat #define stat(path,st) rb_w32_stat(path,st) +#undef fstat +#define fstat(fd,st) rb_w32_fstat(fd,st) #undef execv #define execv(path,argv) rb_w32_aspawn(P_OVERLAY,path,argv) #if !defined(__BORLANDC__) && !defined(_WIN32_WCE) @@ -203,9 +203,9 @@ extern int rb_w32_mkdir(const char *, in extern int rb_w32_rmdir(const char *); extern int rb_w32_unlink(const char *); extern int rb_w32_stat(const char *, struct stat *); +extern int rb_w32_fstat(int, struct stat *); #ifdef __BORLANDC__ -extern int rb_w32_fstat(int, struct stat *); extern FILE *rb_w32_fopen(const char *, const char *); extern FILE *rb_w32_fdopen(int, const char *); extern FILE *rb_w32_fsopen(const char *, const char *, int);