Ruby  1.9.3p484(2013-11-22revision43786)
win32.c
Go to the documentation of this file.
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/ruby.h"
14 #include "ruby/encoding.h"
15 #include "dln.h"
16 #include <fcntl.h>
17 #include <process.h>
18 #include <sys/stat.h>
19 /* #include <sys/wait.h> */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include <ctype.h>
25 
26 #include <windows.h>
27 #include <winbase.h>
28 #include <wincon.h>
29 #include <share.h>
30 #include <shlobj.h>
31 #include <mbstring.h>
32 #if _MSC_VER >= 1400
33 #include <crtdbg.h>
34 #include <rtcapi.h>
35 #endif
36 #ifdef __MINGW32__
37 #include <mswsock.h>
38 #endif
39 #include "ruby/win32.h"
40 #include "win32/dir.h"
41 #define isdirsep(x) ((x) == '/' || (x) == '\\')
42 
43 #undef stat
44 #undef fclose
45 #undef close
46 #undef setsockopt
47 
48 #if defined __BORLANDC__
49 # define _filbuf _fgetc
50 # define _flsbuf _fputc
51 # define enough_to_get(n) (--(n) >= 0)
52 # define enough_to_put(n) (++(n) < 0)
53 #else
54 # define enough_to_get(n) (--(n) >= 0)
55 # define enough_to_put(n) (--(n) >= 0)
56 #endif
57 
58 #ifdef WIN32_DEBUG
59 #define Debug(something) something
60 #else
61 #define Debug(something) /* nothing */
62 #endif
63 
64 #define TO_SOCKET(x) _get_osfhandle(x)
65 
66 static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE, DWORD);
67 static int has_redirection(const char *);
68 int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
69 static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
70 static int wstati64(const WCHAR *path, struct stati64 *st);
71 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
72 
73 #define RUBY_CRITICAL(expr) do { expr; } while (0)
74 
75 /* errno mapping */
76 static struct {
78  int err;
79 } errmap[] = {
80  { ERROR_INVALID_FUNCTION, EINVAL },
81  { ERROR_FILE_NOT_FOUND, ENOENT },
82  { ERROR_PATH_NOT_FOUND, ENOENT },
83  { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
84  { ERROR_ACCESS_DENIED, EACCES },
85  { ERROR_INVALID_HANDLE, EBADF },
86  { ERROR_ARENA_TRASHED, ENOMEM },
87  { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
88  { ERROR_INVALID_BLOCK, ENOMEM },
89  { ERROR_BAD_ENVIRONMENT, E2BIG },
90  { ERROR_BAD_FORMAT, ENOEXEC },
91  { ERROR_INVALID_ACCESS, EINVAL },
92  { ERROR_INVALID_DATA, EINVAL },
93  { ERROR_INVALID_DRIVE, ENOENT },
94  { ERROR_CURRENT_DIRECTORY, EACCES },
95  { ERROR_NOT_SAME_DEVICE, EXDEV },
96  { ERROR_NO_MORE_FILES, ENOENT },
97  { ERROR_WRITE_PROTECT, EROFS },
98  { ERROR_BAD_UNIT, ENODEV },
99  { ERROR_NOT_READY, ENXIO },
100  { ERROR_BAD_COMMAND, EACCES },
101  { ERROR_CRC, EACCES },
102  { ERROR_BAD_LENGTH, EACCES },
103  { ERROR_SEEK, EIO },
104  { ERROR_NOT_DOS_DISK, EACCES },
105  { ERROR_SECTOR_NOT_FOUND, EACCES },
106  { ERROR_OUT_OF_PAPER, EACCES },
107  { ERROR_WRITE_FAULT, EIO },
108  { ERROR_READ_FAULT, EIO },
109  { ERROR_GEN_FAILURE, EACCES },
110  { ERROR_LOCK_VIOLATION, EACCES },
111  { ERROR_SHARING_VIOLATION, EACCES },
112  { ERROR_WRONG_DISK, EACCES },
113  { ERROR_SHARING_BUFFER_EXCEEDED, EACCES },
114  { ERROR_BAD_NETPATH, ENOENT },
115  { ERROR_NETWORK_ACCESS_DENIED, EACCES },
116  { ERROR_BAD_NET_NAME, ENOENT },
117  { ERROR_FILE_EXISTS, EEXIST },
118  { ERROR_CANNOT_MAKE, EACCES },
119  { ERROR_FAIL_I24, EACCES },
120  { ERROR_INVALID_PARAMETER, EINVAL },
121  { ERROR_NO_PROC_SLOTS, EAGAIN },
122  { ERROR_DRIVE_LOCKED, EACCES },
123  { ERROR_BROKEN_PIPE, EPIPE },
124  { ERROR_DISK_FULL, ENOSPC },
125  { ERROR_INVALID_TARGET_HANDLE, EBADF },
126  { ERROR_INVALID_HANDLE, EINVAL },
127  { ERROR_WAIT_NO_CHILDREN, ECHILD },
128  { ERROR_CHILD_NOT_COMPLETE, ECHILD },
129  { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
130  { ERROR_NEGATIVE_SEEK, EINVAL },
131  { ERROR_SEEK_ON_DEVICE, EACCES },
132  { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
133  { ERROR_DIRECTORY, ENOTDIR },
134  { ERROR_NOT_LOCKED, EACCES },
135  { ERROR_BAD_PATHNAME, ENOENT },
136  { ERROR_MAX_THRDS_REACHED, EAGAIN },
137  { ERROR_LOCK_FAILED, EACCES },
138  { ERROR_ALREADY_EXISTS, EEXIST },
139  { ERROR_INVALID_STARTING_CODESEG, ENOEXEC },
140  { ERROR_INVALID_STACKSEG, ENOEXEC },
141  { ERROR_INVALID_MODULETYPE, ENOEXEC },
142  { ERROR_INVALID_EXE_SIGNATURE, ENOEXEC },
143  { ERROR_EXE_MARKED_INVALID, ENOEXEC },
144  { ERROR_BAD_EXE_FORMAT, ENOEXEC },
145  { ERROR_ITERATED_DATA_EXCEEDS_64k,ENOEXEC },
146  { ERROR_INVALID_MINALLOCSIZE, ENOEXEC },
147  { ERROR_DYNLINK_FROM_INVALID_RING,ENOEXEC },
148  { ERROR_IOPL_NOT_ENABLED, ENOEXEC },
149  { ERROR_INVALID_SEGDPL, ENOEXEC },
150  { ERROR_AUTODATASEG_EXCEEDS_64k, ENOEXEC },
151  { ERROR_RING2SEG_MUST_BE_MOVABLE, ENOEXEC },
152  { ERROR_RELOC_CHAIN_XEEDS_SEGLIM, ENOEXEC },
153  { ERROR_INFLOOP_IN_RELOC_CHAIN, ENOEXEC },
154  { ERROR_FILENAME_EXCED_RANGE, ENOENT },
155  { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
156 #ifndef ERROR_PIPE_LOCAL
157 #define ERROR_PIPE_LOCAL 229L
158 #endif
159  { ERROR_PIPE_LOCAL, EPIPE },
160  { ERROR_BAD_PIPE, EPIPE },
161  { ERROR_PIPE_BUSY, EAGAIN },
162  { ERROR_NO_DATA, EPIPE },
163  { ERROR_PIPE_NOT_CONNECTED, EPIPE },
164  { ERROR_OPERATION_ABORTED, EINTR },
165  { ERROR_NOT_ENOUGH_QUOTA, ENOMEM },
166  { ERROR_MOD_NOT_FOUND, ENOENT },
167  { WSAEINTR, EINTR },
168  { WSAEBADF, EBADF },
169  { WSAEACCES, EACCES },
170  { WSAEFAULT, EFAULT },
171  { WSAEINVAL, EINVAL },
172  { WSAEMFILE, EMFILE },
173  { WSAEWOULDBLOCK, EWOULDBLOCK },
174  { WSAEINPROGRESS, EINPROGRESS },
175  { WSAEALREADY, EALREADY },
176  { WSAENOTSOCK, ENOTSOCK },
177  { WSAEDESTADDRREQ, EDESTADDRREQ },
178  { WSAEMSGSIZE, EMSGSIZE },
179  { WSAEPROTOTYPE, EPROTOTYPE },
180  { WSAENOPROTOOPT, ENOPROTOOPT },
181  { WSAEPROTONOSUPPORT, EPROTONOSUPPORT },
182  { WSAESOCKTNOSUPPORT, ESOCKTNOSUPPORT },
183  { WSAEOPNOTSUPP, EOPNOTSUPP },
184  { WSAEPFNOSUPPORT, EPFNOSUPPORT },
185  { WSAEAFNOSUPPORT, EAFNOSUPPORT },
186  { WSAEADDRINUSE, EADDRINUSE },
187  { WSAEADDRNOTAVAIL, EADDRNOTAVAIL },
188  { WSAENETDOWN, ENETDOWN },
189  { WSAENETUNREACH, ENETUNREACH },
190  { WSAENETRESET, ENETRESET },
191  { WSAECONNABORTED, ECONNABORTED },
192  { WSAECONNRESET, ECONNRESET },
193  { WSAENOBUFS, ENOBUFS },
194  { WSAEISCONN, EISCONN },
195  { WSAENOTCONN, ENOTCONN },
196  { WSAESHUTDOWN, ESHUTDOWN },
197  { WSAETOOMANYREFS, ETOOMANYREFS },
198  { WSAETIMEDOUT, ETIMEDOUT },
199  { WSAECONNREFUSED, ECONNREFUSED },
200  { WSAELOOP, ELOOP },
201  { WSAENAMETOOLONG, ENAMETOOLONG },
202  { WSAEHOSTDOWN, EHOSTDOWN },
203  { WSAEHOSTUNREACH, EHOSTUNREACH },
204  { WSAEPROCLIM, EPROCLIM },
205  { WSAENOTEMPTY, ENOTEMPTY },
206  { WSAEUSERS, EUSERS },
207  { WSAEDQUOT, EDQUOT },
208  { WSAESTALE, ESTALE },
209  { WSAEREMOTE, EREMOTE },
210 };
211 
212 int
214 {
215  int i;
216 
217  if (winerr == 0) {
218  return 0;
219  }
220 
221  for (i = 0; i < (int)(sizeof(errmap) / sizeof(*errmap)); i++) {
222  if (errmap[i].winerr == winerr) {
223  return errmap[i].err;
224  }
225  }
226 
227  if (winerr >= WSABASEERR) {
228  return winerr;
229  }
230  return EINVAL;
231 }
232 
233 #define map_errno rb_w32_map_errno
234 
235 static const char *NTLoginName;
236 
237 static OSVERSIONINFO osver;
238 
239 static void
241 {
242  memset(&osver, 0, sizeof(OSVERSIONINFO));
243  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
244  GetVersionEx(&osver);
245 }
246 
247 #ifdef _M_IX86
248 DWORD
249 rb_w32_osid(void)
250 {
251  return osver.dwPlatformId;
252 }
253 #endif
254 
255 DWORD
257 {
258  return osver.dwMajorVersion;
259 }
260 
261 #define IsWinNT() rb_w32_iswinnt()
262 #define IsWin95() rb_w32_iswin95()
263 #ifdef WIN95
264 #define IfWin95(win95, winnt) (IsWin95() ? (win95) : (winnt))
265 #else
266 #define IfWin95(win95, winnt) (winnt)
267 #endif
268 
269 HANDLE
271 {
272  static HANDLE current_process_handle = NULL;
273  HANDLE h;
274 
275  if (!current_process_handle)
276  current_process_handle = GetCurrentProcess();
277  if (!DuplicateHandle(current_process_handle, GetCurrentThread(),
278  current_process_handle, &h,
279  0, FALSE, DUPLICATE_SAME_ACCESS))
280  return NULL;
281  return h;
282 }
283 
284 /* simulate flock by locking a range on the file */
285 
286 
287 #define LK_ERR(f,i) \
288  do { \
289  if (f) \
290  i = 0; \
291  else { \
292  DWORD err = GetLastError(); \
293  if (err == ERROR_LOCK_VIOLATION || err == ERROR_IO_PENDING) \
294  errno = EWOULDBLOCK; \
295  else if (err == ERROR_NOT_LOCKED) \
296  i = 0; \
297  else \
298  errno = map_errno(err); \
299  } \
300  } while (0)
301 #define LK_LEN ULONG_MAX
302 
303 static uintptr_t
305 {
306  OVERLAPPED o;
307  int i = -1;
308  const HANDLE fh = (HANDLE)self;
309  const int oper = argc;
310 
311  memset(&o, 0, sizeof(o));
312 
313  switch(oper) {
314  case LOCK_SH: /* shared lock */
315  LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
316  break;
317  case LOCK_EX: /* exclusive lock */
318  LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
319  break;
320  case LOCK_SH|LOCK_NB: /* non-blocking shared lock */
321  LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
322  break;
323  case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */
324  LK_ERR(LockFileEx(fh,
325  LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
326  0, LK_LEN, LK_LEN, &o), i);
327  break;
328  case LOCK_UN: /* unlock lock */
329  case LOCK_UN|LOCK_NB: /* unlock is always non-blocking, I hope */
330  LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
331  break;
332  default: /* unknown */
333  errno = EINVAL;
334  break;
335  }
336  return i;
337 }
338 
339 #ifdef WIN95
340 static uintptr_t
341 flock_win95(uintptr_t self, int argc, uintptr_t* argv)
342 {
343  int i = -1;
344  const HANDLE fh = (HANDLE)self;
345  const int oper = argc;
346 
347  switch(oper) {
348  case LOCK_EX:
349  do {
350  LK_ERR(LockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
351  } while (i && errno == EWOULDBLOCK);
352  break;
353  case LOCK_EX|LOCK_NB:
354  LK_ERR(LockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
355  break;
356  case LOCK_UN:
357  case LOCK_UN|LOCK_NB:
358  LK_ERR(UnlockFile(fh, 0, 0, LK_LEN, LK_LEN), i);
359  break;
360  default:
361  errno = EINVAL;
362  break;
363  }
364  return i;
365 }
366 #endif
367 
368 #undef LK_ERR
369 
370 int
371 flock(int fd, int oper)
372 {
373 #ifdef WIN95
374  static asynchronous_func_t locker = NULL;
375 
376  if (!locker) {
377  if (IsWinNT())
378  locker = flock_winnt;
379  else
380  locker = flock_win95;
381  }
382 #else
383  const asynchronous_func_t locker = flock_winnt;
384 #endif
385 
386  return rb_w32_asynchronize(locker,
387  (VALUE)_get_osfhandle(fd), oper, NULL,
388  (DWORD)-1);
389 }
390 
391 static inline WCHAR *
392 translate_wchar(WCHAR *p, int from, int to)
393 {
394  for (; *p; p++) {
395  if (*p == from)
396  *p = to;
397  }
398  return p;
399 }
400 
401 static inline char *
402 translate_char(char *p, int from, int to)
403 {
404  while (*p) {
405  if ((unsigned char)*p == from)
406  *p = to;
407  p = CharNext(p);
408  }
409  return p;
410 }
411 
412 #ifndef CSIDL_LOCAL_APPDATA
413 #define CSIDL_LOCAL_APPDATA 28
414 #endif
415 #ifndef CSIDL_COMMON_APPDATA
416 #define CSIDL_COMMON_APPDATA 35
417 #endif
418 #ifndef CSIDL_WINDOWS
419 #define CSIDL_WINDOWS 36
420 #endif
421 #ifndef CSIDL_SYSTEM
422 #define CSIDL_SYSTEM 37
423 #endif
424 #ifndef CSIDL_PROFILE
425 #define CSIDL_PROFILE 40
426 #endif
427 
428 static BOOL
429 get_special_folder(int n, WCHAR *env)
430 {
431  LPITEMIDLIST pidl;
432  LPMALLOC alloc;
433  BOOL f = FALSE;
434  if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
435  f = SHGetPathFromIDListW(pidl, env);
436  SHGetMalloc(&alloc);
437  alloc->lpVtbl->Free(alloc, pidl);
438  alloc->lpVtbl->Release(alloc);
439  }
440  return f;
441 }
442 
443 static void
444 regulate_path(WCHAR *path)
445 {
446  WCHAR *p = translate_wchar(path, L'\\', L'/');
447  if (p - path == 2 && path[1] == L':') {
448  *p++ = L'/';
449  *p = L'\0';
450  }
451 }
452 
453 static FARPROC
454 get_proc_address(const char *module, const char *func, HANDLE *mh)
455 {
456  HANDLE h;
457  FARPROC ptr;
458 
459  if (mh)
460  h = LoadLibrary(module);
461  else
462  h = GetModuleHandle(module);
463  if (!h)
464  return NULL;
465 
466  ptr = GetProcAddress(h, func);
467  if (mh) {
468  if (ptr)
469  *mh = h;
470  else
471  FreeLibrary(h);
472  }
473  return ptr;
474 }
475 
476 static UINT
477 get_system_directory(WCHAR *path, UINT len)
478 {
479  typedef UINT WINAPI wgetdir_func(WCHAR*, UINT);
480  FARPROC ptr =
481  get_proc_address("kernel32", "GetSystemWindowsDirectoryW", NULL);
482  if (ptr)
483  return (*(wgetdir_func *)ptr)(path, len);
484  return GetWindowsDirectoryW(path, len);
485 }
486 
487 #define numberof(array) (sizeof(array) / sizeof(*array))
488 
489 VALUE
491 {
492  WCHAR path[_MAX_PATH];
493 
494  if (!get_special_folder(type, path)) return Qnil;
495  regulate_path(path);
497 }
498 
499 UINT
500 rb_w32_system_tmpdir(WCHAR *path, UINT len)
501 {
502  static const WCHAR temp[] = L"temp";
503  WCHAR *p;
504 
506  if (get_system_directory(path, len)) return 0;
507  }
508  p = translate_wchar(path, L'\\', L'/');
509  if (*(p - 1) != L'/') *p++ = L'/';
510  if (p - path + numberof(temp) >= len) return 0;
511  memcpy(p, temp, sizeof(temp));
512  return p - path + numberof(temp) - 1;
513 }
514 
515 static void
516 init_env(void)
517 {
518  static const WCHAR TMPDIR[] = L"TMPDIR";
519  struct {WCHAR name[6], eq, val[_MAX_PATH];} wk;
520  DWORD len;
521  BOOL f;
522 #define env wk.val
523 #define set_env_val(vname) do { \
524  typedef char namesizecheck[numberof(wk.name) < numberof(vname) - 1 ? -1 : 1]; \
525  WCHAR *const buf = wk.name + numberof(wk.name) - numberof(vname) + 1; \
526  MEMCPY(buf, vname, WCHAR, numberof(vname) - 1); \
527  _wputenv(buf); \
528  } while (0)
529 
530  wk.eq = L'=';
531 
532  if (!GetEnvironmentVariableW(L"HOME", env, numberof(env))) {
533  f = FALSE;
534  if (GetEnvironmentVariableW(L"HOMEDRIVE", env, numberof(env)))
535  len = lstrlenW(env);
536  else
537  len = 0;
538  if (GetEnvironmentVariableW(L"HOMEPATH", env + len, numberof(env) - len) || len) {
539  f = TRUE;
540  }
541  else if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
542  f = TRUE;
543  }
544  else if (get_special_folder(CSIDL_PROFILE, env)) {
545  f = TRUE;
546  }
547  else if (get_special_folder(CSIDL_PERSONAL, env)) {
548  f = TRUE;
549  }
550  if (f) {
552  set_env_val(L"HOME");
553  }
554  }
555 
556  if (!GetEnvironmentVariableW(L"USER", env, numberof(env))) {
557  if (!GetEnvironmentVariableW(L"USERNAME", env, numberof(env)) &&
558  !GetUserNameW(env, (len = numberof(env), &len))) {
559  NTLoginName = "<Unknown>";
560  return;
561  }
562  set_env_val(L"USER");
563  }
564  NTLoginName = strdup(rb_w32_getenv("USER"));
565 
566  if (!GetEnvironmentVariableW(TMPDIR, env, numberof(env)) &&
567  !GetEnvironmentVariableW(L"TMP", env, numberof(env)) &&
568  !GetEnvironmentVariableW(L"TEMP", env, numberof(env)) &&
570  set_env_val(TMPDIR);
571  }
572 
573 #undef env
574 #undef set_env_val
575 }
576 
577 
578 typedef BOOL (WINAPI *cancel_io_t)(HANDLE);
580 
581 static void
583 {
584  if (!cancel_io)
585  cancel_io = (cancel_io_t)get_proc_address("kernel32", "CancelIo", NULL);
586 }
587 
588 static void init_stdhandle(void);
589 
590 #if RT_VER >= 80
591 static void
592 invalid_parameter(const wchar_t *expr, const wchar_t *func, const wchar_t *file, unsigned int line, uintptr_t dummy)
593 {
594  // nothing to do
595 }
596 
597 int ruby_w32_rtc_error;
598 
599 static int __cdecl
600 rtc_error_handler(int e, const char *src, int line, const char *exe, const char *fmt, ...)
601 {
602  va_list ap;
603  VALUE str;
604 
605  if (!ruby_w32_rtc_error) return 0;
606  str = rb_sprintf("%s:%d: ", src, line);
607  va_start(ap, fmt);
608  rb_str_vcatf(str, fmt, ap);
609  va_end(ap);
610  rb_str_cat(str, "\n", 1);
612  return 0;
613 }
614 #endif
615 
616 static CRITICAL_SECTION select_mutex;
617 static int NtSocketsInitialized = 0;
619 static char *envarea;
620 
621 static void
623 {
624  if (NtSocketsInitialized) {
625  WSACleanup();
626  st_free_table(socklist);
627  socklist = NULL;
628  NtSocketsInitialized = 0;
629  }
630  if (envarea) {
631  FreeEnvironmentStrings(envarea);
632  envarea = NULL;
633  }
634  DeleteCriticalSection(&select_mutex);
635 }
636 
637 static void
639 {
640  WORD version;
641  WSADATA retdata;
642 
643  //
644  // initalize the winsock interface and insure that it's
645  // cleaned up at exit.
646  //
647  version = MAKEWORD(2, 0);
648  if (WSAStartup(version, &retdata))
649  rb_fatal ("Unable to locate winsock library!\n");
650  if (LOBYTE(retdata.wVersion) != 2)
651  rb_fatal("could not find version 2 of winsock dll\n");
652 
653  socklist = st_init_numtable();
654 
655  NtSocketsInitialized = 1;
656 }
657 
658 //
659 // Initialization stuff
660 //
661 void
662 rb_w32_sysinit(int *argc, char ***argv)
663 {
664 #if RT_VER >= 80
665  static void set_pioinfo_extra(void);
666 
667  _CrtSetReportMode(_CRT_ASSERT, 0);
668  _set_invalid_parameter_handler(invalid_parameter);
669  _RTC_SetErrorFunc(rtc_error_handler);
670  set_pioinfo_extra();
671 #else
672  SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
673 #endif
674 
675  get_version();
676 
677  //
678  // subvert cmd.exe's feeble attempt at command line parsing
679  //
680  *argc = rb_w32_cmdvector(GetCommandLine(), argv);
681 
682  //
683  // Now set up the correct time stuff
684  //
685 
686  tzset();
687 
688  init_env();
689 
690  init_func();
691 
692  init_stdhandle();
693 
694  InitializeCriticalSection(&select_mutex);
695 
696  atexit(exit_handler);
697 
698  // Initialize Winsock
699  StartSockets();
700 }
701 
702 char *
703 getlogin(void)
704 {
705  return (char *)NTLoginName;
706 }
707 
708 #define MAXCHILDNUM 256 /* max num of child processes */
709 
710 static struct ChildRecord {
711  HANDLE hProcess; /* process handle */
712  rb_pid_t pid; /* process id */
714 
715 #define FOREACH_CHILD(v) do { \
716  struct ChildRecord* v; \
717  for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
718 #define END_FOREACH_CHILD } while (0)
719 
720 static struct ChildRecord *
722 {
723 
724  FOREACH_CHILD(child) {
725  if (child->pid == pid) {
726  return child;
727  }
729  return NULL;
730 }
731 
732 static struct ChildRecord *
734 {
735 
736  FOREACH_CHILD(child) {
737  if (child->hProcess == h) {
738  return child;
739  }
741  return NULL;
742 }
743 
744 static void
746 {
747  HANDLE h = child->hProcess;
748  child->hProcess = NULL;
749  child->pid = 0;
750  CloseHandle(h);
751 }
752 
753 static struct ChildRecord *
755 {
756  FOREACH_CHILD(child) {
757  if (!child->pid) {
758  child->pid = -1; /* lock the slot */
759  child->hProcess = NULL;
760  return child;
761  }
763  return NULL;
764 }
765 
766 
767 /*
768  ruby -lne 'BEGIN{$cmds = Hash.new(0); $mask = 1}'
769  -e '$cmds[$_.downcase] |= $mask' -e '$mask <<= 1 if ARGF.eof'
770  -e 'END{$cmds.sort.each{|n,f|puts " \"\\#{f.to_s(8)}\" #{n.dump} + 1,"}}'
771  98cmd ntcmd
772  */
773 static const char *const szInternalCmds[] = {
774  "\2" "assoc" + 1,
775  "\3" "break" + 1,
776  "\3" "call" + 1,
777  "\3" "cd" + 1,
778  "\1" "chcp" + 1,
779  "\3" "chdir" + 1,
780  "\3" "cls" + 1,
781  "\2" "color" + 1,
782  "\3" "copy" + 1,
783  "\1" "ctty" + 1,
784  "\3" "date" + 1,
785  "\3" "del" + 1,
786  "\3" "dir" + 1,
787  "\3" "echo" + 1,
788  "\2" "endlocal" + 1,
789  "\3" "erase" + 1,
790  "\3" "exit" + 1,
791  "\3" "for" + 1,
792  "\2" "ftype" + 1,
793  "\3" "goto" + 1,
794  "\3" "if" + 1,
795  "\1" "lfnfor" + 1,
796  "\1" "lh" + 1,
797  "\1" "lock" + 1,
798  "\3" "md" + 1,
799  "\3" "mkdir" + 1,
800  "\2" "move" + 1,
801  "\3" "path" + 1,
802  "\3" "pause" + 1,
803  "\2" "popd" + 1,
804  "\3" "prompt" + 1,
805  "\2" "pushd" + 1,
806  "\3" "rd" + 1,
807  "\3" "rem" + 1,
808  "\3" "ren" + 1,
809  "\3" "rename" + 1,
810  "\3" "rmdir" + 1,
811  "\3" "set" + 1,
812  "\2" "setlocal" + 1,
813  "\3" "shift" + 1,
814  "\2" "start" + 1,
815  "\3" "time" + 1,
816  "\2" "title" + 1,
817  "\1" "truename" + 1,
818  "\3" "type" + 1,
819  "\1" "unlock" + 1,
820  "\3" "ver" + 1,
821  "\3" "verify" + 1,
822  "\3" "vol" + 1,
823 };
824 
825 static int
826 internal_match(const void *key, const void *elem)
827 {
828  return strcmp(key, *(const char *const *)elem);
829 }
830 
831 static int
832 is_command_com(const char *interp)
833 {
834  int i = strlen(interp) - 11;
835 
836  if ((i == 0 || i > 0 && isdirsep(interp[i-1])) &&
837  strcasecmp(interp+i, "command.com") == 0) {
838  return 1;
839  }
840  return 0;
841 }
842 
843 static int internal_cmd_match(const char *cmdname, int nt);
844 
845 static int
846 is_internal_cmd(const char *cmd, int nt)
847 {
848  char cmdname[9], *b = cmdname, c;
849 
850  do {
851  if (!(c = *cmd++)) return 0;
852  } while (isspace(c));
853  if (c == '@')
854  return 1;
855  while (isalpha(c)) {
856  *b++ = tolower(c);
857  if (b == cmdname + sizeof(cmdname)) return 0;
858  c = *cmd++;
859  }
860  if (c == '.') c = *cmd;
861  switch (c) {
862  case '<': case '>': case '|':
863  return 1;
864  case '\0': case ' ': case '\t': case '\n':
865  break;
866  default:
867  return 0;
868  }
869  *b = 0;
870  return internal_cmd_match(cmdname, nt);
871 }
872 
873 static int
874 internal_cmd_match(const char *cmdname, int nt)
875 {
876  char **nm;
877 
878  nm = bsearch(cmdname, szInternalCmds,
879  sizeof(szInternalCmds) / sizeof(*szInternalCmds),
880  sizeof(*szInternalCmds),
882  if (!nm || !(nm[0][-1] & (nt ? 2 : 1)))
883  return 0;
884  return 1;
885 }
886 
887 SOCKET
889 {
890  return _get_osfhandle(fh);
891 }
892 
893 static int
894 join_argv(char *cmd, char *const *argv, BOOL escape)
895 {
896  const char *p, *s;
897  char *q, *const *t;
898  int len, n, bs, quote;
899 
900  for (t = argv, q = cmd, len = 0; p = *t; t++) {
901  quote = 0;
902  s = p;
903  if (!*p || strpbrk(p, " \t\"'")) {
904  quote = 1;
905  len++;
906  if (q) *q++ = '"';
907  }
908  for (bs = 0; *p; ++p) {
909  switch (*p) {
910  case '\\':
911  ++bs;
912  break;
913  case '"':
914  len += n = p - s;
915  if (q) {
916  memcpy(q, s, n);
917  q += n;
918  }
919  s = p;
920  len += ++bs;
921  if (q) {
922  memset(q, '\\', bs);
923  q += bs;
924  }
925  bs = 0;
926  break;
927  case '<': case '>': case '|': case '^':
928  if (escape && !quote) {
929  len += (n = p - s) + 1;
930  if (q) {
931  memcpy(q, s, n);
932  q += n;
933  *q++ = '^';
934  }
935  s = p;
936  break;
937  }
938  default:
939  bs = 0;
940  p = CharNext(p) - 1;
941  break;
942  }
943  }
944  len += (n = p - s) + 1;
945  if (quote) len++;
946  if (q) {
947  memcpy(q, s, n);
948  q += n;
949  if (quote) *q++ = '"';
950  *q++ = ' ';
951  }
952  }
953  if (q > cmd) --len;
954  if (q) {
955  if (q > cmd) --q;
956  *q = '\0';
957  }
958  return len;
959 }
960 
961 #ifdef HAVE_SYS_PARAM_H
962 # include <sys/param.h>
963 #else
964 # define MAXPATHLEN 512
965 #endif
966 
967 #define STRNDUPV(ptr, v, src, len) \
968  (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
969 
970 static int
972 {
973  switch (mode) {
974  case P_NOWAIT:
975  case P_OVERLAY:
976  return 0;
977  default:
978  errno = EINVAL;
979  return -1;
980  }
981 }
982 
983 static rb_pid_t
984 child_result(struct ChildRecord *child, int mode)
985 {
986  DWORD exitcode;
987 
988  if (!child) {
989  return -1;
990  }
991 
992  switch (mode) {
993  case P_NOWAIT:
994  return child->pid;
995  case P_OVERLAY:
996  WaitForSingleObject(child->hProcess, INFINITE);
997  GetExitCodeProcess(child->hProcess, &exitcode);
998  CloseChildHandle(child);
999  _exit(exitcode);
1000  default:
1001  return -1; /* not reached */
1002  }
1003 }
1004 
1005 static struct ChildRecord *
1006 CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
1007  HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
1008 {
1009  BOOL fRet;
1010  STARTUPINFOW aStartupInfo;
1011  PROCESS_INFORMATION aProcessInformation;
1012  SECURITY_ATTRIBUTES sa;
1013  struct ChildRecord *child;
1014 
1015  if (!cmd && !prog) {
1016  errno = EFAULT;
1017  return NULL;
1018  }
1019 
1020  child = FindFreeChildSlot();
1021  if (!child) {
1022  errno = EAGAIN;
1023  return NULL;
1024  }
1025 
1026  if (!psa) {
1027  sa.nLength = sizeof (SECURITY_ATTRIBUTES);
1028  sa.lpSecurityDescriptor = NULL;
1029  sa.bInheritHandle = TRUE;
1030  psa = &sa;
1031  }
1032 
1033  memset(&aStartupInfo, 0, sizeof(aStartupInfo));
1034  memset(&aProcessInformation, 0, sizeof(aProcessInformation));
1035  aStartupInfo.cb = sizeof(aStartupInfo);
1036  aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
1037  if (hInput) {
1038  aStartupInfo.hStdInput = hInput;
1039  }
1040  else {
1041  aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1042  }
1043  if (hOutput) {
1044  aStartupInfo.hStdOutput = hOutput;
1045  }
1046  else {
1047  aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1048  }
1049  if (hError) {
1050  aStartupInfo.hStdError = hError;
1051  }
1052  else {
1053  aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1054  }
1055 
1056  dwCreationFlags |= NORMAL_PRIORITY_CLASS;
1057 
1058  if (lstrlenW(cmd) > 32767) {
1059  child->pid = 0; /* release the slot */
1060  errno = E2BIG;
1061  return NULL;
1062  }
1063 
1064  RUBY_CRITICAL({
1065  fRet = CreateProcessW(prog, (WCHAR *)cmd, psa, psa,
1066  psa->bInheritHandle, dwCreationFlags, NULL, NULL,
1067  &aStartupInfo, &aProcessInformation);
1068  errno = map_errno(GetLastError());
1069  });
1070 
1071  if (!fRet) {
1072  child->pid = 0; /* release the slot */
1073  return NULL;
1074  }
1075 
1076  CloseHandle(aProcessInformation.hThread);
1077 
1078  child->hProcess = aProcessInformation.hProcess;
1079  child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
1080 
1081  if (!IsWinNT()) {
1082  /* On Win9x, make pid positive similarly to cygwin and perl */
1083  child->pid = -child->pid;
1084  }
1085 
1086  return child;
1087 }
1088 
1089 static int
1090 is_batch(const char *cmd)
1091 {
1092  int len = strlen(cmd);
1093  if (len <= 4) return 0;
1094  cmd += len - 4;
1095  if (*cmd++ != '.') return 0;
1096  if (strcasecmp(cmd, "bat") == 0) return 1;
1097  if (strcasecmp(cmd, "cmd") == 0) return 1;
1098  return 0;
1099 }
1100 
1101 static UINT filecp(void);
1102 static WCHAR *mbstr_to_wstr(UINT, const char *, int, long *);
1103 static char *wstr_to_mbstr(UINT, const WCHAR *, int, long *);
1104 #define acp_to_wstr(str, plen) mbstr_to_wstr(CP_ACP, str, -1, plen)
1105 #define wstr_to_acp(str, plen) wstr_to_mbstr(CP_ACP, str, -1, plen)
1106 #define filecp_to_wstr(str, plen) mbstr_to_wstr(filecp(), str, -1, plen)
1107 #define wstr_to_filecp(str, plen) wstr_to_mbstr(filecp(), str, -1, plen)
1108 #define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
1109 #define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
1110 
1111 rb_pid_t
1112 rb_w32_spawn(int mode, const char *cmd, const char *prog)
1113 {
1114  char fbuf[MAXPATHLEN];
1115  char *p = NULL;
1116  const char *shell = NULL;
1117  WCHAR *wcmd, *wshell;
1118  rb_pid_t ret;
1119  VALUE v = 0;
1120  VALUE v2 = 0;
1121 
1122  if (check_spawn_mode(mode)) return -1;
1123 
1124  if (prog) {
1125  if (!(p = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1126  shell = prog;
1127  }
1128  else {
1129  shell = p;
1130  translate_char(p, '/', '\\');
1131  }
1132  }
1133  else {
1134  int redir = -1;
1135  int nt;
1136  while (ISSPACE(*cmd)) cmd++;
1137  if ((shell = getenv("RUBYSHELL")) && (redir = has_redirection(cmd))) {
1138  char *tmp = ALLOCV(v, strlen(shell) + strlen(cmd) + sizeof(" -c ") + 2);
1139  sprintf(tmp, "%s -c \"%s\"", shell, cmd);
1140  cmd = tmp;
1141  }
1142  else if ((shell = getenv("COMSPEC")) &&
1143  (nt = !is_command_com(shell),
1144  (redir < 0 ? has_redirection(cmd) : redir) ||
1145  is_internal_cmd(cmd, nt))) {
1146  char *tmp = ALLOCV(v, strlen(shell) + strlen(cmd) + sizeof(" /c ") + (nt ? 2 : 0));
1147  sprintf(tmp, nt ? "%s /c \"%s\"" : "%s /c %s", shell, cmd);
1148  cmd = tmp;
1149  }
1150  else {
1151  int len = 0, quote = (*cmd == '"') ? '"' : (*cmd == '\'') ? '\'' : 0;
1152  for (prog = cmd + !!quote;; prog = CharNext(prog)) {
1153  if (!*prog) {
1154  len = prog - cmd;
1155  shell = cmd;
1156  break;
1157  }
1158  if ((unsigned char)*prog == quote) {
1159  len = prog++ - cmd - 1;
1160  STRNDUPV(p, v2, cmd + 1, len);
1161  shell = p;
1162  break;
1163  }
1164  if (quote) continue;
1165  if (ISSPACE(*prog) || strchr("<>|*?\"", *prog)) {
1166  len = prog - cmd;
1167  STRNDUPV(p, v2, cmd, len);
1168  shell = p;
1169  break;
1170  }
1171  }
1172  shell = dln_find_exe_r(shell, NULL, fbuf, sizeof(fbuf));
1173  if (!shell) {
1174  shell = p ? p : cmd;
1175  }
1176  else {
1177  len = strlen(shell);
1178  if (strchr(shell, ' ')) quote = -1;
1179  if (shell == fbuf) {
1180  p = fbuf;
1181  }
1182  else if (shell != p && strchr(shell, '/')) {
1183  STRNDUPV(p, v2, shell, len);
1184  shell = p;
1185  }
1186  if (p) translate_char(p, '/', '\\');
1187  if (is_batch(shell)) {
1188  int alen = strlen(prog);
1189  cmd = p = ALLOCV(v, len + alen + (quote ? 2 : 0) + 1);
1190  if (quote) *p++ = '"';
1191  memcpy(p, shell, len);
1192  p += len;
1193  if (quote) *p++ = '"';
1194  memcpy(p, prog, alen + 1);
1195  shell = 0;
1196  }
1197  }
1198  }
1199  }
1200 
1201  /* assume ACP */
1202  wcmd = cmd ? acp_to_wstr(cmd, NULL) : NULL;
1203  if (v) ALLOCV_END(v);
1204  wshell = shell ? acp_to_wstr(shell, NULL) : NULL;
1205  if (v2) ALLOCV_END(v2);
1206 
1207  ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL, 0), mode);
1208  free(wshell);
1209  free(wcmd);
1210  return ret;
1211 }
1212 
1213 rb_pid_t
1214 rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1215 {
1216  int c_switch = 0;
1217  size_t len;
1218  BOOL ntcmd = FALSE, tmpnt;
1219  const char *shell;
1220  char *cmd, fbuf[MAXPATHLEN];
1221  WCHAR *wcmd, *wprog;
1222  rb_pid_t ret;
1223  VALUE v = 0;
1224 
1225  if (check_spawn_mode(mode)) return -1;
1226 
1227  if (!prog) prog = argv[0];
1228  if ((shell = getenv("COMSPEC")) &&
1229  internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
1230  ntcmd = tmpnt;
1231  prog = shell;
1232  c_switch = 1;
1233  }
1234  else if ((cmd = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1235  if (cmd == prog) strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1236  translate_char(cmd, '/', '\\');
1237  prog = cmd;
1238  }
1239  else if (strchr(prog, '/')) {
1240  len = strlen(prog);
1241  if (len < sizeof(fbuf))
1242  strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1243  else
1244  STRNDUPV(cmd, v, prog, len);
1245  translate_char(cmd, '/', '\\');
1246  prog = cmd;
1247  }
1248  if (c_switch || is_batch(prog)) {
1249  char *progs[2];
1250  progs[0] = (char *)prog;
1251  progs[1] = NULL;
1252  len = join_argv(NULL, progs, ntcmd);
1253  if (c_switch) len += 3;
1254  else ++argv;
1255  if (argv[0]) len += join_argv(NULL, argv, ntcmd);
1256  cmd = ALLOCV(v, len);
1257  join_argv(cmd, progs, ntcmd);
1258  if (c_switch) strlcat(cmd, " /c", len);
1259  if (argv[0]) join_argv(cmd + strlcat(cmd, " ", len), argv, ntcmd);
1260  prog = c_switch ? shell : 0;
1261  }
1262  else {
1263  len = join_argv(NULL, argv, FALSE);
1264  cmd = ALLOCV(v, len);
1265  join_argv(cmd, argv, FALSE);
1266  }
1267 
1268  /* assume ACP */
1269  wcmd = cmd ? acp_to_wstr(cmd, NULL) : NULL;
1270  if (v) ALLOCV_END(v);
1271  wprog = prog ? acp_to_wstr(prog, NULL) : NULL;
1272 
1273  ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL, flags), mode);
1274  free(wprog);
1275  free(wcmd);
1276  return ret;
1277 }
1278 
1279 rb_pid_t
1280 rb_w32_aspawn(int mode, const char *prog, char *const *argv)
1281 {
1282  return rb_w32_aspawn_flags(mode, prog, argv, 0);
1283 }
1284 
1285 /* License: Artistic or GPL */
1286 typedef struct _NtCmdLineElement {
1288  char *str;
1289  int len;
1290  int flags;
1292 
1293 //
1294 // Possible values for flags
1295 //
1296 
1297 #define NTGLOB 0x1 // element contains a wildcard
1298 #define NTMALLOC 0x2 // string in element was malloc'ed
1299 #define NTSTRING 0x4 // element contains a quoted string
1300 
1301 static int
1302 insert(const char *path, VALUE vinfo, void *enc)
1303 {
1304  NtCmdLineElement *tmpcurr;
1305  NtCmdLineElement ***tail = (NtCmdLineElement ***)vinfo;
1306 
1307  tmpcurr = (NtCmdLineElement *)malloc(sizeof(NtCmdLineElement));
1308  if (!tmpcurr) return -1;
1309  MEMZERO(tmpcurr, NtCmdLineElement, 1);
1310  tmpcurr->len = strlen(path);
1311  tmpcurr->str = strdup(path);
1312  if (!tmpcurr->str) return -1;
1313  tmpcurr->flags |= NTMALLOC;
1314  **tail = tmpcurr;
1315  *tail = &tmpcurr->next;
1316 
1317  return 0;
1318 }
1319 
1320 
1321 static NtCmdLineElement **
1323 {
1324  char buffer[MAXPATHLEN], *buf = buffer;
1325  char *p;
1326  NtCmdLineElement **last = tail;
1327  int status;
1328 
1329  if (patt->len >= MAXPATHLEN)
1330  if (!(buf = malloc(patt->len + 1))) return 0;
1331 
1332  strlcpy(buf, patt->str, patt->len + 1);
1333  buf[patt->len] = '\0';
1334  for (p = buf; *p; p = CharNext(p))
1335  if (*p == '\\')
1336  *p = '/';
1337  status = ruby_brace_glob(buf, 0, insert, (VALUE)&tail);
1338  if (buf != buffer)
1339  free(buf);
1340 
1341  if (status || last == tail) return 0;
1342  if (patt->flags & NTMALLOC)
1343  free(patt->str);
1344  free(patt);
1345  return tail;
1346 }
1347 
1348 //
1349 // Check a command string to determine if it has I/O redirection
1350 // characters that require it to be executed by a command interpreter
1351 //
1352 
1353 static int
1354 has_redirection(const char *cmd)
1355 {
1356  char quote = '\0';
1357  const char *ptr;
1358 
1359  //
1360  // Scan the string, looking for redirection characters (< or >), pipe
1361  // character (|) or newline (\n) that are not in a quoted string
1362  //
1363 
1364  for (ptr = cmd; *ptr;) {
1365  switch (*ptr) {
1366  case '\'':
1367  case '\"':
1368  if (!quote)
1369  quote = *ptr;
1370  else if (quote == *ptr)
1371  quote = '\0';
1372  ptr++;
1373  break;
1374 
1375  case '>':
1376  case '<':
1377  case '|':
1378  case '&':
1379  case '\n':
1380  if (!quote)
1381  return TRUE;
1382  ptr++;
1383  break;
1384 
1385  case '%':
1386  if (*++ptr != '_' && !ISALPHA(*ptr)) break;
1387  while (*++ptr == '_' || ISALNUM(*ptr));
1388  if (*ptr++ == '%') return TRUE;
1389  break;
1390 
1391  case '\\':
1392  ptr++;
1393  default:
1394  ptr = CharNext(ptr);
1395  break;
1396  }
1397  }
1398  return FALSE;
1399 }
1400 
1401 static inline char *
1402 skipspace(char *ptr)
1403 {
1404  while (ISSPACE(*ptr))
1405  ptr++;
1406  return ptr;
1407 }
1408 
1409 int
1410 rb_w32_cmdvector(const char *cmd, char ***vec)
1411 {
1412  int globbing, len;
1413  int elements, strsz, done;
1414  int slashes, escape;
1415  char *ptr, *base, *buffer, *cmdline;
1416  char **vptr;
1417  char quote;
1418  NtCmdLineElement *curr, **tail;
1419  NtCmdLineElement *cmdhead = NULL, **cmdtail = &cmdhead;
1420 
1421  //
1422  // just return if we don't have a command line
1423  //
1424 
1425  while (ISSPACE(*cmd))
1426  cmd++;
1427  if (!*cmd) {
1428  *vec = NULL;
1429  return 0;
1430  }
1431 
1432  ptr = cmdline = strdup(cmd);
1433 
1434  //
1435  // Ok, parse the command line, building a list of CmdLineElements.
1436  // When we've finished, and it's an input command (meaning that it's
1437  // the processes argv), we'll do globing and then build the argument
1438  // vector.
1439  // The outer loop does one interation for each element seen.
1440  // The inner loop does one interation for each character in the element.
1441  //
1442 
1443  while (*(ptr = skipspace(ptr))) {
1444  base = ptr;
1445  quote = slashes = globbing = escape = 0;
1446  for (done = 0; !done && *ptr; ) {
1447  //
1448  // Switch on the current character. We only care about the
1449  // white-space characters, the wild-card characters, and the
1450  // quote characters.
1451  //
1452 
1453  switch (*ptr) {
1454  case '\\':
1455  if (quote != '\'') slashes++;
1456  break;
1457 
1458  case ' ':
1459  case '\t':
1460  case '\n':
1461  //
1462  // if we're not in a string, then we're finished with this
1463  // element
1464  //
1465 
1466  if (!quote) {
1467  *ptr = 0;
1468  done = 1;
1469  }
1470  break;
1471 
1472  case '*':
1473  case '?':
1474  case '[':
1475  case '{':
1476  //
1477  // record the fact that this element has a wildcard character
1478  // N.B. Don't glob if inside a single quoted string
1479  //
1480 
1481  if (quote != '\'')
1482  globbing++;
1483  slashes = 0;
1484  break;
1485 
1486  case '\'':
1487  case '\"':
1488  //
1489  // if we're already in a string, see if this is the
1490  // terminating close-quote. If it is, we're finished with
1491  // the string, but not neccessarily with the element.
1492  // If we're not already in a string, start one.
1493  //
1494 
1495  if (!(slashes & 1)) {
1496  if (!quote)
1497  quote = *ptr;
1498  else if (quote == *ptr) {
1499  if (quote == '"' && quote == ptr[1])
1500  ptr++;
1501  quote = '\0';
1502  }
1503  }
1504  escape++;
1505  slashes = 0;
1506  break;
1507 
1508  default:
1509  ptr = CharNext(ptr);
1510  slashes = 0;
1511  continue;
1512  }
1513  ptr++;
1514  }
1515 
1516  //
1517  // when we get here, we've got a pair of pointers to the element,
1518  // base and ptr. Base points to the start of the element while ptr
1519  // points to the character following the element.
1520  //
1521 
1522  len = ptr - base;
1523  if (done) --len;
1524 
1525  //
1526  // if it's an input vector element and it's enclosed by quotes,
1527  // we can remove them.
1528  //
1529 
1530  if (escape) {
1531  char *p = base, c;
1532  slashes = quote = 0;
1533  while (p < base + len) {
1534  switch (c = *p) {
1535  case '\\':
1536  p++;
1537  if (quote != '\'') slashes++;
1538  break;
1539 
1540  case '\'':
1541  case '"':
1542  if (!(slashes & 1) && quote && quote != c) {
1543  p++;
1544  slashes = 0;
1545  break;
1546  }
1547  memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
1548  base + len - p);
1549  len -= ((slashes + 1) >> 1) + (~slashes & 1);
1550  p -= (slashes + 1) >> 1;
1551  if (!(slashes & 1)) {
1552  if (quote) {
1553  if (quote == '"' && quote == *p)
1554  p++;
1555  quote = '\0';
1556  }
1557  else
1558  quote = c;
1559  }
1560  else
1561  p++;
1562  slashes = 0;
1563  break;
1564 
1565  default:
1566  p = CharNext(p);
1567  slashes = 0;
1568  break;
1569  }
1570  }
1571  }
1572 
1573  curr = (NtCmdLineElement *)calloc(sizeof(NtCmdLineElement), 1);
1574  if (!curr) goto do_nothing;
1575  curr->str = base;
1576  curr->len = len;
1577 
1578  if (globbing && (tail = cmdglob(curr, cmdtail))) {
1579  cmdtail = tail;
1580  }
1581  else {
1582  *cmdtail = curr;
1583  cmdtail = &curr->next;
1584  }
1585  }
1586 
1587  //
1588  // Almost done!
1589  // Count up the elements, then allocate space for a vector of pointers
1590  // (argv) and a string table for the elements.
1591  //
1592 
1593  for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
1594  elements++;
1595  strsz += (curr->len + 1);
1596  }
1597 
1598  len = (elements+1)*sizeof(char *) + strsz;
1599  buffer = (char *)malloc(len);
1600  if (!buffer) {
1601  do_nothing:
1602  while (curr = cmdhead) {
1603  cmdhead = curr->next;
1604  if (curr->flags & NTMALLOC) free(curr->str);
1605  free(curr);
1606  }
1607  free(cmdline);
1608  for (vptr = *vec; *vptr; ++vptr);
1609  return vptr - *vec;
1610  }
1611 
1612  //
1613  // make vptr point to the start of the buffer
1614  // and ptr point to the area we'll consider the string table.
1615  //
1616  // buffer (*vec)
1617  // |
1618  // V ^---------------------V
1619  // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1620  // | | | .... | NULL | | ..... |\0 | | ..... |\0 |...
1621  // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1622  // |- elements+1 -| ^ 1st element ^ 2nd element
1623 
1624  vptr = (char **) buffer;
1625 
1626  ptr = buffer + (elements+1) * sizeof(char *);
1627 
1628  while (curr = cmdhead) {
1629  strlcpy(ptr, curr->str, curr->len + 1);
1630  *vptr++ = ptr;
1631  ptr += curr->len + 1;
1632  cmdhead = curr->next;
1633  if (curr->flags & NTMALLOC) free(curr->str);
1634  free(curr);
1635  }
1636  *vptr = 0;
1637 
1638  *vec = (char **) buffer;
1639  free(cmdline);
1640  return elements;
1641 }
1642 
1643 //
1644 // UNIX compatible directory access functions for NT
1645 //
1646 
1647 #define PATHLEN 1024
1648 
1649 //
1650 // The idea here is to read all the directory names into a string table
1651 // (separated by nulls) and when one of the other dir functions is called
1652 // return the pointer to the current file name.
1653 //
1654 
1655 #define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] & (1 << (i) % CHAR_BIT))
1656 #define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
1657 
1658 #define BitOfIsDir(n) ((n) * 2)
1659 #define BitOfIsRep(n) ((n) * 2 + 1)
1660 #define DIRENT_PER_CHAR (CHAR_BIT / 2)
1661 
1662 static HANDLE
1663 open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
1664 {
1665  HANDLE fh;
1666  static const WCHAR wildcard[] = L"\\*";
1667  WCHAR *scanname;
1668  WCHAR *p;
1669  int len;
1670  VALUE v;
1671 
1672  //
1673  // Create the search pattern
1674  //
1675  len = lstrlenW(filename);
1676  scanname = ALLOCV_N(WCHAR, v, len + sizeof(wildcard) / sizeof(WCHAR));
1677  lstrcpyW(scanname, filename);
1678  p = CharPrevW(scanname, scanname + len);
1679  if (*p == L'/' || *p == L'\\' || *p == L':')
1680  lstrcatW(scanname, wildcard + 1);
1681  else
1682  lstrcatW(scanname, wildcard);
1683 
1684  //
1685  // do the FindFirstFile call
1686  //
1687  fh = FindFirstFileW(scanname, fd);
1688  ALLOCV_END(v);
1689  if (fh == INVALID_HANDLE_VALUE) {
1690  errno = map_errno(GetLastError());
1691  }
1692  return fh;
1693 }
1694 
1695 static DIR *
1696 opendir_internal(WCHAR *wpath, const char *filename)
1697 {
1698  struct stati64 sbuf;
1699  WIN32_FIND_DATAW fd;
1700  HANDLE fh;
1701  DIR *p;
1702  long len;
1703  long idx;
1704  WCHAR *tmpW;
1705  char *tmp;
1706 
1707  //
1708  // check to see if we've got a directory
1709  //
1710  if (wstati64(wpath, &sbuf) < 0) {
1711  return NULL;
1712  }
1713  if (!(sbuf.st_mode & S_IFDIR) &&
1714  (!ISALPHA(filename[0]) || filename[1] != ':' || filename[2] != '\0' ||
1715  ((1 << ((filename[0] & 0x5f) - 'A')) & GetLogicalDrives()) == 0)) {
1716  errno = ENOTDIR;
1717  return NULL;
1718  }
1719  fh = open_dir_handle(wpath, &fd);
1720  if (fh == INVALID_HANDLE_VALUE) {
1721  return NULL;
1722  }
1723 
1724  //
1725  // Get us a DIR structure
1726  //
1727  p = calloc(sizeof(DIR), 1);
1728  if (p == NULL)
1729  return NULL;
1730 
1731  idx = 0;
1732 
1733  //
1734  // loop finding all the files that match the wildcard
1735  // (which should be all of them in this directory!).
1736  // the variable idx should point one past the null terminator
1737  // of the previous string found.
1738  //
1739  do {
1740  len = lstrlenW(fd.cFileName) + 1;
1741 
1742  //
1743  // bump the string table size by enough for the
1744  // new name and it's null terminator
1745  //
1746  tmpW = realloc(p->start, (idx + len) * sizeof(WCHAR));
1747  if (!tmpW) {
1748  error:
1749  rb_w32_closedir(p);
1750  FindClose(fh);
1751  errno = ENOMEM;
1752  return NULL;
1753  }
1754 
1755  p->start = tmpW;
1756  memcpy(&p->start[idx], fd.cFileName, len * sizeof(WCHAR));
1757 
1758  if (p->nfiles % DIRENT_PER_CHAR == 0) {
1759  tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
1760  if (!tmp)
1761  goto error;
1762  p->bits = tmp;
1763  p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
1764  }
1765  if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1766  SetBit(p->bits, BitOfIsDir(p->nfiles));
1767  if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1768  SetBit(p->bits, BitOfIsRep(p->nfiles));
1769 
1770  p->nfiles++;
1771  idx += len;
1772  } while (FindNextFileW(fh, &fd));
1773  FindClose(fh);
1774  p->size = idx;
1775  p->curr = p->start;
1776  return p;
1777 }
1778 
1779 static inline UINT
1780 filecp(void)
1781 {
1782  UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
1783  return cp;
1784 }
1785 
1786 static char *
1787 wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
1788 {
1789  char *ptr;
1790  int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL) - 1;
1791  if (!(ptr = malloc(len + 1))) return 0;
1792  WideCharToMultiByte(cp, 0, wstr, clen, ptr, len + 1, NULL, NULL);
1793  if (plen) *plen = len;
1794  return ptr;
1795 }
1796 
1797 static WCHAR *
1798 mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
1799 {
1800  WCHAR *ptr;
1801  int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0) - 1;
1802  if (!(ptr = malloc(sizeof(WCHAR) * (len + 1)))) return 0;
1803  MultiByteToWideChar(cp, 0, str, clen, ptr, len + 1);
1804  if (plen) *plen = len;
1805  return ptr;
1806 }
1807 
1808 DIR *
1809 rb_w32_opendir(const char *filename)
1810 {
1811  DIR *ret;
1812  WCHAR *wpath = filecp_to_wstr(filename, NULL);
1813  if (!wpath)
1814  return NULL;
1815  ret = opendir_internal(wpath, filename);
1816  free(wpath);
1817  return ret;
1818 }
1819 
1820 DIR *
1821 rb_w32_uopendir(const char *filename)
1822 {
1823  DIR *ret;
1824  WCHAR *wpath = utf8_to_wstr(filename, NULL);
1825  if (!wpath)
1826  return NULL;
1827  ret = opendir_internal(wpath, filename);
1828  free(wpath);
1829  return ret;
1830 }
1831 
1832 //
1833 // Move to next entry
1834 //
1835 
1836 static void
1838 {
1839  if (dirp->curr) {
1840  dirp->loc++;
1841  dirp->curr += lstrlenW(dirp->curr) + 1;
1842  if (dirp->curr >= (dirp->start + dirp->size)) {
1843  dirp->curr = NULL;
1844  }
1845  }
1846 }
1847 
1848 //
1849 // Readdir just returns the current string pointer and bumps the
1850 // string pointer to the next entry.
1851 //
1852 static BOOL
1853 win32_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *dummy)
1854 {
1855  if (!(entry->d_name = wstr_to_filecp(file, &entry->d_namlen)))
1856  return FALSE;
1857  return TRUE;
1858 }
1859 
1860 VALUE
1861 rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
1862 {
1863  static rb_encoding *utf16 = (rb_encoding *)-1;
1864  VALUE src;
1865 
1866  if (utf16 == (rb_encoding *)-1) {
1867  utf16 = rb_enc_find("UTF-16LE");
1868  if (utf16 == rb_ascii8bit_encoding())
1869  utf16 = NULL;
1870  }
1871  if (!utf16)
1872  /* maybe miniruby */
1873  return Qnil;
1874 
1875  src = rb_enc_str_new((char *)wstr, lstrlenW(wstr) * sizeof(WCHAR), utf16);
1877 }
1878 
1879 char *
1880 rb_w32_conv_from_wstr(const WCHAR *wstr, long *lenp, rb_encoding *enc)
1881 {
1882  VALUE str = rb_w32_conv_from_wchar(wstr, enc);
1883  long len;
1884  char *ptr;
1885 
1886  if (NIL_P(str)) return wstr_to_filecp(wstr, lenp);
1887  *lenp = len = RSTRING_LEN(str);
1888  memcpy(ptr = malloc(len + 1), RSTRING_PTR(str), len);
1889  ptr[len] = '\0';
1890  return ptr;
1891 }
1892 
1893 static BOOL
1894 ruby_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *enc)
1895 {
1896  if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
1897  return FALSE;
1898  return TRUE;
1899 }
1900 
1901 static struct direct *
1902 readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, struct direct *, rb_encoding *), rb_encoding *enc)
1903 {
1904  static int dummy = 0;
1905 
1906  if (dirp->curr) {
1907 
1908  //
1909  // first set up the structure to return
1910  //
1911  if (dirp->dirstr.d_name)
1912  free(dirp->dirstr.d_name);
1913  conv(dirp->curr, &dirp->dirstr, enc);
1914 
1915  //
1916  // Fake inode
1917  //
1918  dirp->dirstr.d_ino = dummy++;
1919 
1920  //
1921  // Attributes
1922  //
1923  dirp->dirstr.d_isdir = GetBit(dirp->bits, BitOfIsDir(dirp->loc));
1924  dirp->dirstr.d_isrep = GetBit(dirp->bits, BitOfIsRep(dirp->loc));
1925 
1926  //
1927  // Now set up for the next call to readdir
1928  //
1929 
1930  move_to_next_entry(dirp);
1931 
1932  return &(dirp->dirstr);
1933 
1934  }
1935  else
1936  return NULL;
1937 }
1938 
1939 struct direct *
1941 {
1942  return readdir_internal(dirp, win32_direct_conv, NULL);
1943 }
1944 
1945 struct direct *
1947 {
1948  if (enc == rb_ascii8bit_encoding())
1949  return readdir_internal(dirp, win32_direct_conv, NULL);
1950  else
1951  return readdir_internal(dirp, ruby_direct_conv, enc);
1952 }
1953 
1954 //
1955 // Telldir returns the current string pointer position
1956 //
1957 
1958 long
1960 {
1961  return dirp->loc;
1962 }
1963 
1964 //
1965 // Seekdir moves the string pointer to a previously saved position
1966 // (Saved by telldir).
1967 
1968 void
1969 rb_w32_seekdir(DIR *dirp, long loc)
1970 {
1971  if (dirp->loc > loc) rb_w32_rewinddir(dirp);
1972 
1973  while (dirp->curr && dirp->loc < loc) {
1974  move_to_next_entry(dirp);
1975  }
1976 }
1977 
1978 //
1979 // Rewinddir resets the string pointer to the start
1980 //
1981 
1982 void
1984 {
1985  dirp->curr = dirp->start;
1986  dirp->loc = 0;
1987 }
1988 
1989 //
1990 // This just free's the memory allocated by opendir
1991 //
1992 
1993 void
1995 {
1996  if (dirp) {
1997  if (dirp->dirstr.d_name)
1998  free(dirp->dirstr.d_name);
1999  if (dirp->start)
2000  free(dirp->start);
2001  if (dirp->bits)
2002  free(dirp->bits);
2003  free(dirp);
2004  }
2005 }
2006 
2007 #if (defined _MT || defined __MSVCRT__) && !defined __BORLANDC__
2008 #define MSVCRT_THREADS
2009 #endif
2010 #ifdef MSVCRT_THREADS
2011 # define MTHREAD_ONLY(x) x
2012 # define STHREAD_ONLY(x)
2013 #elif defined(__BORLANDC__)
2014 # define MTHREAD_ONLY(x)
2015 # define STHREAD_ONLY(x)
2016 #else
2017 # define MTHREAD_ONLY(x)
2018 # define STHREAD_ONLY(x) x
2019 #endif
2020 
2021 typedef struct {
2022  intptr_t osfhnd; /* underlying OS file HANDLE */
2023  char osfile; /* attributes of file (e.g., open in text mode?) */
2024  char pipech; /* one char buffer for handles opened on pipes */
2025 #ifdef MSVCRT_THREADS
2026  int lockinitflag;
2027  CRITICAL_SECTION lock;
2028 #endif
2029 #if RT_VER >= 80
2030  char textmode;
2031  char pipech2[2];
2032 #endif
2033 } ioinfo;
2034 
2035 #if !defined _CRTIMP || defined __MINGW32__
2036 #undef _CRTIMP
2037 #define _CRTIMP __declspec(dllimport)
2038 #endif
2039 
2040 #if !defined(__BORLANDC__)
2041 EXTERN_C _CRTIMP ioinfo * __pioinfo[];
2042 
2043 #define IOINFO_L2E 5
2044 #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
2045 #define _pioinfo(i) ((ioinfo*)((char*)(__pioinfo[i >> IOINFO_L2E]) + (i & (IOINFO_ARRAY_ELTS - 1)) * (sizeof(ioinfo) + pioinfo_extra)))
2046 #define _osfhnd(i) (_pioinfo(i)->osfhnd)
2047 #define _osfile(i) (_pioinfo(i)->osfile)
2048 #define _pipech(i) (_pioinfo(i)->pipech)
2049 
2050 #if RT_VER >= 80
2051 static size_t pioinfo_extra = 0; /* workaround for VC++8 SP1 */
2052 
2053 static void
2054 set_pioinfo_extra(void)
2055 {
2056  int fd;
2057 
2058  fd = _open("NUL", O_RDONLY);
2059  for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra += sizeof(void *)) {
2060  if (_osfhnd(fd) == _get_osfhandle(fd)) {
2061  break;
2062  }
2063  }
2064  _close(fd);
2065 
2066  if (pioinfo_extra > 64) {
2067  /* not found, maybe something wrong... */
2068  pioinfo_extra = 0;
2069  }
2070 }
2071 #else
2072 #define pioinfo_extra 0
2073 #endif
2074 
2075 #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2076 #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
2077 
2078 #define FOPEN 0x01 /* file handle open */
2079 #define FEOFLAG 0x02 /* end of file has been encountered */
2080 #define FPIPE 0x08 /* file handle refers to a pipe */
2081 #define FNOINHERIT 0x10 /* file handle opened O_NOINHERIT */
2082 #define FAPPEND 0x20 /* file handle opened O_APPEND */
2083 #define FDEV 0x40 /* file handle refers to device */
2084 #define FTEXT 0x80 /* file handle is in text mode */
2085 
2086 static int is_socket(SOCKET);
2087 static int is_console(SOCKET);
2088 
2089 int
2091 {
2092  return cancel_io != NULL && (is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd)));
2093 }
2094 
2095 static int
2096 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2097 {
2098  int fh;
2099  char fileflags; /* _osfile flags */
2100  HANDLE hF;
2101 
2102  /* copy relevant flags from second parameter */
2103  fileflags = FDEV;
2104 
2105  if (flags & O_APPEND)
2106  fileflags |= FAPPEND;
2107 
2108  if (flags & O_TEXT)
2109  fileflags |= FTEXT;
2110 
2111  if (flags & O_NOINHERIT)
2112  fileflags |= FNOINHERIT;
2113 
2114  /* attempt to allocate a C Runtime file handle */
2115  hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2116  fh = _open_osfhandle((intptr_t)hF, 0);
2117  CloseHandle(hF);
2118  if (fh == -1) {
2119  errno = EMFILE; /* too many open files */
2120  _doserrno = 0L; /* not an OS error */
2121  }
2122  else {
2123 
2124  MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fh)->lock)));
2125  /* the file is open. now, set the info in _osfhnd array */
2126  _set_osfhnd(fh, osfhandle);
2127 
2128  fileflags |= FOPEN; /* mark as open */
2129 
2130  _set_osflags(fh, fileflags); /* set osfile entry */
2131  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fh)->lock));
2132  }
2133  return fh; /* return handle */
2134 }
2135 
2136 static void
2138 {
2139  int nullfd = -1;
2140  int keep = 0;
2141 #define open_null(fd) \
2142  (((nullfd < 0) ? \
2143  (nullfd = open("NUL", O_RDWR)) : 0), \
2144  ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)), \
2145  (fd))
2146 
2147  if (fileno(stdin) < 0) {
2148  stdin->_file = open_null(0);
2149  }
2150  else {
2151  setmode(fileno(stdin), O_BINARY);
2152  }
2153  if (fileno(stdout) < 0) {
2154  stdout->_file = open_null(1);
2155  }
2156  if (fileno(stderr) < 0) {
2157  stderr->_file = open_null(2);
2158  }
2159  if (nullfd >= 0 && !keep) close(nullfd);
2160  setvbuf(stderr, NULL, _IONBF, 0);
2161 }
2162 #else
2163 
2164 #define _set_osfhnd(fh, osfh) (void)((fh), (osfh))
2165 #define _set_osflags(fh, flags) (void)((fh), (flags))
2166 
2167 static void
2168 init_stdhandle(void)
2169 {
2170 }
2171 #endif
2172 
2173 #ifdef __BORLANDC__
2174 static int
2175 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2176 {
2177  int fd = _open_osfhandle(osfhandle, flags);
2178  if (fd == -1) {
2179  errno = EMFILE; /* too many open files */
2180  _doserrno = 0L; /* not an OS error */
2181  }
2182  return fd;
2183 }
2184 #endif
2185 
2186 #undef getsockopt
2187 
2188 static int
2189 is_socket(SOCKET sock)
2190 {
2191  if (st_lookup(socklist, (st_data_t)sock, NULL))
2192  return TRUE;
2193  else
2194  return FALSE;
2195 }
2196 
2197 int
2199 {
2200  return is_socket(TO_SOCKET(fd));
2201 }
2202 
2203 //
2204 // Since the errors returned by the socket error function
2205 // WSAGetLastError() are not known by the library routine strerror
2206 // we have to roll our own.
2207 //
2208 
2209 #undef strerror
2210 
2211 char *
2213 {
2214  static char buffer[512];
2215  DWORD source = 0;
2216  char *p;
2217 
2218 #if defined __BORLANDC__ && defined ENOTEMPTY // _sys_errlist is broken
2219  switch (e) {
2220  case ENAMETOOLONG:
2221  return "Filename too long";
2222  case ENOTEMPTY:
2223  return "Directory not empty";
2224  }
2225 #endif
2226 
2227  if (e < 0 || e > sys_nerr) {
2228  if (e < 0)
2229  e = GetLastError();
2230 #if WSAEWOULDBLOCK != EWOULDBLOCK
2231  else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
2232  static int s = -1;
2233  int i;
2234  if (s < 0)
2235  for (s = 0; s < (int)(sizeof(errmap)/sizeof(*errmap)); s++)
2236  if (errmap[s].winerr == WSAEWOULDBLOCK)
2237  break;
2238  for (i = s; i < (int)(sizeof(errmap)/sizeof(*errmap)); i++)
2239  if (errmap[i].err == e) {
2240  e = errmap[i].winerr;
2241  break;
2242  }
2243  }
2244 #endif
2245  if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2246  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
2247  MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
2248  buffer, sizeof(buffer), NULL) == 0 &&
2249  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2250  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
2251  buffer, sizeof(buffer), NULL) == 0)
2252  strlcpy(buffer, "Unknown Error", sizeof(buffer));
2253  }
2254  else
2255  strlcpy(buffer, strerror(e), sizeof(buffer));
2256 
2257  p = buffer;
2258  while ((p = strpbrk(p, "\r\n")) != NULL) {
2259  memmove(p, p + 1, strlen(p));
2260  }
2261  return buffer;
2262 }
2263 
2264 //
2265 // various stubs
2266 //
2267 
2268 
2269 // Ownership
2270 //
2271 // Just pretend that everyone is a superuser. NT will let us know if
2272 // we don't really have permission to do something.
2273 //
2274 
2275 #define ROOT_UID 0
2276 #define ROOT_GID 0
2277 
2278 rb_uid_t
2279 getuid(void)
2280 {
2281  return ROOT_UID;
2282 }
2283 
2284 rb_uid_t
2285 geteuid(void)
2286 {
2287  return ROOT_UID;
2288 }
2289 
2290 rb_gid_t
2291 getgid(void)
2292 {
2293  return ROOT_GID;
2294 }
2295 
2296 rb_gid_t
2297 getegid(void)
2298 {
2299  return ROOT_GID;
2300 }
2301 
2302 int
2303 setuid(rb_uid_t uid)
2304 {
2305  return (uid == ROOT_UID ? 0 : -1);
2306 }
2307 
2308 int
2309 setgid(rb_gid_t gid)
2310 {
2311  return (gid == ROOT_GID ? 0 : -1);
2312 }
2313 
2314 //
2315 // File system stuff
2316 //
2317 
2318 int
2319 ioctl(int i, int u, ...)
2320 {
2321  errno = EINVAL;
2322  return -1;
2323 }
2324 
2325 void
2326 rb_w32_fdset(int fd, fd_set *set)
2327 {
2328  FD_SET(fd, set);
2329 }
2330 
2331 #undef FD_CLR
2332 
2333 void
2334 rb_w32_fdclr(int fd, fd_set *set)
2335 {
2336  unsigned int i;
2337  SOCKET s = TO_SOCKET(fd);
2338 
2339  for (i = 0; i < set->fd_count; i++) {
2340  if (set->fd_array[i] == s) {
2341  memmove(&set->fd_array[i], &set->fd_array[i+1],
2342  sizeof(set->fd_array[0]) * (--set->fd_count - i));
2343  break;
2344  }
2345  }
2346 }
2347 
2348 #undef FD_ISSET
2349 
2350 int
2351 rb_w32_fdisset(int fd, fd_set *set)
2352 {
2353  int ret;
2354  SOCKET s = TO_SOCKET(fd);
2355  if (s == (SOCKET)INVALID_HANDLE_VALUE)
2356  return 0;
2357  RUBY_CRITICAL(ret = __WSAFDIsSet(s, set));
2358  return ret;
2359 }
2360 
2361 void
2362 rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
2363 {
2364  max = min(src->fd_count, (UINT)max);
2365  if ((UINT)dst->capa < (UINT)max) {
2366  dst->capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2367  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2368  }
2369 
2370  memcpy(dst->fdset->fd_array, src->fd_array,
2371  max * sizeof(src->fd_array[0]));
2372  dst->fdset->fd_count = src->fd_count;
2373 }
2374 
2375 /* License: Ruby's */
2376 void
2378 {
2379  if ((UINT)dst->capa < src->fdset->fd_count) {
2380  dst->capa = (src->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2381  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2382  }
2383 
2384  memcpy(dst->fdset->fd_array, src->fdset->fd_array,
2385  src->fdset->fd_count * sizeof(src->fdset->fd_array[0]));
2386  dst->fdset->fd_count = src->fdset->fd_count;
2387 }
2388 
2389 //
2390 // Networking trampolines
2391 // These are used to avoid socket startup/shutdown overhead in case
2392 // the socket routines aren't used.
2393 //
2394 
2395 #undef select
2396 
2397 static int
2398 extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
2399 {
2400  unsigned int s = 0;
2401  unsigned int m = 0;
2402  if (!src) return 0;
2403 
2404  while (s < src->fd_count) {
2405  SOCKET fd = src->fd_array[s];
2406 
2407  if (!func || (*func)(fd)) {
2408  if (dst) { /* move it to dst */
2409  unsigned int d;
2410 
2411  for (d = 0; d < dst->fdset->fd_count; d++) {
2412  if (dst->fdset->fd_array[d] == fd)
2413  break;
2414  }
2415  if (d == dst->fdset->fd_count) {
2416  if ((int)dst->fdset->fd_count >= dst->capa) {
2417  dst->capa = (dst->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2418  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2419  }
2420  dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
2421  }
2422  memmove(
2423  &src->fd_array[s],
2424  &src->fd_array[s+1],
2425  sizeof(src->fd_array[0]) * (--src->fd_count - s));
2426  }
2427  else {
2428  m++;
2429  s++;
2430  }
2431  }
2432  else s++;
2433  }
2434 
2435  return dst ? dst->fdset->fd_count : m;
2436 }
2437 
2438 static int
2439 copy_fd(fd_set *dst, fd_set *src)
2440 {
2441  unsigned int s;
2442  if (!src || !dst) return 0;
2443 
2444  for (s = 0; s < src->fd_count; ++s) {
2445  SOCKET fd = src->fd_array[s];
2446  unsigned int d;
2447  for (d = 0; d < dst->fd_count; ++d) {
2448  if (dst->fd_array[d] == fd)
2449  break;
2450  }
2451  if (d == dst->fd_count && d < FD_SETSIZE) {
2452  dst->fd_array[dst->fd_count++] = fd;
2453  }
2454  }
2455 
2456  return dst->fd_count;
2457 }
2458 
2459 static int
2460 is_not_socket(SOCKET sock)
2461 {
2462  return !is_socket(sock);
2463 }
2464 
2465 static int
2466 is_pipe(SOCKET sock) /* DONT call this for SOCKET! it clains it is PIPE. */
2467 {
2468  int ret;
2469 
2470  RUBY_CRITICAL({
2471  ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
2472  });
2473 
2474  return ret;
2475 }
2476 
2477 static int
2478 is_readable_pipe(SOCKET sock) /* call this for pipe only */
2479 {
2480  int ret;
2481  DWORD n = 0;
2482 
2483  RUBY_CRITICAL(
2484  if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
2485  ret = (n > 0);
2486  }
2487  else {
2488  ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
2489  }
2490  );
2491 
2492  return ret;
2493 }
2494 
2495 static int
2496 is_console(SOCKET sock) /* DONT call this for SOCKET! */
2497 {
2498  int ret;
2499  DWORD n = 0;
2500  INPUT_RECORD ir;
2501 
2502  RUBY_CRITICAL(
2503  ret = (PeekConsoleInput((HANDLE)sock, &ir, 1, &n))
2504  );
2505 
2506  return ret;
2507 }
2508 
2509 static int
2510 is_readable_console(SOCKET sock) /* call this for console only */
2511 {
2512  int ret = 0;
2513  DWORD n = 0;
2514  INPUT_RECORD ir;
2515 
2516  RUBY_CRITICAL(
2517  if (PeekConsoleInput((HANDLE)sock, &ir, 1, &n) && n > 0) {
2518  if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
2519  ir.Event.KeyEvent.uChar.AsciiChar) {
2520  ret = 1;
2521  }
2522  else {
2523  ReadConsoleInput((HANDLE)sock, &ir, 1, &n);
2524  }
2525  }
2526  );
2527 
2528  return ret;
2529 }
2530 
2531 static int
2532 is_invalid_handle(SOCKET sock)
2533 {
2534  return (HANDLE)sock == INVALID_HANDLE_VALUE;
2535 }
2536 
2537 static int
2538 do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
2539  struct timeval *timeout)
2540 {
2541  int r = 0;
2542 
2543  if (nfds == 0) {
2544  if (timeout)
2545  rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
2546  else
2547  rb_w32_sleep(INFINITE);
2548  }
2549  else {
2550  RUBY_CRITICAL(
2551  EnterCriticalSection(&select_mutex);
2552  r = select(nfds, rd, wr, ex, timeout);
2553  LeaveCriticalSection(&select_mutex);
2554  if (r == SOCKET_ERROR) {
2555  errno = map_errno(WSAGetLastError());
2556  r = -1;
2557  }
2558  );
2559  }
2560 
2561  return r;
2562 }
2563 
2564 /*
2565  * rest -= wait
2566  * return 0 if rest is smaller than wait.
2567  */
2568 int
2569 rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
2570 {
2571  if (rest->tv_sec < wait->tv_sec) {
2572  return 0;
2573  }
2574  while (rest->tv_usec < wait->tv_usec) {
2575  if (rest->tv_sec <= wait->tv_sec) {
2576  return 0;
2577  }
2578  rest->tv_sec -= 1;
2579  rest->tv_usec += 1000 * 1000;
2580  }
2581  rest->tv_sec -= wait->tv_sec;
2582  rest->tv_usec -= wait->tv_usec;
2583  return rest->tv_sec != 0 || rest->tv_usec != 0;
2584 }
2585 
2586 static inline int
2587 compare(const struct timeval *t1, const struct timeval *t2)
2588 {
2589  if (t1->tv_sec < t2->tv_sec)
2590  return -1;
2591  if (t1->tv_sec > t2->tv_sec)
2592  return 1;
2593  if (t1->tv_usec < t2->tv_usec)
2594  return -1;
2595  if (t1->tv_usec > t2->tv_usec)
2596  return 1;
2597  return 0;
2598 }
2599 
2600 #undef Sleep
2601 
2602 int rb_w32_check_interrupt(void *); /* @internal */
2603 
2604 /* @internal */
2605 int
2606 rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
2607  struct timeval *timeout, void *th)
2608 {
2609  int r;
2610  rb_fdset_t pipe_rd;
2611  rb_fdset_t cons_rd;
2612  rb_fdset_t else_rd;
2613  rb_fdset_t else_wr;
2614  rb_fdset_t except;
2615  int nonsock = 0;
2616  struct timeval limit = {0, 0};
2617 
2618  if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
2619  errno = EINVAL;
2620  return -1;
2621  }
2622 
2623  if (timeout) {
2624  if (timeout->tv_sec < 0 ||
2625  timeout->tv_usec < 0 ||
2626  timeout->tv_usec >= 1000000) {
2627  errno = EINVAL;
2628  return -1;
2629  }
2630  gettimeofday(&limit, NULL);
2631  limit.tv_sec += timeout->tv_sec;
2632  limit.tv_usec += timeout->tv_usec;
2633  if (limit.tv_usec >= 1000000) {
2634  limit.tv_usec -= 1000000;
2635  limit.tv_sec++;
2636  }
2637  }
2638 
2639  if (!NtSocketsInitialized) {
2640  StartSockets();
2641  }
2642 
2643  // assume else_{rd,wr} (other than socket, pipe reader, console reader)
2644  // are always readable/writable. but this implementation still has
2645  // problem. if pipe's buffer is full, writing to pipe will block
2646  // until some data is read from pipe. but ruby is single threaded system,
2647  // so whole system will be blocked forever.
2648 
2649  rb_fd_init(&else_rd);
2650  nonsock += extract_fd(&else_rd, rd, is_not_socket);
2651 
2652  rb_fd_init(&else_wr);
2653  nonsock += extract_fd(&else_wr, wr, is_not_socket);
2654 
2655  // check invalid handles
2656  if (extract_fd(NULL, else_rd.fdset, is_invalid_handle) > 0 ||
2657  extract_fd(NULL, else_wr.fdset, is_invalid_handle) > 0) {
2658  rb_fd_term(&else_wr);
2659  rb_fd_term(&else_rd);
2660  errno = EBADF;
2661  return -1;
2662  }
2663 
2664  rb_fd_init(&pipe_rd);
2665  extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
2666 
2667  rb_fd_init(&cons_rd);
2668  extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
2669 
2670  rb_fd_init(&except);
2671  extract_fd(&except, ex, is_not_socket); // drop only
2672 
2673  r = 0;
2674  if (rd && (int)rd->fd_count > r) r = (int)rd->fd_count;
2675  if (wr && (int)wr->fd_count > r) r = (int)wr->fd_count;
2676  if (ex && (int)ex->fd_count > r) r = (int)ex->fd_count;
2677  if (nfds > r) nfds = r;
2678 
2679  {
2680  struct timeval rest;
2681  struct timeval wait;
2682  struct timeval zero;
2683  wait.tv_sec = 0; wait.tv_usec = 10 * 1000; // 10ms
2684  zero.tv_sec = 0; zero.tv_usec = 0; // 0ms
2685  for (;;) {
2686  if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
2687  r = -1;
2688  break;
2689  }
2690  if (nonsock) {
2691  // modifying {else,pipe,cons}_rd is safe because
2692  // if they are modified, function returns immediately.
2693  extract_fd(&else_rd, pipe_rd.fdset, is_readable_pipe);
2694  extract_fd(&else_rd, cons_rd.fdset, is_readable_console);
2695  }
2696 
2697  if (else_rd.fdset->fd_count || else_wr.fdset->fd_count) {
2698  r = do_select(nfds, rd, wr, ex, &zero); // polling
2699  if (r < 0) break; // XXX: should I ignore error and return signaled handles?
2700  r += copy_fd(rd, else_rd.fdset);
2701  r += copy_fd(wr, else_wr.fdset);
2702  if (ex)
2703  r += ex->fd_count;
2704  break;
2705  }
2706  else {
2707  struct timeval *dowait = &wait;
2708 
2709  fd_set orig_rd;
2710  fd_set orig_wr;
2711  fd_set orig_ex;
2712 
2713  FD_ZERO(&orig_rd);
2714  FD_ZERO(&orig_wr);
2715  FD_ZERO(&orig_ex);
2716 
2717  if (rd) copy_fd(&orig_rd, rd);
2718  if (wr) copy_fd(&orig_wr, wr);
2719  if (ex) copy_fd(&orig_ex, ex);
2720  r = do_select(nfds, rd, wr, ex, &zero); // polling
2721  if (r != 0) break; // signaled or error
2722  if (rd) copy_fd(rd, &orig_rd);
2723  if (wr) copy_fd(wr, &orig_wr);
2724  if (ex) copy_fd(ex, &orig_ex);
2725 
2726  if (timeout) {
2727  struct timeval now;
2728  gettimeofday(&now, NULL);
2729  rest = limit;
2730  if (!rb_w32_time_subtract(&rest, &now)) break;
2731  if (compare(&rest, &wait) < 0) dowait = &rest;
2732  }
2733  Sleep(dowait->tv_sec * 1000 + dowait->tv_usec / 1000);
2734  }
2735  }
2736  }
2737 
2738  rb_fd_term(&except);
2739  rb_fd_term(&cons_rd);
2740  rb_fd_term(&pipe_rd);
2741  rb_fd_term(&else_wr);
2742  rb_fd_term(&else_rd);
2743 
2744  return r;
2745 }
2746 
2747 int WSAAPI
2748 rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
2749  struct timeval *timeout)
2750 {
2751  return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
2752 }
2753 
2754 static FARPROC
2755 get_wsa_extension_function(SOCKET s, GUID *guid)
2756 {
2757  DWORD dmy;
2758  FARPROC ptr = NULL;
2759 
2760  WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, guid, sizeof(*guid),
2761  &ptr, sizeof(ptr), &dmy, NULL, NULL);
2762  if (!ptr)
2763  errno = ENOSYS;
2764  return ptr;
2765 }
2766 
2767 #undef accept
2768 
2769 int WSAAPI
2770 rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
2771 {
2772  SOCKET r;
2773  int fd;
2774 
2775  if (!NtSocketsInitialized) {
2776  StartSockets();
2777  }
2778  RUBY_CRITICAL({
2779  HANDLE h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2780  fd = rb_w32_open_osfhandle((intptr_t)h, O_RDWR|O_BINARY|O_NOINHERIT);
2781  if (fd != -1) {
2782  r = accept(TO_SOCKET(s), addr, addrlen);
2783  if (r != INVALID_SOCKET) {
2784  MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
2785  _set_osfhnd(fd, r);
2786  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
2787  CloseHandle(h);
2788  st_insert(socklist, (st_data_t)r, (st_data_t)0);
2789  }
2790  else {
2791  errno = map_errno(WSAGetLastError());
2792  close(fd);
2793  fd = -1;
2794  }
2795  }
2796  else
2797  CloseHandle(h);
2798  });
2799  return fd;
2800 }
2801 
2802 #undef bind
2803 
2804 int WSAAPI
2805 rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
2806 {
2807  int r;
2808 
2809  if (!NtSocketsInitialized) {
2810  StartSockets();
2811  }
2812  RUBY_CRITICAL({
2813  r = bind(TO_SOCKET(s), addr, addrlen);
2814  if (r == SOCKET_ERROR)
2815  errno = map_errno(WSAGetLastError());
2816  });
2817  return r;
2818 }
2819 
2820 #undef connect
2821 
2822 int WSAAPI
2823 rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
2824 {
2825  int r;
2826  if (!NtSocketsInitialized) {
2827  StartSockets();
2828  }
2829  RUBY_CRITICAL({
2830  r = connect(TO_SOCKET(s), addr, addrlen);
2831  if (r == SOCKET_ERROR) {
2832  int err = WSAGetLastError();
2833  if (err != WSAEWOULDBLOCK)
2834  errno = map_errno(err);
2835  else
2836  errno = EINPROGRESS;
2837  }
2838  });
2839  return r;
2840 }
2841 
2842 
2843 #undef getpeername
2844 
2845 int WSAAPI
2846 rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
2847 {
2848  int r;
2849  if (!NtSocketsInitialized) {
2850  StartSockets();
2851  }
2852  RUBY_CRITICAL({
2853  r = getpeername(TO_SOCKET(s), addr, addrlen);
2854  if (r == SOCKET_ERROR)
2855  errno = map_errno(WSAGetLastError());
2856  });
2857  return r;
2858 }
2859 
2860 #undef getsockname
2861 
2862 int WSAAPI
2863 rb_w32_getsockname(int s, struct sockaddr *addr, int *addrlen)
2864 {
2865  int r;
2866  if (!NtSocketsInitialized) {
2867  StartSockets();
2868  }
2869  RUBY_CRITICAL({
2870  r = getsockname(TO_SOCKET(s), addr, addrlen);
2871  if (r == SOCKET_ERROR)
2872  errno = map_errno(WSAGetLastError());
2873  });
2874  return r;
2875 }
2876 
2877 int WSAAPI
2878 rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
2879 {
2880  int r;
2881  if (!NtSocketsInitialized) {
2882  StartSockets();
2883  }
2884  RUBY_CRITICAL({
2885  r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
2886  if (r == SOCKET_ERROR)
2887  errno = map_errno(WSAGetLastError());
2888  });
2889  return r;
2890 }
2891 
2892 #undef ioctlsocket
2893 
2894 int WSAAPI
2895 rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
2896 {
2897  int r;
2898  if (!NtSocketsInitialized) {
2899  StartSockets();
2900  }
2901  RUBY_CRITICAL({
2902  r = ioctlsocket(TO_SOCKET(s), cmd, argp);
2903  if (r == SOCKET_ERROR)
2904  errno = map_errno(WSAGetLastError());
2905  });
2906  return r;
2907 }
2908 
2909 #undef listen
2910 
2911 int WSAAPI
2912 rb_w32_listen(int s, int backlog)
2913 {
2914  int r;
2915  if (!NtSocketsInitialized) {
2916  StartSockets();
2917  }
2918  RUBY_CRITICAL({
2919  r = listen(TO_SOCKET(s), backlog);
2920  if (r == SOCKET_ERROR)
2921  errno = map_errno(WSAGetLastError());
2922  });
2923  return r;
2924 }
2925 
2926 #undef recv
2927 #undef recvfrom
2928 #undef send
2929 #undef sendto
2930 
2931 static int
2932 finish_overlapped_socket(SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
2933 {
2934  DWORD flg;
2935  int err;
2936 
2937  if (result != SOCKET_ERROR)
2938  *len = size;
2939  else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
2940  switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
2941  case WAIT_OBJECT_0:
2942  RUBY_CRITICAL(
2943  result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg)
2944  );
2945  if (result) {
2946  *len = size;
2947  break;
2948  }
2949  /* thru */
2950  default:
2951  errno = map_errno(WSAGetLastError());
2952  /* thru */
2953  case WAIT_OBJECT_0 + 1:
2954  /* interrupted */
2955  *len = -1;
2956  cancel_io((HANDLE)s);
2957  break;
2958  }
2959  }
2960  else {
2961  errno = map_errno(err);
2962  *len = -1;
2963  }
2964  CloseHandle(wol->hEvent);
2965 
2966  return result;
2967 }
2968 
2969 static int
2970 overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
2971  struct sockaddr *addr, int *addrlen)
2972 {
2973  int r;
2974  int ret;
2975  int mode;
2976  st_data_t data;
2977  DWORD flg;
2978  WSAOVERLAPPED wol;
2979  WSABUF wbuf;
2980  SOCKET s;
2981 
2982  if (!NtSocketsInitialized)
2983  StartSockets();
2984 
2985  s = TO_SOCKET(fd);
2986  st_lookup(socklist, (st_data_t)s, &data);
2987  mode = (int)data;
2988  if (!cancel_io || (mode & O_NONBLOCK)) {
2989  RUBY_CRITICAL({
2990  if (input) {
2991  if (addr && addrlen)
2992  r = recvfrom(s, buf, len, flags, addr, addrlen);
2993  else
2994  r = recv(s, buf, len, flags);
2995  }
2996  else {
2997  if (addr && addrlen)
2998  r = sendto(s, buf, len, flags, addr, *addrlen);
2999  else
3000  r = send(s, buf, len, flags);
3001  }
3002  if (r == SOCKET_ERROR)
3003  errno = map_errno(WSAGetLastError());
3004  });
3005  }
3006  else {
3007  DWORD size;
3008  DWORD rlen;
3009  wbuf.len = len;
3010  wbuf.buf = buf;
3011  memset(&wol, 0, sizeof(wol));
3012  RUBY_CRITICAL({
3013  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3014  if (input) {
3015  flg = flags;
3016  if (addr && addrlen)
3017  ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3018  &wol, NULL);
3019  else
3020  ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3021  }
3022  else {
3023  if (addr && addrlen)
3024  ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3025  &wol, NULL);
3026  else
3027  ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3028  }
3029  });
3030 
3031  finish_overlapped_socket(s, &wol, ret, &rlen, size);
3032  r = (int)rlen;
3033  }
3034 
3035  return r;
3036 }
3037 
3038 int WSAAPI
3039 rb_w32_recv(int fd, char *buf, int len, int flags)
3040 {
3041  return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
3042 }
3043 
3044 int WSAAPI
3045 rb_w32_recvfrom(int fd, char *buf, int len, int flags,
3046  struct sockaddr *from, int *fromlen)
3047 {
3048  return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
3049 }
3050 
3051 int WSAAPI
3052 rb_w32_send(int fd, const char *buf, int len, int flags)
3053 {
3054  return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
3055 }
3056 
3057 int WSAAPI
3058 rb_w32_sendto(int fd, const char *buf, int len, int flags,
3059  const struct sockaddr *to, int tolen)
3060 {
3061  return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
3062  (struct sockaddr *)to, &tolen);
3063 }
3064 
3065 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3066 typedef struct {
3067  SOCKADDR *name;
3068  int namelen;
3069  WSABUF *lpBuffers;
3071  WSABUF Control;
3073 } WSAMSG;
3074 #endif
3075 #ifndef WSAID_WSARECVMSG
3076 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3077 #endif
3078 #ifndef WSAID_WSASENDMSG
3079 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3080 #endif
3081 
3082 #define msghdr_to_wsamsg(msg, wsamsg) \
3083  do { \
3084  int i; \
3085  (wsamsg)->name = (msg)->msg_name; \
3086  (wsamsg)->namelen = (msg)->msg_namelen; \
3087  (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3088  (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3089  for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3090  (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3091  (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3092  } \
3093  (wsamsg)->Control.buf = (msg)->msg_control; \
3094  (wsamsg)->Control.len = (msg)->msg_controllen; \
3095  (wsamsg)->dwFlags = (msg)->msg_flags; \
3096  } while (0)
3097 
3098 int
3099 recvmsg(int fd, struct msghdr *msg, int flags)
3100 {
3101  typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3102  static WSARecvMsg_t pWSARecvMsg = NULL;
3103  WSAMSG wsamsg;
3104  SOCKET s;
3105  st_data_t data;
3106  int mode;
3107  DWORD len;
3108  int ret;
3109 
3110  if (!NtSocketsInitialized)
3111  StartSockets();
3112 
3113  s = TO_SOCKET(fd);
3114 
3115  if (!pWSARecvMsg) {
3116  static GUID guid = WSAID_WSARECVMSG;
3117  pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, &guid);
3118  if (!pWSARecvMsg)
3119  return -1;
3120  }
3121 
3122  msghdr_to_wsamsg(msg, &wsamsg);
3123  wsamsg.dwFlags |= flags;
3124 
3125  st_lookup(socklist, (st_data_t)s, &data);
3126  mode = (int)data;
3127  if (!cancel_io || (mode & O_NONBLOCK)) {
3128  RUBY_CRITICAL({
3129  if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
3130  errno = map_errno(WSAGetLastError());
3131  len = -1;
3132  }
3133  });
3134  }
3135  else {
3136  DWORD size;
3137  WSAOVERLAPPED wol;
3138  memset(&wol, 0, sizeof(wol));
3139  RUBY_CRITICAL({
3140  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3141  ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3142  });
3143 
3144  ret = finish_overlapped_socket(s, &wol, ret, &len, size);
3145  }
3146  if (ret == SOCKET_ERROR)
3147  return -1;
3148 
3149  /* WSAMSG to msghdr */
3150  msg->msg_name = wsamsg.name;
3151  msg->msg_namelen = wsamsg.namelen;
3152  msg->msg_flags = wsamsg.dwFlags;
3153 
3154  return len;
3155 }
3156 
3157 int
3158 sendmsg(int fd, const struct msghdr *msg, int flags)
3159 {
3160  typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3161  static WSASendMsg_t pWSASendMsg = NULL;
3162  WSAMSG wsamsg;
3163  SOCKET s;
3164  st_data_t data;
3165  int mode;
3166  DWORD len;
3167  int ret;
3168 
3169  if (!NtSocketsInitialized)
3170  StartSockets();
3171 
3172  s = TO_SOCKET(fd);
3173 
3174  if (!pWSASendMsg) {
3175  static GUID guid = WSAID_WSASENDMSG;
3176  pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, &guid);
3177  if (!pWSASendMsg)
3178  return -1;
3179  }
3180 
3181  msghdr_to_wsamsg(msg, &wsamsg);
3182 
3183  st_lookup(socklist, (st_data_t)s, &data);
3184  mode = (int)data;
3185  if (!cancel_io || (mode & O_NONBLOCK)) {
3186  RUBY_CRITICAL({
3187  if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
3188  errno = map_errno(WSAGetLastError());
3189  len = -1;
3190  }
3191  });
3192  }
3193  else {
3194  DWORD size;
3195  WSAOVERLAPPED wol;
3196  memset(&wol, 0, sizeof(wol));
3197  RUBY_CRITICAL({
3198  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3199  ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3200  });
3201 
3202  finish_overlapped_socket(s, &wol, ret, &len, size);
3203  }
3204 
3205  return len;
3206 }
3207 
3208 #undef setsockopt
3209 
3210 int WSAAPI
3211 rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
3212 {
3213  int r;
3214  if (!NtSocketsInitialized) {
3215  StartSockets();
3216  }
3217  RUBY_CRITICAL({
3218  r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3219  if (r == SOCKET_ERROR)
3220  errno = map_errno(WSAGetLastError());
3221  });
3222  return r;
3223 }
3224 
3225 #undef shutdown
3226 
3227 int WSAAPI
3228 rb_w32_shutdown(int s, int how)
3229 {
3230  int r;
3231  if (!NtSocketsInitialized) {
3232  StartSockets();
3233  }
3234  RUBY_CRITICAL({
3235  r = shutdown(TO_SOCKET(s), how);
3236  if (r == SOCKET_ERROR)
3237  errno = map_errno(WSAGetLastError());
3238  });
3239  return r;
3240 }
3241 
3242 static SOCKET
3243 open_ifs_socket(int af, int type, int protocol)
3244 {
3245  unsigned long proto_buffers_len = 0;
3246  int error_code;
3247  SOCKET out = INVALID_SOCKET;
3248 
3249  if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3250  error_code = WSAGetLastError();
3251  if (error_code == WSAENOBUFS) {
3252  WSAPROTOCOL_INFO *proto_buffers;
3253  int protocols_available = 0;
3254 
3255  proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3256  if (!proto_buffers) {
3257  WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3258  return INVALID_SOCKET;
3259  }
3260 
3261  protocols_available =
3262  WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3263  if (protocols_available != SOCKET_ERROR) {
3264  int i;
3265  for (i = 0; i < protocols_available; i++) {
3266  if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3267  (type != proto_buffers[i].iSocketType) ||
3268  (protocol != 0 && protocol != proto_buffers[i].iProtocol))
3269  continue;
3270 
3271  if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3272  continue;
3273 
3274  out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
3275  WSA_FLAG_OVERLAPPED);
3276  break;
3277  }
3278  if (out == INVALID_SOCKET)
3279  out = WSASocket(af, type, protocol, NULL, 0, 0);
3280  }
3281 
3282  free(proto_buffers);
3283  }
3284  }
3285 
3286  return out;
3287 }
3288 
3289 #undef socket
3290 
3291 int WSAAPI
3292 rb_w32_socket(int af, int type, int protocol)
3293 {
3294  SOCKET s;
3295  int fd;
3296 
3297  if (!NtSocketsInitialized) {
3298  StartSockets();
3299  }
3300  RUBY_CRITICAL({
3301  s = open_ifs_socket(af, type, protocol);
3302  if (s == INVALID_SOCKET) {
3303  errno = map_errno(WSAGetLastError());
3304  fd = -1;
3305  }
3306  else {
3307  fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3308  if (fd != -1)
3309  st_insert(socklist, (st_data_t)s, (st_data_t)0);
3310  else
3311  closesocket(s);
3312  }
3313  });
3314  return fd;
3315 }
3316 
3317 #undef gethostbyaddr
3318 
3319 struct hostent * WSAAPI
3320 rb_w32_gethostbyaddr(const char *addr, int len, int type)
3321 {
3322  struct hostent *r;
3323  if (!NtSocketsInitialized) {
3324  StartSockets();
3325  }
3326  RUBY_CRITICAL({
3327  r = gethostbyaddr(addr, len, type);
3328  if (r == NULL)
3329  errno = map_errno(WSAGetLastError());
3330  });
3331  return r;
3332 }
3333 
3334 #undef gethostbyname
3335 
3336 struct hostent * WSAAPI
3338 {
3339  struct hostent *r;
3340  if (!NtSocketsInitialized) {
3341  StartSockets();
3342  }
3343  RUBY_CRITICAL({
3344  r = gethostbyname(name);
3345  if (r == NULL)
3346  errno = map_errno(WSAGetLastError());
3347  });
3348  return r;
3349 }
3350 
3351 #undef gethostname
3352 
3353 int WSAAPI
3355 {
3356  int r;
3357  if (!NtSocketsInitialized) {
3358  StartSockets();
3359  }
3360  RUBY_CRITICAL({
3361  r = gethostname(name, len);
3362  if (r == SOCKET_ERROR)
3363  errno = map_errno(WSAGetLastError());
3364  });
3365  return r;
3366 }
3367 
3368 #undef getprotobyname
3369 
3370 struct protoent * WSAAPI
3372 {
3373  struct protoent *r;
3374  if (!NtSocketsInitialized) {
3375  StartSockets();
3376  }
3377  RUBY_CRITICAL({
3378  r = getprotobyname(name);
3379  if (r == NULL)
3380  errno = map_errno(WSAGetLastError());
3381  });
3382  return r;
3383 }
3384 
3385 #undef getprotobynumber
3386 
3387 struct protoent * WSAAPI
3389 {
3390  struct protoent *r;
3391  if (!NtSocketsInitialized) {
3392  StartSockets();
3393  }
3394  RUBY_CRITICAL({
3395  r = getprotobynumber(num);
3396  if (r == NULL)
3397  errno = map_errno(WSAGetLastError());
3398  });
3399  return r;
3400 }
3401 
3402 #undef getservbyname
3403 
3404 struct servent * WSAAPI
3405 rb_w32_getservbyname(const char *name, const char *proto)
3406 {
3407  struct servent *r;
3408  if (!NtSocketsInitialized) {
3409  StartSockets();
3410  }
3411  RUBY_CRITICAL({
3412  r = getservbyname(name, proto);
3413  if (r == NULL)
3414  errno = map_errno(WSAGetLastError());
3415  });
3416  return r;
3417 }
3418 
3419 #undef getservbyport
3420 
3421 struct servent * WSAAPI
3422 rb_w32_getservbyport(int port, const char *proto)
3423 {
3424  struct servent *r;
3425  if (!NtSocketsInitialized) {
3426  StartSockets();
3427  }
3428  RUBY_CRITICAL({
3429  r = getservbyport(port, proto);
3430  if (r == NULL)
3431  errno = map_errno(WSAGetLastError());
3432  });
3433  return r;
3434 }
3435 
3436 static int
3437 socketpair_internal(int af, int type, int protocol, SOCKET *sv)
3438 {
3439  SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
3440  struct sockaddr_in sock_in4;
3441 #ifdef INET6
3442  struct sockaddr_in6 sock_in6;
3443 #endif
3444  struct sockaddr *addr;
3445  int ret = -1;
3446  int len;
3447 
3448  if (!NtSocketsInitialized) {
3449  StartSockets();
3450  }
3451 
3452  switch (af) {
3453  case AF_INET:
3454 #if defined PF_INET && PF_INET != AF_INET
3455  case PF_INET:
3456 #endif
3457  sock_in4.sin_family = AF_INET;
3458  sock_in4.sin_port = 0;
3459  sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
3460  addr = (struct sockaddr *)&sock_in4;
3461  len = sizeof(sock_in4);
3462  break;
3463 #ifdef INET6
3464  case AF_INET6:
3465  memset(&sock_in6, 0, sizeof(sock_in6));
3466  sock_in6.sin6_family = AF_INET6;
3467  sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
3468  addr = (struct sockaddr *)&sock_in6;
3469  len = sizeof(sock_in6);
3470  break;
3471 #endif
3472  default:
3473  errno = EAFNOSUPPORT;
3474  return -1;
3475  }
3476  if (type != SOCK_STREAM) {
3477  errno = EPROTOTYPE;
3478  return -1;
3479  }
3480 
3481  sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
3482  sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
3483  RUBY_CRITICAL({
3484  do {
3485  svr = open_ifs_socket(af, type, protocol);
3486  if (svr == INVALID_SOCKET)
3487  break;
3488  if (bind(svr, addr, len) < 0)
3489  break;
3490  if (getsockname(svr, addr, &len) < 0)
3491  break;
3492  if (type == SOCK_STREAM)
3493  listen(svr, 5);
3494 
3495  w = open_ifs_socket(af, type, protocol);
3496  if (w == INVALID_SOCKET)
3497  break;
3498  if (connect(w, addr, len) < 0)
3499  break;
3500 
3501  r = accept(svr, addr, &len);
3502  if (r == INVALID_SOCKET)
3503  break;
3504 
3505  ret = 0;
3506  } while (0);
3507 
3508  if (ret < 0) {
3509  errno = map_errno(WSAGetLastError());
3510  if (r != INVALID_SOCKET)
3511  closesocket(r);
3512  if (w != INVALID_SOCKET)
3513  closesocket(w);
3514  }
3515  else {
3516  sv[0] = r;
3517  sv[1] = w;
3518  }
3519  if (svr != INVALID_SOCKET)
3520  closesocket(svr);
3521  });
3522 
3523  return ret;
3524 }
3525 
3526 int
3527 rb_w32_socketpair(int af, int type, int protocol, int *sv)
3528 {
3529  SOCKET pair[2];
3530 
3531  if (socketpair_internal(af, type, protocol, pair) < 0)
3532  return -1;
3533  sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
3534  if (sv[0] == -1) {
3535  closesocket(pair[0]);
3536  closesocket(pair[1]);
3537  return -1;
3538  }
3539  sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
3540  if (sv[1] == -1) {
3541  rb_w32_close(sv[0]);
3542  closesocket(pair[1]);
3543  return -1;
3544  }
3545  st_insert(socklist, (st_data_t)pair[0], (st_data_t)0);
3546  st_insert(socklist, (st_data_t)pair[1], (st_data_t)0);
3547 
3548  return 0;
3549 }
3550 
3551 //
3552 // Networking stubs
3553 //
3554 
3555 void endhostent(void) {}
3556 void endnetent(void) {}
3557 void endprotoent(void) {}
3558 void endservent(void) {}
3559 
3560 struct netent *getnetent (void) {return (struct netent *) NULL;}
3561 
3562 struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
3563 
3564 struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
3565 
3566 struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
3567 
3568 struct servent *getservent (void) {return (struct servent *) NULL;}
3569 
3570 void sethostent (int stayopen) {}
3571 
3572 void setnetent (int stayopen) {}
3573 
3574 void setprotoent (int stayopen) {}
3575 
3576 void setservent (int stayopen) {}
3577 
3578 int
3579 fcntl(int fd, int cmd, ...)
3580 {
3581  SOCKET sock = TO_SOCKET(fd);
3582  va_list va;
3583  int arg;
3584  int ret;
3585  int flag = 0;
3586  st_data_t data;
3587  u_long ioctlArg;
3588 
3589  if (!is_socket(sock)) {
3590  errno = EBADF;
3591  return -1;
3592  }
3593  if (cmd != F_SETFL) {
3594  errno = EINVAL;
3595  return -1;
3596  }
3597 
3598  va_start(va, cmd);
3599  arg = va_arg(va, int);
3600  va_end(va);
3601  st_lookup(socklist, (st_data_t)sock, &data);
3602  flag = (int)data;
3603  if (arg & O_NONBLOCK) {
3604  flag |= O_NONBLOCK;
3605  ioctlArg = 1;
3606  }
3607  else {
3608  flag &= ~O_NONBLOCK;
3609  ioctlArg = 0;
3610  }
3611  RUBY_CRITICAL({
3612  ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
3613  if (ret == 0)
3614  st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
3615  else
3616  errno = map_errno(WSAGetLastError());
3617  });
3618 
3619  return ret;
3620 }
3621 
3622 #ifndef WNOHANG
3623 #define WNOHANG -1
3624 #endif
3625 
3626 static rb_pid_t
3627 poll_child_status(struct ChildRecord *child, int *stat_loc)
3628 {
3629  DWORD exitcode;
3630  DWORD err;
3631 
3632  if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
3633  /* If an error occured, return immediatly. */
3634  error_exit:
3635  err = GetLastError();
3636  if (err == ERROR_INVALID_PARAMETER)
3637  errno = ECHILD;
3638  else {
3639  if (GetLastError() == ERROR_INVALID_HANDLE)
3640  errno = EINVAL;
3641  else
3642  errno = map_errno(GetLastError());
3643  }
3644  CloseChildHandle(child);
3645  return -1;
3646  }
3647  if (exitcode != STILL_ACTIVE) {
3648  rb_pid_t pid;
3649  /* If already died, wait process's real termination. */
3650  if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
3651  goto error_exit;
3652  }
3653  pid = child->pid;
3654  CloseChildHandle(child);
3655  if (stat_loc) *stat_loc = exitcode << 8;
3656  return pid;
3657  }
3658  return 0;
3659 }
3660 
3661 rb_pid_t
3662 waitpid(rb_pid_t pid, int *stat_loc, int options)
3663 {
3664  DWORD timeout;
3665 
3666  if (options == WNOHANG) {
3667  timeout = 0;
3668  }
3669  else {
3670  timeout = INFINITE;
3671  }
3672 
3673  if (pid == -1) {
3674  int count = 0;
3675  int ret;
3676  HANDLE events[MAXCHILDNUM];
3677 
3678  FOREACH_CHILD(child) {
3679  if (!child->pid || child->pid < 0) continue;
3680  if ((pid = poll_child_status(child, stat_loc))) return pid;
3681  events[count++] = child->hProcess;
3683  if (!count) {
3684  errno = ECHILD;
3685  return -1;
3686  }
3687 
3688  ret = rb_w32_wait_events_blocking(events, count, timeout);
3689  if (ret == WAIT_TIMEOUT) return 0;
3690  if ((ret -= WAIT_OBJECT_0) == count) {
3691  return -1;
3692  }
3693  if (ret > count) {
3694  errno = map_errno(GetLastError());
3695  return -1;
3696  }
3697 
3698  return poll_child_status(FindChildSlotByHandle(events[ret]), stat_loc);
3699  }
3700  else {
3701  struct ChildRecord* child = FindChildSlot(pid);
3702  if (!child) {
3703  errno = ECHILD;
3704  return -1;
3705  }
3706 
3707  while (!(pid = poll_child_status(child, stat_loc))) {
3708  /* wait... */
3709  if (rb_w32_wait_events_blocking(&child->hProcess, 1, timeout) != WAIT_OBJECT_0) {
3710  /* still active */
3711  pid = 0;
3712  break;
3713  }
3714  }
3715  }
3716 
3717  return pid;
3718 }
3719 
3720 #include <sys/timeb.h>
3721 
3722 static int
3723 filetime_to_timeval(const FILETIME* ft, struct timeval *tv)
3724 {
3725  ULARGE_INTEGER tmp;
3726  unsigned LONG_LONG lt;
3727 
3728  tmp.LowPart = ft->dwLowDateTime;
3729  tmp.HighPart = ft->dwHighDateTime;
3730  lt = tmp.QuadPart;
3731 
3732  /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
3733  convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
3734  the first leap second is at 1972/06/30, so we doesn't need to think
3735  about it. */
3736  lt /= 10; /* to usec */
3737  lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * 1000 * 1000;
3738 
3739  tv->tv_sec = (long)(lt / (1000 * 1000));
3740  tv->tv_usec = (long)(lt % (1000 * 1000));
3741 
3742  return tv->tv_sec > 0 ? 0 : -1;
3743 }
3744 
3745 int _cdecl
3746 gettimeofday(struct timeval *tv, struct timezone *tz)
3747 {
3748  FILETIME ft;
3749 
3750  GetSystemTimeAsFileTime(&ft);
3751  filetime_to_timeval(&ft, tv);
3752 
3753  return 0;
3754 }
3755 
3756 char *
3757 rb_w32_getcwd(char *buffer, int size)
3758 {
3759  char *p = buffer;
3760  int len;
3761 
3762  len = GetCurrentDirectory(0, NULL);
3763  if (!len) {
3764  errno = map_errno(GetLastError());
3765  return NULL;
3766  }
3767 
3768  if (p) {
3769  if (size < len) {
3770  errno = ERANGE;
3771  return NULL;
3772  }
3773  }
3774  else {
3775  p = malloc(len);
3776  size = len;
3777  if (!p) {
3778  errno = ENOMEM;
3779  return NULL;
3780  }
3781  }
3782 
3783  if (!GetCurrentDirectory(size, p)) {
3784  errno = map_errno(GetLastError());
3785  if (!buffer)
3786  free(p);
3787  return NULL;
3788  }
3789 
3790  translate_char(p, '\\', '/');
3791 
3792  return p;
3793 }
3794 
3795 int
3796 chown(const char *path, int owner, int group)
3797 {
3798  return 0;
3799 }
3800 
3801 int
3802 rb_w32_uchown(const char *path, int owner, int group)
3803 {
3804  return 0;
3805 }
3806 
3807 int
3808 kill(int pid, int sig)
3809 {
3810  int ret = 0;
3811  DWORD err;
3812 
3813  if (pid < 0 || pid == 0 && sig != SIGINT) {
3814  errno = EINVAL;
3815  return -1;
3816  }
3817 
3818  (void)IfWin95(pid = -pid, 0);
3819  if ((unsigned int)pid == GetCurrentProcessId() &&
3820  (sig != 0 && sig != SIGKILL)) {
3821  if ((ret = raise(sig)) != 0) {
3822  /* MSVCRT doesn't set errno... */
3823  errno = EINVAL;
3824  }
3825  return ret;
3826  }
3827 
3828  switch (sig) {
3829  case 0:
3830  RUBY_CRITICAL({
3831  HANDLE hProc =
3832  OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
3833  if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
3834  if (GetLastError() == ERROR_INVALID_PARAMETER) {
3835  errno = ESRCH;
3836  }
3837  else {
3838  errno = EPERM;
3839  }
3840  ret = -1;
3841  }
3842  else {
3843  CloseHandle(hProc);
3844  }
3845  });
3846  break;
3847 
3848  case SIGINT:
3849  RUBY_CRITICAL({
3850  DWORD ctrlEvent = CTRL_C_EVENT;
3851  if (pid != 0) {
3852  /* CTRL+C signal cannot be generated for process groups.
3853  * Instead, we use CTRL+BREAK signal. */
3854  ctrlEvent = CTRL_BREAK_EVENT;
3855  }
3856  if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
3857  if ((err = GetLastError()) == 0)
3858  errno = EPERM;
3859  else
3860  errno = map_errno(GetLastError());
3861  ret = -1;
3862  }
3863  });
3864  break;
3865 
3866  case SIGKILL:
3867  RUBY_CRITICAL({
3868  HANDLE hProc;
3869  struct ChildRecord* child = FindChildSlot(pid);
3870  if (child) {
3871  hProc = child->hProcess;
3872  }
3873  else {
3874  hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
3875  }
3876  if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
3877  if (GetLastError() == ERROR_INVALID_PARAMETER) {
3878  errno = ESRCH;
3879  }
3880  else {
3881  errno = EPERM;
3882  }
3883  ret = -1;
3884  }
3885  else {
3886  DWORD status;
3887  if (!GetExitCodeProcess(hProc, &status)) {
3888  errno = map_errno(GetLastError());
3889  ret = -1;
3890  }
3891  else if (status == STILL_ACTIVE) {
3892  if (!TerminateProcess(hProc, 0)) {
3893  errno = EPERM;
3894  ret = -1;
3895  }
3896  }
3897  else {
3898  errno = ESRCH;
3899  ret = -1;
3900  }
3901  if (!child) {
3902  CloseHandle(hProc);
3903  }
3904  }
3905  });
3906  break;
3907 
3908  default:
3909  errno = EINVAL;
3910  ret = -1;
3911  break;
3912  }
3913 
3914  return ret;
3915 }
3916 
3917 static int
3918 wlink(const WCHAR *from, const WCHAR *to)
3919 {
3920  typedef BOOL (WINAPI link_func)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
3921  static link_func *pCreateHardLinkW = NULL;
3922  static int myerrno = 0;
3923 
3924  if (!pCreateHardLinkW && !myerrno) {
3925  pCreateHardLinkW = (link_func *)get_proc_address("kernel32", "CreateHardLinkW", NULL);
3926  if (!pCreateHardLinkW)
3927  myerrno = ENOSYS;
3928  }
3929  if (!pCreateHardLinkW) {
3930  errno = myerrno;
3931  return -1;
3932  }
3933 
3934  if (!pCreateHardLinkW(to, from, NULL)) {
3935  errno = map_errno(GetLastError());
3936  return -1;
3937  }
3938 
3939  return 0;
3940 }
3941 
3942 int
3943 rb_w32_ulink(const char *from, const char *to)
3944 {
3945  WCHAR *wfrom;
3946  WCHAR *wto;
3947  int ret;
3948 
3949  if (!(wfrom = utf8_to_wstr(from, NULL)))
3950  return -1;
3951  if (!(wto = utf8_to_wstr(to, NULL))) {
3952  free(wfrom);
3953  return -1;
3954  }
3955  ret = wlink(wfrom, wto);
3956  free(wto);
3957  free(wfrom);
3958  return ret;
3959 }
3960 
3961 int
3962 link(const char *from, const char *to)
3963 {
3964  WCHAR *wfrom;
3965  WCHAR *wto;
3966  int ret;
3967 
3968  if (!(wfrom = filecp_to_wstr(from, NULL)))
3969  return -1;
3970  if (!(wto = filecp_to_wstr(to, NULL))) {
3971  free(wfrom);
3972  return -1;
3973  }
3974  ret = wlink(wfrom, wto);
3975  free(wto);
3976  free(wfrom);
3977  return ret;
3978 }
3979 
3980 int
3981 wait(int *status)
3982 {
3983  return waitpid(-1, status, 0);
3984 }
3985 
3986 char *
3987 rb_w32_getenv(const char *name)
3988 {
3989  int len = strlen(name);
3990  char *env;
3991 
3992  if (len == 0) return NULL;
3993  if (envarea) FreeEnvironmentStrings(envarea);
3994  envarea = GetEnvironmentStrings();
3995  if (!envarea) {
3996  map_errno(GetLastError());
3997  return NULL;
3998  }
3999 
4000  for (env = envarea; *env; env += strlen(env) + 1)
4001  if (strncasecmp(env, name, len) == 0 && *(env + len) == '=')
4002  return env + len + 1;
4003 
4004  return NULL;
4005 }
4006 
4007 static int
4008 wrename(const WCHAR *oldpath, const WCHAR *newpath)
4009 {
4010  int res = 0;
4011  int oldatts;
4012  int newatts;
4013 
4014  oldatts = GetFileAttributesW(oldpath);
4015  newatts = GetFileAttributesW(newpath);
4016 
4017  if (oldatts == -1) {
4018  errno = map_errno(GetLastError());
4019  return -1;
4020  }
4021 
4022  RUBY_CRITICAL({
4023  if (newatts != -1 && newatts & FILE_ATTRIBUTE_READONLY)
4024  SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
4025 
4026  if (IsWinNT()) {
4027  if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
4028  res = -1;
4029  }
4030  else {
4031  if (!MoveFileW(oldpath, newpath))
4032  res = -1;
4033 
4034  if (res) {
4035  switch (GetLastError()) {
4036  case ERROR_ALREADY_EXISTS:
4037  case ERROR_FILE_EXISTS:
4038  for (;;) {
4039  if (!DeleteFileW(newpath) && GetLastError() != ERROR_FILE_NOT_FOUND)
4040  break;
4041  else if (MoveFileW(oldpath, newpath)) {
4042  res = 0;
4043  break;
4044  }
4045  }
4046  }
4047  }
4048  }
4049 
4050  if (res)
4051  errno = map_errno(GetLastError());
4052  else
4053  SetFileAttributesW(newpath, oldatts);
4054  });
4055 
4056  return res;
4057 }
4058 
4059 int rb_w32_urename(const char *from, const char *to)
4060 {
4061  WCHAR *wfrom;
4062  WCHAR *wto;
4063  int ret = -1;
4064 
4065  if (!(wfrom = utf8_to_wstr(from, NULL)))
4066  return -1;
4067  if (!(wto = utf8_to_wstr(to, NULL))) {
4068  free(wfrom);
4069  return -1;
4070  }
4071  ret = wrename(wfrom, wto);
4072  free(wto);
4073  free(wfrom);
4074  return ret;
4075 }
4076 
4077 int rb_w32_rename(const char *from, const char *to)
4078 {
4079  WCHAR *wfrom;
4080  WCHAR *wto;
4081  int ret = -1;
4082 
4083  if (!(wfrom = filecp_to_wstr(from, NULL)))
4084  return -1;
4085  if (!(wto = filecp_to_wstr(to, NULL))) {
4086  free(wfrom);
4087  return -1;
4088  }
4089  ret = wrename(wfrom, wto);
4090  free(wto);
4091  free(wfrom);
4092  return ret;
4093 }
4094 
4095 static int
4096 isUNCRoot(const WCHAR *path)
4097 {
4098  if (path[0] == L'\\' && path[1] == L'\\') {
4099  const WCHAR *p;
4100  for (p = path + 2; *p; p++) {
4101  if (*p == L'\\')
4102  break;
4103  }
4104  if (p[0] && p[1]) {
4105  for (p++; *p; p++) {
4106  if (*p == L'\\')
4107  break;
4108  }
4109  if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
4110  return 1;
4111  }
4112  }
4113  return 0;
4114 }
4115 
4116 #define COPY_STAT(src, dest, size_cast) do { \
4117  (dest).st_dev = (src).st_dev; \
4118  (dest).st_ino = (src).st_ino; \
4119  (dest).st_mode = (src).st_mode; \
4120  (dest).st_nlink = (src).st_nlink; \
4121  (dest).st_uid = (src).st_uid; \
4122  (dest).st_gid = (src).st_gid; \
4123  (dest).st_rdev = (src).st_rdev; \
4124  (dest).st_size = size_cast(src).st_size; \
4125  (dest).st_atime = (src).st_atime; \
4126  (dest).st_mtime = (src).st_mtime; \
4127  (dest).st_ctime = (src).st_ctime; \
4128  } while (0)
4129 
4130 static time_t filetime_to_unixtime(const FILETIME *ft);
4131 
4132 #undef fstat
4133 int
4134 rb_w32_fstat(int fd, struct stat *st)
4135 {
4136  BY_HANDLE_FILE_INFORMATION info;
4137  int ret = fstat(fd, st);
4138 
4139  if (ret) return ret;
4140 #ifdef __BORLANDC__
4141  st->st_mode &= ~(S_IWGRP | S_IWOTH);
4142 #endif
4143  if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
4144 #ifdef __BORLANDC__
4145  if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
4146  st->st_mode |= S_IWUSR;
4147  }
4148 #endif
4149  st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
4150  st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
4151  st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
4152  }
4153  return ret;
4154 }
4155 
4156 int
4157 rb_w32_fstati64(int fd, struct stati64 *st)
4158 {
4159  BY_HANDLE_FILE_INFORMATION info;
4160  struct stat tmp;
4161  int ret = fstat(fd, &tmp);
4162 
4163  if (ret) return ret;
4164 #ifdef __BORLANDC__
4165  tmp.st_mode &= ~(S_IWGRP | S_IWOTH);
4166 #endif
4167  COPY_STAT(tmp, *st, +);
4168  if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
4169 #ifdef __BORLANDC__
4170  if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
4171  st->st_mode |= S_IWUSR;
4172  }
4173 #endif
4174  st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
4175  st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
4176  st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
4177  st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
4178  }
4179  return ret;
4180 }
4181 
4182 static time_t
4183 filetime_to_unixtime(const FILETIME *ft)
4184 {
4185  struct timeval tv;
4186 
4187  if (filetime_to_timeval(ft, &tv) == (time_t)-1)
4188  return 0;
4189  else
4190  return tv.tv_sec;
4191 }
4192 
4193 static unsigned
4194 fileattr_to_unixmode(DWORD attr, const WCHAR *path)
4195 {
4196  unsigned mode = 0;
4197 
4198  if (attr & FILE_ATTRIBUTE_READONLY) {
4199  mode |= S_IREAD;
4200  }
4201  else {
4202  mode |= S_IREAD | S_IWRITE | S_IWUSR;
4203  }
4204 
4205  if (attr & FILE_ATTRIBUTE_DIRECTORY) {
4206  mode |= S_IFDIR | S_IEXEC;
4207  }
4208  else {
4209  mode |= S_IFREG;
4210  }
4211 
4212  if (path && (mode & S_IFREG)) {
4213  const WCHAR *end = path + lstrlenW(path);
4214  while (path < end) {
4215  end = CharPrevW(path, end);
4216  if (*end == L'.') {
4217  if ((_wcsicmp(end, L".bat") == 0) ||
4218  (_wcsicmp(end, L".cmd") == 0) ||
4219  (_wcsicmp(end, L".com") == 0) ||
4220  (_wcsicmp(end, L".exe") == 0)) {
4221  mode |= S_IEXEC;
4222  }
4223  break;
4224  }
4225  }
4226  }
4227 
4228  mode |= (mode & 0700) >> 3;
4229  mode |= (mode & 0700) >> 6;
4230 
4231  return mode;
4232 }
4233 
4234 static int
4235 check_valid_dir(const WCHAR *path)
4236 {
4237  WIN32_FIND_DATAW fd;
4238  HANDLE fh;
4239 
4240  /* GetFileAttributes() determines "..." as directory. */
4241  /* We recheck it by FindFirstFile(). */
4242  if (wcsstr(path, L"...") == NULL)
4243  return 0;
4244 
4245  fh = open_dir_handle(path, &fd);
4246  if (fh == INVALID_HANDLE_VALUE)
4247  return -1;
4248  FindClose(fh);
4249  return 0;
4250 }
4251 
4252 static int
4253 winnt_stat(const WCHAR *path, struct stati64 *st)
4254 {
4255  HANDLE h;
4256  WIN32_FIND_DATAW wfd;
4257  WIN32_FILE_ATTRIBUTE_DATA wfa;
4258 
4259  memset(st, 0, sizeof(*st));
4260  st->st_nlink = 1;
4261 
4262  if (wcspbrk(path, L"?*")) {
4263  errno = ENOENT;
4264  return -1;
4265  }
4266  if (GetFileAttributesExW(path, GetFileExInfoStandard, (void*)&wfa)) {
4267  if (wfa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
4268  if (check_valid_dir(path)) return -1;
4269  st->st_size = 0;
4270  }
4271  else {
4272  st->st_size = ((__int64)wfa.nFileSizeHigh << 32) | wfa.nFileSizeLow;
4273  }
4274  st->st_mode = fileattr_to_unixmode(wfa.dwFileAttributes, path);
4275  st->st_atime = filetime_to_unixtime(&wfa.ftLastAccessTime);
4276  st->st_mtime = filetime_to_unixtime(&wfa.ftLastWriteTime);
4277  st->st_ctime = filetime_to_unixtime(&wfa.ftCreationTime);
4278  }
4279  else {
4280  /* GetFileAttributesEx failed; check why. */
4281  int e = GetLastError();
4282 
4283  if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME)
4284  || (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) {
4285  errno = map_errno(e);
4286  return -1;
4287  }
4288 
4289  /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
4290  h = FindFirstFileW(path, &wfd);
4291  if (h != INVALID_HANDLE_VALUE) {
4292  FindClose(h);
4293  st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path);
4294  st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
4295  st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
4296  st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
4297  st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
4298  }
4299  else {
4300  errno = map_errno(GetLastError());
4301  return -1;
4302  }
4303  }
4304 
4305  st->st_dev = st->st_rdev = (iswalpha(path[0]) && path[1] == L':') ?
4306  towupper(path[0]) - L'A' : _getdrive() - 1;
4307 
4308  return 0;
4309 }
4310 
4311 #ifdef WIN95
4312 static int
4313 win95_stat(const WCHAR *path, struct stati64 *st)
4314 {
4315  int ret = _wstati64(path, st);
4316  if (ret) return ret;
4317  if (st->st_mode & S_IFDIR) {
4318  return check_valid_dir(path);
4319  }
4320  return 0;
4321 }
4322 #else
4323 #define win95_stat(path, st) -1
4324 #endif
4325 
4326 int
4327 rb_w32_stat(const char *path, struct stat *st)
4328 {
4329  struct stati64 tmp;
4330 
4331  if (rb_w32_stati64(path, &tmp)) return -1;
4332  COPY_STAT(tmp, *st, (_off_t));
4333  return 0;
4334 }
4335 
4336 static int
4337 wstati64(const WCHAR *path, struct stati64 *st)
4338 {
4339  const WCHAR *p;
4340  WCHAR *buf1, *s, *end;
4341  int len, size;
4342  int ret;
4343  VALUE v;
4344 
4345  if (!path || !st) {
4346  errno = EFAULT;
4347  return -1;
4348  }
4349  size = lstrlenW(path) + 2;
4350  buf1 = ALLOCV_N(WCHAR, v, size);
4351  for (p = path, s = buf1; *p; p++, s++) {
4352  if (*p == L'/')
4353  *s = L'\\';
4354  else
4355  *s = *p;
4356  }
4357  *s = '\0';
4358  len = s - buf1;
4359  if (!len || L'\"' == *(--s)) {
4360  errno = ENOENT;
4361  return -1;
4362  }
4363  end = buf1 + len - 1;
4364 
4365  if (isUNCRoot(buf1)) {
4366  if (*end == L'.')
4367  *end = L'\0';
4368  else if (*end != L'\\')
4369  lstrcatW(buf1, L"\\");
4370  }
4371  else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
4372  lstrcatW(buf1, L".");
4373 
4374  ret = IsWinNT() ? winnt_stat(buf1, st) : win95_stat(buf1, st);
4375  if (ret == 0) {
4376  st->st_mode &= ~(S_IWGRP | S_IWOTH);
4377  }
4378  if (v)
4379  ALLOCV_END(v);
4380 
4381  return ret;
4382 }
4383 
4384 int
4385 rb_w32_ustati64(const char *path, struct stati64 *st)
4386 {
4387  WCHAR *wpath;
4388  int ret;
4389 
4390  if (!(wpath = utf8_to_wstr(path, NULL)))
4391  return -1;
4392  ret = wstati64(wpath, st);
4393  free(wpath);
4394  return ret;
4395 }
4396 
4397 int
4398 rb_w32_stati64(const char *path, struct stati64 *st)
4399 {
4400  WCHAR *wpath;
4401  int ret;
4402 
4403  if (!(wpath = filecp_to_wstr(path, NULL)))
4404  return -1;
4405  ret = wstati64(wpath, st);
4406  free(wpath);
4407  return ret;
4408 }
4409 
4410 int
4411 rb_w32_access(const char *path, int mode)
4412 {
4413  struct stati64 stat;
4414  if (rb_w32_stati64(path, &stat) != 0)
4415  return -1;
4416  mode <<= 6;
4417  if ((stat.st_mode & mode) != mode) {
4418  errno = EACCES;
4419  return -1;
4420  }
4421  return 0;
4422 }
4423 
4424 int
4425 rb_w32_uaccess(const char *path, int mode)
4426 {
4427  struct stati64 stat;
4428  if (rb_w32_ustati64(path, &stat) != 0)
4429  return -1;
4430  mode <<= 6;
4431  if ((stat.st_mode & mode) != mode) {
4432  errno = EACCES;
4433  return -1;
4434  }
4435  return 0;
4436 }
4437 
4438 static int
4440 {
4441  long upos, lpos, usize, lsize, uend, lend;
4442  off_t end;
4443  int ret = -1;
4444  DWORD e;
4445 
4446  if (((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
4447  (e = GetLastError())) ||
4448  ((lend = GetFileSize(h, (DWORD *)&uend)) == -1L && (e = GetLastError()))) {
4449  errno = map_errno(e);
4450  return -1;
4451  }
4452  end = ((off_t)uend << 32) | (unsigned long)lend;
4453  usize = (long)(size >> 32);
4454  lsize = (long)size;
4455  if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
4456  (e = GetLastError())) {
4457  errno = map_errno(e);
4458  }
4459  else if (!SetEndOfFile(h)) {
4460  errno = map_errno(GetLastError());
4461  }
4462  else {
4463  ret = 0;
4464  }
4465  SetFilePointer(h, lpos, &upos, SEEK_SET);
4466  return ret;
4467 }
4468 
4469 int
4470 rb_w32_truncate(const char *path, off_t length)
4471 {
4472  HANDLE h;
4473  int ret;
4474 #ifdef WIN95
4475  if (IsWin95()) {
4476  int fd = open(path, O_WRONLY), e = 0;
4477  if (fd == -1) return -1;
4478  ret = chsize(fd, (unsigned long)length);
4479  if (ret == -1) e = errno;
4480  close(fd);
4481  if (ret == -1) errno = e;
4482  return ret;
4483  }
4484 #endif
4485  h = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
4486  if (h == INVALID_HANDLE_VALUE) {
4487  errno = map_errno(GetLastError());
4488  return -1;
4489  }
4490  ret = rb_chsize(h, length);
4491  CloseHandle(h);
4492  return ret;
4493 }
4494 
4495 int
4496 rb_w32_ftruncate(int fd, off_t length)
4497 {
4498  HANDLE h;
4499 
4500 #ifdef WIN95
4501  if (IsWin95()) {
4502  return chsize(fd, (unsigned long)length);
4503  }
4504 #endif
4505  h = (HANDLE)_get_osfhandle(fd);
4506  if (h == (HANDLE)-1) return -1;
4507  return rb_chsize(h, length);
4508 }
4509 
4510 #ifdef __BORLANDC__
4511 off_t
4512 _filelengthi64(int fd)
4513 {
4514  DWORD u, l;
4515  int e;
4516 
4517  l = GetFileSize((HANDLE)_get_osfhandle(fd), &u);
4518  if (l == (DWORD)-1L && (e = GetLastError())) {
4519  errno = map_errno(e);
4520  return (off_t)-1;
4521  }
4522  return ((off_t)u << 32) | l;
4523 }
4524 
4525 off_t
4526 _lseeki64(int fd, off_t offset, int whence)
4527 {
4528  long u, l;
4529  int e;
4530  HANDLE h = (HANDLE)_get_osfhandle(fd);
4531 
4532  if (!h) {
4533  errno = EBADF;
4534  return -1;
4535  }
4536  u = (long)(offset >> 32);
4537  if ((l = SetFilePointer(h, (long)offset, &u, whence)) == -1L &&
4538  (e = GetLastError())) {
4539  errno = map_errno(e);
4540  return -1;
4541  }
4542  return ((off_t)u << 32) | l;
4543 }
4544 #endif
4545 
4546 int
4547 fseeko(FILE *stream, off_t offset, int whence)
4548 {
4549  off_t pos;
4550  switch (whence) {
4551  case SEEK_CUR:
4552  if (fgetpos(stream, (fpos_t *)&pos))
4553  return -1;
4554  pos += offset;
4555  break;
4556  case SEEK_END:
4557  if ((pos = _filelengthi64(fileno(stream))) == (off_t)-1)
4558  return -1;
4559  pos += offset;
4560  break;
4561  default:
4562  pos = offset;
4563  break;
4564  }
4565  return fsetpos(stream, (fpos_t *)&pos);
4566 }
4567 
4568 off_t
4570 {
4571  off_t pos;
4572  if (fgetpos(stream, (fpos_t *)&pos)) return (off_t)-1;
4573  return pos;
4574 }
4575 
4576 static long
4577 filetime_to_clock(FILETIME *ft)
4578 {
4579  __int64 qw = ft->dwHighDateTime;
4580  qw <<= 32;
4581  qw |= ft->dwLowDateTime;
4582  qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
4583  return (long) qw;
4584 }
4585 
4586 int
4587 rb_w32_times(struct tms *tmbuf)
4588 {
4589  FILETIME create, exit, kernel, user;
4590 
4591  if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
4592  tmbuf->tms_utime = filetime_to_clock(&user);
4593  tmbuf->tms_stime = filetime_to_clock(&kernel);
4594  tmbuf->tms_cutime = 0;
4595  tmbuf->tms_cstime = 0;
4596  }
4597  else {
4598  tmbuf->tms_utime = clock();
4599  tmbuf->tms_stime = 0;
4600  tmbuf->tms_cutime = 0;
4601  tmbuf->tms_cstime = 0;
4602  }
4603  return 0;
4604 }
4605 
4606 #define yield_once() Sleep(0)
4607 #define yield_until(condition) do yield_once(); while (!(condition))
4608 
4609 static void
4611 {
4612  yield_once();
4614 }
4615 
4616 #if defined __BORLANDC__
4617 #undef read
4618 int
4619 read(int fd, void *buf, size_t size)
4620 {
4621  int ret = _read(fd, buf, size);
4622  if ((ret < 0) && (errno == EPIPE)) {
4623  errno = 0;
4624  ret = 0;
4625  }
4626  catch_interrupt();
4627  return ret;
4628 }
4629 #endif
4630 
4631 #undef fgetc
4632 int
4634 {
4635  int c;
4636  if (enough_to_get(stream->FILE_COUNT)) {
4637  c = (unsigned char)*stream->FILE_READPTR++;
4638  }
4639  else {
4640  c = _filbuf(stream);
4641 #if defined __BORLANDC__
4642  if ((c == EOF) && (errno == EPIPE)) {
4643  clearerr(stream);
4644  }
4645 #endif
4646  catch_interrupt();
4647  }
4648  return c;
4649 }
4650 
4651 #undef fputc
4652 int
4653 rb_w32_putc(int c, FILE* stream)
4654 {
4655  if (enough_to_put(stream->FILE_COUNT)) {
4656  c = (unsigned char)(*stream->FILE_READPTR++ = (char)c);
4657  }
4658  else {
4659  c = _flsbuf(c, stream);
4660  catch_interrupt();
4661  }
4662  return c;
4663 }
4664 
4666  /* output field */
4667  void* stackaddr;
4668  int errnum;
4669 
4670  /* input field */
4673  int argc;
4675 };
4676 
4677 static DWORD WINAPI
4679 {
4680  DWORD ret;
4681  struct asynchronous_arg_t *arg = argp;
4682  arg->stackaddr = &argp;
4683  ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
4684  arg->errnum = errno;
4685  return ret;
4686 }
4687 
4688 uintptr_t
4690  int argc, uintptr_t* argv, uintptr_t intrval)
4691 {
4692  DWORD val;
4693  BOOL interrupted = FALSE;
4694  HANDLE thr;
4695 
4696  RUBY_CRITICAL({
4697  struct asynchronous_arg_t arg;
4698 
4699  arg.stackaddr = NULL;
4700  arg.errnum = 0;
4701  arg.func = func;
4702  arg.self = self;
4703  arg.argc = argc;
4704  arg.argv = argv;
4705 
4706  thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
4707 
4708  if (thr) {
4709  yield_until(arg.stackaddr);
4710 
4711  if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
4712  interrupted = TRUE;
4713 
4714  if (TerminateThread(thr, intrval)) {
4715  yield_once();
4716  }
4717  }
4718 
4719  GetExitCodeThread(thr, &val);
4720  CloseHandle(thr);
4721 
4722  if (interrupted) {
4723  /* must release stack of killed thread, why doesn't Windows? */
4724  MEMORY_BASIC_INFORMATION m;
4725 
4726  memset(&m, 0, sizeof(m));
4727  if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
4728  Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
4729  arg.stackaddr, GetLastError()));
4730  }
4731  else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
4732  Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
4733  m.AllocationBase, GetLastError()));
4734  }
4735  errno = EINTR;
4736  }
4737  else {
4738  errno = arg.errnum;
4739  }
4740  }
4741  });
4742 
4743  if (!thr) {
4744  rb_fatal("failed to launch waiter thread:%ld", GetLastError());
4745  }
4746 
4747  return val;
4748 }
4749 
4750 char **
4752 {
4753  char *envtop, *env;
4754  char **myenvtop, **myenv;
4755  int num;
4756 
4757  /*
4758  * We avoid values started with `='. If you want to deal those values,
4759  * change this function, and some functions in hash.c which recognize
4760  * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
4761  * CygWin deals these values by changing first `=' to '!'. But we don't
4762  * use such trick and follow cmd.exe's way that just doesn't show these
4763  * values.
4764  * (U.N. 2001-11-15)
4765  */
4766  envtop = GetEnvironmentStrings();
4767  for (env = envtop, num = 0; *env; env += strlen(env) + 1)
4768  if (*env != '=') num++;
4769 
4770  myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
4771  for (env = envtop, myenv = myenvtop; *env; env += strlen(env) + 1) {
4772  if (*env != '=') {
4773  if (!(*myenv = strdup(env))) {
4774  break;
4775  }
4776  myenv++;
4777  }
4778  }
4779  *myenv = NULL;
4780  FreeEnvironmentStrings(envtop);
4781 
4782  return myenvtop;
4783 }
4784 
4785 void
4787 {
4788  char **t = env;
4789 
4790  while (*t) free(*t++);
4791  free(env);
4792 }
4793 
4794 rb_pid_t
4796 {
4797  rb_pid_t pid;
4798 
4799  pid = GetCurrentProcessId();
4800 
4801  (void)IfWin95(pid = -pid, 0);
4802 
4803  return pid;
4804 }
4805 
4806 
4807 rb_pid_t
4809 {
4810  typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
4811  static query_func *pNtQueryInformationProcess = NULL;
4812  rb_pid_t ppid = 0;
4813 
4814  if (!IsWin95() && rb_w32_osver() >= 5) {
4815  if (!pNtQueryInformationProcess)
4816  pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
4817  if (pNtQueryInformationProcess) {
4818  struct {
4819  long ExitStatus;
4820  void* PebBaseAddress;
4821  uintptr_t AffinityMask;
4822  uintptr_t BasePriority;
4823  uintptr_t UniqueProcessId;
4824  uintptr_t ParentProcessId;
4825  } pbi;
4826  ULONG len;
4827  long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
4828  if (!ret) {
4829  ppid = pbi.ParentProcessId;
4830  }
4831  }
4832  }
4833 
4834  return ppid;
4835 }
4836 
4837 int
4838 rb_w32_uopen(const char *file, int oflag, ...)
4839 {
4840  WCHAR *wfile;
4841  int ret;
4842  int pmode;
4843 
4844  va_list arg;
4845  va_start(arg, oflag);
4846  pmode = va_arg(arg, int);
4847  va_end(arg);
4848 
4849  if (!(wfile = utf8_to_wstr(file, NULL)))
4850  return -1;
4851  ret = rb_w32_wopen(wfile, oflag, pmode);
4852  free(wfile);
4853  return ret;
4854 }
4855 
4856 int
4857 rb_w32_open(const char *file, int oflag, ...)
4858 {
4859  WCHAR *wfile;
4860  int ret;
4861  int pmode;
4862 
4863  va_list arg;
4864  va_start(arg, oflag);
4865  pmode = va_arg(arg, int);
4866  va_end(arg);
4867 
4868  if ((oflag & O_TEXT) || !(oflag & O_BINARY))
4869  return _open(file, oflag, pmode);
4870 
4871  if (!(wfile = filecp_to_wstr(file, NULL)))
4872  return -1;
4873  ret = rb_w32_wopen(wfile, oflag, pmode);
4874  free(wfile);
4875  return ret;
4876 }
4877 
4878 int
4879 rb_w32_wopen(const WCHAR *file, int oflag, ...)
4880 {
4881  char flags = 0;
4882  int fd;
4883  DWORD access;
4884  DWORD create;
4885  DWORD attr = FILE_ATTRIBUTE_NORMAL;
4886  SECURITY_ATTRIBUTES sec;
4887  HANDLE h;
4888 
4889  if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
4890  va_list arg;
4891  int pmode;
4892  va_start(arg, oflag);
4893  pmode = va_arg(arg, int);
4894  va_end(arg);
4895  return _wopen(file, oflag, pmode);
4896  }
4897 
4898  sec.nLength = sizeof(sec);
4899  sec.lpSecurityDescriptor = NULL;
4900  if (oflag & O_NOINHERIT) {
4901  sec.bInheritHandle = FALSE;
4902  flags |= FNOINHERIT;
4903  }
4904  else {
4905  sec.bInheritHandle = TRUE;
4906  }
4907  oflag &= ~O_NOINHERIT;
4908 
4909  /* always open with binary mode */
4910  oflag &= ~(O_BINARY | O_TEXT);
4911 
4912  switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
4913  case O_RDWR:
4914  access = GENERIC_READ | GENERIC_WRITE;
4915  break;
4916  case O_RDONLY:
4917  access = GENERIC_READ;
4918  break;
4919  case O_WRONLY:
4920  access = GENERIC_WRITE;
4921  break;
4922  default:
4923  errno = EINVAL;
4924  return -1;
4925  }
4926  oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
4927 
4928  switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
4929  case O_CREAT:
4930  create = OPEN_ALWAYS;
4931  break;
4932  case 0:
4933  case O_EXCL:
4934  create = OPEN_EXISTING;
4935  break;
4936  case O_CREAT | O_EXCL:
4937  case O_CREAT | O_EXCL | O_TRUNC:
4938  create = CREATE_NEW;
4939  break;
4940  case O_TRUNC:
4941  case O_TRUNC | O_EXCL:
4942  create = TRUNCATE_EXISTING;
4943  break;
4944  case O_CREAT | O_TRUNC:
4945  create = CREATE_ALWAYS;
4946  break;
4947  default:
4948  errno = EINVAL;
4949  return -1;
4950  }
4951  if (oflag & O_CREAT) {
4952  va_list arg;
4953  int pmode;
4954  va_start(arg, oflag);
4955  pmode = va_arg(arg, int);
4956  va_end(arg);
4957  /* TODO: we need to check umask here, but it's not exported... */
4958  if (!(pmode & S_IWRITE))
4959  attr = FILE_ATTRIBUTE_READONLY;
4960  }
4961  oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
4962 
4963  if (oflag & O_TEMPORARY) {
4964  attr |= FILE_FLAG_DELETE_ON_CLOSE;
4965  access |= DELETE;
4966  }
4967  oflag &= ~O_TEMPORARY;
4968 
4969  if (oflag & _O_SHORT_LIVED)
4970  attr |= FILE_ATTRIBUTE_TEMPORARY;
4971  oflag &= ~_O_SHORT_LIVED;
4972 
4973  switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
4974  case 0:
4975  break;
4976  case O_SEQUENTIAL:
4977  attr |= FILE_FLAG_SEQUENTIAL_SCAN;
4978  break;
4979  case O_RANDOM:
4980  attr |= FILE_FLAG_RANDOM_ACCESS;
4981  break;
4982  default:
4983  errno = EINVAL;
4984  return -1;
4985  }
4986  oflag &= ~(O_SEQUENTIAL | O_RANDOM);
4987 
4988  if (oflag & ~O_APPEND) {
4989  errno = EINVAL;
4990  return -1;
4991  }
4992 
4993  /* allocate a C Runtime file handle */
4994  RUBY_CRITICAL({
4995  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
4996  fd = _open_osfhandle((intptr_t)h, 0);
4997  CloseHandle(h);
4998  });
4999  if (fd == -1) {
5000  errno = EMFILE;
5001  return -1;
5002  }
5003  RUBY_CRITICAL({
5004  MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
5005  _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
5006  _set_osflags(fd, 0);
5007 
5008  h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec,
5009  create, attr, NULL);
5010  if (h == INVALID_HANDLE_VALUE) {
5011  errno = map_errno(GetLastError());
5012  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5013  fd = -1;
5014  goto quit;
5015  }
5016 
5017  switch (GetFileType(h)) {
5018  case FILE_TYPE_CHAR:
5019  flags |= FDEV;
5020  break;
5021  case FILE_TYPE_PIPE:
5022  flags |= FPIPE;
5023  break;
5024  case FILE_TYPE_UNKNOWN:
5025  errno = map_errno(GetLastError());
5026  CloseHandle(h);
5027  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5028  fd = -1;
5029  goto quit;
5030  }
5031  if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
5032  flags |= FAPPEND;
5033 
5034  _set_osfhnd(fd, (intptr_t)h);
5035  _osfile(fd) = flags | FOPEN;
5036 
5037  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5038  quit:
5039  ;
5040  });
5041 
5042  return fd;
5043 }
5044 
5045 int
5047 {
5048  int fd = fileno(fp);
5049  SOCKET sock = TO_SOCKET(fd);
5050  int save_errno = errno;
5051 
5052  if (fflush(fp)) return -1;
5053  if (!is_socket(sock)) {
5054  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
5055  return fclose(fp);
5056  }
5057  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
5058  fclose(fp);
5059  errno = save_errno;
5060  if (closesocket(sock) == SOCKET_ERROR) {
5061  errno = map_errno(WSAGetLastError());
5062  return -1;
5063  }
5064  return 0;
5065 }
5066 
5067 int
5068 rb_w32_pipe(int fds[2])
5069 {
5070  static DWORD serial = 0;
5071  char name[] = "\\\\.\\pipe\\ruby0000000000000000-0000000000000000";
5072  char *p;
5073  SECURITY_ATTRIBUTES sec;
5074  HANDLE hRead, hWrite, h;
5075  int fdRead, fdWrite;
5076  int ret;
5077 
5078  /* if doesn't have CancelIo, use default pipe function */
5079  if (!cancel_io)
5080  return _pipe(fds, 65536L, _O_NOINHERIT);
5081 
5082  p = strchr(name, '0');
5083  snprintf(p, strlen(p) + 1, "%"PRI_PIDT_PREFIX"x-%lx", rb_w32_getpid(), serial++);
5084 
5085  sec.nLength = sizeof(sec);
5086  sec.lpSecurityDescriptor = NULL;
5087  sec.bInheritHandle = FALSE;
5088 
5089  RUBY_CRITICAL({
5090  hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
5091  0, 2, 65536, 65536, 0, &sec);
5092  });
5093  if (hRead == INVALID_HANDLE_VALUE) {
5094  DWORD err = GetLastError();
5095  if (err == ERROR_PIPE_BUSY)
5096  errno = EMFILE;
5097  else
5098  errno = map_errno(GetLastError());
5099  return -1;
5100  }
5101 
5102  RUBY_CRITICAL({
5103  hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
5104  OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
5105  });
5106  if (hWrite == INVALID_HANDLE_VALUE) {
5107  errno = map_errno(GetLastError());
5108  CloseHandle(hRead);
5109  return -1;
5110  }
5111 
5112  RUBY_CRITICAL(do {
5113  ret = 0;
5114  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5115  fdRead = _open_osfhandle((intptr_t)h, 0);
5116  CloseHandle(h);
5117  if (fdRead == -1) {
5118  errno = EMFILE;
5119  CloseHandle(hWrite);
5120  CloseHandle(hRead);
5121  ret = -1;
5122  break;
5123  }
5124 
5125  MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdRead)->lock)));
5126  _set_osfhnd(fdRead, (intptr_t)hRead);
5127  _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
5128  MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdRead)->lock)));
5129  } while (0));
5130  if (ret)
5131  return ret;
5132 
5133  RUBY_CRITICAL(do {
5134  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5135  fdWrite = _open_osfhandle((intptr_t)h, 0);
5136  CloseHandle(h);
5137  if (fdWrite == -1) {
5138  errno = EMFILE;
5139  CloseHandle(hWrite);
5140  ret = -1;
5141  break;
5142  }
5143  MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdWrite)->lock)));
5144  _set_osfhnd(fdWrite, (intptr_t)hWrite);
5145  _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
5146  MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdWrite)->lock)));
5147  } while (0));
5148  if (ret) {
5149  rb_w32_close(fdRead);
5150  return ret;
5151  }
5152 
5153  fds[0] = fdRead;
5154  fds[1] = fdWrite;
5155 
5156  return 0;
5157 }
5158 
5159 int
5161 {
5162  SOCKET sock = TO_SOCKET(fd);
5163  int save_errno = errno;
5164  st_data_t key;
5165 
5166  if (!is_socket(sock)) {
5167  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
5168  return _close(fd);
5169  }
5170  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
5171  key = (st_data_t)sock;
5172  st_delete(socklist, &key, NULL);
5173  sock = (SOCKET)key;
5174  _close(fd);
5175  errno = save_errno;
5176  if (closesocket(sock) == SOCKET_ERROR) {
5177  errno = map_errno(WSAGetLastError());
5178  return -1;
5179  }
5180  return 0;
5181 }
5182 
5183 static int
5184 setup_overlapped(OVERLAPPED *ol, int fd)
5185 {
5186  memset(ol, 0, sizeof(*ol));
5187  if (!(_osfile(fd) & (FDEV | FPIPE))) {
5188  LONG high = 0;
5189  DWORD method = _osfile(fd) & FAPPEND ? FILE_END : FILE_CURRENT;
5190  DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method);
5191 #ifndef INVALID_SET_FILE_POINTER
5192 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
5193 #endif
5194  if (low == INVALID_SET_FILE_POINTER) {
5195  DWORD err = GetLastError();
5196  if (err != NO_ERROR) {
5197  errno = map_errno(err);
5198  return -1;
5199  }
5200  }
5201  ol->Offset = low;
5202  ol->OffsetHigh = high;
5203  }
5204  ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
5205  if (!ol->hEvent) {
5206  errno = map_errno(GetLastError());
5207  return -1;
5208  }
5209  return 0;
5210 }
5211 
5212 static void
5213 finish_overlapped(OVERLAPPED *ol, int fd, DWORD size)
5214 {
5215  CloseHandle(ol->hEvent);
5216 
5217  if (!(_osfile(fd) & (FDEV | FPIPE))) {
5218  LONG high = ol->OffsetHigh;
5219  DWORD low = ol->Offset + size;
5220  if (low < ol->Offset)
5221  ++high;
5222  SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
5223  }
5224 }
5225 
5226 #undef read
5227 ssize_t
5228 rb_w32_read(int fd, void *buf, size_t size)
5229 {
5230  SOCKET sock = TO_SOCKET(fd);
5231  DWORD read;
5232  DWORD wait;
5233  DWORD err;
5234  size_t len;
5235  size_t ret;
5236  OVERLAPPED ol, *pol = NULL;
5237  BOOL isconsole;
5238  BOOL islineinput = FALSE;
5239  int start = 0;
5240 
5241  if (is_socket(sock))
5242  return rb_w32_recv(fd, buf, size, 0);
5243 
5244  // validate fd by using _get_osfhandle() because we cannot access _nhandle
5245  if (_get_osfhandle(fd) == -1) {
5246  return -1;
5247  }
5248 
5249  if (_osfile(fd) & FTEXT) {
5250  return _read(fd, buf, size);
5251  }
5252 
5253  MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
5254 
5255  if (!size || _osfile(fd) & FEOFLAG) {
5256  _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
5257  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5258  return 0;
5259  }
5260 
5261  ret = 0;
5262  isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
5263  if (isconsole) {
5264  DWORD mode;
5265  GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
5266  islineinput = (mode & ENABLE_LINE_INPUT) != 0;
5267  }
5268  retry:
5269  /* get rid of console reading bug */
5270  if (isconsole) {
5271  if (start)
5272  len = 1;
5273  else {
5274  len = 0;
5275  start = 1;
5276  }
5277  }
5278  else
5279  len = size;
5280  size -= len;
5281 
5282  /* if have cancel_io, use Overlapped I/O */
5283  if (cancel_io) {
5284  if (setup_overlapped(&ol, fd)) {
5285  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5286  return -1;
5287  }
5288 
5289  pol = &ol;
5290  }
5291 
5292  if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, pol)) {
5293  err = GetLastError();
5294  if (err != ERROR_IO_PENDING) {
5295  if (pol) CloseHandle(ol.hEvent);
5296  if (err == ERROR_ACCESS_DENIED)
5297  errno = EBADF;
5298  else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
5299  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5300  return 0;
5301  }
5302  else
5303  errno = map_errno(err);
5304 
5305  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5306  return -1;
5307  }
5308 
5309  if (pol) {
5310  wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
5311  if (wait != WAIT_OBJECT_0) {
5312  if (wait == WAIT_OBJECT_0 + 1)
5313  errno = EINTR;
5314  else
5315  errno = map_errno(GetLastError());
5316  CloseHandle(ol.hEvent);
5317  cancel_io((HANDLE)_osfhnd(fd));
5318  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5319  return -1;
5320  }
5321 
5322  if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
5323  (err = GetLastError()) != ERROR_HANDLE_EOF) {
5324  int ret = 0;
5325  if (err != ERROR_BROKEN_PIPE) {
5326  errno = map_errno(err);
5327  ret = -1;
5328  }
5329  CloseHandle(ol.hEvent);
5330  cancel_io((HANDLE)_osfhnd(fd));
5331  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5332  return ret;
5333  }
5334  }
5335  }
5336 
5337  if (pol) {
5338  finish_overlapped(&ol, fd, read);
5339  }
5340 
5341  ret += read;
5342  if (read >= len) {
5343  buf = (char *)buf + read;
5344  if (!(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
5345  goto retry;
5346  }
5347  if (read == 0)
5348  _set_osflags(fd, _osfile(fd) | FEOFLAG);
5349 
5350 
5351  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5352 
5353  return ret;
5354 }
5355 
5356 #undef write
5357 ssize_t
5358 rb_w32_write(int fd, const void *buf, size_t size)
5359 {
5360  SOCKET sock = TO_SOCKET(fd);
5361  DWORD written;
5362  DWORD wait;
5363  DWORD err;
5364  size_t len;
5365  size_t ret;
5366  OVERLAPPED ol, *pol = NULL;
5367 
5368  if (is_socket(sock))
5369  return rb_w32_send(fd, buf, size, 0);
5370 
5371  // validate fd by using _get_osfhandle() because we cannot access _nhandle
5372  if (_get_osfhandle(fd) == -1) {
5373  return -1;
5374  }
5375 
5376  if ((_osfile(fd) & FTEXT) &&
5377  (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
5378  return _write(fd, buf, size);
5379  }
5380 
5381  MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
5382 
5383  if (!size || _osfile(fd) & FEOFLAG) {
5384  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5385  return 0;
5386  }
5387 
5388  ret = 0;
5389  retry:
5390  /* get rid of console writing bug */
5391  len = (_osfile(fd) & FDEV) ? min(32 * 1024, size) : size;
5392  size -= len;
5393 
5394  /* if have cancel_io, use Overlapped I/O */
5395  if (cancel_io) {
5396  if (setup_overlapped(&ol, fd)) {
5397  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5398  return -1;
5399  }
5400 
5401  pol = &ol;
5402  }
5403 
5404  if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, pol)) {
5405  err = GetLastError();
5406  if (err != ERROR_IO_PENDING) {
5407  if (pol) CloseHandle(ol.hEvent);
5408  if (err == ERROR_ACCESS_DENIED)
5409  errno = EBADF;
5410  else
5411  errno = map_errno(err);
5412 
5413  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5414  return -1;
5415  }
5416 
5417  if (pol) {
5418  wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
5419  if (wait != WAIT_OBJECT_0) {
5420  if (wait == WAIT_OBJECT_0 + 1)
5421  errno = EINTR;
5422  else
5423  errno = map_errno(GetLastError());
5424  CloseHandle(ol.hEvent);
5425  cancel_io((HANDLE)_osfhnd(fd));
5426  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5427  return -1;
5428  }
5429 
5430  if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written,
5431  TRUE)) {
5432  errno = map_errno(err);
5433  CloseHandle(ol.hEvent);
5434  cancel_io((HANDLE)_osfhnd(fd));
5435  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5436  return -1;
5437  }
5438  }
5439  }
5440 
5441  if (pol) {
5442  finish_overlapped(&ol, fd, written);
5443  }
5444 
5445  ret += written;
5446  if (written == len) {
5447  buf = (const char *)buf + len;
5448  if (size > 0)
5449  goto retry;
5450  }
5451 
5452  MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5453 
5454  return ret;
5455 }
5456 
5457 long
5459 {
5460  static int disable;
5461  HANDLE handle;
5462  DWORD dwMode, reslen;
5463  VALUE str = strarg;
5464 
5465  if (disable) return -1L;
5466  handle = (HANDLE)_osfhnd(fd);
5467  if (!GetConsoleMode(handle, &dwMode) ||
5468  !rb_econv_has_convpath_p(rb_enc_name(rb_enc_get(str)), "UTF-16LE"))
5469  return -1L;
5470 
5471  str = rb_str_encode(str, rb_enc_from_encoding(rb_enc_find("UTF-16LE")),
5473  if (!WriteConsoleW(handle, (LPWSTR)RSTRING_PTR(str), RSTRING_LEN(str)/2,
5474  &reslen, NULL)) {
5475  if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
5476  disable = TRUE;
5477  return -1L;
5478  }
5479  return (long)reslen;
5480 }
5481 
5482 static int
5483 unixtime_to_filetime(time_t time, FILETIME *ft)
5484 {
5485  ULARGE_INTEGER tmp;
5486 
5487  tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
5488  ft->dwLowDateTime = tmp.LowPart;
5489  ft->dwHighDateTime = tmp.HighPart;
5490  return 0;
5491 }
5492 
5493 static int
5494 wutime(const WCHAR *path, const struct utimbuf *times)
5495 {
5496  HANDLE hFile;
5497  FILETIME atime, mtime;
5498  struct stati64 stat;
5499  int ret = 0;
5500 
5501  if (wstati64(path, &stat)) {
5502  return -1;
5503  }
5504 
5505  if (times) {
5506  if (unixtime_to_filetime(times->actime, &atime)) {
5507  return -1;
5508  }
5509  if (unixtime_to_filetime(times->modtime, &mtime)) {
5510  return -1;
5511  }
5512  }
5513  else {
5514  GetSystemTimeAsFileTime(&atime);
5515  mtime = atime;
5516  }
5517 
5518  RUBY_CRITICAL({
5519  const DWORD attr = GetFileAttributesW(path);
5520  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
5521  SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
5522  hFile = CreateFileW(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
5523  IsWin95() ? 0 : FILE_FLAG_BACKUP_SEMANTICS, 0);
5524  if (hFile == INVALID_HANDLE_VALUE) {
5525  errno = map_errno(GetLastError());
5526  ret = -1;
5527  }
5528  else {
5529  if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
5530  errno = map_errno(GetLastError());
5531  ret = -1;
5532  }
5533  CloseHandle(hFile);
5534  }
5535  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
5536  SetFileAttributesW(path, attr);
5537  });
5538 
5539  return ret;
5540 }
5541 
5542 int
5543 rb_w32_uutime(const char *path, const struct utimbuf *times)
5544 {
5545  WCHAR *wpath;
5546  int ret;
5547 
5548  if (!(wpath = utf8_to_wstr(path, NULL)))
5549  return -1;
5550  ret = wutime(wpath, times);
5551  free(wpath);
5552  return ret;
5553 }
5554 
5555 int
5556 rb_w32_utime(const char *path, const struct utimbuf *times)
5557 {
5558  WCHAR *wpath;
5559  int ret;
5560 
5561  if (!(wpath = filecp_to_wstr(path, NULL)))
5562  return -1;
5563  ret = wutime(wpath, times);
5564  free(wpath);
5565  return ret;
5566 }
5567 
5568 int
5569 rb_w32_uchdir(const char *path)
5570 {
5571  WCHAR *wpath;
5572  int ret;
5573 
5574  if (!(wpath = utf8_to_wstr(path, NULL)))
5575  return -1;
5576  ret = _wchdir(wpath);
5577  free(wpath);
5578  return ret;
5579 }
5580 
5581 static int
5582 wmkdir(const WCHAR *wpath, int mode)
5583 {
5584  int ret = -1;
5585 
5586  RUBY_CRITICAL(do {
5587  if (CreateDirectoryW(wpath, NULL) == FALSE) {
5588  errno = map_errno(GetLastError());
5589  break;
5590  }
5591  if (_wchmod(wpath, mode) == -1) {
5592  RemoveDirectoryW(wpath);
5593  break;
5594  }
5595  ret = 0;
5596  } while (0));
5597  return ret;
5598 }
5599 
5600 int
5601 rb_w32_umkdir(const char *path, int mode)
5602 {
5603  WCHAR *wpath;
5604  int ret;
5605 
5606  if (!(wpath = utf8_to_wstr(path, NULL)))
5607  return -1;
5608  ret = wmkdir(wpath, mode);
5609  free(wpath);
5610  return ret;
5611 }
5612 
5613 int
5614 rb_w32_mkdir(const char *path, int mode)
5615 {
5616  WCHAR *wpath;
5617  int ret;
5618 
5619  if (!(wpath = filecp_to_wstr(path, NULL)))
5620  return -1;
5621  ret = wmkdir(wpath, mode);
5622  free(wpath);
5623  return ret;
5624 }
5625 
5626 static int
5627 wrmdir(const WCHAR *wpath)
5628 {
5629  int ret = 0;
5630  RUBY_CRITICAL({
5631  const DWORD attr = GetFileAttributesW(wpath);
5632  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
5633  SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
5634  }
5635  if (RemoveDirectoryW(wpath) == FALSE) {
5636  errno = map_errno(GetLastError());
5637  ret = -1;
5638  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
5639  SetFileAttributesW(wpath, attr);
5640  }
5641  }
5642  });
5643  return ret;
5644 }
5645 
5646 int
5647 rb_w32_rmdir(const char *path)
5648 {
5649  WCHAR *wpath;
5650  int ret;
5651 
5652  if (!(wpath = filecp_to_wstr(path, NULL)))
5653  return -1;
5654  ret = wrmdir(wpath);
5655  free(wpath);
5656  return ret;
5657 }
5658 
5659 int
5660 rb_w32_urmdir(const char *path)
5661 {
5662  WCHAR *wpath;
5663  int ret;
5664 
5665  if (!(wpath = utf8_to_wstr(path, NULL)))
5666  return -1;
5667  ret = wrmdir(wpath);
5668  free(wpath);
5669  return ret;
5670 }
5671 
5672 static int
5673 wunlink(const WCHAR *path)
5674 {
5675  int ret = 0;
5676  RUBY_CRITICAL({
5677  const DWORD attr = GetFileAttributesW(path);
5678  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
5679  SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
5680  }
5681  if (!DeleteFileW(path)) {
5682  errno = map_errno(GetLastError());
5683  ret = -1;
5684  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
5685  SetFileAttributesW(path, attr);
5686  }
5687  }
5688  });
5689  return ret;
5690 }
5691 
5692 int
5693 rb_w32_uunlink(const char *path)
5694 {
5695  WCHAR *wpath;
5696  int ret;
5697 
5698  if (!(wpath = utf8_to_wstr(path, NULL)))
5699  return -1;
5700  ret = wunlink(wpath);
5701  free(wpath);
5702  return ret;
5703 }
5704 
5705 int
5706 rb_w32_unlink(const char *path)
5707 {
5708  WCHAR *wpath;
5709  int ret;
5710 
5711  if (!(wpath = filecp_to_wstr(path, NULL)))
5712  return -1;
5713  ret = wunlink(wpath);
5714  free(wpath);
5715  return ret;
5716 }
5717 
5718 int
5719 rb_w32_uchmod(const char *path, int mode)
5720 {
5721  WCHAR *wpath;
5722  int ret;
5723 
5724  if (!(wpath = utf8_to_wstr(path, NULL)))
5725  return -1;
5726  ret = _wchmod(wpath, mode);
5727  free(wpath);
5728  return ret;
5729 }
5730 
5731 #if !defined(__BORLANDC__)
5732 int
5734 {
5735  DWORD mode;
5736 
5737  // validate fd by using _get_osfhandle() because we cannot access _nhandle
5738  if (_get_osfhandle(fd) == -1) {
5739  return 0;
5740  }
5741  if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
5742  errno = ENOTTY;
5743  return 0;
5744  }
5745  return 1;
5746 }
5747 #endif
5748 
5749 //
5750 // Fix bcc32's stdio bug
5751 //
5752 
5753 #ifdef __BORLANDC__
5754 static int
5755 too_many_files(void)
5756 {
5757  FILE *f;
5758  for (f = _streams; f < _streams + _nfile; f++) {
5759  if (f->fd < 0) return 0;
5760  }
5761  return 1;
5762 }
5763 
5764 #undef fopen
5765 FILE *
5766 rb_w32_fopen(const char *path, const char *mode)
5767 {
5768  FILE *f = (errno = 0, fopen(path, mode));
5769  if (f == NULL && errno == 0) {
5770  if (too_many_files())
5771  errno = EMFILE;
5772  }
5773  return f;
5774 }
5775 
5776 FILE *
5777 rb_w32_fdopen(int handle, const char *type)
5778 {
5779  FILE *f = (errno = 0, _fdopen(handle, (char *)type));
5780  if (f == NULL && errno == 0) {
5781  if (handle < 0)
5782  errno = EBADF;
5783  else if (too_many_files())
5784  errno = EMFILE;
5785  }
5786  return f;
5787 }
5788 
5789 FILE *
5790 rb_w32_fsopen(const char *path, const char *mode, int shflags)
5791 {
5792  FILE *f = (errno = 0, _fsopen(path, mode, shflags));
5793  if (f == NULL && errno == 0) {
5794  if (too_many_files())
5795  errno = EMFILE;
5796  }
5797  return f;
5798 }
5799 #endif
5800 
5801 #if defined(_MSC_VER) && RT_VER <= 60
5802 extern long _ftol(double);
5803 long
5804 _ftol2(double d)
5805 {
5806  return _ftol(d);
5807 }
5808 long
5809 _ftol2_sse(double d)
5810 {
5811  return _ftol(d);
5812 }
5813 #endif
5814 
5815 #ifndef signbit
5816 int
5817 signbit(double x)
5818 {
5819  int *ip = (int *)(&x + 1) - 1;
5820  return *ip < 0;
5821 }
5822 #endif
5823 
5824 char * WSAAPI
5825 rb_w32_inet_ntop(int af, void *addr, char *numaddr, size_t numaddr_len)
5826 {
5827  typedef char *(WSAAPI inet_ntop_t)(int, void *, char *, size_t);
5828  inet_ntop_t *pInetNtop;
5829  pInetNtop = (inet_ntop_t *)get_proc_address("ws2_32", "inet_ntop", NULL);
5830  if(pInetNtop){
5831  return pInetNtop(af,addr,numaddr,numaddr_len);
5832  }else{
5833  struct in_addr in;
5834  memcpy(&in.s_addr, addr, sizeof(in.s_addr));
5835  snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
5836  }
5837  return numaddr;
5838 }
5839 
5840 char
5842  return _osfile(fd) & FTEXT;
5843 }
5844