Logo Search packages:      
Sourcecode: adanaxisgpl version File versions  Download package

win32.c

//%Header {
/*****************************************************************************
 *
 * File: src/MushRuby/win32/win32.c
 *
 * Author: Andy Southgate 2002-2007
 *
 * This file contains original work by Andy Southgate.  The author and his
 * employer (Mushware Limited) irrevocably waive all of their copyright rights
 * vested in this particular version of this file to the furthest extent
 * permitted.  The author and Mushware Limited also irrevocably waive any and
 * all of their intellectual property rights arising from said file and its
 * creation that would otherwise restrict the rights of any party to use and/or
 * distribute the use of, the techniques and methods used herein.  A written
 * waiver can be obtained via http://www.mushware.com/.
 *
 * This software carries NO WARRANTY of any kind.
 *
 ****************************************************************************/
//%Header } 47Dca+/tKwksebazvP3nWg
/*
 *  Copyright (c) 1993, Intergraph Corporation
 *
 *  You may distribute under the terms of either the GNU General Public
 *  License or the Artistic License, as specified in the perl README file.
 *
 *  Various Unix compatibility functions and NT specific functions.
 *
 *  Some of this code was derived from the MSDOS port(s) and the OS/2 port.
 *
 */

#include "ruby.h"
#include "rubysig.h"
#include "dln.h"
#include <fcntl.h>
#include <process.h>
#include <sys/stat.h>
/* #include <sys/wait.h> */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>

#include <windows.h>
#include <winbase.h>
#include <wincon.h>
#ifdef __MINGW32__
#include <mswsock.h>
#endif
#include "win32.h"
#include "win32/dir.h"
#ifdef _WIN32_WCE
#include "wince.h"
#endif
#ifndef index
#define index(x, y) strchr((x), (y))
#endif
#define isdirsep(x) ((x) == '/' || (x) == '\\')

#undef stat
#undef fclose
#undef close
#undef setsockopt

#ifndef bool
#define bool int
#endif

#if defined __BORLANDC__ || defined _WIN32_WCE
#  define _filbuf _fgetc
#  define _flsbuf _fputc
#  define enough_to_get(n) (--(n) >= 0)
#  define enough_to_put(n) (++(n) < 0)
#else
#  define enough_to_get(n) (--(n) >= 0)
#  define enough_to_put(n) (--(n) >= 0)
#endif

#if HAVE_WSAWAITFORMULTIPLEEVENTS
# define USE_INTERRUPT_WINSOCK
#endif

#if USE_INTERRUPT_WINSOCK
# define WaitForMultipleEvents WSAWaitForMultipleEvents
# define CreateSignal() (HANDLE)WSACreateEvent()
# define SetSignal(ev) WSASetEvent(ev)
# define ResetSignal(ev) WSAResetEvent(ev)
#else  /* USE_INTERRUPT_WINSOCK */
# define WaitForMultipleEvents WaitForMultipleObjectsEx
# define CreateSignal() CreateEvent(NULL, FALSE, FALSE, NULL);
# define SetSignal(ev) SetEvent(ev)
# define ResetSignal(ev) (void)0
#endif /* USE_INTERRUPT_WINSOCK */

#ifdef WIN32_DEBUG
#define Debug(something) something
#else
#define Debug(something) /* nothing */
#endif

#define TO_SOCKET(x)    _get_osfhandle(x)

bool NtSyncProcess = TRUE;

static struct ChildRecord *CreateChild(const char *, const char *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE);
static bool has_redirection(const char *);
static void StartSockets ();
static DWORD wait_events(HANDLE event, DWORD timeout);
#if !defined(_WIN32_WCE)
static int rb_w32_open_osfhandle(long osfhandle, int flags);
#else
#define rb_w32_open_osfhandle(osfhandle, flags) _open_osfhandle(osfhandle, flags)
#endif

/* errno mapping */
static struct {
    DWORD winerr;
    int err;
} errmap[] = {
    { ERROR_INVALID_FUNCTION,       EINVAL            },
    { ERROR_FILE_NOT_FOUND,         ENOENT            },
    { ERROR_PATH_NOT_FOUND,         ENOENT            },
    { ERROR_TOO_MANY_OPEN_FILES,    EMFILE            },
    { ERROR_ACCESS_DENIED,          EACCES            },
    { ERROR_INVALID_HANDLE,         EBADF       },
    { ERROR_ARENA_TRASHED,          ENOMEM            },
    { ERROR_NOT_ENOUGH_MEMORY,      ENOMEM            },
    { ERROR_INVALID_BLOCK,          ENOMEM            },
    { ERROR_BAD_ENVIRONMENT,        E2BIG       },
    { ERROR_BAD_FORMAT,       ENOEXEC           },
    { ERROR_INVALID_ACCESS,         EINVAL            },
    { ERROR_INVALID_DATA,           EINVAL            },
    { ERROR_INVALID_DRIVE,          ENOENT            },
    { ERROR_CURRENT_DIRECTORY,      EACCES            },
    { ERROR_NOT_SAME_DEVICE,        EXDEV       },
    { ERROR_NO_MORE_FILES,          ENOENT            },
    { ERROR_WRITE_PROTECT,          EROFS       },
    { ERROR_BAD_UNIT,               ENODEV            },
    { ERROR_NOT_READY,        ENXIO       },
    { ERROR_BAD_COMMAND,            EACCES            },
    { ERROR_CRC,              EACCES            },
    { ERROR_BAD_LENGTH,       EACCES            },
    { ERROR_SEEK,             EIO         },
    { ERROR_NOT_DOS_DISK,           EACCES            },
    { ERROR_SECTOR_NOT_FOUND,       EACCES            },
    { ERROR_OUT_OF_PAPER,           EACCES            },
    { ERROR_WRITE_FAULT,            EIO         },
    { ERROR_READ_FAULT,       EIO         },
    { ERROR_GEN_FAILURE,            EACCES            },
    { ERROR_LOCK_VIOLATION,         EACCES            },
    { ERROR_SHARING_VIOLATION,      EACCES            },
    { ERROR_WRONG_DISK,       EACCES            },
    { ERROR_SHARING_BUFFER_EXCEEDED,      EACCES            },
    { ERROR_BAD_NETPATH,            ENOENT            },
    { ERROR_NETWORK_ACCESS_DENIED,  EACCES            },
    { ERROR_BAD_NET_NAME,           ENOENT            },
    { ERROR_FILE_EXISTS,            EEXIST            },
    { ERROR_CANNOT_MAKE,            EACCES            },
    { ERROR_FAIL_I24,               EACCES            },
    { ERROR_INVALID_PARAMETER,      EINVAL            },
    { ERROR_NO_PROC_SLOTS,          EAGAIN            },
    { ERROR_DRIVE_LOCKED,           EACCES            },
    { ERROR_BROKEN_PIPE,            EPIPE       },
    { ERROR_DISK_FULL,        ENOSPC            },
    { ERROR_INVALID_TARGET_HANDLE,  EBADF       },
    { ERROR_INVALID_HANDLE,         EINVAL            },
    { ERROR_WAIT_NO_CHILDREN,       ECHILD            },
    { ERROR_CHILD_NOT_COMPLETE,     ECHILD            },
    { ERROR_DIRECT_ACCESS_HANDLE,   EBADF       },
    { ERROR_NEGATIVE_SEEK,          EINVAL            },
    { ERROR_SEEK_ON_DEVICE,         EACCES            },
    { ERROR_DIR_NOT_EMPTY,          ENOTEMPTY   },
    { ERROR_DIRECTORY,        ENOTDIR           },
    { ERROR_NOT_LOCKED,       EACCES            },
    { ERROR_BAD_PATHNAME,           ENOENT            },
    { ERROR_MAX_THRDS_REACHED,      EAGAIN            },
    { ERROR_LOCK_FAILED,            EACCES            },
    { ERROR_ALREADY_EXISTS,         EEXIST            },
    { ERROR_INVALID_STARTING_CODESEG,     ENOEXEC           },
    { ERROR_INVALID_STACKSEG,       ENOEXEC           },
    { ERROR_INVALID_MODULETYPE,     ENOEXEC           },
    { ERROR_INVALID_EXE_SIGNATURE,  ENOEXEC           },
    { ERROR_EXE_MARKED_INVALID,     ENOEXEC           },
    { ERROR_BAD_EXE_FORMAT,         ENOEXEC           },
    { ERROR_ITERATED_DATA_EXCEEDS_64k,ENOEXEC         },
    { ERROR_INVALID_MINALLOCSIZE,   ENOEXEC           },
    { ERROR_DYNLINK_FROM_INVALID_RING,ENOEXEC         },
    { ERROR_IOPL_NOT_ENABLED,       ENOEXEC           },
    { ERROR_INVALID_SEGDPL,         ENOEXEC           },
    { ERROR_AUTODATASEG_EXCEEDS_64k,      ENOEXEC           },
    { ERROR_RING2SEG_MUST_BE_MOVABLE,     ENOEXEC           },
    { ERROR_RELOC_CHAIN_XEEDS_SEGLIM,     ENOEXEC           },
    { ERROR_INFLOOP_IN_RELOC_CHAIN, ENOEXEC           },
    { ERROR_FILENAME_EXCED_RANGE,   ENOENT            },
    { ERROR_NESTING_NOT_ALLOWED,    EAGAIN            },
    { ERROR_NOT_ENOUGH_QUOTA,       ENOMEM            },
    { WSAENAMETOOLONG,        ENAMETOOLONG      },
    { WSAENOTEMPTY,                 ENOTEMPTY   }
};

static int
map_errno(DWORD winerr)
{
    int i;

    if (winerr == 0) {
      return 0;
    }

    for (i = 0; i < sizeof(errmap) / sizeof(*errmap); i++) {
      if (errmap[i].winerr == winerr) {
          return errmap[i].err;
      }
    }

    if (winerr >= WSABASEERR) {
      return winerr;
    }
    return EINVAL;
}

static char *NTLoginName;

#ifdef WIN95
static DWORD Win32System = (DWORD)-1;

DWORD
rb_w32_osid(void)
{
    static OSVERSIONINFO osver;

    if (osver.dwPlatformId != Win32System) {
      memset(&osver, 0, sizeof(OSVERSIONINFO));
      osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
      GetVersionEx(&osver);
      Win32System = osver.dwPlatformId;
    }
    return (Win32System);
}
#endif

#define IsWinNT() rb_w32_iswinnt()
#define IsWin95() rb_w32_iswin95()

/* main thread constants */
static struct {
    HANDLE handle;
    DWORD id;
} main_thread;

/* interrupt stuff */
static HANDLE interrupted_event;

HANDLE
GetCurrentThreadHandle(void)
{
    static HANDLE current_process_handle = NULL;
    HANDLE h;

    if (!current_process_handle)
      current_process_handle = GetCurrentProcess();
    if (!DuplicateHandle(current_process_handle, GetCurrentThread(),
                   current_process_handle, &h,
                   0, FALSE, DUPLICATE_SAME_ACCESS))
      return NULL;
    return h;
}

/* simulate flock by locking a range on the file */


#define LK_ERR(f,i) ((f) ? (i = 0) : (errno = GetLastError() == ERROR_LOCK_VIOLATION ? EWOULDBLOCK : EACCES))
#define LK_LEN      ULONG_MAX

static VALUE
flock_winnt(VALUE self, int argc, VALUE* argv)
{
    OVERLAPPED o;
    int i = -1;
    const HANDLE fh = (HANDLE)self;
    const int oper = argc;

    memset(&o, 0, sizeof(o));

    switch(oper) {
      case LOCK_SH:           /* shared lock */
      LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
      break;
      case LOCK_EX:           /* exclusive lock */
      LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
      break;
      case LOCK_SH|LOCK_NB:   /* non-blocking shared lock */
      LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
      break;
      case LOCK_EX|LOCK_NB:   /* non-blocking exclusive lock */
      LK_ERR(LockFileEx(fh,
                    LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
                    0, LK_LEN, LK_LEN, &o), i);
      break;
      case LOCK_UN:           /* unlock lock */
      case LOCK_UN|LOCK_NB:   /* unlock is always non-blocking, I hope */
      LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
      break;
      default:            /* unknown */
      errno = EINVAL;
      break;
    }
    return i;
}

#ifdef WIN95
static VALUE
flock_win95(VALUE self, int argc, VALUE* argv)
{
    int i = -1;
    const HANDLE fh = (HANDLE)self;
    const int oper = argc;

    switch(oper) {
      case LOCK_EX:
      do {
          LK_ERR(LockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
      } while (i && errno == EWOULDBLOCK);
      break;
      case LOCK_EX|LOCK_NB:
      LK_ERR(LockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
      break;
      case LOCK_UN:
      case LOCK_UN|LOCK_NB:
      LK_ERR(UnlockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
      break;
      default:
      errno = EINVAL;
      break;
    }
    return i;
}
#endif

#undef LK_ERR

int
flock(int fd, int oper)
{
#ifdef WIN95
    static asynchronous_func_t locker = NULL;

    if (!locker) {
      if (IsWinNT())
          locker = flock_winnt;
      else
          locker = flock_win95;
    }
#else
    const asynchronous_func_t locker = flock_winnt;
#endif

    return rb_w32_asynchronize(locker,
                        (VALUE)_get_osfhandle(fd), oper, NULL,
                        (DWORD)-1);
}

//
// Initialization stuff
//
void
NtInitialize(int *argc, char ***argv)
{

    WORD version;
    int ret;

    //
    // subvert cmd.exe's feeble attempt at command line parsing
    //
    *argc = rb_w32_cmdvector(GetCommandLine(), argv);

    //
    // Now set up the correct time stuff
    //

    tzset();

    // Initialize Winsock
    StartSockets();

#ifdef _WIN32_WCE
    // free commandline buffer
    wce_FreeCommandLine();
#endif
}

char *
getlogin()
{
    char buffer[200];
    DWORD len = 200;
    extern char *NTLoginName;

    if (NTLoginName == NULL) {
      if (GetUserName(buffer, &len)) {
          NTLoginName = ALLOC_N(char, len+1);
          strncpy(NTLoginName, buffer, len);
          NTLoginName[len] = '\0';
      }
      else {
          NTLoginName = "<Unknown>";
      }
    }
    return NTLoginName;
}

#define MAXCHILDNUM 256 /* max num of child processes */

static struct ChildRecord {
    HANDLE hProcess;    /* process handle */
    rb_pid_t pid; /* process id */
} ChildRecord[MAXCHILDNUM];

#define FOREACH_CHILD(v) do { \
    struct ChildRecord* v; \
    for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
#define END_FOREACH_CHILD } while (0)

static struct ChildRecord *
FindFirstChildSlot(void)
{
    FOREACH_CHILD(child) {
      if (child->pid) return child;
    } END_FOREACH_CHILD;
    return NULL;
}

static struct ChildRecord *
FindChildSlot(rb_pid_t pid)
{

    FOREACH_CHILD(child) {
      if (child->pid == pid) {
          return child;
      }
    } END_FOREACH_CHILD;
    return NULL;
}

static void
CloseChildHandle(struct ChildRecord *child)
{
    HANDLE h = child->hProcess;
    child->hProcess = NULL;
    child->pid = 0;
    CloseHandle(h);
}

static struct ChildRecord *
FindFreeChildSlot(void)
{
    FOREACH_CHILD(child) {
      if (!child->pid) {
          child->pid = -1;    /* lock the slot */
          child->hProcess = NULL;
          return child;
      }
    } END_FOREACH_CHILD;
    return NULL;
}


int
SafeFree(char **vec, int vecc)
{
    //   vec
    //   |
    //   V       ^---------------------V
    //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    //   |   |       | ....  |  NULL |   | ..... |\0 |   | ..... |\0 |...
    //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    //   |-  elements+1             -| ^ 1st element   ^ 2nd element

      char *p;

      p = (char *)vec;
      free(p);

      return 0;
}


/*
  ruby -lne 'BEGIN{$cmds = Hash.new(0); $mask = 1}'
   -e '$cmds[$_.downcase] |= $mask' -e '$mask <<= 1 if ARGF.eof'
   -e 'END{$cmds.sort.each{|n,f|puts "    \"\\#{f.to_s(8)}\" #{n.dump} + 1,"}}'
   98cmd ntcmd
 */
static char *szInternalCmds[] = {
    "\2" "assoc" + 1,
    "\3" "break" + 1,
    "\3" "call" + 1,
    "\3" "cd" + 1,
    "\1" "chcp" + 1,
    "\3" "chdir" + 1,
    "\3" "cls" + 1,
    "\2" "color" + 1,
    "\3" "copy" + 1,
    "\1" "ctty" + 1,
    "\3" "date" + 1,
    "\3" "del" + 1,
    "\3" "dir" + 1,
    "\3" "echo" + 1,
    "\2" "endlocal" + 1,
    "\3" "erase" + 1,
    "\3" "exit" + 1,
    "\3" "for" + 1,
    "\2" "ftype" + 1,
    "\3" "goto" + 1,
    "\3" "if" + 1,
    "\1" "lfnfor" + 1,
    "\1" "lh" + 1,
    "\1" "lock" + 1,
    "\3" "md" + 1,
    "\3" "mkdir" + 1,
    "\2" "move" + 1,
    "\3" "path" + 1,
    "\3" "pause" + 1,
    "\2" "popd" + 1,
    "\3" "prompt" + 1,
    "\2" "pushd" + 1,
    "\3" "rd" + 1,
    "\3" "rem" + 1,
    "\3" "ren" + 1,
    "\3" "rename" + 1,
    "\3" "rmdir" + 1,
    "\3" "set" + 1,
    "\2" "setlocal" + 1,
    "\3" "shift" + 1,
    "\2" "start" + 1,
    "\3" "time" + 1,
    "\2" "title" + 1,
    "\1" "truename" + 1,
    "\3" "type" + 1,
    "\1" "unlock" + 1,
    "\3" "ver" + 1,
    "\3" "verify" + 1,
    "\3" "vol" + 1,
};

static int
internal_match(const void *key, const void *elem)
{
    return strcmp(key, *(const char *const *)elem);
}

static int
isInternalCmd(const char *cmd, const char *interp)
{
    int i, nt = 1;
    char cmdname[9], *b = cmdname, c, **nm;

    i = strlen(interp) - 11;
    if ((i == 0 || i > 0 && isdirsep(interp[i-1])) &&
      strcasecmp(interp+i, "command.com") == 0) {
      nt = 0;
    }
    do {
      if (!(c = *cmd++)) return 0;
    } while (isspace(c));
    while (isalpha(c)) {
      *b++ = tolower(c);
      if (b == cmdname + sizeof(cmdname)) return 0;
      c = *cmd++;
    }
    if (c == '.') c = *cmd;
    switch (c) {
      case '<': case '>': case '|':
      return 1;
      case '\0': case ' ': case '\t': case '\n':
      break;
      default:
      return 0;
    }
    *b = 0;
    nm = bsearch(cmdname, szInternalCmds,
             sizeof(szInternalCmds) / sizeof(*szInternalCmds),
             sizeof(*szInternalCmds),
             internal_match);
    if (!nm || !(nm[0][-1] & (nt ? 2 : 1)))
      return 0;
    return 1;
}


SOCKET
rb_w32_get_osfhandle(int fh)
{
    return _get_osfhandle(fh);
}

rb_pid_t
pipe_exec(char *cmd, int mode, FILE **fpr, FILE **fpw)
{
    struct ChildRecord* child;
    HANDLE hReadIn, hReadOut;
    HANDLE hWriteIn, hWriteOut;
    HANDLE hDupInFile, hDupOutFile;
    HANDLE hCurProc;
    SECURITY_ATTRIBUTES sa;
    BOOL fRet;
    BOOL reading, writing;
    int fd;
    int pipemode;
    char modes[3];
    int ret;

    /* Figure out what we're doing... */
    writing = (mode & (O_WRONLY | O_RDWR)) ? TRUE : FALSE;
    reading = ((mode & O_RDWR) || !writing) ? TRUE : FALSE;
    if (mode & O_BINARY) {
      pipemode = O_BINARY;
      modes[1] = 'b';
      modes[2] = '\0';
    }
    else {
      pipemode = O_TEXT;
      modes[1] = '\0';
    }

    sa.nLength              = sizeof (SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle       = TRUE;
    ret = -1;
    hWriteIn = hReadOut = NULL;

    RUBY_CRITICAL(do {
      /* create pipe */
      hCurProc = GetCurrentProcess();
      if (reading) {
          fRet = CreatePipe(&hReadIn, &hReadOut, &sa, 2048L);
          if (!fRet) {
            errno = map_errno(GetLastError());
            break;
          }
          if (!DuplicateHandle(hCurProc, hReadIn, hCurProc, &hDupInFile, 0,
                         FALSE, DUPLICATE_SAME_ACCESS)) {
            errno = map_errno(GetLastError());
            CloseHandle(hReadIn);
            CloseHandle(hReadOut);
            CloseHandle(hCurProc);
            break;
          }
          CloseHandle(hReadIn);
      }
      if (writing) {
          fRet = CreatePipe(&hWriteIn, &hWriteOut, &sa, 2048L);
          if (!fRet) {
            errno = map_errno(GetLastError());
            write_pipe_failed:
            if (reading) {
                CloseHandle(hDupInFile);
                CloseHandle(hReadOut);
            }
            break;
          }
          if (!DuplicateHandle(hCurProc, hWriteOut, hCurProc, &hDupOutFile, 0,
                         FALSE, DUPLICATE_SAME_ACCESS)) {
            errno = map_errno(GetLastError());
            CloseHandle(hWriteIn);
            CloseHandle(hWriteOut);
            CloseHandle(hCurProc);
            goto write_pipe_failed;
          }
          CloseHandle(hWriteOut);
      }
      CloseHandle(hCurProc);

      /* create child process */
      child = CreateChild(cmd, NULL, &sa, hWriteIn, hReadOut, NULL);
      if (!child) {
          if (reading) {
            CloseHandle(hReadOut);
            CloseHandle(hDupInFile);
          }
          if (writing) {
            CloseHandle(hWriteIn);
            CloseHandle(hDupOutFile);
          }
          break;
      }

      /* associate handle to fp */
      if (reading) {
          fd = rb_w32_open_osfhandle((long)hDupInFile,
                               (_O_RDONLY | pipemode));
          CloseHandle(hReadOut);
          if (fd == -1) {
            CloseHandle(hDupInFile);
            read_open_failed:
            if (writing) {
                CloseHandle(hWriteIn);
                CloseHandle(hDupOutFile);
            }
            CloseChildHandle(child);
            break;
          }
          modes[0] = 'r';
          if ((*fpr = (FILE *)fdopen(fd, modes)) == NULL) {
            _close(fd);
            goto read_open_failed;
          }
      }
      if (writing) {
          fd = rb_w32_open_osfhandle((long)hDupOutFile,
                               (_O_WRONLY | pipemode));
          CloseHandle(hWriteIn);
          if (fd == -1) {
            CloseHandle(hDupOutFile);
            write_open_failed:
            if (reading) {
                fclose(*fpr);
            }
            CloseChildHandle(child);
            break;
          }
          modes[0] = 'w';
          if ((*fpw = (FILE *)fdopen(fd, modes)) == NULL) {
            _close(fd);
            goto write_open_failed;
          }
      }
      ret = child->pid;
    } while (0));

    return ret;
}

extern VALUE rb_last_status;

int
do_spawn(int mode, char *cmd)
{
    struct ChildRecord *child;
    DWORD exitcode;

    switch (mode) {
      case P_WAIT:
      case P_NOWAIT:
      case P_OVERLAY:
      break;
      default:
      errno = EINVAL;
      return -1;
    }

    child = CreateChild(cmd, NULL, NULL, NULL, NULL, NULL);
    if (!child) {
      return -1;
    }

    switch (mode) {
      case P_WAIT:
      rb_syswait(child->pid);
      return NUM2INT(rb_last_status);
      case P_NOWAIT:
      return child->pid;
      case P_OVERLAY:
      WaitForSingleObject(child->hProcess, INFINITE);
      GetExitCodeProcess(child->hProcess, &exitcode);
      CloseChildHandle(child);
      _exit(exitcode);
      default:
      return -1;  /* not reached */
    }
}

int
do_aspawn(int mode, char *prog, char **argv)
{
    char *cmd, *p, *q, *s, **t;
    int len, n, bs, quote;
    struct ChildRecord *child;
    DWORD exitcode;

    switch (mode) {
      case P_WAIT:
      case P_NOWAIT:
      case P_OVERLAY:
      break;
      default:
      errno = EINVAL;
      return -1;
    }

    for (t = argv, len = 0; *t; t++) {
      for (p = *t, n = quote = bs = 0; *p; ++p) {
          switch (*p) {
            case '\\':
            ++bs;
            break;
            case '"':
            n += bs + 1; bs = 0;
            quote = 1;
            break;
            case ' ': case '\t':
            quote = 1;
            default:
            bs = 0;
            p = CharNext(p) - 1;
            break;
          }
      }
      len += p - *t + n + 1;
      if (quote) len += 2;
    }
    cmd = ALLOCA_N(char, len);
    for (t = argv, q = cmd; p = *t; t++) {
      quote = 0;
      s = p;
      if (!*p || strpbrk(p, " \t\"")) {
          quote = 1;
          *q++ = '"';
      }
      for (bs = 0; *p; ++p) {
          switch (*p) {
            case '\\':
            ++bs;
            break;
            case '"':
            memcpy(q, s, n = p - s); q += n; s = p;
            memset(q, '\\', ++bs); q += bs; bs = 0;
            break;
            default:
            bs = 0;
            p = CharNext(p) - 1;
            break;
          }
      }
      memcpy(q, s, n = p - s);
      q += n;
      if (quote) *q++ = '"';
      *q++ = ' ';
    }
    if (q > cmd) --q;
    *q = '\0';

    child = CreateChild(cmd, prog, NULL, NULL, NULL, NULL);
    if (!child) {
      return -1;
    }

    switch (mode) {
      case P_WAIT:
      rb_syswait(child->pid);
      return NUM2INT(rb_last_status);
      case P_NOWAIT:
      return child->pid;
      case P_OVERLAY:
      WaitForSingleObject(child->hProcess, INFINITE);
      GetExitCodeProcess(child->hProcess, &exitcode);
      CloseChildHandle(child);
      _exit(exitcode);
      default:
      return -1;  /* not reached */
    }
}

static struct ChildRecord *
CreateChild(const char *cmd, const char *prog, SECURITY_ATTRIBUTES *psa,
          HANDLE hInput, HANDLE hOutput, HANDLE hError)
{
    BOOL fRet;
    DWORD  dwCreationFlags;
    STARTUPINFO aStartupInfo;
    PROCESS_INFORMATION aProcessInformation;
    SECURITY_ATTRIBUTES sa;
    const char *shell;
    struct ChildRecord *child;
    char *p = NULL;

    if (!cmd && !prog) {
      errno = EFAULT;
      return NULL;
    }

    child = FindFreeChildSlot();
    if (!child) {
      errno = EAGAIN;
      return NULL;
    }

    if (!psa) {
      sa.nLength              = sizeof (SECURITY_ATTRIBUTES);
      sa.lpSecurityDescriptor = NULL;
      sa.bInheritHandle       = TRUE;
      psa = &sa;
    }

    memset(&aStartupInfo, 0, sizeof (STARTUPINFO));
    memset(&aProcessInformation, 0, sizeof (PROCESS_INFORMATION));
    aStartupInfo.cb = sizeof (STARTUPINFO);
    if (hInput || hOutput || hError) {
      aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
      if (hInput) {
          aStartupInfo.hStdInput  = hInput;
      }
      else {
          aStartupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
      }
      if (hOutput) {
          aStartupInfo.hStdOutput = hOutput;
      }
      else {
          aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
      }
      if (hError) {
          aStartupInfo.hStdError = hError;
      }
      else {
          aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
      }
    }

    dwCreationFlags = (NORMAL_PRIORITY_CLASS);

    if (prog) {
      if (!(p = dln_find_exe(prog, NULL))) {
          shell = prog;
      }
    }
    else {
      int redir = -1;
      int len = 0;
      while (ISSPACE(*cmd)) cmd++;
      for (prog = cmd; *prog; prog = CharNext(prog)) {
          if (ISSPACE(*prog)) {
            len = prog - cmd;
            do ++prog; while (ISSPACE(*prog));
            if (!*prog--) break;
          }
          else {
            len = 0;
          }
      }
      if (!len) len = strlen(cmd);
      if ((shell = getenv("RUBYSHELL")) && (redir = has_redirection(cmd))) {
          char *tmp = ALLOCA_N(char, strlen(shell) + len + sizeof(" -c ") + 2);
          sprintf(tmp, "%s -c \"%.*s\"", shell, len, cmd);
          cmd = tmp;
      }
      else if ((shell = getenv("COMSPEC")) &&
             ((redir < 0 ? has_redirection(cmd) : redir) ||
              isInternalCmd(cmd, shell))) {
          char *tmp = ALLOCA_N(char, strlen(shell) + len + sizeof(" /c "));
          sprintf(tmp, "%s /c %.*s", shell, len, cmd);
          cmd = tmp;
      }
      else {
          shell = NULL;
          prog = cmd;
          for (;;) {
            if (!*prog) {
                p = dln_find_exe(cmd, NULL);
                break;
            }
            if (strchr(".:*?\"/\\", *prog)) {
                if (cmd[len]) {
                  char *tmp = ALLOCA_N(char, len + 1);
                  memcpy(tmp, cmd, len);
                  tmp[len] = 0;
                  cmd = tmp;
                }
                break;
            }
            if (ISSPACE(*prog) || strchr("<>|", *prog)) {
                len = prog - cmd;
                p = ALLOCA_N(char, len + 1);
                memcpy(p, cmd, len);
                p[len] = 0;
                p = dln_find_exe(p, NULL);
                break;
            }
            prog++;
          }
      }
    }
    if (p) {
      shell = p;
      while (*p) {
          if ((unsigned char)*p == '/')
            *p = '\\';
          p = CharNext(p);
      }
    }

    RUBY_CRITICAL({
      fRet = CreateProcess(shell, (char *)cmd, psa, psa,
                       psa->bInheritHandle, dwCreationFlags, NULL, NULL,
                       &aStartupInfo, &aProcessInformation);
      errno = map_errno(GetLastError());
    });

    if (!fRet) {
      child->pid = 0;         /* release the slot */
      return NULL;
    }

    CloseHandle(aProcessInformation.hThread);

    child->hProcess = aProcessInformation.hProcess;
    child->pid = (rb_pid_t)aProcessInformation.dwProcessId;

    if (!IsWinNT()) {
      /* On Win9x, make pid positive similarly to cygwin and perl */
      child->pid = -child->pid;
    }

    return child;
}

typedef struct _NtCmdLineElement {
    struct _NtCmdLineElement *next;
    char *str;
    int len;
    int flags;
} NtCmdLineElement;

//
// Possible values for flags
//

#define NTGLOB   0x1    // element contains a wildcard
#define NTMALLOC 0x2    // string in element was malloc'ed
#define NTSTRING 0x4    // element contains a quoted string

static int
insert(const char *path, VALUE vinfo)
{
    NtCmdLineElement *tmpcurr;
    NtCmdLineElement ***tail = (NtCmdLineElement ***)vinfo;

    tmpcurr = ALLOC(NtCmdLineElement);
    MEMZERO(tmpcurr, NtCmdLineElement, 1);
    tmpcurr->len = strlen(path);
    tmpcurr->str = ALLOC_N(char, tmpcurr->len + 1);
    tmpcurr->flags |= NTMALLOC;
    strcpy(tmpcurr->str, path);
    **tail = tmpcurr;
    *tail = &tmpcurr->next;

    return 0;
}

#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#else
# define MAXPATHLEN 512
#endif


static NtCmdLineElement **
cmdglob(NtCmdLineElement *patt, NtCmdLineElement **tail)
{
    char buffer[MAXPATHLEN], *buf = buffer;
    char *p;
    NtCmdLineElement **last = tail;

    if (patt->len >= MAXPATHLEN)
      buf = ruby_xmalloc(patt->len + 1);

    strncpy (buf, patt->str, patt->len);
    buf[patt->len] = '\0';
    for (p = buf; *p; p = CharNext(p))
      if (*p == '\\')
          *p = '/';
    ruby_globi(buf, 0, insert, (VALUE)&tail);
    if (buf != buffer)
      free(buf);

    if (last == tail) return 0;
    if (patt->flags & NTMALLOC)
      free(patt->str);
    free(patt);
    return tail;
}

// 
// Check a command string to determine if it has I/O redirection
// characters that require it to be executed by a command interpreter
//

static bool
has_redirection(const char *cmd)
{
    char quote = '\0';
    const char *ptr;

    //
    // Scan the string, looking for redirection (< or >) or pipe 
    // characters (|) that are not in a quoted string
    //

    for (ptr = cmd; *ptr;) {
      switch (*ptr) {
        case '\'':
        case '\"':
          if (!quote)
            quote = *ptr;
          else if (quote == *ptr)
            quote = '\0';
          ptr++;
          break;

        case '>':
        case '<':
        case '|':
          if (!quote)
            return TRUE;
          ptr++;
          break;

        case '\\':
          ptr++;
        default:
          ptr = CharNext(ptr);
          break;
      }
    }
    return FALSE;
}

static inline char *
skipspace(char *ptr)
{
    while (ISSPACE(*ptr))
      ptr++;
    return ptr;
}

int 
rb_w32_cmdvector(const char *cmd, char ***vec)
{
    int cmdlen, globbing, len, i;
    int elements, strsz, done;
    int slashes, escape;
    char *ptr, *base, *buffer, *cmdline;
    char **vptr;
    char quote;
    NtCmdLineElement *curr, **tail;
    NtCmdLineElement *cmdhead = NULL, **cmdtail = &cmdhead;

    //
    // just return if we don't have a command line
    //

    while (ISSPACE(*cmd))
      cmd++;
    if (!*cmd) {
      *vec = NULL;
      return 0;
    }

    ptr = cmdline = strdup(cmd);

    //
    // Ok, parse the command line, building a list of CmdLineElements.
    // When we've finished, and it's an input command (meaning that it's
    // the processes argv), we'll do globing and then build the argument 
    // vector.
    // The outer loop does one interation for each element seen. 
    // The inner loop does one interation for each character in the element.
    //

    while (*(ptr = skipspace(ptr))) {
      base = ptr;
      quote = slashes = globbing = escape = 0;
      for (done = 0; !done && *ptr; ) {
          //
          // Switch on the current character. We only care about the
          // white-space characters, the  wild-card characters, and the
          // quote characters.
          //

          switch (*ptr) {
            case '\\':
            slashes++;
              break;

            case ' ':
            case '\t':
            case '\n':
            //
            // if we're not in a string, then we're finished with this
            // element
            //

            if (!quote) {
                *ptr = 0;
                done = 1;
            }
            break;

            case '*':
            case '?':
            case '[':
            case '{':
            // 
            // record the fact that this element has a wildcard character
            // N.B. Don't glob if inside a single quoted string
            //

            if (quote != '\'')
                globbing++;
            slashes = 0;
            break;

            case '\'':
            case '\"':
            //
            // if we're already in a string, see if this is the
            // terminating close-quote. If it is, we're finished with 
            // the string, but not neccessarily with the element.
            // If we're not already in a string, start one.
            //

            if (!(slashes & 1)) {
                if (!quote)
                  quote = *ptr;
                else if (quote == *ptr)
                  quote = '\0';
                escape++;
            }
            slashes = 0;
            break;

            default:
            ptr = CharNext(ptr);
            slashes = 0;
            continue;
          }
          ptr++;
      }

      //
      // when we get here, we've got a pair of pointers to the element,
      // base and ptr. Base points to the start of the element while ptr
      // points to the character following the element.
      //

      len = ptr - base;
      if (done) --len;

      //
      // if it's an input vector element and it's enclosed by quotes, 
      // we can remove them.
      //

      if (escape) {
          char *p = base;
          slashes = quote = 0;
          while (p < base + len) {
            switch (*p) {
              case '\\':
                p++;
                slashes++;
                break;

              case '\'':
              case '"':
                if (!(slashes & 1)) {
                  if (!quote)
                      quote = *p;
                  else if (quote == *p)
                      quote = '\0';
                  else {
                      p++;
                      slashes = 0;
                      break;
                  }
                }
                if (base + slashes == p) {
                  base += slashes >> 1;
                  len -= slashes >> 1;
                  slashes &= 1;
                }
                if (base == p) {
                  base = ++p;
                  --len;
                }
                else {
                  memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1), base + len - p);
                  slashes >>= 1;
                  p -= slashes;
                  len -= slashes + 1;
                  slashes = 0;
                }
                break;

              default:
                p = CharNext(p);
                slashes = 0;
                break;
            }
          }
      }

      curr = ALLOC(NtCmdLineElement);
      MEMZERO(curr, NtCmdLineElement, 1);
      curr->str = base;
      curr->len = len;

      if (globbing && (tail = cmdglob(curr, cmdtail))) {
          cmdtail = tail;
      }
      else {
          *cmdtail = curr;
          cmdtail = &curr->next;
      }
    }

    //
    // Almost done! 
    // Count up the elements, then allocate space for a vector of pointers
    // (argv) and a string table for the elements.
    // 

    for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
      elements++;
      strsz += (curr->len + 1);
    }

    len = (elements+1)*sizeof(char *) + strsz;
    buffer = ALLOC_N(char, len);
    
    //
    // make vptr point to the start of the buffer
    // and ptr point to the area we'll consider the string table.
    //
    //   buffer (*vec)
    //   |
    //   V       ^---------------------V
    //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    //   |   |       | ....  | NULL  |   | ..... |\0 |   | ..... |\0 |...
    //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    //   |-  elements+1             -| ^ 1st element   ^ 2nd element

    vptr = (char **) buffer;

    ptr = buffer + (elements+1) * sizeof(char *);

    while (curr = cmdhead) {
      strncpy (ptr, curr->str, curr->len);
      ptr[curr->len] = '\0';
      *vptr++ = ptr;
      ptr += curr->len + 1;
      cmdhead = curr->next;
      if (curr->flags & NTMALLOC) free(curr->str);
      free(curr);
    }
    *vptr = 0;

    *vec = (char **) buffer;
    free(cmdline);
    return elements;
}

//
// UNIX compatible directory access functions for NT
//

#define PATHLEN 1024

//
// The idea here is to read all the directory names into a string table
// (separated by nulls) and when one of the other dir functions is called
// return the pointer to the current file name. 
//

DIR *
rb_w32_opendir(const char *filename)
{
    DIR               *p;
    long               len;
    long               idx;
    char               scannamespc[PATHLEN];
    char          *scanname = scannamespc;
    struct stat          sbuf;
    WIN32_FIND_DATA fd;
    HANDLE          fh;

    //
    // check to see if we've got a directory
    //

    if (rb_w32_stat(filename, &sbuf) < 0)
      return NULL;
    if (!(sbuf.st_mode & S_IFDIR) &&
      (!ISALPHA(filename[0]) || filename[1] != ':' || filename[2] != '\0' ||
      ((1 << (filename[0] & 0x5f) - 'A') & GetLogicalDrives()) == 0)) {
      errno = ENOTDIR;
      return NULL;
    }

    //
    // Get us a DIR structure
    //

    p = xcalloc(sizeof(DIR), 1);
    if (p == NULL)
      return NULL;
    
    //
    // Create the search pattern
    //

    strcpy(scanname, filename);

    if (index("/\\:", *CharPrev(scanname, scanname + strlen(scanname))) == NULL)
      strcat(scanname, "/*");
    else
      strcat(scanname, "*");

    //
    // do the FindFirstFile call
    //

    fh = FindFirstFile(scanname, &fd);
    if (fh == INVALID_HANDLE_VALUE) {
      errno = map_errno(GetLastError());
      return NULL;
    }

    //
    // now allocate the first part of the string table for the
    // filenames that we find.
    //

    idx = strlen(fd.cFileName)+1;
    p->start = ALLOC_N(char, idx);
    strcpy(p->start, fd.cFileName);
    p->nfiles++;

    //
    // loop finding all the files that match the wildcard
    // (which should be all of them in this directory!).
    // the variable idx should point one past the null terminator
    // of the previous string found.
    //
    while (FindNextFile(fh, &fd)) {
      len = strlen(fd.cFileName);

      //
      // bump the string table size by enough for the
      // new name and it's null terminator 
      //

      #define Renew(x, y, z) (x = (z *)realloc(x, y))

      Renew (p->start, idx+len+1, char);
      if (p->start == NULL) {
            rb_fatal ("opendir: malloc failed!\n");
      }
      strcpy(&p->start[idx], fd.cFileName);
      p->nfiles++;
      idx += len+1;
    }
    FindClose(fh);
    p->size = idx;
    p->curr = p->start;
    return p;
}


//
// Readdir just returns the current string pointer and bumps the
// string pointer to the next entry.
//

struct direct  *
rb_w32_readdir(DIR *dirp)
{
    int         len;
    static int  dummy = 0;

    if (dirp->curr) {

      //
      // first set up the structure to return
      //

      len = strlen(dirp->curr);
      strcpy(dirp->dirstr.d_name, dirp->curr);
      dirp->dirstr.d_namlen = len;

      //
      // Fake inode
      //
      dirp->dirstr.d_ino = dummy++;

      //
      // Now set up for the next call to readdir
      //

      dirp->curr += len + 1;
      if (dirp->curr >= (dirp->start + dirp->size)) {
          dirp->curr = NULL;
      }

      return &(dirp->dirstr);

    } else
      return NULL;
}

//
// Telldir returns the current string pointer position
//

long
rb_w32_telldir(DIR *dirp)
{
      return (long) dirp->curr;     /* ouch! pointer to long cast */
}

//
// Seekdir moves the string pointer to a previously saved position
// (Saved by telldir).

void
rb_w32_seekdir(DIR *dirp, long loc)
{
      dirp->curr = (char *) loc;    /* ouch! long to pointer cast */
}

//
// Rewinddir resets the string pointer to the start
//

void
rb_w32_rewinddir(DIR *dirp)
{
      dirp->curr = dirp->start;
}

//
// This just free's the memory allocated by opendir
//

void
rb_w32_closedir(DIR *dirp)
{
      free(dirp->start);
      free(dirp);
}

EXTERN_C void __cdecl _lock_fhandle(int);
EXTERN_C void __cdecl _unlock_fhandle(int);
EXTERN_C void __cdecl _unlock(int);

#if (defined _MT || defined __MSVCRT__) && !defined __BORLANDC__
#define MSVCRT_THREADS
#endif
#ifdef MSVCRT_THREADS
# define MTHREAD_ONLY(x) x
# define STHREAD_ONLY(x)
#elif defined(__BORLANDC__)
# define MTHREAD_ONLY(x)
# define STHREAD_ONLY(x)
#else
# define MTHREAD_ONLY(x)
# define STHREAD_ONLY(x) x
#endif

typedef struct    {
    long osfhnd;    /* underlying OS file HANDLE */
    char osfile;    /* attributes of file (e.g., open in text mode?) */
    char pipech;    /* one char buffer for handles opened on pipes */
#ifdef MSVCRT_THREADS
    int lockinitflag;
    CRITICAL_SECTION lock;
#endif
}     ioinfo;

#if !defined _CRTIMP || defined __MINGW32__
#undef _CRTIMP
#define _CRTIMP __declspec(dllimport)
#endif

#if !defined(__BORLANDC__) && !defined(_WIN32_WCE)
EXTERN_C _CRTIMP ioinfo * __pioinfo[];

#define IOINFO_L2E                  5
#define IOINFO_ARRAY_ELTS     (1 << IOINFO_L2E)
#define _pioinfo(i)     (__pioinfo[i >> IOINFO_L2E] + (i & (IOINFO_ARRAY_ELTS - 1)))
#define _osfhnd(i)  (_pioinfo(i)->osfhnd)
#define _osfile(i)  (_pioinfo(i)->osfile)
#define _pipech(i)  (_pioinfo(i)->pipech)

#define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
#define _set_osflags(fh, flags) (_osfile(fh) = (flags))

#define FOPEN                 0x01  /* file handle open */
#define FNOINHERIT            0x10  /* file handle opened O_NOINHERIT */
#define FAPPEND               0x20  /* file handle opened O_APPEND */
#define FDEV                  0x40  /* file handle refers to device */
#define FTEXT                 0x80  /* file handle is in text mode */

static int
rb_w32_open_osfhandle(long osfhandle, int flags)
{
    int fh;
    char fileflags;           /* _osfile flags */
    HANDLE hF;

    /* copy relevant flags from second parameter */
    fileflags = FDEV;

    if (flags & O_APPEND)
      fileflags |= FAPPEND;

    if (flags & O_TEXT)
      fileflags |= FTEXT;

    if (flags & O_NOINHERIT)
      fileflags |= FNOINHERIT;

    /* attempt to allocate a C Runtime file handle */
    hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
    fh = _open_osfhandle((long)hF, 0);
    CloseHandle(hF);
    if (fh == -1) {
      errno = EMFILE;         /* too many open files */
      _doserrno = 0L;         /* not an OS error */
    }
    else {

      MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fh)->lock)));
      /* the file is open. now, set the info in _osfhnd array */
      _set_osfhnd(fh, osfhandle);

      fileflags |= FOPEN;           /* mark as open */

      _set_osflags(fh, fileflags); /* set osfile entry */
      MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fh)->lock));
    }
    return fh;                /* return handle */
}
#else

#define _set_osfhnd(fh, osfh) (void)((fh), (osfh))
#define _set_osflags(fh, flags) (void)((fh), (flags))

#endif

#ifdef __BORLANDC__
static int
rb_w32_open_osfhandle(long osfhandle, int flags)
{
    int fd = _open_osfhandle(osfhandle, flags);
    if (fd == -1) {
      errno = EMFILE;         /* too many open files */
      _doserrno = 0L;         /* not an OS error */
    }
    return fd;
}
#endif

#undef getsockopt

static int
is_socket(SOCKET fd)
{
    char sockbuf[80];
    int optlen;
    int retval;
    int result = TRUE;

    optlen = sizeof(sockbuf);
    RUBY_CRITICAL({
      retval = getsockopt(fd, SOL_SOCKET, SO_TYPE, sockbuf, &optlen);
      if (retval == SOCKET_ERROR) {
          int iRet;
          iRet = WSAGetLastError();
          if (iRet == WSAENOTSOCK || iRet == WSANOTINITIALISED)
            result = FALSE;
      }
    });

    //
    // If we get here, then fd is actually a socket.
    //

    return result;
}

//
// Since the errors returned by the socket error function 
// WSAGetLastError() are not known by the library routine strerror
// we have to roll our own.
//

#undef strerror

char *
rb_w32_strerror(int e)
{
    static char buffer[512];
#if !defined __MINGW32__
    extern int sys_nerr;
#endif
    DWORD source = 0;
    char *p;

#if defined __BORLANDC__ && defined ENOTEMPTY // _sys_errlist is broken
    switch (e) {
      case ENAMETOOLONG:
      return "Filename too long";
      case ENOTEMPTY:
      return "Directory not empty";
    }
#endif

    if (e < 0 || e > sys_nerr) {
      if (e < 0)
          e = GetLastError();
      if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
                    FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
                    buffer, 512, NULL) == 0) {
          strcpy(buffer, "Unknown Error");
      }
    }
    else {
      strncpy(buffer, strerror(e), sizeof(buffer));
      buffer[sizeof(buffer) - 1] = 0;
    }

    p = buffer;
    while ((p = strpbrk(p, "\r\n")) != NULL) {
      memmove(p, p + 1, strlen(p));
    }
    return buffer;
}

//
// various stubs
//


// Ownership
//
// Just pretend that everyone is a superuser. NT will let us know if
// we don't really have permission to do something.
//

#define ROOT_UID  0
#define ROOT_GID  0

rb_uid_t
getuid(void)
{
      return ROOT_UID;
}

rb_uid_t
geteuid(void)
{
      return ROOT_UID;
}

rb_gid_t
getgid(void)
{
      return ROOT_GID;
}

rb_gid_t
getegid(void)
{
    return ROOT_GID;
}

int
setuid(rb_uid_t uid)
{ 
    return (uid == ROOT_UID ? 0 : -1);
}

int
setgid(rb_gid_t gid)
{
    return (gid == ROOT_GID ? 0 : -1);
}

//
// File system stuff
//

int
/* ioctl(int i, unsigned int u, char *data) */
#ifdef __BORLANDC__
  ioctl(int i, int u, ...)
#else
  ioctl(int i, unsigned int u, long data)
#endif
{
    errno = EINVAL;
    return -1;
}

#undef FD_SET

void
rb_w32_fdset(int fd, fd_set *set)
{
    unsigned int i;
    SOCKET s = TO_SOCKET(fd);

    for (i = 0; i < set->fd_count; i++) {
        if (set->fd_array[i] == s) {
            return;
        }
    }
    if (i == set->fd_count) {
        if (set->fd_count < FD_SETSIZE) {
            set->fd_array[i] = s;
            set->fd_count++;
        }
    }
}

#undef FD_CLR

void
rb_w32_fdclr(int fd, fd_set *set)
{
    unsigned int i;
    SOCKET s = TO_SOCKET(fd);

    for (i = 0; i < set->fd_count; i++) {
        if (set->fd_array[i] == s) {
            while (i < set->fd_count - 1) {
                set->fd_array[i] = set->fd_array[i + 1];
                i++;
            }
            set->fd_count--;
            break;
        }
    }
}

#undef FD_ISSET

int
rb_w32_fdisset(int fd, fd_set *set)
{
    int ret;
    SOCKET s = TO_SOCKET(fd);
    if (s == (SOCKET)INVALID_HANDLE_VALUE)
        return 0;
    RUBY_CRITICAL(ret = __WSAFDIsSet(s, set));
    return ret;
}

//
// Networking trampolines
// These are used to avoid socket startup/shutdown overhead in case 
// the socket routines aren't used.
//

#undef select

static int NtSocketsInitialized = 0;

static int
extract_file_fd(fd_set *set, fd_set *fileset)
{
    int idx;

    fileset->fd_count = 0;
    if (!set)
      return 0;
    for (idx = 0; idx < set->fd_count; idx++) {
      SOCKET fd = set->fd_array[idx];

      if (!is_socket(fd)) {
          int i;

          for (i = 0; i < fileset->fd_count; i++) {
            if (fileset->fd_array[i] == fd) {
                break;
            }
          }
          if (i == fileset->fd_count) {
            if (fileset->fd_count < FD_SETSIZE) {
                fileset->fd_array[i] = fd;
                fileset->fd_count++;
            }
          }
      }
    }
    return fileset->fd_count;
}

long 
rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
             struct timeval *timeout)
{
    long r;
    fd_set file_rd;
    fd_set file_wr;
#ifdef USE_INTERRUPT_WINSOCK
    fd_set trap;
#endif /* USE_INTERRUPT_WINSOCK */
    int file_nfds;

    if (!NtSocketsInitialized) {
      StartSockets();
    }
    r = 0;
    if (rd && rd->fd_count > r) r = rd->fd_count;
    if (wr && wr->fd_count > r) r = wr->fd_count;
    if (ex && ex->fd_count > r) r = ex->fd_count;
    if (nfds > r) nfds = r;
    if (nfds == 0 && timeout) {
      Sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
      return 0;
    }
    file_nfds = extract_file_fd(rd, &file_rd);
    file_nfds += extract_file_fd(wr, &file_wr);
    if (file_nfds)
    {
      // assume normal files are always readable/writable
      // fake read/write fd_set and return value
      if (rd) *rd = file_rd;
      if (wr) *wr = file_wr;
      return file_nfds;
    }

#if USE_INTERRUPT_WINSOCK
    if (ex)
      trap = *ex;
    else
      trap.fd_count = 0;
    if (trap.fd_count < FD_SETSIZE)
      trap.fd_array[trap.fd_count++] = (SOCKET)interrupted_event;
    // else unable to catch interrupt.
    ex = &trap;
#endif /* USE_INTERRUPT_WINSOCK */

    RUBY_CRITICAL({
      r = select(nfds, rd, wr, ex, timeout);
      if (r == SOCKET_ERROR) {
          errno = map_errno(WSAGetLastError());
      }
    });
    return r;
}

static void
StartSockets ()
{
    WORD version;
    WSADATA retdata;
    int ret;
    int iSockOpt;
    
    //
    // initalize the winsock interface and insure that it's
    // cleaned up at exit.
    //
    version = MAKEWORD(1, 1);
    if (ret = WSAStartup(version, &retdata))
      rb_fatal ("Unable to locate winsock library!\n");
    if (LOBYTE(retdata.wVersion) != 1)
      rb_fatal("could not find version 1 of winsock dll\n");

    if (HIBYTE(retdata.wVersion) != 1)
      rb_fatal("could not find version 1 of winsock dll\n");

    atexit((void (*)(void)) WSACleanup);

#ifndef SO_SYNCHRONOUS_NONALERT
#define SO_SYNCHRONOUS_NONALERT 0x20
#endif

    iSockOpt = SO_SYNCHRONOUS_NONALERT;
    /*
     * Enable the use of sockets as filehandles
     */
#ifndef SO_OPENTYPE
#define SO_OPENTYPE     0x7008
#endif

    setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
             (char *)&iSockOpt, sizeof(iSockOpt));

    main_thread.handle = GetCurrentThreadHandle();
    main_thread.id = GetCurrentThreadId();

    interrupted_event = CreateSignal();
    if (!interrupted_event)
      rb_fatal("Unable to create interrupt event!\n");
    NtSocketsInitialized = 1;
}

#undef accept

int
rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
{
    SOCKET r;

    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = accept(TO_SOCKET(s), addr, addrlen);
      if (r == INVALID_SOCKET) {
          errno = map_errno(WSAGetLastError());
          s = -1;
      }
      else {
          s = rb_w32_open_osfhandle(r, O_RDWR|O_BINARY);
      }
    });
    return s;
}

#undef bind

int 
rb_w32_bind(int s, struct sockaddr *addr, int addrlen)
{
    int r;

    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = bind(TO_SOCKET(s), addr, addrlen);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef connect

int 
rb_w32_connect(int s, struct sockaddr *addr, int addrlen)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = connect(TO_SOCKET(s), addr, addrlen);
      if (r == SOCKET_ERROR) {
          r = WSAGetLastError();
          if (r != WSAEWOULDBLOCK) {
            errno = map_errno(r);
          }
          else {
            errno = EINPROGRESS;
            r = -1;
          }
      }
    });
    return r;
}


#undef getpeername

int 
rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = getpeername(TO_SOCKET(s), addr, addrlen);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef getsockname

int 
rb_w32_getsockname(int s, struct sockaddr *addr, int *addrlen)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = getsockname(TO_SOCKET(s), addr, addrlen);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

int 
rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef ioctlsocket

int 
rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = ioctlsocket(TO_SOCKET(s), cmd, argp);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef listen

int 
rb_w32_listen(int s, int backlog)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = listen(TO_SOCKET(s), backlog);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef recv

int 
rb_w32_recv(int s, char *buf, int len, int flags)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = recv(TO_SOCKET(s), buf, len, flags);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef recvfrom

int 
rb_w32_recvfrom(int s, char *buf, int len, int flags, 
            struct sockaddr *from, int *fromlen)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = recvfrom(TO_SOCKET(s), buf, len, flags, from, fromlen);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef send

int 
rb_w32_send(int s, char *buf, int len, int flags)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = send(TO_SOCKET(s), buf, len, flags);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef sendto

int 
rb_w32_sendto(int s, char *buf, int len, int flags, 
            struct sockaddr *to, int tolen)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = sendto(TO_SOCKET(s), buf, len, flags, to, tolen);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef setsockopt

int 
rb_w32_setsockopt(int s, int level, int optname, char *optval, int optlen)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}
    
#undef shutdown

int 
rb_w32_shutdown(int s, int how)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = shutdown(TO_SOCKET(s), how);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef socket

int 
rb_w32_socket(int af, int type, int protocol)
{
    SOCKET s;
    int fd;

    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      s = socket(af, type, protocol);
      if (s == INVALID_SOCKET) {
          errno = map_errno(WSAGetLastError());
          fd = -1;
      }
      else {
          fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY);
      }
    });
    return fd;
}

#undef gethostbyaddr

struct hostent *
rb_w32_gethostbyaddr (char *addr, int len, int type)
{
    struct hostent *r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = gethostbyaddr(addr, len, type);
      if (r == NULL)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef gethostbyname

struct hostent *
rb_w32_gethostbyname (char *name)
{
    struct hostent *r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = gethostbyname(name);
      if (r == NULL)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef gethostname

int
rb_w32_gethostname (char *name, int len)
{
    int r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = gethostname(name, len);
      if (r == SOCKET_ERROR)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef getprotobyname

struct protoent *
rb_w32_getprotobyname (char *name)
{
    struct protoent *r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = getprotobyname(name);
      if (r == NULL)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef getprotobynumber

struct protoent *
rb_w32_getprotobynumber (int num)
{
    struct protoent *r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = getprotobynumber(num);
      if (r == NULL)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef getservbyname

struct servent *
rb_w32_getservbyname (char *name, char *proto)
{
    struct servent *r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = getservbyname(name, proto);
      if (r == NULL)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

#undef getservbyport

struct servent *
rb_w32_getservbyport (int port, char *proto)
{
    struct servent *r;
    if (!NtSocketsInitialized) {
      StartSockets();
    }
    RUBY_CRITICAL({
      r = getservbyport(port, proto);
      if (r == NULL)
          errno = map_errno(WSAGetLastError());
    });
    return r;
}

//
// Networking stubs
//

void endhostent() {}
void endnetent() {}
void endprotoent() {}
void endservent() {}

struct netent *getnetent (void) {return (struct netent *) NULL;}

struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}

struct netent *getnetbyname(char *name) {return (struct netent *)NULL;}

struct protoent *getprotoent (void) {return (struct protoent *) NULL;}

struct servent *getservent (void) {return (struct servent *) NULL;}

void sethostent (int stayopen) {}

void setnetent (int stayopen) {}

void setprotoent (int stayopen) {}

void setservent (int stayopen) {}

int
fcntl(int fd, int cmd, ...)
{
    SOCKET sock = TO_SOCKET(fd);
    va_list va;
    int arg;
    int ret;
    u_long ioctlArg;

    if (!is_socket(sock)) {
      errno = EBADF;
      return -1;
    }
    if (cmd != F_SETFL) {
      errno = EINVAL;
      return -1;
    }

    va_start(va, cmd);
    arg = va_arg(va, int);
    va_end(va);
    if (arg & O_NONBLOCK) {
      ioctlArg = 1;
    }
    else {
      ioctlArg = 0;
    }
    RUBY_CRITICAL({
      ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
      if (ret == -1) {
          errno = map_errno(WSAGetLastError());
      }
    });

    return ret;
}

#ifndef WNOHANG
#define WNOHANG -1
#endif

static rb_pid_t
poll_child_status(struct ChildRecord *child, int *stat_loc)
{
    DWORD exitcode;
    DWORD err;

    if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
      /* If an error occured, return immediatly. */
      err = GetLastError();
      if (err == ERROR_INVALID_PARAMETER)
          errno = ECHILD;
      else
          errno = map_errno(GetLastError());
      CloseChildHandle(child);
      return -1;
    }
    if (exitcode != STILL_ACTIVE) {
      /* If already died, return immediatly. */
      rb_pid_t pid = child->pid;
      CloseChildHandle(child);
      if (stat_loc) *stat_loc = exitcode << 8;
      return pid;
    }
    return 0;
}

rb_pid_t
waitpid (rb_pid_t pid, int *stat_loc, int options)
{
    DWORD timeout;

    if (options == WNOHANG) {
      timeout = 0;
    } else {
      timeout = INFINITE;
    }

    if (pid == -1) {
      int count = 0;
      DWORD ret;
      HANDLE events[MAXCHILDNUM + 1];

      FOREACH_CHILD(child) {
          if (!child->pid || child->pid < 0) continue;
          if ((pid = poll_child_status(child, stat_loc))) return pid;
          events[count++] = child->hProcess;
      } END_FOREACH_CHILD;
      if (!count) {
          errno = ECHILD;
          return -1;
      }
      events[count] = interrupted_event;

      ret = WaitForMultipleEvents(count + 1, events, FALSE, timeout, TRUE);
      if (ret == WAIT_TIMEOUT) return 0;
      if ((ret -= WAIT_OBJECT_0) == count) {
          ResetSignal(interrupted_event);
          errno = EINTR;
          return -1;
      }
      if (ret > count) {
          errno = map_errno(GetLastError());
          return -1;
      }

      return poll_child_status(ChildRecord + ret, stat_loc);
    }
    else {
      struct ChildRecord* child = FindChildSlot(pid);
      if (!child) {
          errno = ECHILD;
          return -1;
      }

      while (!(pid = poll_child_status(child, stat_loc))) {
          /* wait... */
          if (wait_events(child->hProcess, timeout) != WAIT_OBJECT_0) {
            /* still active */
            pid = 0;
            break;
          }
      }
    }

    return pid;
}

#include <sys/timeb.h>

int _cdecl
gettimeofday(struct timeval *tv, struct timezone *tz)
{
    SYSTEMTIME st;
    time_t t;
    struct tm tm;

    GetLocalTime(&st);
    tm.tm_sec = st.wSecond;
    tm.tm_min = st.wMinute;
    tm.tm_hour = st.wHour;
    tm.tm_mday = st.wDay;
    tm.tm_mon = st.wMonth - 1;
    tm.tm_year = st.wYear - 1900;
    tm.tm_isdst = -1;
    t = mktime(&tm);
    tv->tv_sec = t;
    tv->tv_usec = st.wMilliseconds * 1000;

    return 0;
}

char *
rb_w32_getcwd(char *buffer, int size)
{
    int length;
    char *bp;

#undef getcwd
    if (getcwd(buffer, size) == NULL) {
        return NULL;
    }
    length = strlen(buffer);
    if (length >= size) {
        return NULL;
    }

    for (bp = buffer; *bp != '\0'; bp = CharNext(bp)) {
      if (*bp == '\\') {
          *bp = '/';
      }
    }
    return buffer;
}

int
chown(const char *path, int owner, int group)
{
      return 0;
}

int
kill(int pid, int sig)
{
    int ret = 0;
    DWORD err;

    if (pid <= 0) {
      errno = EINVAL;
      return -1;
    }

    if (IsWin95()) pid = -pid;
    if ((unsigned int)pid == GetCurrentProcessId() &&
      (sig != 0 && sig != SIGKILL)) {
      if ((ret = raise(sig)) != 0) {
          /* MSVCRT doesn't set errno... */
          errno = EINVAL;
      }
      return ret;
    }

    switch (sig) {
      case 0:
      RUBY_CRITICAL({
          HANDLE hProc =
            OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
          if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
            if (GetLastError() == ERROR_INVALID_PARAMETER) {
                errno = ESRCH;
            }
            else {
                errno = EPERM;
            }
            ret = -1;
          }
          else {
            CloseHandle(hProc);
          }
      });
      break;

      case SIGINT:
      RUBY_CRITICAL({
          if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, (DWORD)pid)) {
            if ((err = GetLastError()) == 0)
                errno = EPERM;
            else
                errno = map_errno(GetLastError());
            ret = -1;
          }
      });
      break;

      case SIGKILL:
      RUBY_CRITICAL({
          HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD)pid);
          if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
            if (GetLastError() == ERROR_INVALID_PARAMETER) {
                errno = ESRCH;
            }
            else {
                errno = EPERM;
            }
            ret = -1;
          }
          else {
            if (!TerminateProcess(hProc, 0)) {
                errno = EPERM;
                ret = -1;
            }
            CloseHandle(hProc);
          }
      });
      break;

      default:
      errno = EINVAL;
      ret = -1;
      break;
    }

    return ret;
}

int
link(char *from, char *to)
{
    static BOOL (WINAPI *pCreateHardLink)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES) = NULL;
    static int myerrno = 0;

    if (!pCreateHardLink && !myerrno) {
      HANDLE hKernel;

      hKernel = GetModuleHandle("kernel32.dll");
      if (hKernel) {
          pCreateHardLink = (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES))GetProcAddress(hKernel, "CreateHardLinkA");
          if (!pCreateHardLink) {
            myerrno = map_errno(GetLastError());
          }
          CloseHandle(hKernel);
      }
      else {
          myerrno = map_errno(GetLastError());
      }
    }
    if (!pCreateHardLink) {
      errno = myerrno;
      return -1;
    }

    if (!pCreateHardLink(to, from, NULL)) {
      errno = map_errno(GetLastError());
      return -1;
    }

    return 0;
}

int
wait()
{
      return 0;
}

char *
rb_w32_getenv(const char *name)
{
    static char *curitem = NULL;
    static DWORD curlen = 0;
    DWORD needlen;

    if (curitem == NULL || curlen == 0) {
      curlen = 512;
      curitem = ALLOC_N(char, curlen);
    }

    needlen = GetEnvironmentVariable(name, curitem, curlen);
    if (needlen != 0) {
      while (needlen > curlen) {
          REALLOC_N(curitem, char, needlen);
          curlen = needlen;
          needlen = GetEnvironmentVariable(name, curitem, curlen);
      }
    }
    else {
      return NULL;
    }

    return curitem;
}

int
rb_w32_rename(const char *oldpath, const char *newpath)
{
    int res = 0;
    int oldatts;
    int newatts;

    oldatts = GetFileAttributes(oldpath);
    newatts = GetFileAttributes(newpath);

    if (oldatts == -1) {
      errno = map_errno(GetLastError());
      return -1;
    }

    RUBY_CRITICAL({
      if (newatts != -1 && newatts & FILE_ATTRIBUTE_READONLY)
          SetFileAttributesA(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);

      if (!MoveFile(oldpath, newpath))
          res = -1;

      if (res) {
          switch (GetLastError()) {
            case ERROR_ALREADY_EXISTS:
            case ERROR_FILE_EXISTS:
            if (IsWinNT()) {
                if (MoveFileEx(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
                  res = 0;
            } else {
                for (;;) {
                  if (!DeleteFile(newpath) && GetLastError() != ERROR_FILE_NOT_FOUND)
                      break;
                  else if (MoveFile(oldpath, newpath)) {
                      res = 0;
                      break;
                  }
                }
            }
          }
      }

      if (res)
          errno = map_errno(GetLastError());
      else
          SetFileAttributes(newpath, oldatts);
    });

    return res;
}

static int
isUNCRoot(const char *path)
{
    if (path[0] == '\\' && path[1] == '\\') {
      const char *p;
      for (p = path + 2; *p; p = CharNext(p)) {
          if (*p == '\\')
            break;
      }
      if (p[0] && p[1]) {
          for (p++; *p; p = CharNext(p)) {
            if (*p == '\\')
                break;
          }
          if (!p[0] || !p[1] || (p[1] == '.' && !p[2]))
            return 1;
      }
    }
    return 0;
}

static time_t
filetime_to_unixtime(const FILETIME *ft)
{
    FILETIME loc;
    SYSTEMTIME st;
    struct tm tm;
    time_t t;

    if (!FileTimeToLocalFileTime(ft, &loc)) {
      return 0;
    }
    if (!FileTimeToSystemTime(&loc, &st)) {
      return 0;
    }
    memset(&tm, 0, sizeof(tm));
    tm.tm_year = st.wYear - 1900;
    tm.tm_mon = st.wMonth - 1;
    tm.tm_mday = st.wDay;
    tm.tm_hour = st.wHour;
    tm.tm_min = st.wMinute;
    tm.tm_sec = st.wSecond;
    t = mktime(&tm);
    return t == -1 ? 0 : t;
}

static unsigned
fileattr_to_unixmode(DWORD attr, const char *path)
{
    unsigned mode = 0;

    if (attr & FILE_ATTRIBUTE_READONLY) {
      mode |= S_IREAD;
    }
    else {
      mode |= S_IREAD | S_IWRITE | S_IWUSR;
    }

    if (attr & FILE_ATTRIBUTE_DIRECTORY) {
      mode |= S_IFDIR | S_IEXEC;
    }
    else {
      mode |= S_IFREG;
    }

    if (path && (mode & S_IFREG)) {
      const char *end = path + strlen(path);
      while (path < end) {
          end = CharPrev(path, end);
          if (*end == '.') {
            if ((strcmpi(end, ".bat") == 0) ||
                (strcmpi(end, ".cmd") == 0) ||
                (strcmpi(end, ".com") == 0) ||
                (strcmpi(end, ".exe") == 0)) {
                mode |= S_IEXEC;
            }
            break;
          }
      }
    }

    mode |= (mode & 0700) >> 3;
    mode |= (mode & 0700) >> 6;

    return mode;
}

static int
winnt_stat(const char *path, struct stat *st)
{
    HANDLE h;
    WIN32_FIND_DATA wfd;

    memset(st, 0, sizeof(struct stat));
    st->st_nlink = 1;

    if (_mbspbrk(path, "?*")) {
      errno = ENOENT;
      return -1;
    }
    h = FindFirstFile(path, &wfd);
    if (h != INVALID_HANDLE_VALUE) {
      FindClose(h);
      st->st_mode  = fileattr_to_unixmode(wfd.dwFileAttributes, path);
      st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
      st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
      st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
      st->st_size  = wfd.nFileSizeLow; /* TODO: 64bit support */
    }
    else {
      // If runtime stat(2) is called for network shares, it fails on WinNT.
      // Because GetDriveType returns 1 for network shares. (Win98 returns 4)
      DWORD attr = GetFileAttributes(path);
      if (attr == -1) {
          errno = map_errno(GetLastError());
          return -1;
      }
      st->st_mode  = fileattr_to_unixmode(attr, path);
    }

    st->st_dev = st->st_rdev = (isalpha(path[0]) && path[1] == ':') ?
      toupper(path[0]) - 'A' : _getdrive() - 1;

    return 0;
}

int
rb_w32_stat(const char *path, struct stat *st)
{
    const char *p;
    char *buf1, *s, *end;
    int len;
    int ret;

    if (!path || !st) {
      errno = EFAULT;
      return -1;
    }
    buf1 = ALLOCA_N(char, strlen(path) + 2);
    for (p = path, s = buf1; *p; p++, s++) {
      if (*p == '/')
          *s = '\\';
      else
          *s = *p;
    }
    *s = '\0';
    len = s - buf1;
    if (!len || '\"' == *(--s)) {
      errno = ENOENT;
      return -1;
    }
    end = CharPrev(buf1, buf1 + len);

    if (isUNCRoot(buf1)) {
      if (*end == '.')
          *end = '\0';
      else if (*end != '\\')
          strcat(buf1, "\\");
    } else if (*end == '\\' || (buf1 + 1 == end && *end == ':'))
      strcat(buf1, ".");

    ret = IsWinNT() ? winnt_stat(buf1, st) : stat(buf1, st);
    if (ret == 0) {
      st->st_mode &= ~(S_IWGRP | S_IWOTH);
    }
    return ret;
}

static long
filetime_to_clock(FILETIME *ft)
{
    __int64 qw = ft->dwHighDateTime;
    qw <<= 32;
    qw |= ft->dwLowDateTime;
    qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
    return (long) qw;
}

int
rb_w32_times(struct tms *tmbuf)
{
    FILETIME create, exit, kernel, user;

    if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
      tmbuf->tms_utime = filetime_to_clock(&user);
      tmbuf->tms_stime = filetime_to_clock(&kernel);
      tmbuf->tms_cutime = 0;
      tmbuf->tms_cstime = 0;
    }
    else {
      tmbuf->tms_utime = clock();
      tmbuf->tms_stime = 0;
      tmbuf->tms_cutime = 0;
      tmbuf->tms_cstime = 0;
    }
    return 0;
}

#undef Sleep
#define yield_once() Sleep(0)
#define yield_until(condition) do yield_once(); while (!(condition))

static DWORD
wait_events(HANDLE event, DWORD timeout)
{
    HANDLE events[2];
    int count = 0;
    DWORD ret;

    if (event) {
      events[count++] = event;
    }
    events[count++] = interrupted_event;

    ret = WaitForMultipleEvents(count, events, FALSE, timeout, TRUE);

    if (ret == WAIT_OBJECT_0 + count - 1) {
      ResetSignal(interrupted_event);
      errno = EINTR;
    }

    return ret;
}

static CRITICAL_SECTION *
system_state(void)
{
    static int initialized = 0;
    static CRITICAL_SECTION syssect;

    if (!initialized) {
      InitializeCriticalSection(&syssect);
      initialized = 1;
    }
    return &syssect;
}

static LONG flag_interrupt = -1;
static volatile DWORD tlsi_interrupt = TLS_OUT_OF_INDEXES;

void
rb_w32_enter_critical(void)
{
    if (IsWinNT()) {
      EnterCriticalSection(system_state());
      return;
    }

    if (tlsi_interrupt == TLS_OUT_OF_INDEXES) {
      tlsi_interrupt = TlsAlloc();
    }

    {
      DWORD ti = (DWORD)TlsGetValue(tlsi_interrupt);
      while (InterlockedIncrement(&flag_interrupt) > 0 && !ti) {
          InterlockedDecrement(&flag_interrupt);
          Sleep(1);
      }
      TlsSetValue(tlsi_interrupt, (PVOID)++ti);
    }
}

void
rb_w32_leave_critical(void)
{
    if (IsWinNT()) {
      LeaveCriticalSection(system_state());
      return;
    }

    InterlockedDecrement(&flag_interrupt);
    TlsSetValue(tlsi_interrupt, (PVOID)((DWORD)TlsGetValue(tlsi_interrupt) - 1));
}

struct handler_arg_t {
    void (*handler)(int);
    int arg;
    int status;
    int finished;
    HANDLE handshake;
};

static void
rb_w32_call_handler(struct handler_arg_t* h)
{
    int status;
    RUBY_CRITICAL(rb_protect((VALUE (*)(VALUE))h->handler, (VALUE)h->arg, &h->status);
              status = h->status;
              SetEvent(h->handshake));
    if (status) {
      rb_jump_tag(status);
    }
    h->finished = 1;
    yield_until(0);
}

static struct handler_arg_t *
setup_handler(struct handler_arg_t *harg, int arg, void (*handler)(int),
            HANDLE handshake)
{
    harg->handler = handler;
    harg->arg = arg;
    harg->status = 0;
    harg->finished = 0;
    harg->handshake = handshake;
    return harg;
}

static void
setup_call(CONTEXT* ctx, struct handler_arg_t *harg)
{
#ifdef _M_IX86
    DWORD *esp = (DWORD *)ctx->Esp;
    *--esp = (DWORD)harg;
    *--esp = ctx->Eip;
    ctx->Esp = (DWORD)esp;
    ctx->Eip = (DWORD)rb_w32_call_handler;
#else
#ifndef _WIN32_WCE
#error unsupported processor
#endif
#endif
}

int
rb_w32_main_context(int arg, void (*handler)(int))
{
    static HANDLE interrupt_done = NULL;
    struct handler_arg_t harg;
    CONTEXT ctx_orig;
    HANDLE current_thread = GetCurrentThread();
    int old_priority = GetThreadPriority(current_thread);

    if (GetCurrentThreadId() == main_thread.id) return FALSE;

    SetSignal(interrupted_event);

    RUBY_CRITICAL({           /* the main thread must be in user state */
      CONTEXT ctx;

      SuspendThread(main_thread.handle);
      SetThreadPriority(current_thread, GetThreadPriority(main_thread.handle));

      ZeroMemory(&ctx, sizeof(CONTEXT));
      ctx.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
      GetThreadContext(main_thread.handle, &ctx);
      ctx_orig = ctx;

      /* handler context setup */
      if (!interrupt_done) {
          interrupt_done = CreateEvent(NULL, FALSE, FALSE, NULL);
          /* anonymous one-shot event */
      }
      else {
          ResetEvent(interrupt_done);
      }
      setup_call(&ctx, setup_handler(&harg, arg, handler, interrupt_done));

      ctx.ContextFlags = CONTEXT_CONTROL;
      SetThreadContext(main_thread.handle, &ctx);
      ResumeThread(main_thread.handle);
    });

    /* give a chance to the main thread */
    yield_once();
    WaitForSingleObject(interrupt_done, INFINITE); /* handshaking */

    if (!harg.status) {
      /* no exceptions raised, restore old context. */
      RUBY_CRITICAL({
          /* ensure the main thread is in user state. */
          yield_until(harg.finished);

          SuspendThread(main_thread.handle);
          ctx_orig.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
          SetThreadContext(main_thread.handle, &ctx_orig);
          ResumeThread(main_thread.handle);
      });
    }
    /* otherwise leave the main thread raised */

    SetThreadPriority(current_thread, old_priority);

    return TRUE;
}

int
rb_w32_sleep(unsigned long msec)
{
    DWORD ret;
    RUBY_CRITICAL(ret = wait_events(NULL, msec));
    yield_once();
    CHECK_INTS;
    return ret != WAIT_TIMEOUT;
}

static void
catch_interrupt(void)
{
    yield_once();
    RUBY_CRITICAL(wait_events(NULL, 0));
    CHECK_INTS;
}

#undef fgetc
int
rb_w32_getc(FILE* stream)
{
    int c, trap_immediate = rb_trap_immediate;
#ifndef _WIN32_WCE
    if (enough_to_get(stream->FILE_COUNT)) {
      c = (unsigned char)*stream->FILE_READPTR++;
      rb_trap_immediate = trap_immediate;
    }
    else 
#endif
    {
      c = _filbuf(stream);
#if defined __BORLANDC__ || defined _WIN32_WCE
        if ((c == EOF) && (errno == EPIPE)) {
          clearerr(stream);
        }
#endif
      rb_trap_immediate = trap_immediate;
      catch_interrupt();
    }
    return c;
}

#undef fputc
int
rb_w32_putc(int c, FILE* stream)
{
    int trap_immediate = rb_trap_immediate;
#ifndef _WIN32_WCE
    if (enough_to_put(stream->FILE_COUNT)) {
      c = (unsigned char)(*stream->FILE_READPTR++ = (char)c);
      rb_trap_immediate = trap_immediate;
    }
    else 
#endif
    {
      c = _flsbuf(c, stream);
      rb_trap_immediate = trap_immediate;
      catch_interrupt();
    }
    return c;
}

struct asynchronous_arg_t {
    /* output field */
    void* stackaddr;
    int errnum;

    /* input field */
    VALUE (*func)(VALUE self, int argc, VALUE* argv);
    VALUE self;
    int argc;
    VALUE* argv;
};

static DWORD WINAPI
call_asynchronous(PVOID argp)
{
    DWORD ret;
    struct asynchronous_arg_t *arg = argp;
    arg->stackaddr = &argp;
    ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
    arg->errnum = errno;
    return ret;
}

VALUE
rb_w32_asynchronize(asynchronous_func_t func, VALUE self,
                int argc, VALUE* argv, VALUE intrval)
{
    DWORD val;
    BOOL interrupted = FALSE;
    HANDLE thr;

    RUBY_CRITICAL({
      struct asynchronous_arg_t arg;

      arg.stackaddr = NULL;
      arg.errnum = 0;
      arg.func = func;
      arg.self = self;
      arg.argc = argc;
      arg.argv = argv;

      thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);

      if (thr) {
          yield_until(arg.stackaddr);

          if (wait_events(thr, INFINITE) != WAIT_OBJECT_0) {
            interrupted = TRUE;

            if (TerminateThread(thr, intrval)) {
                yield_once();
            }
          }

          GetExitCodeThread(thr, &val);
          CloseHandle(thr);

          if (interrupted) {
            /* must release stack of killed thread, why doesn't Windows? */
            MEMORY_BASIC_INFORMATION m;

            memset(&m, 0, sizeof(m));
            if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
                Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
                          arg.stackaddr, GetLastError()));
            }
            else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
                Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
                          m.AllocationBase, GetLastError()));
            }
            errno = EINTR;
          }
          else {
            errno = arg.errnum;
          }
      }
    });

    if (!thr) {
      rb_fatal("failed to launch waiter thread:%d", GetLastError());
    }

    if (interrupted) {
      CHECK_INTS;
    }

    return val;
}

char **
rb_w32_get_environ(void)
{
    char *envtop, *env;
    char **myenvtop, **myenv;
    int num;

    /*
     * We avoid values started with `='. If you want to deal those values,
     * change this function, and some functions in hash.c which recognize
     * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
     * CygWin deals these values by changing first `=' to '!'. But we don't
     * use such trick and follow cmd.exe's way that just doesn't show these
     * values.
     * (U.N. 2001-11-15)
     */
    envtop = GetEnvironmentStrings();
    for (env = envtop, num = 0; *env; env += strlen(env) + 1)
      if (*env != '=') num++;

    myenvtop = ALLOC_N(char*, num + 1);
    for (env = envtop, myenv = myenvtop; *env; env += strlen(env) + 1) {
      if (*env != '=') {
          *myenv = ALLOC_N(char, strlen(env) + 1);
          strcpy(*myenv, env);
          myenv++;
      }
    }
    *myenv = NULL;
    FreeEnvironmentStrings(envtop);

    return myenvtop;
}

void
rb_w32_free_environ(char **env)
{
    char **t = env;

    while (*t) free(*t++);
    free(env);
}

#undef getpid
rb_pid_t
rb_w32_getpid(void)
{
    rb_pid_t pid;

    pid = getpid();

    if (IsWin95()) pid = -pid;

    return pid;
}

int
rb_w32_fclose(FILE *fp)
{
    int fd = fileno(fp);
    SOCKET sock = TO_SOCKET(fd);

    if (fflush(fp)) return -1;
    if (!is_socket(sock)) {
      UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
      return fclose(fp);
    }
    _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
    fclose(fp);
    if (closesocket(sock) == SOCKET_ERROR) {
      errno = map_errno(WSAGetLastError());
      return -1;
    }
    return 0;
}

int
rb_w32_close(int fd)
{
    SOCKET sock = TO_SOCKET(fd);

    if (!is_socket(sock)) {
      UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
      return _close(fd);
    }
    _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
    _close(fd);
    if (closesocket(sock) == SOCKET_ERROR) {
      errno = map_errno(WSAGetLastError());
      return -1;
    }
    return 0;
}

static int
unixtime_to_filetime(time_t time, FILETIME *ft)
{
    struct tm *tm;
    SYSTEMTIME st;
    FILETIME lt;

    tm = localtime(&time);
    st.wYear = tm->tm_year + 1900;
    st.wMonth = tm->tm_mon + 1;
    st.wDayOfWeek = tm->tm_wday;
    st.wDay = tm->tm_mday;
    st.wHour = tm->tm_hour;
    st.wMinute = tm->tm_min;
    st.wSecond = tm->tm_sec;
    st.wMilliseconds = 0;
    if (!SystemTimeToFileTime(&st, &lt) ||
      !LocalFileTimeToFileTime(&lt, ft)) {
      errno = map_errno(GetLastError());
      return -1;
    }
    return 0;
}

int
rb_w32_utime(const char *path, struct utimbuf *times)
{
    HANDLE hFile;
    SYSTEMTIME st;
    FILETIME atime, mtime;
    struct tm *tm;
    struct stat stat;
    int ret = 0;

    if (rb_w32_stat(path, &stat)) {
      return -1;
    }

    if (unixtime_to_filetime(times->actime, &atime)) {
      return -1;
    }
    if (unixtime_to_filetime(times->modtime, &mtime)) {
      return -1;
    }

    RUBY_CRITICAL({
      hFile = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
                     IsWin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, 0);
      if (hFile == INVALID_HANDLE_VALUE) {
          errno = map_errno(GetLastError());
          ret = -1;
      }
      else {
          if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
            errno = map_errno(GetLastError());
            ret = -1;
          }
          CloseHandle(hFile);
      }
    });

    return ret;
}

int
rb_w32_vsnprintf(char *buf, size_t size, const char *format, va_list va)
{
    int ret = _vsnprintf(buf, size, format, va);
    if (size > 0) buf[size - 1] = 0;
    return ret;
}

int
rb_w32_snprintf(char *buf, size_t size, const char *format, ...)
{
    int ret;
    va_list va;

    va_start(va, format);
    ret = vsnprintf(buf, size, format, va);
    va_end(va);
    return ret;
}

int
rb_w32_mkdir(const char *path, int mode)
{
    int ret = -1;
    RUBY_CRITICAL(do {
      if (CreateDirectory(path, NULL) == FALSE) {
          errno = map_errno(GetLastError());
          break;
      }
      if (chmod(path, mode) == -1) {
          RemoveDirectory(path);
          break;
      }
      ret = 0;
    } while (0));
    return ret;
}

int
rb_w32_rmdir(const char *path)
{
    int ret = 0;
    RUBY_CRITICAL({
      const DWORD attr = GetFileAttributes(path);
      if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
          SetFileAttributes(path, attr & ~FILE_ATTRIBUTE_READONLY);
      }
      if (RemoveDirectory(path) == FALSE) {
          errno = map_errno(GetLastError());
          ret = -1;
          if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
            SetFileAttributes(path, attr);
          }
      }
    });
    return ret;
}

int
rb_w32_unlink(const char *path)
{
    int ret = 0;
    RUBY_CRITICAL({
      const DWORD attr = GetFileAttributes(path);
      if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
          SetFileAttributes(path, attr & ~FILE_ATTRIBUTE_READONLY);
      }
      if (DeleteFile(path) == FALSE) {
          errno = map_errno(GetLastError());
          ret = -1;
          if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
            SetFileAttributes(path, attr);
          }
      }
    });
    return ret;
}

#if !defined(__BORLANDC__) && !defined(_WIN32_WCE)
int
rb_w32_isatty(int fd)
{
    if (!(_osfile(fd) & FOPEN)) {
      errno = EBADF;
      return 0;
    }
    if (!(_osfile(fd) & FDEV)) {
      errno = ENOTTY;
      return 0;
    }
    return 1;
}
#endif

//
// Fix bcc32's stdio bug
//

#ifdef __BORLANDC__
static int
too_many_files()
{
    FILE *f;
    for (f = _streams; f < _streams + _nfile; f++) {
      if (f->fd < 0) return 0;
    }
    return 1;
}

#undef fopen
FILE *
rb_w32_fopen(const char *path, const char *mode)
{
    FILE *f = (errno = 0, fopen(path, mode));
    if (f == NULL && errno == 0) {
      if (too_many_files())
          errno = EMFILE;
    }
    return f;
}

FILE *
rb_w32_fdopen(int handle, const char *type)
{
    FILE *f = (errno = 0, _fdopen(handle, (char *)type));
    if (f == NULL && errno == 0) {
      if (handle < 0)
          errno = EBADF;
      else if (too_many_files())
          errno = EMFILE;
    }
    return f;
}

FILE *
rb_w32_fsopen(const char *path, const char *mode, int shflags)
{
    FILE *f = (errno = 0, _fsopen(path, mode, shflags));
    if (f == NULL && errno == 0) {
      if (too_many_files())
          errno = EMFILE;
    }
    return f;
}
#endif

Generated by  Doxygen 1.6.0   Back to index