Ruby 1.9.3p327(2012-11-10revision37606)
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 '&':
01379           case '\n':
01380             if (!quote)
01381                 return TRUE;
01382             ptr++;
01383             break;
01384 
01385           case '%':
01386             if (*++ptr != '_' && !ISALPHA(*ptr)) break;
01387             while (*++ptr == '_' || ISALNUM(*ptr));
01388             if (*ptr++ == '%') return TRUE;
01389             break;
01390 
01391           case '\\':
01392             ptr++;
01393           default:
01394             ptr = CharNext(ptr);
01395             break;
01396         }
01397     }
01398     return FALSE;
01399 }
01400 
01401 static inline char *
01402 skipspace(char *ptr)
01403 {
01404     while (ISSPACE(*ptr))
01405         ptr++;
01406     return ptr;
01407 }
01408 
01409 int
01410 rb_w32_cmdvector(const char *cmd, char ***vec)
01411 {
01412     int globbing, len;
01413     int elements, strsz, done;
01414     int slashes, escape;
01415     char *ptr, *base, *buffer, *cmdline;
01416     char **vptr;
01417     char quote;
01418     NtCmdLineElement *curr, **tail;
01419     NtCmdLineElement *cmdhead = NULL, **cmdtail = &cmdhead;
01420 
01421     //
01422     // just return if we don't have a command line
01423     //
01424 
01425     while (ISSPACE(*cmd))
01426         cmd++;
01427     if (!*cmd) {
01428         *vec = NULL;
01429         return 0;
01430     }
01431 
01432     ptr = cmdline = strdup(cmd);
01433 
01434     //
01435     // Ok, parse the command line, building a list of CmdLineElements.
01436     // When we've finished, and it's an input command (meaning that it's
01437     // the processes argv), we'll do globing and then build the argument
01438     // vector.
01439     // The outer loop does one interation for each element seen.
01440     // The inner loop does one interation for each character in the element.
01441     //
01442 
01443     while (*(ptr = skipspace(ptr))) {
01444         base = ptr;
01445         quote = slashes = globbing = escape = 0;
01446         for (done = 0; !done && *ptr; ) {
01447             //
01448             // Switch on the current character. We only care about the
01449             // white-space characters, the  wild-card characters, and the
01450             // quote characters.
01451             //
01452 
01453             switch (*ptr) {
01454               case '\\':
01455                 if (quote != '\'') slashes++;
01456                 break;
01457 
01458               case ' ':
01459               case '\t':
01460               case '\n':
01461                 //
01462                 // if we're not in a string, then we're finished with this
01463                 // element
01464                 //
01465 
01466                 if (!quote) {
01467                     *ptr = 0;
01468                     done = 1;
01469                 }
01470                 break;
01471 
01472               case '*':
01473               case '?':
01474               case '[':
01475               case '{':
01476                 //
01477                 // record the fact that this element has a wildcard character
01478                 // N.B. Don't glob if inside a single quoted string
01479                 //
01480 
01481                 if (quote != '\'')
01482                     globbing++;
01483                 slashes = 0;
01484                 break;
01485 
01486               case '\'':
01487               case '\"':
01488                 //
01489                 // if we're already in a string, see if this is the
01490                 // terminating close-quote. If it is, we're finished with
01491                 // the string, but not neccessarily with the element.
01492                 // If we're not already in a string, start one.
01493                 //
01494 
01495                 if (!(slashes & 1)) {
01496                     if (!quote)
01497                         quote = *ptr;
01498                     else if (quote == *ptr) {
01499                         if (quote == '"' && quote == ptr[1])
01500                             ptr++;
01501                         quote = '\0';
01502                     }
01503                 }
01504                 escape++;
01505                 slashes = 0;
01506                 break;
01507 
01508               default:
01509                 ptr = CharNext(ptr);
01510                 slashes = 0;
01511                 continue;
01512             }
01513             ptr++;
01514         }
01515 
01516         //
01517         // when we get here, we've got a pair of pointers to the element,
01518         // base and ptr. Base points to the start of the element while ptr
01519         // points to the character following the element.
01520         //
01521 
01522         len = ptr - base;
01523         if (done) --len;
01524 
01525         //
01526         // if it's an input vector element and it's enclosed by quotes,
01527         // we can remove them.
01528         //
01529 
01530         if (escape) {
01531             char *p = base, c;
01532             slashes = quote = 0;
01533             while (p < base + len) {
01534                 switch (c = *p) {
01535                   case '\\':
01536                     p++;
01537                     if (quote != '\'') slashes++;
01538                     break;
01539 
01540                   case '\'':
01541                   case '"':
01542                     if (!(slashes & 1) && quote && quote != c) {
01543                         p++;
01544                         slashes = 0;
01545                         break;
01546                     }
01547                     memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
01548                            base + len - p);
01549                     len -= ((slashes + 1) >> 1) + (~slashes & 1);
01550                     p -= (slashes + 1) >> 1;
01551                     if (!(slashes & 1)) {
01552                         if (quote) {
01553                             if (quote == '"' && quote == *p)
01554                                 p++;
01555                             quote = '\0';
01556                         }
01557                         else
01558                             quote = c;
01559                     }
01560                     else
01561                         p++;
01562                     slashes = 0;
01563                     break;
01564 
01565                   default:
01566                     p = CharNext(p);
01567                     slashes = 0;
01568                     break;
01569                 }
01570             }
01571         }
01572 
01573         curr = (NtCmdLineElement *)calloc(sizeof(NtCmdLineElement), 1);
01574         if (!curr) goto do_nothing;
01575         curr->str = base;
01576         curr->len = len;
01577 
01578         if (globbing && (tail = cmdglob(curr, cmdtail))) {
01579             cmdtail = tail;
01580         }
01581         else {
01582             *cmdtail = curr;
01583             cmdtail = &curr->next;
01584         }
01585     }
01586 
01587     //
01588     // Almost done!
01589     // Count up the elements, then allocate space for a vector of pointers
01590     // (argv) and a string table for the elements.
01591     //
01592 
01593     for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
01594         elements++;
01595         strsz += (curr->len + 1);
01596     }
01597 
01598     len = (elements+1)*sizeof(char *) + strsz;
01599     buffer = (char *)malloc(len);
01600     if (!buffer) {
01601       do_nothing:
01602         while (curr = cmdhead) {
01603             cmdhead = curr->next;
01604             if (curr->flags & NTMALLOC) free(curr->str);
01605             free(curr);
01606         }
01607         free(cmdline);
01608         for (vptr = *vec; *vptr; ++vptr);
01609         return vptr - *vec;
01610     }
01611 
01612     //
01613     // make vptr point to the start of the buffer
01614     // and ptr point to the area we'll consider the string table.
01615     //
01616     //   buffer (*vec)
01617     //   |
01618     //   V       ^---------------------V
01619     //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
01620     //   |   |       | ....  | NULL  |   | ..... |\0 |   | ..... |\0 |...
01621     //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
01622     //   |-  elements+1             -| ^ 1st element   ^ 2nd element
01623 
01624     vptr = (char **) buffer;
01625 
01626     ptr = buffer + (elements+1) * sizeof(char *);
01627 
01628     while (curr = cmdhead) {
01629         strlcpy(ptr, curr->str, curr->len + 1);
01630         *vptr++ = ptr;
01631         ptr += curr->len + 1;
01632         cmdhead = curr->next;
01633         if (curr->flags & NTMALLOC) free(curr->str);
01634         free(curr);
01635     }
01636     *vptr = 0;
01637 
01638     *vec = (char **) buffer;
01639     free(cmdline);
01640     return elements;
01641 }
01642 
01643 //
01644 // UNIX compatible directory access functions for NT
01645 //
01646 
01647 #define PATHLEN 1024
01648 
01649 //
01650 // The idea here is to read all the directory names into a string table
01651 // (separated by nulls) and when one of the other dir functions is called
01652 // return the pointer to the current file name.
01653 //
01654 
01655 #define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] &  (1 << (i) % CHAR_BIT))
01656 #define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
01657 
01658 #define BitOfIsDir(n) ((n) * 2)
01659 #define BitOfIsRep(n) ((n) * 2 + 1)
01660 #define DIRENT_PER_CHAR (CHAR_BIT / 2)
01661 
01662 static HANDLE
01663 open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
01664 {
01665     HANDLE fh;
01666     static const WCHAR wildcard[] = L"\\*";
01667     WCHAR *scanname;
01668     WCHAR *p;
01669     int len;
01670     VALUE v;
01671 
01672     //
01673     // Create the search pattern
01674     //
01675     len = lstrlenW(filename);
01676     scanname = ALLOCV_N(WCHAR, v, len + sizeof(wildcard) / sizeof(WCHAR));
01677     lstrcpyW(scanname, filename);
01678     p = CharPrevW(scanname, scanname + len);
01679     if (*p == L'/' || *p == L'\\' || *p == L':')
01680         lstrcatW(scanname, wildcard + 1);
01681     else
01682         lstrcatW(scanname, wildcard);
01683 
01684     //
01685     // do the FindFirstFile call
01686     //
01687     fh = FindFirstFileW(scanname, fd);
01688     ALLOCV_END(v);
01689     if (fh == INVALID_HANDLE_VALUE) {
01690         errno = map_errno(GetLastError());
01691     }
01692     return fh;
01693 }
01694 
01695 static DIR *
01696 opendir_internal(WCHAR *wpath, const char *filename)
01697 {
01698     struct stati64 sbuf;
01699     WIN32_FIND_DATAW fd;
01700     HANDLE fh;
01701     DIR *p;
01702     long len;
01703     long idx;
01704     WCHAR *tmpW;
01705     char *tmp;
01706 
01707     //
01708     // check to see if we've got a directory
01709     //
01710     if (wstati64(wpath, &sbuf) < 0) {
01711         return NULL;
01712     }
01713     if (!(sbuf.st_mode & S_IFDIR) &&
01714         (!ISALPHA(filename[0]) || filename[1] != ':' || filename[2] != '\0' ||
01715          ((1 << ((filename[0] & 0x5f) - 'A')) & GetLogicalDrives()) == 0)) {
01716         errno = ENOTDIR;
01717         return NULL;
01718     }
01719     fh = open_dir_handle(wpath, &fd);
01720     if (fh == INVALID_HANDLE_VALUE) {
01721         return NULL;
01722     }
01723 
01724     //
01725     // Get us a DIR structure
01726     //
01727     p = calloc(sizeof(DIR), 1);
01728     if (p == NULL)
01729         return NULL;
01730 
01731     idx = 0;
01732 
01733     //
01734     // loop finding all the files that match the wildcard
01735     // (which should be all of them in this directory!).
01736     // the variable idx should point one past the null terminator
01737     // of the previous string found.
01738     //
01739     do {
01740         len = lstrlenW(fd.cFileName) + 1;
01741 
01742         //
01743         // bump the string table size by enough for the
01744         // new name and it's null terminator
01745         //
01746         tmpW = realloc(p->start, (idx + len) * sizeof(WCHAR));
01747         if (!tmpW) {
01748           error:
01749             rb_w32_closedir(p);
01750             FindClose(fh);
01751             errno = ENOMEM;
01752             return NULL;
01753         }
01754 
01755         p->start = tmpW;
01756         memcpy(&p->start[idx], fd.cFileName, len * sizeof(WCHAR));
01757 
01758         if (p->nfiles % DIRENT_PER_CHAR == 0) {
01759             tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
01760             if (!tmp)
01761                 goto error;
01762             p->bits = tmp;
01763             p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
01764         }
01765         if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
01766             SetBit(p->bits, BitOfIsDir(p->nfiles));
01767         if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
01768             SetBit(p->bits, BitOfIsRep(p->nfiles));
01769 
01770         p->nfiles++;
01771         idx += len;
01772     } while (FindNextFileW(fh, &fd));
01773     FindClose(fh);
01774     p->size = idx;
01775     p->curr = p->start;
01776     return p;
01777 }
01778 
01779 static inline UINT
01780 filecp(void)
01781 {
01782     UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
01783     return cp;
01784 }
01785 
01786 static char *
01787 wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
01788 {
01789     char *ptr;
01790     int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL) - 1;
01791     if (!(ptr = malloc(len + 1))) return 0;
01792     WideCharToMultiByte(cp, 0, wstr, clen, ptr, len + 1, NULL, NULL);
01793     if (plen) *plen = len;
01794     return ptr;
01795 }
01796 
01797 static WCHAR *
01798 mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
01799 {
01800     WCHAR *ptr;
01801     int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0) - 1;
01802     if (!(ptr = malloc(sizeof(WCHAR) * (len + 1)))) return 0;
01803     MultiByteToWideChar(cp, 0, str, clen, ptr, len + 1);
01804     if (plen) *plen = len;
01805     return ptr;
01806 }
01807 
01808 DIR *
01809 rb_w32_opendir(const char *filename)
01810 {
01811     DIR *ret;
01812     WCHAR *wpath = filecp_to_wstr(filename, NULL);
01813     if (!wpath)
01814         return NULL;
01815     ret = opendir_internal(wpath, filename);
01816     free(wpath);
01817     return ret;
01818 }
01819 
01820 DIR *
01821 rb_w32_uopendir(const char *filename)
01822 {
01823     DIR *ret;
01824     WCHAR *wpath = utf8_to_wstr(filename, NULL);
01825     if (!wpath)
01826         return NULL;
01827     ret = opendir_internal(wpath, filename);
01828     free(wpath);
01829     return ret;
01830 }
01831 
01832 //
01833 // Move to next entry
01834 //
01835 
01836 static void
01837 move_to_next_entry(DIR *dirp)
01838 {
01839     if (dirp->curr) {
01840         dirp->loc++;
01841         dirp->curr += lstrlenW(dirp->curr) + 1;
01842         if (dirp->curr >= (dirp->start + dirp->size)) {
01843             dirp->curr = NULL;
01844         }
01845     }
01846 }
01847 
01848 //
01849 // Readdir just returns the current string pointer and bumps the
01850 // string pointer to the next entry.
01851 //
01852 static BOOL
01853 win32_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *dummy)
01854 {
01855     if (!(entry->d_name = wstr_to_filecp(file, &entry->d_namlen)))
01856         return FALSE;
01857     return TRUE;
01858 }
01859 
01860 VALUE
01861 rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
01862 {
01863     static rb_encoding *utf16 = (rb_encoding *)-1;
01864     VALUE src;
01865 
01866     if (utf16 == (rb_encoding *)-1) {
01867         utf16 = rb_enc_find("UTF-16LE");
01868         if (utf16 == rb_ascii8bit_encoding())
01869             utf16 = NULL;
01870     }
01871     if (!utf16)
01872         /* maybe miniruby */
01873         return Qnil;
01874 
01875     src = rb_enc_str_new((char *)wstr, lstrlenW(wstr) * sizeof(WCHAR), utf16);
01876     return rb_str_encode(src, rb_enc_from_encoding(enc), ECONV_UNDEF_REPLACE, Qnil);
01877 }
01878 
01879 char *
01880 rb_w32_conv_from_wstr(const WCHAR *wstr, long *lenp, rb_encoding *enc)
01881 {
01882     VALUE str = rb_w32_conv_from_wchar(wstr, enc);
01883     long len;
01884     char *ptr;
01885 
01886     if (NIL_P(str)) return wstr_to_filecp(wstr, lenp);
01887     *lenp = len = RSTRING_LEN(str);
01888     memcpy(ptr = malloc(len + 1), RSTRING_PTR(str), len);
01889     ptr[len] = '\0';
01890     return ptr;
01891 }
01892 
01893 static BOOL
01894 ruby_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *enc)
01895 {
01896     if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
01897         return FALSE;
01898     return TRUE;
01899 }
01900 
01901 static struct direct *
01902 readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, struct direct *, rb_encoding *), rb_encoding *enc)
01903 {
01904     static int dummy = 0;
01905 
01906     if (dirp->curr) {
01907 
01908         //
01909         // first set up the structure to return
01910         //
01911         if (dirp->dirstr.d_name)
01912             free(dirp->dirstr.d_name);
01913         conv(dirp->curr, &dirp->dirstr, enc);
01914 
01915         //
01916         // Fake inode
01917         //
01918         dirp->dirstr.d_ino = dummy++;
01919 
01920         //
01921         // Attributes
01922         //
01923         dirp->dirstr.d_isdir = GetBit(dirp->bits, BitOfIsDir(dirp->loc));
01924         dirp->dirstr.d_isrep = GetBit(dirp->bits, BitOfIsRep(dirp->loc));
01925 
01926         //
01927         // Now set up for the next call to readdir
01928         //
01929 
01930         move_to_next_entry(dirp);
01931 
01932         return &(dirp->dirstr);
01933 
01934     }
01935     else
01936         return NULL;
01937 }
01938 
01939 struct direct  *
01940 rb_w32_readdir(DIR *dirp)
01941 {
01942     return readdir_internal(dirp, win32_direct_conv, NULL);
01943 }
01944 
01945 struct direct  *
01946 rb_w32_readdir_with_enc(DIR *dirp, rb_encoding *enc)
01947 {
01948     if (enc == rb_ascii8bit_encoding())
01949         return readdir_internal(dirp, win32_direct_conv, NULL);
01950     else
01951         return readdir_internal(dirp, ruby_direct_conv, enc);
01952 }
01953 
01954 //
01955 // Telldir returns the current string pointer position
01956 //
01957 
01958 long
01959 rb_w32_telldir(DIR *dirp)
01960 {
01961     return dirp->loc;
01962 }
01963 
01964 //
01965 // Seekdir moves the string pointer to a previously saved position
01966 // (Saved by telldir).
01967 
01968 void
01969 rb_w32_seekdir(DIR *dirp, long loc)
01970 {
01971     if (dirp->loc > loc) rb_w32_rewinddir(dirp);
01972 
01973     while (dirp->curr && dirp->loc < loc) {
01974         move_to_next_entry(dirp);
01975     }
01976 }
01977 
01978 //
01979 // Rewinddir resets the string pointer to the start
01980 //
01981 
01982 void
01983 rb_w32_rewinddir(DIR *dirp)
01984 {
01985     dirp->curr = dirp->start;
01986     dirp->loc = 0;
01987 }
01988 
01989 //
01990 // This just free's the memory allocated by opendir
01991 //
01992 
01993 void
01994 rb_w32_closedir(DIR *dirp)
01995 {
01996     if (dirp) {
01997         if (dirp->dirstr.d_name)
01998             free(dirp->dirstr.d_name);
01999         if (dirp->start)
02000             free(dirp->start);
02001         if (dirp->bits)
02002             free(dirp->bits);
02003         free(dirp);
02004     }
02005 }
02006 
02007 #if (defined _MT || defined __MSVCRT__) && !defined __BORLANDC__
02008 #define MSVCRT_THREADS
02009 #endif
02010 #ifdef MSVCRT_THREADS
02011 # define MTHREAD_ONLY(x) x
02012 # define STHREAD_ONLY(x)
02013 #elif defined(__BORLANDC__)
02014 # define MTHREAD_ONLY(x)
02015 # define STHREAD_ONLY(x)
02016 #else
02017 # define MTHREAD_ONLY(x)
02018 # define STHREAD_ONLY(x) x
02019 #endif
02020 
02021 typedef struct  {
02022     intptr_t osfhnd;    /* underlying OS file HANDLE */
02023     char osfile;        /* attributes of file (e.g., open in text mode?) */
02024     char pipech;        /* one char buffer for handles opened on pipes */
02025 #ifdef MSVCRT_THREADS
02026     int lockinitflag;
02027     CRITICAL_SECTION lock;
02028 #endif
02029 #if RT_VER >= 80
02030     char textmode;
02031     char pipech2[2];
02032 #endif
02033 }       ioinfo;
02034 
02035 #if !defined _CRTIMP || defined __MINGW32__
02036 #undef _CRTIMP
02037 #define _CRTIMP __declspec(dllimport)
02038 #endif
02039 
02040 #if !defined(__BORLANDC__)
02041 EXTERN_C _CRTIMP ioinfo * __pioinfo[];
02042 
02043 #define IOINFO_L2E                      5
02044 #define IOINFO_ARRAY_ELTS       (1 << IOINFO_L2E)
02045 #define _pioinfo(i)     ((ioinfo*)((char*)(__pioinfo[i >> IOINFO_L2E]) + (i & (IOINFO_ARRAY_ELTS - 1)) * (sizeof(ioinfo) + pioinfo_extra)))
02046 #define _osfhnd(i)  (_pioinfo(i)->osfhnd)
02047 #define _osfile(i)  (_pioinfo(i)->osfile)
02048 #define _pipech(i)  (_pioinfo(i)->pipech)
02049 
02050 #if RT_VER >= 80
02051 static size_t pioinfo_extra = 0;        /* workaround for VC++8 SP1 */
02052 
02053 static void
02054 set_pioinfo_extra(void)
02055 {
02056     int fd;
02057 
02058     fd = _open("NUL", O_RDONLY);
02059     for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra += sizeof(void *)) {
02060         if (_osfhnd(fd) == _get_osfhandle(fd)) {
02061             break;
02062         }
02063     }
02064     _close(fd);
02065 
02066     if (pioinfo_extra > 64) {
02067         /* not found, maybe something wrong... */
02068         pioinfo_extra = 0;
02069     }
02070 }
02071 #else
02072 #define pioinfo_extra 0
02073 #endif
02074 
02075 #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
02076 #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
02077 
02078 #define FOPEN                   0x01    /* file handle open */
02079 #define FEOFLAG                 0x02    /* end of file has been encountered */
02080 #define FPIPE                   0x08    /* file handle refers to a pipe */
02081 #define FNOINHERIT              0x10    /* file handle opened O_NOINHERIT */
02082 #define FAPPEND                 0x20    /* file handle opened O_APPEND */
02083 #define FDEV                    0x40    /* file handle refers to device */
02084 #define FTEXT                   0x80    /* file handle is in text mode */
02085 
02086 static int is_socket(SOCKET);
02087 static int is_console(SOCKET);
02088 
02089 int
02090 rb_w32_io_cancelable_p(int fd)
02091 {
02092     return cancel_io != NULL && (is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd)));
02093 }
02094 
02095 static int
02096 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
02097 {
02098     int fh;
02099     char fileflags;             /* _osfile flags */
02100     HANDLE hF;
02101 
02102     /* copy relevant flags from second parameter */
02103     fileflags = FDEV;
02104 
02105     if (flags & O_APPEND)
02106         fileflags |= FAPPEND;
02107 
02108     if (flags & O_TEXT)
02109         fileflags |= FTEXT;
02110 
02111     if (flags & O_NOINHERIT)
02112         fileflags |= FNOINHERIT;
02113 
02114     /* attempt to allocate a C Runtime file handle */
02115     hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
02116     fh = _open_osfhandle((intptr_t)hF, 0);
02117     CloseHandle(hF);
02118     if (fh == -1) {
02119         errno = EMFILE;         /* too many open files */
02120         _doserrno = 0L;         /* not an OS error */
02121     }
02122     else {
02123 
02124         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fh)->lock)));
02125         /* the file is open. now, set the info in _osfhnd array */
02126         _set_osfhnd(fh, osfhandle);
02127 
02128         fileflags |= FOPEN;             /* mark as open */
02129 
02130         _set_osflags(fh, fileflags); /* set osfile entry */
02131         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fh)->lock));
02132     }
02133     return fh;                  /* return handle */
02134 }
02135 
02136 static void
02137 init_stdhandle(void)
02138 {
02139     int nullfd = -1;
02140     int keep = 0;
02141 #define open_null(fd)                                           \
02142     (((nullfd < 0) ?                                            \
02143       (nullfd = open("NUL", O_RDWR)) : 0),              \
02144      ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)),        \
02145      (fd))
02146 
02147     if (fileno(stdin) < 0) {
02148         stdin->_file = open_null(0);
02149     }
02150     else {
02151         setmode(fileno(stdin), O_BINARY);
02152     }
02153     if (fileno(stdout) < 0) {
02154         stdout->_file = open_null(1);
02155     }
02156     if (fileno(stderr) < 0) {
02157         stderr->_file = open_null(2);
02158     }
02159     if (nullfd >= 0 && !keep) close(nullfd);
02160     setvbuf(stderr, NULL, _IONBF, 0);
02161 }
02162 #else
02163 
02164 #define _set_osfhnd(fh, osfh) (void)((fh), (osfh))
02165 #define _set_osflags(fh, flags) (void)((fh), (flags))
02166 
02167 static void
02168 init_stdhandle(void)
02169 {
02170 }
02171 #endif
02172 
02173 #ifdef __BORLANDC__
02174 static int
02175 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
02176 {
02177     int fd = _open_osfhandle(osfhandle, flags);
02178     if (fd == -1) {
02179         errno = EMFILE;         /* too many open files */
02180         _doserrno = 0L;         /* not an OS error */
02181     }
02182     return fd;
02183 }
02184 #endif
02185 
02186 #undef getsockopt
02187 
02188 static int
02189 is_socket(SOCKET sock)
02190 {
02191     if (st_lookup(socklist, (st_data_t)sock, NULL))
02192         return TRUE;
02193     else
02194         return FALSE;
02195 }
02196 
02197 int
02198 rb_w32_is_socket(int fd)
02199 {
02200     return is_socket(TO_SOCKET(fd));
02201 }
02202 
02203 //
02204 // Since the errors returned by the socket error function
02205 // WSAGetLastError() are not known by the library routine strerror
02206 // we have to roll our own.
02207 //
02208 
02209 #undef strerror
02210 
02211 char *
02212 rb_w32_strerror(int e)
02213 {
02214     static char buffer[512];
02215     DWORD source = 0;
02216     char *p;
02217 
02218 #if defined __BORLANDC__ && defined ENOTEMPTY // _sys_errlist is broken
02219     switch (e) {
02220       case ENAMETOOLONG:
02221         return "Filename too long";
02222       case ENOTEMPTY:
02223         return "Directory not empty";
02224     }
02225 #endif
02226 
02227     if (e < 0 || e > sys_nerr) {
02228         if (e < 0)
02229             e = GetLastError();
02230 #if WSAEWOULDBLOCK != EWOULDBLOCK
02231         else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
02232             static int s = -1;
02233             int i;
02234             if (s < 0)
02235                 for (s = 0; s < (int)(sizeof(errmap)/sizeof(*errmap)); s++)
02236                     if (errmap[s].winerr == WSAEWOULDBLOCK)
02237                         break;
02238             for (i = s; i < (int)(sizeof(errmap)/sizeof(*errmap)); i++)
02239                 if (errmap[i].err == e) {
02240                     e = errmap[i].winerr;
02241                     break;
02242                 }
02243         }
02244 #endif
02245         if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
02246                           FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
02247                           MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
02248                           buffer, sizeof(buffer), NULL) == 0 &&
02249             FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
02250                           FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
02251                           buffer, sizeof(buffer), NULL) == 0)
02252             strlcpy(buffer, "Unknown Error", sizeof(buffer));
02253     }
02254     else
02255         strlcpy(buffer, strerror(e), sizeof(buffer));
02256 
02257     p = buffer;
02258     while ((p = strpbrk(p, "\r\n")) != NULL) {
02259         memmove(p, p + 1, strlen(p));
02260     }
02261     return buffer;
02262 }
02263 
02264 //
02265 // various stubs
02266 //
02267 
02268 
02269 // Ownership
02270 //
02271 // Just pretend that everyone is a superuser. NT will let us know if
02272 // we don't really have permission to do something.
02273 //
02274 
02275 #define ROOT_UID        0
02276 #define ROOT_GID        0
02277 
02278 rb_uid_t
02279 getuid(void)
02280 {
02281         return ROOT_UID;
02282 }
02283 
02284 rb_uid_t
02285 geteuid(void)
02286 {
02287         return ROOT_UID;
02288 }
02289 
02290 rb_gid_t
02291 getgid(void)
02292 {
02293         return ROOT_GID;
02294 }
02295 
02296 rb_gid_t
02297 getegid(void)
02298 {
02299     return ROOT_GID;
02300 }
02301 
02302 int
02303 setuid(rb_uid_t uid)
02304 {
02305     return (uid == ROOT_UID ? 0 : -1);
02306 }
02307 
02308 int
02309 setgid(rb_gid_t gid)
02310 {
02311     return (gid == ROOT_GID ? 0 : -1);
02312 }
02313 
02314 //
02315 // File system stuff
02316 //
02317 
02318 int
02319 ioctl(int i, int u, ...)
02320 {
02321     errno = EINVAL;
02322     return -1;
02323 }
02324 
02325 void
02326 rb_w32_fdset(int fd, fd_set *set)
02327 {
02328     FD_SET(fd, set);
02329 }
02330 
02331 #undef FD_CLR
02332 
02333 void
02334 rb_w32_fdclr(int fd, fd_set *set)
02335 {
02336     unsigned int i;
02337     SOCKET s = TO_SOCKET(fd);
02338 
02339     for (i = 0; i < set->fd_count; i++) {
02340         if (set->fd_array[i] == s) {
02341             memmove(&set->fd_array[i], &set->fd_array[i+1],
02342                     sizeof(set->fd_array[0]) * (--set->fd_count - i));
02343             break;
02344         }
02345     }
02346 }
02347 
02348 #undef FD_ISSET
02349 
02350 int
02351 rb_w32_fdisset(int fd, fd_set *set)
02352 {
02353     int ret;
02354     SOCKET s = TO_SOCKET(fd);
02355     if (s == (SOCKET)INVALID_HANDLE_VALUE)
02356         return 0;
02357     RUBY_CRITICAL(ret = __WSAFDIsSet(s, set));
02358     return ret;
02359 }
02360 
02361 void
02362 rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
02363 {
02364     max = min(src->fd_count, (UINT)max);
02365     if ((UINT)dst->capa < (UINT)max) {
02366         dst->capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
02367         dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
02368     }
02369 
02370     memcpy(dst->fdset->fd_array, src->fd_array,
02371            max * sizeof(src->fd_array[0]));
02372     dst->fdset->fd_count = src->fd_count;
02373 }
02374 
02375 /* License: Ruby's */
02376 void
02377 rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
02378 {
02379     if ((UINT)dst->capa < src->fdset->fd_count) {
02380         dst->capa = (src->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
02381         dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
02382     }
02383 
02384     memcpy(dst->fdset->fd_array, src->fdset->fd_array,
02385            src->fdset->fd_count * sizeof(src->fdset->fd_array[0]));
02386     dst->fdset->fd_count = src->fdset->fd_count;
02387 }
02388 
02389 //
02390 // Networking trampolines
02391 // These are used to avoid socket startup/shutdown overhead in case
02392 // the socket routines aren't used.
02393 //
02394 
02395 #undef select
02396 
02397 static int
02398 extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
02399 {
02400     unsigned int s = 0;
02401     unsigned int m = 0;
02402     if (!src) return 0;
02403 
02404     while (s < src->fd_count) {
02405         SOCKET fd = src->fd_array[s];
02406 
02407         if (!func || (*func)(fd)) {
02408             if (dst) { /* move it to dst */
02409                 unsigned int d;
02410 
02411                 for (d = 0; d < dst->fdset->fd_count; d++) {
02412                     if (dst->fdset->fd_array[d] == fd)
02413                         break;
02414                 }
02415                 if (d == dst->fdset->fd_count) {
02416                     if ((int)dst->fdset->fd_count >= dst->capa) {
02417                         dst->capa = (dst->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
02418                         dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
02419                     }
02420                     dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
02421                 }
02422                 memmove(
02423                     &src->fd_array[s],
02424                     &src->fd_array[s+1],
02425                     sizeof(src->fd_array[0]) * (--src->fd_count - s));
02426             }
02427             else {
02428                 m++;
02429                 s++;
02430             }
02431         }
02432         else s++;
02433     }
02434 
02435     return dst ? dst->fdset->fd_count : m;
02436 }
02437 
02438 static int
02439 copy_fd(fd_set *dst, fd_set *src)
02440 {
02441     unsigned int s;
02442     if (!src || !dst) return 0;
02443 
02444     for (s = 0; s < src->fd_count; ++s) {
02445         SOCKET fd = src->fd_array[s];
02446         unsigned int d;
02447         for (d = 0; d < dst->fd_count; ++d) {
02448             if (dst->fd_array[d] == fd)
02449                 break;
02450         }
02451         if (d == dst->fd_count && d < FD_SETSIZE) {
02452             dst->fd_array[dst->fd_count++] = fd;
02453         }
02454     }
02455 
02456     return dst->fd_count;
02457 }
02458 
02459 static int
02460 is_not_socket(SOCKET sock)
02461 {
02462     return !is_socket(sock);
02463 }
02464 
02465 static int
02466 is_pipe(SOCKET sock) /* DONT call this for SOCKET! it clains it is PIPE. */
02467 {
02468     int ret;
02469 
02470     RUBY_CRITICAL({
02471         ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
02472     });
02473 
02474     return ret;
02475 }
02476 
02477 static int
02478 is_readable_pipe(SOCKET sock) /* call this for pipe only */
02479 {
02480     int ret;
02481     DWORD n = 0;
02482 
02483     RUBY_CRITICAL(
02484         if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
02485             ret = (n > 0);
02486         }
02487         else {
02488             ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
02489         }
02490     );
02491 
02492     return ret;
02493 }
02494 
02495 static int
02496 is_console(SOCKET sock) /* DONT call this for SOCKET! */
02497 {
02498     int ret;
02499     DWORD n = 0;
02500     INPUT_RECORD ir;
02501 
02502     RUBY_CRITICAL(
02503         ret = (PeekConsoleInput((HANDLE)sock, &ir, 1, &n))
02504     );
02505 
02506     return ret;
02507 }
02508 
02509 static int
02510 is_readable_console(SOCKET sock) /* call this for console only */
02511 {
02512     int ret = 0;
02513     DWORD n = 0;
02514     INPUT_RECORD ir;
02515 
02516     RUBY_CRITICAL(
02517         if (PeekConsoleInput((HANDLE)sock, &ir, 1, &n) && n > 0) {
02518             if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
02519                 ir.Event.KeyEvent.uChar.AsciiChar) {
02520                 ret = 1;
02521             }
02522             else {
02523                 ReadConsoleInput((HANDLE)sock, &ir, 1, &n);
02524             }
02525         }
02526     );
02527 
02528     return ret;
02529 }
02530 
02531 static int
02532 is_invalid_handle(SOCKET sock)
02533 {
02534     return (HANDLE)sock == INVALID_HANDLE_VALUE;
02535 }
02536 
02537 static int
02538 do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
02539             struct timeval *timeout)
02540 {
02541     int r = 0;
02542 
02543     if (nfds == 0) {
02544         if (timeout)
02545             rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
02546         else
02547             rb_w32_sleep(INFINITE);
02548     }
02549     else {
02550         RUBY_CRITICAL(
02551             EnterCriticalSection(&select_mutex);
02552             r = select(nfds, rd, wr, ex, timeout);
02553             LeaveCriticalSection(&select_mutex);
02554             if (r == SOCKET_ERROR) {
02555                 errno = map_errno(WSAGetLastError());
02556                 r = -1;
02557             }
02558         );
02559     }
02560 
02561     return r;
02562 }
02563 
02564 /*
02565  * rest -= wait
02566  * return 0 if rest is smaller than wait.
02567  */
02568 int
02569 rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
02570 {
02571     if (rest->tv_sec < wait->tv_sec) {
02572         return 0;
02573     }
02574     while (rest->tv_usec < wait->tv_usec) {
02575         if (rest->tv_sec <= wait->tv_sec) {
02576             return 0;
02577         }
02578         rest->tv_sec -= 1;
02579         rest->tv_usec += 1000 * 1000;
02580     }
02581     rest->tv_sec -= wait->tv_sec;
02582     rest->tv_usec -= wait->tv_usec;
02583     return rest->tv_sec != 0 || rest->tv_usec != 0;
02584 }
02585 
02586 static inline int
02587 compare(const struct timeval *t1, const struct timeval *t2)
02588 {
02589     if (t1->tv_sec < t2->tv_sec)
02590         return -1;
02591     if (t1->tv_sec > t2->tv_sec)
02592         return 1;
02593     if (t1->tv_usec < t2->tv_usec)
02594         return -1;
02595     if (t1->tv_usec > t2->tv_usec)
02596         return 1;
02597     return 0;
02598 }
02599 
02600 #undef Sleep
02601 
02602 int rb_w32_check_interrupt(void *);     /* @internal */
02603 
02604 /* @internal */
02605 int
02606 rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
02607                           struct timeval *timeout, void *th)
02608 {
02609     int r;
02610     rb_fdset_t pipe_rd;
02611     rb_fdset_t cons_rd;
02612     rb_fdset_t else_rd;
02613     rb_fdset_t else_wr;
02614     rb_fdset_t except;
02615     int nonsock = 0;
02616     struct timeval limit = {0, 0};
02617 
02618     if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
02619         errno = EINVAL;
02620         return -1;
02621     }
02622 
02623     if (timeout) {
02624         if (timeout->tv_sec < 0 ||
02625             timeout->tv_usec < 0 ||
02626             timeout->tv_usec >= 1000000) {
02627             errno = EINVAL;
02628             return -1;
02629         }
02630         gettimeofday(&limit, NULL);
02631         limit.tv_sec += timeout->tv_sec;
02632         limit.tv_usec += timeout->tv_usec;
02633         if (limit.tv_usec >= 1000000) {
02634             limit.tv_usec -= 1000000;
02635             limit.tv_sec++;
02636         }
02637     }
02638 
02639     if (!NtSocketsInitialized) {
02640         StartSockets();
02641     }
02642 
02643     // assume else_{rd,wr} (other than socket, pipe reader, console reader)
02644     // are always readable/writable. but this implementation still has
02645     // problem. if pipe's buffer is full, writing to pipe will block
02646     // until some data is read from pipe. but ruby is single threaded system,
02647     // so whole system will be blocked forever.
02648 
02649     rb_fd_init(&else_rd);
02650     nonsock += extract_fd(&else_rd, rd, is_not_socket);
02651 
02652     rb_fd_init(&else_wr);
02653     nonsock += extract_fd(&else_wr, wr, is_not_socket);
02654 
02655     // check invalid handles
02656     if (extract_fd(NULL, else_rd.fdset, is_invalid_handle) > 0 ||
02657         extract_fd(NULL, else_wr.fdset, is_invalid_handle) > 0) {
02658         rb_fd_term(&else_wr);
02659         rb_fd_term(&else_rd);
02660         errno = EBADF;
02661         return -1;
02662     }
02663 
02664     rb_fd_init(&pipe_rd);
02665     extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
02666 
02667     rb_fd_init(&cons_rd);
02668     extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
02669 
02670     rb_fd_init(&except);
02671     extract_fd(&except, ex, is_not_socket); // drop only
02672 
02673     r = 0;
02674     if (rd && (int)rd->fd_count > r) r = (int)rd->fd_count;
02675     if (wr && (int)wr->fd_count > r) r = (int)wr->fd_count;
02676     if (ex && (int)ex->fd_count > r) r = (int)ex->fd_count;
02677     if (nfds > r) nfds = r;
02678 
02679     {
02680         struct timeval rest;
02681         struct timeval wait;
02682         struct timeval zero;
02683         wait.tv_sec = 0; wait.tv_usec = 10 * 1000; // 10ms
02684         zero.tv_sec = 0; zero.tv_usec = 0;         //  0ms
02685         for (;;) {
02686             if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
02687                 r = -1;
02688                 break;
02689             }
02690             if (nonsock) {
02691                 // modifying {else,pipe,cons}_rd is safe because
02692                 // if they are modified, function returns immediately.
02693                 extract_fd(&else_rd, pipe_rd.fdset, is_readable_pipe);
02694                 extract_fd(&else_rd, cons_rd.fdset, is_readable_console);
02695             }
02696 
02697             if (else_rd.fdset->fd_count || else_wr.fdset->fd_count) {
02698                 r = do_select(nfds, rd, wr, ex, &zero); // polling
02699                 if (r < 0) break; // XXX: should I ignore error and return signaled handles?
02700                 r += copy_fd(rd, else_rd.fdset);
02701                 r += copy_fd(wr, else_wr.fdset);
02702                 if (ex)
02703                     r += ex->fd_count;
02704                 break;
02705             }
02706             else {
02707                 struct timeval *dowait = &wait;
02708 
02709                 fd_set orig_rd;
02710                 fd_set orig_wr;
02711                 fd_set orig_ex;
02712 
02713                 FD_ZERO(&orig_rd);
02714                 FD_ZERO(&orig_wr);
02715                 FD_ZERO(&orig_ex);
02716 
02717                 if (rd) copy_fd(&orig_rd, rd);
02718                 if (wr) copy_fd(&orig_wr, wr);
02719                 if (ex) copy_fd(&orig_ex, ex);
02720                 r = do_select(nfds, rd, wr, ex, &zero); // polling
02721                 if (r != 0) break; // signaled or error
02722                 if (rd) copy_fd(rd, &orig_rd);
02723                 if (wr) copy_fd(wr, &orig_wr);
02724                 if (ex) copy_fd(ex, &orig_ex);
02725 
02726                 if (timeout) {
02727                     struct timeval now;
02728                     gettimeofday(&now, NULL);
02729                     rest = limit;
02730                     if (!rb_w32_time_subtract(&rest, &now)) break;
02731                     if (compare(&rest, &wait) < 0) dowait = &rest;
02732                 }
02733                 Sleep(dowait->tv_sec * 1000 + dowait->tv_usec / 1000);
02734             }
02735         }
02736     }
02737 
02738     rb_fd_term(&except);
02739     rb_fd_term(&cons_rd);
02740     rb_fd_term(&pipe_rd);
02741     rb_fd_term(&else_wr);
02742     rb_fd_term(&else_rd);
02743 
02744     return r;
02745 }
02746 
02747 int WSAAPI
02748 rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
02749               struct timeval *timeout)
02750 {
02751     return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
02752 }
02753 
02754 static FARPROC
02755 get_wsa_extension_function(SOCKET s, GUID *guid)
02756 {
02757     DWORD dmy;
02758     FARPROC ptr = NULL;
02759 
02760     WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, guid, sizeof(*guid),
02761              &ptr, sizeof(ptr), &dmy, NULL, NULL);
02762     if (!ptr)
02763         errno = ENOSYS;
02764     return ptr;
02765 }
02766 
02767 #undef accept
02768 
02769 int WSAAPI
02770 rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
02771 {
02772     SOCKET r;
02773     int fd;
02774 
02775     if (!NtSocketsInitialized) {
02776         StartSockets();
02777     }
02778     RUBY_CRITICAL({
02779         HANDLE h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
02780         fd = rb_w32_open_osfhandle((intptr_t)h, O_RDWR|O_BINARY|O_NOINHERIT);
02781         if (fd != -1) {
02782             r = accept(TO_SOCKET(s), addr, addrlen);
02783             if (r != INVALID_SOCKET) {
02784                 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
02785                 _set_osfhnd(fd, r);
02786                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
02787                 CloseHandle(h);
02788                 st_insert(socklist, (st_data_t)r, (st_data_t)0);
02789             }
02790             else {
02791                 errno = map_errno(WSAGetLastError());
02792                 close(fd);
02793                 fd = -1;
02794             }
02795         }
02796         else
02797             CloseHandle(h);
02798     });
02799     return fd;
02800 }
02801 
02802 #undef bind
02803 
02804 int WSAAPI
02805 rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
02806 {
02807     int r;
02808 
02809     if (!NtSocketsInitialized) {
02810         StartSockets();
02811     }
02812     RUBY_CRITICAL({
02813         r = bind(TO_SOCKET(s), addr, addrlen);
02814         if (r == SOCKET_ERROR)
02815             errno = map_errno(WSAGetLastError());
02816     });
02817     return r;
02818 }
02819 
02820 #undef connect
02821 
02822 int WSAAPI
02823 rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
02824 {
02825     int r;
02826     if (!NtSocketsInitialized) {
02827         StartSockets();
02828     }
02829     RUBY_CRITICAL({
02830         r = connect(TO_SOCKET(s), addr, addrlen);
02831         if (r == SOCKET_ERROR) {
02832             int err = WSAGetLastError();
02833             if (err != WSAEWOULDBLOCK)
02834                 errno = map_errno(err);
02835             else
02836                 errno = EINPROGRESS;
02837         }
02838     });
02839     return r;
02840 }
02841 
02842 
02843 #undef getpeername
02844 
02845 int WSAAPI
02846 rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
02847 {
02848     int r;
02849     if (!NtSocketsInitialized) {
02850         StartSockets();
02851     }
02852     RUBY_CRITICAL({
02853         r = getpeername(TO_SOCKET(s), addr, addrlen);
02854         if (r == SOCKET_ERROR)
02855             errno = map_errno(WSAGetLastError());
02856     });
02857     return r;
02858 }
02859 
02860 #undef getsockname
02861 
02862 int WSAAPI
02863 rb_w32_getsockname(int s, struct sockaddr *addr, int *addrlen)
02864 {
02865     int r;
02866     if (!NtSocketsInitialized) {
02867         StartSockets();
02868     }
02869     RUBY_CRITICAL({
02870         r = getsockname(TO_SOCKET(s), addr, addrlen);
02871         if (r == SOCKET_ERROR)
02872             errno = map_errno(WSAGetLastError());
02873     });
02874     return r;
02875 }
02876 
02877 int WSAAPI
02878 rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
02879 {
02880     int r;
02881     if (!NtSocketsInitialized) {
02882         StartSockets();
02883     }
02884     RUBY_CRITICAL({
02885         r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
02886         if (r == SOCKET_ERROR)
02887             errno = map_errno(WSAGetLastError());
02888     });
02889     return r;
02890 }
02891 
02892 #undef ioctlsocket
02893 
02894 int WSAAPI
02895 rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
02896 {
02897     int r;
02898     if (!NtSocketsInitialized) {
02899         StartSockets();
02900     }
02901     RUBY_CRITICAL({
02902         r = ioctlsocket(TO_SOCKET(s), cmd, argp);
02903         if (r == SOCKET_ERROR)
02904             errno = map_errno(WSAGetLastError());
02905     });
02906     return r;
02907 }
02908 
02909 #undef listen
02910 
02911 int WSAAPI
02912 rb_w32_listen(int s, int backlog)
02913 {
02914     int r;
02915     if (!NtSocketsInitialized) {
02916         StartSockets();
02917     }
02918     RUBY_CRITICAL({
02919         r = listen(TO_SOCKET(s), backlog);
02920         if (r == SOCKET_ERROR)
02921             errno = map_errno(WSAGetLastError());
02922     });
02923     return r;
02924 }
02925 
02926 #undef recv
02927 #undef recvfrom
02928 #undef send
02929 #undef sendto
02930 
02931 static int
02932 finish_overlapped_socket(SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
02933 {
02934     DWORD flg;
02935     int err;
02936 
02937     if (result != SOCKET_ERROR)
02938         *len = size;
02939     else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
02940         switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
02941           case WAIT_OBJECT_0:
02942             RUBY_CRITICAL(
02943                 result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg)
02944                 );
02945             if (result) {
02946                 *len = size;
02947                 break;
02948             }
02949             /* thru */
02950           default:
02951             errno = map_errno(WSAGetLastError());
02952             /* thru */
02953           case WAIT_OBJECT_0 + 1:
02954             /* interrupted */
02955             *len = -1;
02956             cancel_io((HANDLE)s);
02957             break;
02958         }
02959     }
02960     else {
02961         errno = map_errno(err);
02962         *len = -1;
02963     }
02964     CloseHandle(wol->hEvent);
02965 
02966     return result;
02967 }
02968 
02969 static int
02970 overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
02971                      struct sockaddr *addr, int *addrlen)
02972 {
02973     int r;
02974     int ret;
02975     int mode;
02976     st_data_t data;
02977     DWORD flg;
02978     WSAOVERLAPPED wol;
02979     WSABUF wbuf;
02980     SOCKET s;
02981 
02982     if (!NtSocketsInitialized)
02983         StartSockets();
02984 
02985     s = TO_SOCKET(fd);
02986     st_lookup(socklist, (st_data_t)s, &data);
02987     mode = (int)data;
02988     if (!cancel_io || (mode & O_NONBLOCK)) {
02989         RUBY_CRITICAL({
02990             if (input) {
02991                 if (addr && addrlen)
02992                     r = recvfrom(s, buf, len, flags, addr, addrlen);
02993                 else
02994                     r = recv(s, buf, len, flags);
02995             }
02996             else {
02997                 if (addr && addrlen)
02998                     r = sendto(s, buf, len, flags, addr, *addrlen);
02999                 else
03000                     r = send(s, buf, len, flags);
03001             }
03002             if (r == SOCKET_ERROR)
03003                 errno = map_errno(WSAGetLastError());
03004         });
03005     }
03006     else {
03007         DWORD size;
03008         DWORD rlen;
03009         wbuf.len = len;
03010         wbuf.buf = buf;
03011         memset(&wol, 0, sizeof(wol));
03012         RUBY_CRITICAL({
03013             wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
03014             if (input) {
03015                 flg = flags;
03016                 if (addr && addrlen)
03017                     ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
03018                                       &wol, NULL);
03019                 else
03020                     ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
03021             }
03022             else {
03023                 if (addr && addrlen)
03024                     ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
03025                                     &wol, NULL);
03026                 else
03027                     ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
03028             }
03029         });
03030 
03031         finish_overlapped_socket(s, &wol, ret, &rlen, size);
03032         r = (int)rlen;
03033     }
03034 
03035     return r;
03036 }
03037 
03038 int WSAAPI
03039 rb_w32_recv(int fd, char *buf, int len, int flags)
03040 {
03041     return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
03042 }
03043 
03044 int WSAAPI
03045 rb_w32_recvfrom(int fd, char *buf, int len, int flags,
03046                 struct sockaddr *from, int *fromlen)
03047 {
03048     return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
03049 }
03050 
03051 int WSAAPI
03052 rb_w32_send(int fd, const char *buf, int len, int flags)
03053 {
03054     return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
03055 }
03056 
03057 int WSAAPI
03058 rb_w32_sendto(int fd, const char *buf, int len, int flags,
03059               const struct sockaddr *to, int tolen)
03060 {
03061     return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
03062                                 (struct sockaddr *)to, &tolen);
03063 }
03064 
03065 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
03066 typedef struct {
03067     SOCKADDR *name;
03068     int namelen;
03069     WSABUF *lpBuffers;
03070     DWORD dwBufferCount;
03071     WSABUF Control;
03072     DWORD dwFlags;
03073 } WSAMSG;
03074 #endif
03075 #ifndef WSAID_WSARECVMSG
03076 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
03077 #endif
03078 #ifndef WSAID_WSASENDMSG
03079 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
03080 #endif
03081 
03082 #define msghdr_to_wsamsg(msg, wsamsg) \
03083     do { \
03084         int i; \
03085         (wsamsg)->name = (msg)->msg_name; \
03086         (wsamsg)->namelen = (msg)->msg_namelen; \
03087         (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
03088         (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
03089         for (i = 0; i < (msg)->msg_iovlen; ++i) { \
03090             (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
03091             (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
03092         } \
03093         (wsamsg)->Control.buf = (msg)->msg_control; \
03094         (wsamsg)->Control.len = (msg)->msg_controllen; \
03095         (wsamsg)->dwFlags = (msg)->msg_flags; \
03096     } while (0)
03097 
03098 int
03099 recvmsg(int fd, struct msghdr *msg, int flags)
03100 {
03101     typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
03102     static WSARecvMsg_t pWSARecvMsg = NULL;
03103     WSAMSG wsamsg;
03104     SOCKET s;
03105     st_data_t data;
03106     int mode;
03107     DWORD len;
03108     int ret;
03109 
03110     if (!NtSocketsInitialized)
03111         StartSockets();
03112 
03113     s = TO_SOCKET(fd);
03114 
03115     if (!pWSARecvMsg) {
03116         static GUID guid = WSAID_WSARECVMSG;
03117         pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, &guid);
03118         if (!pWSARecvMsg)
03119             return -1;
03120     }
03121 
03122     msghdr_to_wsamsg(msg, &wsamsg);
03123     wsamsg.dwFlags |= flags;
03124 
03125     st_lookup(socklist, (st_data_t)s, &data);
03126     mode = (int)data;
03127     if (!cancel_io || (mode & O_NONBLOCK)) {
03128         RUBY_CRITICAL({
03129             if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
03130                 errno = map_errno(WSAGetLastError());
03131                 len = -1;
03132             }
03133         });
03134     }
03135     else {
03136         DWORD size;
03137         WSAOVERLAPPED wol;
03138         memset(&wol, 0, sizeof(wol));
03139         RUBY_CRITICAL({
03140             wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
03141             ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
03142         });
03143 
03144         ret = finish_overlapped_socket(s, &wol, ret, &len, size);
03145     }
03146     if (ret == SOCKET_ERROR)
03147         return -1;
03148 
03149     /* WSAMSG to msghdr */
03150     msg->msg_name = wsamsg.name;
03151     msg->msg_namelen = wsamsg.namelen;
03152     msg->msg_flags = wsamsg.dwFlags;
03153 
03154     return len;
03155 }
03156 
03157 int
03158 sendmsg(int fd, const struct msghdr *msg, int flags)
03159 {
03160     typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
03161     static WSASendMsg_t pWSASendMsg = NULL;
03162     WSAMSG wsamsg;
03163     SOCKET s;
03164     st_data_t data;
03165     int mode;
03166     DWORD len;
03167     int ret;
03168 
03169     if (!NtSocketsInitialized)
03170         StartSockets();
03171 
03172     s = TO_SOCKET(fd);
03173 
03174     if (!pWSASendMsg) {
03175         static GUID guid = WSAID_WSASENDMSG;
03176         pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, &guid);
03177         if (!pWSASendMsg)
03178             return -1;
03179     }
03180 
03181     msghdr_to_wsamsg(msg, &wsamsg);
03182 
03183     st_lookup(socklist, (st_data_t)s, &data);
03184     mode = (int)data;
03185     if (!cancel_io || (mode & O_NONBLOCK)) {
03186         RUBY_CRITICAL({
03187             if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
03188                 errno = map_errno(WSAGetLastError());
03189                 len = -1;
03190             }
03191         });
03192     }
03193     else {
03194         DWORD size;
03195         WSAOVERLAPPED wol;
03196         memset(&wol, 0, sizeof(wol));
03197         RUBY_CRITICAL({
03198             wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
03199             ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
03200         });
03201 
03202         finish_overlapped_socket(s, &wol, ret, &len, size);
03203     }
03204 
03205     return len;
03206 }
03207 
03208 #undef setsockopt
03209 
03210 int WSAAPI
03211 rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
03212 {
03213     int r;
03214     if (!NtSocketsInitialized) {
03215         StartSockets();
03216     }
03217     RUBY_CRITICAL({
03218         r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
03219         if (r == SOCKET_ERROR)
03220             errno = map_errno(WSAGetLastError());
03221     });
03222     return r;
03223 }
03224 
03225 #undef shutdown
03226 
03227 int WSAAPI
03228 rb_w32_shutdown(int s, int how)
03229 {
03230     int r;
03231     if (!NtSocketsInitialized) {
03232         StartSockets();
03233     }
03234     RUBY_CRITICAL({
03235         r = shutdown(TO_SOCKET(s), how);
03236         if (r == SOCKET_ERROR)
03237             errno = map_errno(WSAGetLastError());
03238     });
03239     return r;
03240 }
03241 
03242 static SOCKET
03243 open_ifs_socket(int af, int type, int protocol)
03244 {
03245     unsigned long proto_buffers_len = 0;
03246     int error_code;
03247     SOCKET out = INVALID_SOCKET;
03248 
03249     if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
03250         error_code = WSAGetLastError();
03251         if (error_code == WSAENOBUFS) {
03252             WSAPROTOCOL_INFO *proto_buffers;
03253             int protocols_available = 0;
03254 
03255             proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
03256             if (!proto_buffers) {
03257                 WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
03258                 return INVALID_SOCKET;
03259             }
03260 
03261             protocols_available =
03262                 WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
03263             if (protocols_available != SOCKET_ERROR) {
03264                 int i;
03265                 for (i = 0; i < protocols_available; i++) {
03266                     if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
03267                         (type != proto_buffers[i].iSocketType) ||
03268                         (protocol != 0 && protocol != proto_buffers[i].iProtocol))
03269                         continue;
03270 
03271                     if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
03272                         continue;
03273 
03274                     out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
03275                                     WSA_FLAG_OVERLAPPED);
03276                     break;
03277                 }
03278                 if (out == INVALID_SOCKET)
03279                     out = WSASocket(af, type, protocol, NULL, 0, 0);
03280             }
03281 
03282             free(proto_buffers);
03283         }
03284     }
03285 
03286     return out;
03287 }
03288 
03289 #undef socket
03290 
03291 int WSAAPI
03292 rb_w32_socket(int af, int type, int protocol)
03293 {
03294     SOCKET s;
03295     int fd;
03296 
03297     if (!NtSocketsInitialized) {
03298         StartSockets();
03299     }
03300     RUBY_CRITICAL({
03301         s = open_ifs_socket(af, type, protocol);
03302         if (s == INVALID_SOCKET) {
03303             errno = map_errno(WSAGetLastError());
03304             fd = -1;
03305         }
03306         else {
03307             fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
03308             if (fd != -1)
03309                 st_insert(socklist, (st_data_t)s, (st_data_t)0);
03310             else
03311                 closesocket(s);
03312         }
03313     });
03314     return fd;
03315 }
03316 
03317 #undef gethostbyaddr
03318 
03319 struct hostent * WSAAPI
03320 rb_w32_gethostbyaddr(const char *addr, int len, int type)
03321 {
03322     struct hostent *r;
03323     if (!NtSocketsInitialized) {
03324         StartSockets();
03325     }
03326     RUBY_CRITICAL({
03327         r = gethostbyaddr(addr, len, type);
03328         if (r == NULL)
03329             errno = map_errno(WSAGetLastError());
03330     });
03331     return r;
03332 }
03333 
03334 #undef gethostbyname
03335 
03336 struct hostent * WSAAPI
03337 rb_w32_gethostbyname(const char *name)
03338 {
03339     struct hostent *r;
03340     if (!NtSocketsInitialized) {
03341         StartSockets();
03342     }
03343     RUBY_CRITICAL({
03344         r = gethostbyname(name);
03345         if (r == NULL)
03346             errno = map_errno(WSAGetLastError());
03347     });
03348     return r;
03349 }
03350 
03351 #undef gethostname
03352 
03353 int WSAAPI
03354 rb_w32_gethostname(char *name, int len)
03355 {
03356     int r;
03357     if (!NtSocketsInitialized) {
03358         StartSockets();
03359     }
03360     RUBY_CRITICAL({
03361         r = gethostname(name, len);
03362         if (r == SOCKET_ERROR)
03363             errno = map_errno(WSAGetLastError());
03364     });
03365     return r;
03366 }
03367 
03368 #undef getprotobyname
03369 
03370 struct protoent * WSAAPI
03371 rb_w32_getprotobyname(const char *name)
03372 {
03373     struct protoent *r;
03374     if (!NtSocketsInitialized) {
03375         StartSockets();
03376     }
03377     RUBY_CRITICAL({
03378         r = getprotobyname(name);
03379         if (r == NULL)
03380             errno = map_errno(WSAGetLastError());
03381     });
03382     return r;
03383 }
03384 
03385 #undef getprotobynumber
03386 
03387 struct protoent * WSAAPI
03388 rb_w32_getprotobynumber(int num)
03389 {
03390     struct protoent *r;
03391     if (!NtSocketsInitialized) {
03392         StartSockets();
03393     }
03394     RUBY_CRITICAL({
03395         r = getprotobynumber(num);
03396         if (r == NULL)
03397             errno = map_errno(WSAGetLastError());
03398     });
03399     return r;
03400 }
03401 
03402 #undef getservbyname
03403 
03404 struct servent * WSAAPI
03405 rb_w32_getservbyname(const char *name, const char *proto)
03406 {
03407     struct servent *r;
03408     if (!NtSocketsInitialized) {
03409         StartSockets();
03410     }
03411     RUBY_CRITICAL({
03412         r = getservbyname(name, proto);
03413         if (r == NULL)
03414             errno = map_errno(WSAGetLastError());
03415     });
03416     return r;
03417 }
03418 
03419 #undef getservbyport
03420 
03421 struct servent * WSAAPI
03422 rb_w32_getservbyport(int port, const char *proto)
03423 {
03424     struct servent *r;
03425     if (!NtSocketsInitialized) {
03426         StartSockets();
03427     }
03428     RUBY_CRITICAL({
03429         r = getservbyport(port, proto);
03430         if (r == NULL)
03431             errno = map_errno(WSAGetLastError());
03432     });
03433     return r;
03434 }
03435 
03436 static int
03437 socketpair_internal(int af, int type, int protocol, SOCKET *sv)
03438 {
03439     SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
03440     struct sockaddr_in sock_in4;
03441 #ifdef INET6
03442     struct sockaddr_in6 sock_in6;
03443 #endif
03444     struct sockaddr *addr;
03445     int ret = -1;
03446     int len;
03447 
03448     if (!NtSocketsInitialized) {
03449         StartSockets();
03450     }
03451 
03452     switch (af) {
03453       case AF_INET:
03454 #if defined PF_INET && PF_INET != AF_INET
03455       case PF_INET:
03456 #endif
03457         sock_in4.sin_family = AF_INET;
03458         sock_in4.sin_port = 0;
03459         sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
03460         addr = (struct sockaddr *)&sock_in4;
03461         len = sizeof(sock_in4);
03462         break;
03463 #ifdef INET6
03464       case AF_INET6:
03465         memset(&sock_in6, 0, sizeof(sock_in6));
03466         sock_in6.sin6_family = AF_INET6;
03467         sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
03468         addr = (struct sockaddr *)&sock_in6;
03469         len = sizeof(sock_in6);
03470         break;
03471 #endif
03472       default:
03473         errno = EAFNOSUPPORT;
03474         return -1;
03475     }
03476     if (type != SOCK_STREAM) {
03477         errno = EPROTOTYPE;
03478         return -1;
03479     }
03480 
03481     sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
03482     sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
03483     RUBY_CRITICAL({
03484         do {
03485             svr = open_ifs_socket(af, type, protocol);
03486             if (svr == INVALID_SOCKET)
03487                 break;
03488             if (bind(svr, addr, len) < 0)
03489                 break;
03490             if (getsockname(svr, addr, &len) < 0)
03491                 break;
03492             if (type == SOCK_STREAM)
03493                 listen(svr, 5);
03494 
03495             w = open_ifs_socket(af, type, protocol);
03496             if (w == INVALID_SOCKET)
03497                 break;
03498             if (connect(w, addr, len) < 0)
03499                 break;
03500 
03501             r = accept(svr, addr, &len);
03502             if (r == INVALID_SOCKET)
03503                 break;
03504 
03505             ret = 0;
03506         } while (0);
03507 
03508         if (ret < 0) {
03509             errno = map_errno(WSAGetLastError());
03510             if (r != INVALID_SOCKET)
03511                 closesocket(r);
03512             if (w != INVALID_SOCKET)
03513                 closesocket(w);
03514         }
03515         else {
03516             sv[0] = r;
03517             sv[1] = w;
03518         }
03519         if (svr != INVALID_SOCKET)
03520             closesocket(svr);
03521     });
03522 
03523     return ret;
03524 }
03525 
03526 int
03527 rb_w32_socketpair(int af, int type, int protocol, int *sv)
03528 {
03529     SOCKET pair[2];
03530 
03531     if (socketpair_internal(af, type, protocol, pair) < 0)
03532         return -1;
03533     sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
03534     if (sv[0] == -1) {
03535         closesocket(pair[0]);
03536         closesocket(pair[1]);
03537         return -1;
03538     }
03539     sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
03540     if (sv[1] == -1) {
03541         rb_w32_close(sv[0]);
03542         closesocket(pair[1]);
03543         return -1;
03544     }
03545     st_insert(socklist, (st_data_t)pair[0], (st_data_t)0);
03546     st_insert(socklist, (st_data_t)pair[1], (st_data_t)0);
03547 
03548     return 0;
03549 }
03550 
03551 //
03552 // Networking stubs
03553 //
03554 
03555 void endhostent(void) {}
03556 void endnetent(void) {}
03557 void endprotoent(void) {}
03558 void endservent(void) {}
03559 
03560 struct netent *getnetent (void) {return (struct netent *) NULL;}
03561 
03562 struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
03563 
03564 struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
03565 
03566 struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
03567 
03568 struct servent *getservent (void) {return (struct servent *) NULL;}
03569 
03570 void sethostent (int stayopen) {}
03571 
03572 void setnetent (int stayopen) {}
03573 
03574 void setprotoent (int stayopen) {}
03575 
03576 void setservent (int stayopen) {}
03577 
03578 int
03579 fcntl(int fd, int cmd, ...)
03580 {
03581     SOCKET sock = TO_SOCKET(fd);
03582     va_list va;
03583     int arg;
03584     int ret;
03585     int flag = 0;
03586     st_data_t data;
03587     u_long ioctlArg;
03588 
03589     if (!is_socket(sock)) {
03590         errno = EBADF;
03591         return -1;
03592     }
03593     if (cmd != F_SETFL) {
03594         errno = EINVAL;
03595         return -1;
03596     }
03597 
03598     va_start(va, cmd);
03599     arg = va_arg(va, int);
03600     va_end(va);
03601     st_lookup(socklist, (st_data_t)sock, &data);
03602     flag = (int)data;
03603     if (arg & O_NONBLOCK) {
03604         flag |= O_NONBLOCK;
03605         ioctlArg = 1;
03606     }
03607     else {
03608         flag &= ~O_NONBLOCK;
03609         ioctlArg = 0;
03610     }
03611     RUBY_CRITICAL({
03612         ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
03613         if (ret == 0)
03614             st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
03615         else
03616             errno = map_errno(WSAGetLastError());
03617     });
03618 
03619     return ret;
03620 }
03621 
03622 #ifndef WNOHANG
03623 #define WNOHANG -1
03624 #endif
03625 
03626 static rb_pid_t
03627 poll_child_status(struct ChildRecord *child, int *stat_loc)
03628 {
03629     DWORD exitcode;
03630     DWORD err;
03631 
03632     if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
03633         /* If an error occured, return immediatly. */
03634     error_exit:
03635         err = GetLastError();
03636         if (err == ERROR_INVALID_PARAMETER)
03637             errno = ECHILD;
03638         else {
03639             if (GetLastError() == ERROR_INVALID_HANDLE)
03640                 errno = EINVAL;
03641             else
03642                 errno = map_errno(GetLastError());
03643         }
03644         CloseChildHandle(child);
03645         return -1;
03646     }
03647     if (exitcode != STILL_ACTIVE) {
03648         rb_pid_t pid;
03649         /* If already died, wait process's real termination. */
03650         if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
03651             goto error_exit;
03652         }
03653         pid = child->pid;
03654         CloseChildHandle(child);
03655         if (stat_loc) *stat_loc = exitcode << 8;
03656         return pid;
03657     }
03658     return 0;
03659 }
03660 
03661 rb_pid_t
03662 waitpid(rb_pid_t pid, int *stat_loc, int options)
03663 {
03664     DWORD timeout;
03665 
03666     if (options == WNOHANG) {
03667         timeout = 0;
03668     }
03669     else {
03670         timeout = INFINITE;
03671     }
03672 
03673     if (pid == -1) {
03674         int count = 0;
03675         int ret;
03676         HANDLE events[MAXCHILDNUM];
03677 
03678         FOREACH_CHILD(child) {
03679             if (!child->pid || child->pid < 0) continue;
03680             if ((pid = poll_child_status(child, stat_loc))) return pid;
03681             events[count++] = child->hProcess;
03682         } END_FOREACH_CHILD;
03683         if (!count) {
03684             errno = ECHILD;
03685             return -1;
03686         }
03687 
03688         ret = rb_w32_wait_events_blocking(events, count, timeout);
03689         if (ret == WAIT_TIMEOUT) return 0;
03690         if ((ret -= WAIT_OBJECT_0) == count) {
03691             return -1;
03692         }
03693         if (ret > count) {
03694             errno = map_errno(GetLastError());
03695             return -1;
03696         }
03697 
03698         return poll_child_status(FindChildSlotByHandle(events[ret]), stat_loc);
03699     }
03700     else {
03701         struct ChildRecord* child = FindChildSlot(pid);
03702         if (!child) {
03703             errno = ECHILD;
03704             return -1;
03705         }
03706 
03707         while (!(pid = poll_child_status(child, stat_loc))) {
03708             /* wait... */
03709             if (rb_w32_wait_events_blocking(&child->hProcess, 1, timeout) != WAIT_OBJECT_0) {
03710                 /* still active */
03711                 pid = 0;
03712                 break;
03713             }
03714         }
03715     }
03716 
03717     return pid;
03718 }
03719 
03720 #include <sys/timeb.h>
03721 
03722 static int
03723 filetime_to_timeval(const FILETIME* ft, struct timeval *tv)
03724 {
03725     ULARGE_INTEGER tmp;
03726     unsigned LONG_LONG lt;
03727 
03728     tmp.LowPart = ft->dwLowDateTime;
03729     tmp.HighPart = ft->dwHighDateTime;
03730     lt = tmp.QuadPart;
03731 
03732     /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
03733        convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
03734        the first leap second is at 1972/06/30, so we doesn't need to think
03735        about it. */
03736     lt /= 10;   /* to usec */
03737     lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * 1000 * 1000;
03738 
03739     tv->tv_sec = (long)(lt / (1000 * 1000));
03740     tv->tv_usec = (long)(lt % (1000 * 1000));
03741 
03742     return tv->tv_sec > 0 ? 0 : -1;
03743 }
03744 
03745 int _cdecl
03746 gettimeofday(struct timeval *tv, struct timezone *tz)
03747 {
03748     FILETIME ft;
03749 
03750     GetSystemTimeAsFileTime(&ft);
03751     filetime_to_timeval(&ft, tv);
03752 
03753     return 0;
03754 }
03755 
03756 char *
03757 rb_w32_getcwd(char *buffer, int size)
03758 {
03759     char *p = buffer;
03760     int len;
03761 
03762     len = GetCurrentDirectory(0, NULL);
03763     if (!len) {
03764         errno = map_errno(GetLastError());
03765         return NULL;
03766     }
03767 
03768     if (p) {
03769         if (size < len) {
03770             errno = ERANGE;
03771             return NULL;
03772         }
03773     }
03774     else {
03775         p = malloc(len);
03776         size = len;
03777         if (!p) {
03778             errno = ENOMEM;
03779             return NULL;
03780         }
03781     }
03782 
03783     if (!GetCurrentDirectory(size, p)) {
03784         errno = map_errno(GetLastError());
03785         if (!buffer)
03786             free(p);
03787         return NULL;
03788     }
03789 
03790     translate_char(p, '\\', '/');
03791 
03792     return p;
03793 }
03794 
03795 int
03796 chown(const char *path, int owner, int group)
03797 {
03798     return 0;
03799 }
03800 
03801 int
03802 rb_w32_uchown(const char *path, int owner, int group)
03803 {
03804     return 0;
03805 }
03806 
03807 int
03808 kill(int pid, int sig)
03809 {
03810     int ret = 0;
03811     DWORD err;
03812 
03813     if (pid < 0 || pid == 0 && sig != SIGINT) {
03814         errno = EINVAL;
03815         return -1;
03816     }
03817 
03818     (void)IfWin95(pid = -pid, 0);
03819     if ((unsigned int)pid == GetCurrentProcessId() &&
03820         (sig != 0 && sig != SIGKILL)) {
03821         if ((ret = raise(sig)) != 0) {
03822             /* MSVCRT doesn't set errno... */
03823             errno = EINVAL;
03824         }
03825         return ret;
03826     }
03827 
03828     switch (sig) {
03829       case 0:
03830         RUBY_CRITICAL({
03831             HANDLE hProc =
03832                 OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
03833             if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
03834                 if (GetLastError() == ERROR_INVALID_PARAMETER) {
03835                     errno = ESRCH;
03836                 }
03837                 else {
03838                     errno = EPERM;
03839                 }
03840                 ret = -1;
03841             }
03842             else {
03843                 CloseHandle(hProc);
03844             }
03845         });
03846         break;
03847 
03848       case SIGINT:
03849         RUBY_CRITICAL({
03850             DWORD ctrlEvent = CTRL_C_EVENT;
03851             if (pid != 0) {
03852                 /* CTRL+C signal cannot be generated for process groups.
03853                  * Instead, we use CTRL+BREAK signal. */
03854                 ctrlEvent = CTRL_BREAK_EVENT;
03855             }
03856             if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
03857                 if ((err = GetLastError()) == 0)
03858                     errno = EPERM;
03859                 else
03860                     errno = map_errno(GetLastError());
03861                 ret = -1;
03862             }
03863         });
03864         break;
03865 
03866       case SIGKILL:
03867         RUBY_CRITICAL({
03868             HANDLE hProc;
03869             struct ChildRecord* child = FindChildSlot(pid);
03870             if (child) {
03871                 hProc = child->hProcess;
03872             }
03873             else {
03874                 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
03875             }
03876             if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
03877                 if (GetLastError() == ERROR_INVALID_PARAMETER) {
03878                     errno = ESRCH;
03879                 }
03880                 else {
03881                     errno = EPERM;
03882                 }
03883                 ret = -1;
03884             }
03885             else {
03886                 DWORD status;
03887                 if (!GetExitCodeProcess(hProc, &status)) {
03888                     errno = map_errno(GetLastError());
03889                     ret = -1;
03890                 }
03891                 else if (status == STILL_ACTIVE) {
03892                     if (!TerminateProcess(hProc, 0)) {
03893                         errno = EPERM;
03894                         ret = -1;
03895                     }
03896                 }
03897                 else {
03898                     errno = ESRCH;
03899                     ret = -1;
03900                 }
03901                 if (!child) {
03902                     CloseHandle(hProc);
03903                 }
03904             }
03905         });
03906         break;
03907 
03908       default:
03909         errno = EINVAL;
03910         ret = -1;
03911         break;
03912     }
03913 
03914     return ret;
03915 }
03916 
03917 static int
03918 wlink(const WCHAR *from, const WCHAR *to)
03919 {
03920     typedef BOOL (WINAPI link_func)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
03921     static link_func *pCreateHardLinkW = NULL;
03922     static int myerrno = 0;
03923 
03924     if (!pCreateHardLinkW && !myerrno) {
03925         pCreateHardLinkW = (link_func *)get_proc_address("kernel32", "CreateHardLinkW", NULL);
03926         if (!pCreateHardLinkW)
03927             myerrno = ENOSYS;
03928     }
03929     if (!pCreateHardLinkW) {
03930         errno = myerrno;
03931         return -1;
03932     }
03933 
03934     if (!pCreateHardLinkW(to, from, NULL)) {
03935         errno = map_errno(GetLastError());
03936         return -1;
03937     }
03938 
03939     return 0;
03940 }
03941 
03942 int
03943 rb_w32_ulink(const char *from, const char *to)
03944 {
03945     WCHAR *wfrom;
03946     WCHAR *wto;
03947     int ret;
03948 
03949     if (!(wfrom = utf8_to_wstr(from, NULL)))
03950         return -1;
03951     if (!(wto = utf8_to_wstr(to, NULL))) {
03952         free(wfrom);
03953         return -1;
03954     }
03955     ret = wlink(wfrom, wto);
03956     free(wto);
03957     free(wfrom);
03958     return ret;
03959 }
03960 
03961 int
03962 link(const char *from, const char *to)
03963 {
03964     WCHAR *wfrom;
03965     WCHAR *wto;
03966     int ret;
03967 
03968     if (!(wfrom = filecp_to_wstr(from, NULL)))
03969         return -1;
03970     if (!(wto = filecp_to_wstr(to, NULL))) {
03971         free(wfrom);
03972         return -1;
03973     }
03974     ret = wlink(wfrom, wto);
03975     free(wto);
03976     free(wfrom);
03977     return ret;
03978 }
03979 
03980 int
03981 wait(int *status)
03982 {
03983     return waitpid(-1, status, 0);
03984 }
03985 
03986 char *
03987 rb_w32_getenv(const char *name)
03988 {
03989     int len = strlen(name);
03990     char *env;
03991 
03992     if (len == 0) return NULL;
03993     if (envarea) FreeEnvironmentStrings(envarea);
03994     envarea = GetEnvironmentStrings();
03995     if (!envarea) {
03996         map_errno(GetLastError());
03997         return NULL;
03998     }
03999 
04000     for (env = envarea; *env; env += strlen(env) + 1)
04001         if (strncasecmp(env, name, len) == 0 && *(env + len) == '=')
04002             return env + len + 1;
04003 
04004     return NULL;
04005 }
04006 
04007 static int
04008 wrename(const WCHAR *oldpath, const WCHAR *newpath)
04009 {
04010     int res = 0;
04011     int oldatts;
04012     int newatts;
04013 
04014     oldatts = GetFileAttributesW(oldpath);
04015     newatts = GetFileAttributesW(newpath);
04016 
04017     if (oldatts == -1) {
04018         errno = map_errno(GetLastError());
04019         return -1;
04020     }
04021 
04022     RUBY_CRITICAL({
04023         if (newatts != -1 && newatts & FILE_ATTRIBUTE_READONLY)
04024             SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
04025 
04026         if (!MoveFileW(oldpath, newpath))
04027             res = -1;
04028 
04029         if (res) {
04030             switch (GetLastError()) {
04031               case ERROR_ALREADY_EXISTS:
04032               case ERROR_FILE_EXISTS:
04033                 if (IsWinNT()) {
04034                     if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
04035                         res = 0;
04036                 }
04037                 else {
04038                     for (;;) {
04039                         if (!DeleteFileW(newpath) && GetLastError() != ERROR_FILE_NOT_FOUND)
04040                             break;
04041                         else if (MoveFileW(oldpath, newpath)) {
04042                             res = 0;
04043                             break;
04044                         }
04045                     }
04046                 }
04047             }
04048         }
04049 
04050         if (res)
04051             errno = map_errno(GetLastError());
04052         else
04053             SetFileAttributesW(newpath, oldatts);
04054     });
04055 
04056     return res;
04057 }
04058 
04059 int rb_w32_urename(const char *from, const char *to)
04060 {
04061     WCHAR *wfrom;
04062     WCHAR *wto;
04063     int ret = -1;
04064 
04065     if (!(wfrom = utf8_to_wstr(from, NULL)))
04066         return -1;
04067     if (!(wto = utf8_to_wstr(to, NULL))) {
04068         free(wfrom);
04069         return -1;
04070     }
04071     ret = wrename(wfrom, wto);
04072     free(wto);
04073     free(wfrom);
04074     return ret;
04075 }
04076 
04077 int rb_w32_rename(const char *from, const char *to)
04078 {
04079     WCHAR *wfrom;
04080     WCHAR *wto;
04081     int ret = -1;
04082 
04083     if (!(wfrom = filecp_to_wstr(from, NULL)))
04084         return -1;
04085     if (!(wto = filecp_to_wstr(to, NULL))) {
04086         free(wfrom);
04087         return -1;
04088     }
04089     ret = wrename(wfrom, wto);
04090     free(wto);
04091     free(wfrom);
04092     return ret;
04093 }
04094 
04095 static int
04096 isUNCRoot(const WCHAR *path)
04097 {
04098     if (path[0] == L'\\' && path[1] == L'\\') {
04099         const WCHAR *p;
04100         for (p = path + 2; *p; p++) {
04101             if (*p == L'\\')
04102                 break;
04103         }
04104         if (p[0] && p[1]) {
04105             for (p++; *p; p++) {
04106                 if (*p == L'\\')
04107                     break;
04108             }
04109             if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
04110                 return 1;
04111         }
04112     }
04113     return 0;
04114 }
04115 
04116 #define COPY_STAT(src, dest, size_cast) do {    \
04117         (dest).st_dev   = (src).st_dev;         \
04118         (dest).st_ino   = (src).st_ino;         \
04119         (dest).st_mode  = (src).st_mode;        \
04120         (dest).st_nlink = (src).st_nlink;       \
04121         (dest).st_uid   = (src).st_uid;         \
04122         (dest).st_gid   = (src).st_gid;         \
04123         (dest).st_rdev  = (src).st_rdev;        \
04124         (dest).st_size  = size_cast(src).st_size; \
04125         (dest).st_atime = (src).st_atime;       \
04126         (dest).st_mtime = (src).st_mtime;       \
04127         (dest).st_ctime = (src).st_ctime;       \
04128     } while (0)
04129 
04130 static time_t filetime_to_unixtime(const FILETIME *ft);
04131 
04132 #undef fstat
04133 int
04134 rb_w32_fstat(int fd, struct stat *st)
04135 {
04136     BY_HANDLE_FILE_INFORMATION info;
04137     int ret = fstat(fd, st);
04138 
04139     if (ret) return ret;
04140 #ifdef __BORLANDC__
04141     st->st_mode &= ~(S_IWGRP | S_IWOTH);
04142 #endif
04143     if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
04144 #ifdef __BORLANDC__
04145         if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
04146             st->st_mode |= S_IWUSR;
04147         }
04148 #endif
04149         st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
04150         st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
04151         st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
04152     }
04153     return ret;
04154 }
04155 
04156 int
04157 rb_w32_fstati64(int fd, struct stati64 *st)
04158 {
04159     BY_HANDLE_FILE_INFORMATION info;
04160     struct stat tmp;
04161     int ret = fstat(fd, &tmp);
04162 
04163     if (ret) return ret;
04164 #ifdef __BORLANDC__
04165     tmp.st_mode &= ~(S_IWGRP | S_IWOTH);
04166 #endif
04167     COPY_STAT(tmp, *st, +);
04168     if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
04169 #ifdef __BORLANDC__
04170         if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
04171             st->st_mode |= S_IWUSR;
04172         }
04173 #endif
04174         st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
04175         st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
04176         st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
04177         st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
04178     }
04179     return ret;
04180 }
04181 
04182 static time_t
04183 filetime_to_unixtime(const FILETIME *ft)
04184 {
04185     struct timeval tv;
04186 
04187     if (filetime_to_timeval(ft, &tv) == (time_t)-1)
04188         return 0;
04189     else
04190         return tv.tv_sec;
04191 }
04192 
04193 static unsigned
04194 fileattr_to_unixmode(DWORD attr, const WCHAR *path)
04195 {
04196     unsigned mode = 0;
04197 
04198     if (attr & FILE_ATTRIBUTE_READONLY) {
04199         mode |= S_IREAD;
04200     }
04201     else {
04202         mode |= S_IREAD | S_IWRITE | S_IWUSR;
04203     }
04204 
04205     if (attr & FILE_ATTRIBUTE_DIRECTORY) {
04206         mode |= S_IFDIR | S_IEXEC;
04207     }
04208     else {
04209         mode |= S_IFREG;
04210     }
04211 
04212     if (path && (mode & S_IFREG)) {
04213         const WCHAR *end = path + lstrlenW(path);
04214         while (path < end) {
04215             end = CharPrevW(path, end);
04216             if (*end == L'.') {
04217                 if ((_wcsicmp(end, L".bat") == 0) ||
04218                     (_wcsicmp(end, L".cmd") == 0) ||
04219                     (_wcsicmp(end, L".com") == 0) ||
04220                     (_wcsicmp(end, L".exe") == 0)) {
04221                     mode |= S_IEXEC;
04222                 }
04223                 break;
04224             }
04225         }
04226     }
04227 
04228     mode |= (mode & 0700) >> 3;
04229     mode |= (mode & 0700) >> 6;
04230 
04231     return mode;
04232 }
04233 
04234 static int
04235 check_valid_dir(const WCHAR *path)
04236 {
04237     WIN32_FIND_DATAW fd;
04238     HANDLE fh;
04239 
04240     /* GetFileAttributes() determines "..." as directory. */
04241     /* We recheck it by FindFirstFile(). */
04242     if (wcsstr(path, L"...") == NULL)
04243         return 0;
04244 
04245     fh = open_dir_handle(path, &fd);
04246     if (fh == INVALID_HANDLE_VALUE)
04247         return -1;
04248     FindClose(fh);
04249     return 0;
04250 }
04251 
04252 static int
04253 winnt_stat(const WCHAR *path, struct stati64 *st)
04254 {
04255     HANDLE h;
04256     WIN32_FIND_DATAW wfd;
04257     WIN32_FILE_ATTRIBUTE_DATA wfa;
04258 
04259     memset(st, 0, sizeof(*st));
04260     st->st_nlink = 1;
04261 
04262     if (wcspbrk(path, L"?*")) {
04263         errno = ENOENT;
04264         return -1;
04265     }
04266     if (GetFileAttributesExW(path, GetFileExInfoStandard, (void*)&wfa)) {
04267         if (wfa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
04268             if (check_valid_dir(path)) return -1;
04269             st->st_size = 0;
04270         }
04271         else {
04272             st->st_size = ((__int64)wfa.nFileSizeHigh << 32) | wfa.nFileSizeLow;
04273         }
04274         st->st_mode  = fileattr_to_unixmode(wfa.dwFileAttributes, path);
04275         st->st_atime = filetime_to_unixtime(&wfa.ftLastAccessTime);
04276         st->st_mtime = filetime_to_unixtime(&wfa.ftLastWriteTime);
04277         st->st_ctime = filetime_to_unixtime(&wfa.ftCreationTime);
04278     }
04279     else {
04280         /* GetFileAttributesEx failed; check why. */
04281         int e = GetLastError();
04282 
04283         if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME)
04284             || (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) {
04285             errno = map_errno(e);
04286             return -1;
04287         }
04288 
04289         /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
04290         h = FindFirstFileW(path, &wfd);
04291         if (h != INVALID_HANDLE_VALUE) {
04292             FindClose(h);
04293             st->st_mode  = fileattr_to_unixmode(wfd.dwFileAttributes, path);
04294             st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
04295             st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
04296             st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
04297             st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
04298         }
04299         else {
04300             errno = map_errno(GetLastError());
04301             return -1;
04302         }
04303     }
04304 
04305     st->st_dev = st->st_rdev = (iswalpha(path[0]) && path[1] == L':') ?
04306         towupper(path[0]) - L'A' : _getdrive() - 1;
04307 
04308     return 0;
04309 }
04310 
04311 #ifdef WIN95
04312 static int
04313 win95_stat(const WCHAR *path, struct stati64 *st)
04314 {
04315     int ret = _wstati64(path, st);
04316     if (ret) return ret;
04317     if (st->st_mode & S_IFDIR) {
04318         return check_valid_dir(path);
04319     }
04320     return 0;
04321 }
04322 #else
04323 #define win95_stat(path, st) -1
04324 #endif
04325 
04326 int
04327 rb_w32_stat(const char *path, struct stat *st)
04328 {
04329     struct stati64 tmp;
04330 
04331     if (rb_w32_stati64(path, &tmp)) return -1;
04332     COPY_STAT(tmp, *st, (_off_t));
04333     return 0;
04334 }
04335 
04336 static int
04337 wstati64(const WCHAR *path, struct stati64 *st)
04338 {
04339     const WCHAR *p;
04340     WCHAR *buf1, *s, *end;
04341     int len, size;
04342     int ret;
04343     VALUE v;
04344 
04345     if (!path || !st) {
04346         errno = EFAULT;
04347         return -1;
04348     }
04349     size = lstrlenW(path) + 2;
04350     buf1 = ALLOCV_N(WCHAR, v, size);
04351     for (p = path, s = buf1; *p; p++, s++) {
04352         if (*p == L'/')
04353             *s = L'\\';
04354         else
04355             *s = *p;
04356     }
04357     *s = '\0';
04358     len = s - buf1;
04359     if (!len || L'\"' == *(--s)) {
04360         errno = ENOENT;
04361         return -1;
04362     }
04363     end = buf1 + len - 1;
04364 
04365     if (isUNCRoot(buf1)) {
04366         if (*end == L'.')
04367             *end = L'\0';
04368         else if (*end != L'\\')
04369             lstrcatW(buf1, L"\\");
04370     }
04371     else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
04372         lstrcatW(buf1, L".");
04373 
04374     ret = IsWinNT() ? winnt_stat(buf1, st) : win95_stat(buf1, st);
04375     if (ret == 0) {
04376         st->st_mode &= ~(S_IWGRP | S_IWOTH);
04377     }
04378     if (v)
04379         ALLOCV_END(v);
04380 
04381     return ret;
04382 }
04383 
04384 int
04385 rb_w32_ustati64(const char *path, struct stati64 *st)
04386 {
04387     WCHAR *wpath;
04388     int ret;
04389 
04390     if (!(wpath = utf8_to_wstr(path, NULL)))
04391         return -1;
04392     ret = wstati64(wpath, st);
04393     free(wpath);
04394     return ret;
04395 }
04396 
04397 int
04398 rb_w32_stati64(const char *path, struct stati64 *st)
04399 {
04400     WCHAR *wpath;
04401     int ret;
04402 
04403     if (!(wpath = filecp_to_wstr(path, NULL)))
04404         return -1;
04405     ret = wstati64(wpath, st);
04406     free(wpath);
04407     return ret;
04408 }
04409 
04410 int
04411 rb_w32_access(const char *path, int mode)
04412 {
04413     struct stati64 stat;
04414     if (rb_w32_stati64(path, &stat) != 0)
04415         return -1;
04416     mode <<= 6;
04417     if ((stat.st_mode & mode) != mode) {
04418         errno = EACCES;
04419         return -1;
04420     }
04421     return 0;
04422 }
04423 
04424 int
04425 rb_w32_uaccess(const char *path, int mode)
04426 {
04427     struct stati64 stat;
04428     if (rb_w32_ustati64(path, &stat) != 0)
04429         return -1;
04430     mode <<= 6;
04431     if ((stat.st_mode & mode) != mode) {
04432         errno = EACCES;
04433         return -1;
04434     }
04435     return 0;
04436 }
04437 
04438 static int
04439 rb_chsize(HANDLE h, off_t size)
04440 {
04441     long upos, lpos, usize, lsize, uend, lend;
04442     off_t end;
04443     int ret = -1;
04444     DWORD e;
04445 
04446     if (((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
04447          (e = GetLastError())) ||
04448         ((lend = GetFileSize(h, (DWORD *)&uend)) == -1L && (e = GetLastError()))) {
04449         errno = map_errno(e);
04450         return -1;
04451     }
04452     end = ((off_t)uend << 32) | (unsigned long)lend;
04453     usize = (long)(size >> 32);
04454     lsize = (long)size;
04455     if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
04456         (e = GetLastError())) {
04457         errno = map_errno(e);
04458     }
04459     else if (!SetEndOfFile(h)) {
04460         errno = map_errno(GetLastError());
04461     }
04462     else {
04463         ret = 0;
04464     }
04465     SetFilePointer(h, lpos, &upos, SEEK_SET);
04466     return ret;
04467 }
04468 
04469 int
04470 rb_w32_truncate(const char *path, off_t length)
04471 {
04472     HANDLE h;
04473     int ret;
04474 #ifdef WIN95
04475     if (IsWin95()) {
04476         int fd = open(path, O_WRONLY), e = 0;
04477         if (fd == -1) return -1;
04478         ret = chsize(fd, (unsigned long)length);
04479         if (ret == -1) e = errno;
04480         close(fd);
04481         if (ret == -1) errno = e;
04482         return ret;
04483     }
04484 #endif
04485     h = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
04486     if (h == INVALID_HANDLE_VALUE) {
04487         errno = map_errno(GetLastError());
04488         return -1;
04489     }
04490     ret = rb_chsize(h, length);
04491     CloseHandle(h);
04492     return ret;
04493 }
04494 
04495 int
04496 rb_w32_ftruncate(int fd, off_t length)
04497 {
04498     HANDLE h;
04499 
04500 #ifdef WIN95
04501     if (IsWin95()) {
04502         return chsize(fd, (unsigned long)length);
04503     }
04504 #endif
04505     h = (HANDLE)_get_osfhandle(fd);
04506     if (h == (HANDLE)-1) return -1;
04507     return rb_chsize(h, length);
04508 }
04509 
04510 #ifdef __BORLANDC__
04511 off_t
04512 _filelengthi64(int fd)
04513 {
04514     DWORD u, l;
04515     int e;
04516 
04517     l = GetFileSize((HANDLE)_get_osfhandle(fd), &u);
04518     if (l == (DWORD)-1L && (e = GetLastError())) {
04519         errno = map_errno(e);
04520         return (off_t)-1;
04521     }
04522     return ((off_t)u << 32) | l;
04523 }
04524 
04525 off_t
04526 _lseeki64(int fd, off_t offset, int whence)
04527 {
04528     long u, l;
04529     int e;
04530     HANDLE h = (HANDLE)_get_osfhandle(fd);
04531 
04532     if (!h) {
04533         errno = EBADF;
04534         return -1;
04535     }
04536     u = (long)(offset >> 32);
04537     if ((l = SetFilePointer(h, (long)offset, &u, whence)) == -1L &&
04538         (e = GetLastError())) {
04539         errno = map_errno(e);
04540         return -1;
04541     }
04542     return ((off_t)u << 32) | l;
04543 }
04544 #endif
04545 
04546 int
04547 fseeko(FILE *stream, off_t offset, int whence)
04548 {
04549     off_t pos;
04550     switch (whence) {
04551       case SEEK_CUR:
04552         if (fgetpos(stream, (fpos_t *)&pos))
04553             return -1;
04554         pos += offset;
04555         break;
04556       case SEEK_END:
04557         if ((pos = _filelengthi64(fileno(stream))) == (off_t)-1)
04558             return -1;
04559         pos += offset;
04560         break;
04561       default:
04562         pos = offset;
04563         break;
04564     }
04565     return fsetpos(stream, (fpos_t *)&pos);
04566 }
04567 
04568 off_t
04569 rb_w32_ftello(FILE *stream)
04570 {
04571     off_t pos;
04572     if (fgetpos(stream, (fpos_t *)&pos)) return (off_t)-1;
04573     return pos;
04574 }
04575 
04576 static long
04577 filetime_to_clock(FILETIME *ft)
04578 {
04579     __int64 qw = ft->dwHighDateTime;
04580     qw <<= 32;
04581     qw |= ft->dwLowDateTime;
04582     qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
04583     return (long) qw;
04584 }
04585 
04586 int
04587 rb_w32_times(struct tms *tmbuf)
04588 {
04589     FILETIME create, exit, kernel, user;
04590 
04591     if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
04592         tmbuf->tms_utime = filetime_to_clock(&user);
04593         tmbuf->tms_stime = filetime_to_clock(&kernel);
04594         tmbuf->tms_cutime = 0;
04595         tmbuf->tms_cstime = 0;
04596     }
04597     else {
04598         tmbuf->tms_utime = clock();
04599         tmbuf->tms_stime = 0;
04600         tmbuf->tms_cutime = 0;
04601         tmbuf->tms_cstime = 0;
04602     }
04603     return 0;
04604 }
04605 
04606 #define yield_once() Sleep(0)
04607 #define yield_until(condition) do yield_once(); while (!(condition))
04608 
04609 static void
04610 catch_interrupt(void)
04611 {
04612     yield_once();
04613     RUBY_CRITICAL(rb_w32_wait_events(NULL, 0, 0));
04614 }
04615 
04616 #if defined __BORLANDC__
04617 #undef read
04618 int
04619 read(int fd, void *buf, size_t size)
04620 {
04621     int ret = _read(fd, buf, size);
04622     if ((ret < 0) && (errno == EPIPE)) {
04623         errno = 0;
04624         ret = 0;
04625     }
04626     catch_interrupt();
04627     return ret;
04628 }
04629 #endif
04630 
04631 #undef fgetc
04632 int
04633 rb_w32_getc(FILE* stream)
04634 {
04635     int c;
04636     if (enough_to_get(stream->FILE_COUNT)) {
04637         c = (unsigned char)*stream->FILE_READPTR++;
04638     }
04639     else {
04640         c = _filbuf(stream);
04641 #if defined __BORLANDC__
04642         if ((c == EOF) && (errno == EPIPE)) {
04643             clearerr(stream);
04644         }
04645 #endif
04646         catch_interrupt();
04647     }
04648     return c;
04649 }
04650 
04651 #undef fputc
04652 int
04653 rb_w32_putc(int c, FILE* stream)
04654 {
04655     if (enough_to_put(stream->FILE_COUNT)) {
04656         c = (unsigned char)(*stream->FILE_READPTR++ = (char)c);
04657     }
04658     else {
04659         c = _flsbuf(c, stream);
04660         catch_interrupt();
04661     }
04662     return c;
04663 }
04664 
04665 struct asynchronous_arg_t {
04666     /* output field */
04667     void* stackaddr;
04668     int errnum;
04669 
04670     /* input field */
04671     uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
04672     uintptr_t self;
04673     int argc;
04674     uintptr_t* argv;
04675 };
04676 
04677 static DWORD WINAPI
04678 call_asynchronous(PVOID argp)
04679 {
04680     DWORD ret;
04681     struct asynchronous_arg_t *arg = argp;
04682     arg->stackaddr = &argp;
04683     ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
04684     arg->errnum = errno;
04685     return ret;
04686 }
04687 
04688 uintptr_t
04689 rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
04690                     int argc, uintptr_t* argv, uintptr_t intrval)
04691 {
04692     DWORD val;
04693     BOOL interrupted = FALSE;
04694     HANDLE thr;
04695 
04696     RUBY_CRITICAL({
04697         struct asynchronous_arg_t arg;
04698 
04699         arg.stackaddr = NULL;
04700         arg.errnum = 0;
04701         arg.func = func;
04702         arg.self = self;
04703         arg.argc = argc;
04704         arg.argv = argv;
04705 
04706         thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
04707 
04708         if (thr) {
04709             yield_until(arg.stackaddr);
04710 
04711             if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
04712                 interrupted = TRUE;
04713 
04714                 if (TerminateThread(thr, intrval)) {
04715                     yield_once();
04716                 }
04717             }
04718 
04719             GetExitCodeThread(thr, &val);
04720             CloseHandle(thr);
04721 
04722             if (interrupted) {
04723                 /* must release stack of killed thread, why doesn't Windows? */
04724                 MEMORY_BASIC_INFORMATION m;
04725 
04726                 memset(&m, 0, sizeof(m));
04727                 if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
04728                     Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
04729                                   arg.stackaddr, GetLastError()));
04730                 }
04731                 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
04732                     Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
04733                                   m.AllocationBase, GetLastError()));
04734                 }
04735                 errno = EINTR;
04736             }
04737             else {
04738                 errno = arg.errnum;
04739             }
04740         }
04741     });
04742 
04743     if (!thr) {
04744         rb_fatal("failed to launch waiter thread:%ld", GetLastError());
04745     }
04746 
04747     return val;
04748 }
04749 
04750 char **
04751 rb_w32_get_environ(void)
04752 {
04753     char *envtop, *env;
04754     char **myenvtop, **myenv;
04755     int num;
04756 
04757     /*
04758      * We avoid values started with `='. If you want to deal those values,
04759      * change this function, and some functions in hash.c which recognize
04760      * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
04761      * CygWin deals these values by changing first `=' to '!'. But we don't
04762      * use such trick and follow cmd.exe's way that just doesn't show these
04763      * values.
04764      * (U.N. 2001-11-15)
04765      */
04766     envtop = GetEnvironmentStrings();
04767     for (env = envtop, num = 0; *env; env += strlen(env) + 1)
04768         if (*env != '=') num++;
04769 
04770     myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
04771     for (env = envtop, myenv = myenvtop; *env; env += strlen(env) + 1) {
04772         if (*env != '=') {
04773             if (!(*myenv = strdup(env))) {
04774                 break;
04775             }
04776             myenv++;
04777         }
04778     }
04779     *myenv = NULL;
04780     FreeEnvironmentStrings(envtop);
04781 
04782     return myenvtop;
04783 }
04784 
04785 void
04786 rb_w32_free_environ(char **env)
04787 {
04788     char **t = env;
04789 
04790     while (*t) free(*t++);
04791     free(env);
04792 }
04793 
04794 rb_pid_t
04795 rb_w32_getpid(void)
04796 {
04797     rb_pid_t pid;
04798 
04799     pid = GetCurrentProcessId();
04800 
04801     (void)IfWin95(pid = -pid, 0);
04802 
04803     return pid;
04804 }
04805 
04806 
04807 rb_pid_t
04808 rb_w32_getppid(void)
04809 {
04810     typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
04811     static query_func *pNtQueryInformationProcess = NULL;
04812     rb_pid_t ppid = 0;
04813 
04814     if (!IsWin95() && rb_w32_osver() >= 5) {
04815         if (!pNtQueryInformationProcess)
04816             pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
04817         if (pNtQueryInformationProcess) {
04818             struct {
04819                 long ExitStatus;
04820                 void* PebBaseAddress;
04821                 uintptr_t AffinityMask;
04822                 uintptr_t BasePriority;
04823                 uintptr_t UniqueProcessId;
04824                 uintptr_t ParentProcessId;
04825             } pbi;
04826             ULONG len;
04827             long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
04828             if (!ret) {
04829                 ppid = pbi.ParentProcessId;
04830             }
04831         }
04832     }
04833 
04834     return ppid;
04835 }
04836 
04837 int
04838 rb_w32_uopen(const char *file, int oflag, ...)
04839 {
04840     WCHAR *wfile;
04841     int ret;
04842     int pmode;
04843 
04844     va_list arg;
04845     va_start(arg, oflag);
04846     pmode = va_arg(arg, int);
04847     va_end(arg);
04848 
04849     if (!(wfile = utf8_to_wstr(file, NULL)))
04850         return -1;
04851     ret = rb_w32_wopen(wfile, oflag, pmode);
04852     free(wfile);
04853     return ret;
04854 }
04855 
04856 int
04857 rb_w32_open(const char *file, int oflag, ...)
04858 {
04859     WCHAR *wfile;
04860     int ret;
04861     int pmode;
04862 
04863     va_list arg;
04864     va_start(arg, oflag);
04865     pmode = va_arg(arg, int);
04866     va_end(arg);
04867 
04868     if ((oflag & O_TEXT) || !(oflag & O_BINARY))
04869         return _open(file, oflag, pmode);
04870 
04871     if (!(wfile = filecp_to_wstr(file, NULL)))
04872         return -1;
04873     ret = rb_w32_wopen(wfile, oflag, pmode);
04874     free(wfile);
04875     return ret;
04876 }
04877 
04878 int
04879 rb_w32_wopen(const WCHAR *file, int oflag, ...)
04880 {
04881     char flags = 0;
04882     int fd;
04883     DWORD access;
04884     DWORD create;
04885     DWORD attr = FILE_ATTRIBUTE_NORMAL;
04886     SECURITY_ATTRIBUTES sec;
04887     HANDLE h;
04888 
04889     if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
04890         va_list arg;
04891         int pmode;
04892         va_start(arg, oflag);
04893         pmode = va_arg(arg, int);
04894         va_end(arg);
04895         return _wopen(file, oflag, pmode);
04896     }
04897 
04898     sec.nLength = sizeof(sec);
04899     sec.lpSecurityDescriptor = NULL;
04900     if (oflag & O_NOINHERIT) {
04901         sec.bInheritHandle = FALSE;
04902         flags |= FNOINHERIT;
04903     }
04904     else {
04905         sec.bInheritHandle = TRUE;
04906     }
04907     oflag &= ~O_NOINHERIT;
04908 
04909     /* always open with binary mode */
04910     oflag &= ~(O_BINARY | O_TEXT);
04911 
04912     switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
04913       case O_RDWR:
04914         access = GENERIC_READ | GENERIC_WRITE;
04915         break;
04916       case O_RDONLY:
04917         access = GENERIC_READ;
04918         break;
04919       case O_WRONLY:
04920         access = GENERIC_WRITE;
04921         break;
04922       default:
04923         errno = EINVAL;
04924         return -1;
04925     }
04926     oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
04927 
04928     switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
04929       case O_CREAT:
04930         create = OPEN_ALWAYS;
04931         break;
04932       case 0:
04933       case O_EXCL:
04934         create = OPEN_EXISTING;
04935         break;
04936       case O_CREAT | O_EXCL:
04937       case O_CREAT | O_EXCL | O_TRUNC:
04938         create = CREATE_NEW;
04939         break;
04940       case O_TRUNC:
04941       case O_TRUNC | O_EXCL:
04942         create = TRUNCATE_EXISTING;
04943         break;
04944       case O_CREAT | O_TRUNC:
04945         create = CREATE_ALWAYS;
04946         break;
04947       default:
04948         errno = EINVAL;
04949         return -1;
04950     }
04951     if (oflag & O_CREAT) {
04952         va_list arg;
04953         int pmode;
04954         va_start(arg, oflag);
04955         pmode = va_arg(arg, int);
04956         va_end(arg);
04957         /* TODO: we need to check umask here, but it's not exported... */
04958         if (!(pmode & S_IWRITE))
04959             attr = FILE_ATTRIBUTE_READONLY;
04960     }
04961     oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
04962 
04963     if (oflag & O_TEMPORARY) {
04964         attr |= FILE_FLAG_DELETE_ON_CLOSE;
04965         access |= DELETE;
04966     }
04967     oflag &= ~O_TEMPORARY;
04968 
04969     if (oflag & _O_SHORT_LIVED)
04970         attr |= FILE_ATTRIBUTE_TEMPORARY;
04971     oflag &= ~_O_SHORT_LIVED;
04972 
04973     switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
04974       case 0:
04975         break;
04976       case O_SEQUENTIAL:
04977         attr |= FILE_FLAG_SEQUENTIAL_SCAN;
04978         break;
04979       case O_RANDOM:
04980         attr |= FILE_FLAG_RANDOM_ACCESS;
04981         break;
04982       default:
04983         errno = EINVAL;
04984         return -1;
04985     }
04986     oflag &= ~(O_SEQUENTIAL | O_RANDOM);
04987 
04988     if (oflag & ~O_APPEND) {
04989         errno = EINVAL;
04990         return -1;
04991     }
04992 
04993     /* allocate a C Runtime file handle */
04994     RUBY_CRITICAL({
04995         h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
04996         fd = _open_osfhandle((intptr_t)h, 0);
04997         CloseHandle(h);
04998     });
04999     if (fd == -1) {
05000         errno = EMFILE;
05001         return -1;
05002     }
05003     RUBY_CRITICAL({
05004         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
05005         _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
05006         _set_osflags(fd, 0);
05007 
05008         h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec,
05009                         create, attr, NULL);
05010         if (h == INVALID_HANDLE_VALUE) {
05011             errno = map_errno(GetLastError());
05012             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05013             fd = -1;
05014             goto quit;
05015         }
05016 
05017         switch (GetFileType(h)) {
05018           case FILE_TYPE_CHAR:
05019             flags |= FDEV;
05020             break;
05021           case FILE_TYPE_PIPE:
05022             flags |= FPIPE;
05023             break;
05024           case FILE_TYPE_UNKNOWN:
05025             errno = map_errno(GetLastError());
05026             CloseHandle(h);
05027             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05028             fd = -1;
05029             goto quit;
05030         }
05031         if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
05032             flags |= FAPPEND;
05033 
05034         _set_osfhnd(fd, (intptr_t)h);
05035         _osfile(fd) = flags | FOPEN;
05036 
05037         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05038       quit:
05039         ;
05040     });
05041 
05042     return fd;
05043 }
05044 
05045 int
05046 rb_w32_fclose(FILE *fp)
05047 {
05048     int fd = fileno(fp);
05049     SOCKET sock = TO_SOCKET(fd);
05050     int save_errno = errno;
05051 
05052     if (fflush(fp)) return -1;
05053     if (!is_socket(sock)) {
05054         UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
05055         return fclose(fp);
05056     }
05057     _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
05058     fclose(fp);
05059     errno = save_errno;
05060     if (closesocket(sock) == SOCKET_ERROR) {
05061         errno = map_errno(WSAGetLastError());
05062         return -1;
05063     }
05064     return 0;
05065 }
05066 
05067 int
05068 rb_w32_pipe(int fds[2])
05069 {
05070     static DWORD serial = 0;
05071     char name[] = "\\\\.\\pipe\\ruby0000000000000000-0000000000000000";
05072     char *p;
05073     SECURITY_ATTRIBUTES sec;
05074     HANDLE hRead, hWrite, h;
05075     int fdRead, fdWrite;
05076     int ret;
05077 
05078     /* if doesn't have CancelIo, use default pipe function */
05079     if (!cancel_io)
05080         return _pipe(fds, 65536L, _O_NOINHERIT);
05081 
05082     p = strchr(name, '0');
05083     snprintf(p, strlen(p) + 1, "%"PRI_PIDT_PREFIX"x-%lx", rb_w32_getpid(), serial++);
05084 
05085     sec.nLength = sizeof(sec);
05086     sec.lpSecurityDescriptor = NULL;
05087     sec.bInheritHandle = FALSE;
05088 
05089     RUBY_CRITICAL({
05090         hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
05091                                 0, 2, 65536, 65536, 0, &sec);
05092     });
05093     if (hRead == INVALID_HANDLE_VALUE) {
05094         DWORD err = GetLastError();
05095         if (err == ERROR_PIPE_BUSY)
05096             errno = EMFILE;
05097         else
05098             errno = map_errno(GetLastError());
05099         return -1;
05100     }
05101 
05102     RUBY_CRITICAL({
05103         hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
05104                             OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
05105     });
05106     if (hWrite == INVALID_HANDLE_VALUE) {
05107         errno = map_errno(GetLastError());
05108         CloseHandle(hRead);
05109         return -1;
05110     }
05111 
05112     RUBY_CRITICAL(do {
05113         ret = 0;
05114         h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
05115         fdRead = _open_osfhandle((intptr_t)h, 0);
05116         CloseHandle(h);
05117         if (fdRead == -1) {
05118             errno = EMFILE;
05119             CloseHandle(hWrite);
05120             CloseHandle(hRead);
05121             ret = -1;
05122             break;
05123         }
05124 
05125         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdRead)->lock)));
05126         _set_osfhnd(fdRead, (intptr_t)hRead);
05127         _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
05128         MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdRead)->lock)));
05129     } while (0));
05130     if (ret)
05131         return ret;
05132 
05133     RUBY_CRITICAL(do {
05134         h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
05135         fdWrite = _open_osfhandle((intptr_t)h, 0);
05136         CloseHandle(h);
05137         if (fdWrite == -1) {
05138             errno = EMFILE;
05139             CloseHandle(hWrite);
05140             ret = -1;
05141             break;
05142         }
05143         MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdWrite)->lock)));
05144         _set_osfhnd(fdWrite, (intptr_t)hWrite);
05145         _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
05146         MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdWrite)->lock)));
05147     } while (0));
05148     if (ret) {
05149         rb_w32_close(fdRead);
05150         return ret;
05151     }
05152 
05153     fds[0] = fdRead;
05154     fds[1] = fdWrite;
05155 
05156     return 0;
05157 }
05158 
05159 int
05160 rb_w32_close(int fd)
05161 {
05162     SOCKET sock = TO_SOCKET(fd);
05163     int save_errno = errno;
05164     st_data_t key;
05165 
05166     if (!is_socket(sock)) {
05167         UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
05168         return _close(fd);
05169     }
05170     _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
05171     key = (st_data_t)sock;
05172     st_delete(socklist, &key, NULL);
05173     sock = (SOCKET)key;
05174     _close(fd);
05175     errno = save_errno;
05176     if (closesocket(sock) == SOCKET_ERROR) {
05177         errno = map_errno(WSAGetLastError());
05178         return -1;
05179     }
05180     return 0;
05181 }
05182 
05183 #undef read
05184 ssize_t
05185 rb_w32_read(int fd, void *buf, size_t size)
05186 {
05187     SOCKET sock = TO_SOCKET(fd);
05188     DWORD read;
05189     DWORD wait;
05190     DWORD err;
05191     size_t len;
05192     size_t ret;
05193     OVERLAPPED ol, *pol = NULL;
05194     BOOL isconsole;
05195     BOOL islineinput = FALSE;
05196     int start = 0;
05197 
05198     if (is_socket(sock))
05199         return rb_w32_recv(fd, buf, size, 0);
05200 
05201     // validate fd by using _get_osfhandle() because we cannot access _nhandle
05202     if (_get_osfhandle(fd) == -1) {
05203         return -1;
05204     }
05205 
05206     if (_osfile(fd) & FTEXT) {
05207         return _read(fd, buf, size);
05208     }
05209 
05210     MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
05211 
05212     if (!size || _osfile(fd) & FEOFLAG) {
05213         _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
05214         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05215         return 0;
05216     }
05217 
05218     ret = 0;
05219     isconsole = is_console(_osfhnd(fd));
05220     if (isconsole) {
05221         DWORD mode;
05222         GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
05223         islineinput = (mode & ENABLE_LINE_INPUT) != 0;
05224     }
05225   retry:
05226     /* get rid of console reading bug */
05227     if (isconsole) {
05228         if (start)
05229             len = 1;
05230         else {
05231             len = 0;
05232             start = 1;
05233         }
05234     }
05235     else
05236         len = size;
05237     size -= len;
05238 
05239     /* if have cancel_io, use Overlapped I/O */
05240     if (cancel_io) {
05241         memset(&ol, 0, sizeof(ol));
05242         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05243             LONG high = 0;
05244             DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high,
05245                                        FILE_CURRENT);
05246 #ifndef INVALID_SET_FILE_POINTER
05247 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
05248 #endif
05249             if (low == INVALID_SET_FILE_POINTER) {
05250                 errno = map_errno(GetLastError());
05251                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05252                 return -1;
05253             }
05254             ol.Offset = low;
05255             ol.OffsetHigh = high;
05256         }
05257         ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
05258         if (!ol.hEvent) {
05259             errno = map_errno(GetLastError());
05260             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05261             return -1;
05262         }
05263 
05264         pol = &ol;
05265     }
05266 
05267     if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, pol)) {
05268         err = GetLastError();
05269         if (err != ERROR_IO_PENDING) {
05270             if (pol) CloseHandle(ol.hEvent);
05271             if (err == ERROR_ACCESS_DENIED)
05272                 errno = EBADF;
05273             else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
05274                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05275                 return 0;
05276             }
05277             else
05278                 errno = map_errno(err);
05279 
05280             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05281             return -1;
05282         }
05283 
05284         if (pol) {
05285             wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
05286             if (wait != WAIT_OBJECT_0) {
05287                 if (wait == WAIT_OBJECT_0 + 1)
05288                     errno = EINTR;
05289                 else
05290                     errno = map_errno(GetLastError());
05291                 CloseHandle(ol.hEvent);
05292                 cancel_io((HANDLE)_osfhnd(fd));
05293                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05294                 return -1;
05295             }
05296 
05297             if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
05298                 (err = GetLastError()) != ERROR_HANDLE_EOF) {
05299                 int ret = 0;
05300                 if (err != ERROR_BROKEN_PIPE) {
05301                     errno = map_errno(err);
05302                     ret = -1;
05303                 }
05304                 CloseHandle(ol.hEvent);
05305                 cancel_io((HANDLE)_osfhnd(fd));
05306                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05307                 return ret;
05308             }
05309         }
05310     }
05311 
05312     if (pol) {
05313         CloseHandle(ol.hEvent);
05314 
05315         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05316             LONG high = ol.OffsetHigh;
05317             DWORD low = ol.Offset + read;
05318             if (low < ol.Offset)
05319                 ++high;
05320             SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
05321         }
05322     }
05323 
05324     ret += read;
05325     if (read >= len) {
05326         buf = (char *)buf + read;
05327         if (!(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
05328             goto retry;
05329     }
05330     if (read == 0)
05331         _set_osflags(fd, _osfile(fd) | FEOFLAG);
05332 
05333 
05334     MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05335 
05336     return ret;
05337 }
05338 
05339 #undef write
05340 ssize_t
05341 rb_w32_write(int fd, const void *buf, size_t size)
05342 {
05343     SOCKET sock = TO_SOCKET(fd);
05344     DWORD written;
05345     DWORD wait;
05346     DWORD err;
05347     size_t len;
05348     size_t ret;
05349     OVERLAPPED ol, *pol = NULL;
05350 
05351     if (is_socket(sock))
05352         return rb_w32_send(fd, buf, size, 0);
05353 
05354     // validate fd by using _get_osfhandle() because we cannot access _nhandle
05355     if (_get_osfhandle(fd) == -1) {
05356         return -1;
05357     }
05358 
05359     if ((_osfile(fd) & FTEXT) &&
05360         (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
05361         return _write(fd, buf, size);
05362     }
05363 
05364     MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
05365 
05366     if (!size || _osfile(fd) & FEOFLAG) {
05367         MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05368         return 0;
05369     }
05370 
05371     ret = 0;
05372   retry:
05373     /* get rid of console writing bug */
05374     len = (_osfile(fd) & FDEV) ? min(32 * 1024, size) : size;
05375     size -= len;
05376 
05377     /* if have cancel_io, use Overlapped I/O */
05378     if (cancel_io) {
05379         memset(&ol, 0, sizeof(ol));
05380         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05381             LONG high = 0;
05382             DWORD method = _osfile(fd) & FAPPEND ? FILE_END : FILE_CURRENT;
05383             DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method);
05384 #ifndef INVALID_SET_FILE_POINTER
05385 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
05386 #endif
05387             if (low == INVALID_SET_FILE_POINTER) {
05388                 errno = map_errno(GetLastError());
05389                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05390                 return -1;
05391             }
05392             ol.Offset = low;
05393             ol.OffsetHigh = high;
05394         }
05395         ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
05396         if (!ol.hEvent) {
05397             errno = map_errno(GetLastError());
05398             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05399             return -1;
05400         }
05401 
05402         pol = &ol;
05403     }
05404 
05405     if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, pol)) {
05406         err = GetLastError();
05407         if (err != ERROR_IO_PENDING) {
05408             if (pol) CloseHandle(ol.hEvent);
05409             if (err == ERROR_ACCESS_DENIED)
05410                 errno = EBADF;
05411             else
05412                 errno = map_errno(err);
05413 
05414             MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05415             return -1;
05416         }
05417 
05418         if (pol) {
05419             wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
05420             if (wait != WAIT_OBJECT_0) {
05421                 if (wait == WAIT_OBJECT_0 + 1)
05422                     errno = EINTR;
05423                 else
05424                     errno = map_errno(GetLastError());
05425                 CloseHandle(ol.hEvent);
05426                 cancel_io((HANDLE)_osfhnd(fd));
05427                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05428                 return -1;
05429             }
05430 
05431             if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written,
05432                                      TRUE)) {
05433                 errno = map_errno(err);
05434                 CloseHandle(ol.hEvent);
05435                 cancel_io((HANDLE)_osfhnd(fd));
05436                 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05437                 return -1;
05438             }
05439         }
05440     }
05441 
05442     if (pol) {
05443         CloseHandle(ol.hEvent);
05444 
05445         if (!(_osfile(fd) & (FDEV | FPIPE))) {
05446             LONG high = ol.OffsetHigh;
05447             DWORD low = ol.Offset + written;
05448             if (low < ol.Offset)
05449                 ++high;
05450             SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
05451         }
05452     }
05453 
05454     ret += written;
05455     if (written == len) {
05456         buf = (const char *)buf + len;
05457         if (size > 0)
05458             goto retry;
05459     }
05460 
05461     MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
05462 
05463     return ret;
05464 }
05465 
05466 long
05467 rb_w32_write_console(uintptr_t strarg, int fd)
05468 {
05469     static int disable;
05470     HANDLE handle;
05471     DWORD dwMode, reslen;
05472     VALUE str = strarg;
05473 
05474     if (disable) return -1L;
05475     handle = (HANDLE)_osfhnd(fd);
05476     if (!GetConsoleMode(handle, &dwMode) ||
05477         !rb_econv_has_convpath_p(rb_enc_name(rb_enc_get(str)), "UTF-16LE"))
05478         return -1L;
05479 
05480     str = rb_str_encode(str, rb_enc_from_encoding(rb_enc_find("UTF-16LE")),
05481                         ECONV_INVALID_REPLACE|ECONV_UNDEF_REPLACE, Qnil);
05482     if (!WriteConsoleW(handle, (LPWSTR)RSTRING_PTR(str), RSTRING_LEN(str)/2,
05483                        &reslen, NULL)) {
05484         if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
05485             disable = TRUE;
05486         return -1L;
05487     }
05488     return (long)reslen;
05489 }
05490 
05491 static int
05492 unixtime_to_filetime(time_t time, FILETIME *ft)
05493 {
05494     ULARGE_INTEGER tmp;
05495 
05496     tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
05497     ft->dwLowDateTime = tmp.LowPart;
05498     ft->dwHighDateTime = tmp.HighPart;
05499     return 0;
05500 }
05501 
05502 static int
05503 wutime(const WCHAR *path, const struct utimbuf *times)
05504 {
05505     HANDLE hFile;
05506     FILETIME atime, mtime;
05507     struct stati64 stat;
05508     int ret = 0;
05509 
05510     if (wstati64(path, &stat)) {
05511         return -1;
05512     }
05513 
05514     if (times) {
05515         if (unixtime_to_filetime(times->actime, &atime)) {
05516             return -1;
05517         }
05518         if (unixtime_to_filetime(times->modtime, &mtime)) {
05519             return -1;
05520         }
05521     }
05522     else {
05523         GetSystemTimeAsFileTime(&atime);
05524         mtime = atime;
05525     }
05526 
05527     RUBY_CRITICAL({
05528         const DWORD attr = GetFileAttributesW(path);
05529         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
05530             SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
05531         hFile = CreateFileW(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
05532                             IsWin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, 0);
05533         if (hFile == INVALID_HANDLE_VALUE) {
05534             errno = map_errno(GetLastError());
05535             ret = -1;
05536         }
05537         else {
05538             if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
05539                 errno = map_errno(GetLastError());
05540                 ret = -1;
05541             }
05542             CloseHandle(hFile);
05543         }
05544         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
05545             SetFileAttributesW(path, attr);
05546     });
05547 
05548     return ret;
05549 }
05550 
05551 int
05552 rb_w32_uutime(const char *path, const struct utimbuf *times)
05553 {
05554     WCHAR *wpath;
05555     int ret;
05556 
05557     if (!(wpath = utf8_to_wstr(path, NULL)))
05558         return -1;
05559     ret = wutime(wpath, times);
05560     free(wpath);
05561     return ret;
05562 }
05563 
05564 int
05565 rb_w32_utime(const char *path, const struct utimbuf *times)
05566 {
05567     WCHAR *wpath;
05568     int ret;
05569 
05570     if (!(wpath = filecp_to_wstr(path, NULL)))
05571         return -1;
05572     ret = wutime(wpath, times);
05573     free(wpath);
05574     return ret;
05575 }
05576 
05577 int
05578 rb_w32_uchdir(const char *path)
05579 {
05580     WCHAR *wpath;
05581     int ret;
05582 
05583     if (!(wpath = utf8_to_wstr(path, NULL)))
05584         return -1;
05585     ret = _wchdir(wpath);
05586     free(wpath);
05587     return ret;
05588 }
05589 
05590 static int
05591 wmkdir(const WCHAR *wpath, int mode)
05592 {
05593     int ret = -1;
05594 
05595     RUBY_CRITICAL(do {
05596         if (CreateDirectoryW(wpath, NULL) == FALSE) {
05597             errno = map_errno(GetLastError());
05598             break;
05599         }
05600         if (_wchmod(wpath, mode) == -1) {
05601             RemoveDirectoryW(wpath);
05602             break;
05603         }
05604         ret = 0;
05605     } while (0));
05606     return ret;
05607 }
05608 
05609 int
05610 rb_w32_umkdir(const char *path, int mode)
05611 {
05612     WCHAR *wpath;
05613     int ret;
05614 
05615     if (!(wpath = utf8_to_wstr(path, NULL)))
05616         return -1;
05617     ret = wmkdir(wpath, mode);
05618     free(wpath);
05619     return ret;
05620 }
05621 
05622 int
05623 rb_w32_mkdir(const char *path, int mode)
05624 {
05625     WCHAR *wpath;
05626     int ret;
05627 
05628     if (!(wpath = filecp_to_wstr(path, NULL)))
05629         return -1;
05630     ret = wmkdir(wpath, mode);
05631     free(wpath);
05632     return ret;
05633 }
05634 
05635 static int
05636 wrmdir(const WCHAR *wpath)
05637 {
05638     int ret = 0;
05639     RUBY_CRITICAL({
05640         const DWORD attr = GetFileAttributesW(wpath);
05641         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05642             SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
05643         }
05644         if (RemoveDirectoryW(wpath) == FALSE) {
05645             errno = map_errno(GetLastError());
05646             ret = -1;
05647             if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05648                 SetFileAttributesW(wpath, attr);
05649             }
05650         }
05651     });
05652     return ret;
05653 }
05654 
05655 int
05656 rb_w32_rmdir(const char *path)
05657 {
05658     WCHAR *wpath;
05659     int ret;
05660 
05661     if (!(wpath = filecp_to_wstr(path, NULL)))
05662         return -1;
05663     ret = wrmdir(wpath);
05664     free(wpath);
05665     return ret;
05666 }
05667 
05668 int
05669 rb_w32_urmdir(const char *path)
05670 {
05671     WCHAR *wpath;
05672     int ret;
05673 
05674     if (!(wpath = utf8_to_wstr(path, NULL)))
05675         return -1;
05676     ret = wrmdir(wpath);
05677     free(wpath);
05678     return ret;
05679 }
05680 
05681 static int
05682 wunlink(const WCHAR *path)
05683 {
05684     int ret = 0;
05685     RUBY_CRITICAL({
05686         const DWORD attr = GetFileAttributesW(path);
05687         if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05688             SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
05689         }
05690         if (!DeleteFileW(path)) {
05691             errno = map_errno(GetLastError());
05692             ret = -1;
05693             if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
05694                 SetFileAttributesW(path, attr);
05695             }
05696         }
05697     });
05698     return ret;
05699 }
05700 
05701 int
05702 rb_w32_uunlink(const char *path)
05703 {
05704     WCHAR *wpath;
05705     int ret;
05706 
05707     if (!(wpath = utf8_to_wstr(path, NULL)))
05708         return -1;
05709     ret = wunlink(wpath);
05710     free(wpath);
05711     return ret;
05712 }
05713 
05714 int
05715 rb_w32_unlink(const char *path)
05716 {
05717     WCHAR *wpath;
05718     int ret;
05719 
05720     if (!(wpath = filecp_to_wstr(path, NULL)))
05721         return -1;
05722     ret = wunlink(wpath);
05723     free(wpath);
05724     return ret;
05725 }
05726 
05727 int
05728 rb_w32_uchmod(const char *path, int mode)
05729 {
05730     WCHAR *wpath;
05731     int ret;
05732 
05733     if (!(wpath = utf8_to_wstr(path, NULL)))
05734         return -1;
05735     ret = _wchmod(wpath, mode);
05736     free(wpath);
05737     return ret;
05738 }
05739 
05740 #if !defined(__BORLANDC__)
05741 int
05742 rb_w32_isatty(int fd)
05743 {
05744     DWORD mode;
05745 
05746     // validate fd by using _get_osfhandle() because we cannot access _nhandle
05747     if (_get_osfhandle(fd) == -1) {
05748         return 0;
05749     }
05750     if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
05751         errno = ENOTTY;
05752         return 0;
05753     }
05754     return 1;
05755 }
05756 #endif
05757 
05758 //
05759 // Fix bcc32's stdio bug
05760 //
05761 
05762 #ifdef __BORLANDC__
05763 static int
05764 too_many_files(void)
05765 {
05766     FILE *f;
05767     for (f = _streams; f < _streams + _nfile; f++) {
05768         if (f->fd < 0) return 0;
05769     }
05770     return 1;
05771 }
05772 
05773 #undef fopen
05774 FILE *
05775 rb_w32_fopen(const char *path, const char *mode)
05776 {
05777     FILE *f = (errno = 0, fopen(path, mode));
05778     if (f == NULL && errno == 0) {
05779         if (too_many_files())
05780             errno = EMFILE;
05781     }
05782     return f;
05783 }
05784 
05785 FILE *
05786 rb_w32_fdopen(int handle, const char *type)
05787 {
05788     FILE *f = (errno = 0, _fdopen(handle, (char *)type));
05789     if (f == NULL && errno == 0) {
05790         if (handle < 0)
05791             errno = EBADF;
05792         else if (too_many_files())
05793             errno = EMFILE;
05794     }
05795     return f;
05796 }
05797 
05798 FILE *
05799 rb_w32_fsopen(const char *path, const char *mode, int shflags)
05800 {
05801     FILE *f = (errno = 0, _fsopen(path, mode, shflags));
05802     if (f == NULL && errno == 0) {
05803         if (too_many_files())
05804             errno = EMFILE;
05805     }
05806     return f;
05807 }
05808 #endif
05809 
05810 #if defined(_MSC_VER) && RT_VER <= 60
05811 extern long _ftol(double);
05812 long
05813 _ftol2(double d)
05814 {
05815     return _ftol(d);
05816 }
05817 long
05818 _ftol2_sse(double d)
05819 {
05820     return _ftol(d);
05821 }
05822 #endif
05823 
05824 #ifndef signbit
05825 int
05826 signbit(double x)
05827 {
05828     int *ip = (int *)(&x + 1) - 1;
05829     return *ip < 0;
05830 }
05831 #endif
05832 
05833 char * WSAAPI
05834 rb_w32_inet_ntop(int af, void *addr, char *numaddr, size_t numaddr_len)
05835 {
05836     typedef char *(WSAAPI inet_ntop_t)(int, void *, char *, size_t);
05837     inet_ntop_t *pInetNtop;
05838     pInetNtop = (inet_ntop_t *)get_proc_address("ws2_32", "inet_ntop", NULL);
05839     if(pInetNtop){
05840        return pInetNtop(af,addr,numaddr,numaddr_len);
05841     }else{
05842        struct in_addr in;
05843        memcpy(&in.s_addr, addr, sizeof(in.s_addr));
05844        snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
05845     }
05846     return numaddr;
05847 }
05848 
05849 char
05850 rb_w32_fd_is_text(int fd) {
05851     return _osfile(fd) & FTEXT;
05852 }
05853