win32/win32.c


DEFINITIONS

This source file includes following functions.
  1. CreateSignal
  2. SetSignal
  3. ResetSignal
  4. CreateSignal
  5. SetSignal
  6. ResetSignal
  7. IdOS
  8. IsWin95
  9. IsWinNT
  10. IsWinNT
  11. IsWin95
  12. GetCurrentThreadHandle
  13. flock_winnt
  14. flock_win95
  15. flock
  16. NtInitialize
  17. getlogin
  18. FindFirstChildSlot
  19. FindChildSlot
  20. CloseChildHandle
  21. FindFreeChildSlot
  22. SafeFree
  23. isInternalCmd
  24. rb_w32_get_osfhandle
  25. pipe_exec
  26. do_spawn
  27. CreateChild
  28. NtFreeCmdLine
  29. insert
  30. NtCmdGlob
  31. NtHasRedirection
  32. NtMakeCmdVector
  33. rb_w32_opendir
  34. rb_w32_readdir
  35. rb_w32_telldir
  36. rb_w32_seekdir
  37. rb_w32_rewinddir
  38. rb_w32_closedir
  39. valid_filename
  40. MTHREAD_ONLY
  41. STHREAD_ONLY
  42. MTHREAD_ONLY
  43. STHREAD_ONLY
  44. MTHREAD_ONLY
  45. STHREAD_ONLY
  46. rb_w32_open_osfhandle
  47. is_socket
  48. rb_w32_fddup
  49. rb_w32_fdclose
  50. rb_w32_strerror
  51. getuid
  52. geteuid
  53. getgid
  54. getegid
  55. setuid
  56. setgid
  57. ioctl
  58. rb_w32_fdset
  59. rb_w32_fdclr
  60. rb_w32_fdisset
  61. extract_file_fd
  62. rb_w32_select
  63. StartSockets
  64. rb_w32_accept
  65. rb_w32_bind
  66. rb_w32_connect
  67. rb_w32_getpeername
  68. rb_w32_getsockname
  69. rb_w32_getsockopt
  70. rb_w32_ioctlsocket
  71. rb_w32_listen
  72. rb_w32_recv
  73. rb_w32_recvfrom
  74. rb_w32_send
  75. rb_w32_sendto
  76. rb_w32_setsockopt
  77. rb_w32_shutdown
  78. rb_w32_socket
  79. rb_w32_gethostbyaddr
  80. rb_w32_gethostbyname
  81. rb_w32_gethostname
  82. rb_w32_getprotobyname
  83. rb_w32_getprotobynumber
  84. rb_w32_getservbyname
  85. rb_w32_getservbyport
  86. endhostent
  87. endnetent
  88. endprotoent
  89. endservent
  90. getnetent
  91. getnetbyaddr
  92. getnetbyname
  93. getprotoent
  94. getservent
  95. sethostent
  96. setnetent
  97. setprotoent
  98. setservent
  99. poll_child_status
  100. waitpid
  101. gettimeofday
  102. rb_w32_getcwd
  103. str_grow
  104. chown
  105. kill
  106. link
  107. wait
  108. rb_w32_getenv
  109. rb_w32_rename
  110. isUNCRoot
  111. rb_w32_stat
  112. filetime_to_clock
  113. rb_w32_times
  114. wait_events
  115. system_state
  116. rb_w32_enter_critical
  117. rb_w32_leave_critical
  118. rb_w32_call_handler
  119. setup_handler
  120. setup_call
  121. rb_w32_main_context
  122. rb_w32_sleep
  123. catch_interrupt
  124. rb_w32_getc
  125. rb_w32_putc
  126. call_asynchronous
  127. rb_w32_asynchronize
  128. rb_w32_get_environ
  129. rb_w32_free_environ
  130. rb_w32_getpid


   1  /*
   2   *  Copyright (c) 1993, Intergraph Corporation
   3   *
   4   *  You may distribute under the terms of either the GNU General Public
   5   *  License or the Artistic License, as specified in the perl README file.
   6   *
   7   *  Various Unix compatibility functions and NT specific functions.
   8   *
   9   *  Some of this code was derived from the MSDOS port(s) and the OS/2 port.
  10   *
  11   */
  12  
  13  #include "ruby.h"
  14  #include "rubysig.h"
  15  #include <fcntl.h>
  16  #include <process.h>
  17  #include <sys/stat.h>
  18  /* #include <sys/wait.h> */
  19  #include <stdio.h>
  20  #include <stdlib.h>
  21  #include <errno.h>
  22  #include <assert.h>
  23  
  24  #include <windows.h>
  25  #include <winbase.h>
  26  #include <wincon.h>
  27  #ifdef __MINGW32__
  28  #include <mswsock.h>
  29  #endif
  30  #include "win32.h"
  31  #include "win32/dir.h"
  32  #ifndef index
  33  #define index(x, y) strchr((x), (y))
  34  #endif
  35  #define isdirsep(x) ((x) == '/' || (x) == '\\')
  36  #undef stat
  37  
  38  #ifndef bool
  39  #define bool int
  40  #endif
  41  
  42  #ifdef _M_IX86
  43  # define WIN95 1
  44  #else
  45  # undef  WIN95
  46  #endif
  47  
  48  #ifdef __BORLANDC__
  49  #  define _filbuf _fgetc
  50  #  define _flsbuf fputc
  51  #endif
  52  
  53  #if HAVE_WSAWAITFORMULTIPLEEVENTS
  54  # define USE_INTERRUPT_WINSOCK
  55  #endif
  56  
  57  #if USE_INTERRUPT_WINSOCK
  58  # define WaitForMultipleEvents WSAWaitForMultipleEvents
  59  # define CreateSignal() (HANDLE)WSACreateEvent()
  60  # define SetSignal(ev) WSASetEvent(ev)
  61  # define ResetSignal(ev) WSAResetEvent(ev)
  62  #else  /* USE_INTERRUPT_WINSOCK */
  63  # define WaitForMultipleEvents WaitForMultipleObjectsEx
  64  # define CreateSignal() CreateEvent(NULL, FALSE, FALSE, NULL);
  65  # define SetSignal(ev) SetEvent(ev)
  66  # define ResetSignal(ev) (void)0
  67  #endif /* USE_INTERRUPT_WINSOCK */
  68  
  69  #ifdef WIN32_DEBUG
  70  #define Debug(something) something
  71  #else
  72  #define Debug(something) /* nothing */
  73  #endif
  74  
  75  #define TO_SOCKET(x)    _get_osfhandle(x)
  76  
  77  bool NtSyncProcess = TRUE;
  78  
  79  static struct ChildRecord *CreateChild(char *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE);
  80  static bool NtHasRedirection (char *);
  81  static int valid_filename(char *s);
  82  static void StartSockets ();
  83  static char *str_grow(struct RString *str, size_t new_size);
  84  static DWORD wait_events(HANDLE event, DWORD timeout);
  85  #ifndef __BORLANDC__
  86  static int rb_w32_open_osfhandle(long osfhandle, int flags);
  87  #endif
  88  
  89  char *NTLoginName;
  90  
  91  #ifdef WIN95
  92  DWORD Win32System = (DWORD)-1;
  93  
  94  static DWORD
  95  IdOS(void)
  96  {
  97      static OSVERSIONINFO osver;
  98  
  99      if (osver.dwPlatformId != Win32System) {
 100          memset(&osver, 0, sizeof(OSVERSIONINFO));
 101          osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 102          GetVersionEx(&osver);
 103          Win32System = osver.dwPlatformId;
 104      }
 105      return (Win32System);
 106  }
 107  
 108  static int 
 109  IsWin95(void) {
 110      return (IdOS() == VER_PLATFORM_WIN32_WINDOWS);
 111  }
 112  
 113  static int
 114  IsWinNT(void) {
 115      return (IdOS() == VER_PLATFORM_WIN32_NT);
 116  }
 117  #else
 118  # define IsWinNT() TRUE
 119  # define IsWin95() FALSE
 120  #endif
 121  
 122  /* main thread constants */
 123  static struct {
 124      HANDLE handle;
 125      DWORD id;
 126  } main_thread;
 127  
 128  /* interrupt stuff */
 129  static HANDLE interrupted_event;
 130  
 131  HANDLE GetCurrentThreadHandle(void)
 132  {
 133      static HANDLE current_process_handle = NULL;
 134      HANDLE h;
 135  
 136      if (!current_process_handle)
 137          current_process_handle = GetCurrentProcess();
 138      if (!DuplicateHandle(current_process_handle, GetCurrentThread(),
 139                           current_process_handle, &h,
 140                           0, FALSE, DUPLICATE_SAME_ACCESS))
 141          return NULL;
 142      return h;
 143  }
 144  
 145  /* simulate flock by locking a range on the file */
 146  
 147  
 148  #define LK_ERR(f,i) ((f) ? (i = 0) : (errno = GetLastError()))
 149  #define LK_LEN      0xffff0000
 150  
 151  static VALUE
 152  flock_winnt(VALUE self, int argc, VALUE* argv)
 153  {
 154      OVERLAPPED o;
 155      int i = -1;
 156      const HANDLE fh = (HANDLE)self;
 157      const int oper = argc;
 158  
 159      memset(&o, 0, sizeof(o));
 160  
 161      switch(oper) {
 162        case LOCK_SH:             /* shared lock */
 163          LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o), i);
 164          break;
 165        case LOCK_EX:             /* exclusive lock */
 166          LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o), i);
 167          break;
 168        case LOCK_SH|LOCK_NB:     /* non-blocking shared lock */
 169          LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o), i);
 170          break;
 171        case LOCK_EX|LOCK_NB:     /* non-blocking exclusive lock */
 172          LK_ERR(LockFileEx(fh,
 173                            LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
 174                            0, LK_LEN, 0, &o), i);
 175          if (errno == EDOM)
 176              errno = EWOULDBLOCK;
 177          break;
 178        case LOCK_UN:             /* unlock lock */
 179          if (UnlockFileEx(fh, 0, LK_LEN, 0, &o)) {
 180              i = 0;
 181              if (errno == EDOM)
 182                  errno = EWOULDBLOCK;
 183          }
 184          else {
 185              /* GetLastError() must returns `ERROR_NOT_LOCKED' */
 186              errno = EWOULDBLOCK;
 187          }
 188          break;
 189        default:            /* unknown */
 190          errno = EINVAL;
 191          break;
 192      }
 193      return i;
 194  }
 195  
 196  #ifdef WIN95
 197  static VALUE
 198  flock_win95(VALUE self, int argc, VALUE* argv)
 199  {
 200      int i = -1;
 201      const HANDLE fh = (HANDLE)self;
 202      const int oper = argc;
 203  
 204      switch(oper) {
 205        case LOCK_EX:
 206          while(i == -1) {
 207              LK_ERR(LockFile(fh, 0, 0, LK_LEN, 0), i);
 208              if (errno != EDOM && i == -1) break;
 209          }
 210          break;
 211        case LOCK_EX | LOCK_NB:
 212          LK_ERR(LockFile(fh, 0, 0, LK_LEN, 0), i);
 213          if (errno == EDOM)
 214              errno = EWOULDBLOCK;
 215          break;
 216        case LOCK_UN:
 217          LK_ERR(UnlockFile(fh, 0, 0, LK_LEN, 0), i);
 218          if (errno == EDOM)
 219              errno = EWOULDBLOCK;
 220          break;
 221        default:
 222          errno = EINVAL;
 223          break;
 224      }
 225      return i;
 226  }
 227  #endif
 228  
 229  #undef LK_ERR
 230  #undef LK_LEN
 231  
 232  int
 233  flock(int fd, int oper)
 234  {
 235  #ifdef WIN95
 236      static asynchronous_func_t locker = NULL;
 237  
 238      if (!locker) {
 239          if (IsWinNT())
 240              locker = flock_winnt;
 241          else
 242              locker = flock_win95;
 243      }
 244  #else
 245      const asynchronous_func_t locker = flock_winnt;
 246  #endif
 247  
 248      return rb_w32_asynchronize(locker,
 249                                (VALUE)_get_osfhandle(fd), oper, NULL,
 250                                (DWORD)-1);
 251  }
 252  
 253  //#undef const
 254  //FILE *fdopen(int, const char *);
 255  
 256  //
 257  // Initialization stuff
 258  //
 259  void
 260  NtInitialize(int *argc, char ***argv)
 261  {
 262  
 263      WORD version;
 264      int ret;
 265  
 266      //
 267      // subvert cmd.exe's feeble attempt at command line parsing
 268      //
 269      *argc = NtMakeCmdVector((char *)GetCommandLine(), argv, TRUE);
 270  
 271      //
 272      // Now set up the correct time stuff
 273      //
 274  
 275      tzset();
 276  
 277      // Initialize Winsock
 278      StartSockets();
 279  }
 280  
 281  char *getlogin()
 282  {
 283      char buffer[200];
 284      DWORD len = 200;
 285      extern char *NTLoginName;
 286  
 287      if (NTLoginName == NULL) {
 288          if (GetUserName(buffer, &len)) {
 289              NTLoginName = ALLOC_N(char, len+1);
 290              strncpy(NTLoginName, buffer, len);
 291              NTLoginName[len] = '\0';
 292          }
 293          else {
 294              NTLoginName = "<Unknown>";
 295          }
 296      }
 297      return NTLoginName;
 298  }
 299  
 300  #define MAXCHILDNUM 256 /* max num of child processes */
 301  
 302  struct ChildRecord {
 303      HANDLE hProcess;    /* process handle */
 304      pid_t pid;          /* process id */
 305  } ChildRecord[MAXCHILDNUM];
 306  
 307  #define FOREACH_CHILD(v) do { \
 308      struct ChildRecord* v; \
 309      for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
 310  #define END_FOREACH_CHILD } while (0)
 311  
 312  static struct ChildRecord *
 313  FindFirstChildSlot(void)
 314  {
 315      FOREACH_CHILD(child) {
 316          if (child->pid) return child;
 317      } END_FOREACH_CHILD;
 318      return NULL;
 319  }
 320  
 321  static struct ChildRecord *
 322  FindChildSlot(pid_t pid)
 323  {
 324  
 325      FOREACH_CHILD(child) {
 326          if (child->pid == pid) {
 327              return child;
 328          }
 329      } END_FOREACH_CHILD;
 330      return NULL;
 331  }
 332  
 333  static void
 334  CloseChildHandle(struct ChildRecord *child)
 335  {
 336      HANDLE h = child->hProcess;
 337      child->hProcess = NULL;
 338      child->pid = 0;
 339      CloseHandle(h);
 340  }
 341  
 342  static struct ChildRecord *
 343  FindFreeChildSlot(void)
 344  {
 345      FOREACH_CHILD(child) {
 346          if (!child->pid) {
 347              child->pid = -1;    /* lock the slot */
 348              child->hProcess = NULL;
 349              return child;
 350          }
 351      } END_FOREACH_CHILD;
 352      return NULL;
 353  }
 354  
 355  
 356  int SafeFree(char **vec, int vecc)
 357  {
 358      //   vec
 359      //   |
 360      //   V       ^---------------------V
 361      //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 362      //   |   |       | ....  |  NULL |   | ..... |\0 |   | ..... |\0 |...
 363      //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 364      //   |-  elements+1             -| ^ 1st element   ^ 2nd element
 365  
 366          char *p;
 367  
 368          p = (char *)vec;
 369          free(p);
 370  
 371          return 0;
 372  }
 373  
 374  
 375  static char *szInternalCmds[] = {
 376    "append",
 377    "break",
 378    "call",
 379    "cd",
 380    "chdir",
 381    "cls",
 382    "copy",
 383    "date",
 384    "del",
 385    "dir",
 386    "echo",
 387    "erase",
 388    "label",
 389    "md",
 390    "mkdir",
 391    "path",
 392    "pause",
 393    "rd",
 394    "rem",
 395    "ren",
 396    "rename",
 397    "rmdir",
 398    "set",
 399    "start",
 400    "time",
 401    "type",
 402    "ver",
 403    "vol",
 404    NULL
 405  };
 406  
 407  int
 408  isInternalCmd(char *cmd)
 409  {
 410      int i, fRet=0;
 411      char **vec;
 412      int vecc = NtMakeCmdVector(cmd, &vec, FALSE);
 413  
 414      if (vecc == 0)
 415          return 0;
 416      for( i = 0; szInternalCmds[i] ; i++){
 417          if(!strcasecmp(szInternalCmds[i], vec[0])){
 418              fRet = 1;
 419              break;
 420          }
 421      }
 422  
 423      SafeFree(vec, vecc);
 424  
 425      return fRet;
 426  }
 427  
 428  
 429  SOCKET
 430  rb_w32_get_osfhandle(int fh)
 431  {
 432      return _get_osfhandle(fh);
 433  
 434  }
 435  
 436  pid_t
 437  pipe_exec(char *cmd, int mode, FILE **fpr, FILE **fpw)
 438  {
 439      struct ChildRecord* child;
 440      HANDLE hReadIn, hReadOut;
 441      HANDLE hWriteIn, hWriteOut;
 442      HANDLE hSavedStdIn, hSavedStdOut;
 443      HANDLE hDupInFile, hDupOutFile;
 444      HANDLE hCurProc;
 445      SECURITY_ATTRIBUTES sa;
 446      BOOL fRet;
 447      BOOL reading, writing;
 448      int fdin, fdout;
 449      int pipemode;
 450      char modes[3];
 451      int ret;
 452  
 453      /* Figure out what we're doing... */
 454      writing = (mode & (O_WRONLY | O_RDWR)) ? TRUE : FALSE;
 455      reading = ((mode & O_RDWR) || !writing) ? TRUE : FALSE;
 456      pipemode = (mode & O_BINARY) ? O_BINARY : O_TEXT;
 457  
 458      sa.nLength              = sizeof (SECURITY_ATTRIBUTES);
 459      sa.lpSecurityDescriptor = NULL;
 460      sa.bInheritHandle       = TRUE;
 461  
 462      /* create pipe, save parent's STDIN/STDOUT and redirect them for child */
 463      RUBY_CRITICAL(do {
 464          ret = -1;
 465          hCurProc = GetCurrentProcess();
 466          if (reading) {
 467              fRet = CreatePipe(&hReadIn, &hReadOut, &sa, 2048L);
 468              if (!fRet) {
 469                  errno = GetLastError();
 470                  break;
 471              }
 472              hSavedStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
 473              if (!SetStdHandle(STD_OUTPUT_HANDLE, hReadOut) ||
 474                  !DuplicateHandle(hCurProc, hReadIn, hCurProc, &hDupInFile, 0,
 475                                   FALSE, DUPLICATE_SAME_ACCESS)) {
 476                  errno = GetLastError();
 477                  CloseHandle(hReadIn);
 478                  CloseHandle(hReadOut);
 479                  CloseHandle(hCurProc);
 480                  break;
 481              }
 482              CloseHandle(hReadIn);
 483          }
 484          if (writing) {
 485              fRet = CreatePipe(&hWriteIn, &hWriteOut, &sa, 2048L);
 486              if (!fRet) {
 487                  errno = GetLastError();
 488                  if (reading) {
 489                      CloseHandle(hDupInFile);
 490                      CloseHandle(hReadOut);
 491                  }
 492                  break;
 493              }
 494              hSavedStdIn = GetStdHandle(STD_INPUT_HANDLE);
 495              if (!SetStdHandle(STD_INPUT_HANDLE, hWriteIn) ||
 496                  !DuplicateHandle(hCurProc, hWriteOut, hCurProc, &hDupOutFile, 0,
 497                                   FALSE, DUPLICATE_SAME_ACCESS)) {
 498                  errno = GetLastError();
 499                  CloseHandle(hWriteIn);
 500                  CloseHandle(hWriteOut);
 501                  CloseHandle(hCurProc);
 502                  if (reading) {
 503                      CloseHandle(hDupInFile);
 504                      CloseHandle(hReadOut);
 505                  }
 506                  break;
 507              }
 508              CloseHandle(hWriteOut);
 509          }
 510          CloseHandle(hCurProc);
 511          ret = 0;
 512      } while (0));
 513      if (ret != 0) {
 514          return ret;
 515      }
 516  
 517      /* create child process */
 518      child = CreateChild(cmd, &sa, NULL, NULL, NULL);
 519      if (!child) {
 520          RUBY_CRITICAL({
 521              if (reading) {
 522                  SetStdHandle(STD_OUTPUT_HANDLE, hSavedStdOut);
 523                  CloseHandle(hReadOut);
 524                  CloseHandle(hDupInFile);
 525              }
 526              if (writing) {
 527                  SetStdHandle(STD_INPUT_HANDLE, hSavedStdIn);
 528                  CloseHandle(hWriteIn);
 529                  CloseHandle(hDupOutFile);
 530              }
 531          });
 532          return -1;
 533      }
 534  
 535      /* restore STDIN/STDOUT */
 536      RUBY_CRITICAL(do {
 537          ret = -1;
 538          if (reading) {
 539              if (!SetStdHandle(STD_OUTPUT_HANDLE, hSavedStdOut)) {
 540                  errno = GetLastError();
 541                  CloseChildHandle(child);
 542                  CloseHandle(hReadOut);
 543                  CloseHandle(hDupInFile);
 544                  if (writing) {
 545                      CloseHandle(hWriteIn);
 546                      CloseHandle(hDupOutFile);
 547                  }
 548                  break;
 549              }
 550          }
 551          if (writing) {
 552              if (!SetStdHandle(STD_INPUT_HANDLE, hSavedStdIn)) {
 553                  errno = GetLastError();
 554                  CloseChildHandle(child);
 555                  CloseHandle(hWriteIn);
 556                  CloseHandle(hDupOutFile);
 557                  if (reading) {
 558                      CloseHandle(hReadOut);
 559                      CloseHandle(hDupInFile);
 560                  }
 561                  break;
 562              }
 563          }
 564          ret = 0;
 565      } while (0));
 566      if (ret != 0) {
 567          return ret;
 568      }
 569  
 570      if (reading) {
 571  #ifdef __BORLANDC__
 572          fdin = _open_osfhandle((long)hDupInFile, (_O_RDONLY | pipemode));
 573  #else
 574          fdin = rb_w32_open_osfhandle((long)hDupInFile, (_O_RDONLY | pipemode));
 575  #endif
 576          CloseHandle(hReadOut);
 577          if (fdin == -1) {
 578              CloseHandle(hDupInFile);
 579              if (writing) {
 580                  CloseHandle(hWriteIn);
 581                  CloseHandle(hDupOutFile);
 582              }
 583              CloseChildHandle(child);
 584              return -1;
 585          }
 586      }
 587      if (writing) {
 588  #ifdef __BORLANDC__
 589          fdout = _open_osfhandle((long)hDupOutFile, (_O_WRONLY | pipemode));
 590  #else
 591          fdout = rb_w32_open_osfhandle((long)hDupOutFile,
 592                                        (_O_WRONLY | pipemode));
 593  #endif
 594          CloseHandle(hWriteIn);
 595          if (fdout == -1) {
 596              CloseHandle(hDupOutFile);
 597              if (reading) {
 598                  _close(fdin);
 599              }
 600              CloseChildHandle(child);
 601              return -1;
 602          }
 603      }
 604  
 605      if (reading) {
 606          sprintf(modes, "r%s", pipemode == O_BINARY ? "b" : "");
 607          if ((*fpr = (FILE *)fdopen(fdin, modes)) == NULL) {
 608              _close(fdin);
 609              if (writing) {
 610                  _close(fdout);
 611              }
 612              CloseChildHandle(child);
 613              return -1;
 614          }
 615      }
 616      if (writing) {
 617          sprintf(modes, "w%s", pipemode == O_BINARY ? "b" : "");
 618          if ((*fpw = (FILE *)fdopen(fdout, modes)) == NULL) {
 619              _close(fdout);
 620              if (reading) {
 621                  fclose(*fpr);
 622              }
 623              CloseChildHandle(child);
 624              return -1;
 625          }
 626      }
 627  
 628      return child->pid;
 629  }
 630  
 631  extern VALUE rb_last_status;
 632  
 633  int
 634  do_spawn(cmd)
 635  char *cmd;
 636  {
 637      struct ChildRecord *child = CreateChild(cmd, NULL, NULL, NULL, NULL);
 638      if (!child) {
 639          return -1;
 640      }
 641      rb_syswait(child->pid);
 642      return NUM2INT(rb_last_status);
 643  }
 644  
 645  static struct ChildRecord *
 646  CreateChild(char *cmd, SECURITY_ATTRIBUTES *psa, HANDLE hInput, HANDLE hOutput, HANDLE hError)
 647  {
 648      BOOL fRet;
 649      DWORD  dwCreationFlags;
 650      STARTUPINFO aStartupInfo;
 651      PROCESS_INFORMATION aProcessInformation;
 652      SECURITY_ATTRIBUTES sa;
 653      char *shell;
 654      struct ChildRecord *child;
 655  
 656      child = FindFreeChildSlot();
 657      if (!child) {
 658          errno = EAGAIN;
 659          return NULL;
 660      }
 661  
 662      if (!psa) {
 663          sa.nLength              = sizeof (SECURITY_ATTRIBUTES);
 664          sa.lpSecurityDescriptor = NULL;
 665          sa.bInheritHandle       = TRUE;
 666          psa = &sa;
 667      }
 668  
 669      memset(&aStartupInfo, 0, sizeof (STARTUPINFO));
 670      memset(&aProcessInformation, 0, sizeof (PROCESS_INFORMATION));
 671      aStartupInfo.cb = sizeof (STARTUPINFO);
 672      if (hInput || hOutput || hError) {
 673          aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
 674          if (hInput) {
 675              aStartupInfo.hStdInput  = hInput;
 676          }
 677          else {
 678              aStartupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
 679          }
 680          if (hOutput) {
 681              aStartupInfo.hStdOutput = hOutput;
 682          }
 683          else {
 684              aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
 685          }
 686          if (hError) {
 687              aStartupInfo.hStdError = hError;
 688          }
 689          else {
 690              aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
 691          }
 692      }
 693  
 694      dwCreationFlags = (NORMAL_PRIORITY_CLASS);
 695  
 696      if ((shell = getenv("RUBYSHELL")) && NtHasRedirection(cmd)) {
 697          char *tmp = ALLOCA_N(char, strlen(shell) + strlen(cmd) + sizeof (" -c "));
 698          sprintf(tmp, "%s -c %s", shell, cmd);
 699          cmd = tmp;
 700      }
 701      else if ((shell = getenv("COMSPEC")) &&
 702               (NtHasRedirection(cmd) || isInternalCmd(cmd))) {
 703          char *tmp = ALLOCA_N(char, strlen(shell) + strlen(cmd) + sizeof (" /c "));
 704          sprintf(tmp, "%s /c %s", shell, cmd);
 705          cmd = tmp;
 706      }
 707      else {
 708          shell = NULL;
 709      }
 710  
 711      RUBY_CRITICAL({
 712          fRet = CreateProcess(shell, cmd, psa, psa,
 713                               psa->bInheritHandle, dwCreationFlags, NULL, NULL,
 714                               &aStartupInfo, &aProcessInformation);
 715          errno = GetLastError();
 716      });
 717  
 718      if (!fRet) {
 719          child->pid = 0;         /* release the slot */
 720          return NULL;
 721      }
 722  
 723      CloseHandle(aProcessInformation.hThread);
 724  
 725      child->hProcess = aProcessInformation.hProcess;
 726      child->pid = (pid_t)aProcessInformation.dwProcessId;
 727  
 728      if (!IsWinNT()) {
 729          /* On Win9x, make pid positive similarly to cygwin and perl */
 730          child->pid = -child->pid;
 731      }
 732  
 733      return child;
 734  }
 735  
 736  typedef struct _NtCmdLineElement {
 737      struct _NtCmdLineElement *next, *prev;
 738      char *str;
 739      int len;
 740      int flags;
 741  } NtCmdLineElement;
 742  
 743  //
 744  // Possible values for flags
 745  //
 746  
 747  #define NTGLOB   0x1    // element contains a wildcard
 748  #define NTMALLOC 0x2    // string in element was malloc'ed
 749  #define NTSTRING 0x4    // element contains a quoted string
 750  
 751  NtCmdLineElement *NtCmdHead = NULL, *NtCmdTail = NULL;
 752  
 753  void
 754  NtFreeCmdLine(void)
 755  {
 756      NtCmdLineElement *ptr;
 757      
 758      while(NtCmdHead) {
 759          ptr = NtCmdHead;
 760          NtCmdHead = NtCmdHead->next;
 761          free(ptr);
 762      }
 763      NtCmdHead = NtCmdTail = NULL;
 764  }
 765  
 766  //
 767  // This function expands wild card characters that were spotted 
 768  // during the parse phase. The idea here is to call FindFirstFile and
 769  // FindNextFile with the wildcard pattern specified, and splice in the
 770  // resulting list of new names. If the wildcard pattern doesn't match 
 771  // any existing files, just leave it in the list.
 772  //
 773  typedef struct {
 774      NtCmdLineElement *head;
 775      NtCmdLineElement *tail;
 776  } ListInfo;
 777  
 778  static void
 779  insert(const char *path, VALUE vinfo)
 780  {
 781      NtCmdLineElement *tmpcurr;
 782      ListInfo *listinfo = (ListInfo *)vinfo;
 783  
 784      tmpcurr = ALLOC(NtCmdLineElement);
 785      MEMZERO(tmpcurr, NtCmdLineElement, 1);
 786      tmpcurr->len = strlen(path);
 787      tmpcurr->str = ALLOC_N(char, tmpcurr->len + 1);
 788      tmpcurr->flags |= NTMALLOC;
 789      strcpy(tmpcurr->str, path);
 790      if (listinfo->tail) {
 791          listinfo->tail->next = tmpcurr;
 792          tmpcurr->prev = listinfo->tail;
 793          listinfo->tail = tmpcurr;
 794      }
 795      else {
 796          listinfo->tail = listinfo->head = tmpcurr;
 797      }
 798  }
 799  
 800  #ifdef HAVE_SYS_PARAM_H
 801  # include <sys/param.h>
 802  #else
 803  # define MAXPATHLEN 512
 804  #endif
 805  
 806  void
 807  NtCmdGlob (NtCmdLineElement *patt)
 808  {
 809      ListInfo listinfo;
 810      char buffer[MAXPATHLEN], *buf = buffer;
 811      char *p;
 812  
 813      listinfo.head = listinfo.tail = 0;
 814  
 815      if (patt->len >= MAXPATHLEN)
 816          buf = ruby_xmalloc(patt->len + 1);
 817  
 818      strncpy (buf, patt->str, patt->len);
 819      buf[patt->len] = '\0';
 820      for (p = buf; *p; p = CharNext(p))
 821          if (*p == '\\')
 822              *p = '/';
 823      rb_globi(buf, insert, (VALUE)&listinfo);
 824      if (buf != buffer)
 825          free(buf);
 826  
 827      if (listinfo.head && listinfo.tail) {
 828          listinfo.head->prev = patt->prev;
 829          listinfo.tail->next = patt->next;
 830          if (listinfo.head->prev)
 831              listinfo.head->prev->next = listinfo.head;
 832          if (listinfo.tail->next)
 833              listinfo.tail->next->prev = listinfo.tail;
 834      }
 835      if (patt->flags & NTMALLOC)
 836          free(patt->str);
 837      // free(patt);  //TODO:  memory leak occures here. we have to fix it.
 838  }
 839  
 840  // 
 841  // Check a command string to determine if it has I/O redirection
 842  // characters that require it to be executed by a command interpreter
 843  //
 844  
 845  static bool
 846  NtHasRedirection (char *cmd)
 847  {
 848      int inquote = 0;
 849      char quote = '\0';
 850      char *ptr ;
 851  
 852      //
 853      // Scan the string, looking for redirection (< or >) or pipe 
 854      // characters (|) that are not in a quoted string
 855      //
 856  
 857      for (ptr = cmd; *ptr; ptr++) {
 858  
 859          switch (*ptr) {
 860  
 861            case '\'':
 862            case '\"':
 863              if (inquote) {
 864                  if (quote == *ptr) {
 865                      inquote = 0;
 866                      quote = '\0';
 867                  }
 868              }
 869              else {
 870                  quote = *ptr;
 871                  inquote++;
 872              }
 873              break;
 874  
 875            case '>':
 876            case '<':
 877            case '|':
 878  
 879              if (!inquote)
 880                  return TRUE;
 881          }
 882      }
 883      return FALSE;
 884  }
 885  
 886  
 887  int 
 888  NtMakeCmdVector (char *cmdline, char ***vec, int InputCmd)
 889  {
 890      int cmdlen = strlen(cmdline);
 891      int done, instring, globbing, quoted, len;
 892      int newline, need_free = 0, i;
 893      int elements, strsz;
 894      int slashes = 0;
 895      char *ptr, *base, *buffer;
 896      char **vptr;
 897      char quote;
 898      NtCmdLineElement *curr;
 899  
 900      //
 901      // just return if we don't have a command line
 902      //
 903  
 904      if (cmdlen == 0) {
 905          *vec = NULL;
 906          return 0;
 907      }
 908  
 909      cmdline = strdup(cmdline);
 910  
 911      //
 912      // strip trailing white space
 913      //
 914  
 915      ptr = cmdline+(cmdlen - 1);
 916      while(ptr >= cmdline && ISSPACE(*ptr))
 917          --ptr;
 918      *++ptr = '\0';
 919  
 920  
 921      //
 922      // Ok, parse the command line, building a list of CmdLineElements.
 923      // When we've finished, and it's an input command (meaning that it's
 924      // the processes argv), we'll do globing and then build the argument 
 925      // vector.
 926      // The outer loop does one interation for each element seen. 
 927      // The inner loop does one interation for each character in the element.
 928      //
 929  
 930      for (done = 0, ptr = cmdline; *ptr;) {
 931  
 932          //
 933          // zap any leading whitespace
 934          //
 935  
 936          while(ISSPACE(*ptr))
 937              ptr++;
 938          base = ptr;
 939  
 940          for (done = newline = globbing = instring = quoted = 0; 
 941               *ptr && !done; ptr++) {
 942  
 943              //
 944              // Switch on the current character. We only care about the
 945              // white-space characters, the  wild-card characters, and the
 946              // quote characters.
 947              //
 948  
 949              switch (*ptr) {
 950                case '\\':
 951                  if (ptr[1] == '"') ptr++;
 952                  break;
 953                case ' ':
 954                case '\t':
 955  #if 0
 956                case '/':  // have to do this for NT/DOS option strings
 957  
 958                  //
 959                  // check to see if we're parsing an option switch
 960                  //
 961  
 962                  if (*ptr == '/' && base == ptr)
 963                      continue;
 964  #endif
 965                  //
 966                  // if we're not in a string, then we're finished with this
 967                  // element
 968                  //
 969  
 970                  if (!instring)
 971                      done++;
 972                  break;
 973  
 974                case '*':
 975                case '?':
 976  
 977                  // 
 978                  // record the fact that this element has a wildcard character
 979                  // N.B. Don't glob if inside a single quoted string
 980                  //
 981  
 982                  if (!(instring && quote == '\''))
 983                      globbing++;
 984                  break;
 985  
 986                case '\n':
 987  
 988                  //
 989                  // If this string contains a newline, mark it as such so
 990                  // we can replace it with the two character sequence "\n"
 991                  // (cmd.exe doesn't like raw newlines in strings...sigh).
 992                  //
 993  
 994                  newline++;
 995                  break;
 996  
 997                case '\'':
 998                case '\"':
 999  
1000                  //
1001                  // if we're already in a string, see if this is the
1002                  // terminating close-quote. If it is, we're finished with 
1003                  // the string, but not neccessarily with the element.
1004                  // If we're not already in a string, start one.
1005                  //
1006  
1007                  if (instring) {
1008                      if (quote == *ptr) {
1009                          instring = 0;
1010                          quote = '\0';
1011                      }
1012                  }
1013                  else {
1014                      instring++;
1015                      quote = *ptr;
1016                      quoted++;
1017                  }
1018                  break;
1019              }
1020          }
1021  
1022          //
1023          // need to back up ptr by one due to last increment of for loop
1024          // (if we got out by seeing white space)
1025          //
1026  
1027          if (*ptr)
1028              ptr--;
1029  
1030          //
1031          // when we get here, we've got a pair of pointers to the element,
1032          // base and ptr. Base points to the start of the element while ptr
1033          // points to the character following the element.
1034          //
1035  
1036          curr = ALLOC(NtCmdLineElement);
1037          memset (curr, 0, sizeof(*curr));
1038  
1039          len = ptr - base;
1040  
1041          //
1042          // if it's an input vector element and it's enclosed by quotes, 
1043          // we can remove them.
1044          //
1045  
1046          if (InputCmd && (base[0] == '\"' && base[len-1] == '\"')) {
1047              char *p;
1048              base++;
1049              len -= 2;
1050              base[len] = 0;
1051              for (p = base; p < base + len; p++) {
1052                  if ((p[0] == '\\' || p[0] == '\"') && p[1] == '"') {
1053                      strcpy(p, p + 1);
1054                      len--;
1055                  }
1056              }
1057          }
1058          else if (InputCmd && (base[0] == '\'' && base[len-1] == '\'')) {
1059              base++;
1060              len -= 2;
1061          }
1062  
1063          curr->str = base;
1064          curr->len = len;
1065          curr->flags |= (globbing ? NTGLOB : 0);
1066  
1067          //
1068          // Now put it in the list of elements
1069          //
1070          if (NtCmdTail) {
1071              NtCmdTail->next = curr;
1072              curr->prev = NtCmdTail;
1073              NtCmdTail = curr;
1074          }
1075          else {
1076              NtCmdHead = NtCmdTail = curr;
1077          }
1078      }
1079  
1080      if (InputCmd) {
1081  
1082          //
1083          // When we get here we've finished parsing the command line. Now 
1084          // we need to run the list, expanding any globbing patterns.
1085          //
1086          
1087          for(curr = NtCmdHead; curr; curr = curr->next) {
1088              if (curr->flags & NTGLOB) {
1089                  NtCmdGlob(curr);
1090              }
1091          }
1092      }
1093  
1094      //
1095      // Almost done! 
1096      // Count up the elements, then allocate space for a vector of pointers
1097      // (argv) and a string table for the elements.
1098      // 
1099  
1100      for (elements = 0, strsz = 0, curr = NtCmdHead; curr; curr = curr->next) {
1101          elements++;
1102          strsz += (curr->len + 1);
1103      }
1104  
1105      len = (elements+1)*sizeof(char *) + strsz;
1106      buffer = ALLOC_N(char, len);
1107      
1108      memset (buffer, 0, len);
1109  
1110      //
1111      // make vptr point to the start of the buffer
1112      // and ptr point to the area we'll consider the string table.
1113      //
1114      //   buffer (*vec)
1115      //   |
1116      //   V       ^---------------------V
1117      //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1118      //   |   |       | ....  | NULL  |   | ..... |\0 |   | ..... |\0 |...
1119      //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1120      //   |-  elements+1             -| ^ 1st element   ^ 2nd element
1121  
1122      vptr = (char **) buffer;
1123  
1124      ptr = buffer + (elements+1) * sizeof(char *);
1125  
1126      for (curr =  NtCmdHead; curr;  curr = curr->next) {
1127          strncpy (ptr, curr->str, curr->len);
1128          ptr[curr->len] = '\0';
1129          *vptr++ = ptr;
1130          ptr += curr->len + 1;
1131      }
1132      NtFreeCmdLine();
1133      *vec = (char **) buffer;
1134      free(cmdline);
1135      return elements;
1136  }
1137  
1138  //
1139  // UNIX compatible directory access functions for NT
1140  //
1141  
1142  #define PATHLEN 1024
1143  
1144  //
1145  // The idea here is to read all the directory names into a string table
1146  // (separated by nulls) and when one of the other dir functions is called
1147  // return the pointer to the current file name. 
1148  //
1149  
1150  DIR *
1151  rb_w32_opendir(const char *filename)
1152  {
1153      DIR            *p;
1154      long            len;
1155      long            idx;
1156      char            scannamespc[PATHLEN];
1157      char           *scanname = scannamespc;
1158      struct stat     sbuf;
1159      WIN32_FIND_DATA FindData;
1160      HANDLE          fh;
1161  
1162      //
1163      // check to see if we've got a directory
1164      //
1165  
1166      if ((rb_w32_stat (filename, &sbuf) < 0 ||
1167  #ifdef __BORLANDC__
1168          (unsigned short)(sbuf.st_mode) & _S_IFDIR == 0) &&
1169  #else
1170          sbuf.st_mode & _S_IFDIR == 0) &&
1171  #endif
1172          (!ISALPHA(filename[0]) || filename[1] != ':' || filename[2] != '\0' ||
1173          ((1 << (filename[0] & 0x5f) - 'A') & GetLogicalDrives()) == 0)) {
1174          return NULL;
1175      }
1176  
1177      //
1178      // Get us a DIR structure
1179      //
1180  
1181      p = xcalloc(sizeof(DIR), 1);
1182      if (p == NULL)
1183          return NULL;
1184      
1185      //
1186      // Create the search pattern
1187      //
1188  
1189      strcpy(scanname, filename);
1190  
1191      if (index("/\\:", *CharPrev(scanname, scanname + strlen(scanname))) == NULL)
1192          strcat(scanname, "/*");
1193      else
1194          strcat(scanname, "*");
1195  
1196      //
1197      // do the FindFirstFile call
1198      //
1199  
1200      fh = FindFirstFile (scanname, &FindData);
1201      if (fh == INVALID_HANDLE_VALUE) {
1202          return NULL;
1203      }
1204  
1205      //
1206      // now allocate the first part of the string table for the
1207      // filenames that we find.
1208      //
1209  
1210      idx = strlen(FindData.cFileName)+1;
1211      p->start = ALLOC_N(char, idx);
1212      strcpy (p->start, FindData.cFileName);
1213      p->nfiles++;
1214      
1215      //
1216      // loop finding all the files that match the wildcard
1217      // (which should be all of them in this directory!).
1218      // the variable idx should point one past the null terminator
1219      // of the previous string found.
1220      //
1221      while (FindNextFile(fh, &FindData)) {
1222          len = strlen (FindData.cFileName);
1223  
1224          //
1225          // bump the string table size by enough for the
1226          // new name and it's null terminator 
1227          //
1228  
1229          #define Renew(x, y, z) (x = (z *)realloc(x, y))
1230  
1231          Renew (p->start, idx+len+1, char);
1232          if (p->start == NULL) {
1233              rb_fatal ("opendir: malloc failed!\n");
1234          }
1235          strcpy(&p->start[idx], FindData.cFileName);
1236          p->nfiles++;
1237          idx += len+1;
1238      }
1239      FindClose(fh);
1240      p->size = idx;
1241      p->curr = p->start;
1242      return p;
1243  }
1244  
1245  
1246  //
1247  // Readdir just returns the current string pointer and bumps the
1248  // string pointer to the next entry.
1249  //
1250  
1251  struct direct  *
1252  rb_w32_readdir(DIR *dirp)
1253  {
1254      int         len;
1255      static int  dummy = 0;
1256  
1257      if (dirp->curr) {
1258  
1259          //
1260          // first set up the structure to return
1261          //
1262  
1263          len = strlen(dirp->curr);
1264          strcpy(dirp->dirstr.d_name, dirp->curr);
1265          dirp->dirstr.d_namlen = len;
1266  
1267          //
1268          // Fake inode
1269          //
1270          dirp->dirstr.d_ino = dummy++;
1271  
1272          //
1273          // Now set up for the next call to readdir
1274          //
1275  
1276          dirp->curr += len + 1;
1277          if (dirp->curr >= (dirp->start + dirp->size)) {
1278              dirp->curr = NULL;
1279          }
1280  
1281          return &(dirp->dirstr);
1282  
1283      } else
1284          return NULL;
1285  }
1286  
1287  //
1288  // Telldir returns the current string pointer position
1289  //
1290  
1291  long
1292  rb_w32_telldir(DIR *dirp)
1293  {
1294          return (long) dirp->curr;       /* ouch! pointer to long cast */
1295  }
1296  
1297  //
1298  // Seekdir moves the string pointer to a previously saved position
1299  // (Saved by telldir).
1300  
1301  void
1302  rb_w32_seekdir(DIR *dirp, long loc)
1303  {
1304          dirp->curr = (char *) loc;      /* ouch! long to pointer cast */
1305  }
1306  
1307  //
1308  // Rewinddir resets the string pointer to the start
1309  //
1310  
1311  void
1312  rb_w32_rewinddir(DIR *dirp)
1313  {
1314          dirp->curr = dirp->start;
1315  }
1316  
1317  //
1318  // This just free's the memory allocated by opendir
1319  //
1320  
1321  void
1322  rb_w32_closedir(DIR *dirp)
1323  {
1324          free(dirp->start);
1325          free(dirp);
1326  }
1327  
1328  static int 
1329  valid_filename(char *s)
1330  {
1331      int fd;
1332  
1333      //
1334      // if the file exists, then it's a valid filename!
1335      //
1336  
1337      if (_access(s, 0) == 0) {
1338          return 1;
1339      }
1340  
1341      //
1342      // It doesn't exist, so see if we can open it.
1343      //
1344      
1345      if ((fd = _open(s, _O_CREAT, 0666)) >= 0) {
1346          close(fd);
1347          _unlink (s);    // don't leave it laying around
1348          return 1;
1349      }
1350      return 0;
1351  }
1352  
1353  //
1354  // This is a clone of fdopen so that we can handle the 
1355  // brain damaged version of sockets that NT gets to use.
1356  //
1357  // The problem is that sockets are not real file handles and 
1358  // cannot be fdopen'ed. This causes problems in the do_socket
1359  // routine in doio.c, since it tries to create two file pointers
1360  // for the socket just created. We'll fake out an fdopen and see
1361  // if we can prevent perl from trying to do stdio on sockets.
1362  //
1363  
1364  //EXTERN_C int __cdecl _alloc_osfhnd(void);
1365  //EXTERN_C int __cdecl _set_osfhnd(int fh, long value);
1366  EXTERN_C void __cdecl _lock_fhandle(int);
1367  EXTERN_C void __cdecl _unlock_fhandle(int);
1368  EXTERN_C void __cdecl _unlock(int);
1369  
1370  #if (defined _MT || defined __MSVCRT__) && !defined __BORLANDC__
1371  #define MSVCRT_THREADS
1372  #endif
1373  #ifdef MSVCRT_THREADS
1374  # define MTHREAD_ONLY(x) x
1375  # define STHREAD_ONLY(x)
1376  #elif defined(__BORLANDC__)
1377  # define MTHREAD_ONLY(x)
1378  # define STHREAD_ONLY(x)
1379  #else
1380  # define MTHREAD_ONLY(x)
1381  # define STHREAD_ONLY(x) x
1382  #endif
1383  
1384  typedef struct  {
1385      long osfhnd;    /* underlying OS file HANDLE */
1386      char osfile;    /* attributes of file (e.g., open in text mode?) */
1387      char pipech;    /* one char buffer for handles opened on pipes */
1388  #ifdef MSVCRT_THREADS
1389      int lockinitflag;
1390      CRITICAL_SECTION lock;
1391  #endif
1392  }       ioinfo;
1393  
1394  #if !defined _CRTIMP
1395  #define _CRTIMP __declspec(dllimport)
1396  #endif
1397  
1398  #ifndef __BORLANDC__
1399  EXTERN_C _CRTIMP ioinfo * __pioinfo[];
1400  
1401  #define IOINFO_L2E                      5
1402  #define IOINFO_ARRAY_ELTS       (1 << IOINFO_L2E)
1403  #define _pioinfo(i)     (__pioinfo[i >> IOINFO_L2E] + (i & (IOINFO_ARRAY_ELTS - 1)))
1404  #define _osfhnd(i)  (_pioinfo(i)->osfhnd)
1405  #define _osfile(i)  (_pioinfo(i)->osfile)
1406  #define _pipech(i)  (_pioinfo(i)->pipech)
1407  
1408  #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
1409  #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
1410  
1411  #else
1412  
1413  #define _set_osfhnd(fh, osfh) (void)((fh), (osfh))
1414  #define _set_osflags(fh, flags) (void)((fh), (flags))
1415  
1416  #endif
1417  
1418  #define FOPEN                   0x01    /* file handle open */
1419  #define FNOINHERIT              0x10    /* file handle opened O_NOINHERIT */
1420  #define FAPPEND                 0x20    /* file handle opened O_APPEND */
1421  #define FDEV                    0x40    /* file handle refers to device */
1422  #define FTEXT                   0x80    /* file handle is in text mode */
1423  
1424  static int
1425  rb_w32_open_osfhandle(long osfhandle, int flags)
1426  {
1427      int fh;
1428      char fileflags;             /* _osfile flags */
1429  
1430      /* copy relevant flags from second parameter */
1431      fileflags = FDEV;
1432  
1433      if (flags & O_APPEND)
1434          fileflags |= FAPPEND;
1435  
1436      if (flags & O_TEXT)
1437          fileflags |= FTEXT;
1438  
1439      if (flags & O_NOINHERIT)
1440          fileflags |= FNOINHERIT;
1441  
1442      RUBY_CRITICAL({
1443          /* attempt to allocate a C Runtime file handle */
1444          HANDLE hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
1445          fh = _open_osfhandle((long)hF, 0);
1446          CloseHandle(hF);
1447          if (fh == -1) {
1448              errno = EMFILE;             /* too many open files */
1449              _doserrno = 0L;             /* not an OS error */
1450          }
1451          else {
1452  
1453              MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fh)->lock)));
1454              /* the file is open. now, set the info in _osfhnd array */
1455              _set_osfhnd(fh, osfhandle);
1456  
1457              fileflags |= FOPEN;         /* mark as open */
1458  
1459              _set_osflags(fh, fileflags); /* set osfile entry */
1460              MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fh)->lock));
1461          }
1462      });
1463      return fh;                  /* return handle */
1464  }
1465  
1466  #undef getsockopt
1467  
1468  static int
1469  is_socket(SOCKET fd)
1470  {
1471      char sockbuf[80];
1472      int optlen;
1473      int retval;
1474  
1475      optlen = sizeof(sockbuf);
1476      retval = getsockopt(fd, SOL_SOCKET, SO_TYPE, sockbuf, &optlen);
1477      if (retval == SOCKET_ERROR) {
1478          int iRet;
1479  
1480          iRet = WSAGetLastError();
1481          if (iRet == WSAENOTSOCK || iRet == WSANOTINITIALISED)
1482              return FALSE;
1483      }
1484  
1485      //
1486      // If we get here, then fd is actually a socket.
1487      //
1488  
1489      return TRUE;
1490  }
1491  
1492  int
1493  rb_w32_fddup (int fd)
1494  {
1495      SOCKET s = TO_SOCKET(fd);
1496  
1497      if (s == -1)
1498          return -1;
1499  
1500      return rb_w32_open_osfhandle(s, O_RDWR|O_BINARY);
1501  }
1502  
1503  
1504  void
1505  rb_w32_fdclose(FILE *fp)
1506  {
1507      RUBY_CRITICAL({
1508          STHREAD_ONLY(_free_osfhnd(fileno(fp)));
1509          fclose(fp);
1510      });
1511  }
1512  
1513  //
1514  // Since the errors returned by the socket error function 
1515  // WSAGetLastError() are not known by the library routine strerror
1516  // we have to roll our own.
1517  //
1518  
1519  #undef strerror
1520  
1521  char *
1522  rb_w32_strerror(int e)
1523  {
1524      static char buffer[512];
1525  #if !defined __MINGW32__
1526      extern int sys_nerr;
1527  #endif
1528      DWORD source = 0;
1529      char *p;
1530  
1531      if (e < 0 || e > sys_nerr) {
1532          if (e < 0)
1533              e = GetLastError();
1534          if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1535                            FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
1536                            buffer, 512, NULL) == 0) {
1537              strcpy(buffer, "Unknown Error");
1538          }
1539          for (p = buffer + strlen(buffer) - 1; buffer <= p; p--) {
1540              if (*p != '\r' && *p != '\n') break;
1541              *p = 0;
1542          }
1543          return buffer;
1544      }
1545      return strerror(e);
1546  }
1547  
1548  //
1549  // various stubs
1550  //
1551  
1552  
1553  // Ownership
1554  //
1555  // Just pretend that everyone is a superuser. NT will let us know if
1556  // we don't really have permission to do something.
1557  //
1558  
1559  #define ROOT_UID        0
1560  #define ROOT_GID        0
1561  
1562  UIDTYPE
1563  getuid(void)
1564  {
1565          return ROOT_UID;
1566  }
1567  
1568  UIDTYPE
1569  geteuid(void)
1570  {
1571          return ROOT_UID;
1572  }
1573  
1574  GIDTYPE
1575  getgid(void)
1576  {
1577          return ROOT_GID;
1578  }
1579  
1580  GIDTYPE
1581  getegid(void)
1582  {
1583      return ROOT_GID;
1584  }
1585  
1586  int
1587  setuid(int uid)
1588  { 
1589      return (uid == ROOT_UID ? 0 : -1);
1590  }
1591  
1592  int
1593  setgid(int gid)
1594  {
1595      return (gid == ROOT_GID ? 0 : -1);
1596  }
1597  
1598  //
1599  // File system stuff
1600  //
1601  
1602  int
1603  /* ioctl(int i, unsigned int u, char *data) */
1604  #ifdef __BORLANDC__
1605    ioctl(int i, int u, ...)
1606  #else
1607    ioctl(int i, unsigned int u, long data)
1608  #endif
1609  {
1610      return -1;
1611  }
1612  
1613  #undef FD_SET
1614  
1615  void
1616  rb_w32_fdset(int fd, fd_set *set)
1617  {
1618      unsigned int i;
1619      SOCKET s = TO_SOCKET(fd);
1620  
1621      for (i = 0; i < set->fd_count; i++) {
1622          if (set->fd_array[i] == s) {
1623              return;
1624          }
1625      }
1626      if (i == set->fd_count) {
1627          if (set->fd_count < FD_SETSIZE) {
1628              set->fd_array[i] = s;
1629              set->fd_count++;
1630          }
1631      }
1632  }
1633  
1634  #undef FD_CLR
1635  
1636  void
1637  rb_w32_fdclr(int fd, fd_set *set)
1638  {
1639      unsigned int i;
1640      SOCKET s = TO_SOCKET(fd);
1641  
1642      for (i = 0; i < set->fd_count; i++) {
1643          if (set->fd_array[i] == s) {
1644              while (i < set->fd_count - 1) {
1645                  set->fd_array[i] = set->fd_array[i + 1];
1646                  i++;
1647              }
1648              set->fd_count--;
1649              break;
1650          }
1651      }
1652  }
1653  
1654  #undef FD_ISSET
1655  
1656  int
1657  rb_w32_fdisset(int fd, fd_set *set)
1658  {
1659         return __WSAFDIsSet(TO_SOCKET(fd), set);
1660  }
1661  
1662  //
1663  // Networking trampolines
1664  // These are used to avoid socket startup/shutdown overhead in case 
1665  // the socket routines aren't used.
1666  //
1667  
1668  #undef select
1669  
1670  static int NtSocketsInitialized = 0;
1671  
1672  static int
1673  extract_file_fd(fd_set *set, fd_set *fileset)
1674  {
1675      int idx;
1676  
1677      fileset->fd_count = 0;
1678      if (!set)
1679          return 0;
1680      for (idx = 0; idx < set->fd_count; idx++) {
1681          SOCKET fd = set->fd_array[idx];
1682  
1683          if (!is_socket(fd)) {
1684              int i;
1685  
1686              for (i = 0; i < fileset->fd_count; i++) {
1687                  if (fileset->fd_array[i] == fd) {
1688                      break;
1689                  }
1690              }
1691              if (i == fileset->fd_count) {
1692                  if (fileset->fd_count < FD_SETSIZE) {
1693                      fileset->fd_array[i] = fd;
1694                      fileset->fd_count++;
1695                  }
1696              }
1697          }
1698      }
1699      return fileset->fd_count;
1700  }
1701  
1702  long 
1703  rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
1704                 struct timeval *timeout)
1705  {
1706      long r;
1707      fd_set file_rd;
1708      fd_set file_wr;
1709  #ifdef USE_INTERRUPT_WINSOCK
1710      fd_set trap;
1711  #endif /* USE_INTERRUPT_WINSOCK */
1712      int file_nfds;
1713  
1714      if (!NtSocketsInitialized++) {
1715          StartSockets();
1716      }
1717      r = 0;
1718      if (rd && rd->fd_count > r) r = rd->fd_count;
1719      if (wr && wr->fd_count > r) r = wr->fd_count;
1720      if (ex && ex->fd_count > r) r = ex->fd_count;
1721      if (nfds > r) nfds = r;
1722      if (nfds == 0 && timeout) {
1723          Sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
1724          return 0;
1725      }
1726      file_nfds = extract_file_fd(rd, &file_rd);
1727      file_nfds += extract_file_fd(wr, &file_wr);
1728      if (file_nfds)
1729      {
1730          // assume normal files are always readable/writable
1731          // fake read/write fd_set and return value
1732          if (rd) *rd = file_rd;
1733          if (wr) *wr = file_wr;
1734          return file_nfds;
1735      }
1736  
1737  #if USE_INTERRUPT_WINSOCK
1738      if (ex)
1739          trap = *ex;
1740      else
1741          trap.fd_count = 0;
1742      if (trap.fd_count < FD_SETSIZE)
1743          trap.fd_array[trap.fd_count++] = (SOCKET)interrupted_event;
1744      // else unable to catch interrupt.
1745      ex = &trap;
1746  #endif /* USE_INTERRUPT_WINSOCK */
1747  
1748      RUBY_CRITICAL(r = select (nfds, rd, wr, ex, timeout));
1749      if (r == SOCKET_ERROR) {
1750          errno = WSAGetLastError();
1751          switch (errno) {
1752            case WSAEINTR:
1753              errno = EINTR;
1754              break;
1755          }
1756      }
1757      return r;
1758  }
1759  
1760  static void
1761  StartSockets ()
1762  {
1763      WORD version;
1764      WSADATA retdata;
1765      int ret;
1766          int iSockOpt;
1767      
1768      //
1769      // initalize the winsock interface and insure that it's
1770      // cleaned up at exit.
1771      //
1772      version = MAKEWORD(1, 1);
1773      if (ret = WSAStartup(version, &retdata))
1774          rb_fatal ("Unable to locate winsock library!\n");
1775      if (LOBYTE(retdata.wVersion) != 1)
1776          rb_fatal("could not find version 1 of winsock dll\n");
1777  
1778      if (HIBYTE(retdata.wVersion) != 1)
1779          rb_fatal("could not find version 1 of winsock dll\n");
1780  
1781      atexit((void (*)(void)) WSACleanup);
1782  
1783  #ifndef SO_SYNCHRONOUS_NONALERT
1784  #define SO_SYNCHRONOUS_NONALERT 0x20
1785  #endif
1786  
1787      iSockOpt = SO_SYNCHRONOUS_NONALERT;
1788      /*
1789       * Enable the use of sockets as filehandles
1790       */
1791  #ifndef SO_OPENTYPE
1792  #define SO_OPENTYPE     0x7008
1793  #endif
1794  
1795      setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
1796                 (char *)&iSockOpt, sizeof(iSockOpt));
1797  
1798      main_thread.handle = GetCurrentThreadHandle();
1799      main_thread.id = GetCurrentThreadId();
1800  
1801      interrupted_event = CreateSignal();
1802      if (!interrupted_event)
1803          rb_fatal("Unable to create interrupt event!\n");
1804  }
1805  
1806  #undef accept
1807  
1808  SOCKET
1809  rb_w32_accept (SOCKET s, struct sockaddr *addr, int *addrlen)
1810  {
1811      SOCKET r;
1812  
1813      if (!NtSocketsInitialized++) {
1814          StartSockets();
1815      }
1816      RUBY_CRITICAL(r = accept (TO_SOCKET(s), addr, addrlen));
1817      if (r == INVALID_SOCKET)
1818          errno = WSAGetLastError();
1819      return rb_w32_open_osfhandle(r, O_RDWR|O_BINARY);
1820  }
1821  
1822  #undef bind
1823  
1824  int 
1825  rb_w32_bind (SOCKET s, struct sockaddr *addr, int addrlen)
1826  {
1827      int r;
1828  
1829      if (!NtSocketsInitialized++) {
1830          StartSockets();
1831      }
1832      RUBY_CRITICAL(r = bind (TO_SOCKET(s), addr, addrlen));
1833      if (r == SOCKET_ERROR)
1834          errno = WSAGetLastError();
1835      return r;
1836  }
1837  
1838  #undef connect
1839  
1840  int 
1841  rb_w32_connect (SOCKET s, struct sockaddr *addr, int addrlen)
1842  {
1843      int r;
1844      if (!NtSocketsInitialized++) {
1845          StartSockets();
1846      }
1847      RUBY_CRITICAL(r = connect (TO_SOCKET(s), addr, addrlen));
1848      if (r == SOCKET_ERROR)
1849          errno = WSAGetLastError();
1850      return r;
1851  }
1852  
1853  
1854  #undef getpeername
1855  
1856  int 
1857  rb_w32_getpeername (SOCKET s, struct sockaddr *addr, int *addrlen)
1858  {
1859      int r;
1860      if (!NtSocketsInitialized++) {
1861          StartSockets();
1862      }
1863      RUBY_CRITICAL(r = getpeername (TO_SOCKET(s), addr, addrlen));
1864      if (r == SOCKET_ERROR)
1865          errno = WSAGetLastError();
1866      return r;
1867  }
1868  
1869  #undef getsockname
1870  
1871  int 
1872  rb_w32_getsockname (SOCKET s, struct sockaddr *addr, int *addrlen)
1873  {
1874      int r;
1875      if (!NtSocketsInitialized++) {
1876          StartSockets();
1877      }
1878      RUBY_CRITICAL(r = getsockname (TO_SOCKET(s), addr, addrlen));
1879      if (r == SOCKET_ERROR)
1880          errno = WSAGetLastError();
1881      return r;
1882  }
1883  
1884  int 
1885  rb_w32_getsockopt (SOCKET s, int level, int optname, char *optval, int *optlen)
1886  {
1887      int r;
1888      if (!NtSocketsInitialized++) {
1889          StartSockets();
1890      }
1891      RUBY_CRITICAL(r = getsockopt (TO_SOCKET(s), level, optname, optval, optlen));
1892      if (r == SOCKET_ERROR)
1893          errno = WSAGetLastError();
1894      return r;
1895  }
1896  
1897  #undef ioctlsocket
1898  
1899  int 
1900  rb_w32_ioctlsocket (SOCKET s, long cmd, u_long *argp)
1901  {
1902      int r;
1903      if (!NtSocketsInitialized++) {
1904          StartSockets();
1905      }
1906      RUBY_CRITICAL(r = ioctlsocket (TO_SOCKET(s), cmd, argp));
1907      if (r == SOCKET_ERROR)
1908          errno = WSAGetLastError();
1909      return r;
1910  }
1911  
1912  #undef listen
1913  
1914  int 
1915  rb_w32_listen (SOCKET s, int backlog)
1916  {
1917      int r;
1918      if (!NtSocketsInitialized++) {
1919          StartSockets();
1920      }
1921      RUBY_CRITICAL(r = listen (TO_SOCKET(s), backlog));
1922      if (r == SOCKET_ERROR)
1923          errno = WSAGetLastError();
1924      return r;
1925  }
1926  
1927  #undef recv
1928  
1929  int 
1930  rb_w32_recv (SOCKET s, char *buf, int len, int flags)
1931  {
1932      int r;
1933      if (!NtSocketsInitialized++) {
1934          StartSockets();
1935      }
1936      RUBY_CRITICAL(r = recv (TO_SOCKET(s), buf, len, flags));
1937      if (r == SOCKET_ERROR)
1938          errno = WSAGetLastError();
1939      return r;
1940  }
1941  
1942  #undef recvfrom
1943  
1944  int 
1945  rb_w32_recvfrom (SOCKET s, char *buf, int len, int flags, 
1946                  struct sockaddr *from, int *fromlen)
1947  {
1948      int r;
1949      if (!NtSocketsInitialized++) {
1950          StartSockets();
1951      }
1952      RUBY_CRITICAL(r = recvfrom (TO_SOCKET(s), buf, len, flags, from, fromlen));
1953      if (r == SOCKET_ERROR)
1954          errno =  WSAGetLastError();
1955      return r;
1956  }
1957  
1958  #undef send
1959  
1960  int 
1961  rb_w32_send (SOCKET s, char *buf, int len, int flags)
1962  {
1963      int r;
1964      if (!NtSocketsInitialized++) {
1965          StartSockets();
1966      }
1967      RUBY_CRITICAL(r = send (TO_SOCKET(s), buf, len, flags));
1968      if (r == SOCKET_ERROR)
1969          errno = WSAGetLastError();
1970      return r;
1971  }
1972  
1973  #undef sendto
1974  
1975  int 
1976  rb_w32_sendto (SOCKET s, char *buf, int len, int flags, 
1977                  struct sockaddr *to, int tolen)
1978  {
1979      int r;
1980      if (!NtSocketsInitialized++) {
1981          StartSockets();
1982      }
1983      RUBY_CRITICAL(r = sendto (TO_SOCKET(s), buf, len, flags, to, tolen));
1984      if (r == SOCKET_ERROR)
1985          errno = WSAGetLastError();
1986      return r;
1987  }
1988  
1989  #undef setsockopt
1990  
1991  int 
1992  rb_w32_setsockopt (SOCKET s, int level, int optname, char *optval, int optlen)
1993  {
1994      int r;
1995      if (!NtSocketsInitialized++) {
1996          StartSockets();
1997      }
1998      RUBY_CRITICAL(r = setsockopt (TO_SOCKET(s), level, optname, optval, optlen));
1999      if (r == SOCKET_ERROR)
2000          errno = WSAGetLastError();
2001      return r;
2002  }
2003      
2004  #undef shutdown
2005  
2006  int 
2007  rb_w32_shutdown (SOCKET s, int how)
2008  {
2009      int r;
2010      if (!NtSocketsInitialized++) {
2011          StartSockets();
2012      }
2013      RUBY_CRITICAL(r = shutdown (TO_SOCKET(s), how));
2014      if (r == SOCKET_ERROR)
2015          errno = WSAGetLastError();
2016      return r;
2017  }
2018  
2019  #undef socket
2020  
2021  SOCKET 
2022  rb_w32_socket (int af, int type, int protocol)
2023  {
2024      SOCKET s;
2025      if (!NtSocketsInitialized++) {
2026          StartSockets();
2027      }
2028      RUBY_CRITICAL(s = socket (af, type, protocol));
2029      if (s == INVALID_SOCKET) {
2030          errno = WSAGetLastError();
2031          //fprintf(stderr, "socket fail (%d)", WSAGetLastError());
2032      }
2033  #ifdef __BORLANDC__
2034      return _open_osfhandle(s, O_RDWR|O_BINARY);
2035  #else
2036      return rb_w32_open_osfhandle(s, O_RDWR|O_BINARY);
2037  #endif
2038  }
2039  
2040  #undef gethostbyaddr
2041  
2042  struct hostent *
2043  rb_w32_gethostbyaddr (char *addr, int len, int type)
2044  {
2045      struct hostent *r;
2046      if (!NtSocketsInitialized++) {
2047          StartSockets();
2048      }
2049      RUBY_CRITICAL(r = gethostbyaddr (addr, len, type));
2050      if (r == NULL)
2051          errno = WSAGetLastError();
2052      return r;
2053  }
2054  
2055  #undef gethostbyname
2056  
2057  struct hostent *
2058  rb_w32_gethostbyname (char *name)
2059  {
2060      struct hostent *r;
2061      if (!NtSocketsInitialized++) {
2062          StartSockets();
2063      }
2064      RUBY_CRITICAL(r = gethostbyname (name));
2065      if (r == NULL)
2066          errno = WSAGetLastError();
2067      return r;
2068  }
2069  
2070  #undef gethostname
2071  
2072  int
2073  rb_w32_gethostname (char *name, int len)
2074  {
2075      int r;
2076      if (!NtSocketsInitialized++) {
2077          StartSockets();
2078      }
2079      RUBY_CRITICAL(r = gethostname (name, len));
2080      if (r == SOCKET_ERROR)
2081          errno = WSAGetLastError();
2082      return r;
2083  }
2084  
2085  #undef getprotobyname
2086  
2087  struct protoent *
2088  rb_w32_getprotobyname (char *name)
2089  {
2090      struct protoent *r;
2091      if (!NtSocketsInitialized++) {
2092          StartSockets();
2093      }
2094      RUBY_CRITICAL(r = getprotobyname (name));
2095      if (r == NULL)
2096          errno = WSAGetLastError();
2097      return r;
2098  }
2099  
2100  #undef getprotobynumber
2101  
2102  struct protoent *
2103  rb_w32_getprotobynumber (int num)
2104  {
2105      struct protoent *r;
2106      if (!NtSocketsInitialized++) {
2107          StartSockets();
2108      }
2109      RUBY_CRITICAL(r = getprotobynumber (num));
2110      if (r == NULL)
2111          errno = WSAGetLastError();
2112      return r;
2113  }
2114  
2115  #undef getservbyname
2116  
2117  struct servent *
2118  rb_w32_getservbyname (char *name, char *proto)
2119  {
2120      struct servent *r;
2121      if (!NtSocketsInitialized++) {
2122          StartSockets();
2123      }
2124      RUBY_CRITICAL(r = getservbyname (name, proto));
2125      if (r == NULL)
2126          errno = WSAGetLastError();
2127      return r;
2128  }
2129  
2130  #undef getservbyport
2131  
2132  struct servent *
2133  rb_w32_getservbyport (int port, char *proto)
2134  {
2135      struct servent *r;
2136      if (!NtSocketsInitialized++) {
2137          StartSockets();
2138      }
2139      RUBY_CRITICAL(r = getservbyport (port, proto));
2140      if (r == NULL)
2141          errno = WSAGetLastError();
2142      return r;
2143  }
2144  
2145  //
2146  // Networking stubs
2147  //
2148  
2149  void endhostent() {}
2150  void endnetent() {}
2151  void endprotoent() {}
2152  void endservent() {}
2153  
2154  struct netent *getnetent (void) {return (struct netent *) NULL;}
2155  
2156  struct netent *getnetbyaddr(char *name) {return (struct netent *)NULL;}
2157  
2158  struct netent *getnetbyname(long net, int type) {return (struct netent *)NULL;}
2159  
2160  struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
2161  
2162  struct servent *getservent (void) {return (struct servent *) NULL;}
2163  
2164  void sethostent (int stayopen) {}
2165  
2166  void setnetent (int stayopen) {}
2167  
2168  void setprotoent (int stayopen) {}
2169  
2170  void setservent (int stayopen) {}
2171  
2172  #ifndef WNOHANG
2173  #define WNOHANG -1
2174  #endif
2175  
2176  static pid_t
2177  poll_child_status(struct ChildRecord *child, int *stat_loc)
2178  {
2179      DWORD exitcode;
2180  
2181      if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
2182          /* If an error occured, return immediatly. */
2183          errno = GetLastError();
2184          if (errno == ERROR_INVALID_PARAMETER) {
2185              errno = ECHILD;
2186          }
2187          CloseChildHandle(child);
2188          return -1;
2189      }
2190      if (exitcode != STILL_ACTIVE) {
2191          /* If already died, return immediatly. */
2192          pid_t pid = child->pid;
2193          CloseChildHandle(child);
2194          if (stat_loc) *stat_loc = exitcode << 8;
2195          return pid;
2196      }
2197      return 0;
2198  }
2199  
2200  pid_t
2201  waitpid (pid_t pid, int *stat_loc, int options)
2202  {
2203      DWORD timeout;
2204  
2205      if (options == WNOHANG) {
2206          timeout = 0;
2207      } else {
2208          timeout = INFINITE;
2209      }
2210  
2211      if (pid == -1) {
2212          int count = 0;
2213          DWORD ret;
2214          HANDLE events[MAXCHILDNUM + 1];
2215  
2216          FOREACH_CHILD(child) {
2217              if (!child->pid || child->pid < 0) continue;
2218              if ((pid = poll_child_status(child, stat_loc))) return pid;
2219              events[count++] = child->hProcess;
2220          } END_FOREACH_CHILD;
2221          if (!count) {
2222              errno = ECHILD;
2223              return -1;
2224          }
2225          events[count] = interrupted_event;
2226  
2227          ret = WaitForMultipleEvents(count + 1, events, FALSE, timeout, TRUE);
2228          if (ret == WAIT_TIMEOUT) return 0;
2229          if ((ret -= WAIT_OBJECT_0) == count) {
2230              ResetSignal(interrupted_event);
2231              errno = EINTR;
2232              return -1;
2233          }
2234          if (ret > count) {
2235              errno = GetLastError();
2236              return -1;
2237          }
2238  
2239          return poll_child_status(ChildRecord + ret, stat_loc);
2240      }
2241      else {
2242          struct ChildRecord* child = FindChildSlot(pid);
2243          if (!child) {
2244              errno = ECHILD;
2245              return -1;
2246          }
2247  
2248          while (!(pid = poll_child_status(child, stat_loc))) {
2249              /* wait... */
2250              if (wait_events(child->hProcess, timeout) != WAIT_OBJECT_0) {
2251                  /* still active */
2252                  pid = 0;
2253                  break;
2254              }
2255          }
2256      }
2257  
2258      return pid;
2259  }
2260  
2261  #include <sys/timeb.h>
2262  
2263  int _cdecl
2264  gettimeofday(struct timeval *tv, struct timezone *tz)
2265  {
2266      SYSTEMTIME st;
2267      time_t t;
2268      struct tm tm;
2269  
2270      GetLocalTime(&st);
2271      tm.tm_sec = st.wSecond;
2272      tm.tm_min = st.wMinute;
2273      tm.tm_hour = st.wHour;
2274      tm.tm_mday = st.wDay;
2275      tm.tm_mon = st.wMonth - 1;
2276      tm.tm_year = st.wYear - 1900;
2277      tm.tm_isdst = -1;
2278      t = mktime(&tm);
2279      tv->tv_sec = t;
2280      tv->tv_usec = st.wMilliseconds * 1000;
2281  
2282      return 0;
2283  }
2284  
2285  char *
2286  rb_w32_getcwd(buffer, size)
2287      char *buffer;
2288      int size;
2289  {
2290      int length;
2291      char *bp;
2292  
2293  #ifdef __BORLANDC__
2294  #undef getcwd
2295      if (getcwd(buffer, size) == NULL) {
2296  #else
2297      if (_getcwd(buffer, size) == NULL) {
2298  #endif
2299          return NULL;
2300      }
2301      length = strlen(buffer);
2302      if (length >= size) {
2303          return NULL;
2304      }
2305  
2306      for (bp = buffer; *bp != '\0'; bp = CharNext(bp)) {
2307          if (*bp == '\\') {
2308              *bp = '/';
2309          }
2310      }
2311      return buffer;
2312  }
2313  
2314  static char *
2315  str_grow(struct RString *str, size_t new_size)
2316  {
2317          char *p;
2318  
2319          p = realloc(str->ptr, new_size);
2320          if (p == NULL)
2321                  rb_fatal("cannot grow string\n");
2322  
2323          str->len = new_size;
2324          str->ptr = p;
2325  
2326          return p;
2327  }
2328  
2329  int
2330  chown(const char *path, int owner, int group)
2331  {
2332          return 0;
2333  }
2334  
2335  int
2336  kill(int pid, int sig)
2337  {
2338      int ret = 0;
2339  
2340      if (pid <= 0) {
2341          errno = EINVAL;
2342          return -1;
2343      }
2344  
2345      if (IsWin95()) pid = -pid;
2346      if ((unsigned int)pid == GetCurrentProcessId() && sig != SIGKILL)
2347          return raise(sig);
2348  
2349      switch (sig) {
2350        case SIGINT:
2351          RUBY_CRITICAL({
2352              if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, (DWORD)pid)) {
2353                  if ((errno = GetLastError()) == 0) {
2354                      errno = EPERM;
2355                  }
2356                  ret = -1;
2357              }
2358          });
2359          break;
2360  
2361        case SIGKILL:
2362          RUBY_CRITICAL({
2363              HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD)pid);
2364              if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
2365                  if (GetLastError() == ERROR_INVALID_PARAMETER) {
2366                      errno = ESRCH;
2367                  }
2368                  else {
2369                      errno = EPERM;
2370                  }
2371                  ret = -1;
2372              }
2373              else if (!TerminateProcess(hProc, 0)) {
2374                  errno = EPERM;
2375                  ret = -1;
2376              }
2377              CloseHandle(hProc);
2378          });
2379          break;
2380  
2381        define:
2382          errno = EINVAL;
2383          ret = -1;
2384          break;
2385      }
2386  
2387      return ret;
2388  }
2389  
2390  int
2391  link(char *from, char *to)
2392  {
2393          return -1;
2394  }
2395  
2396  int
2397  wait()
2398  {
2399          return 0;
2400  }
2401  
2402  char *
2403  rb_w32_getenv(const char *name)
2404  {
2405      static char *curitem = NULL;
2406      static DWORD curlen = 0;
2407      DWORD needlen;
2408  
2409      if (curitem == NULL || curlen == 0) {
2410          curlen = 512;
2411          curitem = ALLOC_N(char, curlen);
2412      }
2413  
2414      needlen = GetEnvironmentVariable(name, curitem, curlen);
2415      if (needlen != 0) {
2416          while (needlen > curlen) {
2417              REALLOC_N(curitem, char, needlen);
2418              curlen = needlen;
2419              needlen = GetEnvironmentVariable(name, curitem, curlen);
2420          }
2421      }
2422      else {
2423          return NULL;
2424      }
2425  
2426      return curitem;
2427  }
2428  
2429  int
2430  rb_w32_rename(const char *oldpath, const char *newpath)
2431  {
2432      int res = 0;
2433      int oldatts;
2434      int newatts;
2435  
2436      oldatts = GetFileAttributes(oldpath);
2437      newatts = GetFileAttributes(newpath);
2438  
2439      if (oldatts == -1) {
2440          errno = GetLastError();
2441          return -1;
2442      }
2443  
2444      RUBY_CRITICAL({
2445          if (newatts != -1 && newatts & FILE_ATTRIBUTE_READONLY)
2446              SetFileAttributesA(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
2447  
2448          if (!MoveFile(oldpath, newpath))
2449              res = -1;
2450  
2451          if (res) {
2452              switch (GetLastError()) {
2453                case ERROR_ALREADY_EXISTS:
2454                case ERROR_FILE_EXISTS:
2455                  if (IsWinNT()) {
2456                      if (MoveFileEx(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
2457                          res = 0;
2458                  } else {
2459                      for (;;) {
2460                          if (!DeleteFile(newpath) && GetLastError() != ERROR_FILE_NOT_FOUND)
2461                              break;
2462                          else if (MoveFile(oldpath, newpath)) {
2463                              res = 0;
2464                              break;
2465                          }
2466                      }
2467                  }
2468              }
2469          }
2470  
2471          if (res)
2472              errno = GetLastError();
2473          else
2474              SetFileAttributes(newpath, oldatts);
2475      });
2476  
2477      return res;
2478  }
2479  
2480  static int
2481  isUNCRoot(const char *path)
2482  {
2483      if (path[0] == '\\' && path[1] == '\\') {
2484          const char *p;
2485          for (p = path + 3; *p; p = CharNext(p)) {
2486              if (*p == '\\')
2487                  break;
2488          }
2489          if (p[0] && p[1]) {
2490              for (p++; *p; p = CharNext(p)) {
2491                  if (*p == '\\')
2492                      break;
2493              }
2494              if (!p[0] || !p[1])
2495                  return 1;
2496          }
2497      }
2498      return 0;
2499  }
2500  
2501  int
2502  rb_w32_stat(const char *path, struct stat *st)
2503  {
2504      const char *p;
2505      char *buf1 = ALLOCA_N(char, strlen(path) + 2);
2506      char *buf2 = ALLOCA_N(char, MAXPATHLEN);
2507      char *s;
2508      int len;
2509      int ret;
2510  
2511      for (p = path, s = buf1; *p; p++, s++) {
2512          if (*p == '/')
2513              *s = '\\';
2514          else
2515              *s = *p;
2516      }
2517      *s = '\0';
2518      len = strlen(buf1);
2519      p = CharPrev(buf1, buf1 + len);
2520      if (isUNCRoot(buf1)) {
2521          if (*p != '\\')
2522              strcat(buf1, "\\");
2523      } else if (*p == '\\' || *p == ':')
2524          strcat(buf1, ".");
2525      if (_fullpath(buf2, buf1, MAXPATHLEN)) {
2526          ret = stat(buf2, st);
2527          if (ret == 0) {
2528              st->st_mode &= ~(S_IWGRP | S_IWOTH);
2529          }
2530          return ret;
2531      }
2532      else
2533          return -1;
2534  }
2535  
2536  static long
2537  filetime_to_clock(FILETIME *ft)
2538  {
2539      __int64 qw = ft->dwHighDateTime;
2540      qw <<= 32;
2541      qw |= ft->dwLowDateTime;
2542      qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
2543      return (long) qw;
2544  }
2545  
2546  int
2547  rb_w32_times(struct tms *tmbuf)
2548  {
2549      FILETIME create, exit, kernel, user;
2550  
2551      if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
2552          tmbuf->tms_utime = filetime_to_clock(&user);
2553          tmbuf->tms_stime = filetime_to_clock(&kernel);
2554          tmbuf->tms_cutime = 0;
2555          tmbuf->tms_cstime = 0;
2556      }
2557      else {
2558          tmbuf->tms_utime = clock();
2559          tmbuf->tms_stime = 0;
2560          tmbuf->tms_cutime = 0;
2561          tmbuf->tms_cstime = 0;
2562      }
2563      return 0;
2564  }
2565  
2566  #undef Sleep
2567  #define yield_once() Sleep(0)
2568  #define yield_until(condition) do yield_once(); while (!(condition))
2569  
2570  static DWORD wait_events(HANDLE event, DWORD timeout)
2571  {
2572      HANDLE events[2];
2573      int count = 0;
2574      DWORD ret;
2575  
2576      if (event) {
2577          events[count++] = event;
2578      }
2579      events[count++] = interrupted_event;
2580  
2581      ret = WaitForMultipleEvents(count, events, FALSE, timeout, TRUE);
2582  
2583      if (ret == WAIT_OBJECT_0 + count - 1) {
2584          ResetSignal(interrupted_event);
2585          errno = EINTR;
2586      }
2587  
2588      return ret;
2589  }
2590  
2591  static CRITICAL_SECTION* system_state(void)
2592  {
2593      static int initialized = 0;
2594      static CRITICAL_SECTION syssect;
2595  
2596      if (!initialized) {
2597          InitializeCriticalSection(&syssect);
2598          initialized = 1;
2599      }
2600      return &syssect;
2601  }
2602  
2603  static LONG flag_interrupt = -1;
2604  static volatile DWORD tlsi_interrupt = TLS_OUT_OF_INDEXES;
2605  
2606  void rb_w32_enter_critical(void)
2607  {
2608      if (IsWinNT()) {
2609          EnterCriticalSection(system_state());
2610          return;
2611      }
2612  
2613      if (tlsi_interrupt == TLS_OUT_OF_INDEXES) {
2614          tlsi_interrupt = TlsAlloc();
2615      }
2616  
2617      {
2618          DWORD ti = (DWORD)TlsGetValue(tlsi_interrupt);
2619          while (InterlockedIncrement(&flag_interrupt) > 0 && !ti) {
2620              InterlockedDecrement(&flag_interrupt);
2621              Sleep(1);
2622          }
2623          TlsSetValue(tlsi_interrupt, (PVOID)++ti);
2624      }
2625  }
2626  
2627  void rb_w32_leave_critical(void)
2628  {
2629      if (IsWinNT()) {
2630          LeaveCriticalSection(system_state());
2631          return;
2632      }
2633  
2634      InterlockedDecrement(&flag_interrupt);
2635      TlsSetValue(tlsi_interrupt, (PVOID)((DWORD)TlsGetValue(tlsi_interrupt) - 1));
2636  }
2637  
2638  struct handler_arg_t {
2639      void (*handler)(int);
2640      int arg;
2641      int status;
2642      int finished;
2643      HANDLE handshake;
2644  };
2645  
2646  static void rb_w32_call_handler(struct handler_arg_t* h)
2647  {
2648      int status;
2649      RUBY_CRITICAL(rb_protect((VALUE (*)(VALUE))h->handler, (VALUE)h->arg, &h->status);
2650                    status = h->status;
2651                    SetEvent(h->handshake));
2652      if (status) {
2653          rb_jump_tag(status);
2654      }
2655      h->finished = 1;
2656      Sleep(INFINITE);            /* safe on Win95? */
2657  }
2658  
2659  static struct handler_arg_t* setup_handler(struct handler_arg_t *harg,
2660                                             int arg,
2661                                             void (*handler)(int),
2662                                             HANDLE handshake)
2663  {
2664      harg->handler = handler;
2665      harg->arg = arg;
2666      harg->status = 0;
2667      harg->finished = 0;
2668      harg->handshake = handshake;
2669      return harg;
2670  }
2671  
2672  static void setup_call(CONTEXT* ctx, struct handler_arg_t *harg)
2673  {
2674  #ifdef _M_IX86
2675      DWORD *esp = (DWORD *)ctx->Esp;
2676      *--esp = (DWORD)harg;
2677      *--esp = ctx->Eip;
2678      ctx->Esp = (DWORD)esp;
2679      ctx->Eip = (DWORD)rb_w32_call_handler;
2680  #else
2681  #error unsupported processor
2682  #endif
2683  }
2684  
2685  int rb_w32_main_context(int arg, void (*handler)(int))
2686  {
2687      static HANDLE interrupt_done = NULL;
2688      struct handler_arg_t harg;
2689      CONTEXT ctx_orig;
2690      HANDLE current_thread = GetCurrentThread();
2691      int old_priority = GetThreadPriority(current_thread);
2692  
2693      if (GetCurrentThreadId() == main_thread.id) return FALSE;
2694  
2695      SetSignal(interrupted_event);
2696  
2697      RUBY_CRITICAL({             /* the main thread must be in user state */
2698          CONTEXT ctx;
2699  
2700          SuspendThread(main_thread.handle);
2701          SetThreadPriority(current_thread, GetThreadPriority(main_thread.handle));
2702  
2703          ZeroMemory(&ctx, sizeof(CONTEXT));
2704          ctx.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
2705          GetThreadContext(main_thread.handle, &ctx);
2706          ctx_orig = ctx;
2707  
2708          /* handler context setup */
2709          if (!interrupt_done) {
2710              interrupt_done = CreateEvent(NULL, FALSE, FALSE, NULL);
2711              /* anonymous one-shot event */
2712          }
2713          else {
2714              ResetEvent(interrupt_done);
2715          }
2716          setup_call(&ctx, setup_handler(&harg, arg, handler, interrupt_done));
2717  
2718          ctx.ContextFlags = CONTEXT_CONTROL;
2719          SetThreadContext(main_thread.handle, &ctx);
2720          ResumeThread(main_thread.handle);
2721      });
2722  
2723      /* give a chance to the main thread */
2724      yield_once();
2725      WaitForSingleObject(interrupt_done, INFINITE); /* handshaking */
2726  
2727      if (!harg.status) {
2728          /* no exceptions raised, restore old context. */
2729          RUBY_CRITICAL({
2730              /* ensure the main thread is in user state. */
2731              yield_until(harg.finished);
2732  
2733              SuspendThread(main_thread.handle);
2734              ctx_orig.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
2735              SetThreadContext(main_thread.handle, &ctx_orig);
2736              ResumeThread(main_thread.handle);
2737          });
2738      }
2739      /* otherwise leave the main thread raised */
2740  
2741      SetThreadPriority(current_thread, old_priority);
2742  
2743      return TRUE;
2744  }
2745  
2746  int rb_w32_sleep(unsigned long msec)
2747  {
2748      DWORD ret;
2749      RUBY_CRITICAL(ret = wait_events(NULL, msec));
2750      yield_once();
2751      CHECK_INTS;
2752      return ret != WAIT_TIMEOUT;
2753  }
2754  
2755  static void catch_interrupt(void)
2756  {
2757      yield_once();
2758      RUBY_CRITICAL(wait_events(NULL, 0));
2759      CHECK_INTS;
2760  }
2761  
2762  #undef fgetc
2763  int rb_w32_getc(FILE* stream)
2764  {
2765      int c, trap_immediate = rb_trap_immediate;
2766      if (--stream->FILE_COUNT >= 0) {
2767          c = (unsigned char)*stream->FILE_READPTR++;
2768          rb_trap_immediate = trap_immediate;
2769      }
2770      else {
2771          c = _filbuf(stream);
2772  #ifdef __BORLANDC__
2773          if( ( c == EOF )&&( errno == EPIPE ) )
2774          {
2775            clearerr(stream);
2776          }
2777  #endif
2778          rb_trap_immediate = trap_immediate;
2779          catch_interrupt();
2780      }
2781      return c;
2782  }
2783  
2784  #undef fputc
2785  int rb_w32_putc(int c, FILE* stream)
2786  {
2787      int trap_immediate = rb_trap_immediate;
2788      if (--stream->FILE_COUNT >= 0) {
2789          c = (unsigned char)(*stream->FILE_READPTR++ = (char)c);
2790          rb_trap_immediate = trap_immediate;
2791      }
2792      else {
2793          c = _flsbuf(c, stream);
2794          rb_trap_immediate = trap_immediate;
2795          catch_interrupt();
2796      }
2797      return c;
2798  }
2799  
2800  struct asynchronous_arg_t {
2801      /* output field */
2802      void* stackaddr;
2803  
2804      /* input field */
2805      VALUE (*func)(VALUE self, int argc, VALUE* argv);
2806      VALUE self;
2807      int argc;
2808      VALUE* argv;
2809  };
2810  
2811  static DWORD WINAPI
2812  call_asynchronous(PVOID argp)
2813  {
2814      struct asynchronous_arg_t *arg = argp;
2815      arg->stackaddr = &argp;
2816      return (DWORD)arg->func(arg->self, arg->argc, arg->argv);
2817  }
2818  
2819  VALUE rb_w32_asynchronize(asynchronous_func_t func,
2820                           VALUE self, int argc, VALUE* argv, VALUE intrval)
2821  {
2822      DWORD val;
2823      BOOL interrupted = FALSE;
2824      HANDLE thr;
2825  
2826      RUBY_CRITICAL({
2827          struct asynchronous_arg_t arg;
2828  
2829          arg.stackaddr = NULL;
2830          arg.func = func;
2831          arg.self = self;
2832          arg.argc = argc;
2833          arg.argv = argv;
2834  
2835          thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
2836  
2837          if (thr) {
2838              yield_until(arg.stackaddr);
2839  
2840              if (wait_events(thr, INFINITE) != WAIT_OBJECT_0) {
2841                  interrupted = TRUE;
2842  
2843                  if (TerminateThread(thr, intrval)) {
2844                      yield_once();
2845                  }
2846              }
2847  
2848              GetExitCodeThread(thr, &val);
2849              CloseHandle(thr);
2850  
2851              if (interrupted) {
2852                  /* must release stack of killed thread, why doesn't Windows? */
2853                  MEMORY_BASIC_INFORMATION m;
2854  
2855                  memset(&m, 0, sizeof(m));
2856                  if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
2857                      Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
2858                                    arg.stackaddr, GetLastError()));
2859                  }
2860                  else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
2861                      Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
2862                                    m.AllocationBase, GetLastError()));
2863                  }
2864              }
2865          }
2866      });
2867  
2868      if (!thr) {
2869          rb_fatal("failed to launch waiter thread:%d", GetLastError());
2870      }
2871  
2872      if (interrupted) {
2873          errno = EINTR;
2874          CHECK_INTS;
2875      }
2876  
2877      return val;
2878  }
2879  
2880  char **rb_w32_get_environ(void)
2881  {
2882      char *envtop, *env;
2883      char **myenvtop, **myenv;
2884      int num;
2885  
2886      /*
2887       * We avoid values started with `='. If you want to deal those values,
2888       * change this function, and some functions in hash.c which recognize
2889       * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
2890       * CygWin deals these values by changing first `=' to '!'. But we don't
2891       * use such trick and follow cmd.exe's way that just doesn't show these
2892       * values.
2893       * (U.N. 2001-11-15)
2894       */
2895      envtop = GetEnvironmentStrings();
2896      for (env = envtop, num = 0; *env; env += strlen(env) + 1)
2897          if (*env != '=') num++;
2898  
2899      myenvtop = ALLOC_N(char*, num + 1);
2900      for (env = envtop, myenv = myenvtop; *env; env += strlen(env) + 1) {
2901          if (*env != '=') {
2902              *myenv = ALLOC_N(char, strlen(env) + 1);
2903              strcpy(*myenv, env);
2904              myenv++;
2905          }
2906      }
2907      *myenv = NULL;
2908      FreeEnvironmentStrings(envtop);
2909  
2910      return myenvtop;
2911  }
2912  
2913  void rb_w32_free_environ(char **env)
2914  {
2915      char **t = env;
2916  
2917      while (*t) free(*t++);
2918      free(env);
2919  }
2920  
2921  pid_t rb_w32_getpid(void)
2922  {
2923      pid_t pid;
2924  
2925      pid = _getpid();
2926      if (IsWin95()) pid = -pid;
2927  
2928      return pid;
2929  }