山本です。 "U.Nakamura" <usa / garbagecollect.jp> wrote: (2004/07/02 09:56) >というわけで、とりあえずは > 「st_devとst_inoを正しく設定しないといけないの?」 >というのが問題で、速度などは二の次かなーと思います。 個人的には、両方とも0でいいような気がします。Windows だと、 ファイルハンドルに対してしか同一性を保証できないようなので・・・ >まあ、Cygwinの実装はそれなりに参考になると思います。 http://www.kaimei.org/note/mag/in_cyg.html でしょうか。 st_ino の取得にはかなり複雑な方法が必要なようですね・・・ >st_devとst_inoを完璧に設定するよりは簡単じゃないかなあ。 ああ、IOのインスタンスメソッドにすれば簡単ですね。 (Unix では st_dev, st_ino を使って、Windows でもファイルが開きっぱなしなので GetFileInformationByHandle で問題なし。他の環境では NotImplementedError ?) 最初、stat()のようにパスから比較するのをイメージしていました。 次の点が不完全ですが、とりあえず stat() fstat() を自前で実装してみました。 - UNC で動くか確かめてない - ファイルサイズが32bit(st_size が符号なしなら簡単ですが、そういった保証が得られませんでした) - とりあえず st_dev, st_ino は 0 - IF_SOCK は未実装 Tietew さんのように struct stat を差し替えようと思いましたが、組み込み ruby で アプリ側の struct stat と異なってもいいのか自信がなかったので、とりあえず 32bit にしました。 # 以前ファイルの属性を得るのに、"FindFirstFile" と、"CreateFile + GetFileSizeなど" # を比べて、CreateFileは相当重かった記憶があるのですが、なぜか全然遅くなりませんでした。 # やりかたがまずかったのか、もしくはWin98では重いのかもしれません。 Index: win32.h =================================================================== RCS file: /var/cvs/src/ruby/win32/win32.h,v retrieving revision 1.51 diff -u -w -b -p -r1.51 win32.h --- win32.h 19 Feb 2004 09:08:23 -0000 1.51 +++ win32.h 2 Jul 2004 03:42:22 -0000 @@ -113,12 +113,12 @@ 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) #endif #define fsync(h) _commit(h) #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) Index: win32.c =================================================================== RCS file: /var/cvs/src/ruby/win32/win32.c,v retrieving revision 1.117 diff -u -w -b -p -r1.117 win32.c --- win32.c 21 Jun 2004 00:27:39 -0000 1.117 +++ win32.c 2 Jul 2004 03:41:58 -0000 @@ -2725,23 +2725,80 @@ 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; +} + int -rb_w32_fstat(int fd, struct stat *st) +os_stat(HANDLE h, struct stat *st) { BY_HANDLE_FILE_INFORMATION info; - int ret = fstat(fd, st); - 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)); + switch (GetFileType(h)) { + case FILE_TYPE_DISK: + if (!GetFileInformationByHandle(h, &info)) { + errno = map_errno(GetLastError()); + return -1; + } + st->st_mode = S_IREAD; + if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + st->st_mode |= (S_IFDIR | S_IWRITE | S_IWUSR | S_IEXEC); + else if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + st->st_mode |= (S_IFREG); + else + st->st_mode |= (S_IFREG | S_IWRITE | S_IWUSR); + st->st_nlink = info.nNumberOfLinks; +// st->st_dev = st->st_rdev = info.dwVolumeSerialNumber; + 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; + case FILE_TYPE_PIPE: + st->st_mode = S_IFIFO; + break; + } + return 0; } - return ret; + +int +rb_w32_fstat(int fd, struct stat *st) +{ + long h = _get_osfhandle(fd); + + if (h < 0) + return -1; + else + return os_stat((HANDLE)h, st); } -#endif int rb_w32_stat(const char *path, struct stat *st) @@ -2750,6 +2807,7 @@ rb_w32_stat(const char *path, struct sta char *buf1, *s, *end; int len; int ret; + HANDLE h; if (!path || !st) { errno = EFAULT; @@ -2778,9 +2836,36 @@ 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); + h = CreateFile( + buf1, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + NULL + ); + if (h == INVALID_HANDLE_VALUE) { + errno = map_errno(GetLastError()); + return -1; + } + ret = os_stat(h, st); + CloseHandle(h); + + if (ret == 0 && S_ISREG(st->st_mode)) { + 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; }