Ruby  1.9.3p286(2012-10-12revision37165)
win32/win32.c
Go to the documentation of this file.
00001 /*
00002  *  Copyright (c) 1993, Intergraph Corporation
00003  *
00004  *  You may distribute under the terms of either the GNU General Public
00005  *  License or the Artistic License, as specified in the perl README file.
00006  *
00007  *  Various Unix compatibility functions and NT specific functions.
00008  *
00009  *  Some of this code was derived from the MSDOS port(s) and the OS/2 port.
00010  *
00011  */
00012 
00013 #include "ruby/ruby.h"
00014 #include "ruby/encoding.h"
00015 #include "dln.h"
00016 #include <fcntl.h>
00017 #include <process.h>
00018 #include <sys/stat.h>
00019 /* #include <sys/wait.h> */
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <errno.h>
00023 #include <assert.h>
00024 #include <ctype.h>
00025 
00026 #include <windows.h>
00027 #include <winbase.h>
00028 #include <wincon.h>
00029 #include <share.h>
00030 #include <shlobj.h>
00031 #include <mbstring.h>
00032 #if _MSC_VER >= 1400
00033 #include <crtdbg.h>
00034 #include <rtcapi.h>
00035 #endif
00036 #ifdef __MINGW32__
00037 #include <mswsock.h>
00038 #endif
00039 #include "ruby/win32.h"
00040 #include "win32/dir.h"
00041 #define isdirsep(x) ((x) == '/' || (x) == '\\')
00042 
00043 #undef stat
00044 #undef fclose
00045 #undef close
00046 #undef setsockopt
00047 
00048 #if defined __BORLANDC__
00049 #  define _filbuf _fgetc
00050 #  define _flsbuf _fputc
00051 #  define enough_to_get(n) (--(n) >= 0)
00052 #  define enough_to_put(n) (++(n) < 0)
00053 #else
00054 #  define enough_to_get(n) (--(n) >= 0)
00055 #  define enough_to_put(n) (--(n) >= 0)
00056 #endif
00057 
00058 #ifdef WIN32_DEBUG
00059 #define Debug(something) something
00060 #else
00061 #define Debug(something) /* nothing */
00062 #endif
00063 
00064 #define TO_SOCKET(x)    _get_osfhandle(x)
00065 
00066 static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE, DWORD);
00067 static int has_redirection(const char *);
00068 int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
00069 static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
00070 static int wstati64(const WCHAR *path, struct stati64 *st);
00071 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
00072 
00073 #define RUBY_CRITICAL(expr) do { expr; } while (0)
00074 
00075 /* errno mapping */
00076 static struct {
00077     DWORD winerr;
00078     int err;
00079 } errmap[] = {
00080     {   ERROR_INVALID_FUNCTION,         EINVAL          },
00081     {   ERROR_FILE_NOT_FOUND,           ENOENT          },
00082     {   ERROR_PATH_NOT_FOUND,           ENOENT          },
00083     {   ERROR_TOO_MANY_OPEN_FILES,      EMFILE          },
00084     {   ERROR_ACCESS_DENIED,            EACCES          },
00085     {   ERROR_INVALID_HANDLE,           EBADF           },
00086     {   ERROR_ARENA_TRASHED,            ENOMEM          },
00087     {   ERROR_NOT_ENOUGH_MEMORY,        ENOMEM          },
00088     {   ERROR_INVALID_BLOCK,            ENOMEM          },
00089     {   ERROR_BAD_ENVIRONMENT,          E2BIG           },
00090     {   ERROR_BAD_FORMAT,               ENOEXEC         },
00091     {   ERROR_INVALID_ACCESS,           EINVAL          },
00092     {   ERROR_INVALID_DATA,             EINVAL          },
00093     {   ERROR_INVALID_DRIVE,            ENOENT          },
00094     {   ERROR_CURRENT_DIRECTORY,        EACCES          },
00095     {   ERROR_NOT_SAME_DEVICE,          EXDEV           },
00096     {   ERROR_NO_MORE_FILES,            ENOENT          },
00097     {   ERROR_WRITE_PROTECT,            EROFS           },
00098     {   ERROR_BAD_UNIT,                 ENODEV          },
00099     {   ERROR_NOT_READY,                ENXIO           },
00100     {   ERROR_BAD_COMMAND,              EACCES          },
00101     {   ERROR_CRC,                      EACCES          },
00102     {   ERROR_BAD_LENGTH,               EACCES          },
00103     {   ERROR_SEEK,                     EIO             },
00104     {   ERROR_NOT_DOS_DISK,             EACCES          },
00105     {   ERROR_SECTOR_NOT_FOUND,         EACCES          },
00106     {   ERROR_OUT_OF_PAPER,             EACCES          },
00107     {   ERROR_WRITE_FAULT,              EIO             },
00108     {   ERROR_READ_FAULT,               EIO             },
00109     {   ERROR_GEN_FAILURE,              EACCES          },
00110     {   ERROR_LOCK_VIOLATION,           EACCES          },
00111     {   ERROR_SHARING_VIOLATION,        EACCES          },
00112     {   ERROR_WRONG_DISK,               EACCES          },
00113     {   ERROR_SHARING_BUFFER_EXCEEDED,  EACCES          },
00114     {   ERROR_BAD_NETPATH,              ENOENT          },
00115     {   ERROR_NETWORK_ACCESS_DENIED,    EACCES          },
00116     {   ERROR_BAD_NET_NAME,             ENOENT          },
00117     {   ERROR_FILE_EXISTS,              EEXIST          },
00118     {   ERROR_CANNOT_MAKE,              EACCES          },
00119     {   ERROR_FAIL_I24,                 EACCES          },
00120     {   ERROR_INVALID_PARAMETER,        EINVAL          },
00121     {   ERROR_NO_PROC_SLOTS,            EAGAIN          },
00122     {   ERROR_DRIVE_LOCKED,             EACCES          },
00123     {   ERROR_BROKEN_PIPE,              EPIPE           },
00124     {   ERROR_DISK_FULL,                ENOSPC          },
00125     {   ERROR_INVALID_TARGET_HANDLE,    EBADF           },
00126     {   ERROR_INVALID_HANDLE,           EINVAL          },
00127     {   ERROR_WAIT_NO_CHILDREN,         ECHILD          },
00128     {   ERROR_CHILD_NOT_COMPLETE,       ECHILD          },
00129     {   ERROR_DIRECT_ACCESS_HANDLE,     EBADF           },
00130     {   ERROR_NEGATIVE_SEEK,            EINVAL          },
00131     {   ERROR_SEEK_ON_DEVICE,           EACCES          },
00132     {   ERROR_DIR_NOT_EMPTY,            ENOTEMPTY       },
00133     {   ERROR_DIRECTORY,                ENOTDIR         },
00134     {   ERROR_NOT_LOCKED,               EACCES          },
00135     {   ERROR_BAD_PATHNAME,             ENOENT          },
00136     {   ERROR_MAX_THRDS_REACHED,        EAGAIN          },
00137     {   ERROR_LOCK_FAILED,              EACCES          },
00138     {   ERROR_ALREADY_EXISTS,           EEXIST          },
00139     {   ERROR_INVALID_STARTING_CODESEG, ENOEXEC         },
00140     {   ERROR_INVALID_STACKSEG,         ENOEXEC         },
00141     {   ERROR_INVALID_MODULETYPE,       ENOEXEC         },
00142     {   ERROR_INVALID_EXE_SIGNATURE,    ENOEXEC         },
00143     {   ERROR_EXE_MARKED_INVALID,       ENOEXEC         },
00144     {   ERROR_BAD_EXE_FORMAT,           ENOEXEC         },
00145     {   ERROR_ITERATED_DATA_EXCEEDS_64k,ENOEXEC         },
00146     {   ERROR_INVALID_MINALLOCSIZE,     ENOEXEC         },
00147     {   ERROR_DYNLINK_FROM_INVALID_RING,ENOEXEC         },
00148     {   ERROR_IOPL_NOT_ENABLED,         ENOEXEC         },
00149     {   ERROR_INVALID_SEGDPL,           ENOEXEC         },
00150     {   ERROR_AUTODATASEG_EXCEEDS_64k,  ENOEXEC         },
00151     {   ERROR_RING2SEG_MUST_BE_MOVABLE, ENOEXEC         },
00152     {   ERROR_RELOC_CHAIN_XEEDS_SEGLIM, ENOEXEC         },
00153     {   ERROR_INFLOOP_IN_RELOC_CHAIN,   ENOEXEC         },
00154     {   ERROR_FILENAME_EXCED_RANGE,     ENOENT          },
00155     {   ERROR_NESTING_NOT_ALLOWED,      EAGAIN          },
00156 #ifndef ERROR_PIPE_LOCAL
00157 #define ERROR_PIPE_LOCAL        229L
00158 #endif
00159     {   ERROR_PIPE_LOCAL,               EPIPE           },
00160     {   ERROR_BAD_PIPE,                 EPIPE           },
00161     {   ERROR_PIPE_BUSY,                EAGAIN          },
00162     {   ERROR_NO_DATA,                  EPIPE           },
00163     {   ERROR_PIPE_NOT_CONNECTED,       EPIPE           },
00164     {   ERROR_OPERATION_ABORTED,        EINTR           },
00165     {   ERROR_NOT_ENOUGH_QUOTA,         ENOMEM          },
00166     {   ERROR_MOD_NOT_FOUND,            ENOENT          },
00167     {   WSAEINTR,                       EINTR           },
00168     {   WSAEBADF,                       EBADF           },
00169     {   WSAEACCES,                      EACCES          },
00170     {   WSAEFAULT,                      EFAULT          },
00171     {   WSAEINVAL,                      EINVAL          },
00172     {   WSAEMFILE,                      EMFILE          },
00173     {   WSAEWOULDBLOCK,                 EWOULDBLOCK     },
00174     {   WSAEINPROGRESS,                 EINPROGRESS     },
00175     {   WSAEALREADY,                    EALREADY        },
00176     {   WSAENOTSOCK,                    ENOTSOCK        },
00177     {   WSAEDESTADDRREQ,                EDESTADDRREQ    },
00178     {   WSAEMSGSIZE,                    EMSGSIZE        },
00179     {   WSAEPROTOTYPE,                  EPROTOTYPE      },
00180     {   WSAENOPROTOOPT,                 ENOPROTOOPT     },
00181     {   WSAEPROTONOSUPPORT,             EPROTONOSUPPORT },
00182     {   WSAESOCKTNOSUPPORT,             ESOCKTNOSUPPORT },
00183     {   WSAEOPNOTSUPP,                  EOPNOTSUPP      },
00184     {   WSAEPFNOSUPPORT,                EPFNOSUPPORT    },
00185     {   WSAEAFNOSUPPORT,                EAFNOSUPPORT    },
00186     {   WSAEADDRINUSE,                  EADDRINUSE      },
00187     {   WSAEADDRNOTAVAIL,               EADDRNOTAVAIL   },
00188     {   WSAENETDOWN,                    ENETDOWN        },
00189     {   WSAENETUNREACH,                 ENETUNREACH     },
00190     {   WSAENETRESET,                   ENETRESET       },
00191     {   WSAECONNABORTED,                ECONNABORTED    },
00192     {   WSAECONNRESET,                  ECONNRESET      },
00193     {   WSAENOBUFS,                     ENOBUFS         },
00194     {   WSAEISCONN,                     EISCONN         },
00195     {   WSAENOTCONN,                    ENOTCONN        },
00196     {   WSAESHUTDOWN,                   ESHUTDOWN       },
00197     {   WSAETOOMANYREFS,                ETOOMANYREFS    },
00198     {   WSAETIMEDOUT,                   ETIMEDOUT       },
00199     {   WSAECONNREFUSED,                ECONNREFUSED    },
00200     {   WSAELOOP,                       ELOOP           },
00201     {   WSAENAMETOOLONG,                ENAMETOOLONG    },
00202     {   WSAEHOSTDOWN,                   EHOSTDOWN       },
00203     {   WSAEHOSTUNREACH,                EHOSTUNREACH    },
00204     {   WSAEPROCLIM,                    EPROCLIM        },
00205     {   WSAENOTEMPTY,                   ENOTEMPTY       },
00206     {   WSAEUSERS,                      EUSERS          },
00207     {   WSAEDQUOT,                      EDQUOT          },
00208     {   WSAESTALE,                      ESTALE          },
00209     {   WSAEREMOTE,                     EREMOTE         },
00210 };
00211 
00212 int
00213 rb_w32_map_errno(DWORD winerr)
00214 {
00215     int i;
00216 
00217     if (winerr == 0) {
00218         return 0;
00219     }
00220 
00221     for (i = 0; i < (int)(sizeof(errmap) / sizeof(*errmap)); i++) {
00222         if (errmap[i].winerr == winerr) {
00223             return errmap[i].err;
00224         }
00225     }
00226 
00227     if (winerr >= WSABASEERR) {
00228         return winerr;
00229     }
00230     return EINVAL;
00231 }
00232 
00233 #define map_errno rb_w32_map_errno
00234 
00235 static const char *NTLoginName;
00236 
00237 static OSVERSIONINFO osver;
00238 
00239 static void
00240 get_version(void)
00241 {
00242     memset(&osver, 0, sizeof(OSVERSIONINFO));
00243     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00244     GetVersionEx(&osver);
00245 }
00246 
00247 #ifdef _M_IX86
00248 DWORD
00249 rb_w32_osid(void)
00250 {
00251     return osver.dwPlatformId;
00252 }
00253 #endif
00254 
00255 DWORD
00256 rb_w32_osver(void)
00257 {
00258     return osver.dwMajorVersion;
00259 }
00260 
00261 #define IsWinNT() rb_w32_iswinnt()
00262 #define IsWin95() rb_w32_iswin95()
00263 #ifdef WIN95
00264 #define IfWin95(win95, winnt) (IsWin95() ? (win95) : (winnt))
00265 #else
00266 #define IfWin95(win95, winnt) (winnt)
00267 #endif
00268 
00269 HANDLE
00270 GetCurrentThreadHandle(void)
00271 {
00272     static HANDLE current_process_handle = NULL;
00273     HANDLE h;
00274 
00275     if (!current_process_handle)
00276         current_process_handle = GetCurrentProcess();
00277     if (!DuplicateHandle(current_process_handle, GetCurrentThread(),
00278                          current_process_handle, &h,
00279                          0, FALSE, DUPLICATE_SAME_ACCESS))
00280         return NULL;
00281     return h;
00282 }
00283 
00284 /* simulate flock by locking a range on the file */
00285 
00286 
00287 #define LK_ERR(f,i) \
00288     do {                                                                \
00289         if (f)                                                          \
00290             i = 0;                                                      \
00291         else {                                                          \
00292             DWORD err = GetLastError();                                 \
00293             if (err == ERROR_LOCK_VIOLATION || err == ERROR_IO_PENDING) \
00294                 errno = EWOULDBLOCK;                                    \
00295             else if (err == ERROR_NOT_LOCKED)                           \
00296                 i = 0;                                                  \
00297             else                                                        \
00298                 errno = map_errno(err);                                 \
00299         }                                                               \
00300     } while (0)
00301 #define LK_LEN      ULONG_MAX
00302 
00303 static uintptr_t
00304 flock_winnt(uintptr_t self, int argc, uintptr_t* argv)
00305 {
00306     OVERLAPPED o;
00307     int i = -1;
00308     const HANDLE fh = (HANDLE)self;
00309     const int oper = argc;
00310 
00311     memset(&o, 0, sizeof(o));
00312 
00313     switch(oper) {
00314       case LOCK_SH:             /* shared lock */
00315         LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
00316         break;
00317       case LOCK_EX:             /* exclusive lock */
00318         LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
00319         break;
00320       case LOCK_SH|LOCK_NB:     /* non-blocking shared lock */
00321         LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
00322         break;
00323       case LOCK_EX|LOCK_NB:     /* non-blocking exclusive lock */
00324         LK_ERR(LockFileEx(fh,
00325                           LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
00326                           0, LK_LEN, LK_LEN, &o), i);
00327         break;
00328       case LOCK_UN:             /* unlock lock */
00329       case LOCK_UN|LOCK_NB:     /* unlock is always non-blocking, I hope */
00330         LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
00331         break;
00332       default:            /* unknown */
00333         errno = EINVAL;
00334         break;
00335     }
00336     return i;
00337 }
00338 
00339 #ifdef WIN95
00340 static uintptr_t
00341 flock_win95(uintptr_t self, int argc, uintptr_t* argv)
00342 {
00343     int i = -1;
00344     const HANDLE fh = (HANDLE)self;
00345     const int oper = argc;
00346 
00347     switch(oper) {
00348       case LOCK_EX:
00349         do {
00350             LK_ERR(LockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
00351         } while (i && errno == EWOULDBLOCK);
00352         break;
00353       case LOCK_EX|LOCK_NB:
00354         LK_ERR(LockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
00355         break;
00356       case LOCK_UN:
00357       case LOCK_UN|LOCK_NB:
00358         LK_ERR(UnlockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
00359         break;
00360       default:
00361         errno = EINVAL;
00362         break;
00363     }
00364     return i;
00365 }
00366 #endif
00367 
00368 #undef LK_ERR
00369 
00370 int
00371 flock(int fd, int oper)
00372 {
00373 #ifdef WIN95
00374     static asynchronous_func_t locker = NULL;
00375 
00376     if (!locker) {
00377         if (IsWinNT())
00378             locker = flock_winnt;
00379         else
00380             locker = flock_win95;
00381     }
00382 #else
00383     const asynchronous_func_t locker = flock_winnt;
00384 #endif
00385 
00386     return rb_w32_asynchronize(locker,
00387                               (VALUE)_get_osfhandle(fd), oper, NULL,
00388                               (DWORD)-1);
00389 }
00390 
00391 static inline WCHAR *
00392 translate_wchar(WCHAR *p, int from, int to)
00393 {
00394     for (; *p; p++) {
00395         if (*p == from)
00396             *p = to;
00397     }
00398     return p;
00399 }
00400 
00401 static inline char *
00402 translate_char(char *p, int from, int to)
00403 {
00404     while (*p) {
00405         if ((unsigned char)*p == from)
00406             *p = to;
00407         p = CharNext(p);
00408     }
00409     return p;
00410 }
00411 
00412 #ifndef CSIDL_LOCAL_APPDATA
00413 #define CSIDL_LOCAL_APPDATA 28
00414 #endif
00415 #ifndef CSIDL_COMMON_APPDATA
00416 #define CSIDL_COMMON_APPDATA 35
00417 #endif
00418 #ifndef CSIDL_WINDOWS
00419 #define CSIDL_WINDOWS   36
00420 #endif
00421 #ifndef CSIDL_SYSTEM
00422 #define CSIDL_SYSTEM    37
00423 #endif
00424 #ifndef CSIDL_PROFILE
00425 #define CSIDL_PROFILE 40
00426 #endif
00427 
00428 static BOOL
00429 get_special_folder(int n, WCHAR *env)
00430 {
00431     LPITEMIDLIST pidl;
00432     LPMALLOC alloc;
00433     BOOL f = FALSE;
00434     if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
00435         f = SHGetPathFromIDListW(pidl, env);
00436         SHGetMalloc(&alloc);
00437         alloc->lpVtbl->Free(alloc, pidl);
00438         alloc->lpVtbl->Release(alloc);
00439     }
00440     return f;
00441 }
00442 
00443 static void
00444 regulate_path(WCHAR *path)
00445 {
00446     WCHAR *p = translate_wchar(path, L'\\', L'/');
00447     if (p - path == 2 && path[1] == L':') {
00448         *p++ = L'/';
00449         *p = L'\0';
00450     }
00451 }
00452 
00453 static FARPROC
00454 get_proc_address(const char *module, const char *func, HANDLE *mh)
00455 {
00456     HANDLE h;
00457     FARPROC ptr;
00458 
00459     if (mh)
00460         h = LoadLibrary(module);
00461     else
00462         h = GetModuleHandle(module);
00463     if (!h)
00464         return NULL;
00465 
00466     ptr = GetProcAddress(h, func);
00467     if (mh) {
00468         if (ptr)
00469             *mh = h;
00470         else
00471             FreeLibrary(h);
00472     }
00473     return ptr;
00474 }
00475 
00476 static UINT
00477 get_system_directory(WCHAR *path, UINT len)
00478 {
00479     typedef UINT WINAPI wgetdir_func(WCHAR*, UINT);
00480     FARPROC ptr =
00481         get_proc_address("kernel32", "GetSystemWindowsDirectoryW", NULL);
00482     if (ptr)
00483         return (*(wgetdir_func *)ptr)(path, len);
00484     return GetWindowsDirectoryW(path, len);
00485 }
00486 
00487 #define numberof(array) (sizeof(array) / sizeof(*array))
00488 
00489 VALUE
00490 rb_w32_special_folder(int type)
00491 {
00492     WCHAR path[_MAX_PATH];
00493 
00494     if (!get_special_folder(type, path)) return Qnil;
00495     regulate_path(path);
00496     return rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
00497 }
00498 
00499 UINT
00500 rb_w32_system_tmpdir(WCHAR *path, UINT len)
00501 {
00502     static const WCHAR temp[] = L"temp";
00503     WCHAR *p;
00504 
00505     if (!get_special_folder(CSIDL_LOCAL_APPDATA, path)) {
00506         if (get_system_directory(path, len)) return 0;
00507     }
00508     p = translate_wchar(path, L'\\', L'/');
00509     if (*(p - 1) != L'/') *p++ = L'/';
00510     if (p - path + numberof(temp) >= len) return 0;
00511     memcpy(p, temp, sizeof(temp));
00512     return p - path + numberof(temp) - 1;
00513 }
00514 
00515 static void
00516 init_env(void)
00517 {
00518     static const WCHAR TMPDIR[] = L"TMPDIR";
00519     struct {WCHAR name[6], eq, val[_MAX_PATH];} wk;
00520     DWORD len;
00521     BOOL f;
00522 #define env wk.val
00523 #define set_env_val(vname) do { \
00524         typedef char namesizecheck[numberof(wk.name) < numberof(vname) - 1 ? -1 : 1]; \
00525         WCHAR *const buf = wk.name + numberof(wk.name) - numberof(vname) + 1; \
00526         MEMCPY(buf, vname, WCHAR, numberof(vname) - 1); \
00527         _wputenv(buf); \
00528     } while (0)
00529 
00530     wk.eq = L'=';
00531 
00532     if (!GetEnvironmentVariableW(L"HOME", env, numberof(env))) {
00533         f = FALSE;
00534         if (GetEnvironmentVariableW(L"HOMEDRIVE", env, numberof(env)))
00535             len = lstrlenW(env);
00536         else
00537             len = 0;
00538         if (GetEnvironmentVariableW(L"HOMEPATH", env + len, numberof(env) - len) || len) {
00539             f = TRUE;
00540         }
00541         else if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
00542             f = TRUE;
00543         }
00544         else if (get_special_folder(CSIDL_PROFILE, env)) {
00545             f = TRUE;
00546         }
00547         else if (get_special_folder(CSIDL_PERSONAL, env)) {
00548             f = TRUE;
00549         }
00550         if (f) {
00551             regulate_path(env);
00552             set_env_val(L"HOME");
00553         }
00554     }
00555 
00556     if (!GetEnvironmentVariableW(L"USER", env, numberof(env))) {
00557         if (!GetEnvironmentVariableW(L"USERNAME", env, numberof(env)) &&
00558             !GetUserNameW(env, (len = numberof(env), &len))) {
00559             NTLoginName = "<Unknown>";
00560             return;
00561         }
00562         set_env_val(L"USER");
00563     }
00564     NTLoginName = strdup(rb_w32_getenv("USER"));
00565 
00566     if (!GetEnvironmentVariableW(TMPDIR, env, numberof(env)) &&
00567         !GetEnvironmentVariableW(L"TMP", env, numberof(env)) &&
00568         !GetEnvironmentVariableW(L"TEMP", env, numberof(env)) &&
00569         rb_w32_system_tmpdir(env, numberof(env))) {
00570         set_env_val(TMPDIR);
00571     }
00572 
00573 #undef env
00574 #undef set_env_val
00575 }
00576 
00577 
00578 typedef BOOL (WINAPI *cancel_io_t)(HANDLE);
00579 static cancel_io_t cancel_io = NULL;
00580 
00581 static void
00582 init_func(void)
00583 {
00584     if (!cancel_io)
00585         cancel_io = (cancel_io_t)get_proc_address("kernel32", "CancelIo", NULL);
00586 }
00587 
00588 static void init_stdhandle(void);
00589 
00590 #if RT_VER >= 80
00591 static void
00592 invalid_parameter(const wchar_t *expr, const wchar_t *func, const wchar_t *file, unsigned int line, uintptr_t dummy)
00593 {
00594     // nothing to do
00595 }
00596 
00597 int ruby_w32_rtc_error;
00598 
00599 static int __cdecl
00600 rtc_error_handler(int e, const char *src, int line, const char *exe, const char *fmt, ...)
00601 {
00602     va_list ap;
00603     VALUE str;
00604 
00605     if (!ruby_w32_rtc_error) return 0;
00606     str = rb_sprintf("%s:%d: ", src, line);
00607     va_start(ap, fmt);
00608     rb_str_vcatf(str, fmt, ap);
00609     va_end(ap);
00610     rb_str_cat(str, "\n", 1);
00611     rb_write_error2(RSTRING_PTR(str), RSTRING_LEN(str));
00612     return 0;
00613 }
00614 #endif
00615 
00616 static CRITICAL_SECTION select_mutex;
00617 static int NtSocketsInitialized = 0;
00618 static st_table *socklist = NULL;
00619 static char *envarea;
00620 
00621 static void
00622 exit_handler(void)
00623 {
00624     if (NtSocketsInitialized) {
00625         WSACleanup();
00626         st_free_table(socklist);
00627         socklist = NULL;
00628         NtSocketsInitialized = 0;
00629     }
00630     if (envarea) {
00631         FreeEnvironmentStrings(envarea);
00632         envarea = NULL;
00633     }
00634     DeleteCriticalSection(&select_mutex);
00635 }
00636 
00637 static void
00638 StartSockets(void)
00639 {
00640     WORD version;
00641     WSADATA retdata;
00642 
00643     //
00644     // initalize the winsock interface and insure that it's
00645     // cleaned up at exit.
00646     //
00647     version = MAKEWORD(2, 0);
00648     if (WSAStartup(version, &retdata))
00649         rb_fatal ("Unable to locate winsock library!\n");
00650     if (LOBYTE(retdata.wVersion) != 2)
00651         rb_fatal("could not find version 2 of winsock dll\n");
00652 
00653     socklist = st_init_numtable();
00654 
00655     NtSocketsInitialized = 1;
00656 }
00657 
00658 //
00659 // Initialization stuff
00660 //
00661 void
00662 rb_w32_sysinit(int *argc, char ***argv)
00663 {
00664 #if RT_VER >= 80
00665     static void set_pioinfo_extra(void);
00666 
00667     _CrtSetReportMode(_CRT_ASSERT, 0);
00668     _set_invalid_parameter_handler(invalid_parameter);
00669     _RTC_SetErrorFunc(rtc_error_handler);
00670     set_pioinfo_extra();
00671 #else
00672     SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
00673 #endif
00674 
00675     get_version();
00676 
00677     //
00678     // subvert cmd.exe's feeble attempt at command line parsing
00679     //
00680     *argc = rb_w32_cmdvector(GetCommandLine(), argv);
00681 
00682     //
00683     // Now set up the correct time stuff
00684     //
00685 
00686     tzset();
00687 
00688     init_env();
00689 
00690     init_func();
00691 
00692     init_stdhandle();
00693 
00694     InitializeCriticalSection(&select_mutex);
00695 
00696     atexit(exit_handler);
00697 
00698     // Initialize Winsock
00699     StartSockets();
00700 }
00701 
00702 char *
00703 getlogin(void)
00704 {
00705     return (char *)NTLoginName;
00706 }
00707 
00708 #define MAXCHILDNUM 256 /* max num of child processes */
00709 
00710 static struct ChildRecord {
00711     HANDLE hProcess;    /* process handle */
00712     rb_pid_t pid;       /* process id */
00713 } ChildRecord[MAXCHILDNUM];
00714 
00715 #define FOREACH_CHILD(v) do { \
00716     struct ChildRecord* v; \
00717     for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
00718 #define END_FOREACH_CHILD } while (0)
00719 
00720 static struct ChildRecord *
00721 FindChildSlot(rb_pid_t pid)
00722 {
00723 
00724     FOREACH_CHILD(child) {
00725         if (child->pid == pid) {
00726             return child;
00727         }
00728     } END_FOREACH_CHILD;
00729     return NULL;
00730 }
00731 
00732 static struct ChildRecord *
00733 FindChildSlotByHandle(HANDLE h)
00734 {
00735 
00736     FOREACH_CHILD(child) {
00737         if (child->hProcess == h) {
00738             return child;
00739         }
00740     } END_FOREACH_CHILD;
00741     return NULL;
00742 }
00743 
00744 static void
00745 CloseChildHandle(struct ChildRecord *child)
00746 {
00747     HANDLE h = child->hProcess;
00748     child->hProcess = NULL;
00749     child->pid = 0;
00750     CloseHandle(h);
00751 }
00752 
00753 static struct ChildRecord *
00754 FindFreeChildSlot(void)
00755 {
00756     FOREACH_CHILD(child) {
00757         if (!child->pid) {
00758             child->pid = -1;    /* lock the slot */
00759             child->hProcess = NULL;
00760             return child;
00761         }
00762     } END_FOREACH_CHILD;
00763     return NULL;
00764 }
00765 
00766 
00767 /*
00768   ruby -lne 'BEGIN{$cmds = Hash.new(0); $mask = 1}'
00769    -e '$cmds[$_.downcase] |= $mask' -e '$mask <<= 1 if ARGF.eof'
00770    -e 'END{$cmds.sort.each{|n,f|puts "    \"\\#{f.to_s(8)}\" #{n.dump} + 1,"}}'
00771    98cmd ntcmd
00772  */
00773 static const char *const szInternalCmds[] = {
00774     "\2" "assoc" + 1,
00775     "\3" "break" + 1,
00776     "\3" "call" + 1,
00777     "\3" "cd" + 1,
00778     "\1" "chcp" + 1,
00779     "\3" "chdir" + 1,
00780     "\3" "cls" + 1,
00781     "\2" "color" + 1,
00782     "\3" "copy" + 1,
00783     "\1" "ctty" + 1,
00784     "\3" "date" + 1,
00785     "\3" "del" + 1,
00786     "\3" "dir" + 1,
00787     "\3" "echo" + 1,
00788     "\2" "endlocal" + 1,
00789     "\3" "erase" + 1,
00790     "\3" "exit" + 1,
00791     "\3" "for" + 1,
00792     "\2" "ftype" + 1,
00793     "\3" "goto" + 1,
00794     "\3" "if" + 1,
00795     "\1" "lfnfor" + 1,
00796     "\1" "lh" + 1,
00797     "\1" "lock" + 1,
00798     "\3" "md" + 1,
00799     "\3" "mkdir" + 1,
00800     "\2" "move" + 1,
00801     "\3" "path" + 1,
00802     "\3" "pause" + 1,
00803     "\2" "popd" + 1,
00804     "\3" "prompt" + 1,
00805     "\2" "pushd" + 1,
00806     "\3" "rd" + 1,
00807     "\3" "rem" + 1,
00808     "\3" "ren" + 1,
00809     "\3" "rename" + 1,
00810     "\3" "rmdir" + 1,
00811     "\3" "set" + 1,
00812     "\2" "setlocal" + 1,
00813     "\3" "shift" + 1,
00814     "\2" "start" + 1,
00815     "\3" "time" + 1,
00816     "\2" "title" + 1,
00817     "\1" "truename" + 1,
00818     "\3" "type" + 1,
00819     "\1" "unlock" + 1,
00820     "\3" "ver" + 1,
00821     "\3" "verify" + 1,
00822     "\3" "vol" + 1,
00823 };
00824 
00825 static int
00826 internal_match(const void *key, const void *elem)
00827 {
00828     return strcmp(key, *(const char *const *)elem);
00829 }
00830 
00831 static int
00832 is_command_com(const char *interp)
00833 {
00834     int i = strlen(interp) - 11;
00835 
00836     if ((i == 0 || i > 0 && isdirsep(interp[i-1])) &&
00837         strcasecmp(interp+i, "command.com") == 0) {
00838         return 1;
00839     }
00840     return 0;
00841 }
00842 
00843 static int internal_cmd_match(const char *cmdname, int nt);
00844 
00845 static int
00846 is_internal_cmd(const char *cmd, int nt)
00847 {
00848     char cmdname[9], *b = cmdname, c;
00849 
00850     do {
00851         if (!(c = *cmd++)) return 0;
00852     } while (isspace(c));
00853     if (c == '@')
00854         return 1;
00855     while (isalpha(c)) {
00856         *b++ = tolower(c);
00857         if (b == cmdname + sizeof(cmdname)) return 0;
00858         c = *cmd++;
00859     }
00860     if (c == '.') c = *cmd;
00861     switch (c) {
00862       case '<': case '>': case '|':
00863         return 1;
00864       case '\0': case ' ': case '\t': case '\n':
00865         break;
00866       default:
00867         return 0;
00868     }
00869     *b = 0;
00870     return internal_cmd_match(cmdname, nt);
00871 }
00872 
00873 static int
00874 internal_cmd_match(const char *cmdname, int nt)
00875 {
00876     char **nm;
00877 
00878     nm = bsearch(cmdname, szInternalCmds,
00879                  sizeof(szInternalCmds) / sizeof(*szInternalCmds),
00880                  sizeof(*szInternalCmds),
00881                  internal_match);
00882     if (!nm || !(nm[0][-1] & (nt ? 2 : 1)))
00883         return 0;
00884     return 1;
00885 }
00886 
00887 SOCKET
00888 rb_w32_get_osfhandle(int fh)
00889 {
00890     return _get_osfhandle(fh);
00891 }
00892 
00893 static int
00894 join_argv(char *cmd, char *const *argv, BOOL escape)
00895 {
00896     const char *p, *s;
00897     char *q, *const *t;
00898     int len, n, bs, quote;
00899 
00900     for (t = argv, q = cmd, len = 0; p = *t; t++) {
00901         quote = 0;
00902         s = p;
00903         if (!*p || strpbrk(p, " \t\"'")) {
00904             quote = 1;
00905             len++;
00906             if (q) *q++ = '"';
00907         }
00908         for (bs = 0; *p; ++p) {
00909             switch (*p) {
00910               case '\\':
00911                 ++bs;
00912                 break;
00913               case '"':
00914                 len += n = p - s;
00915                 if (q) {
00916                     memcpy(q, s, n);
00917                     q += n;
00918                 }
00919                 s = p;
00920                 len += ++bs;
00921                 if (q) {
00922                     memset(q, '\\', bs);
00923                     q += bs;
00924                 }
00925                 bs = 0;
00926                 break;
00927               case '<': case '>': case '|': case '^':
00928                 if (escape && !quote) {
00929                     len += (n = p - s) + 1;
00930                     if (q) {
00931                         memcpy(q, s, n);
00932                         q += n;
00933                         *q++ = '^';
00934                     }
00935                     s = p;
00936                     break;
00937                 }
00938               default:
00939                 bs = 0;
00940                 p = CharNext(p) - 1;
00941                 break;
00942             }
00943         }
00944         len += (n = p - s) + 1;
00945         if (quote) len++;
00946         if (q) {
00947             memcpy(q, s, n);
00948             q += n;
00949             if (quote) *q++ = '"';
00950             *q++ = ' ';
00951         }
00952     }
00953     if (q > cmd) --len;
00954     if (q) {
00955         if (q > cmd) --q;
00956         *q = '\0';
00957     }
00958     return len;
00959 }
00960 
00961 #ifdef HAVE_SYS_PARAM_H
00962 # include <sys/param.h>
00963 #else
00964 # define MAXPATHLEN 512
00965 #endif
00966 
00967 #define STRNDUPV(ptr, v, src, len)                                      \
00968     (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
00969 
00970 static int
00971 check_spawn_mode(int mode)
00972 {
00973     switch (mode) {
00974       case P_NOWAIT:
00975       case P_OVERLAY:
00976         return 0;
00977       default:
00978         errno = EINVAL;
00979         return -1;
00980     }
00981 }
00982 
00983 static rb_pid_t
00984 child_result(struct ChildRecord *child, int mode)
00985 {
00986     DWORD exitcode;
00987 
00988     if (!child) {
00989         return -1;
00990     }
00991 
00992     switch (mode) {
00993       case P_NOWAIT:
00994         return child->pid;
00995       case P_OVERLAY:
00996         WaitForSingleObject(child->hProcess, INFINITE);
00997         GetExitCodeProcess(child->hProcess, &exitcode);
00998         CloseChildHandle(child);
00999         _exit(exitcode);
01000       default:
01001         return -1;      /* not reached */
01002     }
01003 }
01004 
01005 static struct ChildRecord *
01006 CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
01007             HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
01008 {
01009     BOOL fRet;
01010     STARTUPINFOW aStartupInfo;
01011     PROCESS_INFORMATION aProcessInformation;
01012     SECURITY_ATTRIBUTES sa;
01013     struct ChildRecord *child;
01014 
01015     if (!cmd && !prog) {
01016         errno = EFAULT;
01017         return NULL;
01018     }
01019 
01020     child = FindFreeChildSlot();
01021     if (!child) {
01022         errno = EAGAIN;
01023         return NULL;
01024     }
01025 
01026     if (!psa) {
01027         sa.nLength              = sizeof (SECURITY_ATTRIBUTES);
01028         sa.lpSecurityDescriptor = NULL;
01029         sa.bInheritHandle       = TRUE;
01030         psa = &sa;
01031     }
01032 
01033     memset(&aStartupInfo, 0, sizeof(aStartupInfo));
01034     memset(&aProcessInformation, 0, sizeof(aProcessInformation));
01035     aStartupInfo.cb = sizeof(aStartupInfo);
01036     aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
01037     if (hInput) {
01038         aStartupInfo.hStdInput  = hInput;
01039     }
01040     else {
01041         aStartupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
01042     }
01043     if (hOutput) {
01044         aStartupInfo.hStdOutput = hOutput;
01045     }
01046     else {
01047         aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
01048     }
01049     if (hError) {
01050         aStartupInfo.hStdError = hError;
01051     }
01052     else {
01053         aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
01054     }
01055 
01056     dwCreationFlags |= NORMAL_PRIORITY_CLASS;
01057 
01058     if (lstrlenW(cmd) > 32767) {
01059         child->pid = 0;         /* release the slot */
01060         errno = E2BIG;
01061         return NULL;
01062     }
01063 
01064     RUBY_CRITICAL({
01065         fRet = CreateProcessW(prog, (WCHAR *)cmd, psa, psa,
01066                               psa->bInheritHandle, dwCreationFlags, NULL, NULL,
01067                               &aStartupInfo, &aProcessInformation);
01068         errno = map_errno(GetLastError());
01069     });
01070 
01071     if (!fRet) {
01072         child->pid = 0;         /* release the slot */
01073         return NULL;
01074     }
01075 
01076     CloseHandle(aProcessInformation.hThread);
01077 
01078     child->hProcess = aProcessInformation.hProcess;
01079     child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
01080 
01081     if (!IsWinNT()) {
01082         /* On Win9x, make pid positive similarly to cygwin and perl */
01083         child->pid = -child->pid;
01084     }
01085 
01086     return child;
01087 }
01088 
01089 static int
01090 is_batch(const char *cmd)
01091 {
01092     int len = strlen(cmd);
01093     if (len <= 4) return 0;
01094     cmd += len - 4;
01095     if (*cmd++ != '.') return 0;
01096     if (strcasecmp(cmd, "bat") == 0) return 1;
01097     if (strcasecmp(cmd, "cmd") == 0) return 1;
01098     return 0;
01099 }
01100 
01101 static UINT filecp(void);
01102 static WCHAR *mbstr_to_wstr(UINT, const char *, int, long *);
01103 static char *wstr_to_mbstr(UINT, const WCHAR *, int, long *);
01104 #define acp_to_wstr(str, plen) mbstr_to_wstr(CP_ACP, str, -1, plen)
01105 #define wstr_to_acp(str, plen) wstr_to_mbstr(CP_ACP, str, -1, plen)
01106 #define filecp_to_wstr(str, plen) mbstr_to_wstr(filecp(), str, -1, plen)
01107 #define wstr_to_filecp(str, plen) wstr_to_mbstr(filecp(), str, -1, plen)
01108 #define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
01109 #define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
01110 
01111 rb_pid_t
01112 rb_w32_spawn(int mode, const char *cmd, const char *prog)
01113 {
01114     char fbuf[MAXPATHLEN];
01115     char *p = NULL;
01116     const char *shell = NULL;
01117     WCHAR *wcmd, *wshell;
01118     rb_pid_t ret;
01119     VALUE v = 0;
01120     VALUE v2 = 0;
01121 
01122     if (check_spawn_mode(mode)) return -1;
01123 
01124     if (prog) {
01125         if (!(p = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
01126             shell = prog;
01127         }
01128         else {
01129             shell = p;
01130             translate_char(p, '/', '\\');
01131         }
01132     }
01133     else {
01134         int redir = -1;
01135         int nt;
01136         while (ISSPACE(*cmd)) cmd++;
01137         if ((shell = getenv("RUBYSHELL")) && (redir = has_redirection(cmd))) {
01138             char *tmp = ALLOCV(v, strlen(shell) + strlen(cmd) + sizeof(" -c ") + 2);
01139             sprintf(tmp, "%s -c \"%s\"", shell, cmd);
01140             cmd = tmp;
01141         }
01142         else if ((shell = getenv("COMSPEC")) &&
01143                  (nt = !is_command_com(shell),
01144                   (redir < 0 ? has_redirection(cmd) : redir) ||
01145                   is_internal_cmd(cmd, nt))) {
01146             char *tmp = ALLOCV(v, strlen(shell) + strlen(cmd) + sizeof(" /c ") + (nt ? 2 : 0));
01147             sprintf(tmp, nt ? "%s /c \"%s\"" : "%s /c %s", shell, cmd);
01148             cmd = tmp;
01149         }
01150         else {
01151             int len = 0, quote = (*cmd == '"') ? '"' : (*cmd == '\'') ? '\'' : 0;
01152             for (prog = cmd + !!quote;; prog = CharNext(prog)) {
01153                 if (!*prog) {
01154                     len = prog - cmd;
01155                     shell = cmd;
01156                     break;
01157                 }
01158                 if ((unsigned char)*prog == quote) {
01159                     len = prog++ - cmd - 1;
01160                     STRNDUPV(p, v2, cmd + 1, len);
01161                     shell = p;
01162                     break;
01163                 }
01164                 if (quote) continue;
01165                 if (ISSPACE(*prog) || strchr("<>|*?\"", *prog)) {
01166                     len = prog - cmd;
01167                     STRNDUPV(p, v2, cmd, len);
01168                     shell = p;
01169                     break;
01170                 }
01171             }
01172             shell = dln_find_exe_r(shell, NULL, fbuf, sizeof(fbuf));
01173             if (!shell) {
01174                 shell = p ? p : cmd;
01175             }
01176             else {
01177                 len = strlen(shell);
01178                 if (strchr(shell, ' ')) quote = -1;
01179                 if (shell == fbuf) {
01180                     p = fbuf;
01181                 }
01182                 else if (shell != p && strchr(shell, '/')) {
01183                     STRNDUPV(p, v2, shell, len);
01184                     shell = p;
01185                 }
01186                 if (p) translate_char(p, '/', '\\');
01187                 if (is_batch(shell)) {
01188                     int alen = strlen(prog);
01189                     cmd = p = ALLOCV(v, len + alen + (quote ? 2 : 0) + 1);
01190                     if (quote) *p++ = '"';
01191                     memcpy(p, shell, len);
01192                     p += len;
01193                     if (quote) *p++ = '"';
01194                     memcpy(p, prog, alen + 1);
01195                     shell = 0;
01196                 }
01197             }
01198         }
01199     }
01200 
01201     /* assume ACP */
01202     wcmd = cmd ? acp_to_wstr(cmd, NULL) : NULL;
01203     if (v) ALLOCV_END(v);
01204     wshell = shell ? acp_to_wstr(shell, NULL) : NULL;
01205     if (v2) ALLOCV_END(v2);
01206 
01207     ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL, 0), mode);
01208     free(wshell);
01209     free(wcmd);
01210     return ret;
01211 }
01212 
01213 rb_pid_t
01214 rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
01215 {
01216     int c_switch = 0;
01217     size_t len;
01218     BOOL ntcmd = FALSE, tmpnt;
01219     const char *shell;
01220     char *cmd, fbuf[MAXPATHLEN];
01221     WCHAR *wcmd, *wprog;
01222     rb_pid_t ret;
01223     VALUE v = 0;
01224 
01225     if (check_spawn_mode(mode)) return -1;
01226 
01227     if (!prog) prog = argv[0];
01228     if ((shell = getenv("COMSPEC")) &&
01229         internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
01230         ntcmd = tmpnt;
01231         prog = shell;
01232         c_switch = 1;
01233     }
01234     else if ((cmd = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
01235         if (cmd == prog) strlcpy(cmd = fbuf, prog, sizeof(fbuf));
01236         translate_char(cmd, '/', '\\');
01237         prog = cmd;
01238     }
01239     else if (strchr(prog, '/')) {
01240         len = strlen(prog);
01241         if (len < sizeof(fbuf))
01242             strlcpy(cmd = fbuf, prog, sizeof(fbuf));
01243         else
01244             STRNDUPV(cmd, v, prog, len);
01245         translate_char(cmd, '/', '\\');
01246         prog = cmd;
01247     }
01248     if (c_switch || is_batch(prog)) {
01249         char *progs[2];
01250         progs[0] = (char *)prog;
01251         progs[1] = NULL;
01252         len = join_argv(NULL, progs, ntcmd);
01253         if (c_switch) len += 3;
01254         else ++argv;
01255         if (argv[0]) len += join_argv(NULL, argv, ntcmd);
01256         cmd = ALLOCV(v, len);
01257         join_argv(cmd, progs, ntcmd);
01258         if (c_switch) strlcat(cmd, " /c", len);
01259         if (argv[0]) join_argv(cmd + strlcat(cmd, " ", len), argv, ntcmd);
01260         prog = c_switch ? shell : 0;
01261     }
01262     else {
01263         len = join_argv(NULL, argv, FALSE);
01264         cmd = ALLOCV(v, len);
01265         join_argv(cmd, argv, FALSE);
01266     }
01267 
01268     /* assume ACP */
01269     wcmd = cmd ? acp_to_wstr(cmd, NULL) : NULL;
01270     if (v) ALLOCV_END(v);
01271     wprog = prog ? acp_to_wstr(prog, NULL) : NULL;
01272 
01273     ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL, flags), mode);
01274     free(wprog);
01275     free(wcmd);
01276     return ret;
01277 }
01278 
01279 rb_pid_t
01280 rb_w32_aspawn(int mode, const char *prog, char *const *argv)
01281 {
01282     return rb_w32_aspawn_flags(mode, prog, argv, 0);
01283 }
01284 
01285 /* License: Artistic or GPL */
01286 typedef struct _NtCmdLineElement {
01287     struct _NtCmdLineElement *next;
01288     char *str;
01289     int len;
01290     int flags;
01291 } NtCmdLineElement;
01292 
01293 //
01294 // Possible values for flags
01295 //
01296 
01297 #define NTGLOB   0x1    // element contains a wildcard
01298 #define NTMALLOC 0x2    // string in element was malloc'ed
01299 #define NTSTRING 0x4    // element contains a quoted string
01300 
01301 static int
01302 insert(const char *path, VALUE vinfo, void *enc)
01303 {
01304     NtCmdLineElement *tmpcurr;
01305     NtCmdLineElement ***tail = (NtCmdLineElement ***)vinfo;
01306 
01307     tmpcurr = (NtCmdLineElement *)malloc(sizeof(NtCmdLineElement));
01308     if (!tmpcurr) return -1;
01309     MEMZERO(tmpcurr, NtCmdLineElement, 1);
01310     tmpcurr->len = strlen(path);
01311     tmpcurr->str = strdup(path);
01312     if (!tmpcurr->str) return -1;
01313     tmpcurr->flags |= NTMALLOC;
01314     **tail = tmpcurr;
01315     *tail = &tmpcurr->next;
01316 
01317     return 0;
01318 }
01319 
01320 
01321 static NtCmdLineElement **
01322 cmdglob(NtCmdLineElement *patt, NtCmdLineElement **tail)
01323 {
01324     char buffer[MAXPATHLEN], *buf = buffer;
01325     char *p;
01326     NtCmdLineElement **last = tail;
01327     int status;
01328 
01329     if (patt->len >= MAXPATHLEN)
01330         if (!(buf = malloc(patt->len + 1))) return 0;
01331 
01332     strlcpy(buf, patt->str, patt->len + 1);
01333     buf[patt->len] = '\0';
01334     for (p = buf; *p; p = CharNext(p))
01335         if (*p == '\\')
01336             *p = '/';
01337     status = ruby_brace_glob(buf, 0, insert, (VALUE)&tail);
01338     if (buf != buffer)
01339         free(buf);
01340 
01341     if (status || last == tail) return 0;
01342     if (patt->flags & NTMALLOC)
01343         free(patt->str);
01344     free(patt);
01345     return tail;
01346 }
01347 
01348 //
01349 // Check a command string to determine if it has I/O redirection
01350 // characters that require it to be executed by a command interpreter
01351 //
01352 
01353 static int
01354 has_redirection(const char *cmd)
01355 {
01356     char quote = '\0';
01357     const char *ptr;
01358 
01359     //
01360     // Scan the string, looking for redirection characters (< or >), pipe
01361     // character (|) or newline (\n) that are not in a quoted string
01362     //
01363 
01364     for (ptr = cmd; *ptr;) {
01365         switch (*ptr) {
01366           case '\'':
01367           case '\"':
01368             if (!quote)
01369                 quote = *ptr;
01370             else if (quote == *ptr)
01371                 quote = '\0';
01372             ptr++;
01373             break;
01374 
01375           case '>':
01376           case '<':
01377           case '|':
01378           case '\n':
01379             if (!quote)
01380                 return TRUE;
01381             ptr++;
01382             break;
01383 
01384           case '%':
01385             if (*++ptr != '_' && !ISALPHA(*ptr)) break;
01386             while (*++ptr == '_' || ISALNUM(*ptr));
01387             if (*ptr++ == '%') return TRUE;
01388             break;
01389 
01390           case '\\':
01391             ptr++;
01392           default:
01393             ptr = CharNext(ptr);
01394             break;
01395         }
01396     }
01397     return FALSE;
01398 }
01399 
01400 static inline char *
01401 skipspace(char *ptr)
01402 {
01403     while (ISSPACE(*ptr))
01404         ptr++;
01405     return ptr;
01406 }
01407 
01408 int
01409 rb_w32_cmdvector(const char *cmd, char ***vec)
01410 {
01411     int globbing, len;
01412     int elements, strsz, done;
01413     int slashes, escape;
01414     char *ptr, *base, *buffer, *cmdline;
01415     char **vptr;
01416     char quote;
01417     NtCmdLineElement *curr, **tail;
01418     NtCmdLineElement *cmdhead = NULL, **cmdtail = &cmdhead;
01419 
01420     //
01421     // just return if we don't have a command line
01422     //
01423 
01424     while (ISSPACE(*cmd))
01425         cmd++;
01426     if (!*cmd) {
01427         *vec = NULL;
01428         return 0;
01429     }
01430 
01431     ptr = cmdline = strdup(cmd);
01432 
01433     //
01434     // Ok, parse the command line, building a list of CmdLineElements.
01435     // When we've finished, and it's an input command (meaning that it's
01436     // the processes argv), we'll do globing and then build the argument
01437     // vector.
01438     // The outer loop does one interation for each element seen.
01439     // The inner loop does one interation for each character in the element.
01440     //
01441 
01442     while (*(ptr = skipspace(ptr))) {
01443         base = ptr;
01444         quote = slashes = globbing = escape = 0;
01445         for (done = 0; !done && *ptr; ) {
01446             //
01447             // Switch on the current character. We only care about the
01448             // white-space characters, the  wild-card characters, and the
01449             // quote characters.
01450             //
01451 
01452             switch (*ptr) {
01453               case '\\':
01454                 if (quote != '\'') slashes++;
01455                 break;
01456 
01457               case ' ':
01458               case '\t':
01459               case '\n':
01460                 //
01461                 // if we're not in a string, then we're finished with this
01462                 // element
01463                 //
01464 
01465                 if (!quote) {
01466                     *ptr = 0;
01467                     done = 1;
01468                 }
01469                 break;
01470 
01471               case '*':
01472               case '?':
01473               case '[':
01474               case '{':
01475                 //
01476                 // record the fact that this element has a wildcard character
01477                 // N.B. Don't glob if inside a single quoted string
01478                 //
01479 
01480                 if (quote != '\'')
01481                     globbing++;
01482                 slashes = 0;
01483                 break;
01484 
01485               case '\'':
01486               case '\"':
01487                 //
01488                 // if we're already in a string, see if this is the
01489                 // terminating close-quote. If it is, we're finished with
01490                 // the string, but not neccessarily with the element.
01491                 // If we're not already in a string, start one.
01492                 //
01493 
01494                 if (!(slashes & 1)) {
01495                     if (!quote)
01496                         quote = *ptr;
01497                     else if (quote == *ptr) {
01498                         if (quote == '"' && quote == ptr[1])
01499                             ptr++;
01500                         quote = '\0';
01501                     }
01502                 }
01503                 escape++;
01504                 slashes = 0;
01505                 break;
01506 
01507               default:
01508                 ptr = CharNext(ptr);
01509                 slashes = 0;
01510                 continue;
01511             }
01512             ptr++;
01513         }
01514 
01515         //
01516         // when we get here, we've got a pair of pointers to the element,
01517         // base and ptr. Base points to the start of the element while ptr
01518         // points to the character following the element.
01519         //
01520 
01521         len = ptr - base;
01522         if (done) --len;
01523 
01524         //
01525         // if it's an input vector element and it's enclosed by quotes,
01526         // we can remove them.
01527         //
01528 
01529         if (escape) {
01530             char *p = base, c;
01531             slashes = quote = 0;
01532             while (p < base + len) {
01533                 switch (c = *p) {
01534                   case '\\':
01535                     p++;
01536                     if (quote != '\'') slashes++;
01537                     break;
01538 
01539                   case '\'':
01540                   case '"':
01541                     if (!(slashes & 1) && quote && quote != c) {
01542                         p++;
01543                         slashes = 0;
01544                         break;
01545                     }
01546                     memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
01547                            base + len - p);
01548                     len -= ((slashes + 1) >> 1) + (~slashes & 1);
01549                     p -= (slashes + 1) >> 1;
01550                     if (!(slashes & 1)) {
01551                         if (quote) {
01552                             if (quote == '"' && quote == *p)
01553                                 p++;
01554                             quote = '\0';
01555                         }
01556                         else
01557                             quote = c;
01558                     }
01559                     else
01560                         p++;
01561                     slashes = 0;
01562                     break;
01563 
01564                   default:
01565                     p = CharNext(p);
01566                     slashes = 0;
01567                     break;
01568                 }
01569             }
01570         }
01571 
01572         curr = (NtCmdLineElement *)calloc(sizeof(NtCmdLineElement), 1);
01573         if (!curr) goto do_nothing;
01574         curr->str = base;
01575         curr->len = len;
01576 
01577         if (globbing && (tail = cmdglob(curr, cmdtail))) {
01578             cmdtail = tail;
01579         }
01580         else {
01581             *cmdtail = curr;
01582             cmdtail = &curr->next;
01583         }
01584     }
01585 
01586     //
01587     // Almost done!
01588     // Count up the elements, then allocate space for a vector of pointers
01589     // (argv) and a string table for the elements.
01590     //
01591 
01592     for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
01593         elements++;
01594         strsz += (curr->len + 1);
01595     }
01596 
01597     len = (elements+1)*sizeof(char *) + strsz;
01598     buffer = (char *)malloc(len);
01599     if (!buffer) {
01600       do_nothing:
01601         while (curr = cmdhead) {
01602             cmdhead = curr->next;
01603             if (curr->flags & NTMALLOC) free(curr->str);
01604             free(curr);
01605         }
01606         free(cmdline);
01607         for (vptr = *vec; *vptr; ++vptr);
01608         return vptr - *vec;
01609     }
01610 
01611     //
01612     // make vptr point to the start of the buffer
01613     // and ptr point to the area we'll consider the string table.
01614     //
01615     //   buffer (*vec)
01616     //   |
01617     //   V       ^---------------------V
01618     //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
01619     //   |   |       | ....  | NULL  |   | ..... |\0 |   | ..... |\0 |...
01620     //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
01621     //   |-  elements+1             -| ^ 1st element   ^ 2nd element
01622 
01623     vptr = (char **) buffer;
01624 
01625     ptr = buffer + (elements+1) * sizeof(char *);
01626 
01627     while (curr = cmdhead) {
01628         strlcpy(ptr, curr->str, curr->len + 1);
01629         *vptr++ = ptr;
01630         ptr += curr->len + 1;
01631         cmdhead = curr->next;
01632         if (curr->flags & NTMALLOC) free(curr->str);
01633         free(curr);
01634     }
01635     *vptr = 0;
01636 
01637     *vec = (char **) buffer;
01638     free(cmdline);
01639     return elements;
01640 }
01641 
01642 //
01643 // UNIX compatible directory access functions for NT
01644 //
01645 
01646 #define PATHLEN 1024
01647 
01648 //
01649 // The idea here is to read all the directory names into a string table
01650 // (separated by nulls) and when one of the other dir functions is called
01651 // return the pointer to the current file name.
01652 //
01653 
01654 #define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] &  (1 << (i) % CHAR_BIT))
01655 #define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
01656 
01657 #define BitOfIsDir(n) ((n) * 2)
01658 #define BitOfIsRep(n) ((n) * 2 + 1)
01659 #define DIRENT_PER_CHAR (CHAR_BIT / 2)
01660 
01661 static HANDLE
01662 open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
01663 {
01664     HANDLE fh;
01665     static const WCHAR wildcard[] = L"\\*";
01666     WCHAR *scanname;
01667     WCHAR *p;
01668     int len;
01669     VALUE v;
01670 
01671     //
01672     // Create the search pattern
01673     //
01674     len = lstrlenW(filename);
01675     scanname = ALLOCV_N(WCHAR, v, len + sizeof(wildcard) / sizeof(WCHAR));
01676     lstrcpyW(scanname, filename);
01677     p = CharPrevW(scanname, scanname + len);
01678     if (*p == L'/' || *p == L'\\' || *p == L':')
01679         lstrcatW(scanname, wildcard + 1);
01680     else
01681         lstrcatW(scanname, wildcard);
01682 
01683     //
01684     // do the FindFirstFile call
01685     //
01686     fh = FindFirstFileW(scanname, fd);
01687     ALLOCV_END(v);
01688     if (fh == INVALID_HANDLE_VALUE) {
01689         errno = map_errno(GetLastError());
01690     }
01691     return fh;
01692 }
01693 
01694 static DIR *
01695 opendir_internal(WCHAR *wpath, const char *filename)
01696 {
01697     struct stati64 sbuf;
01698     WIN32_FIND_DATAW fd;
01699     HANDLE fh;
01700     DIR *p;
01701     long len;
01702     long idx;
01703     WCHAR *tmpW;
01704     char *tmp;
01705 
01706     //
01707     // check to see if we've got a directory
01708     //
01709     if (wstati64(wpath, &sbuf) < 0) {
01710         return NULL;
01711     }
01712     if (!(sbuf.st_mode & S_IFDIR) &&
01713         (!ISALPHA(filename[0]) || filename[1] != ':' || filename[2] != '\0' ||
01714          ((1 << ((filename[0] & 0x5f) - 'A')) & GetLogicalDrives()) == 0)) {
01715         errno = ENOTDIR;
01716         return NULL;
01717     }
01718     fh = open_dir_handle(wpath, &fd);
01719     if (fh == INVALID_HANDLE_VALUE) {
01720         return NULL;
01721     }
01722 
01723     //
01724     // Get us a DIR structure
01725     //
01726     p = calloc(sizeof(DIR), 1);
01727     if (p == NULL)
01728         return NULL;
01729 
01730     idx = 0;
01731 
01732     //
01733     // loop finding all the files that match the wildcard
01734     // (which should be all of them in this directory!).
01735     // the variable idx should point one past the null terminator
01736     // of the previous string found.
01737     //
01738     do {
01739         len = lstrlenW(fd.cFileName) + 1;
01740 
01741         //
01742         // bump the string table size by enough for the
01743         // new name and it's null terminator
01744         //
01745         tmpW = realloc(p->start, (idx + len) * sizeof(WCHAR));
01746         if (!tmpW) {
01747           error:
01748             rb_w32_closedir(p);
01749             FindClose(fh);
01750             errno = ENOMEM;
01751             return NULL;
01752         }
01753 
01754         p->start = tmpW;
01755         memcpy(&p->start[idx], fd.cFileName, len * sizeof(WCHAR));
01756 
01757         if (p->nfiles % DIRENT_PER_CHAR == 0) {
01758             tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
01759             if (!tmp)
01760                 goto error;
01761             p->bits = tmp;
01762             p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
01763         }
01764         if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
01765             SetBit(p->bits, BitOfIsDir(p->nfiles));
01766         if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
01767             SetBit(p->bits, BitOfIsRep(p->nfiles));
01768 
01769         p->nfiles++;
01770         idx += len;
01771     } while (FindNextFileW(fh, &fd));
01772     FindClose(fh);
01773     p->size = idx;
01774     p->curr = p->start;
01775     return p;
01776 }
01777 
01778 static inline UINT
01779 filecp(void)
01780 {
01781     UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
01782     return cp;
01783 }
01784 
01785 static char *
01786 wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
01787 {
01788     char *ptr;
01789     int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL) - 1;
01790     if (!(ptr = malloc(len + 1))) return 0;
01791     WideCharToMultiByte(cp, 0, wstr, clen, ptr, len + 1, NULL, NULL);
01792     if (plen) *plen = len;
01793     return ptr;
01794 }
01795 
01796 static WCHAR *
01797 mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
01798 {
01799     WCHAR *ptr;
01800     int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0) - 1;
01801     if (!(ptr = malloc(sizeof(WCHAR) * (len + 1)))) return 0;
01802     MultiByteToWideChar(cp, 0, str, clen, ptr, len + 1);
01803     if (plen) *plen = len;
01804     return ptr;
01805 }
01806 
01807 DIR *
01808 rb_w32_opendir(const char *filename)
01809 {
01810     DIR *ret;
01811     WCHAR *wpath = filecp_to_wstr(filename, NULL);
01812     if (!wpath)
01813         return NULL;
01814     ret = opendir_internal(wpath, filename);
01815     free(wpath);
01816     return ret;
01817 }
01818 
01819 DIR *
01820 rb_w32_uopendir(const char *filename)
01821 {
01822     DIR *ret;
01823     WCHAR *wpath = utf8_to_wstr(filename, NULL);
01824     if (!wpath)
01825         return NULL;
01826     ret = opendir_internal(wpath, filename);
01827     free(wpath);
01828     return ret;
01829 }
01830 
01831 //
01832 // Move to next entry
01833 //
01834 
01835 static void
01836 move_to_next_entry(DIR *dirp)
01837 {
01838     if (dirp->curr) {
01839         dirp->loc++;
01840         dirp->curr += lstrlenW(dirp->curr) + 1;
01841         if (dirp->curr >= (dirp->start + dirp->size)) {
01842             dirp->curr = NULL;
01843         }
01844     }
01845 }
01846 
01847 //
01848 // Readdir just returns the current string pointer and bumps the
01849 // string pointer to the next entry.
01850 //
01851 static BOOL
01852 win32_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *dummy)
01853 {
01854     if (!(entry->d_name = wstr_to_filecp(file, &entry->d_namlen)))
01855         return FALSE;
01856     return TRUE;
01857 }
01858 
01859 VALUE
01860 rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
01861 {
01862     static rb_encoding *utf16 = (rb_encoding *)-1;
01863     VALUE src;
01864 
01865     if (utf16 == (rb_encoding *)-1) {
01866         utf16 = rb_enc_find("UTF-16LE");
01867         if (utf16 == rb_ascii8bit_encoding())
01868             utf16 = NULL;
01869     }
01870     if (!utf16)
01871         /* maybe miniruby */
01872         return Qnil;
01873 
01874     src = rb_enc_str_new((char *)wstr, lstrlenW(wstr) * sizeof(WCHAR), utf16);
01875     return rb_str_encode(src, rb_enc_from_encoding(enc), ECONV_UNDEF_REPLACE, Qnil);
01876 }
01877 
01878 char *
01879 rb_w32_conv_from_wstr(const WCHAR *wstr, long *lenp, rb_encoding *enc)
01880 {
01881     VALUE str = rb_w32_conv_from_wchar(wstr, enc);
01882     long len;
01883     char *ptr;
01884 
01885     if (NIL_P(str)) return wstr_to_filecp(wstr, lenp);
01886     *lenp = len = RSTRING_LEN(str);
01887     memcpy(ptr = malloc(len + 1), RSTRING_PTR(str), len);
01888     ptr[len] = '\0';
01889     return ptr;
01890 }
01891 
01892 static BOOL
01893 ruby_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *enc)
01894 {
01895     if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
01896         return FALSE;
01897     return TRUE;
01898 }
01899 
01900 static struct direct *
01901 readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, struct direct *, rb_encoding *), rb_encoding *enc)
01902 {
01903     static int dummy = 0;
01904 
01905     if (dirp->curr) {
01906 
01907         //
01908         // first set up the structure to return
01909         //
01910         if (dirp->dirstr.d_name)
01911             free(dirp->dirstr.d_name);
01912         conv(dirp->curr, &dirp->dirstr, enc);
01913 
01914         //
01915         // Fake inode
01916         //
01917         dirp->dirstr.d_ino = dummy++;
01918 
01919         //
01920         // Attributes
01921         //
01922         dirp->dirstr.d_isdir = GetBit(dirp->bits, BitOfIsDir(dirp->loc));
01923         dirp->dirstr.d_isrep = GetBit(dirp->bits, BitOfIsRep(dirp->loc));
01924 
01925         //
01926         // Now set up for the next call to readdir
01927         //
01928 
01929         move_to_next_entry(dirp);
01930 
01931         return &(dirp->dirstr);
01932 
01933     }
01934     else
01935         return NULL;
01936 }
01937 
01938 struct direct  *
01939 rb_w32_readdir(DIR *dirp)
01940 {
01941     return readdir_internal(dirp, win32_direct_conv, NULL);
01942 }
01943 
01944 struct direct  *
01945 rb_w32_readdir_with_enc(DIR *dirp, rb_encoding *enc)
01946 {
01947     if (enc == rb_ascii8bit_encoding())
01948         return readdir_internal(dirp, win32_direct_conv, NULL);
01949     else
01950         return readdir_internal(dirp, ruby_direct_conv, enc);
01951 }
01952 
01953 //
01954 // Telldir returns the current string pointer position
01955 //
01956 
01957 long
01958 rb_w32_telldir(DIR *dirp)
01959 {
01960     return dirp->loc;
01961 }
01962 
01963 //
01964 // Seekdir moves the string pointer to a previously saved position
01965 // (Saved by telldir).
01966 
01967 void
01968 rb_w32_seekdir(DIR *dirp, long loc)
01969 {
01970     if (dirp->loc > loc) rb_w32_rewinddir(dirp);
01971 
01972     while (dirp->curr && dirp->loc < loc) {
01973         move_to_next_entry(dirp);
01974     }
01975 }
01976 
01977 //
01978 // Rewinddir resets the string pointer to the start
01979 //
01980 
01981 void
01982 rb_w32_rewinddir(DIR *dirp)
01983 {
01984     dirp->curr = dirp->start;
01985     dirp->loc = 0;
01986 }
01987 
01988 //
01989 // This just free's the memory allocated by opendir
01990 //
01991 
01992 void
01993 rb_w32_closedir(DIR *dirp)
01994 {
01995     if (dirp) {
01996         if (dirp->dirstr.d_name)
01997             free(dirp->dirstr.d_name);
01998         if (dirp->start)
01999             free(dirp->start);
02000         if (dirp->bits)
02001             free(dirp->bits);
02002         free(dirp);
02003     }
02004 }
02005 
02006 #if (defined _MT || defined __MSVCRT__) && !defined __BORLANDC__
02007 #define MSVCRT_THREADS
02008 #endif
02009 #ifdef MSVCRT_THREADS
02010 # define MTHREAD_ONLY(x) x
02011 # define STHREAD_ONLY(x)
02012 #elif defined(__BORLANDC__)
02013 # define MTHREAD_ONLY(x)
02014 # define STHREAD_ONLY(x)
02015 #else
02016 # define MTHREAD_ONLY(x)
02017 # define STHREAD_ONLY(x) x
02018 #endif
02019 
02020 typedef struct  {
02021     intptr_t osfhnd;    /* underlying OS file HANDLE */
02022     char osfile;        /* attributes of file (e.g., open in text mode?) */
02023     char pipech;        /* one char buffer for handles opened on pipes */
02024 #ifdef MSVCRT_THREADS
02025     int lockinitflag;
02026     CRITICAL_SECTION lock;
02027 #endif
02028 #if RT_VER >= 80
02029     char textmode;
02030     char pipech2[2];
02031 #endif
02032 }       ioinfo;
02033 
02034 #if !defined _CRTIMP || defined __MINGW32__
02035 #undef _CRTIMP
02036 #define _CRTIMP __declspec(dllimport)
02037 #endif
02038 
02039 #if !defined(__BORLANDC__)
02040 EXTERN_C _CRTIMP ioinfo * __pioinfo[];
02041 
02042 #define IOINFO_L2E                      5
02043 #define IOINFO_ARRAY_ELTS       (1 << IOINFO_L2E)
02044 #define _pioinfo(i)     ((ioinfo*)((char*)(__pioinfo[i >> IOINFO_L2E]) + (i & (IOINFO_ARRAY_ELTS - 1)) * (sizeof(ioinfo) + pioinfo_extra)))
02045 #define _osfhnd(i)  (_pioinfo(i)->osfhnd)
02046 #define _osfile(i)  (_pioinfo(i)->osfile)
02047 #define _pipech(i)  (_pioinfo(i)->pipech)
02048 
02049 #if RT_VER >= 80
02050 static size_t pioinfo_extra = 0;        /* workaround for VC++8 SP1 */
02051 
02052 static void
02053 set_pioinfo_extra(void)
02054 {
02055     int fd;
02056 
02057     fd = _open("NUL", O_RDONLY);
02058     for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra += sizeof(void *)) {
02059         if (_osfhnd(fd) == _get_osfhandle(fd)) {
02060             break;
02061         }
02062     }
02063     _close(fd);
02064 
02065     if (pioinfo_extra > 64) {
02066         /* not found, maybe something wrong... */
02067         pioinfo_extra = 0;
02068     }
02069 }
02070 #else
02071 #define pioinfo_extra 0
02072 #endif
02073 
02074 #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
02075 #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
02076 
02077 #define FOPEN                   0x01    /* file handle open */
02078 #define FEOFLAG                 0x02    /* end of file has been encountered */
02079 #define FPIPE                   0x08    /* file handle refers to a pipe */
02080 #define FNOINHERIT              0x10    /* file handle opened O_NOINHERIT */
02081 #define FAPPEND                 0x20    /* file handle opened O_APPEND */
02082 #define FDEV                    0x40    /* file handle refers to device */
02083 #define FTEXT                   0x80    /* file handle is in text mode */
02084 
02085 static int is_socket(SOCKET);
02086 static int is_console(SOCKET);
02087 
02088 int
02089 rb_w32_io_cancelable_p(int fd)
02090 {
02091     return cancel_io != NULL && (is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd)));
02092 }
02093 
02094 static int
02095 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
02096 {
02097     int fh;
02098     char fileflags;             /* _osfile flags */
02099     HANDLE hF;
02100 
02101     /* copy relevant flags from second parameter */
02102     fileflags = FDEV;
02103 
02104     if (flags & O_APPEND)
02105         fileflags |= FAPPEND;
02106 
02107     if (flags & O_TEXT)
02108         fileflags |= FTEXT;
02109 
02110     if (flags & O_NOINHERIT)
02111         fileflags |= FNOINHERIT;
02112 
02113     /* attempt to allocate a C Runtime file handle */
02114     hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
02115     fh = _open_osfhandle((intptr_t)hF, 0);
02116     CloseHandle(hF);
02117     if (fh == -1) {
02118         errno = EMFILE;         /* too many open files */
02119         _doserrno = 0L;         /* not an OS error */
02120     }
02121     else {
02122 
02123         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fh)->lock)));
02124         /* the file is open. now, set the info in _osfhnd array */
02125         _set_osfhnd(fh, osfhandle);
02126 
02127         fileflags |= FOPEN;             /* mark as open */
02128 
02129         _set_osflags(fh, fileflags); /* set osfile entry */
02130         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fh)->lock));
02131     }
02132     return fh;                  /* return handle */
02133 }
02134 
02135 static void
02136 init_stdhandle(void)
02137 {
02138     int nullfd = -1;
02139     int keep = 0;
02140 #define open_null(fd)                                           \
02141     (((nullfd < 0) ?                                            \
02142       (nullfd = open("NUL", O_RDWR)) : 0),              \
02143      ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)),        \
02144      (fd))
02145 
02146     if (fileno(stdin) < 0) {
02147         stdin->_file = open_null(0);
02148     }
02149     else {
02150         setmode(fileno(stdin), O_BINARY);
02151     }
02152     if (fileno(stdout) < 0) {
02153         stdout->_file = open_null(1);
02154     }
02155     if (fileno(stderr) < 0) {
02156         stderr->_file = open_null(2);
02157     }
02158     if (nullfd >= 0 && !keep) close(nullfd);
02159     setvbuf(stderr, NULL, _IONBF, 0);
02160 }
02161 #else
02162 
02163 #define _set_osfhnd(fh, osfh) (void)((fh), (osfh))
02164 #define _set_osflags(fh, flags) (void)((fh), (flags))
02165 
02166 static void
02167 init_stdhandle(void)
02168 {
02169 }
02170 #endif
02171 
02172 #ifdef __BORLANDC__
02173 static int
02174 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
02175 {
02176     int fd = _open_osfhandle(osfhandle, flags);
02177     if (fd == -1) {
02178         errno = EMFILE;         /* too many open files */
02179         _doserrno = 0L;         /* not an OS error */
02180     }
02181     return fd;
02182 }
02183 #endif
02184 
02185 #undef getsockopt
02186 
02187 static int
02188 is_socket(SOCKET sock)
02189 {
02190     if (st_lookup(socklist, (st_data_t)sock, NULL))
02191         return TRUE;
02192     else
02193         return FALSE;
02194 }
02195 
02196 int
02197 rb_w32_is_socket(int fd)
02198 {
02199     return is_socket(TO_SOCKET(fd));
02200 }
02201 
02202 //
02203 // Since the errors returned by the socket error function
02204 // WSAGetLastError() are not known by the library routine strerror
02205 // we have to roll our own.
02206 //
02207 
02208 #undef strerror
02209 
02210 char *
02211 rb_w32_strerror(int e)
02212 {
02213     static char buffer[512];
02214     DWORD source = 0;
02215     char *p;
02216 
02217 #if defined __BORLANDC__ && defined ENOTEMPTY // _sys_errlist is broken
02218     switch (e) {
02219       case ENAMETOOLONG:
02220         return "Filename too long";
02221       case ENOTEMPTY:
02222         return "Directory not empty";
02223     }
02224 #endif
02225 
02226     if (e < 0 || e > sys_nerr) {
02227         if (e < 0)
02228             e = GetLastError();
02229 #if WSAEWOULDBLOCK != EWOULDBLOCK
02230         else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
02231             static int s = -1;
02232             int i;
02233             if (s < 0)
02234                 for (s = 0; s < (int)(sizeof(errmap)/sizeof(*errmap)); s++)
02235                     if (errmap[s].winerr == WSAEWOULDBLOCK)
02236                         break;
02237             for (i = s; i < (int)(sizeof(errmap)/sizeof(*errmap)); i++)
02238                 if (errmap[i].err == e) {
02239                     e = errmap[i].winerr;
02240                     break;
02241                 }
02242         }
02243 #endif
02244         if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
02245                           FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
02246                           MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
02247                           buffer, sizeof(buffer), NULL) == 0 &&
02248             FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
02249                           FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
02250                           buffer, sizeof(buffer), NULL) == 0)
02251             strlcpy(buffer, "Unknown Error", sizeof(buffer));
02252     }
02253     else
02254         strlcpy(buffer, strerror(e), sizeof(buffer));
02255 
02256     p = buffer;
02257     while ((p = strpbrk(p, "\r\n")) != NULL) {
02258         memmove(p, p + 1, strlen(p));
02259     }
02260     return buffer;
02261 }
02262 
02263 //
02264 // various stubs
02265 //
02266 
02267 
02268 // Ownership
02269 //
02270 // Just pretend that everyone is a superuser. NT will let us know if
02271 // we don't really have permission to do something.
02272 //
02273 
02274 #define ROOT_UID        0
02275 #define ROOT_GID        0
02276 
02277 rb_uid_t
02278 getuid(void)
02279 {
02280         return ROOT_UID;
02281 }
02282 
02283 rb_uid_t
02284 geteuid(void)
02285 {
02286         return ROOT_UID;
02287 }
02288 
02289 rb_gid_t
02290 getgid(void)
02291 {
02292         return ROOT_GID;
02293 }
02294 
02295 rb_gid_t
02296 getegid(void)
02297 {
02298     return ROOT_GID;
02299 }
02300 
02301 int
02302 setuid(rb_uid_t uid)
02303 {
02304     return (uid == ROOT_UID ? 0 : -1);
02305 }
02306 
02307 int
02308 setgid(rb_gid_t gid)
02309 {
02310     return (gid == ROOT_GID ? 0 : -1);
02311 }
02312 
02313 //
02314 // File system stuff
02315 //
02316 
02317 int
02318 ioctl(int i, int u, ...)
02319 {
02320     errno = EINVAL;
02321     return -1;
02322 }
02323 
02324 void
02325 rb_w32_fdset(int fd, fd_set *set)
02326 {
02327     FD_SET(fd, set);
02328 }
02329 
02330 #undef FD_CLR
02331 
02332 void
02333 rb_w32_fdclr(int fd, fd_set *set)
02334 {
02335     unsigned int i;
02336     SOCKET s = TO_SOCKET(fd);
02337 
02338     for (i = 0; i < set->fd_count; i++) {
02339         if (set->fd_array[i] == s) {
02340             memmove(&set->fd_array[i], &set->fd_array[i+1],
02341                     sizeof(set->fd_array[0]) * (--set->fd_count - i));
02342             break;
02343         }
02344     }
02345 }
02346 
02347 #undef FD_ISSET
02348 
02349 int
02350 rb_w32_fdisset(int fd, fd_set *set)
02351 {
02352     int ret;
02353     SOCKET s = TO_SOCKET(fd);
02354     if (s == (SOCKET)INVALID_HANDLE_VALUE)
02355         return 0;
02356     RUBY_CRITICAL(ret = __WSAFDIsSet(s, set));
02357     return ret;
02358 }
02359 
02360 void
02361 rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
02362 {
02363     max = min(src->fd_count, (UINT)max);
02364     if ((UINT)dst->capa < (UINT)max) {
02365         dst->capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
02366         dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
02367     }
02368 
02369     memcpy(dst->fdset->fd_array, src->fd_array,
02370            max * sizeof(src->fd_array[0]));
02371     dst->fdset->fd_count = src->fd_count;
02372 }
02373 
02374 /* License: Ruby's */
02375 void
02376 rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
02377 {
02378     if ((UINT)dst->capa < src->fdset->fd_count) {
02379         dst->capa = (src->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
02380         dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
02381     }
02382 
02383     memcpy(dst->fdset->fd_array, src->fdset->fd_array,
02384            src->fdset->fd_count * sizeof(src->fdset->fd_array[0]));
02385     dst->fdset->fd_count = src->fdset->fd_count;
02386 }
02387 
02388 //
02389 // Networking trampolines
02390 // These are used to avoid socket startup/shutdown overhead in case
02391 // the socket routines aren't used.
02392 //
02393 
02394 #undef select
02395 
02396 static int
02397 extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
02398 {
02399     unsigned int s = 0;
02400     unsigned int m = 0;
02401     if (!src) return 0;
02402 
02403     while (s < src->fd_count) {
02404         SOCKET fd = src->fd_array[s];
02405 
02406         if (!func || (*func)(fd)) {
02407             if (dst) { /* move it to dst */
02408                 unsigned int d;
02409 
02410                 for (d = 0; d < dst->fdset->fd_count; d++) {
02411                     if (dst->fdset->fd_array[d] == fd)
02412                         break;
02413                 }
02414                 if (d == dst->fdset->fd_count) {
02415                     if ((int)dst->fdset->fd_count >= dst->capa) {
02416                         dst->capa = (dst->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
02417                         dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
02418                     }
02419                     dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
02420                 }
02421                 memmove(
02422                     &src->fd_array[s],
02423                     &src->fd_array[s+1],
02424                     sizeof(src->fd_array[0]) * (--src->fd_count - s));
02425             }
02426             else {
02427                 m++;
02428                 s++;
02429             }
02430         }
02431         else s++;
02432     }
02433 
02434     return dst ? dst->fdset->fd_count : m;
02435 }
02436 
02437 static int
02438 copy_fd(fd_set *dst, fd_set *src)
02439 {
02440     unsigned int s;
02441     if (!src || !dst) return 0;
02442 
02443     for (s = 0; s < src->fd_count; ++s) {
02444         SOCKET fd = src->fd_array[s];
02445         unsigned int d;
02446         for (d = 0; d < dst->fd_count; ++d) {
02447             if (dst->fd_array[d] == fd)
02448                 break;
02449         }
02450         if (d == dst->fd_count && d < FD_SETSIZE) {
02451             dst->fd_array[dst->fd_count++] = fd;
02452         }
02453     }
02454 
02455     return dst->fd_count;
02456 }
02457 
02458 static int
02459 is_not_socket(SOCKET sock)
02460 {
02461     return !is_socket(sock);
02462 }
02463 
02464 static int
02465 is_pipe(SOCKET sock) /* DONT call this for SOCKET! it clains it is PIPE. */
02466 {
02467     int ret;
02468 
02469     RUBY_CRITICAL({
02470         ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
02471     });
02472 
02473     return ret;
02474 }
02475 
02476 static int
02477 is_readable_pipe(SOCKET sock) /* call this for pipe only */
02478 {
02479     int ret;
02480     DWORD n = 0;
02481 
02482     RUBY_CRITICAL(
02483         if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
02484             ret = (n > 0);
02485         }
02486         else {
02487             ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
02488         }
02489     );
02490 
02491     return ret;
02492 }
02493 
02494 static int
02495 is_console(SOCKET sock) /* DONT call this for SOCKET! */
02496 {
02497     int ret;
02498     DWORD n = 0;
02499     INPUT_RECORD ir;
02500 
02501     RUBY_CRITICAL(
02502         ret = (PeekConsoleInput((HANDLE)sock, &ir, 1, &n))
02503     );
02504 
02505     return ret;
02506 }
02507 
02508 static int
02509 is_readable_console(SOCKET sock) /* call this for console only */
02510 {
02511     int ret = 0;
02512     DWORD n = 0;
02513     INPUT_RECORD ir;
02514 
02515     RUBY_CRITICAL(
02516         if (PeekConsoleInput((HANDLE)sock, &ir, 1, &n) && n > 0) {
02517             if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
02518                 ir.Event.KeyEvent.uChar.AsciiChar) {
02519                 ret = 1;
02520             }
02521             else {
02522                 ReadConsoleInput((HANDLE)sock, &ir, 1, &n);
02523             }
02524         }
02525     );
02526 
02527     return ret;
02528 }
02529 
02530 static int
02531 is_invalid_handle(SOCKET sock)
02532 {
02533     return (HANDLE)sock == INVALID_HANDLE_VALUE;
02534 }
02535 
02536 static int
02537 do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
02538             struct timeval *timeout)
02539 {
02540     int r = 0;
02541 
02542     if (nfds == 0) {
02543         if (timeout)
02544             rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
02545         else
02546             rb_w32_sleep(INFINITE);
02547     }
02548     else {
02549         RUBY_CRITICAL(
02550             EnterCriticalSection(&select_mutex);
02551             r = select(nfds, rd, wr, ex, timeout);
02552             LeaveCriticalSection(&select_mutex);
02553             if (r == SOCKET_ERROR) {
02554                 errno = map_errno(WSAGetLastError());
02555                 r = -1;
02556             }
02557         );
02558     }
02559 
02560     return r;
02561 }
02562 
02563 /*
02564  * rest -= wait
02565  * return 0 if rest is smaller than wait.
02566  */
02567 int
02568 rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
02569 {
02570     if (rest->tv_sec < wait->tv_sec) {
02571         return 0;
02572     }
02573     while (rest->tv_usec < wait->tv_usec) {
02574         if (rest->tv_sec <= wait->tv_sec) {
02575             return 0;
02576         }
02577         rest->tv_sec -= 1;
02578         rest->tv_usec += 1000 * 1000;
02579     }
02580     rest->tv_sec -= wait->tv_sec;
02581     rest->tv_usec -= wait->tv_usec;
02582     return rest->tv_sec != 0 || rest->tv_usec != 0;
02583 }
02584 
02585 static inline int
02586 compare(const struct timeval *t1, const struct timeval *t2)
02587 {
02588     if (t1->tv_sec < t2->tv_sec)
02589         return -1;
02590     if (t1->tv_sec > t2->tv_sec)
02591         return 1;
02592     if (t1->tv_usec < t2->tv_usec)
02593         return -1;
02594     if (t1->tv_usec > t2->tv_usec)
02595         return 1;
02596     return 0;
02597 }
02598 
02599 #undef Sleep
02600 
02601 int rb_w32_check_interrupt(void *);     /* @internal */
02602 
02603 /* @internal */
02604 int
02605 rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
02606                           struct timeval *timeout, void *th)
02607 {
02608     int r;
02609     rb_fdset_t pipe_rd;
02610     rb_fdset_t cons_rd;
02611     rb_fdset_t else_rd;
02612     rb_fdset_t else_wr;
02613     rb_fdset_t except;
02614     int nonsock = 0;
02615     struct timeval limit = {0, 0};
02616 
02617     if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
02618         errno = EINVAL;
02619         return -1;
02620     }
02621 
02622     if (timeout) {
02623         if (timeout->tv_sec < 0 ||
02624             timeout->tv_usec < 0 ||
02625             timeout->tv_usec >= 1000000) {
02626             errno = EINVAL;
02627             return -1;
02628         }
02629         gettimeofday(&limit, NULL);
02630         limit.tv_sec += timeout->tv_sec;
02631         limit.tv_usec += timeout->tv_usec;
02632         if (limit.tv_usec >= 1000000) {
02633             limit.tv_usec -= 1000000;
02634             limit.tv_sec++;
02635         }
02636     }
02637 
02638     if (!NtSocketsInitialized) {
02639         StartSockets();
02640     }
02641 
02642     // assume else_{rd,wr} (other than socket, pipe reader, console reader)
02643     // are always readable/writable. but this implementation still has
02644     // problem. if pipe's buffer is full, writing to pipe will block
02645     // until some data is read from pipe. but ruby is single threaded system,
02646     // so whole system will be blocked forever.
02647 
02648     rb_fd_init(&else_rd);
02649     nonsock += extract_fd(&else_rd, rd, is_not_socket);
02650 
02651     rb_fd_init(&else_wr);
02652     nonsock += extract_fd(&else_wr, wr, is_not_socket);
02653 
02654     // check invalid handles
02655     if (extract_fd(NULL, else_rd.fdset, is_invalid_handle) > 0 ||
02656         extract_fd(NULL, else_wr.fdset, is_invalid_handle) > 0) {
02657         rb_fd_term(&else_wr);
02658         rb_fd_term(&else_rd);
02659         errno = EBADF;
02660         return -1;
02661     }
02662 
02663     rb_fd_init(&pipe_rd);
02664     extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
02665 
02666     rb_fd_init(&cons_rd);
02667     extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
02668 
02669     rb_fd_init(&except);
02670     extract_fd(&except, ex, is_not_socket); // drop only
02671 
02672     r = 0;
02673     if (rd && (int)rd->fd_count > r) r = (int)rd->fd_count;
02674     if (wr && (int)wr->fd_count > r) r = (int)wr->fd_count;
02675     if (ex && (int)ex->fd_count > r) r = (int)ex->fd_count;
02676     if (nfds > r) nfds = r;
02677 
02678     {
02679         struct timeval rest;
02680         struct timeval wait;
02681         struct timeval zero;
02682         wait.tv_sec = 0; wait.tv_usec = 10 * 1000; // 10ms
02683         zero.tv_sec = 0; zero.tv_usec = 0;         //  0ms
02684         for (;;) {
02685             if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
02686                 r = -1;
02687                 break;
02688             }
02689             if (nonsock) {
02690                 // modifying {else,pipe,cons}_rd is safe because
02691                 // if they are modified, function returns immediately.
02692                 extract_fd(&else_rd, pipe_rd.fdset, is_readable_pipe);
02693                 extract_fd(&else_rd, cons_rd.fdset, is_readable_console);
02694             }
02695 
02696             if (else_rd.fdset->fd_count || else_wr.fdset->fd_count) {
02697                 r = do_select(nfds, rd, wr, ex, &zero); // polling
02698                 if (r < 0) break; // XXX: should I ignore error and return signaled handles?
02699                 r += copy_fd(rd, else_rd.fdset);
02700                 r += copy_fd(wr, else_wr.fdset);
02701                 if (ex)
02702                     r += ex->fd_count;
02703                 break;
02704             }
02705             else {
02706                 struct timeval *dowait = &wait;
02707 
02708                 fd_set orig_rd;
02709                 fd_set orig_wr;
02710                 fd_set orig_ex;
02711 
02712                 FD_ZERO(&orig_rd);
02713                 FD_ZERO(&orig_wr);
02714                 FD_ZERO(&orig_ex);
02715 
02716                 if (rd) copy_fd(&orig_rd, rd);
02717                 if (wr) copy_fd(&orig_wr, wr);
02718                 if (ex) copy_fd(&orig_ex, ex);
02719                 r = do_select(nfds, rd, wr, ex, &zero); // polling
02720                 if (r != 0) break; // signaled or error
02721                 if (rd) copy_fd(rd, &orig_rd);
02722                 if (wr) copy_fd(wr, &orig_wr);
02723                 if (ex) copy_fd(ex, &orig_ex);
02724 
02725                 if (timeout) {
02726                     struct timeval now;
02727                     gettimeofday(&now, NULL);
02728                     rest = limit;
02729                     if (!rb_w32_time_subtract(&rest, &now)) break;
02730                     if (compare(&rest, &wait) < 0) dowait = &rest;
02731                 }
02732                 Sleep(dowait->tv_sec * 1000 + dowait->tv_usec / 1000);
02733             }
02734         }
02735     }
02736 
02737     rb_fd_term(&except);
02738     rb_fd_term(&cons_rd);
02739     rb_fd_term(&pipe_rd);
02740     rb_fd_term(&else_wr);
02741     rb_fd_term(&else_rd);
02742 
02743     return r;
02744 }
02745 
02746 int WSAAPI
02747 rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
02748               struct timeval *timeout)
02749 {
02750     return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
02751 }
02752 
02753 static FARPROC
02754 get_wsa_extension_function(SOCKET s, GUID *guid)
02755 {
02756     DWORD dmy;
02757     FARPROC ptr = NULL;
02758 
02759     WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, guid, sizeof(*guid),
02760              &ptr, sizeof(ptr), &dmy, NULL, NULL);
02761     if (!ptr)
02762         errno = ENOSYS;
02763     return ptr;
02764 }
02765 
02766 #undef accept
02767 
02768 int WSAAPI
02769 rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
02770 {
02771     SOCKET r;
02772     int fd;
02773 
02774     if (!NtSocketsInitialized) {
02775         StartSockets();
02776     }
02777     RUBY_CRITICAL({
02778         HANDLE h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
02779         fd = rb_w32_open_osfhandle((intptr_t)h, O_RDWR|O_BINARY|O_NOINHERIT);
02780         if (fd != -1) {
02781             r = accept(TO_SOCKET(s), addr, addrlen);
02782             if (r != INVALID_SOCKET) {
02783                 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
02784                 _set_osfhnd(fd, r);
02785                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
02786                 CloseHandle(h);
02787                 st_insert(socklist, (st_data_t)r, (st_data_t)0);
02788             }
02789             else {
02790                 errno = map_errno(WSAGetLastError());
02791                 close(fd);
02792                 fd = -1;
02793             }
02794         }
02795         else
02796             CloseHandle(h);
02797     });
02798     return fd;
02799 }
02800 
02801 #undef bind
02802 
02803 int WSAAPI
02804 rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
02805 {
02806     int r;
02807 
02808     if (!NtSocketsInitialized) {
02809         StartSockets();
02810     }
02811     RUBY_CRITICAL({
02812         r = bind(TO_SOCKET(s), addr, addrlen);
02813         if (r == SOCKET_ERROR)
02814             errno = map_errno(WSAGetLastError());
02815     });
02816     return r;
02817 }
02818 
02819 #undef connect
02820 
02821 int WSAAPI
02822 rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
02823 {
02824     int r;
02825     if (!NtSocketsInitialized) {
02826         StartSockets();
02827     }
02828     RUBY_CRITICAL({
02829         r = connect(TO_SOCKET(s), addr, addrlen);
02830         if (r == SOCKET_ERROR) {
02831             int err = WSAGetLastError();
02832             if (err != WSAEWOULDBLOCK)
02833                 errno = map_errno(err);
02834             else
02835                 errno = EINPROGRESS;
02836         }
02837     });
02838     return r;
02839 }
02840 
02841 
02842 #undef getpeername
02843 
02844 int WSAAPI
02845 rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
02846 {
02847     int r;
02848     if (!NtSocketsInitialized) {
02849         StartSockets();
02850     }
02851     RUBY_CRITICAL({
02852         r = getpeername(TO_SOCKET(s), addr, addrlen);
02853         if (r == SOCKET_ERROR)
02854             errno = map_errno(WSAGetLastError());
02855     });
02856     return r;
02857 }
02858 
02859 #undef getsockname
02860 
02861 int WSAAPI
02862 rb_w32_getsockname(int s, struct sockaddr *addr, int *addrlen)
02863 {
02864     int r;
02865     if (!NtSocketsInitialized) {
02866         StartSockets();
02867     }
02868     RUBY_CRITICAL({
02869         r = getsockname(TO_SOCKET(s), addr, addrlen);
02870         if (r == SOCKET_ERROR)
02871             errno = map_errno(WSAGetLastError());
02872     });
02873     return r;
02874 }
02875 
02876 int WSAAPI
02877 rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
02878 {
02879     int r;
02880     if (!NtSocketsInitialized) {
02881         StartSockets();
02882     }
02883     RUBY_CRITICAL({
02884         r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
02885         if (r == SOCKET_ERROR)
02886             errno = map_errno(WSAGetLastError());
02887     });
02888     return r;
02889 }
02890 
02891 #undef ioctlsocket
02892 
02893 int WSAAPI
02894 rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
02895 {
02896     int r;
02897     if (!NtSocketsInitialized) {
02898         StartSockets();
02899     }
02900     RUBY_CRITICAL({
02901         r = ioctlsocket(TO_SOCKET(s), cmd, argp);
02902         if (r == SOCKET_ERROR)
02903             errno = map_errno(WSAGetLastError());
02904     });
02905     return r;
02906 }
02907 
02908 #undef listen
02909 
02910 int WSAAPI
02911 rb_w32_listen(int s, int backlog)
02912 {
02913     int r;
02914     if (!NtSocketsInitialized) {
02915         StartSockets();
02916     }
02917     RUBY_CRITICAL({
02918         r = listen(TO_SOCKET(s), backlog);
02919         if (r == SOCKET_ERROR)
02920             errno = map_errno(WSAGetLastError());
02921     });
02922     return r;
02923 }
02924 
02925 #undef recv
02926 #undef recvfrom
02927 #undef send
02928 #undef sendto
02929 
02930 static int
02931 finish_overlapped_socket(SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
02932 {
02933     DWORD flg;
02934     int err;
02935 
02936     if (result != SOCKET_ERROR)
02937         *len = size;
02938     else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
02939         switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
02940           case WAIT_OBJECT_0:
02941             RUBY_CRITICAL(
02942                 result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg)
02943                 );
02944             if (result) {
02945                 *len = size;
02946                 break;
02947             }
02948             /* thru */
02949           default:
02950             errno = map_errno(WSAGetLastError());
02951             /* thru */
02952           case WAIT_OBJECT_0 + 1:
02953             /* interrupted */
02954             *len = -1;
02955             cancel_io((HANDLE)s);
02956             break;
02957         }
02958     }
02959     else {
02960         errno = map_errno(err);
02961         *len = -1;
02962     }
02963     CloseHandle(wol->hEvent);
02964 
02965     return result;
02966 }
02967 
02968 static int
02969 overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
02970                      struct sockaddr *addr, int *addrlen)
02971 {
02972     int r;
02973     int ret;
02974     int mode;
02975     st_data_t data;
02976     DWORD flg;
02977     WSAOVERLAPPED wol;
02978     WSABUF wbuf;
02979     SOCKET s;
02980 
02981     if (!NtSocketsInitialized)
02982         StartSockets();
02983 
02984     s = TO_SOCKET(fd);
02985     st_lookup(socklist, (st_data_t)s, &data);
02986     mode = (int)data;
02987     if (!cancel_io || (mode & O_NONBLOCK)) {
02988         RUBY_CRITICAL({
02989             if (input) {
02990                 if (addr && addrlen)
02991                     r = recvfrom(s, buf, len, flags, addr, addrlen);
02992                 else
02993                     r = recv(s, buf, len, flags);
02994             }
02995             else {
02996                 if (addr && addrlen)
02997                     r = sendto(s, buf, len, flags, addr, *addrlen);
02998                 else
02999                     r = send(s, buf, len, flags);
03000             }
03001             if (r == SOCKET_ERROR)
03002                 errno = map_errno(WSAGetLastError());
03003         });
03004     }
03005     else {
03006         DWORD size;
03007         DWORD rlen;
03008         wbuf.len = len;
03009         wbuf.buf = buf;
03010         memset(&wol, 0, sizeof(wol));
03011         RUBY_CRITICAL({
03012             wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
03013             if (input) {
03014                 flg = flags;
03015                 if (addr && addrlen)
03016                     ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
03017                                       &wol, NULL);
03018                 else
03019                     ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
03020             }
03021             else {
03022                 if (addr && addrlen)
03023                     ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
03024                                     &wol, NULL);
03025                 else
03026                     ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
03027             }
03028         });
03029 
03030         finish_overlapped_socket(s, &wol, ret, &rlen, size);
03031         r = (int)rlen;
03032     }
03033 
03034     return r;
03035 }
03036 
03037 int WSAAPI
03038 rb_w32_recv(int fd, char *buf, int len, int flags)
03039 {
03040     return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
03041 }
03042 
03043 int WSAAPI
03044 rb_w32_recvfrom(int fd, char *buf, int len, int flags,
03045                 struct sockaddr *from, int *fromlen)
03046 {
03047     return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
03048 }
03049 
03050 int WSAAPI
03051 rb_w32_send(int fd, const char *buf, int len, int flags)
03052 {
03053     return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
03054 }
03055 
03056 int WSAAPI
03057 rb_w32_sendto(int fd, const char *buf, int len, int flags,
03058               const struct sockaddr *to, int tolen)
03059 {
03060     return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
03061                                 (struct sockaddr *)to, &tolen);
03062 }
03063 
03064 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
03065 typedef struct {
03066     SOCKADDR *name;
03067     int namelen;
03068     WSABUF *lpBuffers;
03069     DWORD dwBufferCount;
03070     WSABUF Control;
03071     DWORD dwFlags;
03072 } WSAMSG;
03073 #endif
03074 #ifndef WSAID_WSARECVMSG
03075 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
03076 #endif
03077 #ifndef WSAID_WSASENDMSG
03078 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
03079 #endif
03080 
03081 #define msghdr_to_wsamsg(msg, wsamsg) \
03082     do { \
03083         int i; \
03084         (wsamsg)->name = (msg)->msg_name; \
03085         (wsamsg)->namelen = (msg)->msg_namelen; \
03086         (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
03087         (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
03088         for (i = 0; i < (msg)->msg_iovlen; ++i) { \
03089             (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
03090             (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
03091         } \
03092         (wsamsg)->Control.buf = (msg)->msg_control; \
03093         (wsamsg)->Control.len = (msg)->msg_controllen; \
03094         (wsamsg)->dwFlags = (msg)->msg_flags; \
03095     } while (0)
03096 
03097 int
03098 recvmsg(int fd, struct msghdr *msg, int flags)
03099 {
03100     typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
03101     static WSARecvMsg_t pWSARecvMsg = NULL;
03102     WSAMSG wsamsg;
03103     SOCKET s;
03104     st_data_t data;
03105     int mode;
03106     DWORD len;
03107     int ret;
03108 
03109     if (!NtSocketsInitialized)
03110         StartSockets();
03111 
03112     s = TO_SOCKET(fd);
03113 
03114     if (!pWSARecvMsg) {
03115         static GUID guid = WSAID_WSARECVMSG;
03116         pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, &guid);
03117         if (!pWSARecvMsg)
03118             return -1;
03119     }
03120 
03121     msghdr_to_wsamsg(msg, &wsamsg);
03122     wsamsg.dwFlags |= flags;
03123 
03124     st_lookup(socklist, (st_data_t)s, &data);
03125     mode = (int)data;
03126     if (!cancel_io || (mode & O_NONBLOCK)) {
03127         RUBY_CRITICAL({
03128             if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
03129                 errno = map_errno(WSAGetLastError());
03130                 len = -1;
03131             }
03132         });
03133     }
03134     else {
03135         DWORD size;
03136         WSAOVERLAPPED wol;
03137         memset(&wol, 0, sizeof(wol));
03138         RUBY_CRITICAL({
03139             wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
03140             ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
03141         });
03142 
03143         ret = finish_overlapped_socket(s, &wol, ret, &len, size);
03144     }
03145     if (ret == SOCKET_ERROR)
03146         return -1;
03147 
03148     /* WSAMSG to msghdr */
03149     msg->msg_name = wsamsg.name;
03150     msg->msg_namelen = wsamsg.namelen;
03151     msg->msg_flags = wsamsg.dwFlags;
03152 
03153     return len;
03154 }
03155 
03156 int
03157 sendmsg(int fd, const struct msghdr *msg, int flags)
03158 {
03159     typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
03160     static WSASendMsg_t pWSASendMsg = NULL;
03161     WSAMSG wsamsg;
03162     SOCKET s;
03163     st_data_t data;
03164     int mode;
03165     DWORD len;
03166     int ret;
03167 
03168     if (!NtSocketsInitialized)
03169         StartSockets();
03170 
03171     s = TO_SOCKET(fd);
03172 
03173     if (!pWSASendMsg) {
03174         static GUID guid = WSAID_WSASENDMSG;
03175         pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, &guid);
03176         if (!pWSASendMsg)
03177             return -1;
03178     }
03179 
03180     msghdr_to_wsamsg(msg, &wsamsg);
03181 
03182     st_lookup(socklist, (st_data_t)s, &data);
03183     mode = (int)data;
03184     if (!cancel_io || (mode & O_NONBLOCK)) {
03185         RUBY_CRITICAL({
03186             if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
03187                 errno = map_errno(WSAGetLastError());
03188                 len = -1;
03189             }
03190         });
03191     }
03192     else {
03193         DWORD size;
03194         WSAOVERLAPPED wol;
03195         memset(&wol, 0, sizeof(wol));
03196         RUBY_CRITICAL({
03197             wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
03198             ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
03199         });
03200 
03201         finish_overlapped_socket(s, &wol, ret, &len, size);
03202     }
03203 
03204     return len;
03205 }
03206 
03207 #undef setsockopt
03208 
03209 int WSAAPI
03210 rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
03211 {
03212     int r;
03213     if (!NtSocketsInitialized) {
03214         StartSockets();
03215     }
03216     RUBY_CRITICAL({
03217         r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
03218         if (r == SOCKET_ERROR)
03219             errno = map_errno(WSAGetLastError());
03220     });
03221     return r;
03222 }
03223 
03224 #undef shutdown
03225 
03226 int WSAAPI
03227 rb_w32_shutdown(int s, int how)
03228 {
03229     int r;
03230     if (!NtSocketsInitialized) {
03231         StartSockets();
03232     }
03233     RUBY_CRITICAL({
03234         r = shutdown(TO_SOCKET(s), how);
03235         if (r == SOCKET_ERROR)
03236             errno = map_errno(WSAGetLastError());
03237     });
03238     return r;
03239 }
03240 
03241 static SOCKET
03242 open_ifs_socket(int af, int type, int protocol)
03243 {
03244     unsigned long proto_buffers_len = 0;
03245     int error_code;
03246     SOCKET out = INVALID_SOCKET;
03247 
03248     if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
03249         error_code = WSAGetLastError();
03250         if (error_code == WSAENOBUFS) {
03251             WSAPROTOCOL_INFO *proto_buffers;
03252             int protocols_available = 0;
03253 
03254             proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
03255             if (!proto_buffers) {
03256                 WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
03257                 return INVALID_SOCKET;
03258             }
03259 
03260             protocols_available =
03261                 WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
03262             if (protocols_available != SOCKET_ERROR) {
03263                 int i;
03264                 for (i = 0; i < protocols_available; i++) {
03265                     if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
03266                         (type != proto_buffers[i].iSocketType) ||
03267                         (protocol != 0 && protocol != proto_buffers[i].iProtocol))
03268                         continue;
03269 
03270                     if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
03271                         continue;
03272 
03273                     out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
03274                                     WSA_FLAG_OVERLAPPED);
03275                     break;
03276                 }
03277                 if (out == INVALID_SOCKET)
03278                     out = WSASocket(af, type, protocol, NULL, 0, 0);
03279             }
03280 
03281             free(proto_buffers);
03282         }
03283     }
03284 
03285     return out;
03286 }
03287 
03288 #undef socket
03289 
03290 int WSAAPI
03291 rb_w32_socket(int af, int type, int protocol)
03292 {
03293     SOCKET s;
03294     int fd;
03295 
03296     if (!NtSocketsInitialized) {
03297         StartSockets();
03298     }
03299     RUBY_CRITICAL({
03300         s = open_ifs_socket(af, type, protocol);
03301         if (s == INVALID_SOCKET) {
03302             errno = map_errno(WSAGetLastError());
03303             fd = -1;
03304         }
03305         else {
03306             fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
03307             if (fd != -1)
03308                 st_insert(socklist, (st_data_t)s, (st_data_t)0);
03309             else
03310                 closesocket(s);
03311         }
03312     });
03313     return fd;
03314 }
03315 
03316 #undef gethostbyaddr
03317 
03318 struct hostent * WSAAPI
03319 rb_w32_gethostbyaddr(const char *addr, int len, int type)
03320 {
03321     struct hostent *r;
03322     if (!NtSocketsInitialized) {
03323         StartSockets();
03324     }
03325     RUBY_CRITICAL({
03326         r = gethostbyaddr(addr, len, type);
03327         if (r == NULL)
03328             errno = map_errno(WSAGetLastError());
03329     });
03330     return r;
03331 }
03332 
03333 #undef gethostbyname
03334 
03335 struct hostent * WSAAPI
03336 rb_w32_gethostbyname(const char *name)
03337 {
03338     struct hostent *r;
03339     if (!NtSocketsInitialized) {
03340         StartSockets();
03341     }
03342     RUBY_CRITICAL({
03343         r = gethostbyname(name);
03344         if (r == NULL)
03345             errno = map_errno(WSAGetLastError());
03346     });
03347     return r;
03348 }
03349 
03350 #undef gethostname
03351 
03352 int WSAAPI
03353 rb_w32_gethostname(char *name, int len)
03354 {
03355     int r;
03356     if (!NtSocketsInitialized) {
03357         StartSockets();
03358     }
03359     RUBY_CRITICAL({
03360         r = gethostname(name, len);
03361         if (r == SOCKET_ERROR)
03362             errno = map_errno(WSAGetLastError());
03363     });
03364     return r;
03365 }
03366 
03367 #undef getprotobyname
03368 
03369 struct protoent * WSAAPI
03370 rb_w32_getprotobyname(const char *name)
03371 {
03372     struct protoent *r;
03373     if (!NtSocketsInitialized) {
03374         StartSockets();
03375     }
03376     RUBY_CRITICAL({
03377         r = getprotobyname(name);
03378         if (r == NULL)
03379             errno = map_errno(WSAGetLastError());
03380     });
03381     return r;
03382 }
03383 
03384 #undef getprotobynumber
03385 
03386 struct protoent * WSAAPI
03387 rb_w32_getprotobynumber(int num)
03388 {
03389     struct protoent *r;
03390     if (!NtSocketsInitialized) {
03391         StartSockets();
03392     }
03393     RUBY_CRITICAL({
03394         r = getprotobynumber(num);
03395         if (r == NULL)
03396             errno = map_errno(WSAGetLastError());
03397     });
03398     return r;
03399 }
03400 
03401 #undef getservbyname
03402 
03403 struct servent * WSAAPI
03404 rb_w32_getservbyname(const char *name, const char *proto)
03405 {
03406     struct servent *r;
03407     if (!NtSocketsInitialized) {
03408         StartSockets();
03409     }
03410     RUBY_CRITICAL({
03411         r = getservbyname(name, proto);
03412         if (r == NULL)
03413             errno = map_errno(WSAGetLastError());
03414     });
03415     return r;
03416 }
03417 
03418 #undef getservbyport
03419 
03420 struct servent * WSAAPI
03421 rb_w32_getservbyport(int port, const char *proto)
03422 {
03423     struct servent *r;
03424     if (!NtSocketsInitialized) {
03425         StartSockets();
03426     }
03427     RUBY_CRITICAL({
03428         r = getservbyport(port, proto);
03429         if (r == NULL)
03430             errno = map_errno(WSAGetLastError());
03431     });
03432     return r;
03433 }
03434 
03435 static int
03436 socketpair_internal(int af, int type, int protocol, SOCKET *sv)
03437 {
03438     SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
03439     struct sockaddr_in sock_in4;
03440 #ifdef INET6
03441     struct sockaddr_in6 sock_in6;
03442 #endif
03443     struct sockaddr *addr;
03444     int ret = -1;
03445     int len;
03446 
03447     if (!NtSocketsInitialized) {
03448         StartSockets();
03449     }
03450 
03451     switch (af) {
03452       case AF_INET:
03453 #if defined PF_INET && PF_INET != AF_INET
03454       case PF_INET:
03455 #endif
03456         sock_in4.sin_family = AF_INET;
03457         sock_in4.sin_port = 0;
03458         sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
03459         addr = (struct sockaddr *)&sock_in4;
03460         len = sizeof(sock_in4);
03461         break;
03462 #ifdef INET6
03463       case AF_INET6:
03464         memset(&sock_in6, 0, sizeof(sock_in6));
03465         sock_in6.sin6_family = AF_INET6;
03466         sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
03467         addr = (struct sockaddr *)&sock_in6;
03468         len = sizeof(sock_in6);
03469         break;
03470 #endif
03471       default:
03472         errno = EAFNOSUPPORT;
03473         return -1;
03474     }
03475     if (type != SOCK_STREAM) {
03476         errno = EPROTOTYPE;
03477         return -1;
03478     }
03479 
03480     sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
03481     sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
03482     RUBY_CRITICAL({
03483         do {
03484             svr = open_ifs_socket(af, type, protocol);
03485             if (svr == INVALID_SOCKET)
03486                 break;
03487             if (bind(svr, addr, len) < 0)
03488                 break;
03489             if (getsockname(svr, addr, &len) < 0)
03490                 break;
03491             if (type == SOCK_STREAM)
03492                 listen(svr, 5);
03493 
03494             w = open_ifs_socket(af, type, protocol);
03495             if (w == INVALID_SOCKET)
03496                 break;
03497             if (connect(w, addr, len) < 0)
03498                 break;
03499 
03500             r = accept(svr, addr, &len);
03501             if (r == INVALID_SOCKET)
03502                 break;
03503 
03504             ret = 0;
03505         } while (0);
03506 
03507         if (ret < 0) {
03508             errno = map_errno(WSAGetLastError());
03509             if (r != INVALID_SOCKET)
03510                 closesocket(r);
03511             if (w != INVALID_SOCKET)
03512                 closesocket(w);
03513         }
03514         else {
03515             sv[0] = r;
03516             sv[1] = w;
03517         }
03518         if (svr != INVALID_SOCKET)
03519             closesocket(svr);
03520     });
03521 
03522     return ret;
03523 }
03524 
03525 int
03526 rb_w32_socketpair(int af, int type, int protocol, int *sv)
03527 {
03528     SOCKET pair[2];
03529 
03530     if (socketpair_internal(af, type, protocol, pair) < 0)
03531         return -1;
03532     sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
03533     if (sv[0] == -1) {
03534         closesocket(pair[0]);
03535         closesocket(pair[1]);
03536         return -1;
03537     }
03538     sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
03539     if (sv[1] == -1) {
03540         rb_w32_close(sv[0]);
03541         closesocket(pair[1]);
03542         return -1;
03543     }
03544     st_insert(socklist, (st_data_t)pair[0], (st_data_t)0);
03545     st_insert(socklist, (st_data_t)pair[1], (st_data_t)0);
03546 
03547     return 0;
03548 }
03549 
03550 //
03551 // Networking stubs
03552 //
03553 
03554 void endhostent(void) {}
03555 void endnetent(void) {}
03556 void endprotoent(void) {}
03557 void endservent(void) {}
03558 
03559 struct netent *getnetent (void) {return (struct netent *) NULL;}
03560 
03561 struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
03562 
03563 struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
03564 
03565 struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
03566 
03567 struct servent *getservent (void) {return (struct servent *) NULL;}
03568 
03569 void sethostent (int stayopen) {}
03570 
03571 void setnetent (int stayopen) {}
03572 
03573 void setprotoent (int stayopen) {}
03574 
03575 void setservent (int stayopen) {}
03576 
03577 int
03578 fcntl(int fd, int cmd, ...)
03579 {
03580     SOCKET sock = TO_SOCKET(fd);
03581     va_list va;
03582     int arg;
03583     int ret;
03584     int flag = 0;
03585     st_data_t data;
03586     u_long ioctlArg;
03587 
03588     if (!is_socket(sock)) {
03589         errno = EBADF;
03590         return -1;
03591     }
03592     if (cmd != F_SETFL) {
03593         errno = EINVAL;
03594         return -1;
03595     }
03596 
03597     va_start(va, cmd);
03598     arg = va_arg(va, int);
03599     va_end(va);
03600     st_lookup(socklist, (st_data_t)sock, &data);
03601     flag = (int)data;
03602     if (arg & O_NONBLOCK) {
03603         flag |= O_NONBLOCK;
03604         ioctlArg = 1;
03605     }
03606     else {
03607         flag &= ~O_NONBLOCK;
03608         ioctlArg = 0;
03609     }
03610     RUBY_CRITICAL({
03611         ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
03612         if (ret == 0)
03613             st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
03614         else
03615             errno = map_errno(WSAGetLastError());
03616     });
03617 
03618     return ret;
03619 }
03620 
03621 #ifndef WNOHANG
03622 #define WNOHANG -1
03623 #endif
03624 
03625 static rb_pid_t
03626 poll_child_status(struct ChildRecord *child, int *stat_loc)
03627 {
03628     DWORD exitcode;
03629     DWORD err;
03630 
03631     if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
03632         /* If an error occured, return immediatly. */
03633     error_exit:
03634         err = GetLastError();
03635         if (err == ERROR_INVALID_PARAMETER)
03636             errno = ECHILD;
03637         else {
03638             if (GetLastError() == ERROR_INVALID_HANDLE)
03639                 errno = EINVAL;
03640             else
03641                 errno = map_errno(GetLastError());
03642         }
03643         CloseChildHandle(child);
03644         return -1;
03645     }
03646     if (exitcode != STILL_ACTIVE) {
03647         rb_pid_t pid;
03648         /* If already died, wait process's real termination. */
03649         if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
03650             goto error_exit;
03651         }
03652         pid = child->pid;
03653         CloseChildHandle(child);
03654         if (stat_loc) *stat_loc = exitcode << 8;
03655         return pid;
03656     }
03657     return 0;
03658 }
03659 
03660 rb_pid_t
03661 waitpid(rb_pid_t pid, int *stat_loc, int options)
03662 {
03663     DWORD timeout;
03664 
03665     if (options == WNOHANG) {
03666         timeout = 0;
03667     }
03668     else {
03669         timeout = INFINITE;
03670     }
03671 
03672     if (pid == -1) {
03673         int count = 0;
03674         int ret;
03675         HANDLE events[MAXCHILDNUM];
03676 
03677         FOREACH_CHILD(child) {
03678             if (!child->pid || child->pid < 0) continue;
03679             if ((pid = poll_child_status(child, stat_loc))) return pid;
03680             events[count++] = child->hProcess;
03681         } END_FOREACH_CHILD;
03682         if (!count) {
03683             errno = ECHILD;
03684             return -1;
03685         }
03686 
03687         ret = rb_w32_wait_events_blocking(events, count, timeout);
03688         if (ret == WAIT_TIMEOUT) return 0;
03689         if ((ret -= WAIT_OBJECT_0) == count) {
03690             return -1;
03691         }
03692         if (ret > count) {
03693             errno = map_errno(GetLastError());
03694             return -1;
03695         }
03696 
03697         return poll_child_status(FindChildSlotByHandle(events[ret]), stat_loc);
03698     }
03699     else {
03700         struct ChildRecord* child = FindChildSlot(pid);
03701         if (!child) {
03702             errno = ECHILD;
03703             return -1;
03704         }
03705 
03706         while (!(pid = poll_child_status(child, stat_loc))) {
03707             /* wait... */
03708             if (rb_w32_wait_events_blocking(&child->hProcess, 1, timeout) != WAIT_OBJECT_0) {
03709                 /* still active */
03710                 pid = 0;
03711                 break;
03712             }
03713         }
03714     }
03715 
03716     return pid;
03717 }
03718 
03719 #include <sys/timeb.h>
03720 
03721 static int
03722 filetime_to_timeval(const FILETIME* ft, struct timeval *tv)
03723 {
03724     ULARGE_INTEGER tmp;
03725     unsigned LONG_LONG lt;
03726 
03727     tmp.LowPart = ft->dwLowDateTime;
03728     tmp.HighPart = ft->dwHighDateTime;
03729     lt = tmp.QuadPart;
03730 
03731     /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
03732        convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
03733        the first leap second is at 1972/06/30, so we doesn't need to think
03734        about it. */
03735     lt /= 10;   /* to usec */
03736     lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * 1000 * 1000;
03737 
03738     tv->tv_sec = (long)(lt / (1000 * 1000));
03739     tv->tv_usec = (long)(lt % (1000 * 1000));
03740 
03741     return tv->tv_sec > 0 ? 0 : -1;
03742 }
03743 
03744 int _cdecl
03745 gettimeofday(struct timeval *tv, struct timezone *tz)
03746 {
03747     FILETIME ft;
03748 
03749     GetSystemTimeAsFileTime(&ft);
03750     filetime_to_timeval(&ft, tv);
03751 
03752     return 0;
03753 }
03754 
03755 char *
03756 rb_w32_getcwd(char *buffer, int size)
03757 {
03758     char *p = buffer;
03759     int len;
03760 
03761     len = GetCurrentDirectory(0, NULL);
03762     if (!len) {
03763         errno = map_errno(GetLastError());
03764         return NULL;
03765     }
03766 
03767     if (p) {
03768         if (size < len) {
03769             errno = ERANGE;
03770             return NULL;
03771         }
03772     }
03773     else {
03774         p = malloc(len);
03775         size = len;
03776         if (!p) {
03777             errno = ENOMEM;
03778             return NULL;
03779         }
03780     }
03781 
03782     if (!GetCurrentDirectory(size, p)) {
03783         errno = map_errno(GetLastError());
03784         if (!buffer)
03785             free(p);
03786         return NULL;
03787     }
03788 
03789     translate_char(p, '\\', '/');
03790 
03791     return p;
03792 }
03793 
03794 int
03795 chown(const char *path, int owner, int group)
03796 {
03797     return 0;
03798 }
03799 
03800 int
03801 rb_w32_uchown(const char *path, int owner, int group)
03802 {
03803     return 0;
03804 }
03805 
03806 int
03807 kill(int pid, int sig)
03808 {
03809     int ret = 0;
03810     DWORD err;
03811 
03812     if (pid < 0 || pid == 0 && sig != SIGINT) {
03813         errno = EINVAL;
03814         return -1;
03815     }
03816 
03817     (void)IfWin95(pid = -pid, 0);
03818     if ((unsigned int)pid == GetCurrentProcessId() &&
03819         (sig != 0 && sig != SIGKILL)) {
03820         if ((ret = raise(sig)) != 0) {
03821             /* MSVCRT doesn't set errno... */
03822             errno = EINVAL;
03823         }
03824         return ret;
03825     }
03826 
03827     switch (sig) {
03828       case 0:
03829         RUBY_CRITICAL({
03830             HANDLE hProc =
03831                 OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
03832             if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
03833                 if (GetLastError() == ERROR_INVALID_PARAMETER) {
03834                     errno = ESRCH;
03835                 }
03836                 else {
03837                     errno = EPERM;
03838                 }
03839                 ret = -1;
03840             }
03841             else {
03842                 CloseHandle(hProc);
03843             }
03844         });
03845         break;
03846 
03847       case SIGINT:
03848         RUBY_CRITICAL({
03849             DWORD ctrlEvent = CTRL_C_EVENT;
03850             if (pid != 0) {
03851                 /* CTRL+C signal cannot be generated for process groups.
03852                  * Instead, we use CTRL+BREAK signal. */
03853                 ctrlEvent = CTRL_BREAK_EVENT;
03854             }
03855             if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
03856                 if ((err = GetLastError()) == 0)
03857                     errno = EPERM;
03858                 else
03859                     errno = map_errno(GetLastError());
03860                 ret = -1;
03861             }
03862         });
03863         break;
03864 
03865       case SIGKILL:
03866         RUBY_CRITICAL({
03867             HANDLE hProc;
03868             struct ChildRecord* child = FindChildSlot(pid);
03869             if (child) {
03870                 hProc = child->hProcess;
03871             }
03872             else {
03873                 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
03874             }
03875             if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
03876                 if (GetLastError() == ERROR_INVALID_PARAMETER) {
03877                     errno = ESRCH;
03878                 }
03879                 else {
03880                     errno = EPERM;
03881                 }
03882                 ret = -1;
03883             }
03884             else {
03885                 DWORD status;
03886                 if (!GetExitCodeProcess(hProc, &status)) {
03887                     errno = map_errno(GetLastError());
03888                     ret = -1;
03889                 }
03890                 else if (status == STILL_ACTIVE) {
03891                     if (!TerminateProcess(hProc, 0)) {
03892                         errno = EPERM;
03893                         ret = -1;
03894                     }
03895                 }
03896                 else {
03897                     errno = ESRCH;
03898                     ret = -1;
03899                 }
03900                 if (!child) {
03901                     CloseHandle(hProc);
03902                 }
03903             }
03904         });
03905         break;
03906 
03907       default:
03908         errno = EINVAL;
03909         ret = -1;
03910         break;
03911     }
03912 
03913     return ret;
03914 }
03915 
03916 static int
03917 wlink(const WCHAR *from, const WCHAR *to)
03918 {
03919     typedef BOOL (WINAPI link_func)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
03920     static link_func *pCreateHardLinkW = NULL;
03921     static int myerrno = 0;
03922 
03923     if (!pCreateHardLinkW && !myerrno) {
03924         pCreateHardLinkW = (link_func *)get_proc_address("kernel32", "CreateHardLinkW", NULL);
03925         if (!pCreateHardLinkW)
03926             myerrno = ENOSYS;
03927     }
03928     if (!pCreateHardLinkW) {
03929         errno = myerrno;
03930         return -1;
03931     }
03932 
03933     if (!pCreateHardLinkW(to, from, NULL)) {
03934         errno = map_errno(GetLastError());
03935         return -1;
03936     }
03937 
03938     return 0;
03939 }
03940 
03941 int
03942 rb_w32_ulink(const char *from, const char *to)
03943 {
03944     WCHAR *wfrom;
03945     WCHAR *wto;
03946     int ret;
03947 
03948     if (!(wfrom = utf8_to_wstr(from, NULL)))
03949         return -1;
03950     if (!(wto = utf8_to_wstr(to, NULL))) {
03951         free(wfrom);
03952         return -1;
03953     }
03954     ret = wlink(wfrom, wto);
03955     free(wto);
03956     free(wfrom);
03957     return ret;
03958 }
03959 
03960 int
03961 link(const char *from, const char *to)
03962 {
03963     WCHAR *wfrom;
03964     WCHAR *wto;
03965     int ret;
03966 
03967     if (!(wfrom = filecp_to_wstr(from, NULL)))
03968         return -1;
03969     if (!(wto = filecp_to_wstr(to, NULL))) {
03970         free(wfrom);
03971         return -1;
03972     }
03973     ret = wlink(wfrom, wto);
03974     free(wto);
03975     free(wfrom);
03976     return ret;
03977 }
03978 
03979 int
03980 wait(int *status)
03981 {
03982     return waitpid(-1, status, 0);
03983 }
03984 
03985 char *
03986 rb_w32_getenv(const char *name)
03987 {
03988     int len = strlen(name);
03989     char *env;
03990 
03991     if (len == 0) return NULL;
03992     if (envarea) FreeEnvironmentStrings(envarea);
03993     envarea = GetEnvironmentStrings();
03994     if (!envarea) {
03995         map_errno(GetLastError());
03996         return NULL;
03997     }
03998 
03999     for (env = envarea; *env; env += strlen(env) + 1)
04000         if (strncasecmp(env, name, len) == 0 && *(env + len) == '=')
04001             return env + len + 1;
04002 
04003     return NULL;
04004 }
04005 
04006 static int
04007 wrename(const WCHAR *oldpath, const WCHAR *newpath)
04008 {
04009     int res = 0;
04010     int oldatts;
04011     int newatts;
04012 
04013     oldatts = GetFileAttributesW(oldpath);
04014     newatts = GetFileAttributesW(newpath);
04015 
04016     if (oldatts == -1) {
04017         errno = map_errno(GetLastError());
04018         return -1;
04019     }
04020 
04021     RUBY_CRITICAL({
04022         if (newatts != -1 && newatts & FILE_ATTRIBUTE_READONLY)
04023             SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
04024 
04025         if (!MoveFileW(oldpath, newpath))
04026             res = -1;
04027 
04028         if (res) {
04029             switch (GetLastError()) {
04030               case ERROR_ALREADY_EXISTS:
04031               case ERROR_FILE_EXISTS:
04032                 if (IsWinNT()) {
04033                     if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
04034                         res = 0;
04035                 }
04036                 else {
04037                     for (;;) {
04038                         if (!DeleteFileW(newpath) && GetLastError() != ERROR_FILE_NOT_FOUND)
04039                             break;
04040                         else if (MoveFileW(oldpath, newpath)) {
04041                             res = 0;
04042                             break;
04043                         }
04044                     }
04045                 }
04046             }
04047         }
04048 
04049         if (res)
04050             errno = map_errno(GetLastError());
04051         else
04052             SetFileAttributesW(newpath, oldatts);
04053     });
04054 
04055     return res;
04056 }
04057 
04058 int rb_w32_urename(const char *from, const char *to)
04059 {
04060     WCHAR *wfrom;
04061     WCHAR *wto;
04062     int ret = -1;
04063 
04064     if (!(wfrom = utf8_to_wstr(from, NULL)))
04065         return -1;
04066     if (!(wto = utf8_to_wstr(to, NULL))) {
04067         free(wfrom);
04068         return -1;
04069     }
04070     ret = wrename(wfrom, wto);
04071     free(wto);
04072     free(wfrom);
04073     return ret;
04074 }
04075 
04076 int rb_w32_rename(const char *from, const char *to)
04077 {
04078     WCHAR *wfrom;
04079     WCHAR *wto;
04080     int ret = -1;
04081 
04082     if (!(wfrom = filecp_to_wstr(from, NULL)))
04083         return -1;
04084     if (!(wto = filecp_to_wstr(to, NULL))) {
04085         free(wfrom);
04086         return -1;
04087     }
04088     ret = wrename(wfrom, wto);
04089     free(wto);
04090     free(wfrom);
04091     return ret;
04092 }
04093 
04094 static int
04095 isUNCRoot(const WCHAR *path)
04096 {
04097     if (path[0] == L'\\' && path[1] == L'\\') {
04098         const WCHAR *p;
04099         for (p = path + 2; *p; p++) {
04100             if (*p == L'\\')
04101                 break;
04102         }
04103         if (p[0] && p[1]) {
04104             for (p++; *p; p++) {
04105                 if (*p == L'\\')
04106                     break;
04107             }
04108             if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
04109                 return 1;
04110         }
04111     }
04112     return 0;
04113 }
04114 
04115 #define COPY_STAT(src, dest, size_cast) do {    \
04116         (dest).st_dev   = (src).st_dev;         \
04117         (dest).st_ino   = (src).st_ino;         \
04118         (dest).st_mode  = (src).st_mode;        \
04119         (dest).st_nlink = (src).st_nlink;       \
04120         (dest).st_uid   = (src).st_uid;         \
04121         (dest).st_gid   = (src).st_gid;         \
04122         (dest).st_rdev  = (src).st_rdev;        \
04123         (dest).st_size  = size_cast(src).st_size; \
04124         (dest).st_atime = (src).st_atime;       \
04125         (dest).st_mtime = (src).st_mtime;       \
04126         (dest).st_ctime = (src).st_ctime;       \
04127     } while (0)
04128 
04129 static time_t filetime_to_unixtime(const FILETIME *ft);
04130 
04131 #undef fstat
04132 int
04133 rb_w32_fstat(int fd, struct stat *st)
04134 {
04135     BY_HANDLE_FILE_INFORMATION info;
04136     int ret = fstat(fd, st);
04137 
04138     if (ret) return ret;
04139 #ifdef __BORLANDC__
04140     st->st_mode &= ~(S_IWGRP | S_IWOTH);
04141 #endif
04142     if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
04143 #ifdef __BORLANDC__
04144         if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
04145             st->st_mode |= S_IWUSR;
04146         }
04147 #endif
04148         st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
04149         st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
04150         st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
04151     }
04152     return ret;
04153 }
04154 
04155 int
04156 rb_w32_fstati64(int fd, struct stati64 *st)
04157 {
04158     BY_HANDLE_FILE_INFORMATION info;
04159     struct stat tmp;
04160     int ret = fstat(fd, &tmp);
04161 
04162     if (ret) return ret;
04163 #ifdef __BORLANDC__
04164     tmp.st_mode &= ~(S_IWGRP | S_IWOTH);
04165 #endif
04166     COPY_STAT(tmp, *st, +);
04167     if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
04168 #ifdef __BORLANDC__
04169         if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
04170             st->st_mode |= S_IWUSR;
04171         }
04172 #endif
04173         st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
04174         st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
04175         st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
04176         st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
04177     }
04178     return ret;
04179 }
04180 
04181 static time_t
04182 filetime_to_unixtime(const FILETIME *ft)
04183 {
04184     struct timeval tv;
04185 
04186     if (filetime_to_timeval(ft, &tv) == (time_t)-1)
04187         return 0;
04188     else
04189         return tv.tv_sec;
04190 }
04191 
04192 static unsigned
04193 fileattr_to_unixmode(DWORD attr, const WCHAR *path)
04194 {
04195     unsigned mode = 0;
04196 
04197     if (attr & FILE_ATTRIBUTE_READONLY) {
04198         mode |= S_IREAD;
04199     }
04200     else {
04201         mode |= S_IREAD | S_IWRITE | S_IWUSR;
04202     }
04203 
04204     if (attr & FILE_ATTRIBUTE_DIRECTORY) {
04205         mode |= S_IFDIR | S_IEXEC;
04206     }
04207     else {
04208         mode |= S_IFREG;
04209     }
04210 
04211     if (path && (mode & S_IFREG)) {
04212         const WCHAR *end = path + lstrlenW(path);
04213         while (path < end) {
04214             end = CharPrevW(path, end);
04215             if (*end == L'.') {
04216                 if ((_wcsicmp(end, L".bat") == 0) ||
04217                     (_wcsicmp(end, L".cmd") == 0) ||
04218                     (_wcsicmp(end, L".com") == 0) ||
04219                     (_wcsicmp(end, L".exe") == 0)) {
04220                     mode |= S_IEXEC;
04221                 }
04222                 break;
04223             }
04224         }
04225     }
04226 
04227     mode |= (mode & 0700) >> 3;
04228     mode |= (mode & 0700) >> 6;
04229 
04230     return mode;
04231 }
04232 
04233 static int
04234 check_valid_dir(const WCHAR *path)
04235 {
04236     WIN32_FIND_DATAW fd;
04237     HANDLE fh;
04238 
04239     /* GetFileAttributes() determines "..." as directory. */
04240     /* We recheck it by FindFirstFile(). */
04241     if (wcsstr(path, L"...") == NULL)
04242         return 0;
04243 
04244     fh = open_dir_handle(path, &fd);
04245     if (fh == INVALID_HANDLE_VALUE)
04246         return -1;
04247     FindClose(fh);
04248     return 0;
04249 }
04250 
04251 static int
04252 winnt_stat(const WCHAR *path, struct stati64 *st)
04253 {
04254     HANDLE h;
04255     WIN32_FIND_DATAW wfd;
04256     WIN32_FILE_ATTRIBUTE_DATA wfa;
04257 
04258     memset(st, 0, sizeof(*st));
04259     st->st_nlink = 1;
04260 
04261     if (wcspbrk(path, L"?*")) {
04262         errno = ENOENT;
04263         return -1;
04264     }
04265     if (GetFileAttributesExW(path, GetFileExInfoStandard, (void*)&wfa)) {
04266         if (wfa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
04267             if (check_valid_dir(path)) return -1;
04268             st->st_size = 0;
04269         }
04270         else {
04271             st->st_size = ((__int64)wfa.nFileSizeHigh << 32) | wfa.nFileSizeLow;
04272         }
04273         st->st_mode  = fileattr_to_unixmode(wfa.dwFileAttributes, path);
04274         st->st_atime = filetime_to_unixtime(&wfa.ftLastAccessTime);
04275         st->st_mtime = filetime_to_unixtime(&wfa.ftLastWriteTime);
04276         st->st_ctime = filetime_to_unixtime(&wfa.ftCreationTime);
04277     }
04278     else {
04279         /* GetFileAttributesEx failed; check why. */
04280         int e = GetLastError();
04281 
04282         if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME)
04283             || (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) {
04284             errno = map_errno(e);
04285             return -1;
04286         }
04287 
04288         /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
04289         h = FindFirstFileW(path, &wfd);
04290         if (h != INVALID_HANDLE_VALUE) {
04291             FindClose(h);
04292             st->st_mode  = fileattr_to_unixmode(wfd.dwFileAttributes, path);
04293             st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
04294             st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
04295             st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
04296             st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
04297         }
04298         else {
04299             errno = map_errno(GetLastError());
04300             return -1;
04301         }
04302     }
04303 
04304     st->st_dev = st->st_rdev = (iswalpha(path[0]) && path[1] == L':') ?
04305         towupper(path[0]) - L'A' : _getdrive() - 1;
04306 
04307     return 0;
04308 }
04309 
04310 #ifdef WIN95
04311 static int
04312 win95_stat(const WCHAR *path, struct stati64 *st)
04313 {
04314     int ret = _wstati64(path, st);
04315     if (ret) return ret;
04316     if (st->st_mode & S_IFDIR) {
04317         return check_valid_dir(path);
04318     }
04319     return 0;
04320 }
04321 #else
04322 #define win95_stat(path, st) -1
04323 #endif
04324 
04325 int
04326 rb_w32_stat(const char *path, struct stat *st)
04327 {
04328     struct stati64 tmp;
04329 
04330     if (rb_w32_stati64(path, &tmp)) return -1;
04331     COPY_STAT(tmp, *st, (_off_t));
04332     return 0;
04333 }
04334 
04335 static int
04336 wstati64(const WCHAR *path, struct stati64 *st)
04337 {
04338     const WCHAR *p;
04339     WCHAR *buf1, *s, *end;
04340     int len, size;
04341     int ret;
04342     VALUE v;
04343 
04344     if (!path || !st) {
04345         errno = EFAULT;
04346         return -1;
04347     }
04348     size = lstrlenW(path) + 2;
04349     buf1 = ALLOCV_N(WCHAR, v, size);
04350     for (p = path, s = buf1; *p; p++, s++) {
04351         if (*p == L'/')
04352             *s = L'\\';
04353         else
04354             *s = *p;
04355     }
04356     *s = '\0';
04357     len = s - buf1;
04358     if (!len || L'\"' == *(--s)) {
04359         errno = ENOENT;
04360         return -1;
04361     }
04362     end = buf1 + len - 1;
04363 
04364     if (isUNCRoot(buf1)) {
04365         if (*end == L'.')
04366             *end = L'\0';
04367         else if (*end != L'\\')
04368             lstrcatW(buf1, L"\\");
04369     }
04370     else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
04371         lstrcatW(buf1, L".");
04372 
04373     ret = IsWinNT() ? winnt_stat(buf1, st) : win95_stat(buf1, st);
04374     if (ret == 0) {
04375         st->st_mode &= ~(S_IWGRP | S_IWOTH);
04376     }
04377     if (v)
04378         ALLOCV_END(v);
04379 
04380     return ret;
04381 }
04382 
04383 int
04384 rb_w32_ustati64(const char *path, struct stati64 *st)
04385 {
04386     WCHAR *wpath;
04387     int ret;
04388 
04389     if (!(wpath = utf8_to_wstr(path, NULL)))
04390         return -1;
04391     ret = wstati64(wpath, st);
04392     free(wpath);
04393     return ret;
04394 }
04395 
04396 int
04397 rb_w32_stati64(const char *path, struct stati64 *st)
04398 {
04399     WCHAR *wpath;
04400     int ret;
04401 
04402     if (!(wpath = filecp_to_wstr(path, NULL)))
04403         return -1;
04404     ret = wstati64(wpath, st);
04405     free(wpath);
04406     return ret;
04407 }
04408 
04409 int
04410 rb_w32_access(const char *path, int mode)
04411 {
04412     struct stati64 stat;
04413     if (rb_w32_stati64(path, &stat) != 0)
04414         return -1;
04415     mode <<= 6;
04416     if ((stat.st_mode & mode) != mode) {
04417         errno = EACCES;
04418         return -1;
04419     }
04420     return 0;
04421 }
04422 
04423 int
04424 rb_w32_uaccess(const char *path, int mode)
04425 {
04426     struct stati64 stat;
04427     if (rb_w32_ustati64(path, &stat) != 0)
04428         return -1;
04429     mode <<= 6;
04430     if ((stat.st_mode & mode) != mode) {
04431         errno = EACCES;
04432         return -1;
04433     }
04434     return 0;
04435 }
04436 
04437 static int
04438 rb_chsize(HANDLE h, off_t size)
04439 {
04440     long upos, lpos, usize, lsize, uend, lend;
04441     off_t end;
04442     int ret = -1;
04443     DWORD e;
04444 
04445     if (((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
04446          (e = GetLastError())) ||
04447         ((lend = GetFileSize(h, (DWORD *)&uend)) == -1L && (e = GetLastError()))) {
04448         errno = map_errno(e);
04449         return -1;
04450     }
04451     end = ((off_t)uend << 32) | (unsigned long)lend;
04452     usize = (long)(size >> 32);
04453     lsize = (long)size;
04454     if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
04455         (e = GetLastError())) {
04456         errno = map_errno(e);
04457     }
04458     else if (!SetEndOfFile(h)) {
04459         errno = map_errno(GetLastError());
04460     }
04461     else {
04462         ret = 0;
04463     }
04464     SetFilePointer(h, lpos, &upos, SEEK_SET);
04465     return ret;
04466 }
04467 
04468 int
04469 rb_w32_truncate(const char *path, off_t length)
04470 {
04471     HANDLE h;
04472     int ret;
04473 #ifdef WIN95
04474     if (IsWin95()) {
04475         int fd = open(path, O_WRONLY), e = 0;
04476         if (fd == -1) return -1;
04477         ret = chsize(fd, (unsigned long)length);
04478         if (ret == -1) e = errno;
04479         close(fd);
04480         if (ret == -1) errno = e;
04481         return ret;
04482     }
04483 #endif
04484     h = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
04485     if (h == INVALID_HANDLE_VALUE) {
04486         errno = map_errno(GetLastError());
04487         return -1;
04488     }
04489     ret = rb_chsize(h, length);
04490     CloseHandle(h);
04491     return ret;
04492 }
04493 
04494 int
04495 rb_w32_ftruncate(int fd, off_t length)
04496 {
04497     HANDLE h;
04498 
04499 #ifdef WIN95
04500     if (IsWin95()) {
04501         return chsize(fd, (unsigned long)length);
04502     }
04503 #endif
04504     h = (HANDLE)_get_osfhandle(fd);
04505     if (h == (HANDLE)-1) return -1;
04506     return rb_chsize(h, length);
04507 }
04508 
04509 #ifdef __BORLANDC__
04510 off_t
04511 _filelengthi64(int fd)
04512 {
04513     DWORD u, l;
04514     int e;
04515 
04516     l = GetFileSize((HANDLE)_get_osfhandle(fd), &u);
04517     if (l == (DWORD)-1L && (e = GetLastError())) {
04518         errno = map_errno(e);
04519         return (off_t)-1;
04520     }
04521     return ((off_t)u << 32) | l;
04522 }
04523 
04524 off_t
04525 _lseeki64(int fd, off_t offset, int whence)
04526 {
04527     long u, l;
04528     int e;
04529     HANDLE h = (HANDLE)_get_osfhandle(fd);
04530 
04531     if (!h) {
04532         errno = EBADF;
04533         return -1;
04534     }
04535     u = (long)(offset >> 32);
04536     if ((l = SetFilePointer(h, (long)offset, &u, whence)) == -1L &&
04537         (e = GetLastError())) {
04538         errno = map_errno(e);
04539         return -1;
04540     }
04541     return ((off_t)u << 32) | l;
04542 }
04543 #endif
04544 
04545 int
04546 fseeko(FILE *stream, off_t offset, int whence)
04547 {
04548     off_t pos;
04549     switch (whence) {
04550       case SEEK_CUR:
04551         if (fgetpos(stream, (fpos_t *)&pos))
04552             return -1;
04553         pos += offset;
04554         break;
04555       case SEEK_END:
04556         if ((pos = _filelengthi64(fileno(stream))) == (off_t)-1)
04557             return -1;
04558         pos += offset;
04559         break;
04560       default:
04561         pos = offset;
04562         break;
04563     }
04564     return fsetpos(stream, (fpos_t *)&pos);
04565 }
04566 
04567 off_t
04568 rb_w32_ftello(FILE *stream)
04569 {
04570     off_t pos;
04571     if (fgetpos(stream, (fpos_t *)&pos)) return (off_t)-1;
04572     return pos;
04573 }
04574 
04575 static long
04576 filetime_to_clock(FILETIME *ft)
04577 {
04578     __int64 qw = ft->dwHighDateTime;
04579     qw <<= 32;
04580     qw |= ft->dwLowDateTime;
04581     qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
04582     return (long) qw;
04583 }
04584 
04585 int
04586 rb_w32_times(struct tms *tmbuf)
04587 {
04588     FILETIME create, exit, kernel, user;
04589 
04590     if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
04591         tmbuf->tms_utime = filetime_to_clock(&user);
04592         tmbuf->tms_stime = filetime_to_clock(&kernel);
04593         tmbuf->tms_cutime = 0;
04594         tmbuf->tms_cstime = 0;
04595     }
04596     else {
04597         tmbuf->tms_utime = clock();
04598         tmbuf->tms_stime = 0;
04599         tmbuf->tms_cutime = 0;
04600         tmbuf->tms_cstime = 0;
04601     }
04602     return 0;
04603 }
04604 
04605 #define yield_once() Sleep(0)
04606 #define yield_until(condition) do yield_once(); while (!(condition))
04607 
04608 static void
04609 catch_interrupt(void)
04610 {
04611     yield_once();
04612     RUBY_CRITICAL(rb_w32_wait_events(NULL, 0, 0));
04613 }
04614 
04615 #if defined __BORLANDC__
04616 #undef read
04617 int
04618 read(int fd, void *buf, size_t size)
04619 {
04620     int ret = _read(fd, buf, size);
04621     if ((ret < 0) && (errno == EPIPE)) {
04622         errno = 0;
04623         ret = 0;
04624     }
04625     catch_interrupt();
04626     return ret;
04627 }
04628 #endif
04629 
04630 #undef fgetc
04631 int
04632 rb_w32_getc(FILE* stream)
04633 {
04634     int c;
04635     if (enough_to_get(stream->FILE_COUNT)) {
04636         c = (unsigned char)*stream->FILE_READPTR++;
04637     }
04638     else {
04639         c = _filbuf(stream);
04640 #if defined __BORLANDC__
04641         if ((c == EOF) && (errno == EPIPE)) {
04642             clearerr(stream);
04643         }
04644 #endif
04645         catch_interrupt();
04646     }
04647     return c;
04648 }
04649 
04650 #undef fputc
04651 int
04652 rb_w32_putc(int c, FILE* stream)
04653 {
04654     if (enough_to_put(stream->FILE_COUNT)) {
04655         c = (unsigned char)(*stream->FILE_READPTR++ = (char)c);
04656     }
04657     else {
04658         c = _flsbuf(c, stream);
04659         catch_interrupt();
04660     }
04661     return c;
04662 }
04663 
04664 struct asynchronous_arg_t {
04665     /* output field */
04666     void* stackaddr;
04667     int errnum;
04668 
04669     /* input field */
04670     uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
04671     uintptr_t self;
04672     int argc;
04673     uintptr_t* argv;
04674 };
04675 
04676 static DWORD WINAPI
04677 call_asynchronous(PVOID argp)
04678 {
04679     DWORD ret;
04680     struct asynchronous_arg_t *arg = argp;
04681     arg->stackaddr = &argp;
04682     ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
04683     arg->errnum = errno;
04684     return ret;
04685 }
04686 
04687 uintptr_t
04688 rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
04689                     int argc, uintptr_t* argv, uintptr_t intrval)
04690 {
04691     DWORD val;
04692     BOOL interrupted = FALSE;
04693     HANDLE thr;
04694 
04695     RUBY_CRITICAL({
04696         struct asynchronous_arg_t arg;
04697 
04698         arg.stackaddr = NULL;
04699         arg.errnum = 0;
04700         arg.func = func;
04701         arg.self = self;
04702         arg.argc = argc;
04703         arg.argv = argv;
04704 
04705         thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
04706 
04707         if (thr) {
04708             yield_until(arg.stackaddr);
04709 
04710             if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
04711                 interrupted = TRUE;
04712 
04713                 if (TerminateThread(thr, intrval)) {
04714                     yield_once();
04715                 }
04716             }
04717 
04718             GetExitCodeThread(thr, &val);
04719             CloseHandle(thr);
04720 
04721             if (interrupted) {
04722                 /* must release stack of killed thread, why doesn't Windows? */
04723                 MEMORY_BASIC_INFORMATION m;
04724 
04725                 memset(&m, 0, sizeof(m));
04726                 if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
04727                     Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
04728                                   arg.stackaddr, GetLastError()));
04729                 }
04730                 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
04731                     Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
04732                                   m.AllocationBase, GetLastError()));
04733                 }
04734                 errno = EINTR;
04735             }
04736             else {
04737                 errno = arg.errnum;
04738             }
04739         }
04740     });
04741 
04742     if (!thr) {
04743         rb_fatal("failed to launch waiter thread:%ld", GetLastError());
04744     }
04745 
04746     return val;
04747 }
04748 
04749 char **
04750 rb_w32_get_environ(void)
04751 {
04752     char *envtop, *env;
04753     char **myenvtop, **myenv;
04754     int num;
04755 
04756     /*
04757      * We avoid values started with `='. If you want to deal those values,
04758      * change this function, and some functions in hash.c which recognize
04759      * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
04760      * CygWin deals these values by changing first `=' to '!'. But we don't
04761      * use such trick and follow cmd.exe's way that just doesn't show these
04762      * values.
04763      * (U.N. 2001-11-15)
04764      */
04765     envtop = GetEnvironmentStrings();
04766     for (env = envtop, num = 0; *env; env += strlen(env) + 1)
04767         if (*env != '=') num++;
04768 
04769     myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
04770     for (env = envtop, myenv = myenvtop; *env; env += strlen(env) + 1) {
04771         if (*env != '=') {
04772             if (!(*myenv = strdup(env))) {
04773                 break;
04774             }
04775             myenv++;
04776         }
04777     }
04778     *myenv = NULL;
04779     FreeEnvironmentStrings(envtop);
04780 
04781     return myenvtop;
04782 }
04783 
04784 void
04785 rb_w32_free_environ(char **env)
04786 {
04787     char **t = env;
04788 
04789     while (*t) free(*t++);
04790     free(env);
04791 }
04792 
04793 rb_pid_t
04794 rb_w32_getpid(void)
04795 {
04796     rb_pid_t pid;
04797 
04798     pid = GetCurrentProcessId();
04799 
04800     (void)IfWin95(pid = -pid, 0);
04801 
04802     return pid;
04803 }
04804 
04805 
04806 rb_pid_t
04807 rb_w32_getppid(void)
04808 {
04809     typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
04810     static query_func *pNtQueryInformationProcess = NULL;
04811     rb_pid_t ppid = 0;
04812 
04813     if (!IsWin95() && rb_w32_osver() >= 5) {
04814         if (!pNtQueryInformationProcess)
04815             pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
04816         if (pNtQueryInformationProcess) {
04817             struct {
04818                 long ExitStatus;
04819                 void* PebBaseAddress;
04820                 uintptr_t AffinityMask;
04821                 uintptr_t BasePriority;
04822                 uintptr_t UniqueProcessId;
04823                 uintptr_t ParentProcessId;
04824             } pbi;
04825             ULONG len;
04826             long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
04827             if (!ret) {
04828                 ppid = pbi.ParentProcessId;
04829             }
04830         }
04831     }
04832 
04833     return ppid;
04834 }
04835 
04836 int
04837 rb_w32_uopen(const char *file, int oflag, ...)
04838 {
04839     WCHAR *wfile;
04840     int ret;
04841     int pmode;
04842 
04843     va_list arg;
04844     va_start(arg, oflag);
04845     pmode = va_arg(arg, int);
04846     va_end(arg);
04847 
04848     if (!(wfile = utf8_to_wstr(file, NULL)))
04849         return -1;
04850     ret = rb_w32_wopen(wfile, oflag, pmode);
04851     free(wfile);
04852     return ret;
04853 }
04854 
04855 int
04856 rb_w32_open(const char *file, int oflag, ...)
04857 {
04858     WCHAR *wfile;
04859     int ret;
04860     int pmode;
04861 
04862     va_list arg;
04863     va_start(arg, oflag);
04864     pmode = va_arg(arg, int);
04865     va_end(arg);
04866 
04867     if ((oflag & O_TEXT) || !(oflag & O_BINARY))
04868         return _open(file, oflag, pmode);
04869 
04870     if (!(wfile = filecp_to_wstr(file, NULL)))
04871         return -1;
04872     ret = rb_w32_wopen(wfile, oflag, pmode);
04873     free(wfile);
04874     return ret;
04875 }
04876 
04877 int
04878 rb_w32_wopen(const WCHAR *file, int oflag, ...)
04879 {
04880     char flags = 0;
04881     int fd;
04882     DWORD access;
04883     DWORD create;
04884     DWORD attr = FILE_ATTRIBUTE_NORMAL;
04885     SECURITY_ATTRIBUTES sec;
04886     HANDLE h;
04887 
04888     if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
04889         va_list arg;
04890         int pmode;
04891         va_start(arg, oflag);
04892         pmode = va_arg(arg, int);
04893         va_end(arg);
04894         return _wopen(file, oflag, pmode);
04895     }
04896 
04897     sec.nLength = sizeof(sec);
04898     sec.lpSecurityDescriptor = NULL;
04899     if (oflag & O_NOINHERIT) {
04900         sec.bInheritHandle = FALSE;
04901         flags |= FNOINHERIT;
04902     }
04903     else {
04904         sec.bInheritHandle = TRUE;
04905     }
04906     oflag &= ~O_NOINHERIT;
04907 
04908     /* always open with binary mode */
04909     oflag &= ~(O_BINARY | O_TEXT);
04910 
04911     switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
04912       case O_RDWR:
04913         access = GENERIC_READ | GENERIC_WRITE;
04914         break;
04915       case O_RDONLY:
04916         access = GENERIC_READ;
04917         break;
04918       case O_WRONLY:
04919         access = GENERIC_WRITE;
04920         break;
04921       default:
04922         errno = EINVAL;
04923         return -1;
04924     }
04925     oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
04926 
04927     switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
04928       case O_CREAT:
04929         create = OPEN_ALWAYS;
04930         break;
04931       case 0:
04932       case O_EXCL:
04933         create = OPEN_EXISTING;
04934         break;
04935       case O_CREAT | O_EXCL:
04936       case O_CREAT | O_EXCL | O_TRUNC:
04937         create = CREATE_NEW;
04938         break;
04939       case O_TRUNC:
04940       case O_TRUNC | O_EXCL:
04941         create = TRUNCATE_EXISTING;
04942         break;
04943       case O_CREAT | O_TRUNC:
04944         create = CREATE_ALWAYS;
04945         break;
04946       default:
04947         errno = EINVAL;
04948         return -1;
04949     }
04950     if (oflag & O_CREAT) {
04951         va_list arg;
04952         int pmode;
04953         va_start(arg, oflag);
04954         pmode = va_arg(arg, int);
04955         va_end(arg);
04956         /* TODO: we need to check umask here, but it's not exported... */
04957         if (!(pmode & S_IWRITE))
04958             attr = FILE_ATTRIBUTE_READONLY;
04959     }
04960     oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
04961 
04962     if (oflag & O_TEMPORARY) {
04963         attr |= FILE_FLAG_DELETE_ON_CLOSE;
04964         access |= DELETE;
04965     }
04966     oflag &= ~O_TEMPORARY;
04967 
04968     if (oflag & _O_SHORT_LIVED)
04969         attr |= FILE_ATTRIBUTE_TEMPORARY;
04970     oflag &= ~_O_SHORT_LIVED;
04971 
04972     switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
04973       case 0:
04974         break;
04975       case O_SEQUENTIAL:
04976         attr |= FILE_FLAG_SEQUENTIAL_SCAN;
04977         break;
04978       case O_RANDOM:
04979         attr |= FILE_FLAG_RANDOM_ACCESS;
04980         break;
04981       default:
04982         errno = EINVAL;
04983         return -1;
04984     }
04985     oflag &= ~(O_SEQUENTIAL | O_RANDOM);
04986 
04987     if (oflag & ~O_APPEND) {
04988         errno = EINVAL;
04989         return -1;
04990     }
04991 
04992     /* allocate a C Runtime file handle */
04993     RUBY_CRITICAL({
04994         h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
04995         fd = _open_osfhandle((intptr_t)h, 0);
04996         CloseHandle(h);
04997     });
04998     if (fd == -1) {
04999         errno = EMFILE;
05000         return -1;
05001     }
05002     RUBY_CRITICAL({
05003         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
05004         _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
05005         _set_osflags(fd, 0);
05006 
05007         h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec,
05008                         create, attr, NULL);
05009         if (h == INVALID_HANDLE_VALUE) {
05010             errno = map_errno(GetLastError());
05011             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05012             fd = -1;
05013             goto quit;
05014         }
05015 
05016         switch (GetFileType(h)) {
05017           case FILE_TYPE_CHAR:
05018             flags |= FDEV;
05019             break;
05020           case FILE_TYPE_PIPE:
05021             flags |= FPIPE;
05022             break;
05023           case FILE_TYPE_UNKNOWN:
05024             errno = map_errno(GetLastError());
05025             CloseHandle(h);
05026             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05027             fd = -1;
05028             goto quit;
05029         }
05030         if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
05031             flags |= FAPPEND;
05032 
05033         _set_osfhnd(fd, (intptr_t)h);
05034         _osfile(fd) = flags | FOPEN;
05035 
05036         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05037       quit:
05038         ;
05039     });
05040 
05041     return fd;
05042 }
05043 
05044 int
05045 rb_w32_fclose(FILE *fp)
05046 {
05047     int fd = fileno(fp);
05048     SOCKET sock = TO_SOCKET(fd);
05049     int save_errno = errno;
05050 
05051     if (fflush(fp)) return -1;
05052     if (!is_socket(sock)) {
05053         UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
05054         return fclose(fp);
05055     }
05056     _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
05057     fclose(fp);
05058     errno = save_errno;
05059     if (closesocket(sock) == SOCKET_ERROR) {
05060         errno = map_errno(WSAGetLastError());
05061         return -1;
05062     }
05063     return 0;
05064 }
05065 
05066 int
05067 rb_w32_pipe(int fds[2])
05068 {
05069     static DWORD serial = 0;
05070     char name[] = "\\\\.\\pipe\\ruby0000000000000000-0000000000000000";
05071     char *p;
05072     SECURITY_ATTRIBUTES sec;
05073     HANDLE hRead, hWrite, h;
05074     int fdRead, fdWrite;
05075     int ret;
05076 
05077     /* if doesn't have CancelIo, use default pipe function */
05078     if (!cancel_io)
05079         return _pipe(fds, 65536L, _O_NOINHERIT);
05080 
05081     p = strchr(name, '0');
05082     snprintf(p, strlen(p) + 1, "%"PRI_PIDT_PREFIX"x-%lx", rb_w32_getpid(), serial++);
05083 
05084     sec.nLength = sizeof(sec);
05085     sec.lpSecurityDescriptor = NULL;
05086     sec.bInheritHandle = FALSE;
05087 
05088     RUBY_CRITICAL({
05089         hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
05090                                 0, 2, 65536, 65536, 0, &sec);
05091     });
05092     if (hRead == INVALID_HANDLE_VALUE) {
05093         DWORD err = GetLastError();
05094         if (err == ERROR_PIPE_BUSY)
05095             errno = EMFILE;
05096         else
05097             errno = map_errno(GetLastError());
05098         return -1;
05099     }
05100 
05101     RUBY_CRITICAL({
05102         hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
05103                             OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
05104     });
05105     if (hWrite == INVALID_HANDLE_VALUE) {
05106         errno = map_errno(GetLastError());
05107         CloseHandle(hRead);
05108         return -1;
05109     }
05110 
05111     RUBY_CRITICAL(do {
05112         ret = 0;
05113         h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
05114         fdRead = _open_osfhandle((intptr_t)h, 0);
05115         CloseHandle(h);
05116         if (fdRead == -1) {
05117             errno = EMFILE;
05118             CloseHandle(hWrite);
05119             CloseHandle(hRead);
05120             ret = -1;
05121             break;
05122         }
05123 
05124         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdRead)->lock)));
05125         _set_osfhnd(fdRead, (intptr_t)hRead);
05126         _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
05127         MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdRead)->lock)));
05128     } while (0));
05129     if (ret)
05130         return ret;
05131 
05132     RUBY_CRITICAL(do {
05133         h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
05134         fdWrite = _open_osfhandle((intptr_t)h, 0);
05135         CloseHandle(h);
05136         if (fdWrite == -1) {
05137             errno = EMFILE;
05138             CloseHandle(hWrite);
05139             ret = -1;
05140             break;
05141         }
05142         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdWrite)->lock)));
05143         _set_osfhnd(fdWrite, (intptr_t)hWrite);
05144         _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
05145         MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdWrite)->lock)));
05146     } while (0));
05147     if (ret) {
05148         rb_w32_close(fdRead);
05149         return ret;
05150     }
05151 
05152     fds[0] = fdRead;
05153     fds[1] = fdWrite;
05154 
05155     return 0;
05156 }
05157 
05158 int
05159 rb_w32_close(int fd)
05160 {
05161     SOCKET sock = TO_SOCKET(fd);
05162     int save_errno = errno;
05163     st_data_t key;
05164 
05165     if (!is_socket(sock)) {
05166         UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
05167         return _close(fd);
05168     }
05169     _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
05170     key = (st_data_t)sock;
05171     st_delete(socklist, &key, NULL);
05172     sock = (SOCKET)key;
05173     _close(fd);
05174     errno = save_errno;
05175     if (closesocket(sock) == SOCKET_ERROR) {
05176         errno = map_errno(WSAGetLastError());
05177         return -1;
05178     }
05179     return 0;
05180 }
05181 
05182 #undef read
05183 ssize_t
05184 rb_w32_read(int fd, void *buf, size_t size)
05185 {
05186     SOCKET sock = TO_SOCKET(fd);
05187     DWORD read;
05188     DWORD wait;
05189     DWORD err;
05190     size_t len;
05191     size_t ret;
05192     OVERLAPPED ol, *pol = NULL;
05193     BOOL isconsole;
05194     BOOL islineinput = FALSE;
05195     int start = 0;
05196 
05197     if (is_socket(sock))
05198         return rb_w32_recv(fd, buf, size, 0);
05199 
05200     // validate fd by using _get_osfhandle() because we cannot access _nhandle
05201     if (_get_osfhandle(fd) == -1) {
05202         return -1;
05203     }
05204 
05205     if (_osfile(fd) & FTEXT) {
05206         return _read(fd, buf, size);
05207     }
05208 
05209     MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
05210 
05211     if (!size || _osfile(fd) & FEOFLAG) {
05212         _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
05213         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05214         return 0;
05215     }
05216 
05217     ret = 0;
05218     isconsole = is_console(_osfhnd(fd));
05219     if (isconsole) {
05220         DWORD mode;
05221         GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
05222         islineinput = (mode & ENABLE_LINE_INPUT) != 0;
05223     }
05224   retry:
05225     /* get rid of console reading bug */
05226     if (isconsole) {
05227         if (start)
05228             len = 1;
05229         else {
05230             len = 0;
05231             start = 1;
05232         }
05233     }
05234     else
05235         len = size;
05236     size -= len;
05237 
05238     /* if have cancel_io, use Overlapped I/O */
05239     if (cancel_io) {
05240         memset(&ol, 0, sizeof(ol));
05241         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05242             LONG high = 0;
05243             DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high,
05244                                        FILE_CURRENT);
05245 #ifndef INVALID_SET_FILE_POINTER
05246 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
05247 #endif
05248             if (low == INVALID_SET_FILE_POINTER) {
05249                 errno = map_errno(GetLastError());
05250                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05251                 return -1;
05252             }
05253             ol.Offset = low;
05254             ol.OffsetHigh = high;
05255         }
05256         ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
05257         if (!ol.hEvent) {
05258             errno = map_errno(GetLastError());
05259             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05260             return -1;
05261         }
05262 
05263         pol = &ol;
05264     }
05265 
05266     if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, pol)) {
05267         err = GetLastError();
05268         if (err != ERROR_IO_PENDING) {
05269             if (pol) CloseHandle(ol.hEvent);
05270             if (err == ERROR_ACCESS_DENIED)
05271                 errno = EBADF;
05272             else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
05273                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05274                 return 0;
05275             }
05276             else
05277                 errno = map_errno(err);
05278 
05279             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05280             return -1;
05281         }
05282 
05283         if (pol) {
05284             wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
05285             if (wait != WAIT_OBJECT_0) {
05286                 if (wait == WAIT_OBJECT_0 + 1)
05287                     errno = EINTR;
05288                 else
05289                     errno = map_errno(GetLastError());
05290                 CloseHandle(ol.hEvent);
05291                 cancel_io((HANDLE)_osfhnd(fd));
05292                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05293                 return -1;
05294             }
05295 
05296             if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
05297                 (err = GetLastError()) != ERROR_HANDLE_EOF) {
05298                 int ret = 0;
05299                 if (err != ERROR_BROKEN_PIPE) {
05300                     errno = map_errno(err);
05301                     ret = -1;
05302                 }
05303                 CloseHandle(ol.hEvent);
05304                 cancel_io((HANDLE)_osfhnd(fd));
05305                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05306                 return ret;
05307             }
05308         }
05309     }
05310 
05311     if (pol) {
05312         CloseHandle(ol.hEvent);
05313 
05314         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05315             LONG high = ol.OffsetHigh;
05316             DWORD low = ol.Offset + read;
05317             if (low < ol.Offset)
05318                 ++high;
05319             SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
05320         }
05321     }
05322 
05323     ret += read;
05324     if (read >= len) {
05325         buf = (char *)buf + read;
05326         if (!(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
05327             goto retry;
05328     }
05329     if (read == 0)
05330         _set_osflags(fd, _osfile(fd) | FEOFLAG);
05331 
05332 
05333     MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05334 
05335     return ret;
05336 }
05337 
05338 #undef write
05339 ssize_t
05340 rb_w32_write(int fd, const void *buf, size_t size)
05341 {
05342     SOCKET sock = TO_SOCKET(fd);
05343     DWORD written;
05344     DWORD wait;
05345     DWORD err;
05346     size_t len;
05347     size_t ret;
05348     OVERLAPPED ol, *pol = NULL;
05349 
05350     if (is_socket(sock))
05351         return rb_w32_send(fd, buf, size, 0);
05352 
05353     // validate fd by using _get_osfhandle() because we cannot access _nhandle
05354     if (_get_osfhandle(fd) == -1) {
05355         return -1;
05356     }
05357 
05358     if ((_osfile(fd) & FTEXT) &&
05359         (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
05360         return _write(fd, buf, size);
05361     }
05362 
05363     MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
05364 
05365     if (!size || _osfile(fd) & FEOFLAG) {
05366         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05367         return 0;
05368     }
05369 
05370     ret = 0;
05371   retry:
05372     /* get rid of console writing bug */
05373     len = (_osfile(fd) & FDEV) ? min(32 * 1024, size) : size;
05374     size -= len;
05375 
05376     /* if have cancel_io, use Overlapped I/O */
05377     if (cancel_io) {
05378         memset(&ol, 0, sizeof(ol));
05379         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05380             LONG high = 0;
05381             DWORD method = _osfile(fd) & FAPPEND ? FILE_END : FILE_CURRENT;
05382             DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method);
05383 #ifndef INVALID_SET_FILE_POINTER
05384 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
05385 #endif
05386             if (low == INVALID_SET_FILE_POINTER) {
05387                 errno = map_errno(GetLastError());
05388                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05389                 return -1;
05390             }
05391             ol.Offset = low;
05392             ol.OffsetHigh = high;
05393         }
05394         ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
05395         if (!ol.hEvent) {
05396             errno = map_errno(GetLastError());
05397             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05398             return -1;
05399         }
05400 
05401         pol = &ol;
05402     }
05403 
05404     if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, pol)) {
05405         err = GetLastError();
05406         if (err != ERROR_IO_PENDING) {
05407             if (pol) CloseHandle(ol.hEvent);
05408             if (err == ERROR_ACCESS_DENIED)
05409                 errno = EBADF;
05410             else
05411                 errno = map_errno(err);
05412 
05413             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05414             return -1;
05415         }
05416 
05417         if (pol) {
05418             wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
05419             if (wait != WAIT_OBJECT_0) {
05420                 if (wait == WAIT_OBJECT_0 + 1)
05421                     errno = EINTR;
05422                 else
05423                     errno = map_errno(GetLastError());
05424                 CloseHandle(ol.hEvent);
05425                 cancel_io((HANDLE)_osfhnd(fd));
05426                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05427                 return -1;
05428             }
05429 
05430             if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written,
05431                                      TRUE)) {
05432                 errno = map_errno(err);
05433                 CloseHandle(ol.hEvent);
05434                 cancel_io((HANDLE)_osfhnd(fd));
05435                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05436                 return -1;
05437             }
05438         }
05439     }
05440 
05441     if (pol) {
05442         CloseHandle(ol.hEvent);
05443 
05444         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05445             LONG high = ol.OffsetHigh;
05446             DWORD low = ol.Offset + written;
05447             if (low < ol.Offset)
05448                 ++high;
05449             SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
05450         }
05451     }
05452 
05453     ret += written;
05454     if (written == len) {
05455         buf = (const char *)buf + len;
05456         if (size > 0)
05457             goto retry;
05458     }
05459 
05460     MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05461 
05462     return ret;
05463 }
05464 
05465 long
05466 rb_w32_write_console(uintptr_t strarg, int fd)
05467 {
05468     static int disable;
05469     HANDLE handle;
05470     DWORD dwMode, reslen;
05471     VALUE str = strarg;
05472 
05473     if (disable) return -1L;
05474     handle = (HANDLE)_osfhnd(fd);
05475     if (!GetConsoleMode(handle, &dwMode) ||
05476         !rb_econv_has_convpath_p(rb_enc_name(rb_enc_get(str)), "UTF-16LE"))
05477         return -1L;
05478 
05479     str = rb_str_encode(str, rb_enc_from_encoding(rb_enc_find("UTF-16LE")),
05480                         ECONV_INVALID_REPLACE|ECONV_UNDEF_REPLACE, Qnil);
05481     if (!WriteConsoleW(handle, (LPWSTR)RSTRING_PTR(str), RSTRING_LEN(str)/2,
05482                        &reslen, NULL)) {
05483         if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
05484             disable = TRUE;
05485         return -1L;
05486     }
05487     return (long)reslen;
05488 }
05489 
05490 static int
05491 unixtime_to_filetime(time_t time, FILETIME *ft)
05492 {
05493     ULARGE_INTEGER tmp;
05494 
05495     tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
05496     ft->dwLowDateTime = tmp.LowPart;
05497     ft->dwHighDateTime = tmp.HighPart;
05498     return 0;
05499 }
05500 
05501 static int
05502 wutime(const WCHAR *path, const struct utimbuf *times)
05503 {
05504     HANDLE hFile;
05505     FILETIME atime, mtime;
05506     struct stati64 stat;
05507     int ret = 0;
05508 
05509     if (wstati64(path, &stat)) {
05510         return -1;
05511     }
05512 
05513     if (times) {
05514         if (unixtime_to_filetime(times->actime, &atime)) {
05515             return -1;
05516         }
05517         if (unixtime_to_filetime(times->modtime, &mtime)) {
05518             return -1;
05519         }
05520     }
05521     else {
05522         GetSystemTimeAsFileTime(&atime);
05523         mtime = atime;
05524     }
05525 
05526     RUBY_CRITICAL({
05527         const DWORD attr = GetFileAttributesW(path);
05528         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
05529             SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
05530         hFile = CreateFileW(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
05531                             IsWin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, 0);
05532         if (hFile == INVALID_HANDLE_VALUE) {
05533             errno = map_errno(GetLastError());
05534             ret = -1;
05535         }
05536         else {
05537             if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
05538                 errno = map_errno(GetLastError());
05539                 ret = -1;
05540             }
05541             CloseHandle(hFile);
05542         }
05543         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
05544             SetFileAttributesW(path, attr);
05545     });
05546 
05547     return ret;
05548 }
05549 
05550 int
05551 rb_w32_uutime(const char *path, const struct utimbuf *times)
05552 {
05553     WCHAR *wpath;
05554     int ret;
05555 
05556     if (!(wpath = utf8_to_wstr(path, NULL)))
05557         return -1;
05558     ret = wutime(wpath, times);
05559     free(wpath);
05560     return ret;
05561 }
05562 
05563 int
05564 rb_w32_utime(const char *path, const struct utimbuf *times)
05565 {
05566     WCHAR *wpath;
05567     int ret;
05568 
05569     if (!(wpath = filecp_to_wstr(path, NULL)))
05570         return -1;
05571     ret = wutime(wpath, times);
05572     free(wpath);
05573     return ret;
05574 }
05575 
05576 int
05577 rb_w32_uchdir(const char *path)
05578 {
05579     WCHAR *wpath;
05580     int ret;
05581 
05582     if (!(wpath = utf8_to_wstr(path, NULL)))
05583         return -1;
05584     ret = _wchdir(wpath);
05585     free(wpath);
05586     return ret;
05587 }
05588 
05589 static int
05590 wmkdir(const WCHAR *wpath, int mode)
05591 {
05592     int ret = -1;
05593 
05594     RUBY_CRITICAL(do {
05595         if (CreateDirectoryW(wpath, NULL) == FALSE) {
05596             errno = map_errno(GetLastError());
05597             break;
05598         }
05599         if (_wchmod(wpath, mode) == -1) {
05600             RemoveDirectoryW(wpath);
05601             break;
05602         }
05603         ret = 0;
05604     } while (0));
05605     return ret;
05606 }
05607 
05608 int
05609 rb_w32_umkdir(const char *path, int mode)
05610 {
05611     WCHAR *wpath;
05612     int ret;
05613 
05614     if (!(wpath = utf8_to_wstr(path, NULL)))
05615         return -1;
05616     ret = wmkdir(wpath, mode);
05617     free(wpath);
05618     return ret;
05619 }
05620 
05621 int
05622 rb_w32_mkdir(const char *path, int mode)
05623 {
05624     WCHAR *wpath;
05625     int ret;
05626 
05627     if (!(wpath = filecp_to_wstr(path, NULL)))
05628         return -1;
05629     ret = wmkdir(wpath, mode);
05630     free(wpath);
05631     return ret;
05632 }
05633 
05634 static int
05635 wrmdir(const WCHAR *wpath)
05636 {
05637     int ret = 0;
05638     RUBY_CRITICAL({
05639         const DWORD attr = GetFileAttributesW(wpath);
05640         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05641             SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
05642         }
05643         if (RemoveDirectoryW(wpath) == FALSE) {
05644             errno = map_errno(GetLastError());
05645             ret = -1;
05646             if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05647                 SetFileAttributesW(wpath, attr);
05648             }
05649         }
05650     });
05651     return ret;
05652 }
05653 
05654 int
05655 rb_w32_rmdir(const char *path)
05656 {
05657     WCHAR *wpath;
05658     int ret;
05659 
05660     if (!(wpath = filecp_to_wstr(path, NULL)))
05661         return -1;
05662     ret = wrmdir(wpath);
05663     free(wpath);
05664     return ret;
05665 }
05666 
05667 int
05668 rb_w32_urmdir(const char *path)
05669 {
05670     WCHAR *wpath;
05671     int ret;
05672 
05673     if (!(wpath = utf8_to_wstr(path, NULL)))
05674         return -1;
05675     ret = wrmdir(wpath);
05676     free(wpath);
05677     return ret;
05678 }
05679 
05680 static int
05681 wunlink(const WCHAR *path)
05682 {
05683     int ret = 0;
05684     RUBY_CRITICAL({
05685         const DWORD attr = GetFileAttributesW(path);
05686         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05687             SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
05688         }
05689         if (!DeleteFileW(path)) {
05690             errno = map_errno(GetLastError());
05691             ret = -1;
05692             if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05693                 SetFileAttributesW(path, attr);
05694             }
05695         }
05696     });
05697     return ret;
05698 }
05699 
05700 int
05701 rb_w32_uunlink(const char *path)
05702 {
05703     WCHAR *wpath;
05704     int ret;
05705 
05706     if (!(wpath = utf8_to_wstr(path, NULL)))
05707         return -1;
05708     ret = wunlink(wpath);
05709     free(wpath);
05710     return ret;
05711 }
05712 
05713 int
05714 rb_w32_unlink(const char *path)
05715 {
05716     WCHAR *wpath;
05717     int ret;
05718 
05719     if (!(wpath = filecp_to_wstr(path, NULL)))
05720         return -1;
05721     ret = wunlink(wpath);
05722     free(wpath);
05723     return ret;
05724 }
05725 
05726 int
05727 rb_w32_uchmod(const char *path, int mode)
05728 {
05729     WCHAR *wpath;
05730     int ret;
05731 
05732     if (!(wpath = utf8_to_wstr(path, NULL)))
05733         return -1;
05734     ret = _wchmod(wpath, mode);
05735     free(wpath);
05736     return ret;
05737 }
05738 
05739 #if !defined(__BORLANDC__)
05740 int
05741 rb_w32_isatty(int fd)
05742 {
05743     DWORD mode;
05744 
05745     // validate fd by using _get_osfhandle() because we cannot access _nhandle
05746     if (_get_osfhandle(fd) == -1) {
05747         return 0;
05748     }
05749     if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
05750         errno = ENOTTY;
05751         return 0;
05752     }
05753     return 1;
05754 }
05755 #endif
05756 
05757 //
05758 // Fix bcc32's stdio bug
05759 //
05760 
05761 #ifdef __BORLANDC__
05762 static int
05763 too_many_files(void)
05764 {
05765     FILE *f;
05766     for (f = _streams; f < _streams + _nfile; f++) {
05767         if (f->fd < 0) return 0;
05768     }
05769     return 1;
05770 }
05771 
05772 #undef fopen
05773 FILE *
05774 rb_w32_fopen(const char *path, const char *mode)
05775 {
05776     FILE *f = (errno = 0, fopen(path, mode));
05777     if (f == NULL && errno == 0) {
05778         if (too_many_files())
05779             errno = EMFILE;
05780     }
05781     return f;
05782 }
05783 
05784 FILE *
05785 rb_w32_fdopen(int handle, const char *type)
05786 {
05787     FILE *f = (errno = 0, _fdopen(handle, (char *)type));
05788     if (f == NULL && errno == 0) {
05789         if (handle < 0)
05790             errno = EBADF;
05791         else if (too_many_files())
05792             errno = EMFILE;
05793     }
05794     return f;
05795 }
05796 
05797 FILE *
05798 rb_w32_fsopen(const char *path, const char *mode, int shflags)
05799 {
05800     FILE *f = (errno = 0, _fsopen(path, mode, shflags));
05801     if (f == NULL && errno == 0) {
05802         if (too_many_files())
05803             errno = EMFILE;
05804     }
05805     return f;
05806 }
05807 #endif
05808 
05809 #if defined(_MSC_VER) && RT_VER <= 60
05810 extern long _ftol(double);
05811 long
05812 _ftol2(double d)
05813 {
05814     return _ftol(d);
05815 }
05816 long
05817 _ftol2_sse(double d)
05818 {
05819     return _ftol(d);
05820 }
05821 #endif
05822 
05823 #ifndef signbit
05824 int
05825 signbit(double x)
05826 {
05827     int *ip = (int *)(&x + 1) - 1;
05828     return *ip < 0;
05829 }
05830 #endif
05831 
05832 char * WSAAPI
05833 rb_w32_inet_ntop(int af, void *addr, char *numaddr, size_t numaddr_len)
05834 {
05835     typedef char *(WSAAPI inet_ntop_t)(int, void *, char *, size_t);
05836     inet_ntop_t *pInetNtop;
05837     pInetNtop = (inet_ntop_t *)get_proc_address("ws2_32", "inet_ntop", NULL);
05838     if(pInetNtop){
05839        return pInetNtop(af,addr,numaddr,numaddr_len);
05840     }else{
05841        struct in_addr in;
05842        memcpy(&in.s_addr, addr, sizeof(in.s_addr));
05843        snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
05844     }
05845     return numaddr;
05846 }
05847 
05848 char
05849 rb_w32_fd_is_text(int fd) {
05850     return _osfile(fd) & FTEXT;
05851 }
05852