• Main Page
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

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

Generated on Thu Sep 8 2011 03:50:49 for Ruby by  doxygen 1.7.1