From: "H.Yamamoto" Date: 2005-11-17T12:14:03+09:00 Subject: [ruby-dev:27726] Re: [ruby-list:41557] Re:Windowsにおける共有フォルダーでのDir.globは一覧を返さない? 山本です。 >| そろそろ9xは忘れたいとこですが、rb_w32_statについて、9xは従来通り、NT系 >| は山本さんのパッチという玉虫色な解決は無しでしょうか? > >私の認識だと、これは1.8においてもバグ、なので、1.8にも修正は >入れたいです。 >で、少なくとも1.8でWin9xを忘れるのはなしです。 >と言っても、もはやWin9xを積極的にサポートするのはいろんな意味 >でしんどいですので、Win9xに関しては現状より悪化しなけりゃOKか >な、と思っています。 > >というわけで、Win9xは従来どおりでNT系はこのパッチ、という分岐 >を仕込むのはもちろんかまわんと思うです、はい。 とりあえずこんな感じでしょうか。CreateFile のフラグをいじることで、 心持ち速くなった気もします。(といっても、 http://gnuwin32.sourceforge.net/compile.html を参考にしただけで, フラグの意味がいまいちよくわかってないのですが) win32/win32.c(3352) の ret = (IsWinNT() ? winnt_stat : win9x_stat)(buf1, st); を ret = (1 ? winnt_stat : win9x_stat)(buf1, st); とか ret = (0 ? winnt_stat : win9x_stat)(buf1, st); にすれば実装を切り替えてテストできると思います。 まだ、 * win9x_stat でドライブレターから st_dev, st_rdev を計算するコード * 64bit ファイルサイズサポート * その他、細かい挙動の確認 は未対応です。64bit 対応は struct stat に細工が必要になるので、 一番後回しにしようと思っています。(できればその前に一度コミットしたい) 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 03:01:37 -0000 @@ -39,7 +39,6 @@ #endif #define isdirsep(x) ((x) == '/' || (x) == '\\') -#undef stat #undef fclose #undef close #undef setsockopt @@ -3164,23 +3163,156 @@ isUNCRoot(const char *path) return 0; } -#ifdef __BORLANDC__ -#undef fstat +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; +} + +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; +// st->st_size = info.nFileSizeHigh; +// st->st_size << 32; +// st->st_size += info.nFileSizeLow; + break; + case FILE_TYPE_CHAR: + st->st_mode = S_IFCHR; + break; +#ifdef S_IFFIFO + case FILE_TYPE_PIPE: + st->st_mode = S_IFIFO; + break; +#endif + } + return 0; +} + int rb_w32_fstat(int fd, struct stat *st) { - BY_HANDLE_FILE_INFORMATION info; - int ret = fstat(fd, st); + long h = _get_osfhandle(fd); + + if (h < 0) + return -1; + else + return os_stat((HANDLE)h, st); +} + +static int +win9x_stat(const char *path, struct stat *st) +{ + HANDLE h; + WIN32_FIND_DATA wfd; + DWORD attr; - if (ret) return ret; - st->st_mode &= ~(S_IWGRP | S_IWOTH); - if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info) && - !(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { - st->st_mode |= S_IWUSR; + memset(st, 0, sizeof(struct stat)); + st->st_nlink = 1; + + h = FindFirstFile(path, &wfd); + if (h != INVALID_HANDLE_VALUE) { + FindClose(h); + st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes); + st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime); + st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime); + st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime); + st->st_size = wfd.nFileSizeLow; + return 0; } + attr = GetFileAttributes(path); + if (attr != -1) { + st->st_mode = fileattr_to_unixmode(attr); + return 0; + } + errno = map_errno(GetLastError()); + return -1; +} + +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); return ret; } -#endif int rb_w32_stat(const char *path, struct stat *st) @@ -3217,9 +3349,22 @@ 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); + ret = (IsWinNT() ? winnt_stat : win9x_stat)(buf1, st); + + if (ret == 0 && (st->st_mode & S_IFREG)) { + end = buf1 + strlen(buf1); + while (buf1 < end) { + end = CharPrev(buf1, 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; } 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 01:30:46 -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)