Ruby  3.4.0dev (2024-11-23 revision b4d13fac3dd5420475aa1e14fdad8137da7e3ee0)
win32.c
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  The parts licensed under above copyright notice are marked as "Artistic or
14  GPL".
15  Another parts are licensed under Ruby's License.
16 
17  Copyright (C) 1993-2011 Yukihiro Matsumoto
18  Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
19  Copyright (C) 2000 Information-technology Promotion Agency, Japan
20  */
21 
22 #undef __STRICT_ANSI__
23 
24 #include "ruby/ruby.h"
25 #include "ruby/encoding.h"
26 #include "ruby/io.h"
27 #include "ruby/util.h"
28 #include <fcntl.h>
29 #include <process.h>
30 #include <sys/stat.h>
31 /* #include <sys/wait.h> */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <assert.h>
36 #include <ctype.h>
37 
38 #include <windows.h>
39 #include <winbase.h>
40 #include <wincon.h>
41 #include <share.h>
42 #include <shlobj.h>
43 #include <mbstring.h>
44 #include <shlwapi.h>
45 #if defined _MSC_VER && _MSC_VER >= 1400
46 #include <crtdbg.h>
47 #include <rtcapi.h>
48 #endif
49 #ifdef __MINGW32__
50 #include <mswsock.h>
51 #endif
52 #ifdef HAVE_AFUNIX_H
53 # include <afunix.h>
54 #endif
55 #include "ruby/win32.h"
56 #include "ruby/vm.h"
57 #include "win32/dir.h"
58 #include "win32/file.h"
59 #include "id.h"
60 #include "internal.h"
61 #include "internal/enc.h"
62 #include "internal/object.h"
63 #include "internal/static_assert.h"
64 #include "ruby/internal/stdbool.h"
65 #include "encindex.h"
66 #define isdirsep(x) ((x) == '/' || (x) == '\\')
67 
68 #if defined _MSC_VER && _MSC_VER <= 1200
69 # define CharNextExA(cp, p, flags) CharNextExA((WORD)(cp), (p), (flags))
70 #endif
71 
72 static int w32_wopen(const WCHAR *file, int oflag, int perm);
73 static int w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat);
74 static char *w32_getenv(const char *name, UINT cp);
75 
76 #undef getenv
77 /*
78  * Do not remove the macros to substitute functions in dln_find.c.
79  */
80 #define DLN_FIND_EXTRA_ARG_DECL ,UINT cp
81 #define DLN_FIND_EXTRA_ARG ,cp
82 #define rb_w32_stati128(path, st) w32_stati128(path, st, cp, FALSE)
83 #define getenv(name) w32_getenv(name, cp) /* Necessarily For dln.c */
84 #undef CharNext
85 #define CharNext(p) CharNextExA(cp, (p), 0)
86 #define dln_find_exe_r rb_w32_udln_find_exe_r
87 #define dln_find_file_r rb_w32_udln_find_file_r
88 #include "dln.h"
89 #include "dln_find.c"
90 #undef MAXPATHLEN
91 #undef rb_w32_stati128
92 #undef dln_find_exe_r
93 #undef dln_find_file_r
94 #define dln_find_exe_r(fname, path, buf, size) rb_w32_udln_find_exe_r(fname, path, buf, size, cp)
95 #define dln_find_file_r(fname, path, buf, size) rb_w32_udln_find_file_r(fname, path, buf, size, cp)
96 #undef CharNext /* no default cp version */
97 #undef getenv
98 
99 #ifndef PATH_MAX
100 # if defined MAX_PATH
101 # define PATH_MAX MAX_PATH
102 # elif defined HAVE_SYS_PARAM_H
103 # include <sys/param.h>
104 # define PATH_MAX MAXPATHLEN
105 # endif
106 #endif
107 #define ENV_MAX 512
108 
109 #undef stat
110 #undef fclose
111 #undef close
112 #undef setsockopt
113 #undef dup2
114 #undef strdup
115 
116 #if RUBY_MSVCRT_VERSION >= 140
117 # define _filbuf _fgetc_nolock
118 # define _flsbuf _fputc_nolock
119 #endif
120 #define enough_to_get(n) (--(n) >= 0)
121 #define enough_to_put(n) (--(n) >= 0)
122 
123 #ifdef WIN32_DEBUG
124 #define Debug(something) something
125 #else
126 #define Debug(something) /* nothing */
127 #endif
128 
129 #define TO_SOCKET(x) _get_osfhandle(x)
130 
131 int rb_w32_reparse_symlink_p(const WCHAR *path);
132 
133 static int has_redirection(const char *, UINT);
134 int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
135 static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
136 static int wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat);
137 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
138 int ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc);
139 static FARPROC get_proc_address(const char *module, const char *func, HANDLE *mh);
140 
141 #define RUBY_CRITICAL if (0) {} else /* just remark */
142 
143 /* errno mapping */
144 static const struct {
145  DWORD winerr;
146  int err;
147 } errmap[] = {
148  { ERROR_INVALID_FUNCTION, EINVAL },
149  { ERROR_FILE_NOT_FOUND, ENOENT },
150  { ERROR_PATH_NOT_FOUND, ENOENT },
151  { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
152  { ERROR_ACCESS_DENIED, EACCES },
153  { ERROR_INVALID_HANDLE, EBADF },
154  { ERROR_ARENA_TRASHED, ENOMEM },
155  { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
156  { ERROR_INVALID_BLOCK, ENOMEM },
157  { ERROR_BAD_ENVIRONMENT, E2BIG },
158  { ERROR_BAD_FORMAT, ENOEXEC },
159  { ERROR_INVALID_ACCESS, EINVAL },
160  { ERROR_INVALID_DATA, EINVAL },
161  { ERROR_INVALID_DRIVE, ENOENT },
162  { ERROR_CURRENT_DIRECTORY, EACCES },
163  { ERROR_NOT_SAME_DEVICE, EXDEV },
164  { ERROR_NO_MORE_FILES, ENOENT },
165  { ERROR_WRITE_PROTECT, EROFS },
166  { ERROR_BAD_UNIT, ENODEV },
167  { ERROR_NOT_READY, ENXIO },
168  { ERROR_BAD_COMMAND, EACCES },
169  { ERROR_CRC, EACCES },
170  { ERROR_BAD_LENGTH, EACCES },
171  { ERROR_SEEK, EIO },
172  { ERROR_NOT_DOS_DISK, EACCES },
173  { ERROR_SECTOR_NOT_FOUND, EACCES },
174  { ERROR_OUT_OF_PAPER, EACCES },
175  { ERROR_WRITE_FAULT, EIO },
176  { ERROR_READ_FAULT, EIO },
177  { ERROR_GEN_FAILURE, EACCES },
178  { ERROR_LOCK_VIOLATION, EACCES },
179  { ERROR_SHARING_VIOLATION, EACCES },
180  { ERROR_WRONG_DISK, EACCES },
181  { ERROR_SHARING_BUFFER_EXCEEDED, EACCES },
182  { ERROR_BAD_NETPATH, ENOENT },
183  { ERROR_NETWORK_ACCESS_DENIED, EACCES },
184  { ERROR_BAD_NET_NAME, ENOENT },
185  { ERROR_FILE_EXISTS, EEXIST },
186  { ERROR_CANNOT_MAKE, EACCES },
187  { ERROR_FAIL_I24, EACCES },
188  { ERROR_INVALID_PARAMETER, EINVAL },
189  { ERROR_NO_PROC_SLOTS, EAGAIN },
190  { ERROR_DRIVE_LOCKED, EACCES },
191  { ERROR_BROKEN_PIPE, EPIPE },
192  { ERROR_DISK_FULL, ENOSPC },
193  { ERROR_INVALID_TARGET_HANDLE, EBADF },
194  { ERROR_INVALID_HANDLE, EINVAL },
195  { ERROR_WAIT_NO_CHILDREN, ECHILD },
196  { ERROR_CHILD_NOT_COMPLETE, ECHILD },
197  { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
198  { ERROR_NEGATIVE_SEEK, EINVAL },
199  { ERROR_SEEK_ON_DEVICE, EACCES },
200  { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
201  { ERROR_DIRECTORY, ENOTDIR },
202  { ERROR_NOT_LOCKED, EACCES },
203  { ERROR_BAD_PATHNAME, ENOENT },
204  { ERROR_MAX_THRDS_REACHED, EAGAIN },
205  { ERROR_LOCK_FAILED, EACCES },
206  { ERROR_ALREADY_EXISTS, EEXIST },
207  { ERROR_INVALID_STARTING_CODESEG, ENOEXEC },
208  { ERROR_INVALID_STACKSEG, ENOEXEC },
209  { ERROR_INVALID_MODULETYPE, ENOEXEC },
210  { ERROR_INVALID_EXE_SIGNATURE, ENOEXEC },
211  { ERROR_EXE_MARKED_INVALID, ENOEXEC },
212  { ERROR_BAD_EXE_FORMAT, ENOEXEC },
213  { ERROR_ITERATED_DATA_EXCEEDS_64k,ENOEXEC },
214  { ERROR_INVALID_MINALLOCSIZE, ENOEXEC },
215  { ERROR_DYNLINK_FROM_INVALID_RING,ENOEXEC },
216  { ERROR_IOPL_NOT_ENABLED, ENOEXEC },
217  { ERROR_INVALID_SEGDPL, ENOEXEC },
218  { ERROR_AUTODATASEG_EXCEEDS_64k, ENOEXEC },
219  { ERROR_RING2SEG_MUST_BE_MOVABLE, ENOEXEC },
220  { ERROR_RELOC_CHAIN_XEEDS_SEGLIM, ENOEXEC },
221  { ERROR_INFLOOP_IN_RELOC_CHAIN, ENOEXEC },
222  { ERROR_FILENAME_EXCED_RANGE, ENOENT },
223  { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
224 #ifndef ERROR_PIPE_LOCAL
225 #define ERROR_PIPE_LOCAL 229L
226 #endif
227  { ERROR_PIPE_LOCAL, EPIPE },
228  { ERROR_BAD_PIPE, EPIPE },
229  { ERROR_PIPE_BUSY, EAGAIN },
230  { ERROR_NO_DATA, EPIPE },
231  { ERROR_PIPE_NOT_CONNECTED, EPIPE },
232  { ERROR_OPERATION_ABORTED, EINTR },
233  { ERROR_NOT_ENOUGH_QUOTA, ENOMEM },
234  { ERROR_MOD_NOT_FOUND, ENOENT },
235  { ERROR_PRIVILEGE_NOT_HELD, EACCES, },
236  { ERROR_CANT_RESOLVE_FILENAME, ELOOP, },
237  { WSAEINTR, EINTR },
238  { WSAEBADF, EBADF },
239  { WSAEACCES, EACCES },
240  { WSAEFAULT, EFAULT },
241  { WSAEINVAL, EINVAL },
242  { WSAEMFILE, EMFILE },
243  { WSAEWOULDBLOCK, EWOULDBLOCK },
244  { WSAEINPROGRESS, EINPROGRESS },
245  { WSAEALREADY, EALREADY },
246  { WSAENOTSOCK, ENOTSOCK },
247  { WSAEDESTADDRREQ, EDESTADDRREQ },
248  { WSAEMSGSIZE, EMSGSIZE },
249  { WSAEPROTOTYPE, EPROTOTYPE },
250  { WSAENOPROTOOPT, ENOPROTOOPT },
251  { WSAEPROTONOSUPPORT, EPROTONOSUPPORT },
252  { WSAESOCKTNOSUPPORT, ESOCKTNOSUPPORT },
253  { WSAEOPNOTSUPP, EOPNOTSUPP },
254  { WSAEPFNOSUPPORT, EPFNOSUPPORT },
255  { WSAEAFNOSUPPORT, EAFNOSUPPORT },
256  { WSAEADDRINUSE, EADDRINUSE },
257  { WSAEADDRNOTAVAIL, EADDRNOTAVAIL },
258  { WSAENETDOWN, ENETDOWN },
259  { WSAENETUNREACH, ENETUNREACH },
260  { WSAENETRESET, ENETRESET },
261  { WSAECONNABORTED, ECONNABORTED },
262  { WSAECONNRESET, ECONNRESET },
263  { WSAENOBUFS, ENOBUFS },
264  { WSAEISCONN, EISCONN },
265  { WSAENOTCONN, ENOTCONN },
266  { WSAESHUTDOWN, ESHUTDOWN },
267  { WSAETOOMANYREFS, ETOOMANYREFS },
268  { WSAETIMEDOUT, ETIMEDOUT },
269  { WSAECONNREFUSED, ECONNREFUSED },
270  { WSAELOOP, ELOOP },
271  { WSAENAMETOOLONG, ENAMETOOLONG },
272  { WSAEHOSTDOWN, EHOSTDOWN },
273  { WSAEHOSTUNREACH, EHOSTUNREACH },
274  { WSAEPROCLIM, EPROCLIM },
275  { WSAENOTEMPTY, ENOTEMPTY },
276  { WSAEUSERS, EUSERS },
277  { WSAEDQUOT, EDQUOT },
278  { WSAESTALE, ESTALE },
279  { WSAEREMOTE, EREMOTE },
280 };
281 
282 /* License: Ruby's */
283 int
284 rb_w32_map_errno(DWORD winerr)
285 {
286  int i;
287 
288  if (winerr == 0) {
289  return 0;
290  }
291 
292  for (i = 0; i < (int)(sizeof(errmap) / sizeof(*errmap)); i++) {
293  if (errmap[i].winerr == winerr) {
294  return errmap[i].err;
295  }
296  }
297 
298  if (winerr >= WSABASEERR) {
299  return winerr;
300  }
301  return EINVAL;
302 }
303 
304 #define map_errno rb_w32_map_errno
305 
306 static const char *NTLoginName;
307 
308 static OSVERSIONINFO osver;
309 
310 /* License: Artistic or GPL */
311 static void
312 get_version(void)
313 {
314  memset(&osver, 0, sizeof(OSVERSIONINFO));
315  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
316  GetVersionEx(&osver);
317 }
318 
319 #ifdef _M_IX86
320 /* License: Artistic or GPL */
321 DWORD
322 rb_w32_osid(void)
323 {
324  return osver.dwPlatformId;
325 }
326 #endif
327 
328 /* License: Artistic or GPL */
329 DWORD
330 rb_w32_osver(void)
331 {
332  return osver.dwMajorVersion;
333 }
334 
335 /* simulate flock by locking a range on the file */
336 
337 /* License: Artistic or GPL */
338 #define LK_ERR(f,i) \
339  do { \
340  if (f) \
341  i = 0; \
342  else { \
343  DWORD err = GetLastError(); \
344  if (err == ERROR_LOCK_VIOLATION || err == ERROR_IO_PENDING) \
345  errno = EWOULDBLOCK; \
346  else if (err == ERROR_NOT_LOCKED) \
347  i = 0; \
348  else \
349  errno = map_errno(err); \
350  } \
351  } while (0)
352 #define LK_LEN ULONG_MAX
353 
354 /* License: Artistic or GPL */
355 static uintptr_t
356 flock_winnt(uintptr_t self, int argc, uintptr_t* argv)
357 {
358  OVERLAPPED o;
359  int i = -1;
360  const HANDLE fh = (HANDLE)self;
361  const int oper = argc;
362 
363  memset(&o, 0, sizeof(o));
364 
365  switch (oper) {
366  case LOCK_SH: /* shared lock */
367  LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
368  break;
369  case LOCK_EX: /* exclusive lock */
370  LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
371  break;
372  case LOCK_SH|LOCK_NB: /* non-blocking shared lock */
373  LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
374  break;
375  case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */
376  LK_ERR(LockFileEx(fh,
377  LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
378  0, LK_LEN, LK_LEN, &o), i);
379  break;
380  case LOCK_UN: /* unlock lock */
381  case LOCK_UN|LOCK_NB: /* unlock is always non-blocking, I hope */
382  LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
383  break;
384  default: /* unknown */
385  errno = EINVAL;
386  break;
387  }
388  return i;
389 }
390 
391 #undef LK_ERR
392 
393 /* License: Artistic or GPL */
394 int
395 flock(int fd, int oper)
396 {
397  const asynchronous_func_t locker = flock_winnt;
398 
399  return rb_w32_asynchronize(locker,
400  (VALUE)_get_osfhandle(fd), oper, NULL,
401  (DWORD)-1);
402 }
403 
404 /* License: Ruby's */
405 static inline WCHAR *
406 translate_wchar(WCHAR *p, int from, int to)
407 {
408  for (; *p; p++) {
409  if (*p == from)
410  *p = to;
411  }
412  return p;
413 }
414 
415 /* License: Ruby's */
416 static inline char *
417 translate_char(char *p, int from, int to, UINT cp)
418 {
419  while (*p) {
420  if ((unsigned char)*p == from)
421  *p = to;
422  p = CharNextExA(cp, p, 0);
423  }
424  return p;
425 }
426 
427 #ifndef CSIDL_LOCAL_APPDATA
428 #define CSIDL_LOCAL_APPDATA 28
429 #endif
430 #ifndef CSIDL_COMMON_APPDATA
431 #define CSIDL_COMMON_APPDATA 35
432 #endif
433 #ifndef CSIDL_WINDOWS
434 #define CSIDL_WINDOWS 36
435 #endif
436 #ifndef CSIDL_SYSTEM
437 #define CSIDL_SYSTEM 37
438 #endif
439 #ifndef CSIDL_PROFILE
440 #define CSIDL_PROFILE 40
441 #endif
442 
443 /* License: Ruby's */
444 static BOOL
445 get_special_folder(int n, WCHAR *buf, size_t len)
446 {
447  LPITEMIDLIST pidl;
448  LPMALLOC alloc;
449  BOOL f = FALSE;
450 
451  if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
452  f = SHGetPathFromIDListEx(pidl, buf, len, 0);
453  SHGetMalloc(&alloc);
454  alloc->lpVtbl->Free(alloc, pidl);
455  alloc->lpVtbl->Release(alloc);
456  }
457  return f;
458 }
459 
460 /* License: Ruby's */
461 static void
462 regulate_path(WCHAR *path)
463 {
464  WCHAR *p = translate_wchar(path, L'\\', L'/');
465  if (p - path == 2 && path[1] == L':') {
466  *p++ = L'/';
467  *p = L'\0';
468  }
469 }
470 
471 /* License: Ruby's */
472 static FARPROC
473 get_proc_address(const char *module, const char *func, HANDLE *mh)
474 {
475  HANDLE h;
476  FARPROC ptr;
477 
478  if (mh)
479  h = LoadLibrary(module);
480  else
481  h = GetModuleHandle(module);
482  if (!h)
483  return NULL;
484 
485  ptr = GetProcAddress(h, func);
486  if (mh) {
487  if (ptr)
488  *mh = h;
489  else
490  FreeLibrary(h);
491  }
492  return ptr;
493 }
494 
495 /* License: Ruby's */
496 VALUE
497 rb_w32_special_folder(int type)
498 {
499  WCHAR path[PATH_MAX];
500 
501  if (!get_special_folder(type, path, numberof(path))) return Qnil;
502  regulate_path(path);
503  return rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
504 }
505 
506 #if defined _MSC_VER && _MSC_VER <= 1200
507 /* License: Ruby's */
508 #define GetSystemWindowsDirectoryW GetWindowsDirectoryW
509 #endif
510 
511 /* License: Ruby's */
512 UINT
513 rb_w32_system_tmpdir(WCHAR *path, UINT len)
514 {
515  static const WCHAR temp[] = L"temp";
516  WCHAR *p;
517 
518  if (!get_special_folder(CSIDL_LOCAL_APPDATA, path, len)) {
519  if (GetSystemWindowsDirectoryW(path, len)) return 0;
520  }
521  p = translate_wchar(path, L'\\', L'/');
522  if (*(p - 1) != L'/') *p++ = L'/';
523  if ((UINT)(p - path + numberof(temp)) >= len) return 0;
524  memcpy(p, temp, sizeof(temp));
525  return (UINT)(p - path + numberof(temp) - 1);
526 }
527 
528 /*
529  Return user's home directory using environment variables combinations.
530  Memory allocated by this function should be manually freed
531  afterwards with xfree.
532 
533  Try:
534  HOME, USERPROFILE, HOMEDRIVE + HOMEPATH environment variables
535  Special Folders - Profile and Personal
536 */
537 WCHAR *
538 rb_w32_home_dir(void)
539 {
540  WCHAR *buffer = NULL;
541  size_t buffer_len = MAX_PATH, len = 0;
542  enum {
543  HOME_NONE, ENV_HOME, ENV_USERPROFILE, ENV_DRIVEPATH
544  } home_type = HOME_NONE;
545 
546  if ((len = GetEnvironmentVariableW(L"HOME", NULL, 0)) != 0) {
547  buffer_len = len;
548  home_type = ENV_HOME;
549  }
550  else if ((len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) != 0) {
551  buffer_len = len;
552  home_type = ENV_USERPROFILE;
553  }
554  else if ((len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) != 0) {
555  buffer_len = len;
556  if ((len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) != 0) {
557  buffer_len += len;
558  home_type = ENV_DRIVEPATH;
559  }
560  }
561 
562  /* can't use xmalloc here, since it's called too early from init_env() */
563  buffer = malloc(sizeof(WCHAR) * buffer_len);
564  if (buffer == NULL) return NULL;
565 
566  switch (home_type) {
567  case ENV_HOME:
568  GetEnvironmentVariableW(L"HOME", buffer, buffer_len);
569  break;
570  case ENV_USERPROFILE:
571  GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len);
572  break;
573  case ENV_DRIVEPATH:
574  len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len);
575  GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len);
576  break;
577  default:
578  if (!get_special_folder(CSIDL_PROFILE, buffer, buffer_len) &&
579  !get_special_folder(CSIDL_PERSONAL, buffer, buffer_len)) {
580  free(buffer);
581  return NULL;
582  }
583  buffer = realloc(buffer, sizeof(WCHAR) * (lstrlenW(buffer) + 1));
584  break;
585  }
586 
587  /* sanitize backslashes with forwardslashes */
588  regulate_path(buffer);
589 
590  return buffer;
591 }
592 
593 /* License: Ruby's */
594 static void
595 init_env(void)
596 {
597  WCHAR env[ENV_MAX];
598 
599  if (!GetEnvironmentVariableW(L"HOME", env, numberof(env))) {
600  WCHAR *whome = rb_w32_home_dir();
601  if (whome) {
602  _wputenv_s(L"HOME", whome);
603  free(whome);
604  }
605  }
606 
607  if (!GetEnvironmentVariableW(L"USER", env, numberof(env))) {
608  DWORD len;
609  if (!GetEnvironmentVariableW(L"USERNAME", env, numberof(env)) &&
610  !GetUserNameW(env, (len = numberof(env), &len))) {
611  NTLoginName = "<Unknown>";
612  }
613  else {
614  _wputenv_s(L"USER", env);
615  NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
616  }
617  }
618  else {
619  NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
620  }
621 
622  if (!GetEnvironmentVariableW(L"TMPDIR", env, numberof(env)) &&
623  !GetEnvironmentVariableW(L"TMP", env, numberof(env)) &&
624  !GetEnvironmentVariableW(L"TEMP", env, numberof(env)) &&
625  rb_w32_system_tmpdir(env, numberof(env))) {
626  _wputenv_s(L"TMPDIR", env);
627  }
628 }
629 
630 static void init_stdhandle(void);
631 
632 #if RUBY_MSVCRT_VERSION >= 80
633 /* License: Ruby's */
634 static void
635 invalid_parameter(const wchar_t *expr, const wchar_t *func, const wchar_t *file, unsigned int line, uintptr_t dummy)
636 {
637  // nothing to do
638 }
639 
640 int ruby_w32_rtc_error;
641 
642 # ifndef __MINGW32__
643 /* License: Ruby's */
645 RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6)
646 static int __cdecl
647 rtc_error_handler(int e, const char *src, int line, const char *exe, const char *fmt, ...)
648 {
649  va_list ap;
650  VALUE str;
651 
652  if (!ruby_w32_rtc_error) return 0;
653  str = rb_sprintf("%s:%d: ", src, line);
654  va_start(ap, fmt);
655  rb_str_vcatf(str, fmt, ap);
656  va_end(ap);
657  rb_str_cat(str, "\n", 1);
659  return 0;
660 }
661 # endif
662 #endif
663 
664 static CRITICAL_SECTION select_mutex;
665 
666 static CRITICAL_SECTION socklist_mutex;
667 static st_table *socklist = NULL;
668 
669 static CRITICAL_SECTION conlist_mutex;
670 static st_table *conlist = NULL;
671 #define conlist_disabled ((st_table *)-1)
672 
673 #define thread_exclusive(obj) \
674  for (bool exclusive_for_##obj = (EnterCriticalSection(&obj##_mutex), true); \
675  exclusive_for_##obj; \
676  exclusive_for_##obj = (LeaveCriticalSection(&obj##_mutex), false))
677 
678 static CRITICAL_SECTION uenvarea_mutex;
679 static char *uenvarea;
680 
681 /* License: Ruby's */
682 struct constat {
683  struct {
684  int state, seq[16], reverse;
685  WORD attr;
686  COORD saved;
687  } vt100;
688 };
689 enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
690 
691 /* License: Ruby's */
692 static int
693 free_conlist(st_data_t key, st_data_t val, st_data_t arg)
694 {
695  xfree((struct constat *)val);
696  return ST_DELETE;
697 }
698 
699 /* License: Ruby's */
700 static void
701 constat_delete(HANDLE h)
702 {
703  thread_exclusive(conlist) {
704  if (conlist && conlist != conlist_disabled) {
705  st_data_t key = (st_data_t)h, val;
706  st_delete(conlist, &key, &val);
707  xfree((struct constat *)val);
708  }
709  }
710 }
711 
712 /* License: Ruby's */
713 static void
714 exit_handler(void)
715 {
716  WSACleanup();
717  DeleteCriticalSection(&select_mutex);
718  DeleteCriticalSection(&socklist_mutex);
719  DeleteCriticalSection(&conlist_mutex);
720  thread_exclusive(uenvarea) {
721  if (uenvarea) {
722  free(uenvarea);
723  uenvarea = NULL;
724  }
725  }
726  DeleteCriticalSection(&uenvarea_mutex);
727 }
728 
729 /* License: Ruby's */
730 static void
731 vm_exit_handler(ruby_vm_t *vm)
732 {
733  EnterCriticalSection(&socklist_mutex);
734  if (socklist) {
735  st_free_table(socklist);
736  socklist = NULL;
737  }
738  LeaveCriticalSection(&socklist_mutex);
739 
740  EnterCriticalSection(&conlist_mutex);
741  if (conlist && conlist != conlist_disabled) {
742  st_foreach(conlist, free_conlist, 0);
743  st_free_table(conlist);
744  conlist = NULL;
745  }
746  LeaveCriticalSection(&conlist_mutex);
747 }
748 
749 #define ATOMIC_LONG_CAS(var, oldval, newval) InterlockedCompareExchange(&(var), (newval), (oldval))
750 
751 /* License: Ruby's */
752 static void
753 install_vm_exit_handler(void)
754 {
755  static LONG installed = 0;
756  LONG i;
757 
758  while ((i = ATOMIC_LONG_CAS(installed, 0, -1)) != 1) {
759  if (i != 0) {
760  Sleep(1);
761  continue;
762  }
763  ruby_vm_at_exit(vm_exit_handler);
764  ATOMIC_LONG_CAS(installed, -1, 1);
765  break;
766  }
767 }
768 
769 /* License: Artistic or GPL */
770 static void
771 StartSockets(void)
772 {
773  WORD version;
774  WSADATA retdata;
775 
776  //
777  // initialize the winsock interface and insure that it's
778  // cleaned up at exit.
779  //
780  version = MAKEWORD(2, 0);
781  if (WSAStartup(version, &retdata))
782  rb_fatal("Unable to locate winsock library!");
783  if (LOBYTE(retdata.wVersion) != 2)
784  rb_fatal("could not find version 2 of winsock dll");
785 
786  InitializeCriticalSection(&select_mutex);
787  InitializeCriticalSection(&socklist_mutex);
788  InitializeCriticalSection(&conlist_mutex);
789 
790  atexit(exit_handler);
791 }
792 
793 #define MAKE_SOCKDATA(af, fl) ((int)((((int)af)<<4)|((fl)&0xFFFF)))
794 #define GET_FAMILY(v) ((int)(((v)>>4)&0xFFFF))
795 #define GET_FLAGS(v) ((int)((v)&0xFFFF))
796 
797 /* License: Ruby's */
798 static inline int
799 socklist_insert(SOCKET sock, int flag)
800 {
801  int ret;
802 
803  thread_exclusive(socklist) {
804  if (!socklist) {
805  socklist = st_init_numtable();
806  install_vm_exit_handler();
807  }
808  ret = st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
809  }
810 
811  return ret;
812 }
813 
814 /* License: Ruby's */
815 static inline int
816 socklist_lookup(SOCKET sock, int *flagp)
817 {
818  st_data_t data;
819  int ret = 0;
820 
821  thread_exclusive(socklist) {
822  if (!socklist) continue;
823  ret = st_lookup(socklist, (st_data_t)sock, &data);
824  if (ret && flagp)
825  *flagp = (int)data;
826  }
827 
828  return ret;
829 }
830 
831 /* License: Ruby's */
832 static inline int
833 socklist_delete(SOCKET *sockp, int *flagp)
834 {
835  st_data_t key;
836  st_data_t data;
837  int ret = 0;
838 
839  thread_exclusive(socklist) {
840  if (!socklist) continue;
841  key = (st_data_t)*sockp;
842  if (flagp)
843  data = (st_data_t)*flagp;
844  ret = st_delete(socklist, &key, &data);
845  if (ret) {
846  *sockp = (SOCKET)key;
847  if (flagp)
848  *flagp = (int)data;
849  }
850  }
851 
852  return ret;
853 }
854 
855 #if RUBY_MSVCRT_VERSION >= 80
856 # ifdef __MINGW32__
857 # define _CrtSetReportMode(type,mode) ((void)0)
858 # define _RTC_SetErrorFunc(func) ((void)0)
859 # endif
860 static void set_pioinfo_extra(void);
861 #endif
862 static int w32_cmdvector(const WCHAR *, char ***, UINT, rb_encoding *);
863 //
864 // Initialization stuff
865 //
866 /* License: Ruby's */
867 void
868 rb_w32_sysinit(int *argc, char ***argv)
869 {
870 #if RUBY_MSVCRT_VERSION >= 80
871 
872  _CrtSetReportMode(_CRT_ASSERT, 0);
873  _set_invalid_parameter_handler(invalid_parameter);
874  _RTC_SetErrorFunc(rtc_error_handler);
875  set_pioinfo_extra();
876 #endif
877  SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
878 
879  get_version();
880 
881  //
882  // subvert cmd.exe's feeble attempt at command line parsing
883  //
884  *argc = w32_cmdvector(GetCommandLineW(), argv, CP_UTF8, &OnigEncodingUTF_8);
885 
886  //
887  // Now set up the correct time stuff
888  //
889 
890  tzset();
891 
892  InitializeCriticalSection(&uenvarea_mutex);
893  init_env();
894 
895  init_stdhandle();
896 
897  // Initialize Winsock
898  StartSockets();
899 }
900 
901 char *
902 getlogin(void)
903 {
904  return (char *)NTLoginName;
905 }
906 
907 #define MAXCHILDNUM 256 /* max num of child processes */
908 
909 /* License: Ruby's */
910 static struct ChildRecord {
911  HANDLE hProcess; /* process handle */
912  rb_pid_t pid; /* process id */
913 } ChildRecord[MAXCHILDNUM];
914 
915 /* License: Ruby's */
916 #define FOREACH_CHILD(v) do { \
917  struct ChildRecord* v; \
918  for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
919 #define END_FOREACH_CHILD } while (0)
920 
921 /* License: Ruby's */
922 static struct ChildRecord *
923 FindChildSlot(rb_pid_t pid)
924 {
925 
926  FOREACH_CHILD(child) {
927  if (child->pid == pid) {
928  return child;
929  }
930  } END_FOREACH_CHILD;
931  return NULL;
932 }
933 
934 /* License: Ruby's */
935 static struct ChildRecord *
936 FindChildSlotByHandle(HANDLE h)
937 {
938 
939  FOREACH_CHILD(child) {
940  if (child->hProcess == h) {
941  return child;
942  }
943  } END_FOREACH_CHILD;
944  return NULL;
945 }
946 
947 /* License: Ruby's */
948 static void
949 CloseChildHandle(struct ChildRecord *child)
950 {
951  HANDLE h = child->hProcess;
952  child->hProcess = NULL;
953  child->pid = 0;
954  CloseHandle(h);
955 }
956 
957 /* License: Ruby's */
958 static struct ChildRecord *
959 FindFreeChildSlot(void)
960 {
961  FOREACH_CHILD(child) {
962  if (!child->pid) {
963  child->pid = -1; /* lock the slot */
964  child->hProcess = NULL;
965  return child;
966  }
967  } END_FOREACH_CHILD;
968  return NULL;
969 }
970 
971 
972 /*
973  ruby -lne 'BEGIN{$cmds = Hash.new(0); $mask = 1}'
974  -e '$cmds[$_.downcase] |= $mask' -e '$mask <<= 1 if ARGF.eof'
975  -e 'END{$cmds.sort.each{|n,f|puts " \"\\#{f.to_s(8)}\" #{n.dump} + 1,"}}'
976  98cmd ntcmd
977  */
978 #define InternalCmdsMax 8
979 static const char szInternalCmds[][InternalCmdsMax+2] = {
980  "\2" "assoc",
981  "\3" "break",
982  "\3" "call",
983  "\3" "cd",
984  "\1" "chcp",
985  "\3" "chdir",
986  "\3" "cls",
987  "\2" "color",
988  "\3" "copy",
989  "\1" "ctty",
990  "\3" "date",
991  "\3" "del",
992  "\3" "dir",
993  "\3" "echo",
994  "\2" "endlocal",
995  "\3" "erase",
996  "\3" "exit",
997  "\3" "for",
998  "\2" "ftype",
999  "\3" "goto",
1000  "\3" "if",
1001  "\1" "lfnfor",
1002  "\1" "lh",
1003  "\1" "lock",
1004  "\3" "md",
1005  "\3" "mkdir",
1006  "\2" "move",
1007  "\3" "path",
1008  "\3" "pause",
1009  "\2" "popd",
1010  "\3" "prompt",
1011  "\2" "pushd",
1012  "\3" "rd",
1013  "\3" "rem",
1014  "\3" "ren",
1015  "\3" "rename",
1016  "\3" "rmdir",
1017  "\3" "set",
1018  "\2" "setlocal",
1019  "\3" "shift",
1020  "\2" "start",
1021  "\3" "time",
1022  "\2" "title",
1023  "\1" "truename",
1024  "\3" "type",
1025  "\1" "unlock",
1026  "\3" "ver",
1027  "\3" "verify",
1028  "\3" "vol",
1029 };
1030 
1031 /* License: Ruby's */
1032 static int
1033 internal_match(const void *key, const void *elem)
1034 {
1035  return strncmp(key, ((const char *)elem) + 1, InternalCmdsMax);
1036 }
1037 
1038 /* License: Ruby's */
1039 static int
1040 is_command_com(const char *interp)
1041 {
1042  int i = strlen(interp) - 11;
1043 
1044  if ((i == 0 || (i > 0 && isdirsep(interp[i-1]))) &&
1045  strcasecmp(interp+i, "command.com") == 0) {
1046  return 1;
1047  }
1048  return 0;
1049 }
1050 
1051 static int internal_cmd_match(const char *cmdname, int nt);
1052 
1053 /* License: Ruby's */
1054 static int
1055 is_internal_cmd(const char *cmd, int nt)
1056 {
1057  char cmdname[9], *b = cmdname, c;
1058 
1059  do {
1060  if (!(c = *cmd++)) return 0;
1061  } while (isspace(c));
1062  if (c == '@')
1063  return 1;
1064  while (isalpha(c)) {
1065  *b++ = tolower(c);
1066  if (b == cmdname + sizeof(cmdname)) return 0;
1067  c = *cmd++;
1068  }
1069  if (c == '.') c = *cmd;
1070  switch (c) {
1071  case '<': case '>': case '|':
1072  return 1;
1073  case '\0': case ' ': case '\t': case '\n':
1074  break;
1075  default:
1076  return 0;
1077  }
1078  *b = 0;
1079  return internal_cmd_match(cmdname, nt);
1080 }
1081 
1082 /* License: Ruby's */
1083 static int
1084 internal_cmd_match(const char *cmdname, int nt)
1085 {
1086  char *nm;
1087 
1088  nm = bsearch(cmdname, szInternalCmds,
1089  sizeof(szInternalCmds) / sizeof(*szInternalCmds),
1090  sizeof(*szInternalCmds),
1091  internal_match);
1092  if (!nm || !(nm[0] & (nt ? 2 : 1)))
1093  return 0;
1094  return 1;
1095 }
1096 
1097 /* License: Ruby's */
1098 SOCKET
1099 rb_w32_get_osfhandle(int fh)
1100 {
1101  return _get_osfhandle(fh);
1102 }
1103 
1104 /* License: Ruby's */
1105 static int
1106 join_argv(char *cmd, char *const *argv, BOOL escape, UINT cp, int backslash)
1107 {
1108  const char *p, *s;
1109  char *q, *const *t;
1110  int len, n, bs, quote;
1111 
1112  for (t = argv, q = cmd, len = 0; (p = *t) != 0; t++) {
1113  quote = 0;
1114  s = p;
1115  if (!*p || strpbrk(p, " \t\"'")) {
1116  quote = 1;
1117  len++;
1118  if (q) *q++ = '"';
1119  }
1120  for (bs = 0; *p; ++p) {
1121  switch (*p) {
1122  case '\\':
1123  ++bs;
1124  break;
1125  case '"':
1126  len += n = p - s;
1127  if (q) {
1128  memcpy(q, s, n);
1129  q += n;
1130  }
1131  s = p;
1132  len += ++bs;
1133  if (q) {
1134  memset(q, '\\', bs);
1135  q += bs;
1136  }
1137  bs = 0;
1138  break;
1139  case '<': case '>': case '|': case '^':
1140  if (escape && !quote) {
1141  len += (n = p - s) + 1;
1142  if (q) {
1143  memcpy(q, s, n);
1144  q += n;
1145  *q++ = '^';
1146  }
1147  s = p;
1148  break;
1149  }
1150  default:
1151  bs = 0;
1152  p = CharNextExA(cp, p, 0) - 1;
1153  break;
1154  }
1155  }
1156  len += (n = p - s) + 1;
1157  if (quote) len++;
1158  if (q) {
1159  memcpy(q, s, n);
1160  if (backslash > 0) {
1161  --backslash;
1162  q[n] = 0;
1163  translate_char(q, '/', '\\', cp);
1164  }
1165  q += n;
1166  if (quote) *q++ = '"';
1167  *q++ = ' ';
1168  }
1169  }
1170  if (q > cmd) --len;
1171  if (q) {
1172  if (q > cmd) --q;
1173  *q = '\0';
1174  }
1175  return len;
1176 }
1177 
1178 /* License: Ruby's */
1179 #define STRNDUPV(ptr, v, src, len) \
1180  (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
1181 
1182 /* License: Ruby's */
1183 static int
1184 check_spawn_mode(int mode)
1185 {
1186  switch (mode) {
1187  case P_NOWAIT:
1188  case P_OVERLAY:
1189  return 0;
1190  default:
1191  errno = EINVAL;
1192  return -1;
1193  }
1194 }
1195 
1196 /* License: Ruby's */
1197 static rb_pid_t
1198 child_result(struct ChildRecord *child, int mode)
1199 {
1200  DWORD exitcode;
1201 
1202  if (!child) {
1203  return -1;
1204  }
1205 
1206  if (mode == P_OVERLAY) {
1207  WaitForSingleObject(child->hProcess, INFINITE);
1208  GetExitCodeProcess(child->hProcess, &exitcode);
1209  CloseChildHandle(child);
1210  _exit(exitcode);
1211  }
1212  return child->pid;
1213 }
1214 
1215 /* License: Ruby's */
1216 static int
1217 CreateChild(struct ChildRecord *child, const WCHAR *cmd, const WCHAR *prog, HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
1218 {
1219  BOOL fRet;
1220  STARTUPINFOW aStartupInfo;
1221  PROCESS_INFORMATION aProcessInformation;
1222  SECURITY_ATTRIBUTES sa;
1223 
1224  if (!cmd && !prog) {
1225  errno = EFAULT;
1226  return FALSE;
1227  }
1228 
1229  if (!child) {
1230  errno = EAGAIN;
1231  return FALSE;
1232  }
1233 
1234  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
1235  sa.lpSecurityDescriptor = NULL;
1236  sa.bInheritHandle = TRUE;
1237 
1238  memset(&aStartupInfo, 0, sizeof(aStartupInfo));
1239  memset(&aProcessInformation, 0, sizeof(aProcessInformation));
1240  aStartupInfo.cb = sizeof(aStartupInfo);
1241  aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
1242  if (hInput) {
1243  aStartupInfo.hStdInput = hInput;
1244  }
1245  else {
1246  aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1247  }
1248  if (hOutput) {
1249  aStartupInfo.hStdOutput = hOutput;
1250  }
1251  else {
1252  aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1253  }
1254  if (hError) {
1255  aStartupInfo.hStdError = hError;
1256  }
1257  else {
1258  aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1259  }
1260 
1261  dwCreationFlags |= NORMAL_PRIORITY_CLASS;
1262 
1263  if (lstrlenW(cmd) > 32767) {
1264  child->pid = 0; /* release the slot */
1265  errno = E2BIG;
1266  return FALSE;
1267  }
1268 
1269  RUBY_CRITICAL {
1270  fRet = CreateProcessW(prog, (WCHAR *)cmd, &sa, &sa,
1271  sa.bInheritHandle, dwCreationFlags, NULL, NULL,
1272  &aStartupInfo, &aProcessInformation);
1273  errno = map_errno(GetLastError());
1274  }
1275 
1276  if (!fRet) {
1277  child->pid = 0; /* release the slot */
1278  return FALSE;
1279  }
1280 
1281  CloseHandle(aProcessInformation.hThread);
1282 
1283  child->hProcess = aProcessInformation.hProcess;
1284  child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
1285 
1286  return TRUE;
1287 }
1288 
1289 /* License: Ruby's */
1290 static int
1291 is_batch(const char *cmd)
1292 {
1293  int len = strlen(cmd);
1294  if (len <= 4) return 0;
1295  cmd += len - 4;
1296  if (*cmd++ != '.') return 0;
1297  if (strcasecmp(cmd, "bat") == 0) return 1;
1298  if (strcasecmp(cmd, "cmd") == 0) return 1;
1299  return 0;
1300 }
1301 
1302 #define filecp rb_w32_filecp
1303 #define mbstr_to_wstr rb_w32_mbstr_to_wstr
1304 #define wstr_to_mbstr rb_w32_wstr_to_mbstr
1305 #define acp_to_wstr(str, plen) mbstr_to_wstr(CP_ACP, str, -1, plen)
1306 #define wstr_to_acp(str, plen) wstr_to_mbstr(CP_ACP, str, -1, plen)
1307 #define filecp_to_wstr(str, plen) mbstr_to_wstr(filecp(), str, -1, plen)
1308 #define wstr_to_filecp(str, plen) wstr_to_mbstr(filecp(), str, -1, plen)
1309 #define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
1310 #define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
1311 
1312 /* License: Ruby's */
1313 HANDLE
1314 rb_w32_start_process(const char *abspath, char *const *argv, int out_fd)
1315 {
1316  /* NOTE: This function is used by RJIT worker, so it can be used parallelly with
1317  Ruby's main thread. So functions touching things shared with main thread can't
1318  be used, like `ALLOCV` that may trigger GC or `FindFreeChildSlot` that finds
1319  a slot from shared memory without atomic locks. */
1320  struct ChildRecord child;
1321  char *cmd;
1322  size_t len;
1323  WCHAR *wcmd = NULL, *wprog = NULL;
1324  HANDLE outHandle = NULL;
1325 
1326  if (out_fd) {
1327  outHandle = (HANDLE)rb_w32_get_osfhandle(out_fd);
1328  }
1329 
1330  len = join_argv(NULL, argv, FALSE, filecp(), 1);
1331  cmd = alloca(sizeof(char) * len);
1332  join_argv(cmd, argv, FALSE, filecp(), 1);
1333 
1334  if (!(wcmd = mbstr_to_wstr(filecp(), cmd, -1, NULL))) {
1335  errno = E2BIG;
1336  return NULL;
1337  }
1338  if (!(wprog = mbstr_to_wstr(filecp(), abspath, -1, NULL))) {
1339  errno = E2BIG;
1340  return NULL;
1341  }
1342 
1343  if (!CreateChild(&child, wcmd, wprog, NULL, outHandle, outHandle, 0)) {
1344  return NULL;
1345  }
1346 
1347  free(wcmd);
1348  free(wprog);
1349  return child.hProcess;
1350 }
1351 
1352 /* License: Artistic or GPL */
1353 static rb_pid_t
1354 w32_spawn(int mode, const char *cmd, const char *prog, UINT cp)
1355 {
1356  char fbuf[PATH_MAX];
1357  char *p = NULL;
1358  const char *shell = NULL;
1359  WCHAR *wcmd = NULL, *wshell = NULL;
1360  int e = 0;
1361  rb_pid_t ret = -1;
1362  VALUE v = 0;
1363  VALUE v2 = 0;
1364  int sep = 0;
1365  char *cmd_sep = NULL;
1366 
1367  if (check_spawn_mode(mode)) return -1;
1368 
1369  if (prog) {
1370  if (!(p = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1371  shell = prog;
1372  }
1373  else {
1374  shell = p;
1375  translate_char(p, '/', '\\', cp);
1376  }
1377  }
1378  else {
1379  int redir = -1;
1380  int nt;
1381  while (ISSPACE(*cmd)) cmd++;
1382  if ((shell = w32_getenv("RUBYSHELL", cp)) && (redir = has_redirection(cmd, cp))) {
1383  size_t shell_len = strlen(shell);
1384  size_t cmd_len = strlen(cmd) + sizeof(" -c ") + 2;
1385  char *tmp = ALLOCV(v, shell_len + cmd_len);
1386  memcpy(tmp, shell, shell_len + 1);
1387  translate_char(tmp, '/', '\\', cp);
1388  snprintf(tmp + shell_len, cmd_len, " -c \"%s\"", cmd);
1389  cmd = tmp;
1390  }
1391  else if ((shell = w32_getenv("COMSPEC", cp)) &&
1392  (nt = !is_command_com(shell),
1393  (redir < 0 ? has_redirection(cmd, cp) : redir) ||
1394  is_internal_cmd(cmd, nt))) {
1395  size_t cmd_len = strlen(shell) + strlen(cmd) + sizeof(" /c ") + (nt ? 2 : 0);
1396  char *tmp = ALLOCV(v, cmd_len);
1397  snprintf(tmp, cmd_len, nt ? "%s /c \"%s\"" : "%s /c %s", shell, cmd);
1398  cmd = tmp;
1399  }
1400  else {
1401  int len = 0, quote = (*cmd == '"') ? '"' : (*cmd == '\'') ? '\'' : 0;
1402  int slash = 0;
1403  for (prog = cmd + !!quote;; prog = CharNextExA(cp, prog, 0)) {
1404  if (*prog == '/') slash = 1;
1405  if (!*prog) {
1406  len = prog - cmd;
1407  if (slash) {
1408  STRNDUPV(p, v2, cmd, len);
1409  cmd = p;
1410  }
1411  shell = cmd;
1412  break;
1413  }
1414  if ((unsigned char)*prog == quote) {
1415  len = prog++ - cmd - 1;
1416  STRNDUPV(p, v2, cmd + 1, len);
1417  shell = p;
1418  break;
1419  }
1420  if (quote) continue;
1421  if (ISSPACE(*prog) || strchr("<>|*?\"", *prog)) {
1422  len = prog - cmd;
1423  STRNDUPV(p, v2, cmd, len + (slash ? strlen(prog) : 0));
1424  if (slash) {
1425  cmd = p;
1426  sep = *(cmd_sep = &p[len]);
1427  *cmd_sep = '\0';
1428  }
1429  shell = p;
1430  break;
1431  }
1432  }
1433  shell = dln_find_exe_r(shell, NULL, fbuf, sizeof(fbuf));
1434  if (p && slash) translate_char(p, '/', '\\', cp);
1435  if (!shell) {
1436  shell = p ? p : cmd;
1437  }
1438  else {
1439  len = strlen(shell);
1440  if (strchr(shell, ' ')) quote = -1;
1441  if (shell == fbuf) {
1442  p = fbuf;
1443  }
1444  else if (shell != p && strchr(shell, '/')) {
1445  STRNDUPV(p, v2, shell, len);
1446  shell = p;
1447  }
1448  if (p) translate_char(p, '/', '\\', cp);
1449  if (is_batch(shell)) {
1450  int alen = strlen(prog);
1451  cmd = p = ALLOCV(v, len + alen + (quote ? 2 : 0) + 1);
1452  if (quote) *p++ = '"';
1453  memcpy(p, shell, len);
1454  p += len;
1455  if (quote) *p++ = '"';
1456  memcpy(p, prog, alen + 1);
1457  shell = 0;
1458  }
1459  }
1460  }
1461  }
1462 
1463  if (!e && shell && !(wshell = mbstr_to_wstr(cp, shell, -1, NULL))) e = E2BIG;
1464  if (cmd_sep) *cmd_sep = sep;
1465  if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1466  if (v2) ALLOCV_END(v2);
1467  if (v) ALLOCV_END(v);
1468 
1469  if (!e) {
1470  struct ChildRecord *child = FindFreeChildSlot();
1471  if (CreateChild(child, wcmd, wshell, NULL, NULL, NULL, 0)) {
1472  ret = child_result(child, mode);
1473  }
1474  }
1475  free(wshell);
1476  free(wcmd);
1477  if (e) errno = e;
1478  return ret;
1479 }
1480 
1481 /* License: Ruby's */
1482 rb_pid_t
1483 rb_w32_spawn(int mode, const char *cmd, const char *prog)
1484 {
1485  /* assume ACP */
1486  return w32_spawn(mode, cmd, prog, filecp());
1487 }
1488 
1489 /* License: Ruby's */
1490 rb_pid_t
1491 rb_w32_uspawn(int mode, const char *cmd, const char *prog)
1492 {
1493  return w32_spawn(mode, cmd, prog, CP_UTF8);
1494 }
1495 
1496 /* License: Artistic or GPL */
1497 static rb_pid_t
1498 w32_spawn_process(int mode, const char *prog, char *const *argv,
1499  int in_fd, int out_fd, int err_fd, DWORD flags, UINT cp)
1500 {
1501  int c_switch = 0;
1502  size_t len;
1503  BOOL ntcmd = FALSE, tmpnt;
1504  const char *shell;
1505  char *cmd, fbuf[PATH_MAX];
1506  WCHAR *wcmd = NULL, *wprog = NULL;
1507  int e = 0;
1508  rb_pid_t ret = -1;
1509  VALUE v = 0;
1510  HANDLE in_handle = NULL, out_handle = NULL, err_handle = NULL;
1511 
1512  if (check_spawn_mode(mode)) return -1;
1513 
1514  if (in_fd >= 0) {
1515  in_handle = (HANDLE)rb_w32_get_osfhandle(in_fd);
1516  }
1517  if (out_fd >= 0) {
1518  out_handle = (HANDLE)rb_w32_get_osfhandle(out_fd);
1519  }
1520  if (err_fd >= 0) {
1521  err_handle = (HANDLE)rb_w32_get_osfhandle(err_fd);
1522  }
1523 
1524  if (!prog) prog = argv[0];
1525  if ((shell = w32_getenv("COMSPEC", cp)) &&
1526  internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
1527  ntcmd = tmpnt;
1528  prog = shell;
1529  c_switch = 1;
1530  }
1531  else if ((cmd = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1532  if (cmd == prog) strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1533  translate_char(cmd, '/', '\\', cp);
1534  prog = cmd;
1535  }
1536  else if (strchr(prog, '/')) {
1537  len = strlen(prog);
1538  if (len < sizeof(fbuf))
1539  strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1540  else
1541  STRNDUPV(cmd, v, prog, len);
1542  translate_char(cmd, '/', '\\', cp);
1543  prog = cmd;
1544  }
1545  if (c_switch || is_batch(prog)) {
1546  char *progs[2];
1547  progs[0] = (char *)prog;
1548  progs[1] = NULL;
1549  len = join_argv(NULL, progs, ntcmd, cp, 1);
1550  if (c_switch) len += 3;
1551  else ++argv;
1552  if (argv[0]) len += join_argv(NULL, argv, ntcmd, cp, 0);
1553  cmd = ALLOCV(v, len);
1554  join_argv(cmd, progs, ntcmd, cp, 1);
1555  if (c_switch) strlcat(cmd, " /c", len);
1556  if (argv[0]) join_argv(cmd + strlcat(cmd, " ", len), argv, ntcmd, cp, 0);
1557  prog = c_switch ? shell : 0;
1558  }
1559  else {
1560  len = join_argv(NULL, argv, FALSE, cp, 1);
1561  cmd = ALLOCV(v, len);
1562  join_argv(cmd, argv, FALSE, cp, 1);
1563  }
1564 
1565  if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1566  if (v) ALLOCV_END(v);
1567  if (!e && prog && !(wprog = mbstr_to_wstr(cp, prog, -1, NULL))) e = E2BIG;
1568 
1569  if (!e) {
1570  struct ChildRecord *child = FindFreeChildSlot();
1571  if (CreateChild(child, wcmd, wprog, in_handle, out_handle, err_handle, flags)) {
1572  ret = child_result(child, mode);
1573  }
1574  }
1575  free(wprog);
1576  free(wcmd);
1577  if (e) errno = e;
1578  return ret;
1579 }
1580 
1581 /* License: Ruby's */
1582 rb_pid_t
1583 rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1584 {
1585  /* assume ACP */
1586  return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, filecp());
1587 }
1588 
1589 /* License: Ruby's */
1590 rb_pid_t
1591 rb_w32_uaspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1592 {
1593  return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, CP_UTF8);
1594 }
1595 
1596 /* License: Ruby's */
1597 rb_pid_t
1598 rb_w32_aspawn(int mode, const char *prog, char *const *argv)
1599 {
1600  return w32_spawn_process(mode, prog, argv, -1, -1, -1, 0, filecp());
1601 }
1602 
1603 /* License: Ruby's */
1604 rb_pid_t
1605 rb_w32_uaspawn(int mode, const char *prog, char *const *argv)
1606 {
1607  return rb_w32_uaspawn_flags(mode, prog, argv, 0);
1608 }
1609 
1610 /* License: Ruby's */
1611 rb_pid_t
1612 rb_w32_uspawn_process(int mode, const char *prog, char *const *argv,
1613  int in_fd, int out_fd, int err_fd, DWORD flags)
1614 {
1615  return w32_spawn_process(mode, prog, argv, in_fd, out_fd, err_fd,
1616  flags, CP_UTF8);
1617 }
1618 
1619 /* License: Artistic or GPL */
1620 typedef struct _NtCmdLineElement {
1621  struct _NtCmdLineElement *next;
1622  char *str;
1623  long len;
1624  int flags;
1626 
1627 //
1628 // Possible values for flags
1629 //
1630 
1631 #define NTGLOB 0x1 // element contains a wildcard
1632 #define NTMALLOC 0x2 // string in element was malloc'ed
1633 #define NTSTRING 0x4 // element contains a quoted string
1634 
1635 /* License: Ruby's */
1636 static int
1637 insert(const char *path, VALUE vinfo, void *enc)
1638 {
1639  NtCmdLineElement *tmpcurr;
1640  NtCmdLineElement ***tail = (NtCmdLineElement ***)vinfo;
1641 
1642  tmpcurr = (NtCmdLineElement *)malloc(sizeof(NtCmdLineElement));
1643  if (!tmpcurr) return -1;
1644  MEMZERO(tmpcurr, NtCmdLineElement, 1);
1645  tmpcurr->len = strlen(path);
1646  tmpcurr->str = strdup(path);
1647  if (!tmpcurr->str) return -1;
1648  tmpcurr->flags |= NTMALLOC;
1649  **tail = tmpcurr;
1650  *tail = &tmpcurr->next;
1651 
1652  return 0;
1653 }
1654 
1655 /* License: Artistic or GPL */
1656 static NtCmdLineElement **
1657 cmdglob(NtCmdLineElement *patt, NtCmdLineElement **tail, UINT cp, rb_encoding *enc)
1658 {
1659  char buffer[PATH_MAX], *buf = buffer;
1660  NtCmdLineElement **last = tail;
1661  int status;
1662 
1663  if (patt->len >= PATH_MAX)
1664  if (!(buf = malloc(patt->len + 1))) return 0;
1665 
1666  memcpy(buf, patt->str, patt->len);
1667  buf[patt->len] = '\0';
1668  translate_char(buf, '\\', '/', cp);
1669  status = ruby_brace_glob_with_enc(buf, 0, insert, (VALUE)&tail, enc);
1670  if (buf != buffer)
1671  free(buf);
1672 
1673  if (status || last == tail) return 0;
1674  if (patt->flags & NTMALLOC)
1675  free(patt->str);
1676  free(patt);
1677  return tail;
1678 }
1679 
1680 //
1681 // Check a command string to determine if it has I/O redirection
1682 // characters that require it to be executed by a command interpreter
1683 //
1684 
1685 /* License: Artistic or GPL */
1686 static int
1687 has_redirection(const char *cmd, UINT cp)
1688 {
1689  char quote = '\0';
1690  const char *ptr;
1691 
1692  //
1693  // Scan the string, looking for redirection characters (< or >), pipe
1694  // character (|) or newline (\n) that are not in a quoted string
1695  //
1696 
1697  for (ptr = cmd; *ptr;) {
1698  switch (*ptr) {
1699  case '\'':
1700  case '\"':
1701  if (!quote)
1702  quote = *ptr;
1703  else if (quote == *ptr)
1704  quote = '\0';
1705  ptr++;
1706  break;
1707 
1708  case '>':
1709  case '<':
1710  case '|':
1711  case '&':
1712  case '\n':
1713  if (!quote)
1714  return TRUE;
1715  ptr++;
1716  break;
1717 
1718  case '%':
1719  if (*++ptr != '_' && !ISALPHA(*ptr)) break;
1720  while (*++ptr == '_' || ISALNUM(*ptr));
1721  if (*ptr++ == '%') return TRUE;
1722  break;
1723 
1724  case '\\':
1725  ptr++;
1726  default:
1727  ptr = CharNextExA(cp, ptr, 0);
1728  break;
1729  }
1730  }
1731  return FALSE;
1732 }
1733 
1734 /* License: Ruby's */
1735 static inline WCHAR *
1736 skipspace(WCHAR *ptr)
1737 {
1738  while (ISSPACE(*ptr))
1739  ptr++;
1740  return ptr;
1741 }
1742 
1743 /* License: Artistic or GPL */
1744 static int
1745 w32_cmdvector(const WCHAR *cmd, char ***vec, UINT cp, rb_encoding *enc)
1746 {
1747  int globbing, len;
1748  int elements, strsz, done;
1749  int slashes, escape;
1750  WCHAR *ptr, *base, *cmdline;
1751  char *cptr, *buffer;
1752  char **vptr;
1753  WCHAR quote;
1754  NtCmdLineElement *curr, **tail;
1755  NtCmdLineElement *cmdhead = NULL, **cmdtail = &cmdhead;
1756 
1757  //
1758  // just return if we don't have a command line
1759  //
1760  while (ISSPACE(*cmd))
1761  cmd++;
1762  if (!*cmd) {
1763  *vec = NULL;
1764  return 0;
1765  }
1766 
1767  ptr = cmdline = wcsdup(cmd);
1768 
1769  //
1770  // Ok, parse the command line, building a list of CmdLineElements.
1771  // When we've finished, and it's an input command (meaning that it's
1772  // the processes argv), we'll do globing and then build the argument
1773  // vector.
1774  // The outer loop does one iteration for each element seen.
1775  // The inner loop does one iteration for each character in the element.
1776  //
1777 
1778  while (*(ptr = skipspace(ptr))) {
1779  base = ptr;
1780  quote = slashes = globbing = escape = 0;
1781  for (done = 0; !done && *ptr; ) {
1782  //
1783  // Switch on the current character. We only care about the
1784  // white-space characters, the wild-card characters, and the
1785  // quote characters.
1786  //
1787 
1788  switch (*ptr) {
1789  case L'\\':
1790  if (quote != L'\'') slashes++;
1791  break;
1792 
1793  case L' ':
1794  case L'\t':
1795  case L'\n':
1796  //
1797  // if we're not in a string, then we're finished with this
1798  // element
1799  //
1800 
1801  if (!quote) {
1802  *ptr = 0;
1803  done = 1;
1804  }
1805  break;
1806 
1807  case L'*':
1808  case L'?':
1809  case L'[':
1810  case L'{':
1811  //
1812  // record the fact that this element has a wildcard character
1813  // N.B. Don't glob if inside a single quoted string
1814  //
1815 
1816  if (quote != L'\'')
1817  globbing++;
1818  slashes = 0;
1819  break;
1820 
1821  case L'\'':
1822  case L'\"':
1823  //
1824  // if we're already in a string, see if this is the
1825  // terminating close-quote. If it is, we're finished with
1826  // the string, but not necessarily with the element.
1827  // If we're not already in a string, start one.
1828  //
1829 
1830  if (!(slashes & 1)) {
1831  if (!quote)
1832  quote = *ptr;
1833  else if (quote == *ptr) {
1834  if (quote == L'"' && quote == ptr[1])
1835  ptr++;
1836  quote = L'\0';
1837  }
1838  }
1839  escape++;
1840  slashes = 0;
1841  break;
1842 
1843  default:
1844  ptr = CharNextW(ptr);
1845  slashes = 0;
1846  continue;
1847  }
1848  ptr++;
1849  }
1850 
1851  //
1852  // when we get here, we've got a pair of pointers to the element,
1853  // base and ptr. Base points to the start of the element while ptr
1854  // points to the character following the element.
1855  //
1856 
1857  len = ptr - base;
1858  if (done) --len;
1859 
1860  //
1861  // if it's an input vector element and it's enclosed by quotes,
1862  // we can remove them.
1863  //
1864 
1865  if (escape) {
1866  WCHAR *p = base, c;
1867  slashes = quote = 0;
1868  while (p < base + len) {
1869  switch (c = *p) {
1870  case L'\\':
1871  p++;
1872  if (quote != L'\'') slashes++;
1873  break;
1874 
1875  case L'\'':
1876  case L'"':
1877  if (!(slashes & 1) && quote && quote != c) {
1878  p++;
1879  slashes = 0;
1880  break;
1881  }
1882  memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
1883  sizeof(WCHAR) * (base + len - p));
1884  len -= ((slashes + 1) >> 1) + (~slashes & 1);
1885  p -= (slashes + 1) >> 1;
1886  if (!(slashes & 1)) {
1887  if (quote) {
1888  if (quote == L'"' && quote == *p)
1889  p++;
1890  quote = L'\0';
1891  }
1892  else
1893  quote = c;
1894  }
1895  else
1896  p++;
1897  slashes = 0;
1898  break;
1899 
1900  default:
1901  p = CharNextW(p);
1902  slashes = 0;
1903  break;
1904  }
1905  }
1906  }
1907 
1908  curr = (NtCmdLineElement *)calloc(1, sizeof(NtCmdLineElement));
1909  if (!curr) goto do_nothing;
1910  curr->str = rb_w32_wstr_to_mbstr(cp, base, len, &curr->len);
1911  curr->flags |= NTMALLOC;
1912 
1913  if (globbing && (tail = cmdglob(curr, cmdtail, cp, enc))) {
1914  cmdtail = tail;
1915  }
1916  else {
1917  *cmdtail = curr;
1918  cmdtail = &curr->next;
1919  }
1920  }
1921 
1922  //
1923  // Almost done!
1924  // Count up the elements, then allocate space for a vector of pointers
1925  // (argv) and a string table for the elements.
1926  //
1927 
1928  for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
1929  elements++;
1930  strsz += (curr->len + 1);
1931  }
1932 
1933  len = (elements+1)*sizeof(char *) + strsz;
1934  buffer = (char *)malloc(len);
1935  if (!buffer) {
1936  do_nothing:
1937  while ((curr = cmdhead) != 0) {
1938  cmdhead = curr->next;
1939  if (curr->flags & NTMALLOC) free(curr->str);
1940  free(curr);
1941  }
1942  free(cmdline);
1943  for (vptr = *vec; *vptr; ++vptr);
1944  return vptr - *vec;
1945  }
1946 
1947  //
1948  // make vptr point to the start of the buffer
1949  // and cptr point to the area we'll consider the string table.
1950  //
1951  // buffer (*vec)
1952  // |
1953  // V ^---------------------V
1954  // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1955  // | | | .... | NULL | | ..... |\0 | | ..... |\0 |...
1956  // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1957  // |- elements+1 -| ^ 1st element ^ 2nd element
1958 
1959  vptr = (char **) buffer;
1960 
1961  cptr = buffer + (elements+1) * sizeof(char *);
1962 
1963  while ((curr = cmdhead) != 0) {
1964  memcpy(cptr, curr->str, curr->len);
1965  cptr[curr->len] = '\0';
1966  *vptr++ = cptr;
1967  cptr += curr->len + 1;
1968  cmdhead = curr->next;
1969  if (curr->flags & NTMALLOC) free(curr->str);
1970  free(curr);
1971  }
1972  *vptr = 0;
1973 
1974  *vec = (char **) buffer;
1975  free(cmdline);
1976  return elements;
1977 }
1978 
1979 //
1980 // UNIX compatible directory access functions for NT
1981 //
1982 
1983 /* License: Ruby's */
1984 /* TODO: better name */
1985 static HANDLE
1986 open_special(const WCHAR *path, DWORD access, DWORD flags)
1987 {
1988  const DWORD share_mode =
1989  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
1990  return CreateFileW(path, access, share_mode, NULL, OPEN_EXISTING,
1991  FILE_FLAG_BACKUP_SEMANTICS|flags, NULL);
1992 }
1993 
1994 //
1995 // The idea here is to read all the directory names into a string table
1996 // (separated by nulls) and when one of the other dir functions is called
1997 // return the pointer to the current file name.
1998 //
1999 
2000 /* License: Ruby's */
2001 #define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] & (1 << (i) % CHAR_BIT))
2002 #define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
2003 
2004 #define BitOfIsDir(n) ((n) * 2)
2005 #define BitOfIsRep(n) ((n) * 2 + 1)
2006 #define DIRENT_PER_CHAR (CHAR_BIT / 2)
2007 
2008 static const WCHAR namespace_prefix[] = {L'\\', L'\\', L'?', L'\\'};
2009 
2010 enum {FINAL_PATH_MAX = PATH_MAX + numberof(namespace_prefix)};
2011 
2012 /* License: Artistic or GPL */
2013 static HANDLE
2014 open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
2015 {
2016  HANDLE fh;
2017  WCHAR fullname[FINAL_PATH_MAX + rb_strlen_lit("\\*")];
2018  WCHAR *p;
2019  int len = 0;
2020 
2021  //
2022  // Create the search pattern
2023  //
2024 
2025  fh = open_special(filename, 0, 0);
2026  if (fh != INVALID_HANDLE_VALUE) {
2027  len = GetFinalPathNameByHandleW(fh, fullname, FINAL_PATH_MAX, 0);
2028  CloseHandle(fh);
2029  if (len >= FINAL_PATH_MAX) {
2030  errno = ENAMETOOLONG;
2031  return INVALID_HANDLE_VALUE;
2032  }
2033  }
2034  if (!len) {
2035  len = lstrlenW(filename);
2036  if (len >= PATH_MAX) {
2037  errno = ENAMETOOLONG;
2038  return INVALID_HANDLE_VALUE;
2039  }
2040  MEMCPY(fullname, filename, WCHAR, len);
2041  }
2042  p = &fullname[len-1];
2043  if (!(isdirsep(*p) || *p == L':')) *++p = L'\\';
2044  *++p = L'*';
2045  *++p = L'\0';
2046 
2047  //
2048  // do the FindFirstFile call
2049  //
2050  fh = FindFirstFileW(fullname, fd);
2051  if (fh == INVALID_HANDLE_VALUE) {
2052  errno = map_errno(GetLastError());
2053  }
2054  return fh;
2055 }
2056 
2057 /* License: Artistic or GPL */
2058 static DIR *
2059 w32_wopendir(const WCHAR *wpath)
2060 {
2061  struct stati128 sbuf;
2062  WIN32_FIND_DATAW fd;
2063  HANDLE fh;
2064  DIR *p;
2065  long pathlen;
2066  long len;
2067  long altlen;
2068  long idx;
2069  WCHAR *tmpW;
2070  char *tmp;
2071 
2072  //
2073  // check to see if we've got a directory
2074  //
2075  if (wstati128(wpath, &sbuf, FALSE) < 0) {
2076  return NULL;
2077  }
2078  if (!(sbuf.st_mode & S_IFDIR) &&
2079  (!ISALPHA(wpath[0]) || wpath[1] != L':' || wpath[2] != L'\0' ||
2080  ((1 << ((wpath[0] & 0x5f) - 'A')) & GetLogicalDrives()) == 0)) {
2081  errno = ENOTDIR;
2082  return NULL;
2083  }
2084  fh = open_dir_handle(wpath, &fd);
2085  if (fh == INVALID_HANDLE_VALUE) {
2086  return NULL;
2087  }
2088 
2089  //
2090  // Get us a DIR structure
2091  //
2092  p = calloc(1, sizeof(DIR));
2093  if (p == NULL)
2094  return NULL;
2095 
2096  pathlen = lstrlenW(wpath);
2097  idx = 0;
2098 
2099  //
2100  // loop finding all the files that match the wildcard
2101  // (which should be all of them in this directory!).
2102  // the variable idx should point one past the null terminator
2103  // of the previous string found.
2104  //
2105  do {
2106  len = lstrlenW(fd.cFileName) + 1;
2107  altlen = lstrlenW(fd.cAlternateFileName) + 1;
2108 
2109  //
2110  // bump the string table size by enough for the
2111  // new name and it's null terminator
2112  //
2113  tmpW = realloc(p->start, (idx + len + altlen) * sizeof(WCHAR));
2114  if (!tmpW) {
2115  error:
2116  rb_w32_closedir(p);
2117  FindClose(fh);
2118  errno = ENOMEM;
2119  return NULL;
2120  }
2121 
2122  p->start = tmpW;
2123  memcpy(&p->start[idx], fd.cFileName, len * sizeof(WCHAR));
2124  memcpy(&p->start[idx + len], fd.cAlternateFileName, altlen * sizeof(WCHAR));
2125 
2126  if (p->nfiles % DIRENT_PER_CHAR == 0) {
2127  tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
2128  if (!tmp)
2129  goto error;
2130  p->bits = tmp;
2131  p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
2132  }
2133  if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2134  SetBit(p->bits, BitOfIsDir(p->nfiles));
2135  if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2136  WCHAR *tmppath = malloc((pathlen + len + 1) * sizeof(WCHAR));
2137  memcpy(tmppath, wpath, pathlen * sizeof(WCHAR));
2138  tmppath[pathlen] = L'\\';
2139  memcpy(tmppath + pathlen + 1, fd.cFileName, len * sizeof(WCHAR));
2140  if (rb_w32_reparse_symlink_p(tmppath))
2141  SetBit(p->bits, BitOfIsRep(p->nfiles));
2142  free(tmppath);
2143  }
2144 
2145  p->nfiles++;
2146  idx += len + altlen;
2147  } while (FindNextFileW(fh, &fd));
2148  FindClose(fh);
2149  p->size = idx;
2150  p->curr = p->start;
2151  return p;
2152 }
2153 
2154 /* License: Ruby's */
2155 UINT
2156 filecp(void)
2157 {
2158  UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
2159  return cp;
2160 }
2161 
2162 /* License: Ruby's */
2163 char *
2164 rb_w32_wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
2165 {
2166  char *ptr;
2167  int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL);
2168  if (!(ptr = malloc(len))) return 0;
2169  WideCharToMultiByte(cp, 0, wstr, clen, ptr, len, NULL, NULL);
2170  if (plen) {
2171  /* exclude NUL only if NUL-terminated string */
2172  if (clen == -1) --len;
2173  *plen = len;
2174  }
2175  return ptr;
2176 }
2177 
2178 /* License: Ruby's */
2179 WCHAR *
2180 rb_w32_mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
2181 {
2182  /* This is used by RJIT worker. Do not trigger GC or call Ruby method here. */
2183  WCHAR *ptr;
2184  int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0);
2185  if (!(ptr = malloc(sizeof(WCHAR) * len))) return 0;
2186  MultiByteToWideChar(cp, 0, str, clen, ptr, len);
2187  if (plen) {
2188  /* exclude NUL only if NUL-terminated string */
2189  if (clen == -1) --len;
2190  *plen = len;
2191  }
2192  return ptr;
2193 }
2194 
2195 /* License: Ruby's */
2196 DIR *
2197 rb_w32_opendir(const char *filename)
2198 {
2199  DIR *ret;
2200  WCHAR *wpath = filecp_to_wstr(filename, NULL);
2201  if (!wpath)
2202  return NULL;
2203  ret = w32_wopendir(wpath);
2204  free(wpath);
2205  return ret;
2206 }
2207 
2208 /* License: Ruby's */
2209 DIR *
2210 rb_w32_uopendir(const char *filename)
2211 {
2212  DIR *ret;
2213  WCHAR *wpath = utf8_to_wstr(filename, NULL);
2214  if (!wpath)
2215  return NULL;
2216  ret = w32_wopendir(wpath);
2217  free(wpath);
2218  return ret;
2219 }
2220 
2221 //
2222 // Move to next entry
2223 //
2224 
2225 /* License: Artistic or GPL */
2226 static void
2227 move_to_next_entry(DIR *dirp)
2228 {
2229  if (dirp->curr) {
2230  dirp->loc++;
2231  dirp->curr += lstrlenW(dirp->curr) + 1;
2232  dirp->curr += lstrlenW(dirp->curr) + 1;
2233  if (dirp->curr >= (dirp->start + dirp->size)) {
2234  dirp->curr = NULL;
2235  }
2236  }
2237 }
2238 
2239 //
2240 // Readdir just returns the current string pointer and bumps the
2241 // string pointer to the next entry.
2242 //
2243 /* License: Ruby's */
2244 static BOOL
2245 win32_direct_conv(const WCHAR *file, const WCHAR *alt, struct direct *entry, const void *enc)
2246 {
2247  UINT cp = *((UINT *)enc);
2248  if (!(entry->d_name = wstr_to_mbstr(cp, file, -1, &entry->d_namlen)))
2249  return FALSE;
2250  if (alt && *alt) {
2251  long altlen = 0;
2252  entry->d_altname = wstr_to_mbstr(cp, alt, -1, &altlen);
2253  entry->d_altlen = altlen;
2254  }
2255  return TRUE;
2256 }
2257 
2258 /* License: Ruby's */
2259 VALUE
2260 rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
2261 {
2262  VALUE src;
2263  long len = lstrlenW(wstr);
2264  int encindex = rb_enc_to_index(enc);
2265 
2266  if (encindex == ENCINDEX_UTF_16LE) {
2267  return rb_enc_str_new((char *)wstr, len * sizeof(WCHAR), enc);
2268  }
2269  else {
2270 #if SIZEOF_INT < SIZEOF_LONG
2271 # error long should equal to int on Windows
2272 #endif
2273  int clen = rb_long2int(len);
2274  len = WideCharToMultiByte(CP_UTF8, 0, wstr, clen, NULL, 0, NULL, NULL);
2275  src = rb_enc_str_new(0, len, rb_enc_from_index(ENCINDEX_UTF_8));
2276  WideCharToMultiByte(CP_UTF8, 0, wstr, clen, RSTRING_PTR(src), len, NULL, NULL);
2277  }
2278  switch (encindex) {
2279  case ENCINDEX_ASCII_8BIT:
2280  case ENCINDEX_US_ASCII:
2281  /* assume UTF-8 */
2282  case ENCINDEX_UTF_8:
2283  /* do nothing */
2284  return src;
2285  }
2286  return rb_str_conv_enc_opts(src, NULL, enc, ECONV_UNDEF_REPLACE, Qnil);
2287 }
2288 
2289 /* License: Ruby's */
2290 char *
2291 rb_w32_conv_from_wstr(const WCHAR *wstr, long *lenp, rb_encoding *enc)
2292 {
2293  VALUE str = rb_w32_conv_from_wchar(wstr, enc);
2294  long len;
2295  char *ptr;
2296 
2297  if (NIL_P(str)) return wstr_to_utf8(wstr, lenp);
2298  *lenp = len = RSTRING_LEN(str);
2299  memcpy(ptr = malloc(len + 1), RSTRING_PTR(str), len);
2300  ptr[len] = '\0';
2301  return ptr;
2302 }
2303 
2304 /* License: Ruby's */
2305 static BOOL
2306 ruby_direct_conv(const WCHAR *file, const WCHAR *alt, struct direct *entry, const void *enc)
2307 {
2308  if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
2309  return FALSE;
2310  if (alt && *alt) {
2311  long altlen = 0;
2312  entry->d_altname = rb_w32_conv_from_wstr(alt, &altlen, enc);
2313  entry->d_altlen = altlen;
2314  }
2315  return TRUE;
2316 }
2317 
2318 /* License: Artistic or GPL */
2319 static struct direct *
2320 readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, const WCHAR *, struct direct *, const void *), const void *enc)
2321 {
2322  static long dummy_ino = 0;
2323 
2324  if (dirp->curr) {
2325 
2326  //
2327  // first set up the structure to return
2328  //
2329  free(dirp->dirstr.d_name);
2330  free(dirp->dirstr.d_altname);
2331  dirp->dirstr.d_altname = 0;
2332  dirp->dirstr.d_altlen = 0;
2333  conv(dirp->curr, dirp->curr + lstrlenW(dirp->curr) + 1, &dirp->dirstr, enc);
2334 
2335  //
2336  // Fake inode
2337  //
2338  dirp->dirstr.d_ino = (ino_t)(InterlockedIncrement(&dummy_ino) - 1);
2339 
2340  //
2341  // Attributes
2342  //
2343  /* ignore FILE_ATTRIBUTE_DIRECTORY as unreliable for reparse points */
2344  if (GetBit(dirp->bits, BitOfIsRep(dirp->loc)))
2345  dirp->dirstr.d_type = DT_LNK;
2346  else if (GetBit(dirp->bits, BitOfIsDir(dirp->loc)))
2347  dirp->dirstr.d_type = DT_DIR;
2348  else
2349  dirp->dirstr.d_type = DT_REG;
2350 
2351  //
2352  // Now set up for the next call to readdir
2353  //
2354 
2355  move_to_next_entry(dirp);
2356 
2357  return &(dirp->dirstr);
2358 
2359  }
2360  else
2361  return NULL;
2362 }
2363 
2364 /* License: Ruby's */
2365 struct direct *
2366 rb_w32_readdir(DIR *dirp, rb_encoding *enc)
2367 {
2368  int idx = rb_enc_to_index(enc);
2369  if (idx == ENCINDEX_ASCII_8BIT) {
2370  const UINT cp = filecp();
2371  return readdir_internal(dirp, win32_direct_conv, &cp);
2372  }
2373  else if (idx == ENCINDEX_UTF_8) {
2374  const UINT cp = CP_UTF8;
2375  return readdir_internal(dirp, win32_direct_conv, &cp);
2376  }
2377  else
2378  return readdir_internal(dirp, ruby_direct_conv, enc);
2379 }
2380 
2381 /* License: Ruby's */
2382 struct direct *
2383 rb_w32_ureaddir(DIR *dirp)
2384 {
2385  const UINT cp = CP_UTF8;
2386  return readdir_internal(dirp, win32_direct_conv, &cp);
2387 }
2388 
2389 //
2390 // Telldir returns the current string pointer position
2391 //
2392 
2393 /* License: Artistic or GPL */
2394 long
2395 rb_w32_telldir(DIR *dirp)
2396 {
2397  return dirp->loc;
2398 }
2399 
2400 //
2401 // Seekdir moves the string pointer to a previously saved position
2402 // (Saved by telldir).
2403 
2404 /* License: Ruby's */
2405 void
2406 rb_w32_seekdir(DIR *dirp, long loc)
2407 {
2408  if (dirp->loc > loc) rb_w32_rewinddir(dirp);
2409 
2410  while (dirp->curr && dirp->loc < loc) {
2411  move_to_next_entry(dirp);
2412  }
2413 }
2414 
2415 //
2416 // Rewinddir resets the string pointer to the start
2417 //
2418 
2419 /* License: Artistic or GPL */
2420 void
2421 rb_w32_rewinddir(DIR *dirp)
2422 {
2423  dirp->curr = dirp->start;
2424  dirp->loc = 0;
2425 }
2426 
2427 //
2428 // This just free's the memory allocated by opendir
2429 //
2430 
2431 /* License: Artistic or GPL */
2432 int
2433 rb_w32_closedir(DIR *dirp)
2434 {
2435  if (dirp) {
2436  free(dirp->dirstr.d_name);
2437  free(dirp->dirstr.d_altname);
2438  free(dirp->start);
2439  free(dirp->bits);
2440  free(dirp);
2441  }
2442  return 0;
2443 }
2444 
2445 #if RUBY_MSVCRT_VERSION >= 140
2446 typedef struct {
2447  union
2448  {
2449  FILE _public_file;
2450  char* _ptr;
2451  };
2452 
2453  char* _base;
2454  int _cnt;
2455  long _flags;
2456  long _file;
2457  int _charbuf;
2458  int _bufsiz;
2459  char* _tmpfname;
2460  CRITICAL_SECTION _lock;
2461 } vcruntime_file;
2462 #define FILE_COUNT(stream) ((vcruntime_file*)stream)->_cnt
2463 #define FILE_READPTR(stream) ((vcruntime_file*)stream)->_ptr
2464 #define FILE_FILENO(stream) ((vcruntime_file*)stream)->_file
2465 #else
2466 #define FILE_COUNT(stream) stream->_cnt
2467 #define FILE_READPTR(stream) stream->_ptr
2468 #define FILE_FILENO(stream) stream->_file
2469 #endif
2470 
2471 /* License: Ruby's */
2472 #if RUBY_MSVCRT_VERSION >= 140
2473 typedef char lowio_text_mode;
2474 typedef char lowio_pipe_lookahead[3];
2475 
2476 typedef struct {
2477  CRITICAL_SECTION lock;
2478  intptr_t osfhnd; // underlying OS file HANDLE
2479  __int64 startpos; // File position that matches buffer start
2480  unsigned char osfile; // Attributes of file (e.g., open in text mode?)
2481  lowio_text_mode textmode;
2482  lowio_pipe_lookahead _pipe_lookahead;
2483 
2484  uint8_t unicode : 1; // Was the file opened as unicode?
2485  uint8_t utf8translations : 1; // Buffer contains translations other than CRLF
2486  uint8_t dbcsBufferUsed : 1; // Is the dbcsBuffer in use?
2487  char dbcsBuffer; // Buffer for the lead byte of DBCS when converting from DBCS to Unicode
2488 } ioinfo;
2489 #else
2490 typedef struct {
2491  intptr_t osfhnd; /* underlying OS file HANDLE */
2492  char osfile; /* attributes of file (e.g., open in text mode?) */
2493  char pipech; /* one char buffer for handles opened on pipes */
2494  int lockinitflag;
2495  CRITICAL_SECTION lock;
2496 #if RUBY_MSVCRT_VERSION >= 80
2497  char textmode;
2498  char pipech2[2];
2499 #endif
2500 } ioinfo;
2501 #endif
2502 
2503 #if !defined _CRTIMP || defined __MINGW32__
2504 #undef _CRTIMP
2505 #define _CRTIMP __declspec(dllimport)
2506 #endif
2507 
2508 #if RUBY_MSVCRT_VERSION >= 140
2509 static ioinfo ** __pioinfo = NULL;
2510 #define IOINFO_L2E 6
2511 #else
2512 EXTERN_C _CRTIMP ioinfo * __pioinfo[];
2513 #define IOINFO_L2E 5
2514 #endif
2515 static inline ioinfo* _pioinfo(int);
2516 
2517 
2518 #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
2519 #define _osfhnd(i) (_pioinfo(i)->osfhnd)
2520 #define _osfile(i) (_pioinfo(i)->osfile)
2521 #define rb_acrt_lowio_lock_fh(i) EnterCriticalSection(&_pioinfo(i)->lock)
2522 #define rb_acrt_lowio_unlock_fh(i) LeaveCriticalSection(&_pioinfo(i)->lock)
2523 
2524 #if RUBY_MSVCRT_VERSION >= 80
2525 static size_t pioinfo_extra = 0; /* workaround for VC++8 SP1 */
2526 
2527 /* License: Ruby's */
2528 static void
2529 set_pioinfo_extra(void)
2530 {
2531 #if RUBY_MSVCRT_VERSION >= 140
2532 # define FUNCTION_RET 0xc3 /* ret */
2533 # ifdef _DEBUG
2534 # define UCRTBASE "ucrtbased.dll"
2535 # else
2536 # define UCRTBASE "ucrtbase.dll"
2537 # endif
2538  /* get __pioinfo addr with _isatty */
2539  /*
2540  * Why Ruby depends to _pioinfo is
2541  * * to associate socket and fd: CRuby creates fd with dummy file handle
2542  * and set socket to emulate Unix-like behavior. Without __pioinfo
2543  * we need something which manages the fd number allocation
2544  * * to implement overlapped I/O for Windows 2000/XP
2545  * * to emulate fcntl(2)
2546  *
2547  * see also
2548  * * https://bugs.ruby-lang.org/issues/11118
2549  * * https://bugs.ruby-lang.org/issues/18605
2550  */
2551  char *p = (char*)get_proc_address(UCRTBASE, "_isatty", NULL);
2552  /* _osfile(fh) & FDEV */
2553 
2554 #if defined(_M_ARM64) || defined(__aarch64__)
2555 #define IS_INSN(pc, name) ((*(pc) & name##_mask) == name##_id)
2556  const int max_num_inst = 500;
2557  uint32_t *start = (uint32_t*)p;
2558  uint32_t *end_limit = (start + max_num_inst);
2559  uint32_t *pc = start;
2560 
2561  if (!p) {
2562  fprintf(stderr, "_isatty proc not found in " UCRTBASE "\n");
2563  _exit(1);
2564  }
2565 
2566  /* end of function */
2567  const uint32_t ret_id = 0xd65f0000;
2568  const uint32_t ret_mask = 0xfffffc1f;
2569  for(; pc < end_limit; pc++) {
2570  if (IS_INSN(pc, ret)) {
2571  break;
2572  }
2573  }
2574  if (pc == end_limit) {
2575  fprintf(stderr, "end of _isatty not found in " UCRTBASE "\n");
2576  _exit(1);
2577  }
2578 
2579  /* pioinfo instruction mark */
2580  const uint32_t adrp_id = 0x90000000;
2581  const uint32_t adrp_mask = 0x9f000000;
2582  const uint32_t add_id = 0x11000000;
2583  const uint32_t add_mask = 0x3fc00000;
2584  for(; pc > start; pc--) {
2585  if (IS_INSN(pc, adrp) && IS_INSN(pc + 1, add)) {
2586  break;
2587  }
2588  }
2589  if(pc == start) {
2590  fprintf(stderr, "pioinfo mark not found in " UCRTBASE "\n");
2591  _exit(1);
2592  }
2593 
2594  /* We now point to instructions that load address of __pioinfo:
2595  * adrp x8, 0x1801d8000
2596  * add x8, x8, #0xdb0
2597  * https://devblogs.microsoft.com/oldnewthing/20220809-00/?p=106955
2598  * The last adrp/add sequence before ret is what we are looking for.
2599  */
2600  const uint32_t adrp_insn = *pc;
2601  const uint32_t adrp_immhi = (adrp_insn & 0x00ffffe0) >> 5;
2602  const uint32_t adrp_immlo = (adrp_insn & 0x60000000) >> (5 + 19 + 5);
2603  /* imm = immhi:immlo:Zeros(12), 64 */
2604  const uint64_t adrp_imm = ((adrp_immhi << 2) | adrp_immlo) << 12;
2605  /* base = PC64<63:12>:Zeros(12) */
2606  const uint64_t adrp_base = (uint64_t)pc & 0xfffffffffffff000;
2607 
2608  const uint32_t add_insn = *(pc + 1);
2609  const uint32_t add_sh = (add_insn & 0x400000) >> (12 + 5 + 5);
2610  /* case sh of
2611  when '0' imm = ZeroExtend(imm12, datasize);
2612  when '1' imm = ZeroExtend(imm12:Zeros(12), datasize); */
2613  const uint64_t add_imm = ((add_insn & 0x3ffc00) >> (5 + 5)) << (add_sh ? 12 : 0);
2614 
2615  __pioinfo = (ioinfo**)(adrp_base + adrp_imm + add_imm);
2616 #else /* _M_ARM64 */
2617  char *pend = p;
2618 
2619 # ifdef _WIN64
2620  int32_t rel;
2621  char *rip;
2622  /* add rsp, _ */
2623 # define FUNCTION_BEFORE_RET_MARK "\x48\x83\xc4"
2624 # define FUNCTION_SKIP_BYTES 1
2625 # ifdef _DEBUG
2626  /* lea rcx,[__pioinfo's addr in RIP-relative 32bit addr] */
2627 # define PIOINFO_MARK "\x48\x8d\x0d"
2628 # else
2629  /* lea rdx,[__pioinfo's addr in RIP-relative 32bit addr] */
2630 # define PIOINFO_MARK "\x48\x8d\x15"
2631 # endif
2632 
2633 # else /* x86 */
2634  /* pop ebp */
2635 # define FUNCTION_BEFORE_RET_MARK "\x5d"
2636  /* leave */
2637 # define FUNCTION_BEFORE_RET_MARK_2 "\xc9"
2638 # define FUNCTION_SKIP_BYTES 0
2639  /* mov eax,dword ptr [eax*4+100EB430h] */
2640 # define PIOINFO_MARK "\x8B\x04\x85"
2641 # endif
2642  if (p) {
2643  for (pend += 10; pend < p + 500; pend++) {
2644  // find end of function
2645  if ((memcmp(pend, FUNCTION_BEFORE_RET_MARK, sizeof(FUNCTION_BEFORE_RET_MARK) - 1) == 0
2646 # ifdef FUNCTION_BEFORE_RET_MARK_2
2647  || memcmp(pend, FUNCTION_BEFORE_RET_MARK_2, sizeof(FUNCTION_BEFORE_RET_MARK_2) - 1) == 0
2648 # endif
2649  ) &&
2650  *(pend + (sizeof(FUNCTION_BEFORE_RET_MARK) - 1) + FUNCTION_SKIP_BYTES) == (char)FUNCTION_RET) {
2651  // search backwards from end of function
2652  for (pend -= (sizeof(PIOINFO_MARK) - 1); pend > p; pend--) {
2653  if (memcmp(pend, PIOINFO_MARK, sizeof(PIOINFO_MARK) - 1) == 0) {
2654  p = pend;
2655  goto found;
2656  }
2657  }
2658  break;
2659  }
2660  }
2661  }
2662  fprintf(stderr, "unexpected " UCRTBASE "\n");
2663  _exit(1);
2664 
2665  found:
2666  p += sizeof(PIOINFO_MARK) - 1;
2667 #ifdef _WIN64
2668  rel = *(int32_t*)(p);
2669  rip = p + sizeof(int32_t);
2670  __pioinfo = (ioinfo**)(rip + rel);
2671 #else
2672  __pioinfo = *(ioinfo***)(p);
2673 #endif
2674 #endif /* _M_ARM64 */
2675 #endif /* RUBY_MSVCRT_VERSION */
2676  int fd;
2677 
2678  fd = _open("NUL", O_RDONLY);
2679  for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra += sizeof(void *)) {
2680  if (_osfhnd(fd) == _get_osfhandle(fd)) {
2681  break;
2682  }
2683  }
2684  _close(fd);
2685 
2686  if (pioinfo_extra > 64) {
2687  /* not found, maybe something wrong... */
2688  pioinfo_extra = 0;
2689  }
2690 }
2691 #else
2692 #define pioinfo_extra 0
2693 #endif
2694 
2695 static inline ioinfo*
2696 _pioinfo(int fd)
2697 {
2698  const size_t sizeof_ioinfo = sizeof(ioinfo) + pioinfo_extra;
2699  return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
2700  (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
2701 }
2702 
2703 #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2704 #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
2705 
2706 #define FOPEN 0x01 /* file handle open */
2707 #define FEOFLAG 0x02 /* end of file has been encountered */
2708 #define FPIPE 0x08 /* file handle refers to a pipe */
2709 #define FNOINHERIT 0x10 /* file handle opened O_NOINHERIT */
2710 #define FAPPEND 0x20 /* file handle opened O_APPEND */
2711 #define FDEV 0x40 /* file handle refers to device */
2712 #define FTEXT 0x80 /* file handle is in text mode */
2713 
2714 static int is_socket(SOCKET);
2715 static int is_console(SOCKET);
2716 
2717 /* License: Ruby's */
2718 int
2719 rb_w32_io_cancelable_p(int fd)
2720 {
2721  return is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd));
2722 }
2723 
2724 /* License: Ruby's */
2725 static int
2726 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2727 {
2728  int fh;
2729  char fileflags; /* _osfile flags */
2730  HANDLE hF;
2731 
2732  /* copy relevant flags from second parameter */
2733  fileflags = FDEV;
2734 
2735  if (flags & O_APPEND)
2736  fileflags |= FAPPEND;
2737 
2738  if (flags & O_TEXT)
2739  fileflags |= FTEXT;
2740 
2741  if (flags & O_NOINHERIT)
2742  fileflags |= FNOINHERIT;
2743 
2744  /* attempt to allocate a C Runtime file handle */
2745  hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2746  fh = _open_osfhandle((intptr_t)hF, 0);
2747  CloseHandle(hF);
2748  if (fh == -1) {
2749  errno = EMFILE; /* too many open files */
2750  _doserrno = 0L; /* not an OS error */
2751  }
2752  else {
2753 
2754  rb_acrt_lowio_lock_fh(fh);
2755  /* the file is open. now, set the info in _osfhnd array */
2756  _set_osfhnd(fh, osfhandle);
2757 
2758  fileflags |= FOPEN; /* mark as open */
2759 
2760  _set_osflags(fh, fileflags); /* set osfile entry */
2761  rb_acrt_lowio_unlock_fh(fh);
2762  }
2763  return fh; /* return handle */
2764 }
2765 
2766 /* License: Ruby's */
2767 static void
2768 init_stdhandle(void)
2769 {
2770  int nullfd = -1;
2771  int keep = 0;
2772 #define open_null(fd) \
2773  (((nullfd < 0) ? \
2774  (nullfd = open("NUL", O_RDWR)) : 0), \
2775  ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)), \
2776  (fd))
2777 
2778  if (fileno(stdin) < 0) {
2779  FILE_FILENO(stdin) = open_null(0);
2780  }
2781  else {
2782  setmode(fileno(stdin), O_BINARY);
2783  }
2784  if (fileno(stdout) < 0) {
2785  FILE_FILENO(stdout) = open_null(1);
2786  }
2787  if (fileno(stderr) < 0) {
2788  FILE_FILENO(stderr) = open_null(2);
2789  }
2790  if (nullfd >= 0 && !keep) close(nullfd);
2791  setvbuf(stderr, NULL, _IONBF, 0);
2792 
2793  {
2794  HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
2795  DWORD m;
2796  if (GetConsoleMode(h, &m)) {
2797 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
2798 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
2799 #endif
2800  SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
2801  }
2802  }
2803 }
2804 
2805 #undef getsockopt
2806 
2807 /* License: Ruby's */
2808 static int
2809 is_socket(SOCKET sock)
2810 {
2811  if (socklist_lookup(sock, NULL))
2812  return TRUE;
2813  else
2814  return FALSE;
2815 }
2816 
2817 /* License: Ruby's */
2818 int
2819 rb_w32_is_socket(int fd)
2820 {
2821  return is_socket(TO_SOCKET(fd));
2822 }
2823 
2824 //
2825 // Since the errors returned by the socket error function
2826 // WSAGetLastError() are not known by the library routine strerror
2827 // we have to roll our own.
2828 //
2829 
2830 #undef strerror
2831 
2832 /* License: Artistic or GPL */
2833 char *
2834 rb_w32_strerror(int e)
2835 {
2836  static char buffer[512];
2837  DWORD source = 0;
2838  char *p;
2839 
2840  if (e < 0 || e > sys_nerr) {
2841  if (e < 0)
2842  e = GetLastError();
2843 #if WSAEWOULDBLOCK != EWOULDBLOCK
2844  else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
2845  static int s = -1;
2846  int i;
2847  if (s < 0)
2848  for (s = 0; s < (int)(sizeof(errmap)/sizeof(*errmap)); s++)
2849  if (errmap[s].winerr == WSAEWOULDBLOCK)
2850  break;
2851  for (i = s; i < (int)(sizeof(errmap)/sizeof(*errmap)); i++)
2852  if (errmap[i].err == e) {
2853  e = errmap[i].winerr;
2854  break;
2855  }
2856  }
2857 #endif
2858  if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2859  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
2860  MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
2861  buffer, sizeof(buffer), NULL) == 0 &&
2862  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2863  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
2864  buffer, sizeof(buffer), NULL) == 0)
2865  strlcpy(buffer, "Unknown Error", sizeof(buffer));
2866  }
2867  else
2868  strlcpy(buffer, strerror(e), sizeof(buffer));
2869 
2870  p = buffer;
2871  while ((p = strpbrk(p, "\r\n")) != NULL) {
2872  memmove(p, p + 1, strlen(p));
2873  }
2874  return buffer;
2875 }
2876 
2877 //
2878 // various stubs
2879 //
2880 
2881 
2882 // Ownership
2883 //
2884 // Just pretend that everyone is a superuser. NT will let us know if
2885 // we don't really have permission to do something.
2886 //
2887 
2888 #define ROOT_UID 0
2889 #define ROOT_GID 0
2890 
2891 /* License: Artistic or GPL */
2892 rb_uid_t
2893 getuid(void)
2894 {
2895  return ROOT_UID;
2896 }
2897 
2898 /* License: Artistic or GPL */
2899 rb_uid_t
2900 geteuid(void)
2901 {
2902  return ROOT_UID;
2903 }
2904 
2905 /* License: Artistic or GPL */
2906 rb_gid_t
2907 getgid(void)
2908 {
2909  return ROOT_GID;
2910 }
2911 
2912 /* License: Artistic or GPL */
2913 rb_gid_t
2914 getegid(void)
2915 {
2916  return ROOT_GID;
2917 }
2918 
2919 /* License: Artistic or GPL */
2920 int
2921 setuid(rb_uid_t uid)
2922 {
2923  return (uid == ROOT_UID ? 0 : -1);
2924 }
2925 
2926 /* License: Artistic or GPL */
2927 int
2928 setgid(rb_gid_t gid)
2929 {
2930  return (gid == ROOT_GID ? 0 : -1);
2931 }
2932 
2933 //
2934 // File system stuff
2935 //
2936 
2937 /* License: Artistic or GPL */
2938 int
2939 ioctl(int i, int u, ...)
2940 {
2941  errno = EINVAL;
2942  return -1;
2943 }
2944 
2945 void
2946 rb_w32_fdset(int fd, fd_set *set)
2947 {
2948  FD_SET(fd, set);
2949 }
2950 
2951 #undef FD_CLR
2952 
2953 /* License: Ruby's */
2954 void
2955 rb_w32_fdclr(int fd, fd_set *set)
2956 {
2957  unsigned int i;
2958  SOCKET s = TO_SOCKET(fd);
2959 
2960  for (i = 0; i < set->fd_count; i++) {
2961  if (set->fd_array[i] == s) {
2962  memmove(&set->fd_array[i], &set->fd_array[i+1],
2963  sizeof(set->fd_array[0]) * (--set->fd_count - i));
2964  break;
2965  }
2966  }
2967 }
2968 
2969 #undef FD_ISSET
2970 
2971 /* License: Ruby's */
2972 int
2973 rb_w32_fdisset(int fd, fd_set *set)
2974 {
2975  int ret;
2976  SOCKET s = TO_SOCKET(fd);
2977  if (s == (SOCKET)INVALID_HANDLE_VALUE)
2978  return 0;
2979  RUBY_CRITICAL {ret = __WSAFDIsSet(s, set);}
2980  return ret;
2981 }
2982 
2983 /* License: Ruby's */
2984 void
2985 rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
2986 {
2987  max = min(src->fd_count, (UINT)max);
2988  if ((UINT)dst->capa < (UINT)max) {
2989  dst->capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2990  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2991  }
2992 
2993  memcpy(dst->fdset->fd_array, src->fd_array,
2994  max * sizeof(src->fd_array[0]));
2995  dst->fdset->fd_count = src->fd_count;
2996 }
2997 
2998 /* License: Ruby's */
2999 void
3001 {
3002  if ((UINT)dst->capa < src->fdset->fd_count) {
3003  dst->capa = (src->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
3004  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
3005  }
3006 
3007  memcpy(dst->fdset->fd_array, src->fdset->fd_array,
3008  src->fdset->fd_count * sizeof(src->fdset->fd_array[0]));
3009  dst->fdset->fd_count = src->fdset->fd_count;
3010 }
3011 
3012 //
3013 // Networking trampolines
3014 // These are used to avoid socket startup/shutdown overhead in case
3015 // the socket routines aren't used.
3016 //
3017 
3018 #undef select
3019 
3020 /* License: Ruby's */
3021 static int
3022 extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
3023 {
3024  unsigned int s = 0;
3025  unsigned int m = 0;
3026  if (!src) return 0;
3027 
3028  while (s < src->fd_count) {
3029  SOCKET fd = src->fd_array[s];
3030 
3031  if (!func || (*func)(fd)) {
3032  if (dst) { /* move it to dst */
3033  unsigned int d;
3034 
3035  for (d = 0; d < dst->fdset->fd_count; d++) {
3036  if (dst->fdset->fd_array[d] == fd)
3037  break;
3038  }
3039  if (d == dst->fdset->fd_count) {
3040  if ((int)dst->fdset->fd_count >= dst->capa) {
3041  dst->capa = (dst->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
3042  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
3043  }
3044  dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
3045  }
3046  memmove(
3047  &src->fd_array[s],
3048  &src->fd_array[s+1],
3049  sizeof(src->fd_array[0]) * (--src->fd_count - s));
3050  }
3051  else {
3052  m++;
3053  s++;
3054  }
3055  }
3056  else s++;
3057  }
3058 
3059  return dst ? dst->fdset->fd_count : m;
3060 }
3061 
3062 /* License: Ruby's */
3063 static int
3064 copy_fd(fd_set *dst, fd_set *src)
3065 {
3066  unsigned int s;
3067  if (!src || !dst) return 0;
3068 
3069  for (s = 0; s < src->fd_count; ++s) {
3070  SOCKET fd = src->fd_array[s];
3071  unsigned int d;
3072  for (d = 0; d < dst->fd_count; ++d) {
3073  if (dst->fd_array[d] == fd)
3074  break;
3075  }
3076  if (d == dst->fd_count && d < FD_SETSIZE) {
3077  dst->fd_array[dst->fd_count++] = fd;
3078  }
3079  }
3080 
3081  return dst->fd_count;
3082 }
3083 
3084 /* License: Ruby's */
3085 static int
3086 is_not_socket(SOCKET sock)
3087 {
3088  return !is_socket(sock);
3089 }
3090 
3091 /* License: Ruby's */
3092 static int
3093 is_pipe(SOCKET sock) /* DONT call this for SOCKET! it claims it is PIPE. */
3094 {
3095  int ret;
3096 
3097  RUBY_CRITICAL {
3098  ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
3099  }
3100 
3101  return ret;
3102 }
3103 
3104 /* License: Ruby's */
3105 static int
3106 is_readable_pipe(SOCKET sock) /* call this for pipe only */
3107 {
3108  int ret;
3109  DWORD n = 0;
3110 
3111  RUBY_CRITICAL {
3112  if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
3113  ret = (n > 0);
3114  }
3115  else {
3116  ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
3117  }
3118  }
3119 
3120  return ret;
3121 }
3122 
3123 /* License: Ruby's */
3124 static int
3125 is_console(SOCKET sock) /* DONT call this for SOCKET! */
3126 {
3127  int ret;
3128  DWORD n = 0;
3129  INPUT_RECORD ir;
3130 
3131  RUBY_CRITICAL {
3132  ret = (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n));
3133  }
3134 
3135  return ret;
3136 }
3137 
3138 /* License: Ruby's */
3139 static int
3140 is_readable_console(SOCKET sock) /* call this for console only */
3141 {
3142  int ret = 0;
3143  DWORD n = 0;
3144  INPUT_RECORD ir;
3145 
3146  RUBY_CRITICAL {
3147  if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) {
3148  if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
3149  ir.Event.KeyEvent.uChar.UnicodeChar) {
3150  ret = 1;
3151  }
3152  else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown &&
3153  ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU /* ALT key */ &&
3154  ir.Event.KeyEvent.uChar.UnicodeChar) {
3155  ret = 1;
3156  }
3157  else {
3158  ReadConsoleInputW((HANDLE)sock, &ir, 1, &n);
3159  }
3160  }
3161  }
3162 
3163  return ret;
3164 }
3165 
3166 /* License: Ruby's */
3167 static int
3168 is_invalid_handle(SOCKET sock)
3169 {
3170  return (HANDLE)sock == INVALID_HANDLE_VALUE;
3171 }
3172 
3173 /* License: Artistic or GPL */
3174 static int
3175 do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3176  struct timeval *timeout)
3177 {
3178  int r = 0;
3179 
3180  if (nfds == 0) {
3181  if (timeout)
3182  rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
3183  else
3184  rb_w32_sleep(INFINITE);
3185  }
3186  else {
3187  RUBY_CRITICAL {
3188  thread_exclusive(select) {
3189  r = select(nfds, rd, wr, ex, timeout);
3190  }
3191  if (r == SOCKET_ERROR) {
3192  errno = map_errno(WSAGetLastError());
3193  r = -1;
3194  }
3195  }
3196  }
3197 
3198  return r;
3199 }
3200 
3201 /*
3202  * rest -= wait
3203  * return 0 if rest is smaller than wait.
3204  */
3205 /* License: Ruby's */
3206 int
3207 rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
3208 {
3209  if (rest->tv_sec < wait->tv_sec) {
3210  return 0;
3211  }
3212  while (rest->tv_usec < wait->tv_usec) {
3213  if (rest->tv_sec <= wait->tv_sec) {
3214  return 0;
3215  }
3216  rest->tv_sec -= 1;
3217  rest->tv_usec += 1000 * 1000;
3218  }
3219  rest->tv_sec -= wait->tv_sec;
3220  rest->tv_usec -= wait->tv_usec;
3221  return rest->tv_sec != 0 || rest->tv_usec != 0;
3222 }
3223 
3224 /* License: Ruby's */
3225 static inline int
3226 compare(const struct timeval *t1, const struct timeval *t2)
3227 {
3228  if (t1->tv_sec < t2->tv_sec)
3229  return -1;
3230  if (t1->tv_sec > t2->tv_sec)
3231  return 1;
3232  if (t1->tv_usec < t2->tv_usec)
3233  return -1;
3234  if (t1->tv_usec > t2->tv_usec)
3235  return 1;
3236  return 0;
3237 }
3238 
3239 #undef Sleep
3240 
3241 int rb_w32_check_interrupt(void *); /* @internal */
3242 
3243 /* @internal */
3244 /* License: Ruby's */
3245 int
3246 rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3247  struct timeval *timeout, void *th)
3248 {
3249  int r;
3250  rb_fdset_t pipe_rd;
3251  rb_fdset_t cons_rd;
3252  rb_fdset_t else_rd;
3253  rb_fdset_t else_wr;
3254  rb_fdset_t except;
3255  int nonsock = 0;
3256  struct timeval limit = {0, 0};
3257 
3258  if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
3259  errno = EINVAL;
3260  return -1;
3261  }
3262 
3263  if (timeout) {
3264  if (timeout->tv_sec < 0 ||
3265  timeout->tv_usec < 0 ||
3266  timeout->tv_usec >= 1000000) {
3267  errno = EINVAL;
3268  return -1;
3269  }
3270  gettimeofday(&limit, NULL);
3271  limit.tv_sec += timeout->tv_sec;
3272  limit.tv_usec += timeout->tv_usec;
3273  if (limit.tv_usec >= 1000000) {
3274  limit.tv_usec -= 1000000;
3275  limit.tv_sec++;
3276  }
3277  }
3278 
3279  // assume else_{rd,wr} (other than socket, pipe reader, console reader)
3280  // are always readable/writable. but this implementation still has
3281  // problem. if pipe's buffer is full, writing to pipe will block
3282  // until some data is read from pipe. but ruby is single threaded system,
3283  // so whole system will be blocked forever.
3284 
3285  rb_fd_init(&else_rd);
3286  nonsock += extract_fd(&else_rd, rd, is_not_socket);
3287 
3288  rb_fd_init(&else_wr);
3289  nonsock += extract_fd(&else_wr, wr, is_not_socket);
3290 
3291  // check invalid handles
3292  if (extract_fd(NULL, else_rd.fdset, is_invalid_handle) > 0 ||
3293  extract_fd(NULL, else_wr.fdset, is_invalid_handle) > 0) {
3294  rb_fd_term(&else_wr);
3295  rb_fd_term(&else_rd);
3296  errno = EBADF;
3297  return -1;
3298  }
3299 
3300  rb_fd_init(&pipe_rd);
3301  extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
3302 
3303  rb_fd_init(&cons_rd);
3304  extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
3305 
3306  rb_fd_init(&except);
3307  extract_fd(&except, ex, is_not_socket); // drop only
3308 
3309  r = 0;
3310  if (rd && (int)rd->fd_count > r) r = (int)rd->fd_count;
3311  if (wr && (int)wr->fd_count > r) r = (int)wr->fd_count;
3312  if (ex && (int)ex->fd_count > r) r = (int)ex->fd_count;
3313  if (nfds > r) nfds = r;
3314 
3315  {
3316  struct timeval rest;
3317  const struct timeval wait = {0, 10 * 1000}; // 10ms
3318  struct timeval zero = {0, 0}; // 0ms
3319  for (;;) {
3320  if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
3321  r = -1;
3322  break;
3323  }
3324  if (nonsock) {
3325  // modifying {else,pipe,cons}_rd is safe because
3326  // if they are modified, function returns immediately.
3327  extract_fd(&else_rd, pipe_rd.fdset, is_readable_pipe);
3328  extract_fd(&else_rd, cons_rd.fdset, is_readable_console);
3329  }
3330 
3331  if (else_rd.fdset->fd_count || else_wr.fdset->fd_count) {
3332  r = do_select(nfds, rd, wr, ex, &zero); // polling
3333  if (r < 0) break; // XXX: should I ignore error and return signaled handles?
3334  r += copy_fd(rd, else_rd.fdset);
3335  r += copy_fd(wr, else_wr.fdset);
3336  if (ex)
3337  r += ex->fd_count;
3338  break;
3339  }
3340  else {
3341  const struct timeval *dowait = &wait;
3342 
3343  fd_set orig_rd;
3344  fd_set orig_wr;
3345  fd_set orig_ex;
3346 
3347  FD_ZERO(&orig_rd);
3348  FD_ZERO(&orig_wr);
3349  FD_ZERO(&orig_ex);
3350 
3351  if (rd) copy_fd(&orig_rd, rd);
3352  if (wr) copy_fd(&orig_wr, wr);
3353  if (ex) copy_fd(&orig_ex, ex);
3354  r = do_select(nfds, rd, wr, ex, &zero); // polling
3355  if (r != 0) break; // signaled or error
3356  if (rd) copy_fd(rd, &orig_rd);
3357  if (wr) copy_fd(wr, &orig_wr);
3358  if (ex) copy_fd(ex, &orig_ex);
3359 
3360  if (timeout) {
3361  struct timeval now;
3362  gettimeofday(&now, NULL);
3363  rest = limit;
3364  if (!rb_w32_time_subtract(&rest, &now)) break;
3365  if (compare(&rest, &wait) < 0) dowait = &rest;
3366  }
3367  Sleep(dowait->tv_sec * 1000 + (dowait->tv_usec + 999) / 1000);
3368  }
3369  }
3370  }
3371 
3372  rb_fd_term(&except);
3373  rb_fd_term(&cons_rd);
3374  rb_fd_term(&pipe_rd);
3375  rb_fd_term(&else_wr);
3376  rb_fd_term(&else_rd);
3377 
3378  return r;
3379 }
3380 
3381 /* License: Ruby's */
3382 int WSAAPI
3383 rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3384  struct timeval *timeout)
3385 {
3386  return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
3387 }
3388 
3389 /* License: Ruby's */
3390 static FARPROC
3391 get_wsa_extension_function(SOCKET s, GUID guid)
3392 {
3393  DWORD dmy;
3394  FARPROC ptr = NULL;
3395 
3396  WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
3397  &ptr, sizeof(ptr), &dmy, NULL, NULL);
3398  if (!ptr)
3399  errno = ENOSYS;
3400  return ptr;
3401 }
3402 
3403 #undef accept
3404 
3405 /* License: Artistic or GPL */
3406 int WSAAPI
3407 rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
3408 {
3409  SOCKET r;
3410  int fd;
3411 
3412  RUBY_CRITICAL {
3413  r = accept(TO_SOCKET(s), addr, addrlen);
3414  if (r != INVALID_SOCKET) {
3415  SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
3416  fd = rb_w32_open_osfhandle((intptr_t)r, O_RDWR|O_BINARY|O_NOINHERIT);
3417  if (fd != -1)
3418  socklist_insert(r, 0);
3419  else
3420  closesocket(r);
3421  }
3422  else {
3423  errno = map_errno(WSAGetLastError());
3424  fd = -1;
3425  }
3426  }
3427  return fd;
3428 }
3429 
3430 #undef bind
3431 
3432 /* License: Artistic or GPL */
3433 int WSAAPI
3434 rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
3435 {
3436  int r;
3437 
3438  RUBY_CRITICAL {
3439  r = bind(TO_SOCKET(s), addr, addrlen);
3440  if (r == SOCKET_ERROR)
3441  errno = map_errno(WSAGetLastError());
3442  }
3443  return r;
3444 }
3445 
3446 #undef connect
3447 
3448 /* License: Artistic or GPL */
3449 int WSAAPI
3450 rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
3451 {
3452  int r;
3453  RUBY_CRITICAL {
3454  r = connect(TO_SOCKET(s), addr, addrlen);
3455  if (r == SOCKET_ERROR) {
3456  int err = WSAGetLastError();
3457  if (err != WSAEWOULDBLOCK)
3458  errno = map_errno(err);
3459  else
3460  errno = EINPROGRESS;
3461  }
3462  }
3463  return r;
3464 }
3465 
3466 
3467 #undef getpeername
3468 
3469 /* License: Artistic or GPL */
3470 int WSAAPI
3471 rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
3472 {
3473  int r;
3474  RUBY_CRITICAL {
3475  r = getpeername(TO_SOCKET(s), addr, addrlen);
3476  if (r == SOCKET_ERROR)
3477  errno = map_errno(WSAGetLastError());
3478  }
3479  return r;
3480 }
3481 
3482 #undef getsockname
3483 
3484 /* License: Artistic or GPL */
3485 int WSAAPI
3486 rb_w32_getsockname(int fd, struct sockaddr *addr, int *addrlen)
3487 {
3488  int sock;
3489  int r;
3490  RUBY_CRITICAL {
3491  sock = TO_SOCKET(fd);
3492  r = getsockname(sock, addr, addrlen);
3493  if (r == SOCKET_ERROR) {
3494  DWORD wsaerror = WSAGetLastError();
3495  if (wsaerror == WSAEINVAL) {
3496  int flags;
3497  if (socklist_lookup(sock, &flags)) {
3498  int af = GET_FAMILY(flags);
3499  if (af) {
3500  memset(addr, 0, *addrlen);
3501  addr->sa_family = af;
3502  return 0;
3503  }
3504  }
3505  }
3506  errno = map_errno(wsaerror);
3507  }
3508  }
3509  return r;
3510 }
3511 
3512 #undef getsockopt
3513 
3514 /* License: Artistic or GPL */
3515 int WSAAPI
3516 rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
3517 {
3518  int r;
3519  RUBY_CRITICAL {
3520  r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3521  if (r == SOCKET_ERROR)
3522  errno = map_errno(WSAGetLastError());
3523  }
3524  return r;
3525 }
3526 
3527 #undef ioctlsocket
3528 
3529 /* License: Artistic or GPL */
3530 int WSAAPI
3531 rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
3532 {
3533  int r;
3534  RUBY_CRITICAL {
3535  r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3536  if (r == SOCKET_ERROR)
3537  errno = map_errno(WSAGetLastError());
3538  }
3539  return r;
3540 }
3541 
3542 #undef listen
3543 
3544 /* License: Artistic or GPL */
3545 int WSAAPI
3546 rb_w32_listen(int s, int backlog)
3547 {
3548  int r;
3549  RUBY_CRITICAL {
3550  r = listen(TO_SOCKET(s), backlog);
3551  if (r == SOCKET_ERROR)
3552  errno = map_errno(WSAGetLastError());
3553  }
3554  return r;
3555 }
3556 
3557 #undef recv
3558 #undef recvfrom
3559 #undef send
3560 #undef sendto
3561 
3562 /* License: Ruby's */
3563 static int
3564 finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
3565 {
3566  DWORD flg;
3567  int err;
3568 
3569  if (result != SOCKET_ERROR)
3570  *len = size;
3571  else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
3572  switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
3573  case WAIT_OBJECT_0:
3574  RUBY_CRITICAL {
3575  result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg);
3576  }
3577  if (result) {
3578  result = 0;
3579  *len = size;
3580  break;
3581  }
3582  result = SOCKET_ERROR;
3583  /* thru */
3584  default:
3585  if ((err = WSAGetLastError()) == WSAECONNABORTED && !input)
3586  errno = EPIPE;
3587  else if (err == WSAEMSGSIZE && input) {
3588  result = 0;
3589  *len = size;
3590  break;
3591  }
3592  else
3593  errno = map_errno(err);
3594  /* thru */
3595  case WAIT_OBJECT_0 + 1:
3596  /* interrupted */
3597  *len = -1;
3598  CancelIo((HANDLE)s);
3599  break;
3600  }
3601  }
3602  else {
3603  if (err == WSAECONNABORTED && !input)
3604  errno = EPIPE;
3605  else
3606  errno = map_errno(err);
3607  *len = -1;
3608  }
3609  CloseHandle(wol->hEvent);
3610 
3611  return result;
3612 }
3613 
3614 /* License: Artistic or GPL */
3615 static int
3616 overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
3617  struct sockaddr *addr, int *addrlen)
3618 {
3619  int r;
3620  int ret;
3621  int mode = 0;
3622  DWORD flg;
3623  WSAOVERLAPPED wol;
3624  WSABUF wbuf;
3625  SOCKET s;
3626 
3627  s = TO_SOCKET(fd);
3628  socklist_lookup(s, &mode);
3629  if (GET_FLAGS(mode) & O_NONBLOCK) {
3630  RUBY_CRITICAL {
3631  if (input) {
3632  if (addr && addrlen)
3633  r = recvfrom(s, buf, len, flags, addr, addrlen);
3634  else
3635  r = recv(s, buf, len, flags);
3636  if (r == SOCKET_ERROR)
3637  errno = map_errno(WSAGetLastError());
3638  }
3639  else {
3640  if (addr && addrlen)
3641  r = sendto(s, buf, len, flags, addr, *addrlen);
3642  else
3643  r = send(s, buf, len, flags);
3644  if (r == SOCKET_ERROR) {
3645  DWORD err = WSAGetLastError();
3646  if (err == WSAECONNABORTED)
3647  errno = EPIPE;
3648  else
3649  errno = map_errno(err);
3650  }
3651  }
3652  }
3653  }
3654  else {
3655  DWORD size;
3656  DWORD rlen;
3657  wbuf.len = len;
3658  wbuf.buf = buf;
3659  memset(&wol, 0, sizeof(wol));
3660  RUBY_CRITICAL {
3661  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3662  if (input) {
3663  flg = flags;
3664  if (addr && addrlen)
3665  ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3666  &wol, NULL);
3667  else
3668  ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3669  }
3670  else {
3671  if (addr && addrlen)
3672  ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3673  &wol, NULL);
3674  else
3675  ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3676  }
3677  }
3678 
3679  finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3680  r = (int)rlen;
3681  }
3682 
3683  return r;
3684 }
3685 
3686 /* License: Ruby's */
3687 int WSAAPI
3688 rb_w32_recv(int fd, char *buf, int len, int flags)
3689 {
3690  return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
3691 }
3692 
3693 /* License: Ruby's */
3694 int WSAAPI
3695 rb_w32_recvfrom(int fd, char *buf, int len, int flags,
3696  struct sockaddr *from, int *fromlen)
3697 {
3698  return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
3699 }
3700 
3701 /* License: Ruby's */
3702 int WSAAPI
3703 rb_w32_send(int fd, const char *buf, int len, int flags)
3704 {
3705  return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
3706 }
3707 
3708 /* License: Ruby's */
3709 int WSAAPI
3710 rb_w32_sendto(int fd, const char *buf, int len, int flags,
3711  const struct sockaddr *to, int tolen)
3712 {
3713  return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
3714  (struct sockaddr *)to, &tolen);
3715 }
3716 
3717 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3718 /* License: Ruby's */
3719 typedef struct {
3720  SOCKADDR *name;
3721  int namelen;
3722  WSABUF *lpBuffers;
3723  DWORD dwBufferCount;
3724  WSABUF Control;
3725  DWORD dwFlags;
3726 } WSAMSG;
3727 #endif
3728 #ifndef WSAID_WSARECVMSG
3729 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3730 #endif
3731 #ifndef WSAID_WSASENDMSG
3732 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3733 #endif
3734 
3735 /* License: Ruby's */
3736 #define msghdr_to_wsamsg(msg, wsamsg) \
3737  do { \
3738  int i; \
3739  (wsamsg)->name = (msg)->msg_name; \
3740  (wsamsg)->namelen = (msg)->msg_namelen; \
3741  (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3742  (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3743  for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3744  (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3745  (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3746  } \
3747  (wsamsg)->Control.buf = (msg)->msg_control; \
3748  (wsamsg)->Control.len = (msg)->msg_controllen; \
3749  (wsamsg)->dwFlags = (msg)->msg_flags; \
3750  } while (0)
3751 
3752 /* License: Ruby's */
3753 int
3754 recvmsg(int fd, struct msghdr *msg, int flags)
3755 {
3756  typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3757  static WSARecvMsg_t pWSARecvMsg = NULL;
3758  WSAMSG wsamsg;
3759  SOCKET s;
3760  int mode = 0;
3761  DWORD len;
3762  int ret;
3763 
3764  s = TO_SOCKET(fd);
3765 
3766  if (!pWSARecvMsg) {
3767  static const GUID guid = WSAID_WSARECVMSG;
3768  pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, guid);
3769  if (!pWSARecvMsg)
3770  return -1;
3771  }
3772 
3773  msghdr_to_wsamsg(msg, &wsamsg);
3774  wsamsg.dwFlags |= flags;
3775 
3776  socklist_lookup(s, &mode);
3777  if (GET_FLAGS(mode) & O_NONBLOCK) {
3778  RUBY_CRITICAL {
3779  if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
3780  errno = map_errno(WSAGetLastError());
3781  len = -1;
3782  }
3783  }
3784  }
3785  else {
3786  DWORD size;
3787  WSAOVERLAPPED wol;
3788  memset(&wol, 0, sizeof(wol));
3789  RUBY_CRITICAL {
3790  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3791  ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3792  }
3793 
3794  ret = finish_overlapped_socket(TRUE, s, &wol, ret, &len, size);
3795  }
3796  if (ret == SOCKET_ERROR)
3797  return -1;
3798 
3799  /* WSAMSG to msghdr */
3800  msg->msg_name = wsamsg.name;
3801  msg->msg_namelen = wsamsg.namelen;
3802  msg->msg_flags = wsamsg.dwFlags;
3803 
3804  return len;
3805 }
3806 
3807 /* License: Ruby's */
3808 int
3809 sendmsg(int fd, const struct msghdr *msg, int flags)
3810 {
3811  typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3812  static WSASendMsg_t pWSASendMsg = NULL;
3813  WSAMSG wsamsg;
3814  SOCKET s;
3815  int mode = 0;
3816  DWORD len;
3817  int ret;
3818 
3819  s = TO_SOCKET(fd);
3820 
3821  if (!pWSASendMsg) {
3822  static const GUID guid = WSAID_WSASENDMSG;
3823  pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, guid);
3824  if (!pWSASendMsg)
3825  return -1;
3826  }
3827 
3828  msghdr_to_wsamsg(msg, &wsamsg);
3829 
3830  socklist_lookup(s, &mode);
3831  if (GET_FLAGS(mode) & O_NONBLOCK) {
3832  RUBY_CRITICAL {
3833  if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
3834  errno = map_errno(WSAGetLastError());
3835  len = -1;
3836  }
3837  }
3838  }
3839  else {
3840  DWORD size;
3841  WSAOVERLAPPED wol;
3842  memset(&wol, 0, sizeof(wol));
3843  RUBY_CRITICAL {
3844  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3845  ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3846  }
3847 
3848  finish_overlapped_socket(FALSE, s, &wol, ret, &len, size);
3849  }
3850 
3851  return len;
3852 }
3853 
3854 #undef setsockopt
3855 
3856 /* License: Artistic or GPL */
3857 int WSAAPI
3858 rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
3859 {
3860  int r;
3861  RUBY_CRITICAL {
3862  r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3863  if (r == SOCKET_ERROR)
3864  errno = map_errno(WSAGetLastError());
3865  }
3866  return r;
3867 }
3868 
3869 #undef shutdown
3870 
3871 /* License: Artistic or GPL */
3872 int WSAAPI
3873 rb_w32_shutdown(int s, int how)
3874 {
3875  int r;
3876  RUBY_CRITICAL {
3877  r = shutdown(TO_SOCKET(s), how);
3878  if (r == SOCKET_ERROR)
3879  errno = map_errno(WSAGetLastError());
3880  }
3881  return r;
3882 }
3883 
3884 /* License: Ruby's */
3885 static SOCKET
3886 open_ifs_socket(int af, int type, int protocol)
3887 {
3888  unsigned long proto_buffers_len = 0;
3889  int error_code;
3890  SOCKET out = INVALID_SOCKET;
3891 
3892  if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3893  error_code = WSAGetLastError();
3894  if (error_code == WSAENOBUFS) {
3895  WSAPROTOCOL_INFO *proto_buffers;
3896  int protocols_available = 0;
3897 
3898  proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3899  if (!proto_buffers) {
3900  WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3901  return INVALID_SOCKET;
3902  }
3903 
3904  protocols_available =
3905  WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3906  if (protocols_available != SOCKET_ERROR) {
3907  int i;
3908  for (i = 0; i < protocols_available; i++) {
3909  if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3910  (type != proto_buffers[i].iSocketType) ||
3911  (protocol != 0 && protocol != proto_buffers[i].iProtocol))
3912  continue;
3913 
3914  if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3915  continue;
3916 
3917  out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
3918  WSA_FLAG_OVERLAPPED);
3919  break;
3920  }
3921  if (out == INVALID_SOCKET)
3922  out = WSASocket(af, type, protocol, NULL, 0, 0);
3923  if (out != INVALID_SOCKET)
3924  SetHandleInformation((HANDLE)out, HANDLE_FLAG_INHERIT, 0);
3925  }
3926 
3927  free(proto_buffers);
3928  }
3929  }
3930 
3931  return out;
3932 }
3933 
3934 #undef socket
3935 
3936 /* License: Artistic or GPL */
3937 int WSAAPI
3938 rb_w32_socket(int af, int type, int protocol)
3939 {
3940  SOCKET s;
3941  int fd;
3942 
3943  RUBY_CRITICAL {
3944  s = open_ifs_socket(af, type, protocol);
3945  if (s == INVALID_SOCKET) {
3946  errno = map_errno(WSAGetLastError());
3947  fd = -1;
3948  }
3949  else {
3950  fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3951  if (fd != -1)
3952  socklist_insert(s, MAKE_SOCKDATA(af, 0));
3953  else
3954  closesocket(s);
3955  }
3956  }
3957  return fd;
3958 }
3959 
3960 #undef gethostbyaddr
3961 
3962 /* License: Artistic or GPL */
3963 struct hostent * WSAAPI
3964 rb_w32_gethostbyaddr(const char *addr, int len, int type)
3965 {
3966  struct hostent *r;
3967  RUBY_CRITICAL {
3968  r = gethostbyaddr(addr, len, type);
3969  if (r == NULL)
3970  errno = map_errno(WSAGetLastError());
3971  }
3972  return r;
3973 }
3974 
3975 #undef gethostbyname
3976 
3977 /* License: Artistic or GPL */
3978 struct hostent * WSAAPI
3979 rb_w32_gethostbyname(const char *name)
3980 {
3981  struct hostent *r;
3982  RUBY_CRITICAL {
3983  r = gethostbyname(name);
3984  if (r == NULL)
3985  errno = map_errno(WSAGetLastError());
3986  }
3987  return r;
3988 }
3989 
3990 #undef gethostname
3991 
3992 /* License: Artistic or GPL */
3993 int WSAAPI
3994 rb_w32_gethostname(char *name, int len)
3995 {
3996  int r;
3997  RUBY_CRITICAL {
3998  r = gethostname(name, len);
3999  if (r == SOCKET_ERROR)
4000  errno = map_errno(WSAGetLastError());
4001  }
4002  return r;
4003 }
4004 
4005 #undef getprotobyname
4006 
4007 /* License: Artistic or GPL */
4008 struct protoent * WSAAPI
4009 rb_w32_getprotobyname(const char *name)
4010 {
4011  struct protoent *r;
4012  RUBY_CRITICAL {
4013  r = getprotobyname(name);
4014  if (r == NULL)
4015  errno = map_errno(WSAGetLastError());
4016  }
4017  return r;
4018 }
4019 
4020 #undef getprotobynumber
4021 
4022 /* License: Artistic or GPL */
4023 struct protoent * WSAAPI
4024 rb_w32_getprotobynumber(int num)
4025 {
4026  struct protoent *r;
4027  RUBY_CRITICAL {
4028  r = getprotobynumber(num);
4029  if (r == NULL)
4030  errno = map_errno(WSAGetLastError());
4031  }
4032  return r;
4033 }
4034 
4035 #undef getservbyname
4036 
4037 /* License: Artistic or GPL */
4038 struct servent * WSAAPI
4039 rb_w32_getservbyname(const char *name, const char *proto)
4040 {
4041  struct servent *r;
4042  RUBY_CRITICAL {
4043  r = getservbyname(name, proto);
4044  if (r == NULL)
4045  errno = map_errno(WSAGetLastError());
4046  }
4047  return r;
4048 }
4049 
4050 #undef getservbyport
4051 
4052 /* License: Artistic or GPL */
4053 struct servent * WSAAPI
4054 rb_w32_getservbyport(int port, const char *proto)
4055 {
4056  struct servent *r;
4057  RUBY_CRITICAL {
4058  r = getservbyport(port, proto);
4059  if (r == NULL)
4060  errno = map_errno(WSAGetLastError());
4061  }
4062  return r;
4063 }
4064 
4065 #ifdef HAVE_AFUNIX_H
4066 
4067 /* License: Ruby's */
4068 static size_t
4069 socketpair_unix_path(struct sockaddr_un *sock_un)
4070 {
4071  SOCKET listener;
4072  WCHAR wpath[sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path)] = L"";
4073 
4074  /* AF_UNIX/SOCK_STREAM became available in Windows 10
4075  * See https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows
4076  */
4077  listener = socket(AF_UNIX, SOCK_STREAM, 0);
4078  if (listener == INVALID_SOCKET)
4079  return 0;
4080 
4081  memset(sock_un, 0, sizeof(*sock_un));
4082  sock_un->sun_family = AF_UNIX;
4083 
4084  /* Abstract sockets (filesystem-independent) don't work, contrary to
4085  * the claims of the aforementioned blog post:
4086  * https://github.com/microsoft/WSL/issues/4240#issuecomment-549663217
4087  *
4088  * So we must use a named path, and that comes with all the attendant
4089  * problems of permissions and collisions. Trying various temporary
4090  * directories and putting high-res time and PID in the filename.
4091  */
4092  for (int try = 0; ; try++) {
4093  LARGE_INTEGER ticks;
4094  size_t path_len = 0;
4095  const size_t maxpath = sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path);
4096 
4097  switch (try) {
4098  case 0:
4099  /* user temp dir from TMP or TEMP env var, it ends with a backslash */
4100  path_len = GetTempPathW(maxpath, wpath);
4101  break;
4102  case 1:
4103  wcsncpy(wpath, L"C:/Temp/", maxpath);
4104  path_len = lstrlenW(wpath);
4105  break;
4106  case 2:
4107  /* Current directory */
4108  path_len = 0;
4109  break;
4110  case 3:
4111  closesocket(listener);
4112  return 0;
4113  }
4114 
4115  /* Windows UNIXSocket implementation expects UTF-8 instead of UTF16 */
4116  path_len = WideCharToMultiByte(CP_UTF8, 0, wpath, path_len, sock_un->sun_path, maxpath, NULL, NULL);
4117  QueryPerformanceCounter(&ticks);
4118  path_len += snprintf(sock_un->sun_path + path_len,
4119  maxpath - path_len,
4120  "%lld-%ld.($)",
4121  ticks.QuadPart,
4122  GetCurrentProcessId());
4123 
4124  /* Convert to UTF16 for DeleteFileW */
4125  MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
4126 
4127  if (bind(listener, (struct sockaddr *)sock_un, sizeof(*sock_un)) != SOCKET_ERROR)
4128  break;
4129  }
4130  closesocket(listener);
4131  DeleteFileW(wpath);
4132  return sizeof(*sock_un);
4133 }
4134 #endif
4135 
4136 /* License: Ruby's */
4137 static int
4138 socketpair_internal(int af, int type, int protocol, SOCKET *sv)
4139 {
4140  SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
4141  struct sockaddr_in sock_in4;
4142 
4143 #ifdef INET6
4144  struct sockaddr_in6 sock_in6;
4145 #endif
4146 
4147 #ifdef HAVE_AFUNIX_H
4148  struct sockaddr_un sock_un = {0, {0}};
4149  WCHAR wpath[sizeof(sock_un.sun_path)/sizeof(*sock_un.sun_path)] = L"";
4150 #endif
4151 
4152  struct sockaddr *addr;
4153  int ret = -1;
4154  int len;
4155 
4156  switch (af) {
4157  case AF_INET:
4158 #if defined PF_INET && PF_INET != AF_INET
4159  case PF_INET:
4160 #endif
4161  sock_in4.sin_family = AF_INET;
4162  sock_in4.sin_port = 0;
4163  sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4164  addr = (struct sockaddr *)&sock_in4;
4165  len = sizeof(sock_in4);
4166  break;
4167 #ifdef INET6
4168  case AF_INET6:
4169  memset(&sock_in6, 0, sizeof(sock_in6));
4170  sock_in6.sin6_family = AF_INET6;
4171  sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
4172  addr = (struct sockaddr *)&sock_in6;
4173  len = sizeof(sock_in6);
4174  break;
4175 #endif
4176 #ifdef HAVE_AFUNIX_H
4177  case AF_UNIX:
4178  addr = (struct sockaddr *)&sock_un;
4179  len = socketpair_unix_path(&sock_un);
4180  MultiByteToWideChar(CP_UTF8, 0, sock_un.sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
4181  if (len)
4182  break;
4183  /* fall through */
4184 #endif
4185  default:
4186  errno = EAFNOSUPPORT;
4187  return -1;
4188  }
4189  if (type != SOCK_STREAM) {
4190  errno = EPROTOTYPE;
4191  return -1;
4192  }
4193 
4194  sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
4195  sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
4196  RUBY_CRITICAL {
4197  do {
4198  svr = open_ifs_socket(af, type, protocol);
4199  if (svr == INVALID_SOCKET)
4200  break;
4201  if (bind(svr, addr, len) < 0)
4202  break;
4203  if (getsockname(svr, addr, &len) < 0)
4204  break;
4205  if (type == SOCK_STREAM)
4206  listen(svr, 5);
4207 
4208  w = open_ifs_socket(af, type, protocol);
4209  if (w == INVALID_SOCKET)
4210  break;
4211  if (connect(w, addr, len) < 0)
4212  break;
4213 
4214  r = accept(svr, addr, &len);
4215  if (r == INVALID_SOCKET)
4216  break;
4217  SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
4218 
4219  ret = 0;
4220  } while (0);
4221 
4222  if (ret < 0) {
4223  errno = map_errno(WSAGetLastError());
4224  if (r != INVALID_SOCKET)
4225  closesocket(r);
4226  if (w != INVALID_SOCKET)
4227  closesocket(w);
4228  }
4229  else {
4230  sv[0] = r;
4231  sv[1] = w;
4232  }
4233  if (svr != INVALID_SOCKET)
4234  closesocket(svr);
4235 #ifdef HAVE_AFUNIX_H
4236  if (sock_un.sun_family == AF_UNIX)
4237  DeleteFileW(wpath);
4238 #endif
4239  }
4240 
4241  return ret;
4242 }
4243 
4244 /* License: Ruby's */
4245 int
4246 socketpair(int af, int type, int protocol, int *sv)
4247 {
4248  SOCKET pair[2];
4249 
4250  if (socketpair_internal(af, type, protocol, pair) < 0)
4251  return -1;
4252  sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
4253  if (sv[0] == -1) {
4254  closesocket(pair[0]);
4255  closesocket(pair[1]);
4256  return -1;
4257  }
4258  sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
4259  if (sv[1] == -1) {
4260  rb_w32_close(sv[0]);
4261  closesocket(pair[1]);
4262  return -1;
4263  }
4264  socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
4265  socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
4266 
4267  return 0;
4268 }
4269 
4270 #if !defined(_MSC_VER) || _MSC_VER >= 1400
4271 /* License: Ruby's */
4272 static void
4273 str2guid(const char *str, GUID *guid)
4274 {
4275 #define hex2byte(str) \
4276  ((isdigit(*(str)) ? *(str) - '0' : toupper(*(str)) - 'A' + 10) << 4 | (isdigit(*((str) + 1)) ? *((str) + 1) - '0' : toupper(*((str) + 1)) - 'A' + 10))
4277  char *end;
4278  int i;
4279  if (*str == '{') str++;
4280  guid->Data1 = (long)strtoul(str, &end, 16);
4281  str += 9;
4282  guid->Data2 = (unsigned short)strtoul(str, &end, 16);
4283  str += 5;
4284  guid->Data3 = (unsigned short)strtoul(str, &end, 16);
4285  str += 5;
4286  guid->Data4[0] = hex2byte(str);
4287  str += 2;
4288  guid->Data4[1] = hex2byte(str);
4289  str += 3;
4290  for (i = 0; i < 6; i++) {
4291  guid->Data4[i + 2] = hex2byte(str);
4292  str += 2;
4293  }
4294 }
4295 
4296 /* License: Ruby's */
4297 #ifndef HAVE_TYPE_NET_LUID
4298  typedef struct {
4299  uint64_t Value;
4300  struct {
4301  uint64_t Reserved :24;
4302  uint64_t NetLuidIndex :24;
4303  uint64_t IfType :16;
4304  } Info;
4305  } NET_LUID;
4306 #endif
4307 
4308 int
4309 getifaddrs(struct ifaddrs **ifap)
4310 {
4311  ULONG size = 0;
4312  ULONG ret;
4313  IP_ADAPTER_ADDRESSES *root, *addr;
4314  struct ifaddrs *prev;
4315 
4316  ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
4317  if (ret != ERROR_BUFFER_OVERFLOW) {
4318  errno = map_errno(ret);
4319  return -1;
4320  }
4321  root = ruby_xmalloc(size);
4322  ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, root, &size);
4323  if (ret != ERROR_SUCCESS) {
4324  errno = map_errno(ret);
4325  ruby_xfree(root);
4326  return -1;
4327  }
4328 
4329  for (prev = NULL, addr = root; addr; addr = addr->Next) {
4330  struct ifaddrs *ifa = ruby_xcalloc(1, sizeof(*ifa));
4331  char name[IFNAMSIZ];
4332  GUID guid;
4333  NET_LUID luid;
4334 
4335  if (prev)
4336  prev->ifa_next = ifa;
4337  else
4338  *ifap = ifa;
4339 
4340  str2guid(addr->AdapterName, &guid);
4341  if (ConvertInterfaceGuidToLuid(&guid, &luid) == NO_ERROR &&
4342  ConvertInterfaceLuidToNameA(&luid, name, sizeof(name)) == NO_ERROR) {
4343  ifa->ifa_name = ruby_strdup(name);
4344  }
4345  else {
4346  ifa->ifa_name = ruby_strdup(addr->AdapterName);
4347  }
4348 
4349  if (addr->IfType & IF_TYPE_SOFTWARE_LOOPBACK)
4350  ifa->ifa_flags |= IFF_LOOPBACK;
4351  if (addr->OperStatus == IfOperStatusUp) {
4352  ifa->ifa_flags |= IFF_UP;
4353 
4354  if (addr->FirstUnicastAddress) {
4355  IP_ADAPTER_UNICAST_ADDRESS *cur;
4356  int added = 0;
4357  for (cur = addr->FirstUnicastAddress; cur; cur = cur->Next) {
4358  if (cur->Flags & IP_ADAPTER_ADDRESS_TRANSIENT ||
4359  cur->DadState == IpDadStateDeprecated) {
4360  continue;
4361  }
4362  if (added) {
4363  prev = ifa;
4364  ifa = ruby_xcalloc(1, sizeof(*ifa));
4365  prev->ifa_next = ifa;
4366  ifa->ifa_name = ruby_strdup(prev->ifa_name);
4367  ifa->ifa_flags = prev->ifa_flags;
4368  }
4369  ifa->ifa_addr = ruby_xmalloc(cur->Address.iSockaddrLength);
4370  memcpy(ifa->ifa_addr, cur->Address.lpSockaddr,
4371  cur->Address.iSockaddrLength);
4372  added = 1;
4373  }
4374  }
4375  }
4376 
4377  prev = ifa;
4378  }
4379 
4380  ruby_xfree(root);
4381  return 0;
4382 }
4383 
4384 /* License: Ruby's */
4385 void
4386 freeifaddrs(struct ifaddrs *ifp)
4387 {
4388  while (ifp) {
4389  struct ifaddrs *next = ifp->ifa_next;
4390  ruby_xfree(ifp->ifa_addr);
4391  ruby_xfree(ifp->ifa_name);
4392  ruby_xfree(ifp);
4393  ifp = next;
4394  }
4395 }
4396 #endif
4397 
4398 #if 0 // Have never been used
4399 //
4400 // Networking stubs
4401 //
4402 
4403 void endhostent(void) {}
4404 void endnetent(void) {}
4405 void endprotoent(void) {}
4406 void endservent(void) {}
4407 
4408 struct netent *getnetent (void) {return (struct netent *) NULL;}
4409 
4410 struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
4411 
4412 struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
4413 
4414 struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
4415 
4416 struct servent *getservent (void) {return (struct servent *) NULL;}
4417 
4418 void sethostent (int stayopen) {}
4419 
4420 void setnetent (int stayopen) {}
4421 
4422 void setprotoent (int stayopen) {}
4423 
4424 void setservent (int stayopen) {}
4425 #endif
4426 
4427 int rb_w32_set_nonblock2(int fd, int nonblock);
4428 
4429 /* License: Ruby's */
4430 static int
4431 setfl(SOCKET sock, int arg)
4432 {
4433  int ret;
4434  int af = 0;
4435  int flag = 0;
4436  u_long ioctlArg;
4437 
4438  socklist_lookup(sock, &flag);
4439  af = GET_FAMILY(flag);
4440  flag = GET_FLAGS(flag);
4441  if (arg & O_NONBLOCK) {
4442  flag |= O_NONBLOCK;
4443  ioctlArg = 1;
4444  }
4445  else {
4446  flag &= ~O_NONBLOCK;
4447  ioctlArg = 0;
4448  }
4449  RUBY_CRITICAL {
4450  ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
4451  if (ret == 0)
4452  socklist_insert(sock, MAKE_SOCKDATA(af, flag));
4453  else
4454  errno = map_errno(WSAGetLastError());
4455  }
4456 
4457  return ret;
4458 }
4459 
4460 /* License: Ruby's */
4461 static int
4462 dupfd(HANDLE hDup, int flags, int minfd)
4463 {
4464  int save_errno;
4465  int ret;
4466  int fds[32];
4467  int filled = 0;
4468 
4469  do {
4470  ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
4471  if (ret == -1) {
4472  goto close_fds_and_return;
4473  }
4474  if (ret >= minfd) {
4475  goto close_fds_and_return;
4476  }
4477  fds[filled++] = ret;
4478  } while (filled < (int)numberof(fds));
4479 
4480  ret = dupfd(hDup, flags, minfd);
4481 
4482  close_fds_and_return:
4483  save_errno = errno;
4484  while (filled > 0) {
4485  int fd = fds[--filled];
4486  _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
4487  close(fd);
4488  }
4489  errno = save_errno;
4490 
4491  return ret;
4492 }
4493 
4494 /* License: Ruby's */
4495 int
4496 fcntl(int fd, int cmd, ...)
4497 {
4498  va_list va;
4499  int arg;
4500  DWORD flag;
4501 
4502  switch (cmd) {
4503  case F_SETFL: {
4504  va_start(va, cmd);
4505  arg = va_arg(va, int);
4506  va_end(va);
4507  return rb_w32_set_nonblock2(fd, arg);
4508  }
4509  case F_DUPFD: case F_DUPFD_CLOEXEC: {
4510  int ret;
4511  HANDLE hDup;
4512  flag = _osfile(fd);
4513  if (!(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
4514  GetCurrentProcess(), &hDup, 0L,
4515  cmd == F_DUPFD && !(flag & FNOINHERIT),
4516  DUPLICATE_SAME_ACCESS))) {
4517  errno = map_errno(GetLastError());
4518  return -1;
4519  }
4520 
4521  va_start(va, cmd);
4522  arg = va_arg(va, int);
4523  va_end(va);
4524 
4525  if (cmd != F_DUPFD)
4526  flag |= FNOINHERIT;
4527  else
4528  flag &= ~FNOINHERIT;
4529  if ((ret = dupfd(hDup, flag, arg)) == -1)
4530  CloseHandle(hDup);
4531  return ret;
4532  }
4533  case F_GETFD: {
4534  SIGNED_VALUE h = _get_osfhandle(fd);
4535  if (h == -1) return -1;
4536  if (!GetHandleInformation((HANDLE)h, &flag)) {
4537  errno = map_errno(GetLastError());
4538  return -1;
4539  }
4540  return (flag & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
4541  }
4542  case F_SETFD: {
4543  SIGNED_VALUE h = _get_osfhandle(fd);
4544  if (h == -1) return -1;
4545  va_start(va, cmd);
4546  arg = va_arg(va, int);
4547  va_end(va);
4548  if (!SetHandleInformation((HANDLE)h, HANDLE_FLAG_INHERIT,
4549  (arg & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT)) {
4550  errno = map_errno(GetLastError());
4551  return -1;
4552  }
4553  if (arg & FD_CLOEXEC)
4554  _osfile(fd) |= FNOINHERIT;
4555  else
4556  _osfile(fd) &= ~FNOINHERIT;
4557  return 0;
4558  }
4559  default:
4560  errno = EINVAL;
4561  return -1;
4562  }
4563 }
4564 
4565 /* License: Ruby's */
4566 int
4567 rb_w32_set_nonblock2(int fd, int nonblock)
4568 {
4569  SOCKET sock = TO_SOCKET(fd);
4570  if (is_socket(sock)) {
4571  return setfl(sock, nonblock ? O_NONBLOCK : 0);
4572  }
4573  else if (is_pipe(sock)) {
4574  DWORD state;
4575  if (!GetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL, NULL, NULL, 0)) {
4576  errno = map_errno(GetLastError());
4577  return -1;
4578  }
4579  if (nonblock) {
4580  state |= PIPE_NOWAIT;
4581  }
4582  else {
4583  state &= ~PIPE_NOWAIT;
4584  }
4585  if (!SetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL)) {
4586  errno = map_errno(GetLastError());
4587  return -1;
4588  }
4589  return 0;
4590  }
4591  else {
4592  errno = EBADF;
4593  return -1;
4594  }
4595 }
4596 
4597 int
4598 rb_w32_set_nonblock(int fd)
4599 {
4600  return rb_w32_set_nonblock2(fd, TRUE);
4601 }
4602 
4603 #ifndef WNOHANG
4604 #define WNOHANG -1
4605 #endif
4606 
4607 /* License: Ruby's */
4608 static rb_pid_t
4609 poll_child_status(struct ChildRecord *child, int *stat_loc)
4610 {
4611  DWORD exitcode;
4612  DWORD err;
4613 
4614  if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
4615  /* If an error occurred, return immediately. */
4616  err = GetLastError();
4617  switch (err) {
4618  case ERROR_INVALID_PARAMETER:
4619  errno = ECHILD;
4620  break;
4621  case ERROR_INVALID_HANDLE:
4622  errno = EINVAL;
4623  break;
4624  default:
4625  errno = map_errno(err);
4626  break;
4627  }
4628  error_exit:
4629  CloseChildHandle(child);
4630  return -1;
4631  }
4632  if (exitcode != STILL_ACTIVE) {
4633  rb_pid_t pid;
4634  /* If already died, wait process's real termination. */
4635  if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
4636  goto error_exit;
4637  }
4638  pid = child->pid;
4639  CloseChildHandle(child);
4640  if (stat_loc) {
4641  *stat_loc = exitcode << 8;
4642  if (exitcode & 0xC0000000) {
4643  static const struct {
4644  DWORD status;
4645  int sig;
4646  } table[] = {
4647  {STATUS_ACCESS_VIOLATION, SIGSEGV},
4648  {STATUS_ILLEGAL_INSTRUCTION, SIGILL},
4649  {STATUS_PRIVILEGED_INSTRUCTION, SIGILL},
4650  {STATUS_FLOAT_DENORMAL_OPERAND, SIGFPE},
4651  {STATUS_FLOAT_DIVIDE_BY_ZERO, SIGFPE},
4652  {STATUS_FLOAT_INEXACT_RESULT, SIGFPE},
4653  {STATUS_FLOAT_INVALID_OPERATION, SIGFPE},
4654  {STATUS_FLOAT_OVERFLOW, SIGFPE},
4655  {STATUS_FLOAT_STACK_CHECK, SIGFPE},
4656  {STATUS_FLOAT_UNDERFLOW, SIGFPE},
4657 #ifdef STATUS_FLOAT_MULTIPLE_FAULTS
4658  {STATUS_FLOAT_MULTIPLE_FAULTS, SIGFPE},
4659 #endif
4660 #ifdef STATUS_FLOAT_MULTIPLE_TRAPS
4661  {STATUS_FLOAT_MULTIPLE_TRAPS, SIGFPE},
4662 #endif
4663  {STATUS_CONTROL_C_EXIT, SIGINT},
4664  };
4665  int i;
4666  for (i = 0; i < (int)numberof(table); i++) {
4667  if (table[i].status == exitcode) {
4668  *stat_loc |= table[i].sig;
4669  break;
4670  }
4671  }
4672  // if unknown status, assume SEGV
4673  if (i >= (int)numberof(table))
4674  *stat_loc |= SIGSEGV;
4675  }
4676  }
4677  return pid;
4678  }
4679  return 0;
4680 }
4681 
4682 /* License: Artistic or GPL */
4683 rb_pid_t
4684 waitpid(rb_pid_t pid, int *stat_loc, int options)
4685 {
4686  DWORD timeout;
4687 
4688  /* Artistic or GPL part start */
4689  if (options == WNOHANG) {
4690  timeout = 0;
4691  }
4692  else {
4693  timeout = INFINITE;
4694  }
4695  /* Artistic or GPL part end */
4696 
4697  if (pid == -1) {
4698  int count = 0;
4699  int ret;
4700  HANDLE events[MAXCHILDNUM];
4701  struct ChildRecord* cause;
4702 
4703  FOREACH_CHILD(child) {
4704  if (!child->pid || child->pid < 0) continue;
4705  if ((pid = poll_child_status(child, stat_loc))) return pid;
4706  events[count++] = child->hProcess;
4707  } END_FOREACH_CHILD;
4708  if (!count) {
4709  errno = ECHILD;
4710  return -1;
4711  }
4712 
4713  ret = rb_w32_wait_events_blocking(events, count, timeout);
4714  if (ret == WAIT_TIMEOUT) return 0;
4715  if ((ret -= WAIT_OBJECT_0) == count) {
4716  return -1;
4717  }
4718  if (ret > count) {
4719  errno = map_errno(GetLastError());
4720  return -1;
4721  }
4722 
4723  cause = FindChildSlotByHandle(events[ret]);
4724  if (!cause) {
4725  errno = ECHILD;
4726  return -1;
4727  }
4728  return poll_child_status(cause, stat_loc);
4729  }
4730  else {
4731  struct ChildRecord* child = FindChildSlot(pid);
4732  int retried = 0;
4733  if (!child) {
4734  errno = ECHILD;
4735  return -1;
4736  }
4737 
4738  while (!(pid = poll_child_status(child, stat_loc))) {
4739  /* wait... */
4740  int ret = rb_w32_wait_events_blocking(&child->hProcess, 1, timeout);
4741  if (ret == WAIT_OBJECT_0 + 1) return -1; /* maybe EINTR */
4742  if (ret != WAIT_OBJECT_0) {
4743  /* still active */
4744  if (options & WNOHANG) {
4745  pid = 0;
4746  break;
4747  }
4748  ++retried;
4749  }
4750  }
4751  if (pid == -1 && retried) pid = 0;
4752  }
4753 
4754  return pid;
4755 }
4756 
4757 #include <sys/timeb.h>
4758 
4759 /* License: Ruby's */
4760 /* split FILETIME value into UNIX time and sub-seconds in NT ticks */
4761 static time_t
4762 filetime_split(const FILETIME* ft, long *subsec)
4763 {
4764  ULARGE_INTEGER tmp;
4765  unsigned LONG_LONG lt;
4766  const unsigned LONG_LONG subsec_unit = (unsigned LONG_LONG)10 * 1000 * 1000;
4767 
4768  tmp.LowPart = ft->dwLowDateTime;
4769  tmp.HighPart = ft->dwHighDateTime;
4770  lt = tmp.QuadPart;
4771 
4772  /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
4773  convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
4774  the first leap second is at 1972/06/30, so we doesn't need to think
4775  about it. */
4776  lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * subsec_unit;
4777 
4778  *subsec = (long)(lt % subsec_unit);
4779  return (time_t)(lt / subsec_unit);
4780 }
4781 
4782 /* License: Ruby's */
4783 int __cdecl
4784 gettimeofday(struct timeval *tv, struct timezone *tz)
4785 {
4786  FILETIME ft;
4787  long subsec;
4788 
4789  GetSystemTimePreciseAsFileTime(&ft);
4790  tv->tv_sec = filetime_split(&ft, &subsec);
4791  tv->tv_usec = subsec / 10;
4792 
4793  return 0;
4794 }
4795 
4796 /* License: Ruby's */
4797 int
4798 clock_gettime(clockid_t clock_id, struct timespec *sp)
4799 {
4800  switch (clock_id) {
4801  case CLOCK_REALTIME:
4802  {
4803  FILETIME ft;
4804  long subsec;
4805 
4806  GetSystemTimePreciseAsFileTime(&ft);
4807  sp->tv_sec = filetime_split(&ft, &subsec);
4808  sp->tv_nsec = subsec * 100;
4809  return 0;
4810  }
4811  case CLOCK_MONOTONIC:
4812  {
4813  LARGE_INTEGER freq;
4814  LARGE_INTEGER count;
4815  if (!QueryPerformanceFrequency(&freq)) {
4816  errno = map_errno(GetLastError());
4817  return -1;
4818  }
4819  if (!QueryPerformanceCounter(&count)) {
4820  errno = map_errno(GetLastError());
4821  return -1;
4822  }
4823  sp->tv_sec = count.QuadPart / freq.QuadPart;
4824  if (freq.QuadPart < 1000000000)
4825  sp->tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
4826  else
4827  sp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
4828  return 0;
4829  }
4830  default:
4831  errno = EINVAL;
4832  return -1;
4833  }
4834 }
4835 
4836 /* License: Ruby's */
4837 int
4838 clock_getres(clockid_t clock_id, struct timespec *sp)
4839 {
4840  switch (clock_id) {
4841  case CLOCK_REALTIME:
4842  {
4843  sp->tv_sec = 0;
4844  sp->tv_nsec = 1000;
4845  return 0;
4846  }
4847  case CLOCK_MONOTONIC:
4848  {
4849  LARGE_INTEGER freq;
4850  if (!QueryPerformanceFrequency(&freq)) {
4851  errno = map_errno(GetLastError());
4852  return -1;
4853  }
4854  sp->tv_sec = 0;
4855  sp->tv_nsec = (long)(1000000000.0 / freq.QuadPart);
4856  return 0;
4857  }
4858  default:
4859  errno = EINVAL;
4860  return -1;
4861  }
4862 }
4863 
4864 /* License: Ruby's */
4865 static char *
4866 w32_getcwd(char *buffer, int size, UINT cp, void *alloc(int, void *), void *arg)
4867 {
4868  WCHAR *p;
4869  int wlen, len;
4870 
4871  len = GetCurrentDirectoryW(0, NULL);
4872  if (!len) {
4873  errno = map_errno(GetLastError());
4874  return NULL;
4875  }
4876 
4877  if (buffer && size < len) {
4878  errno = ERANGE;
4879  return NULL;
4880  }
4881 
4882  p = ALLOCA_N(WCHAR, len);
4883  if (!GetCurrentDirectoryW(len, p)) {
4884  errno = map_errno(GetLastError());
4885  return NULL;
4886  }
4887 
4888  wlen = translate_wchar(p, L'\\', L'/') - p + 1;
4889  len = WideCharToMultiByte(cp, 0, p, wlen, NULL, 0, NULL, NULL);
4890  if (buffer) {
4891  if (size < len) {
4892  errno = ERANGE;
4893  return NULL;
4894  }
4895  }
4896  else {
4897  buffer = (*alloc)(len, arg);
4898  if (!buffer) {
4899  errno = ENOMEM;
4900  return NULL;
4901  }
4902  }
4903  WideCharToMultiByte(cp, 0, p, wlen, buffer, len, NULL, NULL);
4904 
4905  return buffer;
4906 }
4907 
4908 /* License: Ruby's */
4909 static void *
4910 getcwd_alloc(int size, void *dummy)
4911 {
4912  return malloc(size);
4913 }
4914 
4915 /* License: Ruby's */
4916 char *
4917 rb_w32_getcwd(char *buffer, int size)
4918 {
4919  return w32_getcwd(buffer, size, filecp(), getcwd_alloc, NULL);
4920 }
4921 
4922 /* License: Ruby's */
4923 char *
4924 rb_w32_ugetcwd(char *buffer, int size)
4925 {
4926  return w32_getcwd(buffer, size, CP_UTF8, getcwd_alloc, NULL);
4927 }
4928 
4929 /* License: Ruby's */
4930 static void *
4931 getcwd_value(int size, void *arg)
4932 {
4933  VALUE str = *(VALUE *)arg = rb_utf8_str_new(0, size - 1);
4934  return RSTRING_PTR(str);
4935 }
4936 
4937 /* License: Ruby's */
4938 VALUE
4939 rb_dir_getwd_ospath(void)
4940 {
4941  VALUE cwd = Qnil;
4942  w32_getcwd(NULL, 0, CP_UTF8, getcwd_value, &cwd);
4943  return cwd;
4944 }
4945 
4946 /* License: Artistic or GPL */
4947 int
4948 chown(const char *path, int owner, int group)
4949 {
4950  return 0;
4951 }
4952 
4953 /* License: Artistic or GPL */
4954 int
4955 rb_w32_uchown(const char *path, int owner, int group)
4956 {
4957  return 0;
4958 }
4959 
4960 int
4961 lchown(const char *path, int owner, int group)
4962 {
4963  return 0;
4964 }
4965 
4966 int
4967 rb_w32_ulchown(const char *path, int owner, int group)
4968 {
4969  return 0;
4970 }
4971 
4972 /* License: Ruby's */
4973 int
4974 kill(rb_pid_t pid, int sig)
4975 {
4976  int ret = 0;
4977  DWORD err;
4978 
4979  if (pid < 0 || (pid == 0 && sig != SIGINT)) {
4980  errno = EINVAL;
4981  return -1;
4982  }
4983 
4984  if ((unsigned int)pid == GetCurrentProcessId() &&
4985  (sig != 0 && sig != SIGKILL)) {
4986  if ((ret = raise(sig)) != 0) {
4987  /* MSVCRT doesn't set errno... */
4988  errno = EINVAL;
4989  }
4990  return ret;
4991  }
4992 
4993  switch (sig) {
4994  case 0:
4995  RUBY_CRITICAL {
4996  HANDLE hProc =
4997  OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4998  if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4999  if (GetLastError() == ERROR_INVALID_PARAMETER) {
5000  errno = ESRCH;
5001  }
5002  else {
5003  errno = EPERM;
5004  }
5005  ret = -1;
5006  }
5007  else {
5008  CloseHandle(hProc);
5009  }
5010  }
5011  break;
5012 
5013  case SIGINT:
5014  RUBY_CRITICAL {
5015  DWORD ctrlEvent = CTRL_C_EVENT;
5016  if (pid != 0) {
5017  /* CTRL+C signal cannot be generated for process groups.
5018  * Instead, we use CTRL+BREAK signal. */
5019  ctrlEvent = CTRL_BREAK_EVENT;
5020  }
5021  if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
5022  if ((err = GetLastError()) == 0)
5023  errno = EPERM;
5024  else
5025  errno = map_errno(GetLastError());
5026  ret = -1;
5027  }
5028  }
5029  break;
5030 
5031  case SIGKILL:
5032  RUBY_CRITICAL {
5033  HANDLE hProc;
5034  struct ChildRecord* child = FindChildSlot(pid);
5035  if (child) {
5036  hProc = child->hProcess;
5037  }
5038  else {
5039  hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
5040  }
5041  if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
5042  if (GetLastError() == ERROR_INVALID_PARAMETER) {
5043  errno = ESRCH;
5044  }
5045  else {
5046  errno = EPERM;
5047  }
5048  ret = -1;
5049  }
5050  else {
5051  DWORD status;
5052  if (!GetExitCodeProcess(hProc, &status)) {
5053  errno = map_errno(GetLastError());
5054  ret = -1;
5055  }
5056  else if (status == STILL_ACTIVE) {
5057  if (!TerminateProcess(hProc, 0)) {
5058  errno = EPERM;
5059  ret = -1;
5060  }
5061  }
5062  else {
5063  errno = ESRCH;
5064  ret = -1;
5065  }
5066  if (!child) {
5067  CloseHandle(hProc);
5068  }
5069  }
5070  }
5071  break;
5072 
5073  default:
5074  errno = EINVAL;
5075  ret = -1;
5076  break;
5077  }
5078 
5079  return ret;
5080 }
5081 
5082 /* License: Ruby's */
5083 static int
5084 wlink(const WCHAR *from, const WCHAR *to)
5085 {
5086  if (!CreateHardLinkW(to, from, NULL)) {
5087  errno = map_errno(GetLastError());
5088  return -1;
5089  }
5090 
5091  return 0;
5092 }
5093 
5094 /* License: Ruby's */
5095 int
5096 rb_w32_ulink(const char *from, const char *to)
5097 {
5098  WCHAR *wfrom;
5099  WCHAR *wto;
5100  int ret;
5101 
5102  if (!(wfrom = utf8_to_wstr(from, NULL)))
5103  return -1;
5104  if (!(wto = utf8_to_wstr(to, NULL))) {
5105  free(wfrom);
5106  return -1;
5107  }
5108  ret = wlink(wfrom, wto);
5109  free(wto);
5110  free(wfrom);
5111  return ret;
5112 }
5113 
5114 /* License: Ruby's */
5115 int
5116 link(const char *from, const char *to)
5117 {
5118  WCHAR *wfrom;
5119  WCHAR *wto;
5120  int ret;
5121 
5122  if (!(wfrom = filecp_to_wstr(from, NULL)))
5123  return -1;
5124  if (!(wto = filecp_to_wstr(to, NULL))) {
5125  free(wfrom);
5126  return -1;
5127  }
5128  ret = wlink(wfrom, wto);
5129  free(wto);
5130  free(wfrom);
5131  return ret;
5132 }
5133 
5134 /* License: Public Domain, copied from mingw headers */
5135 #ifndef FILE_DEVICE_FILE_SYSTEM
5136 # define FILE_DEVICE_FILE_SYSTEM 0x00000009
5137 #endif
5138 #ifndef FSCTL_GET_REPARSE_POINT
5139 # define FSCTL_GET_REPARSE_POINT ((0x9<<16)|(42<<2))
5140 #endif
5141 #ifndef IO_REPARSE_TAG_SYMLINK
5142 # define IO_REPARSE_TAG_SYMLINK 0xA000000CL
5143 #endif
5144 
5145 /* License: Ruby's */
5146 static int
5147 reparse_symlink(const WCHAR *path, rb_w32_reparse_buffer_t *rp, size_t size)
5148 {
5149  HANDLE f;
5150  DWORD ret;
5151  int e = 0;
5152 
5153  f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5154  if (f == INVALID_HANDLE_VALUE) {
5155  return GetLastError();
5156  }
5157 
5158  if (!DeviceIoControl(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
5159  rp, size, &ret, NULL)) {
5160  e = GetLastError();
5161  }
5162  else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
5163  rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
5164  e = ERROR_INVALID_PARAMETER;
5165  }
5166  CloseHandle(f);
5167  return e;
5168 }
5169 
5170 /* License: Ruby's */
5171 int
5172 rb_w32_reparse_symlink_p(const WCHAR *path)
5173 {
5174  VALUE wtmp = 0;
5175  rb_w32_reparse_buffer_t rbuf, *rp = &rbuf;
5176  WCHAR *wbuf;
5177  DWORD len;
5178  int e;
5179 
5180  e = rb_w32_read_reparse_point(path, rp, sizeof(rbuf), &wbuf, &len);
5181  if (e == ERROR_MORE_DATA) {
5182  size_t size = rb_w32_reparse_buffer_size(len + 1);
5183  rp = ALLOCV(wtmp, size);
5184  e = rb_w32_read_reparse_point(path, rp, size, &wbuf, &len);
5185  ALLOCV_END(wtmp);
5186  }
5187  switch (e) {
5188  case 0:
5189  case ERROR_MORE_DATA:
5190  return TRUE;
5191  }
5192  return FALSE;
5193 }
5194 
5195 /* License: Ruby's */
5196 int
5197 rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
5198  size_t bufsize, WCHAR **result, DWORD *len)
5199 {
5200  int e = reparse_symlink(path, rp, bufsize);
5201  DWORD ret = 0;
5202 
5203  if (!e || e == ERROR_MORE_DATA) {
5204  void *name;
5205  if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
5206  name = ((char *)rp->SymbolicLinkReparseBuffer.PathBuffer +
5207  rp->SymbolicLinkReparseBuffer.PrintNameOffset);
5208  ret = rp->SymbolicLinkReparseBuffer.PrintNameLength;
5209  *len = ret / sizeof(WCHAR);
5210  }
5211  else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
5212  static const WCHAR volume[] = L"Volume{";
5213  enum {volume_prefix_len = rb_strlen_lit("\\??\\")};
5214  name = ((char *)rp->MountPointReparseBuffer.PathBuffer +
5215  rp->MountPointReparseBuffer.SubstituteNameOffset +
5216  volume_prefix_len * sizeof(WCHAR));
5217  ret = rp->MountPointReparseBuffer.SubstituteNameLength;
5218  *len = ret / sizeof(WCHAR);
5219  ret -= volume_prefix_len * sizeof(WCHAR);
5220  if (ret > sizeof(volume) - 1 * sizeof(WCHAR) &&
5221  memcmp(name, volume, sizeof(volume) - 1 * sizeof(WCHAR)) == 0)
5222  return -1;
5223  }
5224  else {
5225  return -1;
5226  }
5227  *result = name;
5228  if (e) {
5229  if ((char *)name + ret + sizeof(WCHAR) > (char *)rp + bufsize)
5230  return e;
5231  /* SubstituteName is not used */
5232  }
5233  ((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0';
5234  translate_wchar(name, L'\\', L'/');
5235  return 0;
5236  }
5237  else {
5238  return e;
5239  }
5240 }
5241 
5242 /* License: Ruby's */
5243 static ssize_t
5244 w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize)
5245 {
5246  VALUE rp_buf, rp_buf_bigger = 0;
5247  DWORD len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0);
5248  size_t size = rb_w32_reparse_buffer_size(bufsize);
5249  WCHAR *wname;
5250  WCHAR *wpath = ALLOCV(rp_buf, sizeof(WCHAR) * len + size);
5251  rb_w32_reparse_buffer_t *rp = (void *)(wpath + len);
5252  ssize_t ret;
5253  int e;
5254 
5255  MultiByteToWideChar(cp, 0, path, -1, wpath, len);
5256  e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5257  if (e == ERROR_MORE_DATA) {
5258  size = rb_w32_reparse_buffer_size(len + 1);
5259  rp = ALLOCV(rp_buf_bigger, size);
5260  e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5261  }
5262  if (e) {
5263  ALLOCV_END(rp_buf);
5264  ALLOCV_END(rp_buf_bigger);
5265  errno = e == -1 ? EINVAL : map_errno(e);
5266  return -1;
5267  }
5268  len = lstrlenW(wname);
5269  ret = WideCharToMultiByte(cp, 0, wname, len, buf, bufsize, NULL, NULL);
5270  ALLOCV_END(rp_buf);
5271  ALLOCV_END(rp_buf_bigger);
5272  if (!ret) {
5273  ret = bufsize;
5274  }
5275  return ret;
5276 }
5277 
5278 /* License: Ruby's */
5279 ssize_t
5280 rb_w32_ureadlink(const char *path, char *buf, size_t bufsize)
5281 {
5282  return w32_readlink(CP_UTF8, path, buf, bufsize);
5283 }
5284 
5285 /* License: Ruby's */
5286 ssize_t
5287 readlink(const char *path, char *buf, size_t bufsize)
5288 {
5289  return w32_readlink(filecp(), path, buf, bufsize);
5290 }
5291 
5292 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
5293 #define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
5294 #endif
5295 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
5296 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
5297 #endif
5298 
5299 /* License: Ruby's */
5300 static int
5301 w32_symlink(UINT cp, const char *src, const char *link)
5302 {
5303  int atts, len1, len2;
5304  VALUE buf;
5305  WCHAR *wsrc, *wlink;
5306  DWORD flag = 0;
5307  BOOLEAN ret;
5308  int e;
5309 
5310  static DWORD create_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5311 
5312  if (!*link) {
5313  errno = ENOENT;
5314  return -1;
5315  }
5316  if (!*src) {
5317  errno = EINVAL;
5318  return -1;
5319  }
5320  len1 = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
5321  len2 = MultiByteToWideChar(cp, 0, link, -1, NULL, 0);
5322  wsrc = ALLOCV_N(WCHAR, buf, len1+len2);
5323  wlink = wsrc + len1;
5324  MultiByteToWideChar(cp, 0, src, -1, wsrc, len1);
5325  MultiByteToWideChar(cp, 0, link, -1, wlink, len2);
5326  translate_wchar(wsrc, L'/', L'\\');
5327 
5328  atts = GetFileAttributesW(wsrc);
5329  if (atts != -1 && atts & FILE_ATTRIBUTE_DIRECTORY)
5330  flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
5331  ret = CreateSymbolicLinkW(wlink, wsrc, flag |= create_flag);
5332  if (!ret &&
5333  (e = GetLastError()) == ERROR_INVALID_PARAMETER &&
5334  (flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
5335  create_flag = 0;
5336  flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5337  ret = CreateSymbolicLinkW(wlink, wsrc, flag);
5338  if (!ret) e = GetLastError();
5339  }
5340  ALLOCV_END(buf);
5341 
5342  if (!ret) {
5343  errno = map_errno(e);
5344  return -1;
5345  }
5346  return 0;
5347 }
5348 
5349 /* License: Ruby's */
5350 int
5351 rb_w32_usymlink(const char *src, const char *link)
5352 {
5353  return w32_symlink(CP_UTF8, src, link);
5354 }
5355 
5356 /* License: Ruby's */
5357 int
5358 symlink(const char *src, const char *link)
5359 {
5360  return w32_symlink(filecp(), src, link);
5361 }
5362 
5363 /* License: Ruby's */
5364 rb_pid_t
5365 wait(int *status)
5366 {
5367  return waitpid(-1, status, 0);
5368 }
5369 
5370 /* License: Ruby's */
5371 static char *
5372 w32_getenv(const char *name, UINT cp)
5373 {
5374  WCHAR *wenvarea, *wenv;
5375  int len = strlen(name);
5376  char *env, *found = NULL;
5377  int wlen;
5378 
5379  if (len == 0) return NULL;
5380 
5381  if (!NTLoginName) {
5382  /* initialized in init_env, uenvarea_mutex should have been
5383  * initialized before it */
5384  return getenv(name);
5385  }
5386 
5387  thread_exclusive(uenvarea) {
5388  if (uenvarea) {
5389  free(uenvarea);
5390  uenvarea = NULL;
5391  }
5392  wenvarea = GetEnvironmentStringsW();
5393  if (!wenvarea) {
5394  map_errno(GetLastError());
5395  continue;
5396  }
5397  for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
5398  wlen += lstrlenW(wenv) + 1;
5399  uenvarea = wstr_to_mbstr(cp, wenvarea, wlen, NULL);
5400  FreeEnvironmentStringsW(wenvarea);
5401  if (!uenvarea)
5402  continue;
5403 
5404  for (env = uenvarea; *env; env += strlen(env) + 1) {
5405  if (strncasecmp(env, name, len) == 0 && *(env + len) == '=') {
5406  found = env + len + 1;
5407  break;
5408  }
5409  }
5410  }
5411 
5412  return found;
5413 }
5414 
5415 /* License: Ruby's */
5416 char *
5417 rb_w32_ugetenv(const char *name)
5418 {
5419  return w32_getenv(name, CP_UTF8);
5420 }
5421 
5422 /* License: Ruby's */
5423 char *
5424 rb_w32_getenv(const char *name)
5425 {
5426  return w32_getenv(name, CP_ACP);
5427 }
5428 
5429 /* License: Ruby's */
5430 static DWORD
5431 get_attr_vsn(const WCHAR *path, DWORD *atts, DWORD *vsn)
5432 {
5433  BY_HANDLE_FILE_INFORMATION st = {0};
5434  DWORD e = 0;
5435  HANDLE h = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5436 
5437  if (h == INVALID_HANDLE_VALUE) {
5438  e = GetLastError();
5439  ASSUME(e);
5440  return e;
5441  }
5442  if (!GetFileInformationByHandle(h, &st)) {
5443  e = GetLastError();
5444  ASSUME(e);
5445  }
5446  else {
5447  *atts = st.dwFileAttributes;
5448  *vsn = st.dwVolumeSerialNumber;
5449  }
5450  CloseHandle(h);
5451  return e;
5452 }
5453 
5454 /* License: Artistic or GPL */
5455 static int
5456 wrename(const WCHAR *oldpath, const WCHAR *newpath)
5457 {
5458  int res = 0;
5459  DWORD oldatts = 0, newatts = (DWORD)-1;
5460  DWORD oldvsn = 0, newvsn = 0, e;
5461 
5462  e = get_attr_vsn(oldpath, &oldatts, &oldvsn);
5463  if (e) {
5464  errno = map_errno(e);
5465  return -1;
5466  }
5467  if (oldatts & FILE_ATTRIBUTE_REPARSE_POINT) {
5468  HANDLE fh = open_special(oldpath, 0, 0);
5469  if (fh == INVALID_HANDLE_VALUE) {
5470  e = GetLastError();
5471  if (e == ERROR_CANT_RESOLVE_FILENAME) {
5472  errno = ELOOP;
5473  return -1;
5474  }
5475  }
5476  CloseHandle(fh);
5477  }
5478  get_attr_vsn(newpath, &newatts, &newvsn);
5479 
5480  RUBY_CRITICAL {
5481  if (newatts != (DWORD)-1 && newatts & FILE_ATTRIBUTE_READONLY)
5482  SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
5483 
5484  if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
5485  res = -1;
5486 
5487  if (res) {
5488  DWORD e = GetLastError();
5489  if ((e == ERROR_ACCESS_DENIED) && (oldatts & FILE_ATTRIBUTE_DIRECTORY) &&
5490  oldvsn != newvsn)
5491  errno = EXDEV;
5492  else
5493  errno = map_errno(e);
5494  }
5495  else
5496  SetFileAttributesW(newpath, oldatts);
5497  }
5498 
5499  return res;
5500 }
5501 
5502 /* License: Ruby's */
5503 int
5504 rb_w32_urename(const char *from, const char *to)
5505 {
5506  WCHAR *wfrom;
5507  WCHAR *wto;
5508  int ret = -1;
5509 
5510  if (!(wfrom = utf8_to_wstr(from, NULL)))
5511  return -1;
5512  if (!(wto = utf8_to_wstr(to, NULL))) {
5513  free(wfrom);
5514  return -1;
5515  }
5516  ret = wrename(wfrom, wto);
5517  free(wto);
5518  free(wfrom);
5519  return ret;
5520 }
5521 
5522 /* License: Ruby's */
5523 int
5524 rb_w32_rename(const char *from, const char *to)
5525 {
5526  WCHAR *wfrom;
5527  WCHAR *wto;
5528  int ret = -1;
5529 
5530  if (!(wfrom = filecp_to_wstr(from, NULL)))
5531  return -1;
5532  if (!(wto = filecp_to_wstr(to, NULL))) {
5533  free(wfrom);
5534  return -1;
5535  }
5536  ret = wrename(wfrom, wto);
5537  free(wto);
5538  free(wfrom);
5539  return ret;
5540 }
5541 
5542 /* License: Ruby's */
5543 static int
5544 isUNCRoot(const WCHAR *path)
5545 {
5546  if (path[0] == L'\\' && path[1] == L'\\') {
5547  const WCHAR *p = path + 2;
5548  if (p[0] == L'?' && p[1] == L'\\') {
5549  p += 2;
5550  }
5551  for (; *p; p++) {
5552  if (*p == L'\\')
5553  break;
5554  }
5555  if (p[0] && p[1]) {
5556  for (p++; *p; p++) {
5557  if (*p == L'\\')
5558  break;
5559  }
5560  if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
5561  return 1;
5562  }
5563  }
5564  return 0;
5565 }
5566 
5567 #define COPY_STAT(src, dest, size_cast) do { \
5568  (dest).st_dev = (src).st_dev; \
5569  (dest).st_ino = (src).st_ino; \
5570  (dest).st_mode = (src).st_mode; \
5571  (dest).st_nlink = (src).st_nlink; \
5572  (dest).st_uid = (src).st_uid; \
5573  (dest).st_gid = (src).st_gid; \
5574  (dest).st_rdev = (src).st_rdev; \
5575  (dest).st_size = size_cast(src).st_size; \
5576  (dest).st_atime = (src).st_atime; \
5577  (dest).st_mtime = (src).st_mtime; \
5578  (dest).st_ctime = (src).st_ctime; \
5579  } while (0)
5580 
5581 static time_t filetime_to_unixtime(const FILETIME *ft);
5582 static long filetime_to_nsec(const FILETIME *ft);
5583 static WCHAR *name_for_stat(WCHAR *buf, const WCHAR *path);
5584 static DWORD stati128_handle(HANDLE h, struct stati128 *st);
5585 
5586 #undef fstat
5587 /* License: Ruby's */
5588 int
5589 rb_w32_fstat(int fd, struct stat *st)
5590 {
5591  BY_HANDLE_FILE_INFORMATION info;
5592  int ret = fstat(fd, st);
5593 
5594  if (ret) return ret;
5595  if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) return ret;
5596  if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
5597  st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5598  st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5599  st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5600  }
5601  return ret;
5602 }
5603 
5604 /* License: Ruby's */
5605 int
5606 rb_w32_fstati128(int fd, struct stati128 *st)
5607 {
5608  struct stat tmp;
5609  int ret = fstat(fd, &tmp);
5610 
5611  if (ret) return ret;
5612  COPY_STAT(tmp, *st, +);
5613  stati128_handle((HANDLE)_get_osfhandle(fd), st);
5614  return ret;
5615 }
5616 
5617 #if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5618 typedef struct {
5619  BYTE Identifier[16];
5620 } FILE_ID_128;
5621 #endif
5622 
5623 #if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5624 #define FileIdInfo 0x12
5625 
5626 typedef struct {
5627  unsigned LONG_LONG VolumeSerialNumber;
5628  FILE_ID_128 FileId;
5629 } FILE_ID_INFO;
5630 #endif
5631 
5632 static BOOL
5633 get_ino(HANDLE h, FILE_ID_INFO *id)
5634 {
5635  return GetFileInformationByHandleEx(h, FileIdInfo, id, sizeof(*id));
5636 }
5637 
5638 /* License: Ruby's */
5639 static DWORD
5640 stati128_handle(HANDLE h, struct stati128 *st)
5641 {
5642  BY_HANDLE_FILE_INFORMATION info;
5643  DWORD attr = (DWORD)-1;
5644 
5645  if (GetFileInformationByHandle(h, &info)) {
5646  FILE_ID_INFO fii;
5647  st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
5648  st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5649  st->st_atimensec = filetime_to_nsec(&info.ftLastAccessTime);
5650  st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5651  st->st_mtimensec = filetime_to_nsec(&info.ftLastWriteTime);
5652  st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5653  st->st_ctimensec = filetime_to_nsec(&info.ftCreationTime);
5654  st->st_nlink = info.nNumberOfLinks;
5655  attr = info.dwFileAttributes;
5656  if (get_ino(h, &fii)) {
5657  st->st_ino = *((unsigned __int64 *)&fii.FileId);
5658  st->st_inohigh = *((__int64 *)&fii.FileId + 1);
5659  }
5660  else {
5661  st->st_ino = ((__int64)info.nFileIndexHigh << 32) | info.nFileIndexLow;
5662  st->st_inohigh = 0;
5663  }
5664  }
5665  return attr;
5666 }
5667 
5668 /* License: Ruby's */
5669 static time_t
5670 filetime_to_unixtime(const FILETIME *ft)
5671 {
5672  long subsec;
5673  time_t t = filetime_split(ft, &subsec);
5674 
5675  if (t < 0) return 0;
5676  return t;
5677 }
5678 
5679 /* License: Ruby's */
5680 static long
5681 filetime_to_nsec(const FILETIME *ft)
5682 {
5683  ULARGE_INTEGER tmp;
5684  tmp.LowPart = ft->dwLowDateTime;
5685  tmp.HighPart = ft->dwHighDateTime;
5686  return (long)(tmp.QuadPart % 10000000) * 100;
5687 }
5688 
5689 /* License: Ruby's */
5690 static unsigned
5691 fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode)
5692 {
5693  if (attr & FILE_ATTRIBUTE_READONLY) {
5694  mode |= S_IREAD;
5695  }
5696  else {
5697  mode |= S_IREAD | S_IWRITE | S_IWUSR;
5698  }
5699 
5700  if (mode & S_IFMT) {
5701  /* format is already set */
5702  }
5703  else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5704  /* Only used by stat_by_find in the case the file can not be opened.
5705  * In this case we can't get more details. */
5706  }
5707  else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5708  mode |= S_IFDIR | S_IEXEC;
5709  }
5710  else {
5711  mode |= S_IFREG;
5712  }
5713 
5714  if (path && (mode & S_IFREG)) {
5715  const WCHAR *end = path + lstrlenW(path);
5716  while (path < end) {
5717  end = CharPrevW(path, end);
5718  if (*end == L'.') {
5719  if ((_wcsicmp(end, L".bat") == 0) ||
5720  (_wcsicmp(end, L".cmd") == 0) ||
5721  (_wcsicmp(end, L".com") == 0) ||
5722  (_wcsicmp(end, L".exe") == 0)) {
5723  mode |= S_IEXEC;
5724  }
5725  break;
5726  }
5727  if (!iswalnum(*end)) break;
5728  }
5729  }
5730 
5731  mode |= (mode & 0500) >> 3;
5732  mode |= (mode & 0500) >> 6;
5733 
5734  return mode;
5735 }
5736 
5737 /* License: Ruby's */
5738 static int
5739 check_valid_dir(const WCHAR *path)
5740 {
5741  WIN32_FIND_DATAW fd;
5742  HANDLE fh;
5743  WCHAR full[PATH_MAX];
5744  WCHAR *dmy;
5745  WCHAR *p, *q;
5746 
5747  /* GetFileAttributes() determines "..." as directory. */
5748  /* We recheck it by FindFirstFile(). */
5749  if (!(p = wcsstr(path, L"...")))
5750  return 0;
5751  q = p + wcsspn(p, L".");
5752  if ((p == path || wcschr(L":/\\", *(p - 1))) &&
5753  (!*q || wcschr(L":/\\", *q))) {
5754  errno = ENOENT;
5755  return -1;
5756  }
5757 
5758  /* if the specified path is the root of a drive and the drive is empty, */
5759  /* FindFirstFile() returns INVALID_HANDLE_VALUE. */
5760  if (!GetFullPathNameW(path, sizeof(full) / sizeof(WCHAR), full, &dmy)) {
5761  errno = map_errno(GetLastError());
5762  return -1;
5763  }
5764  if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5765  return 0;
5766 
5767  fh = open_dir_handle(path, &fd);
5768  if (fh == INVALID_HANDLE_VALUE)
5769  return -1;
5770  FindClose(fh);
5771  return 0;
5772 }
5773 
5774 /* License: Ruby's */
5775 static int
5776 stat_by_find(const WCHAR *path, struct stati128 *st)
5777 {
5778  HANDLE h;
5779  WIN32_FIND_DATAW wfd;
5780 
5781  /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
5782  h = FindFirstFileW(path, &wfd);
5783  if (h == INVALID_HANDLE_VALUE) {
5784  errno = map_errno(GetLastError());
5785  return -1;
5786  }
5787  FindClose(h);
5788  st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path, 0);
5789  st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
5790  st->st_atimensec = filetime_to_nsec(&wfd.ftLastAccessTime);
5791  st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
5792  st->st_mtimensec = filetime_to_nsec(&wfd.ftLastWriteTime);
5793  st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
5794  st->st_ctimensec = filetime_to_nsec(&wfd.ftCreationTime);
5795  st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
5796  st->st_nlink = 1;
5797  return 0;
5798 }
5799 
5800 /* License: Ruby's */
5801 static int
5802 path_drive(const WCHAR *path)
5803 {
5804  return (iswalpha(path[0]) && path[1] == L':') ?
5805  towupper(path[0]) - L'A' : _getdrive() - 1;
5806 }
5807 
5808 /* License: Ruby's */
5809 static int
5810 winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
5811 {
5812  DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
5813  HANDLE f;
5814  WCHAR finalname[PATH_MAX];
5815  int open_error;
5816 
5817  memset(st, 0, sizeof(*st));
5818  f = open_special(path, 0, flags);
5819  open_error = GetLastError();
5820  if (f == INVALID_HANDLE_VALUE && !lstat) {
5821  /* Support stat (not only lstat) of UNIXSocket */
5822  FILE_ATTRIBUTE_TAG_INFO attr_info;
5823  DWORD e;
5824 
5825  f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5826  e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5827  &attr_info, sizeof(attr_info));
5828  if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
5829  CloseHandle(f);
5830  f = INVALID_HANDLE_VALUE;
5831  }
5832  }
5833  if (f != INVALID_HANDLE_VALUE) {
5834  DWORD attr = stati128_handle(f, st);
5835  const DWORD len = GetFinalPathNameByHandleW(f, finalname, numberof(finalname), 0);
5836  unsigned mode = 0;
5837  switch (GetFileType(f)) {
5838  case FILE_TYPE_CHAR:
5839  mode = S_IFCHR;
5840  break;
5841  case FILE_TYPE_PIPE:
5842  mode = S_IFIFO;
5843  break;
5844  default:
5845  if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5846  FILE_ATTRIBUTE_TAG_INFO attr_info;
5847  DWORD e;
5848 
5849  e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5850  &attr_info, sizeof(attr_info));
5851  if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) {
5852  st->st_size = 0;
5853  mode |= S_IFSOCK;
5854  }
5855  else if (rb_w32_reparse_symlink_p(path)) {
5856  /* TODO: size in which encoding? */
5857  st->st_size = 0;
5858  mode |= S_IFLNK | S_IEXEC;
5859  }
5860  else {
5861  mode |= S_IFDIR | S_IEXEC;
5862  }
5863  }
5864  }
5865  CloseHandle(f);
5866  if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5867  if (check_valid_dir(path)) return -1;
5868  }
5869  st->st_mode = fileattr_to_unixmode(attr, path, mode);
5870  if (len) {
5871  finalname[min(len, numberof(finalname)-1)] = L'\0';
5872  path = finalname;
5873  if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
5874  path += numberof(namespace_prefix);
5875  }
5876  }
5877  else {
5878  if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME)
5879  || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) {
5880  errno = map_errno(open_error);
5881  return -1;
5882  }
5883 
5884  if (stat_by_find(path, st)) return -1;
5885  }
5886 
5887  st->st_dev = st->st_rdev = path_drive(path);
5888 
5889  return 0;
5890 }
5891 
5892 /* License: Ruby's */
5893 int
5894 rb_w32_stat(const char *path, struct stat *st)
5895 {
5896  struct stati128 tmp;
5897 
5898  if (w32_stati128(path, &tmp, filecp(), FALSE)) return -1;
5899  COPY_STAT(tmp, *st, (_off_t));
5900  return 0;
5901 }
5902 
5903 /* License: Ruby's */
5904 static int
5905 wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat)
5906 {
5907  WCHAR *buf1;
5908  int ret, size;
5909  VALUE v;
5910 
5911  if (!path || !st) {
5912  errno = EFAULT;
5913  return -1;
5914  }
5915  size = lstrlenW(path) + 2;
5916  buf1 = ALLOCV_N(WCHAR, v, size);
5917  if (!(path = name_for_stat(buf1, path)))
5918  return -1;
5919  ret = winnt_stat(path, st, lstat);
5920  if (v)
5921  ALLOCV_END(v);
5922 
5923  return ret;
5924 }
5925 
5926 /* License: Ruby's */
5927 static WCHAR *
5928 name_for_stat(WCHAR *buf1, const WCHAR *path)
5929 {
5930  const WCHAR *p;
5931  WCHAR *s, *end;
5932  int len;
5933 
5934  for (p = path, s = buf1; *p; p++, s++) {
5935  if (*p == L'/')
5936  *s = L'\\';
5937  else
5938  *s = *p;
5939  }
5940  *s = '\0';
5941  len = s - buf1;
5942  if (!len || L'\"' == *(--s)) {
5943  errno = ENOENT;
5944  return NULL;
5945  }
5946  end = buf1 + len - 1;
5947 
5948  if (isUNCRoot(buf1)) {
5949  if (*end == L'.')
5950  *end = L'\0';
5951  else if (*end != L'\\')
5952  lstrcatW(buf1, L"\\");
5953  }
5954  else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
5955  lstrcatW(buf1, L".");
5956 
5957  return buf1;
5958 }
5959 
5960 /* License: Ruby's */
5961 int
5962 rb_w32_ustati128(const char *path, struct stati128 *st)
5963 {
5964  return w32_stati128(path, st, CP_UTF8, FALSE);
5965 }
5966 
5967 /* License: Ruby's */
5968 int
5969 rb_w32_stati128(const char *path, struct stati128 *st)
5970 {
5971  return w32_stati128(path, st, filecp(), FALSE);
5972 }
5973 
5974 /* License: Ruby's */
5975 static int
5976 w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat)
5977 {
5978  WCHAR *wpath;
5979  int ret;
5980 
5981  if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
5982  return -1;
5983  ret = wstati128(wpath, st, lstat);
5984  free(wpath);
5985  return ret;
5986 }
5987 
5988 /* License: Ruby's */
5989 int
5990 rb_w32_ulstati128(const char *path, struct stati128 *st)
5991 {
5992  return w32_stati128(path, st, CP_UTF8, TRUE);
5993 }
5994 
5995 /* License: Ruby's */
5996 int
5997 rb_w32_lstati128(const char *path, struct stati128 *st)
5998 {
5999  return w32_stati128(path, st, filecp(), TRUE);
6000 }
6001 
6002 /* License: Ruby's */
6003 rb_off_t
6004 rb_w32_lseek(int fd, rb_off_t ofs, int whence)
6005 {
6006  SOCKET sock = TO_SOCKET(fd);
6007  if (is_socket(sock) || is_pipe(sock)) {
6008  errno = ESPIPE;
6009  return -1;
6010  }
6011  return _lseeki64(fd, ofs, whence);
6012 }
6013 
6014 /* License: Ruby's */
6015 static int
6016 w32_access(const char *path, int mode, UINT cp)
6017 {
6018  struct stati128 stat;
6019  if (w32_stati128(path, &stat, cp, FALSE) != 0)
6020  return -1;
6021  mode <<= 6;
6022  if ((stat.st_mode & mode) != mode) {
6023  errno = EACCES;
6024  return -1;
6025  }
6026  return 0;
6027 }
6028 
6029 /* License: Ruby's */
6030 int
6031 rb_w32_access(const char *path, int mode)
6032 {
6033  return w32_access(path, mode, filecp());
6034 }
6035 
6036 /* License: Ruby's */
6037 int
6038 rb_w32_uaccess(const char *path, int mode)
6039 {
6040  return w32_access(path, mode, CP_UTF8);
6041 }
6042 
6043 /* License: Ruby's */
6044 static int
6045 rb_chsize(HANDLE h, rb_off_t size)
6046 {
6047  long upos, lpos, usize, lsize;
6048  int ret = -1;
6049  DWORD e;
6050 
6051  if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6052  (e = GetLastError())) {
6053  errno = map_errno(e);
6054  return -1;
6055  }
6056  usize = (long)(size >> 32);
6057  lsize = (long)size;
6058  if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
6059  (e = GetLastError())) {
6060  errno = map_errno(e);
6061  }
6062  else if (!SetEndOfFile(h)) {
6063  errno = map_errno(GetLastError());
6064  }
6065  else {
6066  ret = 0;
6067  }
6068  SetFilePointer(h, lpos, &upos, SEEK_SET);
6069  return ret;
6070 }
6071 
6072 /* License: Ruby's */
6073 static int
6074 w32_truncate(const char *path, rb_off_t length, UINT cp)
6075 {
6076  HANDLE h;
6077  int ret;
6078  WCHAR *wpath;
6079 
6080  if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6081  return -1;
6082  h = CreateFileW(wpath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
6083  if (h == INVALID_HANDLE_VALUE) {
6084  errno = map_errno(GetLastError());
6085  free(wpath);
6086  return -1;
6087  }
6088  free(wpath);
6089  ret = rb_chsize(h, length);
6090  CloseHandle(h);
6091  return ret;
6092 }
6093 
6094 /* License: Ruby's */
6095 int
6096 rb_w32_utruncate(const char *path, rb_off_t length)
6097 {
6098  return w32_truncate(path, length, CP_UTF8);
6099 }
6100 
6101 /* License: Ruby's */
6102 int
6103 rb_w32_truncate(const char *path, rb_off_t length)
6104 {
6105  return w32_truncate(path, length, filecp());
6106 }
6107 
6108 /* License: Ruby's */
6109 int
6110 rb_w32_ftruncate(int fd, rb_off_t length)
6111 {
6112  HANDLE h;
6113 
6114  h = (HANDLE)_get_osfhandle(fd);
6115  if (h == (HANDLE)-1) return -1;
6116  return rb_chsize(h, length);
6117 }
6118 
6119 /* License: Ruby's */
6120 static long
6121 filetime_to_clock(FILETIME *ft)
6122 {
6123  __int64 qw = ft->dwHighDateTime;
6124  qw <<= 32;
6125  qw |= ft->dwLowDateTime;
6126  qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
6127  return (long) qw;
6128 }
6129 
6130 /* License: Ruby's */
6131 int
6132 rb_w32_times(struct tms *tmbuf)
6133 {
6134  FILETIME create, exit, kernel, user;
6135 
6136  if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
6137  tmbuf->tms_utime = filetime_to_clock(&user);
6138  tmbuf->tms_stime = filetime_to_clock(&kernel);
6139  tmbuf->tms_cutime = 0;
6140  tmbuf->tms_cstime = 0;
6141  }
6142  else {
6143  tmbuf->tms_utime = clock();
6144  tmbuf->tms_stime = 0;
6145  tmbuf->tms_cutime = 0;
6146  tmbuf->tms_cstime = 0;
6147  }
6148  return 0;
6149 }
6150 
6151 
6152 /* License: Ruby's */
6153 #define yield_once() Sleep(0)
6154 #define yield_until(condition) do yield_once(); while (!(condition))
6155 
6156 /* License: Ruby's */
6158  /* output field */
6159  void* stackaddr;
6160  int errnum;
6161 
6162  /* input field */
6163  uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
6164  uintptr_t self;
6165  int argc;
6166  uintptr_t* argv;
6167 };
6168 
6169 /* License: Ruby's */
6170 static DWORD WINAPI
6171 call_asynchronous(PVOID argp)
6172 {
6173  DWORD ret;
6174  struct asynchronous_arg_t *arg = argp;
6175  arg->stackaddr = &argp;
6176  ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
6177  arg->errnum = errno;
6178  return ret;
6179 }
6180 
6181 /* License: Ruby's */
6182 uintptr_t
6183 rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
6184  int argc, uintptr_t* argv, uintptr_t intrval)
6185 {
6186  DWORD val;
6187  BOOL interrupted = FALSE;
6188  HANDLE thr;
6189 
6190  RUBY_CRITICAL {
6191  struct asynchronous_arg_t arg;
6192 
6193  arg.stackaddr = NULL;
6194  arg.errnum = 0;
6195  arg.func = func;
6196  arg.self = self;
6197  arg.argc = argc;
6198  arg.argv = argv;
6199 
6200  thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6201 
6202  if (thr) {
6203  yield_until(arg.stackaddr);
6204 
6205  if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6206  interrupted = TRUE;
6207 
6208  if (TerminateThread(thr, intrval)) {
6209  yield_once();
6210  }
6211  }
6212 
6213  GetExitCodeThread(thr, &val);
6214  CloseHandle(thr);
6215 
6216  if (interrupted) {
6217  /* must release stack of killed thread, why doesn't Windows? */
6218  MEMORY_BASIC_INFORMATION m;
6219 
6220  memset(&m, 0, sizeof(m));
6221  if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
6222  Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
6223  arg.stackaddr, GetLastError()));
6224  }
6225  else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6226  Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
6227  m.AllocationBase, GetLastError()));
6228  }
6229  errno = EINTR;
6230  }
6231  else {
6232  errno = arg.errnum;
6233  }
6234  }
6235  }
6236 
6237  if (!thr) {
6238  rb_fatal("failed to launch waiter thread:%ld", GetLastError());
6239  }
6240 
6241  return val;
6242 }
6243 
6244 /* License: Ruby's */
6245 char **
6246 rb_w32_get_environ(void)
6247 {
6248  WCHAR *envtop, *env;
6249  char **myenvtop, **myenv;
6250  int num;
6251 
6252  /*
6253  * We avoid values started with `='. If you want to deal those values,
6254  * change this function, and some functions in hash.c which recognize
6255  * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
6256  * CygWin deals these values by changing first `=' to '!'. But we don't
6257  * use such trick and follow cmd.exe's way that just doesn't show these
6258  * values.
6259  *
6260  * This function returns UTF-8 strings.
6261  */
6262  envtop = GetEnvironmentStringsW();
6263  for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6264  if (*env != '=') num++;
6265 
6266  myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
6267  for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
6268  if (*env != '=') {
6269  if (!(*myenv = wstr_to_utf8(env, NULL))) {
6270  break;
6271  }
6272  myenv++;
6273  }
6274  }
6275  *myenv = NULL;
6276  FreeEnvironmentStringsW(envtop);
6277 
6278  return myenvtop;
6279 }
6280 
6281 /* License: Ruby's */
6282 void
6283 rb_w32_free_environ(char **env)
6284 {
6285  char **t = env;
6286 
6287  while (*t) free(*t++);
6288  free(env);
6289 }
6290 
6291 /* License: Ruby's */
6292 rb_pid_t
6293 rb_w32_getpid(void)
6294 {
6295  return GetCurrentProcessId();
6296 }
6297 
6298 
6299 /* License: Ruby's */
6300 rb_pid_t
6301 rb_w32_getppid(void)
6302 {
6303  typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
6304  static query_func *pNtQueryInformationProcess = (query_func *)-1;
6305  rb_pid_t ppid = 0;
6306 
6307  if (pNtQueryInformationProcess == (query_func *)-1)
6308  pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
6309  if (pNtQueryInformationProcess) {
6310  struct {
6311  long ExitStatus;
6312  void* PebBaseAddress;
6313  uintptr_t AffinityMask;
6314  uintptr_t BasePriority;
6315  uintptr_t UniqueProcessId;
6316  uintptr_t ParentProcessId;
6317  } pbi;
6318  ULONG len;
6319  long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
6320  if (!ret) {
6321  ppid = pbi.ParentProcessId;
6322  }
6323  }
6324 
6325  return ppid;
6326 }
6327 
6328 STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
6329 
6330 /* License: Ruby's */
6331 #define set_new_std_handle(newfd, handle) do { \
6332  if ((unsigned)(newfd) > 2) break; \
6333  SetStdHandle(STD_INPUT_HANDLE+(STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)*(newfd), \
6334  (handle)); \
6335  } while (0)
6336 #define set_new_std_fd(newfd) set_new_std_handle(newfd, (HANDLE)rb_w32_get_osfhandle(newfd))
6337 
6338 /* License: Ruby's */
6339 int
6340 rb_w32_dup2(int oldfd, int newfd)
6341 {
6342  int ret;
6343 
6344  if (oldfd == newfd) return newfd;
6345  ret = dup2(oldfd, newfd);
6346  if (ret < 0) return ret;
6347  set_new_std_fd(newfd);
6348  return newfd;
6349 }
6350 
6351 /* License: Ruby's */
6352 int
6353 rb_w32_uopen(const char *file, int oflag, ...)
6354 {
6355  WCHAR *wfile;
6356  int ret;
6357  int pmode;
6358 
6359  va_list arg;
6360  va_start(arg, oflag);
6361  pmode = va_arg(arg, int);
6362  va_end(arg);
6363 
6364  if (!(wfile = utf8_to_wstr(file, NULL)))
6365  return -1;
6366  ret = w32_wopen(wfile, oflag, pmode);
6367  free(wfile);
6368  return ret;
6369 }
6370 
6371 /* License: Ruby's */
6372 static int
6373 check_if_wdir(const WCHAR *wfile)
6374 {
6375  DWORD attr = GetFileAttributesW(wfile);
6376  if (attr == (DWORD)-1L ||
6377  !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6378  check_valid_dir(wfile)) {
6379  return FALSE;
6380  }
6381  errno = EISDIR;
6382  return TRUE;
6383 }
6384 
6385 /* License: Ruby's */
6386 int
6387 rb_w32_open(const char *file, int oflag, ...)
6388 {
6389  WCHAR *wfile;
6390  int ret;
6391  int pmode;
6392 
6393  va_list arg;
6394  va_start(arg, oflag);
6395  pmode = va_arg(arg, int);
6396  va_end(arg);
6397 
6398  if (!(wfile = filecp_to_wstr(file, NULL)))
6399  return -1;
6400  ret = w32_wopen(wfile, oflag, pmode);
6401  free(wfile);
6402  return ret;
6403 }
6404 
6405 /* License: Ruby's */
6406 int
6407 rb_w32_wopen(const WCHAR *file, int oflag, ...)
6408 {
6409  int pmode = 0;
6410 
6411  if (oflag & O_CREAT) {
6412  va_list arg;
6413  va_start(arg, oflag);
6414  pmode = va_arg(arg, int);
6415  va_end(arg);
6416  }
6417 
6418  return w32_wopen(file, oflag, pmode);
6419 }
6420 
6421 static int
6422 w32_wopen(const WCHAR *file, int oflag, int pmode)
6423 {
6424  char flags = 0;
6425  int fd;
6426  DWORD access;
6427  DWORD create;
6428  DWORD attr = FILE_ATTRIBUTE_NORMAL;
6429  SECURITY_ATTRIBUTES sec;
6430  HANDLE h;
6431  int share_delete;
6432 
6433  share_delete = oflag & O_SHARE_DELETE ? FILE_SHARE_DELETE : 0;
6434  oflag &= ~O_SHARE_DELETE;
6435  if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
6436  fd = _wopen(file, oflag, pmode);
6437  if (fd == -1) {
6438  switch (errno) {
6439  case EACCES:
6440  check_if_wdir(file);
6441  break;
6442  case EINVAL:
6443  errno = map_errno(GetLastError());
6444  break;
6445  }
6446  }
6447  return fd;
6448  }
6449 
6450  sec.nLength = sizeof(sec);
6451  sec.lpSecurityDescriptor = NULL;
6452  if (oflag & O_NOINHERIT) {
6453  sec.bInheritHandle = FALSE;
6454  flags |= FNOINHERIT;
6455  }
6456  else {
6457  sec.bInheritHandle = TRUE;
6458  }
6459  oflag &= ~O_NOINHERIT;
6460 
6461  /* always open with binary mode */
6462  oflag &= ~(O_BINARY | O_TEXT);
6463 
6464  switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
6465  case O_RDWR:
6466  access = GENERIC_READ | GENERIC_WRITE;
6467  break;
6468  case O_RDONLY:
6469  access = GENERIC_READ;
6470  break;
6471  case O_WRONLY:
6472  access = GENERIC_WRITE;
6473  break;
6474  default:
6475  errno = EINVAL;
6476  return -1;
6477  }
6478  oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
6479 
6480  switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
6481  case O_CREAT:
6482  create = OPEN_ALWAYS;
6483  break;
6484  case 0:
6485  case O_EXCL:
6486  create = OPEN_EXISTING;
6487  break;
6488  case O_CREAT | O_EXCL:
6489  case O_CREAT | O_EXCL | O_TRUNC:
6490  create = CREATE_NEW;
6491  break;
6492  case O_TRUNC:
6493  case O_TRUNC | O_EXCL:
6494  create = TRUNCATE_EXISTING;
6495  break;
6496  case O_CREAT | O_TRUNC:
6497  create = CREATE_ALWAYS;
6498  break;
6499  default:
6500  errno = EINVAL;
6501  return -1;
6502  }
6503  if (oflag & O_CREAT) {
6504  /* TODO: we need to check umask here, but it's not exported... */
6505  if (!(pmode & S_IWRITE))
6506  attr = FILE_ATTRIBUTE_READONLY;
6507  }
6508  oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6509 
6510  if (oflag & O_TEMPORARY) {
6511  attr |= FILE_FLAG_DELETE_ON_CLOSE;
6512  access |= DELETE;
6513  }
6514  oflag &= ~O_TEMPORARY;
6515 
6516  if (oflag & _O_SHORT_LIVED)
6517  attr |= FILE_ATTRIBUTE_TEMPORARY;
6518  oflag &= ~_O_SHORT_LIVED;
6519 
6520  switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
6521  case 0:
6522  break;
6523  case O_SEQUENTIAL:
6524  attr |= FILE_FLAG_SEQUENTIAL_SCAN;
6525  break;
6526  case O_RANDOM:
6527  attr |= FILE_FLAG_RANDOM_ACCESS;
6528  break;
6529  default:
6530  errno = EINVAL;
6531  return -1;
6532  }
6533  oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6534 
6535  if (oflag & ~O_APPEND) {
6536  errno = EINVAL;
6537  return -1;
6538  }
6539 
6540  /* allocate a C Runtime file handle */
6541  RUBY_CRITICAL {
6542  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6543  fd = _open_osfhandle((intptr_t)h, 0);
6544  CloseHandle(h);
6545  }
6546  if (fd == -1) {
6547  errno = EMFILE;
6548  return -1;
6549  }
6550  RUBY_CRITICAL {
6551  rb_acrt_lowio_lock_fh(fd);
6552  _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6553  _set_osflags(fd, 0);
6554 
6555  h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE | share_delete, &sec, create, attr, NULL);
6556  if (h == INVALID_HANDLE_VALUE) {
6557  DWORD e = GetLastError();
6558  if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
6559  errno = map_errno(e);
6560  rb_acrt_lowio_unlock_fh(fd);
6561  fd = -1;
6562  goto quit;
6563  }
6564 
6565  switch (GetFileType(h)) {
6566  case FILE_TYPE_CHAR:
6567  flags |= FDEV;
6568  break;
6569  case FILE_TYPE_PIPE:
6570  flags |= FPIPE;
6571  break;
6572  case FILE_TYPE_UNKNOWN:
6573  errno = map_errno(GetLastError());
6574  CloseHandle(h);
6575  rb_acrt_lowio_unlock_fh(fd);
6576  fd = -1;
6577  goto quit;
6578  }
6579  if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6580  flags |= FAPPEND;
6581 
6582  _set_osfhnd(fd, (intptr_t)h);
6583  _set_osflags(fd, flags | FOPEN);
6584 
6585  rb_acrt_lowio_unlock_fh(fd);
6586  quit:
6587  ;
6588  }
6589 
6590  return fd;
6591 }
6592 
6593 /* License: Ruby's */
6594 int
6595 rb_w32_fclose(FILE *fp)
6596 {
6597  int fd = fileno(fp);
6598  SOCKET sock = TO_SOCKET(fd);
6599  int save_errno = errno;
6600 
6601  if (fflush(fp)) return -1;
6602  if (!is_socket(sock)) {
6603  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6604  return fclose(fp);
6605  }
6606  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6607  fclose(fp);
6608  errno = save_errno;
6609  if (closesocket(sock) == SOCKET_ERROR) {
6610  errno = map_errno(WSAGetLastError());
6611  return -1;
6612  }
6613  return 0;
6614 }
6615 
6616 /* License: Ruby's */
6617 int
6618 rb_w32_pipe(int fds[2])
6619 {
6620  static long serial = 0;
6621  static const char prefix[] = "\\\\.\\pipe\\ruby";
6622  enum {
6623  width_of_prefix = (int)sizeof(prefix) - 1,
6624  width_of_pid = (int)sizeof(rb_pid_t) * 2,
6625  width_of_serial = (int)sizeof(serial) * 2,
6626  width_of_ids = width_of_pid + 1 + width_of_serial + 1
6627  };
6628  char name[sizeof(prefix) + width_of_ids];
6629  SECURITY_ATTRIBUTES sec;
6630  HANDLE hRead, hWrite, h;
6631  int fdRead, fdWrite;
6632  int ret;
6633 
6634  memcpy(name, prefix, width_of_prefix);
6635  snprintf(name + width_of_prefix, width_of_ids, "%.*"PRI_PIDT_PREFIX"x-%.*lx",
6636  width_of_pid, rb_w32_getpid(), width_of_serial, InterlockedIncrement(&serial)-1);
6637 
6638  sec.nLength = sizeof(sec);
6639  sec.lpSecurityDescriptor = NULL;
6640  sec.bInheritHandle = FALSE;
6641 
6642  RUBY_CRITICAL {
6643  hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6644  0, 2, 65536, 65536, 0, &sec);
6645  }
6646  if (hRead == INVALID_HANDLE_VALUE) {
6647  DWORD err = GetLastError();
6648  if (err == ERROR_PIPE_BUSY)
6649  errno = EMFILE;
6650  else
6651  errno = map_errno(GetLastError());
6652  return -1;
6653  }
6654 
6655  RUBY_CRITICAL {
6656  hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6657  OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6658  }
6659  if (hWrite == INVALID_HANDLE_VALUE) {
6660  errno = map_errno(GetLastError());
6661  CloseHandle(hRead);
6662  return -1;
6663  }
6664 
6665  RUBY_CRITICAL do {
6666  ret = 0;
6667  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6668  fdRead = _open_osfhandle((intptr_t)h, 0);
6669  CloseHandle(h);
6670  if (fdRead == -1) {
6671  errno = EMFILE;
6672  CloseHandle(hWrite);
6673  CloseHandle(hRead);
6674  ret = -1;
6675  break;
6676  }
6677 
6678  rb_acrt_lowio_lock_fh(fdRead);
6679  _set_osfhnd(fdRead, (intptr_t)hRead);
6680  _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
6681  rb_acrt_lowio_unlock_fh(fdRead);
6682  } while (0);
6683  if (ret)
6684  return ret;
6685 
6686  RUBY_CRITICAL do {
6687  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6688  fdWrite = _open_osfhandle((intptr_t)h, 0);
6689  CloseHandle(h);
6690  if (fdWrite == -1) {
6691  errno = EMFILE;
6692  CloseHandle(hWrite);
6693  ret = -1;
6694  break;
6695  }
6696  rb_acrt_lowio_lock_fh(fdWrite);
6697  _set_osfhnd(fdWrite, (intptr_t)hWrite);
6698  _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
6699  rb_acrt_lowio_unlock_fh(fdWrite);
6700  } while (0);
6701  if (ret) {
6702  rb_w32_close(fdRead);
6703  return ret;
6704  }
6705 
6706  fds[0] = fdRead;
6707  fds[1] = fdWrite;
6708 
6709  return 0;
6710 }
6711 
6712 /* License: Ruby's */
6713 static int
6714 console_emulator_p(void)
6715 {
6716 #ifdef _WIN32_WCE
6717  return FALSE;
6718 #else
6719  const void *const func = WriteConsoleW;
6720  HMODULE k;
6721  MEMORY_BASIC_INFORMATION m;
6722 
6723  memset(&m, 0, sizeof(m));
6724  if (!VirtualQuery(func, &m, sizeof(m))) {
6725  return FALSE;
6726  }
6727  k = GetModuleHandle("kernel32.dll");
6728  if (!k) return FALSE;
6729  return (HMODULE)m.AllocationBase != k;
6730 #endif
6731 }
6732 
6733 /* License: Ruby's */
6734 static struct constat *
6735 constat_handle(HANDLE h)
6736 {
6737  st_data_t data;
6738  struct constat *p = NULL;
6739  thread_exclusive(conlist) {
6740  if (!conlist) {
6741  if (console_emulator_p()) {
6742  conlist = conlist_disabled;
6743  continue;
6744  }
6745  conlist = st_init_numtable();
6746  install_vm_exit_handler();
6747  }
6748  else if (conlist == conlist_disabled) {
6749  continue;
6750  }
6751  if (st_lookup(conlist, (st_data_t)h, &data)) {
6752  p = (struct constat *)data;
6753  }
6754  else {
6755  CONSOLE_SCREEN_BUFFER_INFO csbi;
6756  p = ALLOC(struct constat);
6757  p->vt100.state = constat_init;
6758  p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6759  p->vt100.reverse = 0;
6760  p->vt100.saved.X = p->vt100.saved.Y = 0;
6761  if (GetConsoleScreenBufferInfo(h, &csbi)) {
6762  p->vt100.attr = csbi.wAttributes;
6763  }
6764  st_insert(conlist, (st_data_t)h, (st_data_t)p);
6765  }
6766  }
6767  return p;
6768 }
6769 
6770 /* License: Ruby's */
6771 static void
6772 constat_reset(HANDLE h)
6773 {
6774  st_data_t data;
6775  struct constat *p;
6776  thread_exclusive(conlist) {
6777  if (!conlist || conlist == conlist_disabled) continue;
6778  if (!st_lookup(conlist, (st_data_t)h, &data)) continue;
6779  p = (struct constat *)data;
6780  p->vt100.state = constat_init;
6781  }
6782 }
6783 
6784 #define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6785 #define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6786 
6787 #define constat_attr_color_reverse(attr) \
6788  ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6789  (((attr) & FOREGROUND_MASK) << 4) | \
6790  (((attr) & BACKGROUND_MASK) >> 4)
6791 
6792 /* License: Ruby's */
6793 static WORD
6794 constat_attr(int count, const int *seq, WORD attr, WORD default_attr, int *reverse)
6795 {
6796  int rev = *reverse;
6797  WORD bold;
6798 
6799  if (!count) return attr;
6800  if (rev) attr = constat_attr_color_reverse(attr);
6801  bold = attr & FOREGROUND_INTENSITY;
6802  attr &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
6803 
6804  while (count-- > 0) {
6805  switch (*seq++) {
6806  case 0:
6807  attr = default_attr;
6808  rev = 0;
6809  bold = 0;
6810  break;
6811  case 1:
6812  bold = FOREGROUND_INTENSITY;
6813  break;
6814  case 4:
6815 #ifndef COMMON_LVB_UNDERSCORE
6816 #define COMMON_LVB_UNDERSCORE 0x8000
6817 #endif
6818  attr |= COMMON_LVB_UNDERSCORE;
6819  break;
6820  case 7:
6821  rev = 1;
6822  break;
6823 
6824  case 30:
6825  attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
6826  break;
6827  case 17:
6828  case 31:
6829  attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)) | FOREGROUND_RED;
6830  break;
6831  case 18:
6832  case 32:
6833  attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_RED)) | FOREGROUND_GREEN;
6834  break;
6835  case 19:
6836  case 33:
6837  attr = (attr & ~FOREGROUND_BLUE) | FOREGROUND_GREEN | FOREGROUND_RED;
6838  break;
6839  case 20:
6840  case 34:
6841  attr = (attr & ~(FOREGROUND_GREEN | FOREGROUND_RED)) | FOREGROUND_BLUE;
6842  break;
6843  case 21:
6844  case 35:
6845  attr = (attr & ~FOREGROUND_GREEN) | FOREGROUND_BLUE | FOREGROUND_RED;
6846  break;
6847  case 22:
6848  case 36:
6849  attr = (attr & ~FOREGROUND_RED) | FOREGROUND_BLUE | FOREGROUND_GREEN;
6850  break;
6851  case 23:
6852  case 37:
6853  attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6854  break;
6855 
6856  case 40:
6857  attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
6858  break;
6859  case 41:
6860  attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN)) | BACKGROUND_RED;
6861  break;
6862  case 42:
6863  attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_RED)) | BACKGROUND_GREEN;
6864  break;
6865  case 43:
6866  attr = (attr & ~BACKGROUND_BLUE) | BACKGROUND_GREEN | BACKGROUND_RED;
6867  break;
6868  case 44:
6869  attr = (attr & ~(BACKGROUND_GREEN | BACKGROUND_RED)) | BACKGROUND_BLUE;
6870  break;
6871  case 45:
6872  attr = (attr & ~BACKGROUND_GREEN) | BACKGROUND_BLUE | BACKGROUND_RED;
6873  break;
6874  case 46:
6875  attr = (attr & ~BACKGROUND_RED) | BACKGROUND_BLUE | BACKGROUND_GREEN;
6876  break;
6877  case 47:
6878  attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
6879  break;
6880  }
6881  }
6882  attr |= bold;
6883  if (rev) attr = constat_attr_color_reverse(attr);
6884  *reverse = rev;
6885  return attr;
6886 }
6887 
6888 /* License: Ruby's */
6889 static void
6890 constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
6891 {
6892  DWORD written;
6893 
6894  FillConsoleOutputAttribute(handle, attr, len, pos, &written);
6895  FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
6896 }
6897 
6898 /* License: Ruby's */
6899 static void
6900 constat_apply(HANDLE handle, struct constat *s, WCHAR w)
6901 {
6902  CONSOLE_SCREEN_BUFFER_INFO csbi;
6903  const int *seq = s->vt100.seq;
6904  int count = s->vt100.state;
6905  int arg0, arg1 = 1;
6906  COORD pos;
6907 
6908  if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
6909  arg0 = (count > 0 && seq[0] > 0);
6910  if (arg0) arg1 = seq[0];
6911  switch (w) {
6912  case L'm':
6913  SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr, &s->vt100.reverse));
6914  break;
6915  case L'F':
6916  csbi.dwCursorPosition.X = 0;
6917  case L'A':
6918  csbi.dwCursorPosition.Y -= arg1;
6919  if (csbi.dwCursorPosition.Y < csbi.srWindow.Top)
6920  csbi.dwCursorPosition.Y = csbi.srWindow.Top;
6921  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6922  break;
6923  case L'E':
6924  csbi.dwCursorPosition.X = 0;
6925  case L'B':
6926  case L'e':
6927  csbi.dwCursorPosition.Y += arg1;
6928  if (csbi.dwCursorPosition.Y > csbi.srWindow.Bottom)
6929  csbi.dwCursorPosition.Y = csbi.srWindow.Bottom;
6930  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6931  break;
6932  case L'C':
6933  csbi.dwCursorPosition.X += arg1;
6934  if (csbi.dwCursorPosition.X >= csbi.srWindow.Right)
6935  csbi.dwCursorPosition.X = csbi.srWindow.Right;
6936  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6937  break;
6938  case L'D':
6939  csbi.dwCursorPosition.X -= arg1;
6940  if (csbi.dwCursorPosition.X < csbi.srWindow.Left)
6941  csbi.dwCursorPosition.X = csbi.srWindow.Left;
6942  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6943  break;
6944  case L'G':
6945  case L'`':
6946  arg1 += csbi.srWindow.Left;
6947  if (arg1 > csbi.srWindow.Right)
6948  arg1 = csbi.srWindow.Right;
6949  csbi.dwCursorPosition.X = arg1;
6950  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6951  break;
6952  case L'd':
6953  arg1 += csbi.srWindow.Top;
6954  if (arg1 > csbi.srWindow.Bottom)
6955  arg1 = csbi.srWindow.Bottom;
6956  csbi.dwCursorPosition.Y = arg1;
6957  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6958  break;
6959  case L'H':
6960  case L'f':
6961  pos.Y = arg1 + csbi.srWindow.Top - 1;
6962  if (pos.Y > csbi.srWindow.Bottom) pos.Y = csbi.srWindow.Bottom;
6963  if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
6964  pos.X = arg1 + csbi.srWindow.Left - 1;
6965  if (pos.X > csbi.srWindow.Right) pos.X = csbi.srWindow.Right;
6966  SetConsoleCursorPosition(handle, pos);
6967  break;
6968  case L'J':
6969  switch (arg0 ? arg1 : 0) {
6970  case 0: /* erase after cursor */
6971  constat_clear(handle, csbi.wAttributes,
6972  (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.dwCursorPosition.Y + 1)
6973  - csbi.dwCursorPosition.X),
6974  csbi.dwCursorPosition);
6975  break;
6976  case 1: /* erase before *and* cursor */
6977  pos.X = 0;
6978  pos.Y = csbi.srWindow.Top;
6979  constat_clear(handle, csbi.wAttributes,
6980  (csbi.dwSize.X * (csbi.dwCursorPosition.Y - csbi.srWindow.Top)
6981  + csbi.dwCursorPosition.X + 1),
6982  pos);
6983  break;
6984  case 2: /* erase entire screen */
6985  pos.X = 0;
6986  pos.Y = csbi.srWindow.Top;
6987  constat_clear(handle, csbi.wAttributes,
6988  (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1)),
6989  pos);
6990  break;
6991  case 3: /* erase entire screen */
6992  pos.X = 0;
6993  pos.Y = 0;
6994  constat_clear(handle, csbi.wAttributes,
6995  (csbi.dwSize.X * csbi.dwSize.Y),
6996  pos);
6997  break;
6998  }
6999  break;
7000  case L'K':
7001  switch (arg0 ? arg1 : 0) {
7002  case 0: /* erase after cursor */
7003  constat_clear(handle, csbi.wAttributes,
7004  (csbi.dwSize.X - csbi.dwCursorPosition.X),
7005  csbi.dwCursorPosition);
7006  break;
7007  case 1: /* erase before *and* cursor */
7008  pos.X = 0;
7009  pos.Y = csbi.dwCursorPosition.Y;
7010  constat_clear(handle, csbi.wAttributes,
7011  csbi.dwCursorPosition.X + 1, pos);
7012  break;
7013  case 2: /* erase entire line */
7014  pos.X = 0;
7015  pos.Y = csbi.dwCursorPosition.Y;
7016  constat_clear(handle, csbi.wAttributes,
7017  csbi.dwSize.X, pos);
7018  break;
7019  }
7020  break;
7021  case L's':
7022  s->vt100.saved = csbi.dwCursorPosition;
7023  break;
7024  case L'u':
7025  SetConsoleCursorPosition(handle, s->vt100.saved);
7026  break;
7027  case L'h':
7028  if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7029  CONSOLE_CURSOR_INFO cci;
7030  GetConsoleCursorInfo(handle, &cci);
7031  cci.bVisible = TRUE;
7032  SetConsoleCursorInfo(handle, &cci);
7033  }
7034  break;
7035  case L'l':
7036  if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7037  CONSOLE_CURSOR_INFO cci;
7038  GetConsoleCursorInfo(handle, &cci);
7039  cci.bVisible = FALSE;
7040  SetConsoleCursorInfo(handle, &cci);
7041  }
7042  break;
7043  }
7044 }
7045 
7046 /* get rid of console writing bug; assume WriteConsole and WriteFile
7047  * on a console share the same limit. */
7048 static const long MAXSIZE_CONSOLE_WRITING = 31366;
7049 
7050 /* License: Ruby's */
7051 static long
7052 constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
7053 {
7054  const WCHAR *ptr = *ptrp;
7055  long rest, len = *lenp;
7056  while (len-- > 0) {
7057  WCHAR wc = *ptr++;
7058  if (wc == 0x1b) {
7059  rest = *lenp - len - 1;
7060  if (s->vt100.state == constat_esc) {
7061  rest++; /* reuse this ESC */
7062  }
7063  s->vt100.state = constat_init;
7064  if (len > 0 && *ptr != L'[') continue;
7065  s->vt100.state = constat_esc;
7066  }
7067  else if (s->vt100.state == constat_esc) {
7068  if (wc != L'[') {
7069  /* TODO: supply dropped ESC at beginning */
7070  s->vt100.state = constat_init;
7071  continue;
7072  }
7073  rest = *lenp - len - 1;
7074  if (rest > 0) --rest;
7075  s->vt100.state = constat_seq;
7076  s->vt100.seq[0] = 0;
7077  }
7078  else if (s->vt100.state >= constat_seq) {
7079  if (wc >= L'0' && wc <= L'9') {
7080  if (s->vt100.state < (int)numberof(s->vt100.seq)) {
7081  int *seq = &s->vt100.seq[s->vt100.state];
7082  *seq = (*seq * 10) + (wc - L'0');
7083  }
7084  }
7085  else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
7086  s->vt100.seq[s->vt100.state++] = -1;
7087  }
7088  else {
7089  do {
7090  if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
7091  s->vt100.seq[s->vt100.state] = 0;
7092  }
7093  else {
7094  s->vt100.state = (int)numberof(s->vt100.seq);
7095  }
7096  } while (0);
7097  if (wc != L';') {
7098  constat_apply(h, s, wc);
7099  s->vt100.state = constat_init;
7100  }
7101  }
7102  rest = 0;
7103  }
7104  else if ((rest = *lenp - len) < MAXSIZE_CONSOLE_WRITING) {
7105  continue;
7106  }
7107  *ptrp = ptr;
7108  *lenp = len;
7109  return rest;
7110  }
7111  len = *lenp;
7112  *ptrp = ptr;
7113  *lenp = 0;
7114  return len;
7115 }
7116 
7117 
7118 /* License: Ruby's */
7119 int
7120 rb_w32_close(int fd)
7121 {
7122  SOCKET sock = TO_SOCKET(fd);
7123  int save_errno = errno;
7124 
7125  if (!is_socket(sock)) {
7126  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7127  constat_delete((HANDLE)sock);
7128  return _close(fd);
7129  }
7130  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
7131  socklist_delete(&sock, NULL);
7132  _close(fd);
7133  errno = save_errno;
7134  if (closesocket(sock) == SOCKET_ERROR) {
7135  errno = map_errno(WSAGetLastError());
7136  return -1;
7137  }
7138  return 0;
7139 }
7140 
7141 #ifndef INVALID_SET_FILE_POINTER
7142 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
7143 #endif
7144 
7145 static int
7146 setup_overlapped(OVERLAPPED *ol, int fd, int iswrite, rb_off_t *_offset)
7147 {
7148  memset(ol, 0, sizeof(*ol));
7149 
7150  // On mode:a, it can write only FILE_END.
7151  // On mode:a+, though it can write only FILE_END,
7152  // it can read from everywhere.
7153  DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
7154 
7155  if (_offset) {
7156  // Explicit offset was provided (pread/pwrite) - use it:
7157  uint64_t offset = *_offset;
7158  ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
7159  ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
7160 
7161  // Update _offset with the current offset:
7162  LARGE_INTEGER seek_offset = {0}, current_offset = {0};
7163  if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, &current_offset, seek_method)) {
7164  DWORD last_error = GetLastError();
7165  if (last_error != NO_ERROR) {
7166  errno = map_errno(last_error);
7167  return -1;
7168  }
7169  }
7170 
7171  // As we need to restore the current offset later, we save it here:
7172  *_offset = current_offset.QuadPart;
7173  }
7174  else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7175  LONG high = 0;
7176  DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method);
7177 
7178  if (low == INVALID_SET_FILE_POINTER) {
7179  DWORD err = GetLastError();
7180  if (err != NO_ERROR) {
7181  errno = map_errno(err);
7182  return -1;
7183  }
7184  }
7185 
7186  ol->Offset = low;
7187  ol->OffsetHigh = high;
7188  }
7189 
7190  ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7191  if (!ol->hEvent) {
7192  errno = map_errno(GetLastError());
7193  return -1;
7194  }
7195  return 0;
7196 }
7197 
7198 static void
7199 finish_overlapped(OVERLAPPED *ol, int fd, DWORD size, rb_off_t *_offset)
7200 {
7201  CloseHandle(ol->hEvent);
7202 
7203  if (_offset) {
7204  // If we were doing a `pread`/`pwrite`, we need to restore the current that was saved in setup_overlapped:
7205  DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN;
7206 
7207  LARGE_INTEGER seek_offset = {0};
7208  if (seek_method == FILE_BEGIN) {
7209  seek_offset.QuadPart = *_offset;
7210  }
7211 
7212  SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method);
7213  }
7214  else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7215  LONG high = ol->OffsetHigh;
7216  DWORD low = ol->Offset + size;
7217  if (low < ol->Offset)
7218  ++high;
7219  SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
7220  }
7221 }
7222 
7223 #undef read
7224 /* License: Ruby's */
7225 static ssize_t
7226 rb_w32_read_internal(int fd, void *buf, size_t size, rb_off_t *offset)
7227 {
7228  SOCKET sock = TO_SOCKET(fd);
7229  DWORD read;
7230  DWORD wait;
7231  DWORD err;
7232  size_t len;
7233  size_t ret;
7234  OVERLAPPED ol;
7235  BOOL isconsole;
7236  BOOL islineinput = FALSE;
7237  int start = 0;
7238 
7239  if (is_socket(sock))
7240  return rb_w32_recv(fd, buf, size, 0);
7241 
7242  // validate fd by using _get_osfhandle() because we cannot access _nhandle
7243  if (_get_osfhandle(fd) == -1) {
7244  return -1;
7245  }
7246 
7247  if (!offset && _osfile(fd) & FTEXT) {
7248  return _read(fd, buf, size);
7249  }
7250 
7251  rb_acrt_lowio_lock_fh(fd);
7252 
7253  if (!size || _osfile(fd) & FEOFLAG) {
7254  _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7255  rb_acrt_lowio_unlock_fh(fd);
7256  return 0;
7257  }
7258 
7259  ret = 0;
7260  isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
7261  if (isconsole) {
7262  DWORD mode;
7263  GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
7264  islineinput = (mode & ENABLE_LINE_INPUT) != 0;
7265  }
7266  retry:
7267  /* get rid of console reading bug */
7268  if (isconsole) {
7269  constat_reset((HANDLE)_osfhnd(fd));
7270  if (start)
7271  len = 1;
7272  else {
7273  len = 0;
7274  start = 1;
7275  }
7276  }
7277  else
7278  len = size;
7279  size -= len;
7280 
7281  if (setup_overlapped(&ol, fd, FALSE, offset)) {
7282  rb_acrt_lowio_unlock_fh(fd);
7283  return -1;
7284  }
7285 
7286  if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, &ol)) {
7287  err = GetLastError();
7288  if (err == ERROR_NO_DATA && (_osfile(fd) & FPIPE)) {
7289  DWORD state;
7290  if (GetNamedPipeHandleState((HANDLE)_osfhnd(fd), &state, NULL, NULL, NULL, NULL, 0) && (state & PIPE_NOWAIT)) {
7291  errno = EWOULDBLOCK;
7292  }
7293  else {
7294  errno = map_errno(err);
7295  }
7296  rb_acrt_lowio_unlock_fh(fd);
7297  return -1;
7298  }
7299  else if (err != ERROR_IO_PENDING) {
7300  CloseHandle(ol.hEvent);
7301  if (err == ERROR_ACCESS_DENIED)
7302  errno = EBADF;
7303  else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
7304  rb_acrt_lowio_unlock_fh(fd);
7305  return 0;
7306  }
7307  else
7308  errno = map_errno(err);
7309 
7310  rb_acrt_lowio_unlock_fh(fd);
7311  return -1;
7312  }
7313 
7314  wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7315  if (wait != WAIT_OBJECT_0) {
7316  if (wait == WAIT_OBJECT_0 + 1)
7317  errno = EINTR;
7318  else
7319  errno = map_errno(GetLastError());
7320  CloseHandle(ol.hEvent);
7321  CancelIo((HANDLE)_osfhnd(fd));
7322  rb_acrt_lowio_unlock_fh(fd);
7323  return -1;
7324  }
7325 
7326  if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
7327  (err = GetLastError()) != ERROR_HANDLE_EOF) {
7328  int ret = 0;
7329  if (err != ERROR_BROKEN_PIPE) {
7330  errno = map_errno(err);
7331  ret = -1;
7332  }
7333  CloseHandle(ol.hEvent);
7334  CancelIo((HANDLE)_osfhnd(fd));
7335  rb_acrt_lowio_unlock_fh(fd);
7336  return ret;
7337  }
7338  }
7339  else {
7340  err = GetLastError();
7341  errno = map_errno(err);
7342  }
7343 
7344  finish_overlapped(&ol, fd, read, offset);
7345 
7346  ret += read;
7347  if (read >= len) {
7348  buf = (char *)buf + read;
7349  if (err != ERROR_OPERATION_ABORTED &&
7350  !(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
7351  goto retry;
7352  }
7353  if (read == 0)
7354  _set_osflags(fd, _osfile(fd) | FEOFLAG);
7355 
7356 
7357  rb_acrt_lowio_unlock_fh(fd);
7358 
7359  return ret;
7360 }
7361 
7362 #undef write
7363 /* License: Ruby's */
7364 static ssize_t
7365 rb_w32_write_internal(int fd, const void *buf, size_t size, rb_off_t *offset)
7366 {
7367  SOCKET sock = TO_SOCKET(fd);
7368  DWORD written;
7369  DWORD wait;
7370  DWORD err;
7371  size_t len;
7372  size_t ret;
7373  OVERLAPPED ol;
7374 
7375  if (is_socket(sock))
7376  return rb_w32_send(fd, buf, size, 0);
7377 
7378  // validate fd by using _get_osfhandle() because we cannot access _nhandle
7379  if (_get_osfhandle(fd) == -1) {
7380  return -1;
7381  }
7382 
7383  // If an offset is given, we can't use `_write`.
7384  if (!offset && (_osfile(fd) & FTEXT) &&
7385  (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
7386  ssize_t w = _write(fd, buf, size);
7387  if (w == (ssize_t)-1 && errno == EINVAL) {
7388  errno = map_errno(GetLastError());
7389  }
7390  return w;
7391  }
7392 
7393  rb_acrt_lowio_lock_fh(fd);
7394 
7395  if (!size || _osfile(fd) & FEOFLAG) {
7396  rb_acrt_lowio_unlock_fh(fd);
7397  return 0;
7398  }
7399 
7400  ret = 0;
7401  retry:
7402  len = (_osfile(fd) & FDEV) ? min(MAXSIZE_CONSOLE_WRITING, size) : size;
7403  size -= len;
7404  retry2:
7405 
7406  // Provide the requested offset.
7407  if (setup_overlapped(&ol, fd, TRUE, offset)) {
7408  rb_acrt_lowio_unlock_fh(fd);
7409  return -1;
7410  }
7411 
7412  if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, &ol)) {
7413  err = GetLastError();
7414  if (err != ERROR_IO_PENDING) {
7415  CloseHandle(ol.hEvent);
7416  if (err == ERROR_ACCESS_DENIED)
7417  errno = EBADF;
7418  else
7419  errno = map_errno(err);
7420 
7421  rb_acrt_lowio_unlock_fh(fd);
7422  return -1;
7423  }
7424 
7425  wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7426  if (wait != WAIT_OBJECT_0) {
7427  if (wait == WAIT_OBJECT_0 + 1)
7428  errno = EINTR;
7429  else
7430  errno = map_errno(GetLastError());
7431  CloseHandle(ol.hEvent);
7432  CancelIo((HANDLE)_osfhnd(fd));
7433  rb_acrt_lowio_unlock_fh(fd);
7434  return -1;
7435  }
7436 
7437  if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) {
7438  errno = map_errno(GetLastError());
7439  CloseHandle(ol.hEvent);
7440  CancelIo((HANDLE)_osfhnd(fd));
7441  rb_acrt_lowio_unlock_fh(fd);
7442  return -1;
7443  }
7444  }
7445 
7446  finish_overlapped(&ol, fd, written, offset);
7447 
7448  ret += written;
7449  if (written == len) {
7450  buf = (const char *)buf + len;
7451  if (size > 0)
7452  goto retry;
7453  }
7454  if (ret == 0) {
7455  size_t newlen = len / 2;
7456  if (newlen > 0) {
7457  size += len - newlen;
7458  len = newlen;
7459  goto retry2;
7460  }
7461  ret = -1;
7462  errno = EWOULDBLOCK;
7463  }
7464 
7465  rb_acrt_lowio_unlock_fh(fd);
7466 
7467  return ret;
7468 }
7469 
7470 ssize_t
7471 rb_w32_read(int fd, void *buf, size_t size)
7472 {
7473  return rb_w32_read_internal(fd, buf, size, NULL);
7474 }
7475 
7476 ssize_t
7477 rb_w32_write(int fd, const void *buf, size_t size)
7478 {
7479  return rb_w32_write_internal(fd, buf, size, NULL);
7480 }
7481 
7482 ssize_t
7483 rb_w32_pread(int descriptor, void *base, size_t size, rb_off_t offset)
7484 {
7485  return rb_w32_read_internal(descriptor, base, size, &offset);
7486 }
7487 
7488 ssize_t
7489 rb_w32_pwrite(int descriptor, const void *base, size_t size, rb_off_t offset)
7490 {
7491  return rb_w32_write_internal(descriptor, base, size, &offset);
7492 }
7493 
7494 /* License: Ruby's */
7495 long
7496 rb_w32_write_console(uintptr_t strarg, int fd)
7497 {
7498  HANDLE handle;
7499  DWORD dwMode, reslen;
7500  VALUE str = strarg;
7501  int encindex;
7502  WCHAR *wbuffer = 0;
7503  const WCHAR *ptr, *next;
7504  struct constat *s;
7505  long len;
7506 
7507  handle = (HANDLE)_osfhnd(fd);
7508  if (!GetConsoleMode(handle, &dwMode))
7509  return -1L;
7510 
7511  s = constat_handle(handle);
7512  if (!s) return -1L;
7513  encindex = ENCODING_GET(str);
7514  switch (encindex) {
7515  default:
7516  if (!rb_econv_has_convpath_p(rb_enc_name(rb_enc_from_index(encindex)), "UTF-8"))
7517  return -1L;
7518  str = rb_str_conv_enc_opts(str, NULL, rb_enc_from_index(ENCINDEX_UTF_8),
7520  /* fall through */
7521  case ENCINDEX_US_ASCII:
7522  case ENCINDEX_ASCII_8BIT:
7523  /* assume UTF-8 */
7524  case ENCINDEX_UTF_8:
7525  ptr = wbuffer = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(str), RSTRING_LEN(str), &len);
7526  if (!ptr) return -1L;
7527  break;
7528  case ENCINDEX_UTF_16LE:
7529  ptr = (const WCHAR *)RSTRING_PTR(str);
7530  len = RSTRING_LEN(str) / sizeof(WCHAR);
7531  break;
7532  }
7533  reslen = 0;
7534  if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7535  if (!WriteConsoleW(handle, ptr, len, &reslen, NULL))
7536  reslen = (DWORD)-1L;
7537  }
7538  else {
7539  while (len > 0) {
7540  long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
7541  reslen += next - ptr;
7542  if (curlen > 0) {
7543  DWORD written;
7544  if (!WriteConsoleW(handle, ptr, curlen, &written, NULL)) {
7545  reslen = (DWORD)-1L;
7546  break;
7547  }
7548  }
7549  ptr = next;
7550  }
7551  }
7552  RB_GC_GUARD(str);
7553  free(wbuffer);
7554  return (long)reslen;
7555 }
7556 
7557 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7558 /* License: Ruby's */
7559 static int
7560 unixtime_to_filetime(time_t time, FILETIME *ft)
7561 {
7562  ULARGE_INTEGER tmp;
7563 
7564  tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7565  ft->dwLowDateTime = tmp.LowPart;
7566  ft->dwHighDateTime = tmp.HighPart;
7567  return 0;
7568 }
7569 #endif
7570 
7571 /* License: Ruby's */
7572 static int
7573 timespec_to_filetime(const struct timespec *ts, FILETIME *ft)
7574 {
7575  ULARGE_INTEGER tmp;
7576 
7577  tmp.QuadPart = ((LONG_LONG)ts->tv_sec + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7578  tmp.QuadPart += ts->tv_nsec / 100;
7579  ft->dwLowDateTime = tmp.LowPart;
7580  ft->dwHighDateTime = tmp.HighPart;
7581  return 0;
7582 }
7583 
7584 /* License: Ruby's */
7585 static int
7586 wutimensat(int dirfd, const WCHAR *path, const struct timespec *times, int flags)
7587 {
7588  HANDLE hFile;
7589  FILETIME atime, mtime;
7590  struct stati128 stat;
7591  int ret = 0;
7592 
7593  /* TODO: When path is absolute, dirfd should be ignored. */
7594  if (dirfd != AT_FDCWD) {
7595  errno = ENOSYS;
7596  return -1;
7597  }
7598 
7599  if (flags != 0) {
7600  errno = EINVAL; /* AT_SYMLINK_NOFOLLOW isn't supported. */
7601  return -1;
7602  }
7603 
7604  if (wstati128(path, &stat, FALSE)) {
7605  return -1;
7606  }
7607 
7608  if (times) {
7609  if (timespec_to_filetime(&times[0], &atime)) {
7610  return -1;
7611  }
7612  if (timespec_to_filetime(&times[1], &mtime)) {
7613  return -1;
7614  }
7615  }
7616  else {
7617  GetSystemTimePreciseAsFileTime(&atime);
7618  mtime = atime;
7619  }
7620 
7621  RUBY_CRITICAL {
7622  const DWORD attr = GetFileAttributesW(path);
7623  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7624  SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7625  hFile = open_special(path, GENERIC_WRITE, 0);
7626  if (hFile == INVALID_HANDLE_VALUE) {
7627  errno = map_errno(GetLastError());
7628  ret = -1;
7629  }
7630  else {
7631  if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7632  errno = map_errno(GetLastError());
7633  ret = -1;
7634  }
7635  CloseHandle(hFile);
7636  }
7637  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7638  SetFileAttributesW(path, attr);
7639  }
7640 
7641  return ret;
7642 }
7643 
7644 /* License: Ruby's */
7645 static int
7646 w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags, UINT cp)
7647 {
7648  WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7649  int ret = -1;
7650 
7651  if (wpath) {
7652  ret = wutimensat(dirfd, wpath, times, flags);
7653  free(wpath);
7654  }
7655  return ret;
7656 }
7657 
7658 /* License: Ruby's */
7659 int
7660 rb_w32_uutime(const char *path, const struct utimbuf *times)
7661 {
7662  struct timespec ts[2];
7663 
7664  ts[0].tv_sec = times->actime;
7665  ts[0].tv_nsec = 0;
7666  ts[1].tv_sec = times->modtime;
7667  ts[1].tv_nsec = 0;
7668  return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7669 }
7670 
7671 /* License: Ruby's */
7672 int
7673 rb_w32_utime(const char *path, const struct utimbuf *times)
7674 {
7675  struct timespec ts[2];
7676 
7677  ts[0].tv_sec = times->actime;
7678  ts[0].tv_nsec = 0;
7679  ts[1].tv_sec = times->modtime;
7680  ts[1].tv_nsec = 0;
7681  return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7682 }
7683 
7684 /* License: Ruby's */
7685 int
7686 rb_w32_uutimes(const char *path, const struct timeval *times)
7687 {
7688  struct timespec ts[2];
7689 
7690  ts[0].tv_sec = times[0].tv_sec;
7691  ts[0].tv_nsec = times[0].tv_usec * 1000;
7692  ts[1].tv_sec = times[1].tv_sec;
7693  ts[1].tv_nsec = times[1].tv_usec * 1000;
7694  return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7695 }
7696 
7697 /* License: Ruby's */
7698 int
7699 rb_w32_utimes(const char *path, const struct timeval *times)
7700 {
7701  struct timespec ts[2];
7702 
7703  ts[0].tv_sec = times[0].tv_sec;
7704  ts[0].tv_nsec = times[0].tv_usec * 1000;
7705  ts[1].tv_sec = times[1].tv_sec;
7706  ts[1].tv_nsec = times[1].tv_usec * 1000;
7707  return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7708 }
7709 
7710 /* License: Ruby's */
7711 int
7712 rb_w32_uutimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7713 {
7714  return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7715 }
7716 
7717 /* License: Ruby's */
7718 int
7719 rb_w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7720 {
7721  return w32_utimensat(dirfd, path, times, flags, filecp());
7722 }
7723 
7724 /* License: Ruby's */
7725 int
7726 rb_w32_uchdir(const char *path)
7727 {
7728  WCHAR *wpath;
7729  int ret;
7730 
7731  if (!(wpath = utf8_to_wstr(path, NULL)))
7732  return -1;
7733  ret = _wchdir(wpath);
7734  free(wpath);
7735  return ret;
7736 }
7737 
7738 /* License: Ruby's */
7739 static int
7740 wmkdir(const WCHAR *wpath, int mode)
7741 {
7742  int ret = -1;
7743 
7744  RUBY_CRITICAL do {
7745  if (CreateDirectoryW(wpath, NULL) == FALSE) {
7746  errno = map_errno(GetLastError());
7747  break;
7748  }
7749  if (_wchmod(wpath, mode) == -1) {
7750  RemoveDirectoryW(wpath);
7751  break;
7752  }
7753  ret = 0;
7754  } while (0);
7755  return ret;
7756 }
7757 
7758 /* License: Ruby's */
7759 int
7760 rb_w32_umkdir(const char *path, int mode)
7761 {
7762  WCHAR *wpath;
7763  int ret;
7764 
7765  if (!(wpath = utf8_to_wstr(path, NULL)))
7766  return -1;
7767  ret = wmkdir(wpath, mode);
7768  free(wpath);
7769  return ret;
7770 }
7771 
7772 /* License: Ruby's */
7773 int
7774 rb_w32_mkdir(const char *path, int mode)
7775 {
7776  WCHAR *wpath;
7777  int ret;
7778 
7779  if (!(wpath = filecp_to_wstr(path, NULL)))
7780  return -1;
7781  ret = wmkdir(wpath, mode);
7782  free(wpath);
7783  return ret;
7784 }
7785 
7786 /* License: Ruby's */
7787 static int
7788 wrmdir(const WCHAR *wpath)
7789 {
7790  int ret = 0;
7791  RUBY_CRITICAL {
7792  const DWORD attr = GetFileAttributesW(wpath);
7793  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7794  SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
7795  }
7796  if (RemoveDirectoryW(wpath) == FALSE) {
7797  errno = map_errno(GetLastError());
7798  ret = -1;
7799  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7800  SetFileAttributesW(wpath, attr);
7801  }
7802  }
7803  }
7804  return ret;
7805 }
7806 
7807 /* License: Ruby's */
7808 int
7809 rb_w32_rmdir(const char *path)
7810 {
7811  WCHAR *wpath;
7812  int ret;
7813 
7814  if (!(wpath = filecp_to_wstr(path, NULL)))
7815  return -1;
7816  ret = wrmdir(wpath);
7817  free(wpath);
7818  return ret;
7819 }
7820 
7821 /* License: Ruby's */
7822 int
7823 rb_w32_urmdir(const char *path)
7824 {
7825  WCHAR *wpath;
7826  int ret;
7827 
7828  if (!(wpath = utf8_to_wstr(path, NULL)))
7829  return -1;
7830  ret = wrmdir(wpath);
7831  free(wpath);
7832  return ret;
7833 }
7834 
7835 /* License: Ruby's */
7836 static int
7837 wunlink(const WCHAR *path)
7838 {
7839  int ret = 0;
7840  const DWORD SYMLINKD = FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY;
7841  RUBY_CRITICAL {
7842  const DWORD attr = GetFileAttributesW(path);
7843  if (attr == (DWORD)-1) {
7844  }
7845  else if ((attr & SYMLINKD) == SYMLINKD) {
7846  ret = RemoveDirectoryW(path);
7847  }
7848  else {
7849  if (attr & FILE_ATTRIBUTE_READONLY) {
7850  SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7851  }
7852  ret = DeleteFileW(path);
7853  }
7854  if (!ret) {
7855  errno = map_errno(GetLastError());
7856  ret = -1;
7857  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7858  SetFileAttributesW(path, attr);
7859  }
7860  }
7861  }
7862  return ret;
7863 }
7864 
7865 /* License: Ruby's */
7866 int
7867 rb_w32_uunlink(const char *path)
7868 {
7869  WCHAR *wpath;
7870  int ret;
7871 
7872  if (!(wpath = utf8_to_wstr(path, NULL)))
7873  return -1;
7874  ret = wunlink(wpath);
7875  free(wpath);
7876  return ret;
7877 }
7878 
7879 /* License: Ruby's */
7880 int
7881 rb_w32_unlink(const char *path)
7882 {
7883  WCHAR *wpath;
7884  int ret;
7885 
7886  if (!(wpath = filecp_to_wstr(path, NULL)))
7887  return -1;
7888  ret = wunlink(wpath);
7889  free(wpath);
7890  return ret;
7891 }
7892 
7893 /* License: Ruby's */
7894 int
7895 rb_w32_uchmod(const char *path, int mode)
7896 {
7897  WCHAR *wpath;
7898  int ret;
7899 
7900  if (!(wpath = utf8_to_wstr(path, NULL)))
7901  return -1;
7902  ret = _wchmod(wpath, mode);
7903  free(wpath);
7904  return ret;
7905 }
7906 
7907 /* License: Ruby's */
7908 int
7909 fchmod(int fd, int mode)
7910 {
7911  /* from winbase.h of the mingw-w64 runtime package. */
7912  struct {
7913  LARGE_INTEGER CreationTime;
7914  LARGE_INTEGER LastAccessTime;
7915  LARGE_INTEGER LastWriteTime;
7916  LARGE_INTEGER ChangeTime;
7917  DWORD FileAttributes;
7918  } info = {{{0}}, {{0}}, {{0}},}; /* fields with 0 are unchanged */
7919  HANDLE h = (HANDLE)_get_osfhandle(fd);
7920 
7921  info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
7922  if (!(mode & 0200)) info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
7923  if (!SetFileInformationByHandle(h, 0, &info, sizeof(info))) {
7924  errno = map_errno(GetLastError());
7925  return -1;
7926  }
7927  return 0;
7928 }
7929 
7930 /* License: Ruby's */
7931 int
7932 rb_w32_isatty(int fd)
7933 {
7934  DWORD mode;
7935 
7936  // validate fd by using _get_osfhandle() because we cannot access _nhandle
7937  if (_get_osfhandle(fd) == -1) {
7938  return 0;
7939  }
7940  if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
7941  errno = ENOTTY;
7942  return 0;
7943  }
7944  return 1;
7945 }
7946 
7947 #if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7948 extern long _ftol(double);
7949 /* License: Ruby's */
7950 long
7951 _ftol2(double d)
7952 {
7953  return _ftol(d);
7954 }
7955 
7956 /* License: Ruby's */
7957 long
7958 _ftol2_sse(double d)
7959 {
7960  return _ftol(d);
7961 }
7962 #endif
7963 
7964 #ifndef signbit
7965 /* License: Ruby's */
7966 int
7967 signbit(double x)
7968 {
7969  int *ip = (int *)(&x + 1) - 1;
7970  return *ip < 0;
7971 }
7972 #endif
7973 
7974 /* License: Ruby's */
7975 const char * WSAAPI
7976 rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
7977 {
7978  return (inet_ntop)(af, (void *)addr, numaddr, numaddr_len);
7979 }
7980 
7981 /* License: Ruby's */
7982 int WSAAPI
7983 rb_w32_inet_pton(int af, const char *src, void *dst)
7984 {
7985  return (inet_pton)(af, src, dst);
7986 }
7987 
7988 /* License: Ruby's */
7989 char
7990 rb_w32_fd_is_text(int fd)
7991 {
7992  return _osfile(fd) & FTEXT;
7993 }
7994 
7995 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7996 /* License: Ruby's */
7997 static int
7998 unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
7999 {
8000  FILETIME ft;
8001  if (unixtime_to_filetime(t, &ft)) return -1;
8002  if (!FileTimeToSystemTime(&ft, st)) return -1;
8003  return 0;
8004 }
8005 
8006 /* License: Ruby's */
8007 static void
8008 systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
8009 {
8010  int y = st->wYear, m = st->wMonth, d = st->wDay;
8011  t->tm_sec = st->wSecond;
8012  t->tm_min = st->wMinute;
8013  t->tm_hour = st->wHour;
8014  t->tm_mday = st->wDay;
8015  t->tm_mon = st->wMonth - 1;
8016  t->tm_year = y - 1900;
8017  t->tm_wday = st->wDayOfWeek;
8018  switch (m) {
8019  case 1:
8020  break;
8021  case 2:
8022  d += 31;
8023  break;
8024  default:
8025  d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
8026  d += ((m - 3) * 153 + 2) / 5;
8027  break;
8028  }
8029  t->tm_yday = d - 1;
8030 }
8031 
8032 /* License: Ruby's */
8033 static int
8034 systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
8035 {
8036  TIME_ZONE_INFORMATION stdtz;
8037  SYSTEMTIME sst;
8038 
8039  if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
8040  if (!tz) {
8041  GetTimeZoneInformation(&stdtz);
8042  tz = &stdtz;
8043  }
8044  if (tz->StandardBias == tz->DaylightBias) return 0;
8045  if (!tz->StandardDate.wMonth) return 0;
8046  if (!tz->DaylightDate.wMonth) return 0;
8047  if (tz != &stdtz) stdtz = *tz;
8048 
8049  stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
8050  if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst)) return 0;
8051  if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
8052  return 0;
8053  return 1;
8054 }
8055 #endif
8056 
8057 #ifdef HAVE__GMTIME64_S
8058 # ifndef HAVE__LOCALTIME64_S
8059 /* assume same as _gmtime64_s() */
8060 # define HAVE__LOCALTIME64_S 1
8061 # endif
8062 # ifndef MINGW_HAS_SECURE_API
8063  _CRTIMP errno_t __cdecl _gmtime64_s(struct tm* tm, const __time64_t *time);
8064  _CRTIMP errno_t __cdecl _localtime64_s(struct tm* tm, const __time64_t *time);
8065 # endif
8066 # define gmtime_s _gmtime64_s
8067 # define localtime_s _localtime64_s
8068 #endif
8069 
8070 /* License: Ruby's */
8071 struct tm *
8072 gmtime_r(const time_t *tp, struct tm *rp)
8073 {
8074  int e = EINVAL;
8075  if (!tp || !rp) {
8076  error:
8077  errno = e;
8078  return NULL;
8079  }
8080 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8081  e = gmtime_s(rp, tp);
8082  if (e != 0) goto error;
8083 #else
8084  {
8085  SYSTEMTIME st;
8086  if (unixtime_to_systemtime(*tp, &st)) goto error;
8087  rp->tm_isdst = 0;
8088  systemtime_to_tm(&st, rp);
8089  }
8090 #endif
8091  return rp;
8092 }
8093 
8094 /* License: Ruby's */
8095 struct tm *
8096 localtime_r(const time_t *tp, struct tm *rp)
8097 {
8098  int e = EINVAL;
8099  if (!tp || !rp) {
8100  error:
8101  errno = e;
8102  return NULL;
8103  }
8104 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8105  e = localtime_s(rp, tp);
8106  if (e) goto error;
8107 #else
8108  {
8109  SYSTEMTIME gst, lst;
8110  if (unixtime_to_systemtime(*tp, &gst)) goto error;
8111  rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
8112  systemtime_to_tm(&lst, rp);
8113  }
8114 #endif
8115  return rp;
8116 }
8117 
8118 /* License: Ruby's */
8119 int
8120 rb_w32_wrap_io_handle(HANDLE h, int flags)
8121 {
8122  BOOL tmp;
8123  int len = sizeof(tmp);
8124  int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (char *)&tmp, &len);
8125  if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
8126  int f = 0;
8127  if (flags & O_NONBLOCK) {
8128  flags &= ~O_NONBLOCK;
8129  f = O_NONBLOCK;
8130  }
8131  socklist_insert((SOCKET)h, f);
8132  }
8133  else if (flags & O_NONBLOCK) {
8134  errno = EINVAL;
8135  return -1;
8136  }
8137  return rb_w32_open_osfhandle((intptr_t)h, flags);
8138 }
8139 
8140 /* License: Ruby's */
8141 int
8142 rb_w32_unwrap_io_handle(int fd)
8143 {
8144  SOCKET sock = TO_SOCKET(fd);
8145  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
8146  if (!is_socket(sock)) {
8147  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
8148  constat_delete((HANDLE)sock);
8149  }
8150  else {
8151  socklist_delete(&sock, NULL);
8152  }
8153  return _close(fd);
8154 }
8155 
8156 #if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8157 /*
8158  * Set floating point precision for pow() of mingw-w64 x86.
8159  * With default precision the result is not proper on WinXP.
8160  */
8161 double
8162 rb_w32_pow(double x, double y)
8163 {
8164 #undef pow
8165  double r;
8166  unsigned int default_control = _controlfp(0, 0);
8167  _controlfp(_PC_64, _MCW_PC);
8168  r = pow(x, y);
8169  /* Restore setting */
8170  _controlfp(default_control, _MCW_PC);
8171  return r;
8172 }
8173 #endif
8174 
8175 typedef struct {
8176  BOOL file_id_p;
8177  union {
8178  BY_HANDLE_FILE_INFORMATION bhfi;
8179  FILE_ID_INFO fii;
8180  } info;
8181 } w32_io_info_t;
8182 
8183 static HANDLE
8184 w32_io_info(VALUE *file, w32_io_info_t *st)
8185 {
8186  VALUE tmp;
8187  HANDLE f, ret = 0;
8188 
8189  tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
8190  if (!NIL_P(tmp)) {
8191  f = (HANDLE)rb_w32_get_osfhandle(rb_io_descriptor(tmp));
8192  if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
8193  }
8194  else {
8195  VALUE tmp;
8196  WCHAR *ptr;
8197  int len;
8198  VALUE v;
8199 
8200  FilePathValue(*file);
8201  tmp = rb_str_encode_ospath(*file);
8202  len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
8203  ptr = ALLOCV_N(WCHAR, v, len);
8204  MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
8205  f = CreateFileW(ptr, 0,
8206  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
8207  FILE_FLAG_BACKUP_SEMANTICS, NULL);
8208  ALLOCV_END(v);
8209  if (f == INVALID_HANDLE_VALUE) return f;
8210  ret = f;
8211  }
8212  if (GetFileType(f) == FILE_TYPE_DISK) {
8213  ZeroMemory(st, sizeof(*st));
8214  if (get_ino(f, &st->info.fii)) {
8215  st->file_id_p = TRUE;
8216  return ret;
8217  }
8218  else if (GetLastError() != ERROR_INVALID_PARAMETER) {
8219  CloseHandle(f);
8220  return INVALID_HANDLE_VALUE;
8221  }
8222  /* this API may not work at files on non Microsoft SMB
8223  * server, fallback to old API then. */
8224  if (GetFileInformationByHandle(f, &st->info.bhfi)) {
8225  st->file_id_p = FALSE;
8226  return ret;
8227  }
8228  }
8229  if (ret) CloseHandle(ret);
8230  return INVALID_HANDLE_VALUE;
8231 }
8232 
8233 static VALUE
8234 close_handle(VALUE h)
8235 {
8236  CloseHandle((HANDLE)h);
8237  return Qfalse;
8238 }
8239 
8241  VALUE *fname;
8242  w32_io_info_t *st;
8243 };
8244 
8245 static VALUE
8246 call_w32_io_info(VALUE arg)
8247 {
8248  struct w32_io_info_args *p = (void *)arg;
8249  return (VALUE)w32_io_info(p->fname, p->st);
8250 }
8251 
8252 VALUE
8253 rb_w32_file_identical_p(VALUE fname1, VALUE fname2)
8254 {
8255  w32_io_info_t st1, st2;
8256  HANDLE f1 = 0, f2 = 0;
8257 
8258  f1 = w32_io_info(&fname1, &st1);
8259  if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
8260  if (f1) {
8261  struct w32_io_info_args arg;
8262  arg.fname = &fname2;
8263  arg.st = &st2;
8264  f2 = (HANDLE)rb_ensure(call_w32_io_info, (VALUE)&arg, close_handle, (VALUE)f1);
8265  }
8266  else {
8267  f2 = w32_io_info(&fname2, &st2);
8268  }
8269  if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
8270  if (f2) CloseHandle(f2);
8271 
8272  if (st1.file_id_p != st2.file_id_p) return Qfalse;
8273  if (!st1.file_id_p) {
8274  if (st1.info.bhfi.dwVolumeSerialNumber == st2.info.bhfi.dwVolumeSerialNumber &&
8275  st1.info.bhfi.nFileIndexHigh == st2.info.bhfi.nFileIndexHigh &&
8276  st1.info.bhfi.nFileIndexLow == st2.info.bhfi.nFileIndexLow)
8277  return Qtrue;
8278  }
8279  else {
8280  if (st1.info.fii.VolumeSerialNumber == st2.info.fii.VolumeSerialNumber &&
8281  memcmp(&st1.info.fii.FileId, &st2.info.fii.FileId, sizeof(FILE_ID_128)) == 0)
8282  return Qtrue;
8283  }
8284  return Qfalse;
8285 }
8286 
8287 int
8288 rb_w32_set_thread_description(HANDLE th, const WCHAR *name)
8289 {
8290  int result = FALSE;
8291  typedef HRESULT (WINAPI *set_thread_description_func)(HANDLE, PCWSTR);
8292  static set_thread_description_func set_thread_description =
8293  (set_thread_description_func)-1;
8294  if (set_thread_description == (set_thread_description_func)-1) {
8295  /* Since Windows 10, version 1607 and Windows Server 2016 */
8296  set_thread_description = (set_thread_description_func)
8297  get_proc_address("kernel32", "SetThreadDescription", NULL);
8298  }
8299  if (set_thread_description) {
8300  result = set_thread_description(th, name);
8301  }
8302  return result;
8303 }
8304 
8305 int
8306 rb_w32_set_thread_description_str(HANDLE th, VALUE name)
8307 {
8308  int idx, result = FALSE;
8309  WCHAR *s;
8310 
8311  if (NIL_P(name)) {
8312  return rb_w32_set_thread_description(th, L"");
8313  }
8314  s = (WCHAR *)StringValueCStr(name);
8315  idx = rb_enc_get_index(name);
8316  if (idx == ENCINDEX_UTF_16LE) {
8317  result = rb_w32_set_thread_description(th, s);
8318  }
8319  else {
8320  name = rb_str_conv_enc(name, rb_enc_from_index(idx), rb_utf8_encoding());
8321  s = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(name), RSTRING_LEN(name)+1, NULL);
8322  result = rb_w32_set_thread_description(th, s);
8323  free(s);
8324  }
8325  RB_GC_GUARD(name);
8326  return result;
8327 }
8328 
8329 VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notimplement;
8330 
8331 #if RUBY_MSVCRT_VERSION < 120
8332 #include "missing/nextafter.c"
8333 #endif
8334 
8335 void *
8336 rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset)
8337 {
8338  void *ptr;
8339  //DWORD protect = 0;
8340  DWORD protect = PAGE_EXECUTE_READWRITE;
8341 
8342  if (fd > 0 || offset) {
8343  /* not supported */
8344  errno = EINVAL;
8345  return MAP_FAILED;
8346  }
8347 
8348 /*
8349  if (prot & PROT_EXEC) {
8350  if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8351  else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8352  else protect = PAGE_EXECUTE;
8353  }
8354  else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8355  else if (prot & PROT_READ) protect = PAGE_READONLY;
8356 */
8357  ptr = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, protect);
8358  if (!ptr) {
8359  errno = rb_w32_map_errno(GetLastError());
8360  return MAP_FAILED;
8361  }
8362 
8363  return ptr;
8364 }
8365 
8366 int
8367 rb_w32_munmap(void *addr, size_t len)
8368 {
8369  if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8370  errno = rb_w32_map_errno(GetLastError());
8371  return -1;
8372  }
8373 
8374  return 0;
8375 }
8376 
8377 inline int
8378 rb_w32_mprotect(void *addr, size_t len, int prot)
8379 {
8380 /*
8381  DWORD protect = 0;
8382  if (prot & PROT_EXEC) {
8383  if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8384  else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8385  else protect = PAGE_EXECUTE;
8386  }
8387  else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8388  else if (prot & PROT_READ) protect = PAGE_READONLY;
8389  if (!VirtualProtect(addr, len, protect, NULL)) {
8390  errno = rb_w32_map_errno(GetLastError());
8391  return -1;
8392  }
8393 */
8394  if (prot & PROT_EXEC) {
8395  if (!FlushInstructionCache(GetCurrentProcess(), addr, len)) {
8396  errno = rb_w32_map_errno(GetLastError());
8397  return -1;
8398  }
8399  }
8400  return 0;
8401 }
#define LONG_LONG
Definition: long_long.h:38
#define RBIMPL_ATTR_FORMAT(x, y, z)
Wraps (or simulates) __attribute__((format))
Definition: format.h:27
int ruby_glob_func(const char *path, VALUE arg, void *enc)
Type of a glob callback function.
Definition: glob.h:49
#define T_FILE
Old name of RUBY_T_FILE.
Definition: value_type.h:62
#define ALLOCV
Old name of RB_ALLOCV.
Definition: memory.h:399
#define ISSPACE
Old name of rb_isspace.
Definition: ctype.h:88
#define ALLOC
Old name of RB_ALLOC.
Definition: memory.h:395
#define xfree
Old name of ruby_xfree.
Definition: xmalloc.h:58
#define xrealloc
Old name of ruby_xrealloc.
Definition: xmalloc.h:56
#define ECONV_UNDEF_REPLACE
Old name of RUBY_ECONV_UNDEF_REPLACE.
Definition: transcode.h:526
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
Definition: encoding.h:109
#define ECONV_INVALID_REPLACE
Old name of RUBY_ECONV_INVALID_REPLACE.
Definition: transcode.h:524
#define ASSUME
Old name of RBIMPL_ASSUME.
Definition: assume.h:27
#define ISALPHA
Old name of rb_isalpha.
Definition: ctype.h:92
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition: memory.h:400
#define ISALNUM
Old name of rb_isalnum.
Definition: ctype.h:91
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition: memory.h:401
void rb_fatal(const char *fmt,...)
Raises the unsung "fatal" exception.
Definition: error.c:3686
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1045
Encoding relates APIs.
int rb_enc_get_index(VALUE obj)
Queries the index of the encoding of the passed object, if any.
Definition: encoding.c:920
int rb_enc_to_index(rb_encoding *enc)
Queries the index of the encoding.
Definition: encoding.c:191
rb_encoding * rb_utf8_encoding(void)
Queries the encoding that represents UTF-8.
Definition: encoding.c:1475
rb_encoding * rb_enc_from_index(int idx)
Identical to rb_find_encoding(), except it takes an encoding index instead of a Ruby object.
Definition: encoding.c:402
static const char * rb_enc_name(rb_encoding *enc)
Queries the (canonical) name of the passed encoding.
Definition: encoding.h:417
rb_encoding * rb_filesystem_encoding(void)
Queries the "filesystem" encoding.
Definition: encoding.c:1537
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
Definition: string.c:1285
VALUE rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts)
Identical to rb_str_conv_enc(), except it additionally takes IO encoder options.
Definition: string.c:1169
VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
Identical to rb_str_new(), except it additionally takes an encoding.
Definition: string.c:1068
int rb_econv_has_convpath_p(const char *from_encoding, const char *to_encoding)
Queries if there is more than one way to convert between the passed two encodings.
Definition: transcode.c:3211
VALUE rb_str_encode_ospath(VALUE path)
Converts a string into an "OS Path" encoding, if any.
Definition: file.c:252
void rb_write_error2(const char *str, long len)
Identical to rb_write_error(), except it additionally takes the message's length.
Definition: io.c:9101
VALUE rb_utf8_str_new(const char *ptr, long len)
Identical to rb_str_new(), except it generates a string of "UTF-8" encoding.
Definition: string.c:1062
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition: string.c:3445
#define rb_strlen_lit(str)
Length of a string literal.
Definition: string.h:1692
VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker)
Raises rb_eNotImpError.
Definition: vm_method.c:481
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition: io.c:2892
char * ptr
Pointer to the underlying memory region, of at least capa bytes.
Definition: io.h:2
int len
Length of the buffer.
Definition: io.h:8
char * ruby_strdup(const char *str)
This is our own version of strdup(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
Definition: util.c:536
#define strdup(s)
Just another name of ruby_strdup.
Definition: util.h:187
void ruby_vm_at_exit(void(*func)(ruby_vm_t *))
ruby_vm_at_exit registers a function func to be invoked when a VM passed away.
Definition: vm.c:877
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
Definition: sprintf.c:1217
VALUE rb_str_vcatf(VALUE dst, const char *fmt, va_list ap)
Identical to rb_str_catf(), except it takes a va_list.
Definition: sprintf.c:1230
void rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
Identical to rb_w32_fd_copy(), except it copies unlimited number of file descriptors.
Definition: win32.c:3000
void rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
Destructively overwrites an fdset with another.
Definition: win32.c:2985
void rb_fd_term(rb_fdset_t *f)
Destroys the rb_fdset_t, releasing any memory and resources it used.
#define rb_long2int
Just another name of rb_long2int_inline.
Definition: long.h:62
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition: memory.h:367
#define ALLOCA_N(type, n)
Definition: memory.h:287
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition: memory.h:355
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition: memory.h:162
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
Definition: cxxanyargs.hpp:432
#define RBIMPL_ATTR_NONNULL(list)
Wraps (or simulates) __attribute__((nonnull))
Definition: nonnull.h:27
#define PRI_PIDT_PREFIX
A rb_sprintf() format prefix to be used for a pid_t parameter.
Definition: pid_t.h:38
#define rb_fd_init
Initialises the :given :rb_fdset_t.
Definition: posix.h:63
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition: rstring.h:416
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
Definition: rstring.h:367
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition: rstring.h:89
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition: ruby.h:90
#define errno
Ractor-aware version of errno.
Definition: ruby.h:388
C99 shim for <stdbool.h>
Definition: dir.h:21
Definition: win32.c:3719
Definition: win32.c:682
Definition: dir.h:13
Definition: win32.h:234
Definition: win32.c:2490
Definition: win32.h:223
The data structure which wraps the fd_set bitmap used by select(2).
Definition: largesize.h:71
fd_set * fdset
File descriptors buffer.
Definition: largesize.h:73
int capa
Maximum allowed number of FDs.
Definition: win32.h:50
Definition: st.h:79
Definition: win32.h:701
Definition: file.c:3021
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
Definition: value.h:63
uintptr_t VALUE
Type that represents a Ruby object.
Definition: value.h:40
void * ruby_xmalloc(size_t size)
Allocates a storage instance.
Definition: gc.c:4215
void ruby_xfree(void *ptr)
Deallocates a storage instance.
Definition: gc.c:4299
void * ruby_xcalloc(size_t nelems, size_t elemsiz)
Identical to ruby_xmalloc2(), except it returns a zero-filled storage instance.
Definition: gc.c:4239