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

Generated on Wed Sep 8 2010 21:55:28 for Ruby by  doxygen 1.7.1