Ruby  3.4.0dev (2024-12-06 revision 892c46283a5ea4179500d951c9d4866c0051f27b)
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 = 0x7fc00000;
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  const int64_t adrp_sign = (adrp_insn & 0x00800000) ? ~0x001fffff : 0;
2604  /* imm = SignExtend(immhi:immlo:Zeros(12), 64) */
2605  const int64_t adrp_imm = (adrp_sign | (adrp_immhi << 2) | adrp_immlo) << 12;
2606  /* base = PC64<63:12>:Zeros(12) */
2607  const uint64_t adrp_base = (uint64_t)pc & 0xfffffffffffff000;
2608 
2609  const uint32_t add_insn = *(pc + 1);
2610  const uint64_t add_imm = (add_insn & 0x3ffc00) >> (5 + 5);
2611 
2612  __pioinfo = (ioinfo**)(adrp_base + adrp_imm + add_imm);
2613 #else /* _M_ARM64 */
2614  char *pend = p;
2615 
2616 # ifdef _WIN64
2617  int32_t rel;
2618  char *rip;
2619  /* add rsp, _ */
2620 # define FUNCTION_BEFORE_RET_MARK "\x48\x83\xc4"
2621 # define FUNCTION_SKIP_BYTES 1
2622 # ifdef _DEBUG
2623  /* lea rcx,[__pioinfo's addr in RIP-relative 32bit addr] */
2624 # define PIOINFO_MARK "\x48\x8d\x0d"
2625 # else
2626  /* lea rdx,[__pioinfo's addr in RIP-relative 32bit addr] */
2627 # define PIOINFO_MARK "\x48\x8d\x15"
2628 # endif
2629 
2630 # else /* x86 */
2631  /* pop ebp */
2632 # define FUNCTION_BEFORE_RET_MARK "\x5d"
2633  /* leave */
2634 # define FUNCTION_BEFORE_RET_MARK_2 "\xc9"
2635 # define FUNCTION_SKIP_BYTES 0
2636  /* mov eax,dword ptr [eax*4+100EB430h] */
2637 # define PIOINFO_MARK "\x8B\x04\x85"
2638 # endif
2639  if (p) {
2640  for (pend += 10; pend < p + 500; pend++) {
2641  // find end of function
2642  if ((memcmp(pend, FUNCTION_BEFORE_RET_MARK, sizeof(FUNCTION_BEFORE_RET_MARK) - 1) == 0
2643 # ifdef FUNCTION_BEFORE_RET_MARK_2
2644  || memcmp(pend, FUNCTION_BEFORE_RET_MARK_2, sizeof(FUNCTION_BEFORE_RET_MARK_2) - 1) == 0
2645 # endif
2646  ) &&
2647  *(pend + (sizeof(FUNCTION_BEFORE_RET_MARK) - 1) + FUNCTION_SKIP_BYTES) == (char)FUNCTION_RET) {
2648  // search backwards from end of function
2649  for (pend -= (sizeof(PIOINFO_MARK) - 1); pend > p; pend--) {
2650  if (memcmp(pend, PIOINFO_MARK, sizeof(PIOINFO_MARK) - 1) == 0) {
2651  p = pend;
2652  goto found;
2653  }
2654  }
2655  break;
2656  }
2657  }
2658  }
2659  fprintf(stderr, "unexpected " UCRTBASE "\n");
2660  _exit(1);
2661 
2662  found:
2663  p += sizeof(PIOINFO_MARK) - 1;
2664 #ifdef _WIN64
2665  rel = *(int32_t*)(p);
2666  rip = p + sizeof(int32_t);
2667  __pioinfo = (ioinfo**)(rip + rel);
2668 #else
2669  __pioinfo = *(ioinfo***)(p);
2670 #endif
2671 #endif /* _M_ARM64 */
2672 #endif /* RUBY_MSVCRT_VERSION */
2673  int fd;
2674 
2675  fd = _open("NUL", O_RDONLY);
2676  for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra += sizeof(void *)) {
2677  if (_osfhnd(fd) == _get_osfhandle(fd)) {
2678  break;
2679  }
2680  }
2681  _close(fd);
2682 
2683  if (pioinfo_extra > 64) {
2684  /* not found, maybe something wrong... */
2685  pioinfo_extra = 0;
2686  }
2687 }
2688 #else
2689 #define pioinfo_extra 0
2690 #endif
2691 
2692 static inline ioinfo*
2693 _pioinfo(int fd)
2694 {
2695  const size_t sizeof_ioinfo = sizeof(ioinfo) + pioinfo_extra;
2696  return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
2697  (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
2698 }
2699 
2700 #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2701 #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
2702 
2703 #define FOPEN 0x01 /* file handle open */
2704 #define FEOFLAG 0x02 /* end of file has been encountered */
2705 #define FPIPE 0x08 /* file handle refers to a pipe */
2706 #define FNOINHERIT 0x10 /* file handle opened O_NOINHERIT */
2707 #define FAPPEND 0x20 /* file handle opened O_APPEND */
2708 #define FDEV 0x40 /* file handle refers to device */
2709 #define FTEXT 0x80 /* file handle is in text mode */
2710 
2711 static int is_socket(SOCKET);
2712 static int is_console(SOCKET);
2713 
2714 /* License: Ruby's */
2715 int
2716 rb_w32_io_cancelable_p(int fd)
2717 {
2718  return is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd));
2719 }
2720 
2721 /* License: Ruby's */
2722 static int
2723 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2724 {
2725  int fh;
2726  char fileflags; /* _osfile flags */
2727  HANDLE hF;
2728 
2729  /* copy relevant flags from second parameter */
2730  fileflags = FDEV;
2731 
2732  if (flags & O_APPEND)
2733  fileflags |= FAPPEND;
2734 
2735  if (flags & O_TEXT)
2736  fileflags |= FTEXT;
2737 
2738  if (flags & O_NOINHERIT)
2739  fileflags |= FNOINHERIT;
2740 
2741  /* attempt to allocate a C Runtime file handle */
2742  hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2743  fh = _open_osfhandle((intptr_t)hF, 0);
2744  CloseHandle(hF);
2745  if (fh == -1) {
2746  errno = EMFILE; /* too many open files */
2747  _doserrno = 0L; /* not an OS error */
2748  }
2749  else {
2750 
2751  rb_acrt_lowio_lock_fh(fh);
2752  /* the file is open. now, set the info in _osfhnd array */
2753  _set_osfhnd(fh, osfhandle);
2754 
2755  fileflags |= FOPEN; /* mark as open */
2756 
2757  _set_osflags(fh, fileflags); /* set osfile entry */
2758  rb_acrt_lowio_unlock_fh(fh);
2759  }
2760  return fh; /* return handle */
2761 }
2762 
2763 /* License: Ruby's */
2764 static void
2765 init_stdhandle(void)
2766 {
2767  int nullfd = -1;
2768  int keep = 0;
2769 #define open_null(fd) \
2770  (((nullfd < 0) ? \
2771  (nullfd = open("NUL", O_RDWR)) : 0), \
2772  ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)), \
2773  (fd))
2774 
2775  if (fileno(stdin) < 0) {
2776  FILE_FILENO(stdin) = open_null(0);
2777  }
2778  else {
2779  setmode(fileno(stdin), O_BINARY);
2780  }
2781  if (fileno(stdout) < 0) {
2782  FILE_FILENO(stdout) = open_null(1);
2783  }
2784  if (fileno(stderr) < 0) {
2785  FILE_FILENO(stderr) = open_null(2);
2786  }
2787  if (nullfd >= 0 && !keep) close(nullfd);
2788  setvbuf(stderr, NULL, _IONBF, 0);
2789 
2790  {
2791  HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
2792  DWORD m;
2793  if (GetConsoleMode(h, &m)) {
2794 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
2795 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
2796 #endif
2797  SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
2798  }
2799  }
2800 }
2801 
2802 #undef getsockopt
2803 
2804 /* License: Ruby's */
2805 static int
2806 is_socket(SOCKET sock)
2807 {
2808  if (socklist_lookup(sock, NULL))
2809  return TRUE;
2810  else
2811  return FALSE;
2812 }
2813 
2814 /* License: Ruby's */
2815 int
2816 rb_w32_is_socket(int fd)
2817 {
2818  return is_socket(TO_SOCKET(fd));
2819 }
2820 
2821 //
2822 // Since the errors returned by the socket error function
2823 // WSAGetLastError() are not known by the library routine strerror
2824 // we have to roll our own.
2825 //
2826 
2827 #undef strerror
2828 
2829 /* License: Artistic or GPL */
2830 char *
2831 rb_w32_strerror(int e)
2832 {
2833  static char buffer[512];
2834  DWORD source = 0;
2835  char *p;
2836 
2837  if (e < 0 || e > sys_nerr) {
2838  if (e < 0)
2839  e = GetLastError();
2840 #if WSAEWOULDBLOCK != EWOULDBLOCK
2841  else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
2842  static int s = -1;
2843  int i;
2844  if (s < 0)
2845  for (s = 0; s < (int)(sizeof(errmap)/sizeof(*errmap)); s++)
2846  if (errmap[s].winerr == WSAEWOULDBLOCK)
2847  break;
2848  for (i = s; i < (int)(sizeof(errmap)/sizeof(*errmap)); i++)
2849  if (errmap[i].err == e) {
2850  e = errmap[i].winerr;
2851  break;
2852  }
2853  }
2854 #endif
2855  if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2856  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
2857  MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
2858  buffer, sizeof(buffer), NULL) == 0 &&
2859  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2860  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
2861  buffer, sizeof(buffer), NULL) == 0)
2862  strlcpy(buffer, "Unknown Error", sizeof(buffer));
2863  }
2864  else
2865  strlcpy(buffer, strerror(e), sizeof(buffer));
2866 
2867  p = buffer;
2868  while ((p = strpbrk(p, "\r\n")) != NULL) {
2869  memmove(p, p + 1, strlen(p));
2870  }
2871  return buffer;
2872 }
2873 
2874 //
2875 // various stubs
2876 //
2877 
2878 
2879 // Ownership
2880 //
2881 // Just pretend that everyone is a superuser. NT will let us know if
2882 // we don't really have permission to do something.
2883 //
2884 
2885 #define ROOT_UID 0
2886 #define ROOT_GID 0
2887 
2888 /* License: Artistic or GPL */
2889 rb_uid_t
2890 getuid(void)
2891 {
2892  return ROOT_UID;
2893 }
2894 
2895 /* License: Artistic or GPL */
2896 rb_uid_t
2897 geteuid(void)
2898 {
2899  return ROOT_UID;
2900 }
2901 
2902 /* License: Artistic or GPL */
2903 rb_gid_t
2904 getgid(void)
2905 {
2906  return ROOT_GID;
2907 }
2908 
2909 /* License: Artistic or GPL */
2910 rb_gid_t
2911 getegid(void)
2912 {
2913  return ROOT_GID;
2914 }
2915 
2916 /* License: Artistic or GPL */
2917 int
2918 setuid(rb_uid_t uid)
2919 {
2920  return (uid == ROOT_UID ? 0 : -1);
2921 }
2922 
2923 /* License: Artistic or GPL */
2924 int
2925 setgid(rb_gid_t gid)
2926 {
2927  return (gid == ROOT_GID ? 0 : -1);
2928 }
2929 
2930 //
2931 // File system stuff
2932 //
2933 
2934 /* License: Artistic or GPL */
2935 int
2936 ioctl(int i, int u, ...)
2937 {
2938  errno = EINVAL;
2939  return -1;
2940 }
2941 
2942 void
2943 rb_w32_fdset(int fd, fd_set *set)
2944 {
2945  FD_SET(fd, set);
2946 }
2947 
2948 #undef FD_CLR
2949 
2950 /* License: Ruby's */
2951 void
2952 rb_w32_fdclr(int fd, fd_set *set)
2953 {
2954  unsigned int i;
2955  SOCKET s = TO_SOCKET(fd);
2956 
2957  for (i = 0; i < set->fd_count; i++) {
2958  if (set->fd_array[i] == s) {
2959  memmove(&set->fd_array[i], &set->fd_array[i+1],
2960  sizeof(set->fd_array[0]) * (--set->fd_count - i));
2961  break;
2962  }
2963  }
2964 }
2965 
2966 #undef FD_ISSET
2967 
2968 /* License: Ruby's */
2969 int
2970 rb_w32_fdisset(int fd, fd_set *set)
2971 {
2972  int ret;
2973  SOCKET s = TO_SOCKET(fd);
2974  if (s == (SOCKET)INVALID_HANDLE_VALUE)
2975  return 0;
2976  RUBY_CRITICAL {ret = __WSAFDIsSet(s, set);}
2977  return ret;
2978 }
2979 
2980 /* License: Ruby's */
2981 void
2982 rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
2983 {
2984  max = min(src->fd_count, (UINT)max);
2985  if ((UINT)dst->capa < (UINT)max) {
2986  dst->capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2987  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2988  }
2989 
2990  memcpy(dst->fdset->fd_array, src->fd_array,
2991  max * sizeof(src->fd_array[0]));
2992  dst->fdset->fd_count = src->fd_count;
2993 }
2994 
2995 /* License: Ruby's */
2996 void
2998 {
2999  if ((UINT)dst->capa < src->fdset->fd_count) {
3000  dst->capa = (src->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
3001  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
3002  }
3003 
3004  memcpy(dst->fdset->fd_array, src->fdset->fd_array,
3005  src->fdset->fd_count * sizeof(src->fdset->fd_array[0]));
3006  dst->fdset->fd_count = src->fdset->fd_count;
3007 }
3008 
3009 //
3010 // Networking trampolines
3011 // These are used to avoid socket startup/shutdown overhead in case
3012 // the socket routines aren't used.
3013 //
3014 
3015 #undef select
3016 
3017 /* License: Ruby's */
3018 static int
3019 extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
3020 {
3021  unsigned int s = 0;
3022  unsigned int m = 0;
3023  if (!src) return 0;
3024 
3025  while (s < src->fd_count) {
3026  SOCKET fd = src->fd_array[s];
3027 
3028  if (!func || (*func)(fd)) {
3029  if (dst) { /* move it to dst */
3030  unsigned int d;
3031 
3032  for (d = 0; d < dst->fdset->fd_count; d++) {
3033  if (dst->fdset->fd_array[d] == fd)
3034  break;
3035  }
3036  if (d == dst->fdset->fd_count) {
3037  if ((int)dst->fdset->fd_count >= dst->capa) {
3038  dst->capa = (dst->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
3039  dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
3040  }
3041  dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
3042  }
3043  memmove(
3044  &src->fd_array[s],
3045  &src->fd_array[s+1],
3046  sizeof(src->fd_array[0]) * (--src->fd_count - s));
3047  }
3048  else {
3049  m++;
3050  s++;
3051  }
3052  }
3053  else s++;
3054  }
3055 
3056  return dst ? dst->fdset->fd_count : m;
3057 }
3058 
3059 /* License: Ruby's */
3060 static int
3061 copy_fd(fd_set *dst, fd_set *src)
3062 {
3063  unsigned int s;
3064  if (!src || !dst) return 0;
3065 
3066  for (s = 0; s < src->fd_count; ++s) {
3067  SOCKET fd = src->fd_array[s];
3068  unsigned int d;
3069  for (d = 0; d < dst->fd_count; ++d) {
3070  if (dst->fd_array[d] == fd)
3071  break;
3072  }
3073  if (d == dst->fd_count && d < FD_SETSIZE) {
3074  dst->fd_array[dst->fd_count++] = fd;
3075  }
3076  }
3077 
3078  return dst->fd_count;
3079 }
3080 
3081 /* License: Ruby's */
3082 static int
3083 is_not_socket(SOCKET sock)
3084 {
3085  return !is_socket(sock);
3086 }
3087 
3088 /* License: Ruby's */
3089 static int
3090 is_pipe(SOCKET sock) /* DONT call this for SOCKET! it claims it is PIPE. */
3091 {
3092  int ret;
3093 
3094  RUBY_CRITICAL {
3095  ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
3096  }
3097 
3098  return ret;
3099 }
3100 
3101 /* License: Ruby's */
3102 static int
3103 is_readable_pipe(SOCKET sock) /* call this for pipe only */
3104 {
3105  int ret;
3106  DWORD n = 0;
3107 
3108  RUBY_CRITICAL {
3109  if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
3110  ret = (n > 0);
3111  }
3112  else {
3113  ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
3114  }
3115  }
3116 
3117  return ret;
3118 }
3119 
3120 /* License: Ruby's */
3121 static int
3122 is_console(SOCKET sock) /* DONT call this for SOCKET! */
3123 {
3124  int ret;
3125  DWORD n = 0;
3126  INPUT_RECORD ir;
3127 
3128  RUBY_CRITICAL {
3129  ret = (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n));
3130  }
3131 
3132  return ret;
3133 }
3134 
3135 /* License: Ruby's */
3136 static int
3137 is_readable_console(SOCKET sock) /* call this for console only */
3138 {
3139  int ret = 0;
3140  DWORD n = 0;
3141  INPUT_RECORD ir;
3142 
3143  RUBY_CRITICAL {
3144  if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) {
3145  if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
3146  ir.Event.KeyEvent.uChar.UnicodeChar) {
3147  ret = 1;
3148  }
3149  else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown &&
3150  ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU /* ALT key */ &&
3151  ir.Event.KeyEvent.uChar.UnicodeChar) {
3152  ret = 1;
3153  }
3154  else {
3155  ReadConsoleInputW((HANDLE)sock, &ir, 1, &n);
3156  }
3157  }
3158  }
3159 
3160  return ret;
3161 }
3162 
3163 /* License: Ruby's */
3164 static int
3165 is_invalid_handle(SOCKET sock)
3166 {
3167  return (HANDLE)sock == INVALID_HANDLE_VALUE;
3168 }
3169 
3170 /* License: Artistic or GPL */
3171 static int
3172 do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3173  struct timeval *timeout)
3174 {
3175  int r = 0;
3176 
3177  if (nfds == 0) {
3178  if (timeout)
3179  rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
3180  else
3181  rb_w32_sleep(INFINITE);
3182  }
3183  else {
3184  RUBY_CRITICAL {
3185  thread_exclusive(select) {
3186  r = select(nfds, rd, wr, ex, timeout);
3187  }
3188  if (r == SOCKET_ERROR) {
3189  errno = map_errno(WSAGetLastError());
3190  r = -1;
3191  }
3192  }
3193  }
3194 
3195  return r;
3196 }
3197 
3198 /*
3199  * rest -= wait
3200  * return 0 if rest is smaller than wait.
3201  */
3202 /* License: Ruby's */
3203 int
3204 rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
3205 {
3206  if (rest->tv_sec < wait->tv_sec) {
3207  return 0;
3208  }
3209  while (rest->tv_usec < wait->tv_usec) {
3210  if (rest->tv_sec <= wait->tv_sec) {
3211  return 0;
3212  }
3213  rest->tv_sec -= 1;
3214  rest->tv_usec += 1000 * 1000;
3215  }
3216  rest->tv_sec -= wait->tv_sec;
3217  rest->tv_usec -= wait->tv_usec;
3218  return rest->tv_sec != 0 || rest->tv_usec != 0;
3219 }
3220 
3221 /* License: Ruby's */
3222 static inline int
3223 compare(const struct timeval *t1, const struct timeval *t2)
3224 {
3225  if (t1->tv_sec < t2->tv_sec)
3226  return -1;
3227  if (t1->tv_sec > t2->tv_sec)
3228  return 1;
3229  if (t1->tv_usec < t2->tv_usec)
3230  return -1;
3231  if (t1->tv_usec > t2->tv_usec)
3232  return 1;
3233  return 0;
3234 }
3235 
3236 #undef Sleep
3237 
3238 int rb_w32_check_interrupt(void *); /* @internal */
3239 
3240 /* @internal */
3241 /* License: Ruby's */
3242 int
3243 rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3244  struct timeval *timeout, void *th)
3245 {
3246  int r;
3247  rb_fdset_t pipe_rd;
3248  rb_fdset_t cons_rd;
3249  rb_fdset_t else_rd;
3250  rb_fdset_t else_wr;
3251  rb_fdset_t except;
3252  int nonsock = 0;
3253  struct timeval limit = {0, 0};
3254 
3255  if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
3256  errno = EINVAL;
3257  return -1;
3258  }
3259 
3260  if (timeout) {
3261  if (timeout->tv_sec < 0 ||
3262  timeout->tv_usec < 0 ||
3263  timeout->tv_usec >= 1000000) {
3264  errno = EINVAL;
3265  return -1;
3266  }
3267  gettimeofday(&limit, NULL);
3268  limit.tv_sec += timeout->tv_sec;
3269  limit.tv_usec += timeout->tv_usec;
3270  if (limit.tv_usec >= 1000000) {
3271  limit.tv_usec -= 1000000;
3272  limit.tv_sec++;
3273  }
3274  }
3275 
3276  // assume else_{rd,wr} (other than socket, pipe reader, console reader)
3277  // are always readable/writable. but this implementation still has
3278  // problem. if pipe's buffer is full, writing to pipe will block
3279  // until some data is read from pipe. but ruby is single threaded system,
3280  // so whole system will be blocked forever.
3281 
3282  rb_fd_init(&else_rd);
3283  nonsock += extract_fd(&else_rd, rd, is_not_socket);
3284 
3285  rb_fd_init(&else_wr);
3286  nonsock += extract_fd(&else_wr, wr, is_not_socket);
3287 
3288  // check invalid handles
3289  if (extract_fd(NULL, else_rd.fdset, is_invalid_handle) > 0 ||
3290  extract_fd(NULL, else_wr.fdset, is_invalid_handle) > 0) {
3291  rb_fd_term(&else_wr);
3292  rb_fd_term(&else_rd);
3293  errno = EBADF;
3294  return -1;
3295  }
3296 
3297  rb_fd_init(&pipe_rd);
3298  extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
3299 
3300  rb_fd_init(&cons_rd);
3301  extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
3302 
3303  rb_fd_init(&except);
3304  extract_fd(&except, ex, is_not_socket); // drop only
3305 
3306  r = 0;
3307  if (rd && (int)rd->fd_count > r) r = (int)rd->fd_count;
3308  if (wr && (int)wr->fd_count > r) r = (int)wr->fd_count;
3309  if (ex && (int)ex->fd_count > r) r = (int)ex->fd_count;
3310  if (nfds > r) nfds = r;
3311 
3312  {
3313  struct timeval rest;
3314  const struct timeval wait = {0, 10 * 1000}; // 10ms
3315  struct timeval zero = {0, 0}; // 0ms
3316  for (;;) {
3317  if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
3318  r = -1;
3319  break;
3320  }
3321  if (nonsock) {
3322  // modifying {else,pipe,cons}_rd is safe because
3323  // if they are modified, function returns immediately.
3324  extract_fd(&else_rd, pipe_rd.fdset, is_readable_pipe);
3325  extract_fd(&else_rd, cons_rd.fdset, is_readable_console);
3326  }
3327 
3328  if (else_rd.fdset->fd_count || else_wr.fdset->fd_count) {
3329  r = do_select(nfds, rd, wr, ex, &zero); // polling
3330  if (r < 0) break; // XXX: should I ignore error and return signaled handles?
3331  r += copy_fd(rd, else_rd.fdset);
3332  r += copy_fd(wr, else_wr.fdset);
3333  if (ex)
3334  r += ex->fd_count;
3335  break;
3336  }
3337  else {
3338  const struct timeval *dowait = &wait;
3339 
3340  fd_set orig_rd;
3341  fd_set orig_wr;
3342  fd_set orig_ex;
3343 
3344  FD_ZERO(&orig_rd);
3345  FD_ZERO(&orig_wr);
3346  FD_ZERO(&orig_ex);
3347 
3348  if (rd) copy_fd(&orig_rd, rd);
3349  if (wr) copy_fd(&orig_wr, wr);
3350  if (ex) copy_fd(&orig_ex, ex);
3351  r = do_select(nfds, rd, wr, ex, &zero); // polling
3352  if (r != 0) break; // signaled or error
3353  if (rd) copy_fd(rd, &orig_rd);
3354  if (wr) copy_fd(wr, &orig_wr);
3355  if (ex) copy_fd(ex, &orig_ex);
3356 
3357  if (timeout) {
3358  struct timeval now;
3359  gettimeofday(&now, NULL);
3360  rest = limit;
3361  if (!rb_w32_time_subtract(&rest, &now)) break;
3362  if (compare(&rest, &wait) < 0) dowait = &rest;
3363  }
3364  Sleep(dowait->tv_sec * 1000 + (dowait->tv_usec + 999) / 1000);
3365  }
3366  }
3367  }
3368 
3369  rb_fd_term(&except);
3370  rb_fd_term(&cons_rd);
3371  rb_fd_term(&pipe_rd);
3372  rb_fd_term(&else_wr);
3373  rb_fd_term(&else_rd);
3374 
3375  return r;
3376 }
3377 
3378 /* License: Ruby's */
3379 int WSAAPI
3380 rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3381  struct timeval *timeout)
3382 {
3383  return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
3384 }
3385 
3386 /* License: Ruby's */
3387 static FARPROC
3388 get_wsa_extension_function(SOCKET s, GUID guid)
3389 {
3390  DWORD dmy;
3391  FARPROC ptr = NULL;
3392 
3393  WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
3394  &ptr, sizeof(ptr), &dmy, NULL, NULL);
3395  if (!ptr)
3396  errno = ENOSYS;
3397  return ptr;
3398 }
3399 
3400 #undef accept
3401 
3402 /* License: Artistic or GPL */
3403 int WSAAPI
3404 rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
3405 {
3406  SOCKET r;
3407  int fd;
3408 
3409  RUBY_CRITICAL {
3410  r = accept(TO_SOCKET(s), addr, addrlen);
3411  if (r != INVALID_SOCKET) {
3412  SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
3413  fd = rb_w32_open_osfhandle((intptr_t)r, O_RDWR|O_BINARY|O_NOINHERIT);
3414  if (fd != -1)
3415  socklist_insert(r, 0);
3416  else
3417  closesocket(r);
3418  }
3419  else {
3420  errno = map_errno(WSAGetLastError());
3421  fd = -1;
3422  }
3423  }
3424  return fd;
3425 }
3426 
3427 #undef bind
3428 
3429 /* License: Artistic or GPL */
3430 int WSAAPI
3431 rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
3432 {
3433  int r;
3434 
3435  RUBY_CRITICAL {
3436  r = bind(TO_SOCKET(s), addr, addrlen);
3437  if (r == SOCKET_ERROR)
3438  errno = map_errno(WSAGetLastError());
3439  }
3440  return r;
3441 }
3442 
3443 #undef connect
3444 
3445 /* License: Artistic or GPL */
3446 int WSAAPI
3447 rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
3448 {
3449  int r;
3450  RUBY_CRITICAL {
3451  r = connect(TO_SOCKET(s), addr, addrlen);
3452  if (r == SOCKET_ERROR) {
3453  int err = WSAGetLastError();
3454  if (err != WSAEWOULDBLOCK)
3455  errno = map_errno(err);
3456  else
3457  errno = EINPROGRESS;
3458  }
3459  }
3460  return r;
3461 }
3462 
3463 
3464 #undef getpeername
3465 
3466 /* License: Artistic or GPL */
3467 int WSAAPI
3468 rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
3469 {
3470  int r;
3471  RUBY_CRITICAL {
3472  r = getpeername(TO_SOCKET(s), addr, addrlen);
3473  if (r == SOCKET_ERROR)
3474  errno = map_errno(WSAGetLastError());
3475  }
3476  return r;
3477 }
3478 
3479 #undef getsockname
3480 
3481 /* License: Artistic or GPL */
3482 int WSAAPI
3483 rb_w32_getsockname(int fd, struct sockaddr *addr, int *addrlen)
3484 {
3485  int sock;
3486  int r;
3487  RUBY_CRITICAL {
3488  sock = TO_SOCKET(fd);
3489  r = getsockname(sock, addr, addrlen);
3490  if (r == SOCKET_ERROR) {
3491  DWORD wsaerror = WSAGetLastError();
3492  if (wsaerror == WSAEINVAL) {
3493  int flags;
3494  if (socklist_lookup(sock, &flags)) {
3495  int af = GET_FAMILY(flags);
3496  if (af) {
3497  memset(addr, 0, *addrlen);
3498  addr->sa_family = af;
3499  return 0;
3500  }
3501  }
3502  }
3503  errno = map_errno(wsaerror);
3504  }
3505  }
3506  return r;
3507 }
3508 
3509 #undef getsockopt
3510 
3511 /* License: Artistic or GPL */
3512 int WSAAPI
3513 rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
3514 {
3515  int r;
3516  RUBY_CRITICAL {
3517  r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3518  if (r == SOCKET_ERROR)
3519  errno = map_errno(WSAGetLastError());
3520  }
3521  return r;
3522 }
3523 
3524 #undef ioctlsocket
3525 
3526 /* License: Artistic or GPL */
3527 int WSAAPI
3528 rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
3529 {
3530  int r;
3531  RUBY_CRITICAL {
3532  r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3533  if (r == SOCKET_ERROR)
3534  errno = map_errno(WSAGetLastError());
3535  }
3536  return r;
3537 }
3538 
3539 #undef listen
3540 
3541 /* License: Artistic or GPL */
3542 int WSAAPI
3543 rb_w32_listen(int s, int backlog)
3544 {
3545  int r;
3546  RUBY_CRITICAL {
3547  r = listen(TO_SOCKET(s), backlog);
3548  if (r == SOCKET_ERROR)
3549  errno = map_errno(WSAGetLastError());
3550  }
3551  return r;
3552 }
3553 
3554 #undef recv
3555 #undef recvfrom
3556 #undef send
3557 #undef sendto
3558 
3559 /* License: Ruby's */
3560 static int
3561 finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
3562 {
3563  DWORD flg;
3564  int err;
3565 
3566  if (result != SOCKET_ERROR)
3567  *len = size;
3568  else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
3569  switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
3570  case WAIT_OBJECT_0:
3571  RUBY_CRITICAL {
3572  result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg);
3573  }
3574  if (result) {
3575  result = 0;
3576  *len = size;
3577  break;
3578  }
3579  result = SOCKET_ERROR;
3580  /* thru */
3581  default:
3582  if ((err = WSAGetLastError()) == WSAECONNABORTED && !input)
3583  errno = EPIPE;
3584  else if (err == WSAEMSGSIZE && input) {
3585  result = 0;
3586  *len = size;
3587  break;
3588  }
3589  else
3590  errno = map_errno(err);
3591  /* thru */
3592  case WAIT_OBJECT_0 + 1:
3593  /* interrupted */
3594  *len = -1;
3595  CancelIo((HANDLE)s);
3596  break;
3597  }
3598  }
3599  else {
3600  if (err == WSAECONNABORTED && !input)
3601  errno = EPIPE;
3602  else
3603  errno = map_errno(err);
3604  *len = -1;
3605  }
3606  CloseHandle(wol->hEvent);
3607 
3608  return result;
3609 }
3610 
3611 /* License: Artistic or GPL */
3612 static int
3613 overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
3614  struct sockaddr *addr, int *addrlen)
3615 {
3616  int r;
3617  int ret;
3618  int mode = 0;
3619  DWORD flg;
3620  WSAOVERLAPPED wol;
3621  WSABUF wbuf;
3622  SOCKET s;
3623 
3624  s = TO_SOCKET(fd);
3625  socklist_lookup(s, &mode);
3626  if (GET_FLAGS(mode) & O_NONBLOCK) {
3627  RUBY_CRITICAL {
3628  if (input) {
3629  if (addr && addrlen)
3630  r = recvfrom(s, buf, len, flags, addr, addrlen);
3631  else
3632  r = recv(s, buf, len, flags);
3633  if (r == SOCKET_ERROR)
3634  errno = map_errno(WSAGetLastError());
3635  }
3636  else {
3637  if (addr && addrlen)
3638  r = sendto(s, buf, len, flags, addr, *addrlen);
3639  else
3640  r = send(s, buf, len, flags);
3641  if (r == SOCKET_ERROR) {
3642  DWORD err = WSAGetLastError();
3643  if (err == WSAECONNABORTED)
3644  errno = EPIPE;
3645  else
3646  errno = map_errno(err);
3647  }
3648  }
3649  }
3650  }
3651  else {
3652  DWORD size;
3653  DWORD rlen;
3654  wbuf.len = len;
3655  wbuf.buf = buf;
3656  memset(&wol, 0, sizeof(wol));
3657  RUBY_CRITICAL {
3658  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3659  if (input) {
3660  flg = flags;
3661  if (addr && addrlen)
3662  ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3663  &wol, NULL);
3664  else
3665  ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3666  }
3667  else {
3668  if (addr && addrlen)
3669  ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3670  &wol, NULL);
3671  else
3672  ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3673  }
3674  }
3675 
3676  finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3677  r = (int)rlen;
3678  }
3679 
3680  return r;
3681 }
3682 
3683 /* License: Ruby's */
3684 int WSAAPI
3685 rb_w32_recv(int fd, char *buf, int len, int flags)
3686 {
3687  return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
3688 }
3689 
3690 /* License: Ruby's */
3691 int WSAAPI
3692 rb_w32_recvfrom(int fd, char *buf, int len, int flags,
3693  struct sockaddr *from, int *fromlen)
3694 {
3695  return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
3696 }
3697 
3698 /* License: Ruby's */
3699 int WSAAPI
3700 rb_w32_send(int fd, const char *buf, int len, int flags)
3701 {
3702  return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
3703 }
3704 
3705 /* License: Ruby's */
3706 int WSAAPI
3707 rb_w32_sendto(int fd, const char *buf, int len, int flags,
3708  const struct sockaddr *to, int tolen)
3709 {
3710  return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
3711  (struct sockaddr *)to, &tolen);
3712 }
3713 
3714 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3715 /* License: Ruby's */
3716 typedef struct {
3717  SOCKADDR *name;
3718  int namelen;
3719  WSABUF *lpBuffers;
3720  DWORD dwBufferCount;
3721  WSABUF Control;
3722  DWORD dwFlags;
3723 } WSAMSG;
3724 #endif
3725 #ifndef WSAID_WSARECVMSG
3726 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3727 #endif
3728 #ifndef WSAID_WSASENDMSG
3729 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3730 #endif
3731 
3732 /* License: Ruby's */
3733 #define msghdr_to_wsamsg(msg, wsamsg) \
3734  do { \
3735  int i; \
3736  (wsamsg)->name = (msg)->msg_name; \
3737  (wsamsg)->namelen = (msg)->msg_namelen; \
3738  (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3739  (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3740  for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3741  (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3742  (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3743  } \
3744  (wsamsg)->Control.buf = (msg)->msg_control; \
3745  (wsamsg)->Control.len = (msg)->msg_controllen; \
3746  (wsamsg)->dwFlags = (msg)->msg_flags; \
3747  } while (0)
3748 
3749 /* License: Ruby's */
3750 int
3751 recvmsg(int fd, struct msghdr *msg, int flags)
3752 {
3753  typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3754  static WSARecvMsg_t pWSARecvMsg = NULL;
3755  WSAMSG wsamsg;
3756  SOCKET s;
3757  int mode = 0;
3758  DWORD len;
3759  int ret;
3760 
3761  s = TO_SOCKET(fd);
3762 
3763  if (!pWSARecvMsg) {
3764  static const GUID guid = WSAID_WSARECVMSG;
3765  pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, guid);
3766  if (!pWSARecvMsg)
3767  return -1;
3768  }
3769 
3770  msghdr_to_wsamsg(msg, &wsamsg);
3771  wsamsg.dwFlags |= flags;
3772 
3773  socklist_lookup(s, &mode);
3774  if (GET_FLAGS(mode) & O_NONBLOCK) {
3775  RUBY_CRITICAL {
3776  if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
3777  errno = map_errno(WSAGetLastError());
3778  len = -1;
3779  }
3780  }
3781  }
3782  else {
3783  DWORD size;
3784  WSAOVERLAPPED wol;
3785  memset(&wol, 0, sizeof(wol));
3786  RUBY_CRITICAL {
3787  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3788  ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3789  }
3790 
3791  ret = finish_overlapped_socket(TRUE, s, &wol, ret, &len, size);
3792  }
3793  if (ret == SOCKET_ERROR)
3794  return -1;
3795 
3796  /* WSAMSG to msghdr */
3797  msg->msg_name = wsamsg.name;
3798  msg->msg_namelen = wsamsg.namelen;
3799  msg->msg_flags = wsamsg.dwFlags;
3800 
3801  return len;
3802 }
3803 
3804 /* License: Ruby's */
3805 int
3806 sendmsg(int fd, const struct msghdr *msg, int flags)
3807 {
3808  typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3809  static WSASendMsg_t pWSASendMsg = NULL;
3810  WSAMSG wsamsg;
3811  SOCKET s;
3812  int mode = 0;
3813  DWORD len;
3814  int ret;
3815 
3816  s = TO_SOCKET(fd);
3817 
3818  if (!pWSASendMsg) {
3819  static const GUID guid = WSAID_WSASENDMSG;
3820  pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, guid);
3821  if (!pWSASendMsg)
3822  return -1;
3823  }
3824 
3825  msghdr_to_wsamsg(msg, &wsamsg);
3826 
3827  socklist_lookup(s, &mode);
3828  if (GET_FLAGS(mode) & O_NONBLOCK) {
3829  RUBY_CRITICAL {
3830  if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
3831  errno = map_errno(WSAGetLastError());
3832  len = -1;
3833  }
3834  }
3835  }
3836  else {
3837  DWORD size;
3838  WSAOVERLAPPED wol;
3839  memset(&wol, 0, sizeof(wol));
3840  RUBY_CRITICAL {
3841  wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3842  ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3843  }
3844 
3845  finish_overlapped_socket(FALSE, s, &wol, ret, &len, size);
3846  }
3847 
3848  return len;
3849 }
3850 
3851 #undef setsockopt
3852 
3853 /* License: Artistic or GPL */
3854 int WSAAPI
3855 rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
3856 {
3857  int r;
3858  RUBY_CRITICAL {
3859  r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3860  if (r == SOCKET_ERROR)
3861  errno = map_errno(WSAGetLastError());
3862  }
3863  return r;
3864 }
3865 
3866 #undef shutdown
3867 
3868 /* License: Artistic or GPL */
3869 int WSAAPI
3870 rb_w32_shutdown(int s, int how)
3871 {
3872  int r;
3873  RUBY_CRITICAL {
3874  r = shutdown(TO_SOCKET(s), how);
3875  if (r == SOCKET_ERROR)
3876  errno = map_errno(WSAGetLastError());
3877  }
3878  return r;
3879 }
3880 
3881 /* License: Ruby's */
3882 static SOCKET
3883 open_ifs_socket(int af, int type, int protocol)
3884 {
3885  unsigned long proto_buffers_len = 0;
3886  int error_code;
3887  SOCKET out = INVALID_SOCKET;
3888 
3889  if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3890  error_code = WSAGetLastError();
3891  if (error_code == WSAENOBUFS) {
3892  WSAPROTOCOL_INFO *proto_buffers;
3893  int protocols_available = 0;
3894 
3895  proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3896  if (!proto_buffers) {
3897  WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3898  return INVALID_SOCKET;
3899  }
3900 
3901  protocols_available =
3902  WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3903  if (protocols_available != SOCKET_ERROR) {
3904  int i;
3905  for (i = 0; i < protocols_available; i++) {
3906  if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3907  (type != proto_buffers[i].iSocketType) ||
3908  (protocol != 0 && protocol != proto_buffers[i].iProtocol))
3909  continue;
3910 
3911  if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3912  continue;
3913 
3914  out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
3915  WSA_FLAG_OVERLAPPED);
3916  break;
3917  }
3918  if (out == INVALID_SOCKET)
3919  out = WSASocket(af, type, protocol, NULL, 0, 0);
3920  if (out != INVALID_SOCKET)
3921  SetHandleInformation((HANDLE)out, HANDLE_FLAG_INHERIT, 0);
3922  }
3923 
3924  free(proto_buffers);
3925  }
3926  }
3927 
3928  return out;
3929 }
3930 
3931 #undef socket
3932 
3933 /* License: Artistic or GPL */
3934 int WSAAPI
3935 rb_w32_socket(int af, int type, int protocol)
3936 {
3937  SOCKET s;
3938  int fd;
3939 
3940  RUBY_CRITICAL {
3941  s = open_ifs_socket(af, type, protocol);
3942  if (s == INVALID_SOCKET) {
3943  errno = map_errno(WSAGetLastError());
3944  fd = -1;
3945  }
3946  else {
3947  fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3948  if (fd != -1)
3949  socklist_insert(s, MAKE_SOCKDATA(af, 0));
3950  else
3951  closesocket(s);
3952  }
3953  }
3954  return fd;
3955 }
3956 
3957 #undef gethostbyaddr
3958 
3959 /* License: Artistic or GPL */
3960 struct hostent * WSAAPI
3961 rb_w32_gethostbyaddr(const char *addr, int len, int type)
3962 {
3963  struct hostent *r;
3964  RUBY_CRITICAL {
3965  r = gethostbyaddr(addr, len, type);
3966  if (r == NULL)
3967  errno = map_errno(WSAGetLastError());
3968  }
3969  return r;
3970 }
3971 
3972 #undef gethostbyname
3973 
3974 /* License: Artistic or GPL */
3975 struct hostent * WSAAPI
3976 rb_w32_gethostbyname(const char *name)
3977 {
3978  struct hostent *r;
3979  RUBY_CRITICAL {
3980  r = gethostbyname(name);
3981  if (r == NULL)
3982  errno = map_errno(WSAGetLastError());
3983  }
3984  return r;
3985 }
3986 
3987 #undef gethostname
3988 
3989 /* License: Artistic or GPL */
3990 int WSAAPI
3991 rb_w32_gethostname(char *name, int len)
3992 {
3993  int r;
3994  RUBY_CRITICAL {
3995  r = gethostname(name, len);
3996  if (r == SOCKET_ERROR)
3997  errno = map_errno(WSAGetLastError());
3998  }
3999  return r;
4000 }
4001 
4002 #undef getprotobyname
4003 
4004 /* License: Artistic or GPL */
4005 struct protoent * WSAAPI
4006 rb_w32_getprotobyname(const char *name)
4007 {
4008  struct protoent *r;
4009  RUBY_CRITICAL {
4010  r = getprotobyname(name);
4011  if (r == NULL)
4012  errno = map_errno(WSAGetLastError());
4013  }
4014  return r;
4015 }
4016 
4017 #undef getprotobynumber
4018 
4019 /* License: Artistic or GPL */
4020 struct protoent * WSAAPI
4021 rb_w32_getprotobynumber(int num)
4022 {
4023  struct protoent *r;
4024  RUBY_CRITICAL {
4025  r = getprotobynumber(num);
4026  if (r == NULL)
4027  errno = map_errno(WSAGetLastError());
4028  }
4029  return r;
4030 }
4031 
4032 #undef getservbyname
4033 
4034 /* License: Artistic or GPL */
4035 struct servent * WSAAPI
4036 rb_w32_getservbyname(const char *name, const char *proto)
4037 {
4038  struct servent *r;
4039  RUBY_CRITICAL {
4040  r = getservbyname(name, proto);
4041  if (r == NULL)
4042  errno = map_errno(WSAGetLastError());
4043  }
4044  return r;
4045 }
4046 
4047 #undef getservbyport
4048 
4049 /* License: Artistic or GPL */
4050 struct servent * WSAAPI
4051 rb_w32_getservbyport(int port, const char *proto)
4052 {
4053  struct servent *r;
4054  RUBY_CRITICAL {
4055  r = getservbyport(port, proto);
4056  if (r == NULL)
4057  errno = map_errno(WSAGetLastError());
4058  }
4059  return r;
4060 }
4061 
4062 #ifdef HAVE_AFUNIX_H
4063 
4064 /* License: Ruby's */
4065 static size_t
4066 socketpair_unix_path(struct sockaddr_un *sock_un)
4067 {
4068  SOCKET listener;
4069  WCHAR wpath[sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path)] = L"";
4070 
4071  /* AF_UNIX/SOCK_STREAM became available in Windows 10
4072  * See https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows
4073  */
4074  listener = socket(AF_UNIX, SOCK_STREAM, 0);
4075  if (listener == INVALID_SOCKET)
4076  return 0;
4077 
4078  memset(sock_un, 0, sizeof(*sock_un));
4079  sock_un->sun_family = AF_UNIX;
4080 
4081  /* Abstract sockets (filesystem-independent) don't work, contrary to
4082  * the claims of the aforementioned blog post:
4083  * https://github.com/microsoft/WSL/issues/4240#issuecomment-549663217
4084  *
4085  * So we must use a named path, and that comes with all the attendant
4086  * problems of permissions and collisions. Trying various temporary
4087  * directories and putting high-res time and PID in the filename.
4088  */
4089  for (int try = 0; ; try++) {
4090  LARGE_INTEGER ticks;
4091  size_t path_len = 0;
4092  const size_t maxpath = sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path);
4093 
4094  switch (try) {
4095  case 0:
4096  /* user temp dir from TMP or TEMP env var, it ends with a backslash */
4097  path_len = GetTempPathW(maxpath, wpath);
4098  break;
4099  case 1:
4100  wcsncpy(wpath, L"C:/Temp/", maxpath);
4101  path_len = lstrlenW(wpath);
4102  break;
4103  case 2:
4104  /* Current directory */
4105  path_len = 0;
4106  break;
4107  case 3:
4108  closesocket(listener);
4109  return 0;
4110  }
4111 
4112  /* Windows UNIXSocket implementation expects UTF-8 instead of UTF16 */
4113  path_len = WideCharToMultiByte(CP_UTF8, 0, wpath, path_len, sock_un->sun_path, maxpath, NULL, NULL);
4114  QueryPerformanceCounter(&ticks);
4115  path_len += snprintf(sock_un->sun_path + path_len,
4116  maxpath - path_len,
4117  "%lld-%ld.($)",
4118  ticks.QuadPart,
4119  GetCurrentProcessId());
4120 
4121  /* Convert to UTF16 for DeleteFileW */
4122  MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
4123 
4124  if (bind(listener, (struct sockaddr *)sock_un, sizeof(*sock_un)) != SOCKET_ERROR)
4125  break;
4126  }
4127  closesocket(listener);
4128  DeleteFileW(wpath);
4129  return sizeof(*sock_un);
4130 }
4131 #endif
4132 
4133 /* License: Ruby's */
4134 static int
4135 socketpair_internal(int af, int type, int protocol, SOCKET *sv)
4136 {
4137  SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
4138  struct sockaddr_in sock_in4;
4139 
4140 #ifdef INET6
4141  struct sockaddr_in6 sock_in6;
4142 #endif
4143 
4144 #ifdef HAVE_AFUNIX_H
4145  struct sockaddr_un sock_un = {0, {0}};
4146  WCHAR wpath[sizeof(sock_un.sun_path)/sizeof(*sock_un.sun_path)] = L"";
4147 #endif
4148 
4149  struct sockaddr *addr;
4150  int ret = -1;
4151  int len;
4152 
4153  switch (af) {
4154  case AF_INET:
4155 #if defined PF_INET && PF_INET != AF_INET
4156  case PF_INET:
4157 #endif
4158  sock_in4.sin_family = AF_INET;
4159  sock_in4.sin_port = 0;
4160  sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4161  addr = (struct sockaddr *)&sock_in4;
4162  len = sizeof(sock_in4);
4163  break;
4164 #ifdef INET6
4165  case AF_INET6:
4166  memset(&sock_in6, 0, sizeof(sock_in6));
4167  sock_in6.sin6_family = AF_INET6;
4168  sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
4169  addr = (struct sockaddr *)&sock_in6;
4170  len = sizeof(sock_in6);
4171  break;
4172 #endif
4173 #ifdef HAVE_AFUNIX_H
4174  case AF_UNIX:
4175  addr = (struct sockaddr *)&sock_un;
4176  len = socketpair_unix_path(&sock_un);
4177  MultiByteToWideChar(CP_UTF8, 0, sock_un.sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
4178  if (len)
4179  break;
4180  /* fall through */
4181 #endif
4182  default:
4183  errno = EAFNOSUPPORT;
4184  return -1;
4185  }
4186  if (type != SOCK_STREAM) {
4187  errno = EPROTOTYPE;
4188  return -1;
4189  }
4190 
4191  sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
4192  sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
4193  RUBY_CRITICAL {
4194  do {
4195  svr = open_ifs_socket(af, type, protocol);
4196  if (svr == INVALID_SOCKET)
4197  break;
4198  if (bind(svr, addr, len) < 0)
4199  break;
4200  if (getsockname(svr, addr, &len) < 0)
4201  break;
4202  if (type == SOCK_STREAM)
4203  listen(svr, 5);
4204 
4205  w = open_ifs_socket(af, type, protocol);
4206  if (w == INVALID_SOCKET)
4207  break;
4208  if (connect(w, addr, len) < 0)
4209  break;
4210 
4211  r = accept(svr, addr, &len);
4212  if (r == INVALID_SOCKET)
4213  break;
4214  SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
4215 
4216  ret = 0;
4217  } while (0);
4218 
4219  if (ret < 0) {
4220  errno = map_errno(WSAGetLastError());
4221  if (r != INVALID_SOCKET)
4222  closesocket(r);
4223  if (w != INVALID_SOCKET)
4224  closesocket(w);
4225  }
4226  else {
4227  sv[0] = r;
4228  sv[1] = w;
4229  }
4230  if (svr != INVALID_SOCKET)
4231  closesocket(svr);
4232 #ifdef HAVE_AFUNIX_H
4233  if (sock_un.sun_family == AF_UNIX)
4234  DeleteFileW(wpath);
4235 #endif
4236  }
4237 
4238  return ret;
4239 }
4240 
4241 /* License: Ruby's */
4242 int
4243 socketpair(int af, int type, int protocol, int *sv)
4244 {
4245  SOCKET pair[2];
4246 
4247  if (socketpair_internal(af, type, protocol, pair) < 0)
4248  return -1;
4249  sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
4250  if (sv[0] == -1) {
4251  closesocket(pair[0]);
4252  closesocket(pair[1]);
4253  return -1;
4254  }
4255  sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
4256  if (sv[1] == -1) {
4257  rb_w32_close(sv[0]);
4258  closesocket(pair[1]);
4259  return -1;
4260  }
4261  socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
4262  socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
4263 
4264  return 0;
4265 }
4266 
4267 #if !defined(_MSC_VER) || _MSC_VER >= 1400
4268 /* License: Ruby's */
4269 static void
4270 str2guid(const char *str, GUID *guid)
4271 {
4272 #define hex2byte(str) \
4273  ((isdigit(*(str)) ? *(str) - '0' : toupper(*(str)) - 'A' + 10) << 4 | (isdigit(*((str) + 1)) ? *((str) + 1) - '0' : toupper(*((str) + 1)) - 'A' + 10))
4274  char *end;
4275  int i;
4276  if (*str == '{') str++;
4277  guid->Data1 = (long)strtoul(str, &end, 16);
4278  str += 9;
4279  guid->Data2 = (unsigned short)strtoul(str, &end, 16);
4280  str += 5;
4281  guid->Data3 = (unsigned short)strtoul(str, &end, 16);
4282  str += 5;
4283  guid->Data4[0] = hex2byte(str);
4284  str += 2;
4285  guid->Data4[1] = hex2byte(str);
4286  str += 3;
4287  for (i = 0; i < 6; i++) {
4288  guid->Data4[i + 2] = hex2byte(str);
4289  str += 2;
4290  }
4291 }
4292 
4293 /* License: Ruby's */
4294 #ifndef HAVE_TYPE_NET_LUID
4295  typedef struct {
4296  uint64_t Value;
4297  struct {
4298  uint64_t Reserved :24;
4299  uint64_t NetLuidIndex :24;
4300  uint64_t IfType :16;
4301  } Info;
4302  } NET_LUID;
4303 #endif
4304 
4305 int
4306 getifaddrs(struct ifaddrs **ifap)
4307 {
4308  ULONG size = 0;
4309  ULONG ret;
4310  IP_ADAPTER_ADDRESSES *root, *addr;
4311  struct ifaddrs *prev;
4312 
4313  ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
4314  if (ret != ERROR_BUFFER_OVERFLOW) {
4315  errno = map_errno(ret);
4316  return -1;
4317  }
4318  root = ruby_xmalloc(size);
4319  ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, root, &size);
4320  if (ret != ERROR_SUCCESS) {
4321  errno = map_errno(ret);
4322  ruby_xfree(root);
4323  return -1;
4324  }
4325 
4326  for (prev = NULL, addr = root; addr; addr = addr->Next) {
4327  struct ifaddrs *ifa = ruby_xcalloc(1, sizeof(*ifa));
4328  char name[IFNAMSIZ];
4329  GUID guid;
4330  NET_LUID luid;
4331 
4332  if (prev)
4333  prev->ifa_next = ifa;
4334  else
4335  *ifap = ifa;
4336 
4337  str2guid(addr->AdapterName, &guid);
4338  if (ConvertInterfaceGuidToLuid(&guid, &luid) == NO_ERROR &&
4339  ConvertInterfaceLuidToNameA(&luid, name, sizeof(name)) == NO_ERROR) {
4340  ifa->ifa_name = ruby_strdup(name);
4341  }
4342  else {
4343  ifa->ifa_name = ruby_strdup(addr->AdapterName);
4344  }
4345 
4346  if (addr->IfType & IF_TYPE_SOFTWARE_LOOPBACK)
4347  ifa->ifa_flags |= IFF_LOOPBACK;
4348  if (addr->OperStatus == IfOperStatusUp) {
4349  ifa->ifa_flags |= IFF_UP;
4350 
4351  if (addr->FirstUnicastAddress) {
4352  IP_ADAPTER_UNICAST_ADDRESS *cur;
4353  int added = 0;
4354  for (cur = addr->FirstUnicastAddress; cur; cur = cur->Next) {
4355  if (cur->Flags & IP_ADAPTER_ADDRESS_TRANSIENT ||
4356  cur->DadState == IpDadStateDeprecated) {
4357  continue;
4358  }
4359  if (added) {
4360  prev = ifa;
4361  ifa = ruby_xcalloc(1, sizeof(*ifa));
4362  prev->ifa_next = ifa;
4363  ifa->ifa_name = ruby_strdup(prev->ifa_name);
4364  ifa->ifa_flags = prev->ifa_flags;
4365  }
4366  ifa->ifa_addr = ruby_xmalloc(cur->Address.iSockaddrLength);
4367  memcpy(ifa->ifa_addr, cur->Address.lpSockaddr,
4368  cur->Address.iSockaddrLength);
4369  added = 1;
4370  }
4371  }
4372  }
4373 
4374  prev = ifa;
4375  }
4376 
4377  ruby_xfree(root);
4378  return 0;
4379 }
4380 
4381 /* License: Ruby's */
4382 void
4383 freeifaddrs(struct ifaddrs *ifp)
4384 {
4385  while (ifp) {
4386  struct ifaddrs *next = ifp->ifa_next;
4387  ruby_xfree(ifp->ifa_addr);
4388  ruby_xfree(ifp->ifa_name);
4389  ruby_xfree(ifp);
4390  ifp = next;
4391  }
4392 }
4393 #endif
4394 
4395 #if 0 // Have never been used
4396 //
4397 // Networking stubs
4398 //
4399 
4400 void endhostent(void) {}
4401 void endnetent(void) {}
4402 void endprotoent(void) {}
4403 void endservent(void) {}
4404 
4405 struct netent *getnetent (void) {return (struct netent *) NULL;}
4406 
4407 struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
4408 
4409 struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
4410 
4411 struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
4412 
4413 struct servent *getservent (void) {return (struct servent *) NULL;}
4414 
4415 void sethostent (int stayopen) {}
4416 
4417 void setnetent (int stayopen) {}
4418 
4419 void setprotoent (int stayopen) {}
4420 
4421 void setservent (int stayopen) {}
4422 #endif
4423 
4424 int rb_w32_set_nonblock2(int fd, int nonblock);
4425 
4426 /* License: Ruby's */
4427 static int
4428 setfl(SOCKET sock, int arg)
4429 {
4430  int ret;
4431  int af = 0;
4432  int flag = 0;
4433  u_long ioctlArg;
4434 
4435  socklist_lookup(sock, &flag);
4436  af = GET_FAMILY(flag);
4437  flag = GET_FLAGS(flag);
4438  if (arg & O_NONBLOCK) {
4439  flag |= O_NONBLOCK;
4440  ioctlArg = 1;
4441  }
4442  else {
4443  flag &= ~O_NONBLOCK;
4444  ioctlArg = 0;
4445  }
4446  RUBY_CRITICAL {
4447  ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
4448  if (ret == 0)
4449  socklist_insert(sock, MAKE_SOCKDATA(af, flag));
4450  else
4451  errno = map_errno(WSAGetLastError());
4452  }
4453 
4454  return ret;
4455 }
4456 
4457 /* License: Ruby's */
4458 static int
4459 dupfd(HANDLE hDup, int flags, int minfd)
4460 {
4461  int save_errno;
4462  int ret;
4463  int fds[32];
4464  int filled = 0;
4465 
4466  do {
4467  ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
4468  if (ret == -1) {
4469  goto close_fds_and_return;
4470  }
4471  if (ret >= minfd) {
4472  goto close_fds_and_return;
4473  }
4474  fds[filled++] = ret;
4475  } while (filled < (int)numberof(fds));
4476 
4477  ret = dupfd(hDup, flags, minfd);
4478 
4479  close_fds_and_return:
4480  save_errno = errno;
4481  while (filled > 0) {
4482  int fd = fds[--filled];
4483  _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
4484  close(fd);
4485  }
4486  errno = save_errno;
4487 
4488  return ret;
4489 }
4490 
4491 /* License: Ruby's */
4492 int
4493 fcntl(int fd, int cmd, ...)
4494 {
4495  va_list va;
4496  int arg;
4497  DWORD flag;
4498 
4499  switch (cmd) {
4500  case F_SETFL: {
4501  va_start(va, cmd);
4502  arg = va_arg(va, int);
4503  va_end(va);
4504  return rb_w32_set_nonblock2(fd, arg);
4505  }
4506  case F_DUPFD: case F_DUPFD_CLOEXEC: {
4507  int ret;
4508  HANDLE hDup;
4509  flag = _osfile(fd);
4510  if (!(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
4511  GetCurrentProcess(), &hDup, 0L,
4512  cmd == F_DUPFD && !(flag & FNOINHERIT),
4513  DUPLICATE_SAME_ACCESS))) {
4514  errno = map_errno(GetLastError());
4515  return -1;
4516  }
4517 
4518  va_start(va, cmd);
4519  arg = va_arg(va, int);
4520  va_end(va);
4521 
4522  if (cmd != F_DUPFD)
4523  flag |= FNOINHERIT;
4524  else
4525  flag &= ~FNOINHERIT;
4526  if ((ret = dupfd(hDup, flag, arg)) == -1)
4527  CloseHandle(hDup);
4528  return ret;
4529  }
4530  case F_GETFD: {
4531  SIGNED_VALUE h = _get_osfhandle(fd);
4532  if (h == -1) return -1;
4533  if (!GetHandleInformation((HANDLE)h, &flag)) {
4534  errno = map_errno(GetLastError());
4535  return -1;
4536  }
4537  return (flag & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
4538  }
4539  case F_SETFD: {
4540  SIGNED_VALUE h = _get_osfhandle(fd);
4541  if (h == -1) return -1;
4542  va_start(va, cmd);
4543  arg = va_arg(va, int);
4544  va_end(va);
4545  if (!SetHandleInformation((HANDLE)h, HANDLE_FLAG_INHERIT,
4546  (arg & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT)) {
4547  errno = map_errno(GetLastError());
4548  return -1;
4549  }
4550  if (arg & FD_CLOEXEC)
4551  _osfile(fd) |= FNOINHERIT;
4552  else
4553  _osfile(fd) &= ~FNOINHERIT;
4554  return 0;
4555  }
4556  default:
4557  errno = EINVAL;
4558  return -1;
4559  }
4560 }
4561 
4562 /* License: Ruby's */
4563 int
4564 rb_w32_set_nonblock2(int fd, int nonblock)
4565 {
4566  SOCKET sock = TO_SOCKET(fd);
4567  if (is_socket(sock)) {
4568  return setfl(sock, nonblock ? O_NONBLOCK : 0);
4569  }
4570  else if (is_pipe(sock)) {
4571  DWORD state;
4572  if (!GetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL, NULL, NULL, 0)) {
4573  errno = map_errno(GetLastError());
4574  return -1;
4575  }
4576  if (nonblock) {
4577  state |= PIPE_NOWAIT;
4578  }
4579  else {
4580  state &= ~PIPE_NOWAIT;
4581  }
4582  if (!SetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL)) {
4583  errno = map_errno(GetLastError());
4584  return -1;
4585  }
4586  return 0;
4587  }
4588  else {
4589  errno = EBADF;
4590  return -1;
4591  }
4592 }
4593 
4594 int
4595 rb_w32_set_nonblock(int fd)
4596 {
4597  return rb_w32_set_nonblock2(fd, TRUE);
4598 }
4599 
4600 #ifndef WNOHANG
4601 #define WNOHANG -1
4602 #endif
4603 
4604 /* License: Ruby's */
4605 static rb_pid_t
4606 poll_child_status(struct ChildRecord *child, int *stat_loc)
4607 {
4608  DWORD exitcode;
4609  DWORD err;
4610 
4611  if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
4612  /* If an error occurred, return immediately. */
4613  err = GetLastError();
4614  switch (err) {
4615  case ERROR_INVALID_PARAMETER:
4616  errno = ECHILD;
4617  break;
4618  case ERROR_INVALID_HANDLE:
4619  errno = EINVAL;
4620  break;
4621  default:
4622  errno = map_errno(err);
4623  break;
4624  }
4625  error_exit:
4626  CloseChildHandle(child);
4627  return -1;
4628  }
4629  if (exitcode != STILL_ACTIVE) {
4630  rb_pid_t pid;
4631  /* If already died, wait process's real termination. */
4632  if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
4633  goto error_exit;
4634  }
4635  pid = child->pid;
4636  CloseChildHandle(child);
4637  if (stat_loc) {
4638  *stat_loc = exitcode << 8;
4639  if (exitcode & 0xC0000000) {
4640  static const struct {
4641  DWORD status;
4642  int sig;
4643  } table[] = {
4644  {STATUS_ACCESS_VIOLATION, SIGSEGV},
4645  {STATUS_ILLEGAL_INSTRUCTION, SIGILL},
4646  {STATUS_PRIVILEGED_INSTRUCTION, SIGILL},
4647  {STATUS_FLOAT_DENORMAL_OPERAND, SIGFPE},
4648  {STATUS_FLOAT_DIVIDE_BY_ZERO, SIGFPE},
4649  {STATUS_FLOAT_INEXACT_RESULT, SIGFPE},
4650  {STATUS_FLOAT_INVALID_OPERATION, SIGFPE},
4651  {STATUS_FLOAT_OVERFLOW, SIGFPE},
4652  {STATUS_FLOAT_STACK_CHECK, SIGFPE},
4653  {STATUS_FLOAT_UNDERFLOW, SIGFPE},
4654 #ifdef STATUS_FLOAT_MULTIPLE_FAULTS
4655  {STATUS_FLOAT_MULTIPLE_FAULTS, SIGFPE},
4656 #endif
4657 #ifdef STATUS_FLOAT_MULTIPLE_TRAPS
4658  {STATUS_FLOAT_MULTIPLE_TRAPS, SIGFPE},
4659 #endif
4660  {STATUS_CONTROL_C_EXIT, SIGINT},
4661  };
4662  int i;
4663  for (i = 0; i < (int)numberof(table); i++) {
4664  if (table[i].status == exitcode) {
4665  *stat_loc |= table[i].sig;
4666  break;
4667  }
4668  }
4669  // if unknown status, assume SEGV
4670  if (i >= (int)numberof(table))
4671  *stat_loc |= SIGSEGV;
4672  }
4673  }
4674  return pid;
4675  }
4676  return 0;
4677 }
4678 
4679 /* License: Artistic or GPL */
4680 rb_pid_t
4681 waitpid(rb_pid_t pid, int *stat_loc, int options)
4682 {
4683  DWORD timeout;
4684 
4685  /* Artistic or GPL part start */
4686  if (options == WNOHANG) {
4687  timeout = 0;
4688  }
4689  else {
4690  timeout = INFINITE;
4691  }
4692  /* Artistic or GPL part end */
4693 
4694  if (pid == -1) {
4695  int count = 0;
4696  int ret;
4697  HANDLE events[MAXCHILDNUM];
4698  struct ChildRecord* cause;
4699 
4700  FOREACH_CHILD(child) {
4701  if (!child->pid || child->pid < 0) continue;
4702  if ((pid = poll_child_status(child, stat_loc))) return pid;
4703  events[count++] = child->hProcess;
4704  } END_FOREACH_CHILD;
4705  if (!count) {
4706  errno = ECHILD;
4707  return -1;
4708  }
4709 
4710  ret = rb_w32_wait_events_blocking(events, count, timeout);
4711  if (ret == WAIT_TIMEOUT) return 0;
4712  if ((ret -= WAIT_OBJECT_0) == count) {
4713  return -1;
4714  }
4715  if (ret > count) {
4716  errno = map_errno(GetLastError());
4717  return -1;
4718  }
4719 
4720  cause = FindChildSlotByHandle(events[ret]);
4721  if (!cause) {
4722  errno = ECHILD;
4723  return -1;
4724  }
4725  return poll_child_status(cause, stat_loc);
4726  }
4727  else {
4728  struct ChildRecord* child = FindChildSlot(pid);
4729  int retried = 0;
4730  if (!child) {
4731  errno = ECHILD;
4732  return -1;
4733  }
4734 
4735  while (!(pid = poll_child_status(child, stat_loc))) {
4736  /* wait... */
4737  int ret = rb_w32_wait_events_blocking(&child->hProcess, 1, timeout);
4738  if (ret == WAIT_OBJECT_0 + 1) return -1; /* maybe EINTR */
4739  if (ret != WAIT_OBJECT_0) {
4740  /* still active */
4741  if (options & WNOHANG) {
4742  pid = 0;
4743  break;
4744  }
4745  ++retried;
4746  }
4747  }
4748  if (pid == -1 && retried) pid = 0;
4749  }
4750 
4751  return pid;
4752 }
4753 
4754 #include <sys/timeb.h>
4755 
4756 /* License: Ruby's */
4757 /* split FILETIME value into UNIX time and sub-seconds in NT ticks */
4758 static time_t
4759 filetime_split(const FILETIME* ft, long *subsec)
4760 {
4761  ULARGE_INTEGER tmp;
4762  unsigned LONG_LONG lt;
4763  const unsigned LONG_LONG subsec_unit = (unsigned LONG_LONG)10 * 1000 * 1000;
4764 
4765  tmp.LowPart = ft->dwLowDateTime;
4766  tmp.HighPart = ft->dwHighDateTime;
4767  lt = tmp.QuadPart;
4768 
4769  /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
4770  convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
4771  the first leap second is at 1972/06/30, so we doesn't need to think
4772  about it. */
4773  lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * subsec_unit;
4774 
4775  *subsec = (long)(lt % subsec_unit);
4776  return (time_t)(lt / subsec_unit);
4777 }
4778 
4779 /* License: Ruby's */
4780 int __cdecl
4781 gettimeofday(struct timeval *tv, struct timezone *tz)
4782 {
4783  FILETIME ft;
4784  long subsec;
4785 
4786  GetSystemTimePreciseAsFileTime(&ft);
4787  tv->tv_sec = filetime_split(&ft, &subsec);
4788  tv->tv_usec = subsec / 10;
4789 
4790  return 0;
4791 }
4792 
4793 /* License: Ruby's */
4794 int
4795 clock_gettime(clockid_t clock_id, struct timespec *sp)
4796 {
4797  switch (clock_id) {
4798  case CLOCK_REALTIME:
4799  {
4800  FILETIME ft;
4801  long subsec;
4802 
4803  GetSystemTimePreciseAsFileTime(&ft);
4804  sp->tv_sec = filetime_split(&ft, &subsec);
4805  sp->tv_nsec = subsec * 100;
4806  return 0;
4807  }
4808  case CLOCK_MONOTONIC:
4809  {
4810  LARGE_INTEGER freq;
4811  LARGE_INTEGER count;
4812  if (!QueryPerformanceFrequency(&freq)) {
4813  errno = map_errno(GetLastError());
4814  return -1;
4815  }
4816  if (!QueryPerformanceCounter(&count)) {
4817  errno = map_errno(GetLastError());
4818  return -1;
4819  }
4820  sp->tv_sec = count.QuadPart / freq.QuadPart;
4821  if (freq.QuadPart < 1000000000)
4822  sp->tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
4823  else
4824  sp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
4825  return 0;
4826  }
4827  default:
4828  errno = EINVAL;
4829  return -1;
4830  }
4831 }
4832 
4833 /* License: Ruby's */
4834 int
4835 clock_getres(clockid_t clock_id, struct timespec *sp)
4836 {
4837  switch (clock_id) {
4838  case CLOCK_REALTIME:
4839  {
4840  sp->tv_sec = 0;
4841  sp->tv_nsec = 1000;
4842  return 0;
4843  }
4844  case CLOCK_MONOTONIC:
4845  {
4846  LARGE_INTEGER freq;
4847  if (!QueryPerformanceFrequency(&freq)) {
4848  errno = map_errno(GetLastError());
4849  return -1;
4850  }
4851  sp->tv_sec = 0;
4852  sp->tv_nsec = (long)(1000000000.0 / freq.QuadPart);
4853  return 0;
4854  }
4855  default:
4856  errno = EINVAL;
4857  return -1;
4858  }
4859 }
4860 
4861 /* License: Ruby's */
4862 static char *
4863 w32_getcwd(char *buffer, int size, UINT cp, void *alloc(int, void *), void *arg)
4864 {
4865  WCHAR *p;
4866  int wlen, len;
4867 
4868  len = GetCurrentDirectoryW(0, NULL);
4869  if (!len) {
4870  errno = map_errno(GetLastError());
4871  return NULL;
4872  }
4873 
4874  if (buffer && size < len) {
4875  errno = ERANGE;
4876  return NULL;
4877  }
4878 
4879  p = ALLOCA_N(WCHAR, len);
4880  if (!GetCurrentDirectoryW(len, p)) {
4881  errno = map_errno(GetLastError());
4882  return NULL;
4883  }
4884 
4885  wlen = translate_wchar(p, L'\\', L'/') - p + 1;
4886  len = WideCharToMultiByte(cp, 0, p, wlen, NULL, 0, NULL, NULL);
4887  if (buffer) {
4888  if (size < len) {
4889  errno = ERANGE;
4890  return NULL;
4891  }
4892  }
4893  else {
4894  buffer = (*alloc)(len, arg);
4895  if (!buffer) {
4896  errno = ENOMEM;
4897  return NULL;
4898  }
4899  }
4900  WideCharToMultiByte(cp, 0, p, wlen, buffer, len, NULL, NULL);
4901 
4902  return buffer;
4903 }
4904 
4905 /* License: Ruby's */
4906 static void *
4907 getcwd_alloc(int size, void *dummy)
4908 {
4909  return malloc(size);
4910 }
4911 
4912 /* License: Ruby's */
4913 char *
4914 rb_w32_getcwd(char *buffer, int size)
4915 {
4916  return w32_getcwd(buffer, size, filecp(), getcwd_alloc, NULL);
4917 }
4918 
4919 /* License: Ruby's */
4920 char *
4921 rb_w32_ugetcwd(char *buffer, int size)
4922 {
4923  return w32_getcwd(buffer, size, CP_UTF8, getcwd_alloc, NULL);
4924 }
4925 
4926 /* License: Ruby's */
4927 static void *
4928 getcwd_value(int size, void *arg)
4929 {
4930  VALUE str = *(VALUE *)arg = rb_utf8_str_new(0, size - 1);
4931  return RSTRING_PTR(str);
4932 }
4933 
4934 /* License: Ruby's */
4935 VALUE
4936 rb_dir_getwd_ospath(void)
4937 {
4938  VALUE cwd = Qnil;
4939  w32_getcwd(NULL, 0, CP_UTF8, getcwd_value, &cwd);
4940  return cwd;
4941 }
4942 
4943 /* License: Artistic or GPL */
4944 int
4945 chown(const char *path, int owner, int group)
4946 {
4947  return 0;
4948 }
4949 
4950 /* License: Artistic or GPL */
4951 int
4952 rb_w32_uchown(const char *path, int owner, int group)
4953 {
4954  return 0;
4955 }
4956 
4957 int
4958 lchown(const char *path, int owner, int group)
4959 {
4960  return 0;
4961 }
4962 
4963 int
4964 rb_w32_ulchown(const char *path, int owner, int group)
4965 {
4966  return 0;
4967 }
4968 
4969 /* License: Ruby's */
4970 int
4971 kill(rb_pid_t pid, int sig)
4972 {
4973  int ret = 0;
4974  DWORD err;
4975 
4976  if (pid < 0 || (pid == 0 && sig != SIGINT)) {
4977  errno = EINVAL;
4978  return -1;
4979  }
4980 
4981  if ((unsigned int)pid == GetCurrentProcessId() &&
4982  (sig != 0 && sig != SIGKILL)) {
4983  if ((ret = raise(sig)) != 0) {
4984  /* MSVCRT doesn't set errno... */
4985  errno = EINVAL;
4986  }
4987  return ret;
4988  }
4989 
4990  switch (sig) {
4991  case 0:
4992  RUBY_CRITICAL {
4993  HANDLE hProc =
4994  OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4995  if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4996  if (GetLastError() == ERROR_INVALID_PARAMETER) {
4997  errno = ESRCH;
4998  }
4999  else {
5000  errno = EPERM;
5001  }
5002  ret = -1;
5003  }
5004  else {
5005  CloseHandle(hProc);
5006  }
5007  }
5008  break;
5009 
5010  case SIGINT:
5011  RUBY_CRITICAL {
5012  DWORD ctrlEvent = CTRL_C_EVENT;
5013  if (pid != 0) {
5014  /* CTRL+C signal cannot be generated for process groups.
5015  * Instead, we use CTRL+BREAK signal. */
5016  ctrlEvent = CTRL_BREAK_EVENT;
5017  }
5018  if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
5019  if ((err = GetLastError()) == 0)
5020  errno = EPERM;
5021  else
5022  errno = map_errno(GetLastError());
5023  ret = -1;
5024  }
5025  }
5026  break;
5027 
5028  case SIGKILL:
5029  RUBY_CRITICAL {
5030  HANDLE hProc;
5031  struct ChildRecord* child = FindChildSlot(pid);
5032  if (child) {
5033  hProc = child->hProcess;
5034  }
5035  else {
5036  hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
5037  }
5038  if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
5039  if (GetLastError() == ERROR_INVALID_PARAMETER) {
5040  errno = ESRCH;
5041  }
5042  else {
5043  errno = EPERM;
5044  }
5045  ret = -1;
5046  }
5047  else {
5048  DWORD status;
5049  if (!GetExitCodeProcess(hProc, &status)) {
5050  errno = map_errno(GetLastError());
5051  ret = -1;
5052  }
5053  else if (status == STILL_ACTIVE) {
5054  if (!TerminateProcess(hProc, 0)) {
5055  errno = EPERM;
5056  ret = -1;
5057  }
5058  }
5059  else {
5060  errno = ESRCH;
5061  ret = -1;
5062  }
5063  if (!child) {
5064  CloseHandle(hProc);
5065  }
5066  }
5067  }
5068  break;
5069 
5070  default:
5071  errno = EINVAL;
5072  ret = -1;
5073  break;
5074  }
5075 
5076  return ret;
5077 }
5078 
5079 /* License: Ruby's */
5080 static int
5081 wlink(const WCHAR *from, const WCHAR *to)
5082 {
5083  if (!CreateHardLinkW(to, from, NULL)) {
5084  errno = map_errno(GetLastError());
5085  return -1;
5086  }
5087 
5088  return 0;
5089 }
5090 
5091 /* License: Ruby's */
5092 int
5093 rb_w32_ulink(const char *from, const char *to)
5094 {
5095  WCHAR *wfrom;
5096  WCHAR *wto;
5097  int ret;
5098 
5099  if (!(wfrom = utf8_to_wstr(from, NULL)))
5100  return -1;
5101  if (!(wto = utf8_to_wstr(to, NULL))) {
5102  free(wfrom);
5103  return -1;
5104  }
5105  ret = wlink(wfrom, wto);
5106  free(wto);
5107  free(wfrom);
5108  return ret;
5109 }
5110 
5111 /* License: Ruby's */
5112 int
5113 link(const char *from, const char *to)
5114 {
5115  WCHAR *wfrom;
5116  WCHAR *wto;
5117  int ret;
5118 
5119  if (!(wfrom = filecp_to_wstr(from, NULL)))
5120  return -1;
5121  if (!(wto = filecp_to_wstr(to, NULL))) {
5122  free(wfrom);
5123  return -1;
5124  }
5125  ret = wlink(wfrom, wto);
5126  free(wto);
5127  free(wfrom);
5128  return ret;
5129 }
5130 
5131 /* License: Public Domain, copied from mingw headers */
5132 #ifndef FILE_DEVICE_FILE_SYSTEM
5133 # define FILE_DEVICE_FILE_SYSTEM 0x00000009
5134 #endif
5135 #ifndef FSCTL_GET_REPARSE_POINT
5136 # define FSCTL_GET_REPARSE_POINT ((0x9<<16)|(42<<2))
5137 #endif
5138 #ifndef IO_REPARSE_TAG_SYMLINK
5139 # define IO_REPARSE_TAG_SYMLINK 0xA000000CL
5140 #endif
5141 
5142 /* License: Ruby's */
5143 static int
5144 reparse_symlink(const WCHAR *path, rb_w32_reparse_buffer_t *rp, size_t size)
5145 {
5146  HANDLE f;
5147  DWORD ret;
5148  int e = 0;
5149 
5150  f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5151  if (f == INVALID_HANDLE_VALUE) {
5152  return GetLastError();
5153  }
5154 
5155  if (!DeviceIoControl(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
5156  rp, size, &ret, NULL)) {
5157  e = GetLastError();
5158  }
5159  else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
5160  rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
5161  e = ERROR_INVALID_PARAMETER;
5162  }
5163  CloseHandle(f);
5164  return e;
5165 }
5166 
5167 /* License: Ruby's */
5168 int
5169 rb_w32_reparse_symlink_p(const WCHAR *path)
5170 {
5171  VALUE wtmp = 0;
5172  rb_w32_reparse_buffer_t rbuf, *rp = &rbuf;
5173  WCHAR *wbuf;
5174  DWORD len;
5175  int e;
5176 
5177  e = rb_w32_read_reparse_point(path, rp, sizeof(rbuf), &wbuf, &len);
5178  if (e == ERROR_MORE_DATA) {
5179  size_t size = rb_w32_reparse_buffer_size(len + 1);
5180  rp = ALLOCV(wtmp, size);
5181  e = rb_w32_read_reparse_point(path, rp, size, &wbuf, &len);
5182  ALLOCV_END(wtmp);
5183  }
5184  switch (e) {
5185  case 0:
5186  case ERROR_MORE_DATA:
5187  return TRUE;
5188  }
5189  return FALSE;
5190 }
5191 
5192 /* License: Ruby's */
5193 int
5194 rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
5195  size_t bufsize, WCHAR **result, DWORD *len)
5196 {
5197  int e = reparse_symlink(path, rp, bufsize);
5198  DWORD ret = 0;
5199 
5200  if (!e || e == ERROR_MORE_DATA) {
5201  void *name;
5202  if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
5203  name = ((char *)rp->SymbolicLinkReparseBuffer.PathBuffer +
5204  rp->SymbolicLinkReparseBuffer.PrintNameOffset);
5205  ret = rp->SymbolicLinkReparseBuffer.PrintNameLength;
5206  *len = ret / sizeof(WCHAR);
5207  }
5208  else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
5209  static const WCHAR volume[] = L"Volume{";
5210  enum {volume_prefix_len = rb_strlen_lit("\\??\\")};
5211  name = ((char *)rp->MountPointReparseBuffer.PathBuffer +
5212  rp->MountPointReparseBuffer.SubstituteNameOffset +
5213  volume_prefix_len * sizeof(WCHAR));
5214  ret = rp->MountPointReparseBuffer.SubstituteNameLength;
5215  *len = ret / sizeof(WCHAR);
5216  ret -= volume_prefix_len * sizeof(WCHAR);
5217  if (ret > sizeof(volume) - 1 * sizeof(WCHAR) &&
5218  memcmp(name, volume, sizeof(volume) - 1 * sizeof(WCHAR)) == 0)
5219  return -1;
5220  }
5221  else {
5222  return -1;
5223  }
5224  *result = name;
5225  if (e) {
5226  if ((char *)name + ret + sizeof(WCHAR) > (char *)rp + bufsize)
5227  return e;
5228  /* SubstituteName is not used */
5229  }
5230  ((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0';
5231  translate_wchar(name, L'\\', L'/');
5232  return 0;
5233  }
5234  else {
5235  return e;
5236  }
5237 }
5238 
5239 /* License: Ruby's */
5240 static ssize_t
5241 w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize)
5242 {
5243  VALUE rp_buf, rp_buf_bigger = 0;
5244  DWORD len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0);
5245  size_t size = rb_w32_reparse_buffer_size(bufsize);
5246  WCHAR *wname;
5247  WCHAR *wpath = ALLOCV(rp_buf, sizeof(WCHAR) * len + size);
5248  rb_w32_reparse_buffer_t *rp = (void *)(wpath + len);
5249  ssize_t ret;
5250  int e;
5251 
5252  MultiByteToWideChar(cp, 0, path, -1, wpath, len);
5253  e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5254  if (e == ERROR_MORE_DATA) {
5255  size = rb_w32_reparse_buffer_size(len + 1);
5256  rp = ALLOCV(rp_buf_bigger, size);
5257  e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5258  }
5259  if (e) {
5260  ALLOCV_END(rp_buf);
5261  ALLOCV_END(rp_buf_bigger);
5262  errno = e == -1 ? EINVAL : map_errno(e);
5263  return -1;
5264  }
5265  len = lstrlenW(wname);
5266  ret = WideCharToMultiByte(cp, 0, wname, len, buf, bufsize, NULL, NULL);
5267  ALLOCV_END(rp_buf);
5268  ALLOCV_END(rp_buf_bigger);
5269  if (!ret) {
5270  ret = bufsize;
5271  }
5272  return ret;
5273 }
5274 
5275 /* License: Ruby's */
5276 ssize_t
5277 rb_w32_ureadlink(const char *path, char *buf, size_t bufsize)
5278 {
5279  return w32_readlink(CP_UTF8, path, buf, bufsize);
5280 }
5281 
5282 /* License: Ruby's */
5283 ssize_t
5284 readlink(const char *path, char *buf, size_t bufsize)
5285 {
5286  return w32_readlink(filecp(), path, buf, bufsize);
5287 }
5288 
5289 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
5290 #define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
5291 #endif
5292 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
5293 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
5294 #endif
5295 
5296 /* License: Ruby's */
5297 static int
5298 w32_symlink(UINT cp, const char *src, const char *link)
5299 {
5300  int atts, len1, len2;
5301  VALUE buf;
5302  WCHAR *wsrc, *wlink;
5303  DWORD flag = 0;
5304  BOOLEAN ret;
5305  int e;
5306 
5307  static DWORD create_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5308 
5309  if (!*link) {
5310  errno = ENOENT;
5311  return -1;
5312  }
5313  if (!*src) {
5314  errno = EINVAL;
5315  return -1;
5316  }
5317  len1 = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
5318  len2 = MultiByteToWideChar(cp, 0, link, -1, NULL, 0);
5319  wsrc = ALLOCV_N(WCHAR, buf, len1+len2);
5320  wlink = wsrc + len1;
5321  MultiByteToWideChar(cp, 0, src, -1, wsrc, len1);
5322  MultiByteToWideChar(cp, 0, link, -1, wlink, len2);
5323  translate_wchar(wsrc, L'/', L'\\');
5324 
5325  atts = GetFileAttributesW(wsrc);
5326  if (atts != -1 && atts & FILE_ATTRIBUTE_DIRECTORY)
5327  flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
5328  ret = CreateSymbolicLinkW(wlink, wsrc, flag |= create_flag);
5329  if (!ret &&
5330  (e = GetLastError()) == ERROR_INVALID_PARAMETER &&
5331  (flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
5332  create_flag = 0;
5333  flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5334  ret = CreateSymbolicLinkW(wlink, wsrc, flag);
5335  if (!ret) e = GetLastError();
5336  }
5337  ALLOCV_END(buf);
5338 
5339  if (!ret) {
5340  errno = map_errno(e);
5341  return -1;
5342  }
5343  return 0;
5344 }
5345 
5346 /* License: Ruby's */
5347 int
5348 rb_w32_usymlink(const char *src, const char *link)
5349 {
5350  return w32_symlink(CP_UTF8, src, link);
5351 }
5352 
5353 /* License: Ruby's */
5354 int
5355 symlink(const char *src, const char *link)
5356 {
5357  return w32_symlink(filecp(), src, link);
5358 }
5359 
5360 /* License: Ruby's */
5361 rb_pid_t
5362 wait(int *status)
5363 {
5364  return waitpid(-1, status, 0);
5365 }
5366 
5367 /* License: Ruby's */
5368 static char *
5369 w32_getenv(const char *name, UINT cp)
5370 {
5371  WCHAR *wenvarea, *wenv;
5372  int len = strlen(name);
5373  char *env, *found = NULL;
5374  int wlen;
5375 
5376  if (len == 0) return NULL;
5377 
5378  if (!NTLoginName) {
5379  /* initialized in init_env, uenvarea_mutex should have been
5380  * initialized before it */
5381  return getenv(name);
5382  }
5383 
5384  thread_exclusive(uenvarea) {
5385  if (uenvarea) {
5386  free(uenvarea);
5387  uenvarea = NULL;
5388  }
5389  wenvarea = GetEnvironmentStringsW();
5390  if (!wenvarea) {
5391  map_errno(GetLastError());
5392  continue;
5393  }
5394  for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
5395  wlen += lstrlenW(wenv) + 1;
5396  uenvarea = wstr_to_mbstr(cp, wenvarea, wlen, NULL);
5397  FreeEnvironmentStringsW(wenvarea);
5398  if (!uenvarea)
5399  continue;
5400 
5401  for (env = uenvarea; *env; env += strlen(env) + 1) {
5402  if (strncasecmp(env, name, len) == 0 && *(env + len) == '=') {
5403  found = env + len + 1;
5404  break;
5405  }
5406  }
5407  }
5408 
5409  return found;
5410 }
5411 
5412 /* License: Ruby's */
5413 char *
5414 rb_w32_ugetenv(const char *name)
5415 {
5416  return w32_getenv(name, CP_UTF8);
5417 }
5418 
5419 /* License: Ruby's */
5420 char *
5421 rb_w32_getenv(const char *name)
5422 {
5423  return w32_getenv(name, CP_ACP);
5424 }
5425 
5426 /* License: Ruby's */
5427 static DWORD
5428 get_attr_vsn(const WCHAR *path, DWORD *atts, DWORD *vsn)
5429 {
5430  BY_HANDLE_FILE_INFORMATION st = {0};
5431  DWORD e = 0;
5432  HANDLE h = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5433 
5434  if (h == INVALID_HANDLE_VALUE) {
5435  e = GetLastError();
5436  ASSUME(e);
5437  return e;
5438  }
5439  if (!GetFileInformationByHandle(h, &st)) {
5440  e = GetLastError();
5441  ASSUME(e);
5442  }
5443  else {
5444  *atts = st.dwFileAttributes;
5445  *vsn = st.dwVolumeSerialNumber;
5446  }
5447  CloseHandle(h);
5448  return e;
5449 }
5450 
5451 /* License: Artistic or GPL */
5452 static int
5453 wrename(const WCHAR *oldpath, const WCHAR *newpath)
5454 {
5455  int res = 0;
5456  DWORD oldatts = 0, newatts = (DWORD)-1;
5457  DWORD oldvsn = 0, newvsn = 0, e;
5458 
5459  e = get_attr_vsn(oldpath, &oldatts, &oldvsn);
5460  if (e) {
5461  errno = map_errno(e);
5462  return -1;
5463  }
5464  if (oldatts & FILE_ATTRIBUTE_REPARSE_POINT) {
5465  HANDLE fh = open_special(oldpath, 0, 0);
5466  if (fh == INVALID_HANDLE_VALUE) {
5467  e = GetLastError();
5468  if (e == ERROR_CANT_RESOLVE_FILENAME) {
5469  errno = ELOOP;
5470  return -1;
5471  }
5472  }
5473  CloseHandle(fh);
5474  }
5475  get_attr_vsn(newpath, &newatts, &newvsn);
5476 
5477  RUBY_CRITICAL {
5478  if (newatts != (DWORD)-1 && newatts & FILE_ATTRIBUTE_READONLY)
5479  SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
5480 
5481  if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
5482  res = -1;
5483 
5484  if (res) {
5485  DWORD e = GetLastError();
5486  if ((e == ERROR_ACCESS_DENIED) && (oldatts & FILE_ATTRIBUTE_DIRECTORY) &&
5487  oldvsn != newvsn)
5488  errno = EXDEV;
5489  else
5490  errno = map_errno(e);
5491  }
5492  else
5493  SetFileAttributesW(newpath, oldatts);
5494  }
5495 
5496  return res;
5497 }
5498 
5499 /* License: Ruby's */
5500 int
5501 rb_w32_urename(const char *from, const char *to)
5502 {
5503  WCHAR *wfrom;
5504  WCHAR *wto;
5505  int ret = -1;
5506 
5507  if (!(wfrom = utf8_to_wstr(from, NULL)))
5508  return -1;
5509  if (!(wto = utf8_to_wstr(to, NULL))) {
5510  free(wfrom);
5511  return -1;
5512  }
5513  ret = wrename(wfrom, wto);
5514  free(wto);
5515  free(wfrom);
5516  return ret;
5517 }
5518 
5519 /* License: Ruby's */
5520 int
5521 rb_w32_rename(const char *from, const char *to)
5522 {
5523  WCHAR *wfrom;
5524  WCHAR *wto;
5525  int ret = -1;
5526 
5527  if (!(wfrom = filecp_to_wstr(from, NULL)))
5528  return -1;
5529  if (!(wto = filecp_to_wstr(to, NULL))) {
5530  free(wfrom);
5531  return -1;
5532  }
5533  ret = wrename(wfrom, wto);
5534  free(wto);
5535  free(wfrom);
5536  return ret;
5537 }
5538 
5539 /* License: Ruby's */
5540 static int
5541 isUNCRoot(const WCHAR *path)
5542 {
5543  if (path[0] == L'\\' && path[1] == L'\\') {
5544  const WCHAR *p = path + 2;
5545  if (p[0] == L'?' && p[1] == L'\\') {
5546  p += 2;
5547  }
5548  for (; *p; p++) {
5549  if (*p == L'\\')
5550  break;
5551  }
5552  if (p[0] && p[1]) {
5553  for (p++; *p; p++) {
5554  if (*p == L'\\')
5555  break;
5556  }
5557  if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
5558  return 1;
5559  }
5560  }
5561  return 0;
5562 }
5563 
5564 #define COPY_STAT(src, dest, size_cast) do { \
5565  (dest).st_dev = (src).st_dev; \
5566  (dest).st_ino = (src).st_ino; \
5567  (dest).st_mode = (src).st_mode; \
5568  (dest).st_nlink = (src).st_nlink; \
5569  (dest).st_uid = (src).st_uid; \
5570  (dest).st_gid = (src).st_gid; \
5571  (dest).st_rdev = (src).st_rdev; \
5572  (dest).st_size = size_cast(src).st_size; \
5573  (dest).st_atime = (src).st_atime; \
5574  (dest).st_mtime = (src).st_mtime; \
5575  (dest).st_ctime = (src).st_ctime; \
5576  } while (0)
5577 
5578 static time_t filetime_to_unixtime(const FILETIME *ft);
5579 static long filetime_to_nsec(const FILETIME *ft);
5580 static WCHAR *name_for_stat(WCHAR *buf, const WCHAR *path);
5581 static DWORD stati128_handle(HANDLE h, struct stati128 *st);
5582 
5583 #undef fstat
5584 /* License: Ruby's */
5585 int
5586 rb_w32_fstat(int fd, struct stat *st)
5587 {
5588  BY_HANDLE_FILE_INFORMATION info;
5589  int ret = fstat(fd, st);
5590 
5591  if (ret) return ret;
5592  if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) return ret;
5593  if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
5594  st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5595  st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5596  st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5597  }
5598  return ret;
5599 }
5600 
5601 /* License: Ruby's */
5602 int
5603 rb_w32_fstati128(int fd, struct stati128 *st)
5604 {
5605  struct stat tmp;
5606  int ret = fstat(fd, &tmp);
5607 
5608  if (ret) return ret;
5609  COPY_STAT(tmp, *st, +);
5610  stati128_handle((HANDLE)_get_osfhandle(fd), st);
5611  return ret;
5612 }
5613 
5614 #if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5615 typedef struct {
5616  BYTE Identifier[16];
5617 } FILE_ID_128;
5618 #endif
5619 
5620 #if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5621 #define FileIdInfo 0x12
5622 
5623 typedef struct {
5624  unsigned LONG_LONG VolumeSerialNumber;
5625  FILE_ID_128 FileId;
5626 } FILE_ID_INFO;
5627 #endif
5628 
5629 static BOOL
5630 get_ino(HANDLE h, FILE_ID_INFO *id)
5631 {
5632  return GetFileInformationByHandleEx(h, FileIdInfo, id, sizeof(*id));
5633 }
5634 
5635 /* License: Ruby's */
5636 static DWORD
5637 stati128_handle(HANDLE h, struct stati128 *st)
5638 {
5639  BY_HANDLE_FILE_INFORMATION info;
5640  DWORD attr = (DWORD)-1;
5641 
5642  if (GetFileInformationByHandle(h, &info)) {
5643  FILE_ID_INFO fii;
5644  st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
5645  st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5646  st->st_atimensec = filetime_to_nsec(&info.ftLastAccessTime);
5647  st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5648  st->st_mtimensec = filetime_to_nsec(&info.ftLastWriteTime);
5649  st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5650  st->st_ctimensec = filetime_to_nsec(&info.ftCreationTime);
5651  st->st_nlink = info.nNumberOfLinks;
5652  attr = info.dwFileAttributes;
5653  if (get_ino(h, &fii)) {
5654  st->st_ino = *((unsigned __int64 *)&fii.FileId);
5655  st->st_inohigh = *((__int64 *)&fii.FileId + 1);
5656  }
5657  else {
5658  st->st_ino = ((__int64)info.nFileIndexHigh << 32) | info.nFileIndexLow;
5659  st->st_inohigh = 0;
5660  }
5661  }
5662  return attr;
5663 }
5664 
5665 /* License: Ruby's */
5666 static time_t
5667 filetime_to_unixtime(const FILETIME *ft)
5668 {
5669  long subsec;
5670  time_t t = filetime_split(ft, &subsec);
5671 
5672  if (t < 0) return 0;
5673  return t;
5674 }
5675 
5676 /* License: Ruby's */
5677 static long
5678 filetime_to_nsec(const FILETIME *ft)
5679 {
5680  ULARGE_INTEGER tmp;
5681  tmp.LowPart = ft->dwLowDateTime;
5682  tmp.HighPart = ft->dwHighDateTime;
5683  return (long)(tmp.QuadPart % 10000000) * 100;
5684 }
5685 
5686 /* License: Ruby's */
5687 static unsigned
5688 fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode)
5689 {
5690  if (attr & FILE_ATTRIBUTE_READONLY) {
5691  mode |= S_IREAD;
5692  }
5693  else {
5694  mode |= S_IREAD | S_IWRITE | S_IWUSR;
5695  }
5696 
5697  if (mode & S_IFMT) {
5698  /* format is already set */
5699  }
5700  else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5701  /* Only used by stat_by_find in the case the file can not be opened.
5702  * In this case we can't get more details. */
5703  }
5704  else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5705  mode |= S_IFDIR | S_IEXEC;
5706  }
5707  else {
5708  mode |= S_IFREG;
5709  }
5710 
5711  if (path && (mode & S_IFREG)) {
5712  const WCHAR *end = path + lstrlenW(path);
5713  while (path < end) {
5714  end = CharPrevW(path, end);
5715  if (*end == L'.') {
5716  if ((_wcsicmp(end, L".bat") == 0) ||
5717  (_wcsicmp(end, L".cmd") == 0) ||
5718  (_wcsicmp(end, L".com") == 0) ||
5719  (_wcsicmp(end, L".exe") == 0)) {
5720  mode |= S_IEXEC;
5721  }
5722  break;
5723  }
5724  if (!iswalnum(*end)) break;
5725  }
5726  }
5727 
5728  mode |= (mode & 0500) >> 3;
5729  mode |= (mode & 0500) >> 6;
5730 
5731  return mode;
5732 }
5733 
5734 /* License: Ruby's */
5735 static int
5736 check_valid_dir(const WCHAR *path)
5737 {
5738  WIN32_FIND_DATAW fd;
5739  HANDLE fh;
5740  WCHAR full[PATH_MAX];
5741  WCHAR *dmy;
5742  WCHAR *p, *q;
5743 
5744  /* GetFileAttributes() determines "..." as directory. */
5745  /* We recheck it by FindFirstFile(). */
5746  if (!(p = wcsstr(path, L"...")))
5747  return 0;
5748  q = p + wcsspn(p, L".");
5749  if ((p == path || wcschr(L":/\\", *(p - 1))) &&
5750  (!*q || wcschr(L":/\\", *q))) {
5751  errno = ENOENT;
5752  return -1;
5753  }
5754 
5755  /* if the specified path is the root of a drive and the drive is empty, */
5756  /* FindFirstFile() returns INVALID_HANDLE_VALUE. */
5757  if (!GetFullPathNameW(path, sizeof(full) / sizeof(WCHAR), full, &dmy)) {
5758  errno = map_errno(GetLastError());
5759  return -1;
5760  }
5761  if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5762  return 0;
5763 
5764  fh = open_dir_handle(path, &fd);
5765  if (fh == INVALID_HANDLE_VALUE)
5766  return -1;
5767  FindClose(fh);
5768  return 0;
5769 }
5770 
5771 /* License: Ruby's */
5772 static int
5773 stat_by_find(const WCHAR *path, struct stati128 *st)
5774 {
5775  HANDLE h;
5776  WIN32_FIND_DATAW wfd;
5777 
5778  /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
5779  h = FindFirstFileW(path, &wfd);
5780  if (h == INVALID_HANDLE_VALUE) {
5781  errno = map_errno(GetLastError());
5782  return -1;
5783  }
5784  FindClose(h);
5785  st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path, 0);
5786  st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
5787  st->st_atimensec = filetime_to_nsec(&wfd.ftLastAccessTime);
5788  st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
5789  st->st_mtimensec = filetime_to_nsec(&wfd.ftLastWriteTime);
5790  st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
5791  st->st_ctimensec = filetime_to_nsec(&wfd.ftCreationTime);
5792  st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
5793  st->st_nlink = 1;
5794  return 0;
5795 }
5796 
5797 /* License: Ruby's */
5798 static int
5799 path_drive(const WCHAR *path)
5800 {
5801  return (iswalpha(path[0]) && path[1] == L':') ?
5802  towupper(path[0]) - L'A' : _getdrive() - 1;
5803 }
5804 
5805 /* License: Ruby's */
5806 static int
5807 winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
5808 {
5809  DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
5810  HANDLE f;
5811  WCHAR finalname[PATH_MAX];
5812  int open_error;
5813 
5814  memset(st, 0, sizeof(*st));
5815  f = open_special(path, 0, flags);
5816  open_error = GetLastError();
5817  if (f == INVALID_HANDLE_VALUE && !lstat) {
5818  /* Support stat (not only lstat) of UNIXSocket */
5819  FILE_ATTRIBUTE_TAG_INFO attr_info;
5820  DWORD e;
5821 
5822  f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5823  e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5824  &attr_info, sizeof(attr_info));
5825  if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
5826  CloseHandle(f);
5827  f = INVALID_HANDLE_VALUE;
5828  }
5829  }
5830  if (f != INVALID_HANDLE_VALUE) {
5831  DWORD attr = stati128_handle(f, st);
5832  const DWORD len = GetFinalPathNameByHandleW(f, finalname, numberof(finalname), 0);
5833  unsigned mode = 0;
5834  switch (GetFileType(f)) {
5835  case FILE_TYPE_CHAR:
5836  mode = S_IFCHR;
5837  break;
5838  case FILE_TYPE_PIPE:
5839  mode = S_IFIFO;
5840  break;
5841  default:
5842  if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5843  FILE_ATTRIBUTE_TAG_INFO attr_info;
5844  DWORD e;
5845 
5846  e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5847  &attr_info, sizeof(attr_info));
5848  if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) {
5849  st->st_size = 0;
5850  mode |= S_IFSOCK;
5851  }
5852  else if (rb_w32_reparse_symlink_p(path)) {
5853  /* TODO: size in which encoding? */
5854  st->st_size = 0;
5855  mode |= S_IFLNK | S_IEXEC;
5856  }
5857  else {
5858  mode |= S_IFDIR | S_IEXEC;
5859  }
5860  }
5861  }
5862  CloseHandle(f);
5863  if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5864  if (check_valid_dir(path)) return -1;
5865  }
5866  st->st_mode = fileattr_to_unixmode(attr, path, mode);
5867  if (len) {
5868  finalname[min(len, numberof(finalname)-1)] = L'\0';
5869  path = finalname;
5870  if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
5871  path += numberof(namespace_prefix);
5872  }
5873  }
5874  else {
5875  if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME)
5876  || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) {
5877  errno = map_errno(open_error);
5878  return -1;
5879  }
5880 
5881  if (stat_by_find(path, st)) return -1;
5882  }
5883 
5884  st->st_dev = st->st_rdev = path_drive(path);
5885 
5886  return 0;
5887 }
5888 
5889 /* License: Ruby's */
5890 int
5891 rb_w32_stat(const char *path, struct stat *st)
5892 {
5893  struct stati128 tmp;
5894 
5895  if (w32_stati128(path, &tmp, filecp(), FALSE)) return -1;
5896  COPY_STAT(tmp, *st, (_off_t));
5897  return 0;
5898 }
5899 
5900 /* License: Ruby's */
5901 static int
5902 wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat)
5903 {
5904  WCHAR *buf1;
5905  int ret, size;
5906  VALUE v;
5907 
5908  if (!path || !st) {
5909  errno = EFAULT;
5910  return -1;
5911  }
5912  size = lstrlenW(path) + 2;
5913  buf1 = ALLOCV_N(WCHAR, v, size);
5914  if (!(path = name_for_stat(buf1, path)))
5915  return -1;
5916  ret = winnt_stat(path, st, lstat);
5917  if (v)
5918  ALLOCV_END(v);
5919 
5920  return ret;
5921 }
5922 
5923 /* License: Ruby's */
5924 static WCHAR *
5925 name_for_stat(WCHAR *buf1, const WCHAR *path)
5926 {
5927  const WCHAR *p;
5928  WCHAR *s, *end;
5929  int len;
5930 
5931  for (p = path, s = buf1; *p; p++, s++) {
5932  if (*p == L'/')
5933  *s = L'\\';
5934  else
5935  *s = *p;
5936  }
5937  *s = '\0';
5938  len = s - buf1;
5939  if (!len || L'\"' == *(--s)) {
5940  errno = ENOENT;
5941  return NULL;
5942  }
5943  end = buf1 + len - 1;
5944 
5945  if (isUNCRoot(buf1)) {
5946  if (*end == L'.')
5947  *end = L'\0';
5948  else if (*end != L'\\')
5949  lstrcatW(buf1, L"\\");
5950  }
5951  else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
5952  lstrcatW(buf1, L".");
5953 
5954  return buf1;
5955 }
5956 
5957 /* License: Ruby's */
5958 int
5959 rb_w32_ustati128(const char *path, struct stati128 *st)
5960 {
5961  return w32_stati128(path, st, CP_UTF8, FALSE);
5962 }
5963 
5964 /* License: Ruby's */
5965 int
5966 rb_w32_stati128(const char *path, struct stati128 *st)
5967 {
5968  return w32_stati128(path, st, filecp(), FALSE);
5969 }
5970 
5971 /* License: Ruby's */
5972 static int
5973 w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat)
5974 {
5975  WCHAR *wpath;
5976  int ret;
5977 
5978  if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
5979  return -1;
5980  ret = wstati128(wpath, st, lstat);
5981  free(wpath);
5982  return ret;
5983 }
5984 
5985 /* License: Ruby's */
5986 int
5987 rb_w32_ulstati128(const char *path, struct stati128 *st)
5988 {
5989  return w32_stati128(path, st, CP_UTF8, TRUE);
5990 }
5991 
5992 /* License: Ruby's */
5993 int
5994 rb_w32_lstati128(const char *path, struct stati128 *st)
5995 {
5996  return w32_stati128(path, st, filecp(), TRUE);
5997 }
5998 
5999 /* License: Ruby's */
6000 rb_off_t
6001 rb_w32_lseek(int fd, rb_off_t ofs, int whence)
6002 {
6003  SOCKET sock = TO_SOCKET(fd);
6004  if (is_socket(sock) || is_pipe(sock)) {
6005  errno = ESPIPE;
6006  return -1;
6007  }
6008  return _lseeki64(fd, ofs, whence);
6009 }
6010 
6011 /* License: Ruby's */
6012 static int
6013 w32_access(const char *path, int mode, UINT cp)
6014 {
6015  struct stati128 stat;
6016  if (w32_stati128(path, &stat, cp, FALSE) != 0)
6017  return -1;
6018  mode <<= 6;
6019  if ((stat.st_mode & mode) != mode) {
6020  errno = EACCES;
6021  return -1;
6022  }
6023  return 0;
6024 }
6025 
6026 /* License: Ruby's */
6027 int
6028 rb_w32_access(const char *path, int mode)
6029 {
6030  return w32_access(path, mode, filecp());
6031 }
6032 
6033 /* License: Ruby's */
6034 int
6035 rb_w32_uaccess(const char *path, int mode)
6036 {
6037  return w32_access(path, mode, CP_UTF8);
6038 }
6039 
6040 /* License: Ruby's */
6041 static int
6042 rb_chsize(HANDLE h, rb_off_t size)
6043 {
6044  long upos, lpos, usize, lsize;
6045  int ret = -1;
6046  DWORD e;
6047 
6048  if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6049  (e = GetLastError())) {
6050  errno = map_errno(e);
6051  return -1;
6052  }
6053  usize = (long)(size >> 32);
6054  lsize = (long)size;
6055  if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
6056  (e = GetLastError())) {
6057  errno = map_errno(e);
6058  }
6059  else if (!SetEndOfFile(h)) {
6060  errno = map_errno(GetLastError());
6061  }
6062  else {
6063  ret = 0;
6064  }
6065  SetFilePointer(h, lpos, &upos, SEEK_SET);
6066  return ret;
6067 }
6068 
6069 /* License: Ruby's */
6070 static int
6071 w32_truncate(const char *path, rb_off_t length, UINT cp)
6072 {
6073  HANDLE h;
6074  int ret;
6075  WCHAR *wpath;
6076 
6077  if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6078  return -1;
6079  h = CreateFileW(wpath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
6080  if (h == INVALID_HANDLE_VALUE) {
6081  errno = map_errno(GetLastError());
6082  free(wpath);
6083  return -1;
6084  }
6085  free(wpath);
6086  ret = rb_chsize(h, length);
6087  CloseHandle(h);
6088  return ret;
6089 }
6090 
6091 /* License: Ruby's */
6092 int
6093 rb_w32_utruncate(const char *path, rb_off_t length)
6094 {
6095  return w32_truncate(path, length, CP_UTF8);
6096 }
6097 
6098 /* License: Ruby's */
6099 int
6100 rb_w32_truncate(const char *path, rb_off_t length)
6101 {
6102  return w32_truncate(path, length, filecp());
6103 }
6104 
6105 /* License: Ruby's */
6106 int
6107 rb_w32_ftruncate(int fd, rb_off_t length)
6108 {
6109  HANDLE h;
6110 
6111  h = (HANDLE)_get_osfhandle(fd);
6112  if (h == (HANDLE)-1) return -1;
6113  return rb_chsize(h, length);
6114 }
6115 
6116 /* License: Ruby's */
6117 static long
6118 filetime_to_clock(FILETIME *ft)
6119 {
6120  __int64 qw = ft->dwHighDateTime;
6121  qw <<= 32;
6122  qw |= ft->dwLowDateTime;
6123  qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
6124  return (long) qw;
6125 }
6126 
6127 /* License: Ruby's */
6128 int
6129 rb_w32_times(struct tms *tmbuf)
6130 {
6131  FILETIME create, exit, kernel, user;
6132 
6133  if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
6134  tmbuf->tms_utime = filetime_to_clock(&user);
6135  tmbuf->tms_stime = filetime_to_clock(&kernel);
6136  tmbuf->tms_cutime = 0;
6137  tmbuf->tms_cstime = 0;
6138  }
6139  else {
6140  tmbuf->tms_utime = clock();
6141  tmbuf->tms_stime = 0;
6142  tmbuf->tms_cutime = 0;
6143  tmbuf->tms_cstime = 0;
6144  }
6145  return 0;
6146 }
6147 
6148 
6149 /* License: Ruby's */
6150 #define yield_once() Sleep(0)
6151 #define yield_until(condition) do yield_once(); while (!(condition))
6152 
6153 /* License: Ruby's */
6155  /* output field */
6156  void* stackaddr;
6157  int errnum;
6158 
6159  /* input field */
6160  uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
6161  uintptr_t self;
6162  int argc;
6163  uintptr_t* argv;
6164 };
6165 
6166 /* License: Ruby's */
6167 static DWORD WINAPI
6168 call_asynchronous(PVOID argp)
6169 {
6170  DWORD ret;
6171  struct asynchronous_arg_t *arg = argp;
6172  arg->stackaddr = &argp;
6173  ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
6174  arg->errnum = errno;
6175  return ret;
6176 }
6177 
6178 /* License: Ruby's */
6179 uintptr_t
6180 rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
6181  int argc, uintptr_t* argv, uintptr_t intrval)
6182 {
6183  DWORD val;
6184  BOOL interrupted = FALSE;
6185  HANDLE thr;
6186 
6187  RUBY_CRITICAL {
6188  struct asynchronous_arg_t arg;
6189 
6190  arg.stackaddr = NULL;
6191  arg.errnum = 0;
6192  arg.func = func;
6193  arg.self = self;
6194  arg.argc = argc;
6195  arg.argv = argv;
6196 
6197  thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6198 
6199  if (thr) {
6200  yield_until(arg.stackaddr);
6201 
6202  if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6203  interrupted = TRUE;
6204 
6205  if (TerminateThread(thr, intrval)) {
6206  yield_once();
6207  }
6208  }
6209 
6210  GetExitCodeThread(thr, &val);
6211  CloseHandle(thr);
6212 
6213  if (interrupted) {
6214  /* must release stack of killed thread, why doesn't Windows? */
6215  MEMORY_BASIC_INFORMATION m;
6216 
6217  memset(&m, 0, sizeof(m));
6218  if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
6219  Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
6220  arg.stackaddr, GetLastError()));
6221  }
6222  else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6223  Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
6224  m.AllocationBase, GetLastError()));
6225  }
6226  errno = EINTR;
6227  }
6228  else {
6229  errno = arg.errnum;
6230  }
6231  }
6232  }
6233 
6234  if (!thr) {
6235  rb_fatal("failed to launch waiter thread:%ld", GetLastError());
6236  }
6237 
6238  return val;
6239 }
6240 
6241 /* License: Ruby's */
6242 char **
6243 rb_w32_get_environ(void)
6244 {
6245  WCHAR *envtop, *env;
6246  char **myenvtop, **myenv;
6247  int num;
6248 
6249  /*
6250  * We avoid values started with `='. If you want to deal those values,
6251  * change this function, and some functions in hash.c which recognize
6252  * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
6253  * CygWin deals these values by changing first `=' to '!'. But we don't
6254  * use such trick and follow cmd.exe's way that just doesn't show these
6255  * values.
6256  *
6257  * This function returns UTF-8 strings.
6258  */
6259  envtop = GetEnvironmentStringsW();
6260  for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6261  if (*env != '=') num++;
6262 
6263  myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
6264  for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
6265  if (*env != '=') {
6266  if (!(*myenv = wstr_to_utf8(env, NULL))) {
6267  break;
6268  }
6269  myenv++;
6270  }
6271  }
6272  *myenv = NULL;
6273  FreeEnvironmentStringsW(envtop);
6274 
6275  return myenvtop;
6276 }
6277 
6278 /* License: Ruby's */
6279 void
6280 rb_w32_free_environ(char **env)
6281 {
6282  char **t = env;
6283 
6284  while (*t) free(*t++);
6285  free(env);
6286 }
6287 
6288 /* License: Ruby's */
6289 rb_pid_t
6290 rb_w32_getpid(void)
6291 {
6292  return GetCurrentProcessId();
6293 }
6294 
6295 
6296 /* License: Ruby's */
6297 rb_pid_t
6298 rb_w32_getppid(void)
6299 {
6300  typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
6301  static query_func *pNtQueryInformationProcess = (query_func *)-1;
6302  rb_pid_t ppid = 0;
6303 
6304  if (pNtQueryInformationProcess == (query_func *)-1)
6305  pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
6306  if (pNtQueryInformationProcess) {
6307  struct {
6308  long ExitStatus;
6309  void* PebBaseAddress;
6310  uintptr_t AffinityMask;
6311  uintptr_t BasePriority;
6312  uintptr_t UniqueProcessId;
6313  uintptr_t ParentProcessId;
6314  } pbi;
6315  ULONG len;
6316  long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
6317  if (!ret) {
6318  ppid = pbi.ParentProcessId;
6319  }
6320  }
6321 
6322  return ppid;
6323 }
6324 
6325 STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
6326 
6327 /* License: Ruby's */
6328 #define set_new_std_handle(newfd, handle) do { \
6329  if ((unsigned)(newfd) > 2) break; \
6330  SetStdHandle(STD_INPUT_HANDLE+(STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)*(newfd), \
6331  (handle)); \
6332  } while (0)
6333 #define set_new_std_fd(newfd) set_new_std_handle(newfd, (HANDLE)rb_w32_get_osfhandle(newfd))
6334 
6335 /* License: Ruby's */
6336 int
6337 rb_w32_dup2(int oldfd, int newfd)
6338 {
6339  int ret;
6340 
6341  if (oldfd == newfd) return newfd;
6342  ret = dup2(oldfd, newfd);
6343  if (ret < 0) return ret;
6344  set_new_std_fd(newfd);
6345  return newfd;
6346 }
6347 
6348 /* License: Ruby's */
6349 int
6350 rb_w32_uopen(const char *file, int oflag, ...)
6351 {
6352  WCHAR *wfile;
6353  int ret;
6354  int pmode;
6355 
6356  va_list arg;
6357  va_start(arg, oflag);
6358  pmode = va_arg(arg, int);
6359  va_end(arg);
6360 
6361  if (!(wfile = utf8_to_wstr(file, NULL)))
6362  return -1;
6363  ret = w32_wopen(wfile, oflag, pmode);
6364  free(wfile);
6365  return ret;
6366 }
6367 
6368 /* License: Ruby's */
6369 static int
6370 check_if_wdir(const WCHAR *wfile)
6371 {
6372  DWORD attr = GetFileAttributesW(wfile);
6373  if (attr == (DWORD)-1L ||
6374  !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6375  check_valid_dir(wfile)) {
6376  return FALSE;
6377  }
6378  errno = EISDIR;
6379  return TRUE;
6380 }
6381 
6382 /* License: Ruby's */
6383 int
6384 rb_w32_open(const char *file, int oflag, ...)
6385 {
6386  WCHAR *wfile;
6387  int ret;
6388  int pmode;
6389 
6390  va_list arg;
6391  va_start(arg, oflag);
6392  pmode = va_arg(arg, int);
6393  va_end(arg);
6394 
6395  if (!(wfile = filecp_to_wstr(file, NULL)))
6396  return -1;
6397  ret = w32_wopen(wfile, oflag, pmode);
6398  free(wfile);
6399  return ret;
6400 }
6401 
6402 /* License: Ruby's */
6403 int
6404 rb_w32_wopen(const WCHAR *file, int oflag, ...)
6405 {
6406  int pmode = 0;
6407 
6408  if (oflag & O_CREAT) {
6409  va_list arg;
6410  va_start(arg, oflag);
6411  pmode = va_arg(arg, int);
6412  va_end(arg);
6413  }
6414 
6415  return w32_wopen(file, oflag, pmode);
6416 }
6417 
6418 static int
6419 w32_wopen(const WCHAR *file, int oflag, int pmode)
6420 {
6421  char flags = 0;
6422  int fd;
6423  DWORD access;
6424  DWORD create;
6425  DWORD attr = FILE_ATTRIBUTE_NORMAL;
6426  SECURITY_ATTRIBUTES sec;
6427  HANDLE h;
6428  int share_delete;
6429 
6430  share_delete = oflag & O_SHARE_DELETE ? FILE_SHARE_DELETE : 0;
6431  oflag &= ~O_SHARE_DELETE;
6432  if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
6433  fd = _wopen(file, oflag, pmode);
6434  if (fd == -1) {
6435  switch (errno) {
6436  case EACCES:
6437  check_if_wdir(file);
6438  break;
6439  case EINVAL:
6440  errno = map_errno(GetLastError());
6441  break;
6442  }
6443  }
6444  return fd;
6445  }
6446 
6447  sec.nLength = sizeof(sec);
6448  sec.lpSecurityDescriptor = NULL;
6449  if (oflag & O_NOINHERIT) {
6450  sec.bInheritHandle = FALSE;
6451  flags |= FNOINHERIT;
6452  }
6453  else {
6454  sec.bInheritHandle = TRUE;
6455  }
6456  oflag &= ~O_NOINHERIT;
6457 
6458  /* always open with binary mode */
6459  oflag &= ~(O_BINARY | O_TEXT);
6460 
6461  switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
6462  case O_RDWR:
6463  access = GENERIC_READ | GENERIC_WRITE;
6464  break;
6465  case O_RDONLY:
6466  access = GENERIC_READ;
6467  break;
6468  case O_WRONLY:
6469  access = GENERIC_WRITE;
6470  break;
6471  default:
6472  errno = EINVAL;
6473  return -1;
6474  }
6475  oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
6476 
6477  switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
6478  case O_CREAT:
6479  create = OPEN_ALWAYS;
6480  break;
6481  case 0:
6482  case O_EXCL:
6483  create = OPEN_EXISTING;
6484  break;
6485  case O_CREAT | O_EXCL:
6486  case O_CREAT | O_EXCL | O_TRUNC:
6487  create = CREATE_NEW;
6488  break;
6489  case O_TRUNC:
6490  case O_TRUNC | O_EXCL:
6491  create = TRUNCATE_EXISTING;
6492  break;
6493  case O_CREAT | O_TRUNC:
6494  create = CREATE_ALWAYS;
6495  break;
6496  default:
6497  errno = EINVAL;
6498  return -1;
6499  }
6500  if (oflag & O_CREAT) {
6501  /* TODO: we need to check umask here, but it's not exported... */
6502  if (!(pmode & S_IWRITE))
6503  attr = FILE_ATTRIBUTE_READONLY;
6504  }
6505  oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6506 
6507  if (oflag & O_TEMPORARY) {
6508  attr |= FILE_FLAG_DELETE_ON_CLOSE;
6509  access |= DELETE;
6510  }
6511  oflag &= ~O_TEMPORARY;
6512 
6513  if (oflag & _O_SHORT_LIVED)
6514  attr |= FILE_ATTRIBUTE_TEMPORARY;
6515  oflag &= ~_O_SHORT_LIVED;
6516 
6517  switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
6518  case 0:
6519  break;
6520  case O_SEQUENTIAL:
6521  attr |= FILE_FLAG_SEQUENTIAL_SCAN;
6522  break;
6523  case O_RANDOM:
6524  attr |= FILE_FLAG_RANDOM_ACCESS;
6525  break;
6526  default:
6527  errno = EINVAL;
6528  return -1;
6529  }
6530  oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6531 
6532  if (oflag & ~O_APPEND) {
6533  errno = EINVAL;
6534  return -1;
6535  }
6536 
6537  /* allocate a C Runtime file handle */
6538  RUBY_CRITICAL {
6539  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6540  fd = _open_osfhandle((intptr_t)h, 0);
6541  CloseHandle(h);
6542  }
6543  if (fd == -1) {
6544  errno = EMFILE;
6545  return -1;
6546  }
6547  RUBY_CRITICAL {
6548  rb_acrt_lowio_lock_fh(fd);
6549  _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6550  _set_osflags(fd, 0);
6551 
6552  h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE | share_delete, &sec, create, attr, NULL);
6553  if (h == INVALID_HANDLE_VALUE) {
6554  DWORD e = GetLastError();
6555  if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
6556  errno = map_errno(e);
6557  rb_acrt_lowio_unlock_fh(fd);
6558  fd = -1;
6559  goto quit;
6560  }
6561 
6562  switch (GetFileType(h)) {
6563  case FILE_TYPE_CHAR:
6564  flags |= FDEV;
6565  break;
6566  case FILE_TYPE_PIPE:
6567  flags |= FPIPE;
6568  break;
6569  case FILE_TYPE_UNKNOWN:
6570  errno = map_errno(GetLastError());
6571  CloseHandle(h);
6572  rb_acrt_lowio_unlock_fh(fd);
6573  fd = -1;
6574  goto quit;
6575  }
6576  if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6577  flags |= FAPPEND;
6578 
6579  _set_osfhnd(fd, (intptr_t)h);
6580  _set_osflags(fd, flags | FOPEN);
6581 
6582  rb_acrt_lowio_unlock_fh(fd);
6583  quit:
6584  ;
6585  }
6586 
6587  return fd;
6588 }
6589 
6590 /* License: Ruby's */
6591 int
6592 rb_w32_fclose(FILE *fp)
6593 {
6594  int fd = fileno(fp);
6595  SOCKET sock = TO_SOCKET(fd);
6596  int save_errno = errno;
6597 
6598  if (fflush(fp)) return -1;
6599  if (!is_socket(sock)) {
6600  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6601  return fclose(fp);
6602  }
6603  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6604  fclose(fp);
6605  errno = save_errno;
6606  if (closesocket(sock) == SOCKET_ERROR) {
6607  errno = map_errno(WSAGetLastError());
6608  return -1;
6609  }
6610  return 0;
6611 }
6612 
6613 /* License: Ruby's */
6614 int
6615 rb_w32_pipe(int fds[2])
6616 {
6617  static long serial = 0;
6618  static const char prefix[] = "\\\\.\\pipe\\ruby";
6619  enum {
6620  width_of_prefix = (int)sizeof(prefix) - 1,
6621  width_of_pid = (int)sizeof(rb_pid_t) * 2,
6622  width_of_serial = (int)sizeof(serial) * 2,
6623  width_of_ids = width_of_pid + 1 + width_of_serial + 1
6624  };
6625  char name[sizeof(prefix) + width_of_ids];
6626  SECURITY_ATTRIBUTES sec;
6627  HANDLE hRead, hWrite, h;
6628  int fdRead, fdWrite;
6629  int ret;
6630 
6631  memcpy(name, prefix, width_of_prefix);
6632  snprintf(name + width_of_prefix, width_of_ids, "%.*"PRI_PIDT_PREFIX"x-%.*lx",
6633  width_of_pid, rb_w32_getpid(), width_of_serial, InterlockedIncrement(&serial)-1);
6634 
6635  sec.nLength = sizeof(sec);
6636  sec.lpSecurityDescriptor = NULL;
6637  sec.bInheritHandle = FALSE;
6638 
6639  RUBY_CRITICAL {
6640  hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6641  0, 2, 65536, 65536, 0, &sec);
6642  }
6643  if (hRead == INVALID_HANDLE_VALUE) {
6644  DWORD err = GetLastError();
6645  if (err == ERROR_PIPE_BUSY)
6646  errno = EMFILE;
6647  else
6648  errno = map_errno(GetLastError());
6649  return -1;
6650  }
6651 
6652  RUBY_CRITICAL {
6653  hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6654  OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6655  }
6656  if (hWrite == INVALID_HANDLE_VALUE) {
6657  errno = map_errno(GetLastError());
6658  CloseHandle(hRead);
6659  return -1;
6660  }
6661 
6662  RUBY_CRITICAL do {
6663  ret = 0;
6664  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6665  fdRead = _open_osfhandle((intptr_t)h, 0);
6666  CloseHandle(h);
6667  if (fdRead == -1) {
6668  errno = EMFILE;
6669  CloseHandle(hWrite);
6670  CloseHandle(hRead);
6671  ret = -1;
6672  break;
6673  }
6674 
6675  rb_acrt_lowio_lock_fh(fdRead);
6676  _set_osfhnd(fdRead, (intptr_t)hRead);
6677  _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
6678  rb_acrt_lowio_unlock_fh(fdRead);
6679  } while (0);
6680  if (ret)
6681  return ret;
6682 
6683  RUBY_CRITICAL do {
6684  h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6685  fdWrite = _open_osfhandle((intptr_t)h, 0);
6686  CloseHandle(h);
6687  if (fdWrite == -1) {
6688  errno = EMFILE;
6689  CloseHandle(hWrite);
6690  ret = -1;
6691  break;
6692  }
6693  rb_acrt_lowio_lock_fh(fdWrite);
6694  _set_osfhnd(fdWrite, (intptr_t)hWrite);
6695  _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
6696  rb_acrt_lowio_unlock_fh(fdWrite);
6697  } while (0);
6698  if (ret) {
6699  rb_w32_close(fdRead);
6700  return ret;
6701  }
6702 
6703  fds[0] = fdRead;
6704  fds[1] = fdWrite;
6705 
6706  return 0;
6707 }
6708 
6709 /* License: Ruby's */
6710 static int
6711 console_emulator_p(void)
6712 {
6713 #ifdef _WIN32_WCE
6714  return FALSE;
6715 #else
6716  const void *const func = WriteConsoleW;
6717  HMODULE k;
6718  MEMORY_BASIC_INFORMATION m;
6719 
6720  memset(&m, 0, sizeof(m));
6721  if (!VirtualQuery(func, &m, sizeof(m))) {
6722  return FALSE;
6723  }
6724  k = GetModuleHandle("kernel32.dll");
6725  if (!k) return FALSE;
6726  return (HMODULE)m.AllocationBase != k;
6727 #endif
6728 }
6729 
6730 /* License: Ruby's */
6731 static struct constat *
6732 constat_handle(HANDLE h)
6733 {
6734  st_data_t data;
6735  struct constat *p = NULL;
6736  thread_exclusive(conlist) {
6737  if (!conlist) {
6738  if (console_emulator_p()) {
6739  conlist = conlist_disabled;
6740  continue;
6741  }
6742  conlist = st_init_numtable();
6743  install_vm_exit_handler();
6744  }
6745  else if (conlist == conlist_disabled) {
6746  continue;
6747  }
6748  if (st_lookup(conlist, (st_data_t)h, &data)) {
6749  p = (struct constat *)data;
6750  }
6751  else {
6752  CONSOLE_SCREEN_BUFFER_INFO csbi;
6753  p = ALLOC(struct constat);
6754  p->vt100.state = constat_init;
6755  p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6756  p->vt100.reverse = 0;
6757  p->vt100.saved.X = p->vt100.saved.Y = 0;
6758  if (GetConsoleScreenBufferInfo(h, &csbi)) {
6759  p->vt100.attr = csbi.wAttributes;
6760  }
6761  st_insert(conlist, (st_data_t)h, (st_data_t)p);
6762  }
6763  }
6764  return p;
6765 }
6766 
6767 /* License: Ruby's */
6768 static void
6769 constat_reset(HANDLE h)
6770 {
6771  st_data_t data;
6772  struct constat *p;
6773  thread_exclusive(conlist) {
6774  if (!conlist || conlist == conlist_disabled) continue;
6775  if (!st_lookup(conlist, (st_data_t)h, &data)) continue;
6776  p = (struct constat *)data;
6777  p->vt100.state = constat_init;
6778  }
6779 }
6780 
6781 #define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6782 #define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6783 
6784 #define constat_attr_color_reverse(attr) \
6785  ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6786  (((attr) & FOREGROUND_MASK) << 4) | \
6787  (((attr) & BACKGROUND_MASK) >> 4)
6788 
6789 /* License: Ruby's */
6790 static WORD
6791 constat_attr(int count, const int *seq, WORD attr, WORD default_attr, int *reverse)
6792 {
6793  int rev = *reverse;
6794  WORD bold;
6795 
6796  if (!count) return attr;
6797  if (rev) attr = constat_attr_color_reverse(attr);
6798  bold = attr & FOREGROUND_INTENSITY;
6799  attr &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
6800 
6801  while (count-- > 0) {
6802  switch (*seq++) {
6803  case 0:
6804  attr = default_attr;
6805  rev = 0;
6806  bold = 0;
6807  break;
6808  case 1:
6809  bold = FOREGROUND_INTENSITY;
6810  break;
6811  case 4:
6812 #ifndef COMMON_LVB_UNDERSCORE
6813 #define COMMON_LVB_UNDERSCORE 0x8000
6814 #endif
6815  attr |= COMMON_LVB_UNDERSCORE;
6816  break;
6817  case 7:
6818  rev = 1;
6819  break;
6820 
6821  case 30:
6822  attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
6823  break;
6824  case 17:
6825  case 31:
6826  attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)) | FOREGROUND_RED;
6827  break;
6828  case 18:
6829  case 32:
6830  attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_RED)) | FOREGROUND_GREEN;
6831  break;
6832  case 19:
6833  case 33:
6834  attr = (attr & ~FOREGROUND_BLUE) | FOREGROUND_GREEN | FOREGROUND_RED;
6835  break;
6836  case 20:
6837  case 34:
6838  attr = (attr & ~(FOREGROUND_GREEN | FOREGROUND_RED)) | FOREGROUND_BLUE;
6839  break;
6840  case 21:
6841  case 35:
6842  attr = (attr & ~FOREGROUND_GREEN) | FOREGROUND_BLUE | FOREGROUND_RED;
6843  break;
6844  case 22:
6845  case 36:
6846  attr = (attr & ~FOREGROUND_RED) | FOREGROUND_BLUE | FOREGROUND_GREEN;
6847  break;
6848  case 23:
6849  case 37:
6850  attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6851  break;
6852 
6853  case 40:
6854  attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
6855  break;
6856  case 41:
6857  attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN)) | BACKGROUND_RED;
6858  break;
6859  case 42:
6860  attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_RED)) | BACKGROUND_GREEN;
6861  break;
6862  case 43:
6863  attr = (attr & ~BACKGROUND_BLUE) | BACKGROUND_GREEN | BACKGROUND_RED;
6864  break;
6865  case 44:
6866  attr = (attr & ~(BACKGROUND_GREEN | BACKGROUND_RED)) | BACKGROUND_BLUE;
6867  break;
6868  case 45:
6869  attr = (attr & ~BACKGROUND_GREEN) | BACKGROUND_BLUE | BACKGROUND_RED;
6870  break;
6871  case 46:
6872  attr = (attr & ~BACKGROUND_RED) | BACKGROUND_BLUE | BACKGROUND_GREEN;
6873  break;
6874  case 47:
6875  attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
6876  break;
6877  }
6878  }
6879  attr |= bold;
6880  if (rev) attr = constat_attr_color_reverse(attr);
6881  *reverse = rev;
6882  return attr;
6883 }
6884 
6885 /* License: Ruby's */
6886 static void
6887 constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
6888 {
6889  DWORD written;
6890 
6891  FillConsoleOutputAttribute(handle, attr, len, pos, &written);
6892  FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
6893 }
6894 
6895 /* License: Ruby's */
6896 static void
6897 constat_apply(HANDLE handle, struct constat *s, WCHAR w)
6898 {
6899  CONSOLE_SCREEN_BUFFER_INFO csbi;
6900  const int *seq = s->vt100.seq;
6901  int count = s->vt100.state;
6902  int arg0, arg1 = 1;
6903  COORD pos;
6904 
6905  if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
6906  arg0 = (count > 0 && seq[0] > 0);
6907  if (arg0) arg1 = seq[0];
6908  switch (w) {
6909  case L'm':
6910  SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr, &s->vt100.reverse));
6911  break;
6912  case L'F':
6913  csbi.dwCursorPosition.X = 0;
6914  case L'A':
6915  csbi.dwCursorPosition.Y -= arg1;
6916  if (csbi.dwCursorPosition.Y < csbi.srWindow.Top)
6917  csbi.dwCursorPosition.Y = csbi.srWindow.Top;
6918  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6919  break;
6920  case L'E':
6921  csbi.dwCursorPosition.X = 0;
6922  case L'B':
6923  case L'e':
6924  csbi.dwCursorPosition.Y += arg1;
6925  if (csbi.dwCursorPosition.Y > csbi.srWindow.Bottom)
6926  csbi.dwCursorPosition.Y = csbi.srWindow.Bottom;
6927  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6928  break;
6929  case L'C':
6930  csbi.dwCursorPosition.X += arg1;
6931  if (csbi.dwCursorPosition.X >= csbi.srWindow.Right)
6932  csbi.dwCursorPosition.X = csbi.srWindow.Right;
6933  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6934  break;
6935  case L'D':
6936  csbi.dwCursorPosition.X -= arg1;
6937  if (csbi.dwCursorPosition.X < csbi.srWindow.Left)
6938  csbi.dwCursorPosition.X = csbi.srWindow.Left;
6939  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6940  break;
6941  case L'G':
6942  case L'`':
6943  arg1 += csbi.srWindow.Left;
6944  if (arg1 > csbi.srWindow.Right)
6945  arg1 = csbi.srWindow.Right;
6946  csbi.dwCursorPosition.X = arg1;
6947  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6948  break;
6949  case L'd':
6950  arg1 += csbi.srWindow.Top;
6951  if (arg1 > csbi.srWindow.Bottom)
6952  arg1 = csbi.srWindow.Bottom;
6953  csbi.dwCursorPosition.Y = arg1;
6954  SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6955  break;
6956  case L'H':
6957  case L'f':
6958  pos.Y = arg1 + csbi.srWindow.Top - 1;
6959  if (pos.Y > csbi.srWindow.Bottom) pos.Y = csbi.srWindow.Bottom;
6960  if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
6961  pos.X = arg1 + csbi.srWindow.Left - 1;
6962  if (pos.X > csbi.srWindow.Right) pos.X = csbi.srWindow.Right;
6963  SetConsoleCursorPosition(handle, pos);
6964  break;
6965  case L'J':
6966  switch (arg0 ? arg1 : 0) {
6967  case 0: /* erase after cursor */
6968  constat_clear(handle, csbi.wAttributes,
6969  (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.dwCursorPosition.Y + 1)
6970  - csbi.dwCursorPosition.X),
6971  csbi.dwCursorPosition);
6972  break;
6973  case 1: /* erase before *and* cursor */
6974  pos.X = 0;
6975  pos.Y = csbi.srWindow.Top;
6976  constat_clear(handle, csbi.wAttributes,
6977  (csbi.dwSize.X * (csbi.dwCursorPosition.Y - csbi.srWindow.Top)
6978  + csbi.dwCursorPosition.X + 1),
6979  pos);
6980  break;
6981  case 2: /* erase entire screen */
6982  pos.X = 0;
6983  pos.Y = csbi.srWindow.Top;
6984  constat_clear(handle, csbi.wAttributes,
6985  (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1)),
6986  pos);
6987  break;
6988  case 3: /* erase entire screen */
6989  pos.X = 0;
6990  pos.Y = 0;
6991  constat_clear(handle, csbi.wAttributes,
6992  (csbi.dwSize.X * csbi.dwSize.Y),
6993  pos);
6994  break;
6995  }
6996  break;
6997  case L'K':
6998  switch (arg0 ? arg1 : 0) {
6999  case 0: /* erase after cursor */
7000  constat_clear(handle, csbi.wAttributes,
7001  (csbi.dwSize.X - csbi.dwCursorPosition.X),
7002  csbi.dwCursorPosition);
7003  break;
7004  case 1: /* erase before *and* cursor */
7005  pos.X = 0;
7006  pos.Y = csbi.dwCursorPosition.Y;
7007  constat_clear(handle, csbi.wAttributes,
7008  csbi.dwCursorPosition.X + 1, pos);
7009  break;
7010  case 2: /* erase entire line */
7011  pos.X = 0;
7012  pos.Y = csbi.dwCursorPosition.Y;
7013  constat_clear(handle, csbi.wAttributes,
7014  csbi.dwSize.X, pos);
7015  break;
7016  }
7017  break;
7018  case L's':
7019  s->vt100.saved = csbi.dwCursorPosition;
7020  break;
7021  case L'u':
7022  SetConsoleCursorPosition(handle, s->vt100.saved);
7023  break;
7024  case L'h':
7025  if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7026  CONSOLE_CURSOR_INFO cci;
7027  GetConsoleCursorInfo(handle, &cci);
7028  cci.bVisible = TRUE;
7029  SetConsoleCursorInfo(handle, &cci);
7030  }
7031  break;
7032  case L'l':
7033  if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7034  CONSOLE_CURSOR_INFO cci;
7035  GetConsoleCursorInfo(handle, &cci);
7036  cci.bVisible = FALSE;
7037  SetConsoleCursorInfo(handle, &cci);
7038  }
7039  break;
7040  }
7041 }
7042 
7043 /* get rid of console writing bug; assume WriteConsole and WriteFile
7044  * on a console share the same limit. */
7045 static const long MAXSIZE_CONSOLE_WRITING = 31366;
7046 
7047 /* License: Ruby's */
7048 static long
7049 constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
7050 {
7051  const WCHAR *ptr = *ptrp;
7052  long rest, len = *lenp;
7053  while (len-- > 0) {
7054  WCHAR wc = *ptr++;
7055  if (wc == 0x1b) {
7056  rest = *lenp - len - 1;
7057  if (s->vt100.state == constat_esc) {
7058  rest++; /* reuse this ESC */
7059  }
7060  s->vt100.state = constat_init;
7061  if (len > 0 && *ptr != L'[') continue;
7062  s->vt100.state = constat_esc;
7063  }
7064  else if (s->vt100.state == constat_esc) {
7065  if (wc != L'[') {
7066  /* TODO: supply dropped ESC at beginning */
7067  s->vt100.state = constat_init;
7068  continue;
7069  }
7070  rest = *lenp - len - 1;
7071  if (rest > 0) --rest;
7072  s->vt100.state = constat_seq;
7073  s->vt100.seq[0] = 0;
7074  }
7075  else if (s->vt100.state >= constat_seq) {
7076  if (wc >= L'0' && wc <= L'9') {
7077  if (s->vt100.state < (int)numberof(s->vt100.seq)) {
7078  int *seq = &s->vt100.seq[s->vt100.state];
7079  *seq = (*seq * 10) + (wc - L'0');
7080  }
7081  }
7082  else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
7083  s->vt100.seq[s->vt100.state++] = -1;
7084  }
7085  else {
7086  do {
7087  if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
7088  s->vt100.seq[s->vt100.state] = 0;
7089  }
7090  else {
7091  s->vt100.state = (int)numberof(s->vt100.seq);
7092  }
7093  } while (0);
7094  if (wc != L';') {
7095  constat_apply(h, s, wc);
7096  s->vt100.state = constat_init;
7097  }
7098  }
7099  rest = 0;
7100  }
7101  else if ((rest = *lenp - len) < MAXSIZE_CONSOLE_WRITING) {
7102  continue;
7103  }
7104  *ptrp = ptr;
7105  *lenp = len;
7106  return rest;
7107  }
7108  len = *lenp;
7109  *ptrp = ptr;
7110  *lenp = 0;
7111  return len;
7112 }
7113 
7114 
7115 /* License: Ruby's */
7116 int
7117 rb_w32_close(int fd)
7118 {
7119  SOCKET sock = TO_SOCKET(fd);
7120  int save_errno = errno;
7121 
7122  if (!is_socket(sock)) {
7123  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7124  constat_delete((HANDLE)sock);
7125  return _close(fd);
7126  }
7127  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
7128  socklist_delete(&sock, NULL);
7129  _close(fd);
7130  errno = save_errno;
7131  if (closesocket(sock) == SOCKET_ERROR) {
7132  errno = map_errno(WSAGetLastError());
7133  return -1;
7134  }
7135  return 0;
7136 }
7137 
7138 #ifndef INVALID_SET_FILE_POINTER
7139 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
7140 #endif
7141 
7142 static int
7143 setup_overlapped(OVERLAPPED *ol, int fd, int iswrite, rb_off_t *_offset)
7144 {
7145  memset(ol, 0, sizeof(*ol));
7146 
7147  // On mode:a, it can write only FILE_END.
7148  // On mode:a+, though it can write only FILE_END,
7149  // it can read from everywhere.
7150  DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
7151 
7152  if (_offset) {
7153  // Explicit offset was provided (pread/pwrite) - use it:
7154  uint64_t offset = *_offset;
7155  ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
7156  ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
7157 
7158  // Update _offset with the current offset:
7159  LARGE_INTEGER seek_offset = {0}, current_offset = {0};
7160  if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, &current_offset, seek_method)) {
7161  DWORD last_error = GetLastError();
7162  if (last_error != NO_ERROR) {
7163  errno = map_errno(last_error);
7164  return -1;
7165  }
7166  }
7167 
7168  // As we need to restore the current offset later, we save it here:
7169  *_offset = current_offset.QuadPart;
7170  }
7171  else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7172  LONG high = 0;
7173  DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method);
7174 
7175  if (low == INVALID_SET_FILE_POINTER) {
7176  DWORD err = GetLastError();
7177  if (err != NO_ERROR) {
7178  errno = map_errno(err);
7179  return -1;
7180  }
7181  }
7182 
7183  ol->Offset = low;
7184  ol->OffsetHigh = high;
7185  }
7186 
7187  ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7188  if (!ol->hEvent) {
7189  errno = map_errno(GetLastError());
7190  return -1;
7191  }
7192  return 0;
7193 }
7194 
7195 static void
7196 finish_overlapped(OVERLAPPED *ol, int fd, DWORD size, rb_off_t *_offset)
7197 {
7198  CloseHandle(ol->hEvent);
7199 
7200  if (_offset) {
7201  // If we were doing a `pread`/`pwrite`, we need to restore the current that was saved in setup_overlapped:
7202  DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN;
7203 
7204  LARGE_INTEGER seek_offset = {0};
7205  if (seek_method == FILE_BEGIN) {
7206  seek_offset.QuadPart = *_offset;
7207  }
7208 
7209  SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method);
7210  }
7211  else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7212  LONG high = ol->OffsetHigh;
7213  DWORD low = ol->Offset + size;
7214  if (low < ol->Offset)
7215  ++high;
7216  SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
7217  }
7218 }
7219 
7220 #undef read
7221 /* License: Ruby's */
7222 static ssize_t
7223 rb_w32_read_internal(int fd, void *buf, size_t size, rb_off_t *offset)
7224 {
7225  SOCKET sock = TO_SOCKET(fd);
7226  DWORD read;
7227  DWORD wait;
7228  DWORD err;
7229  size_t len;
7230  size_t ret;
7231  OVERLAPPED ol;
7232  BOOL isconsole;
7233  BOOL islineinput = FALSE;
7234  int start = 0;
7235 
7236  if (is_socket(sock))
7237  return rb_w32_recv(fd, buf, size, 0);
7238 
7239  // validate fd by using _get_osfhandle() because we cannot access _nhandle
7240  if (_get_osfhandle(fd) == -1) {
7241  return -1;
7242  }
7243 
7244  if (!offset && _osfile(fd) & FTEXT) {
7245  return _read(fd, buf, size);
7246  }
7247 
7248  rb_acrt_lowio_lock_fh(fd);
7249 
7250  if (!size || _osfile(fd) & FEOFLAG) {
7251  _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7252  rb_acrt_lowio_unlock_fh(fd);
7253  return 0;
7254  }
7255 
7256  ret = 0;
7257  isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
7258  if (isconsole) {
7259  DWORD mode;
7260  GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
7261  islineinput = (mode & ENABLE_LINE_INPUT) != 0;
7262  }
7263  retry:
7264  /* get rid of console reading bug */
7265  if (isconsole) {
7266  constat_reset((HANDLE)_osfhnd(fd));
7267  if (start)
7268  len = 1;
7269  else {
7270  len = 0;
7271  start = 1;
7272  }
7273  }
7274  else
7275  len = size;
7276  size -= len;
7277 
7278  if (setup_overlapped(&ol, fd, FALSE, offset)) {
7279  rb_acrt_lowio_unlock_fh(fd);
7280  return -1;
7281  }
7282 
7283  if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, &ol)) {
7284  err = GetLastError();
7285  if (err == ERROR_NO_DATA && (_osfile(fd) & FPIPE)) {
7286  DWORD state;
7287  if (GetNamedPipeHandleState((HANDLE)_osfhnd(fd), &state, NULL, NULL, NULL, NULL, 0) && (state & PIPE_NOWAIT)) {
7288  errno = EWOULDBLOCK;
7289  }
7290  else {
7291  errno = map_errno(err);
7292  }
7293  rb_acrt_lowio_unlock_fh(fd);
7294  return -1;
7295  }
7296  else if (err != ERROR_IO_PENDING) {
7297  CloseHandle(ol.hEvent);
7298  if (err == ERROR_ACCESS_DENIED)
7299  errno = EBADF;
7300  else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
7301  rb_acrt_lowio_unlock_fh(fd);
7302  return 0;
7303  }
7304  else
7305  errno = map_errno(err);
7306 
7307  rb_acrt_lowio_unlock_fh(fd);
7308  return -1;
7309  }
7310 
7311  wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7312  if (wait != WAIT_OBJECT_0) {
7313  if (wait == WAIT_OBJECT_0 + 1)
7314  errno = EINTR;
7315  else
7316  errno = map_errno(GetLastError());
7317  CloseHandle(ol.hEvent);
7318  CancelIo((HANDLE)_osfhnd(fd));
7319  rb_acrt_lowio_unlock_fh(fd);
7320  return -1;
7321  }
7322 
7323  if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
7324  (err = GetLastError()) != ERROR_HANDLE_EOF) {
7325  int ret = 0;
7326  if (err != ERROR_BROKEN_PIPE) {
7327  errno = map_errno(err);
7328  ret = -1;
7329  }
7330  CloseHandle(ol.hEvent);
7331  CancelIo((HANDLE)_osfhnd(fd));
7332  rb_acrt_lowio_unlock_fh(fd);
7333  return ret;
7334  }
7335  }
7336  else {
7337  err = GetLastError();
7338  errno = map_errno(err);
7339  }
7340 
7341  finish_overlapped(&ol, fd, read, offset);
7342 
7343  ret += read;
7344  if (read >= len) {
7345  buf = (char *)buf + read;
7346  if (err != ERROR_OPERATION_ABORTED &&
7347  !(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
7348  goto retry;
7349  }
7350  if (read == 0)
7351  _set_osflags(fd, _osfile(fd) | FEOFLAG);
7352 
7353 
7354  rb_acrt_lowio_unlock_fh(fd);
7355 
7356  return ret;
7357 }
7358 
7359 #undef write
7360 /* License: Ruby's */
7361 static ssize_t
7362 rb_w32_write_internal(int fd, const void *buf, size_t size, rb_off_t *offset)
7363 {
7364  SOCKET sock = TO_SOCKET(fd);
7365  DWORD written;
7366  DWORD wait;
7367  DWORD err;
7368  size_t len;
7369  size_t ret;
7370  OVERLAPPED ol;
7371 
7372  if (is_socket(sock))
7373  return rb_w32_send(fd, buf, size, 0);
7374 
7375  // validate fd by using _get_osfhandle() because we cannot access _nhandle
7376  if (_get_osfhandle(fd) == -1) {
7377  return -1;
7378  }
7379 
7380  // If an offset is given, we can't use `_write`.
7381  if (!offset && (_osfile(fd) & FTEXT) &&
7382  (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
7383  ssize_t w = _write(fd, buf, size);
7384  if (w == (ssize_t)-1 && errno == EINVAL) {
7385  errno = map_errno(GetLastError());
7386  }
7387  return w;
7388  }
7389 
7390  rb_acrt_lowio_lock_fh(fd);
7391 
7392  if (!size || _osfile(fd) & FEOFLAG) {
7393  rb_acrt_lowio_unlock_fh(fd);
7394  return 0;
7395  }
7396 
7397  ret = 0;
7398  retry:
7399  len = (_osfile(fd) & FDEV) ? min(MAXSIZE_CONSOLE_WRITING, size) : size;
7400  size -= len;
7401  retry2:
7402 
7403  // Provide the requested offset.
7404  if (setup_overlapped(&ol, fd, TRUE, offset)) {
7405  rb_acrt_lowio_unlock_fh(fd);
7406  return -1;
7407  }
7408 
7409  if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, &ol)) {
7410  err = GetLastError();
7411  if (err != ERROR_IO_PENDING) {
7412  CloseHandle(ol.hEvent);
7413  if (err == ERROR_ACCESS_DENIED)
7414  errno = EBADF;
7415  else
7416  errno = map_errno(err);
7417 
7418  rb_acrt_lowio_unlock_fh(fd);
7419  return -1;
7420  }
7421 
7422  wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7423  if (wait != WAIT_OBJECT_0) {
7424  if (wait == WAIT_OBJECT_0 + 1)
7425  errno = EINTR;
7426  else
7427  errno = map_errno(GetLastError());
7428  CloseHandle(ol.hEvent);
7429  CancelIo((HANDLE)_osfhnd(fd));
7430  rb_acrt_lowio_unlock_fh(fd);
7431  return -1;
7432  }
7433 
7434  if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) {
7435  errno = map_errno(GetLastError());
7436  CloseHandle(ol.hEvent);
7437  CancelIo((HANDLE)_osfhnd(fd));
7438  rb_acrt_lowio_unlock_fh(fd);
7439  return -1;
7440  }
7441  }
7442 
7443  finish_overlapped(&ol, fd, written, offset);
7444 
7445  ret += written;
7446  if (written == len) {
7447  buf = (const char *)buf + len;
7448  if (size > 0)
7449  goto retry;
7450  }
7451  if (ret == 0) {
7452  size_t newlen = len / 2;
7453  if (newlen > 0) {
7454  size += len - newlen;
7455  len = newlen;
7456  goto retry2;
7457  }
7458  ret = -1;
7459  errno = EWOULDBLOCK;
7460  }
7461 
7462  rb_acrt_lowio_unlock_fh(fd);
7463 
7464  return ret;
7465 }
7466 
7467 ssize_t
7468 rb_w32_read(int fd, void *buf, size_t size)
7469 {
7470  return rb_w32_read_internal(fd, buf, size, NULL);
7471 }
7472 
7473 ssize_t
7474 rb_w32_write(int fd, const void *buf, size_t size)
7475 {
7476  return rb_w32_write_internal(fd, buf, size, NULL);
7477 }
7478 
7479 ssize_t
7480 rb_w32_pread(int descriptor, void *base, size_t size, rb_off_t offset)
7481 {
7482  return rb_w32_read_internal(descriptor, base, size, &offset);
7483 }
7484 
7485 ssize_t
7486 rb_w32_pwrite(int descriptor, const void *base, size_t size, rb_off_t offset)
7487 {
7488  return rb_w32_write_internal(descriptor, base, size, &offset);
7489 }
7490 
7491 /* License: Ruby's */
7492 long
7493 rb_w32_write_console(uintptr_t strarg, int fd)
7494 {
7495  HANDLE handle;
7496  DWORD dwMode, reslen;
7497  VALUE str = strarg;
7498  int encindex;
7499  WCHAR *wbuffer = 0;
7500  const WCHAR *ptr, *next;
7501  struct constat *s;
7502  long len;
7503 
7504  handle = (HANDLE)_osfhnd(fd);
7505  if (!GetConsoleMode(handle, &dwMode))
7506  return -1L;
7507 
7508  s = constat_handle(handle);
7509  if (!s) return -1L;
7510  encindex = ENCODING_GET(str);
7511  switch (encindex) {
7512  default:
7513  if (!rb_econv_has_convpath_p(rb_enc_name(rb_enc_from_index(encindex)), "UTF-8"))
7514  return -1L;
7515  str = rb_str_conv_enc_opts(str, NULL, rb_enc_from_index(ENCINDEX_UTF_8),
7517  /* fall through */
7518  case ENCINDEX_US_ASCII:
7519  case ENCINDEX_ASCII_8BIT:
7520  /* assume UTF-8 */
7521  case ENCINDEX_UTF_8:
7522  ptr = wbuffer = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(str), RSTRING_LEN(str), &len);
7523  if (!ptr) return -1L;
7524  break;
7525  case ENCINDEX_UTF_16LE:
7526  ptr = (const WCHAR *)RSTRING_PTR(str);
7527  len = RSTRING_LEN(str) / sizeof(WCHAR);
7528  break;
7529  }
7530  reslen = 0;
7531  if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7532  if (!WriteConsoleW(handle, ptr, len, &reslen, NULL))
7533  reslen = (DWORD)-1L;
7534  }
7535  else {
7536  while (len > 0) {
7537  long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
7538  reslen += next - ptr;
7539  if (curlen > 0) {
7540  DWORD written;
7541  if (!WriteConsoleW(handle, ptr, curlen, &written, NULL)) {
7542  reslen = (DWORD)-1L;
7543  break;
7544  }
7545  }
7546  ptr = next;
7547  }
7548  }
7549  RB_GC_GUARD(str);
7550  free(wbuffer);
7551  return (long)reslen;
7552 }
7553 
7554 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7555 /* License: Ruby's */
7556 static int
7557 unixtime_to_filetime(time_t time, FILETIME *ft)
7558 {
7559  ULARGE_INTEGER tmp;
7560 
7561  tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7562  ft->dwLowDateTime = tmp.LowPart;
7563  ft->dwHighDateTime = tmp.HighPart;
7564  return 0;
7565 }
7566 #endif
7567 
7568 /* License: Ruby's */
7569 static int
7570 timespec_to_filetime(const struct timespec *ts, FILETIME *ft)
7571 {
7572  ULARGE_INTEGER tmp;
7573 
7574  tmp.QuadPart = ((LONG_LONG)ts->tv_sec + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7575  tmp.QuadPart += ts->tv_nsec / 100;
7576  ft->dwLowDateTime = tmp.LowPart;
7577  ft->dwHighDateTime = tmp.HighPart;
7578  return 0;
7579 }
7580 
7581 /* License: Ruby's */
7582 static int
7583 wutimensat(int dirfd, const WCHAR *path, const struct timespec *times, int flags)
7584 {
7585  HANDLE hFile;
7586  FILETIME atime, mtime;
7587  struct stati128 stat;
7588  int ret = 0;
7589 
7590  /* TODO: When path is absolute, dirfd should be ignored. */
7591  if (dirfd != AT_FDCWD) {
7592  errno = ENOSYS;
7593  return -1;
7594  }
7595 
7596  if (flags != 0) {
7597  errno = EINVAL; /* AT_SYMLINK_NOFOLLOW isn't supported. */
7598  return -1;
7599  }
7600 
7601  if (wstati128(path, &stat, FALSE)) {
7602  return -1;
7603  }
7604 
7605  if (times) {
7606  if (timespec_to_filetime(&times[0], &atime)) {
7607  return -1;
7608  }
7609  if (timespec_to_filetime(&times[1], &mtime)) {
7610  return -1;
7611  }
7612  }
7613  else {
7614  GetSystemTimePreciseAsFileTime(&atime);
7615  mtime = atime;
7616  }
7617 
7618  RUBY_CRITICAL {
7619  const DWORD attr = GetFileAttributesW(path);
7620  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7621  SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7622  hFile = open_special(path, GENERIC_WRITE, 0);
7623  if (hFile == INVALID_HANDLE_VALUE) {
7624  errno = map_errno(GetLastError());
7625  ret = -1;
7626  }
7627  else {
7628  if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7629  errno = map_errno(GetLastError());
7630  ret = -1;
7631  }
7632  CloseHandle(hFile);
7633  }
7634  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7635  SetFileAttributesW(path, attr);
7636  }
7637 
7638  return ret;
7639 }
7640 
7641 /* License: Ruby's */
7642 static int
7643 w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags, UINT cp)
7644 {
7645  WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7646  int ret = -1;
7647 
7648  if (wpath) {
7649  ret = wutimensat(dirfd, wpath, times, flags);
7650  free(wpath);
7651  }
7652  return ret;
7653 }
7654 
7655 /* License: Ruby's */
7656 int
7657 rb_w32_uutime(const char *path, const struct utimbuf *times)
7658 {
7659  struct timespec ts[2];
7660 
7661  ts[0].tv_sec = times->actime;
7662  ts[0].tv_nsec = 0;
7663  ts[1].tv_sec = times->modtime;
7664  ts[1].tv_nsec = 0;
7665  return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7666 }
7667 
7668 /* License: Ruby's */
7669 int
7670 rb_w32_utime(const char *path, const struct utimbuf *times)
7671 {
7672  struct timespec ts[2];
7673 
7674  ts[0].tv_sec = times->actime;
7675  ts[0].tv_nsec = 0;
7676  ts[1].tv_sec = times->modtime;
7677  ts[1].tv_nsec = 0;
7678  return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7679 }
7680 
7681 /* License: Ruby's */
7682 int
7683 rb_w32_uutimes(const char *path, const struct timeval *times)
7684 {
7685  struct timespec ts[2];
7686 
7687  ts[0].tv_sec = times[0].tv_sec;
7688  ts[0].tv_nsec = times[0].tv_usec * 1000;
7689  ts[1].tv_sec = times[1].tv_sec;
7690  ts[1].tv_nsec = times[1].tv_usec * 1000;
7691  return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7692 }
7693 
7694 /* License: Ruby's */
7695 int
7696 rb_w32_utimes(const char *path, const struct timeval *times)
7697 {
7698  struct timespec ts[2];
7699 
7700  ts[0].tv_sec = times[0].tv_sec;
7701  ts[0].tv_nsec = times[0].tv_usec * 1000;
7702  ts[1].tv_sec = times[1].tv_sec;
7703  ts[1].tv_nsec = times[1].tv_usec * 1000;
7704  return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7705 }
7706 
7707 /* License: Ruby's */
7708 int
7709 rb_w32_uutimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7710 {
7711  return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7712 }
7713 
7714 /* License: Ruby's */
7715 int
7716 rb_w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7717 {
7718  return w32_utimensat(dirfd, path, times, flags, filecp());
7719 }
7720 
7721 /* License: Ruby's */
7722 int
7723 rb_w32_uchdir(const char *path)
7724 {
7725  WCHAR *wpath;
7726  int ret;
7727 
7728  if (!(wpath = utf8_to_wstr(path, NULL)))
7729  return -1;
7730  ret = _wchdir(wpath);
7731  free(wpath);
7732  return ret;
7733 }
7734 
7735 /* License: Ruby's */
7736 static int
7737 wmkdir(const WCHAR *wpath, int mode)
7738 {
7739  int ret = -1;
7740 
7741  RUBY_CRITICAL do {
7742  if (CreateDirectoryW(wpath, NULL) == FALSE) {
7743  errno = map_errno(GetLastError());
7744  break;
7745  }
7746  if (_wchmod(wpath, mode) == -1) {
7747  RemoveDirectoryW(wpath);
7748  break;
7749  }
7750  ret = 0;
7751  } while (0);
7752  return ret;
7753 }
7754 
7755 /* License: Ruby's */
7756 int
7757 rb_w32_umkdir(const char *path, int mode)
7758 {
7759  WCHAR *wpath;
7760  int ret;
7761 
7762  if (!(wpath = utf8_to_wstr(path, NULL)))
7763  return -1;
7764  ret = wmkdir(wpath, mode);
7765  free(wpath);
7766  return ret;
7767 }
7768 
7769 /* License: Ruby's */
7770 int
7771 rb_w32_mkdir(const char *path, int mode)
7772 {
7773  WCHAR *wpath;
7774  int ret;
7775 
7776  if (!(wpath = filecp_to_wstr(path, NULL)))
7777  return -1;
7778  ret = wmkdir(wpath, mode);
7779  free(wpath);
7780  return ret;
7781 }
7782 
7783 /* License: Ruby's */
7784 static int
7785 wrmdir(const WCHAR *wpath)
7786 {
7787  int ret = 0;
7788  RUBY_CRITICAL {
7789  const DWORD attr = GetFileAttributesW(wpath);
7790  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7791  SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
7792  }
7793  if (RemoveDirectoryW(wpath) == FALSE) {
7794  errno = map_errno(GetLastError());
7795  ret = -1;
7796  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7797  SetFileAttributesW(wpath, attr);
7798  }
7799  }
7800  }
7801  return ret;
7802 }
7803 
7804 /* License: Ruby's */
7805 int
7806 rb_w32_rmdir(const char *path)
7807 {
7808  WCHAR *wpath;
7809  int ret;
7810 
7811  if (!(wpath = filecp_to_wstr(path, NULL)))
7812  return -1;
7813  ret = wrmdir(wpath);
7814  free(wpath);
7815  return ret;
7816 }
7817 
7818 /* License: Ruby's */
7819 int
7820 rb_w32_urmdir(const char *path)
7821 {
7822  WCHAR *wpath;
7823  int ret;
7824 
7825  if (!(wpath = utf8_to_wstr(path, NULL)))
7826  return -1;
7827  ret = wrmdir(wpath);
7828  free(wpath);
7829  return ret;
7830 }
7831 
7832 /* License: Ruby's */
7833 static int
7834 wunlink(const WCHAR *path)
7835 {
7836  int ret = 0;
7837  const DWORD SYMLINKD = FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY;
7838  RUBY_CRITICAL {
7839  const DWORD attr = GetFileAttributesW(path);
7840  if (attr == (DWORD)-1) {
7841  }
7842  else if ((attr & SYMLINKD) == SYMLINKD) {
7843  ret = RemoveDirectoryW(path);
7844  }
7845  else {
7846  if (attr & FILE_ATTRIBUTE_READONLY) {
7847  SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7848  }
7849  ret = DeleteFileW(path);
7850  }
7851  if (!ret) {
7852  errno = map_errno(GetLastError());
7853  ret = -1;
7854  if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7855  SetFileAttributesW(path, attr);
7856  }
7857  }
7858  }
7859  return ret;
7860 }
7861 
7862 /* License: Ruby's */
7863 int
7864 rb_w32_uunlink(const char *path)
7865 {
7866  WCHAR *wpath;
7867  int ret;
7868 
7869  if (!(wpath = utf8_to_wstr(path, NULL)))
7870  return -1;
7871  ret = wunlink(wpath);
7872  free(wpath);
7873  return ret;
7874 }
7875 
7876 /* License: Ruby's */
7877 int
7878 rb_w32_unlink(const char *path)
7879 {
7880  WCHAR *wpath;
7881  int ret;
7882 
7883  if (!(wpath = filecp_to_wstr(path, NULL)))
7884  return -1;
7885  ret = wunlink(wpath);
7886  free(wpath);
7887  return ret;
7888 }
7889 
7890 /* License: Ruby's */
7891 int
7892 rb_w32_uchmod(const char *path, int mode)
7893 {
7894  WCHAR *wpath;
7895  int ret;
7896 
7897  if (!(wpath = utf8_to_wstr(path, NULL)))
7898  return -1;
7899  ret = _wchmod(wpath, mode);
7900  free(wpath);
7901  return ret;
7902 }
7903 
7904 /* License: Ruby's */
7905 int
7906 fchmod(int fd, int mode)
7907 {
7908  /* from winbase.h of the mingw-w64 runtime package. */
7909  struct {
7910  LARGE_INTEGER CreationTime;
7911  LARGE_INTEGER LastAccessTime;
7912  LARGE_INTEGER LastWriteTime;
7913  LARGE_INTEGER ChangeTime;
7914  DWORD FileAttributes;
7915  } info = {{{0}}, {{0}}, {{0}},}; /* fields with 0 are unchanged */
7916  HANDLE h = (HANDLE)_get_osfhandle(fd);
7917 
7918  info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
7919  if (!(mode & 0200)) info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
7920  if (!SetFileInformationByHandle(h, 0, &info, sizeof(info))) {
7921  errno = map_errno(GetLastError());
7922  return -1;
7923  }
7924  return 0;
7925 }
7926 
7927 /* License: Ruby's */
7928 int
7929 rb_w32_isatty(int fd)
7930 {
7931  DWORD mode;
7932 
7933  // validate fd by using _get_osfhandle() because we cannot access _nhandle
7934  if (_get_osfhandle(fd) == -1) {
7935  return 0;
7936  }
7937  if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
7938  errno = ENOTTY;
7939  return 0;
7940  }
7941  return 1;
7942 }
7943 
7944 #if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7945 extern long _ftol(double);
7946 /* License: Ruby's */
7947 long
7948 _ftol2(double d)
7949 {
7950  return _ftol(d);
7951 }
7952 
7953 /* License: Ruby's */
7954 long
7955 _ftol2_sse(double d)
7956 {
7957  return _ftol(d);
7958 }
7959 #endif
7960 
7961 #ifndef signbit
7962 /* License: Ruby's */
7963 int
7964 signbit(double x)
7965 {
7966  int *ip = (int *)(&x + 1) - 1;
7967  return *ip < 0;
7968 }
7969 #endif
7970 
7971 /* License: Ruby's */
7972 const char * WSAAPI
7973 rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
7974 {
7975  return (inet_ntop)(af, (void *)addr, numaddr, numaddr_len);
7976 }
7977 
7978 /* License: Ruby's */
7979 int WSAAPI
7980 rb_w32_inet_pton(int af, const char *src, void *dst)
7981 {
7982  return (inet_pton)(af, src, dst);
7983 }
7984 
7985 /* License: Ruby's */
7986 char
7987 rb_w32_fd_is_text(int fd)
7988 {
7989  return _osfile(fd) & FTEXT;
7990 }
7991 
7992 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7993 /* License: Ruby's */
7994 static int
7995 unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
7996 {
7997  FILETIME ft;
7998  if (unixtime_to_filetime(t, &ft)) return -1;
7999  if (!FileTimeToSystemTime(&ft, st)) return -1;
8000  return 0;
8001 }
8002 
8003 /* License: Ruby's */
8004 static void
8005 systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
8006 {
8007  int y = st->wYear, m = st->wMonth, d = st->wDay;
8008  t->tm_sec = st->wSecond;
8009  t->tm_min = st->wMinute;
8010  t->tm_hour = st->wHour;
8011  t->tm_mday = st->wDay;
8012  t->tm_mon = st->wMonth - 1;
8013  t->tm_year = y - 1900;
8014  t->tm_wday = st->wDayOfWeek;
8015  switch (m) {
8016  case 1:
8017  break;
8018  case 2:
8019  d += 31;
8020  break;
8021  default:
8022  d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
8023  d += ((m - 3) * 153 + 2) / 5;
8024  break;
8025  }
8026  t->tm_yday = d - 1;
8027 }
8028 
8029 /* License: Ruby's */
8030 static int
8031 systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
8032 {
8033  TIME_ZONE_INFORMATION stdtz;
8034  SYSTEMTIME sst;
8035 
8036  if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
8037  if (!tz) {
8038  GetTimeZoneInformation(&stdtz);
8039  tz = &stdtz;
8040  }
8041  if (tz->StandardBias == tz->DaylightBias) return 0;
8042  if (!tz->StandardDate.wMonth) return 0;
8043  if (!tz->DaylightDate.wMonth) return 0;
8044  if (tz != &stdtz) stdtz = *tz;
8045 
8046  stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
8047  if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst)) return 0;
8048  if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
8049  return 0;
8050  return 1;
8051 }
8052 #endif
8053 
8054 #ifdef HAVE__GMTIME64_S
8055 # ifndef HAVE__LOCALTIME64_S
8056 /* assume same as _gmtime64_s() */
8057 # define HAVE__LOCALTIME64_S 1
8058 # endif
8059 # ifndef MINGW_HAS_SECURE_API
8060  _CRTIMP errno_t __cdecl _gmtime64_s(struct tm* tm, const __time64_t *time);
8061  _CRTIMP errno_t __cdecl _localtime64_s(struct tm* tm, const __time64_t *time);
8062 # endif
8063 # define gmtime_s _gmtime64_s
8064 # define localtime_s _localtime64_s
8065 #endif
8066 
8067 /* License: Ruby's */
8068 struct tm *
8069 gmtime_r(const time_t *tp, struct tm *rp)
8070 {
8071  int e = EINVAL;
8072  if (!tp || !rp) {
8073  error:
8074  errno = e;
8075  return NULL;
8076  }
8077 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8078  e = gmtime_s(rp, tp);
8079  if (e != 0) goto error;
8080 #else
8081  {
8082  SYSTEMTIME st;
8083  if (unixtime_to_systemtime(*tp, &st)) goto error;
8084  rp->tm_isdst = 0;
8085  systemtime_to_tm(&st, rp);
8086  }
8087 #endif
8088  return rp;
8089 }
8090 
8091 /* License: Ruby's */
8092 struct tm *
8093 localtime_r(const time_t *tp, struct tm *rp)
8094 {
8095  int e = EINVAL;
8096  if (!tp || !rp) {
8097  error:
8098  errno = e;
8099  return NULL;
8100  }
8101 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8102  e = localtime_s(rp, tp);
8103  if (e) goto error;
8104 #else
8105  {
8106  SYSTEMTIME gst, lst;
8107  if (unixtime_to_systemtime(*tp, &gst)) goto error;
8108  rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
8109  systemtime_to_tm(&lst, rp);
8110  }
8111 #endif
8112  return rp;
8113 }
8114 
8115 /* License: Ruby's */
8116 int
8117 rb_w32_wrap_io_handle(HANDLE h, int flags)
8118 {
8119  BOOL tmp;
8120  int len = sizeof(tmp);
8121  int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (char *)&tmp, &len);
8122  if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
8123  int f = 0;
8124  if (flags & O_NONBLOCK) {
8125  flags &= ~O_NONBLOCK;
8126  f = O_NONBLOCK;
8127  }
8128  socklist_insert((SOCKET)h, f);
8129  }
8130  else if (flags & O_NONBLOCK) {
8131  errno = EINVAL;
8132  return -1;
8133  }
8134  return rb_w32_open_osfhandle((intptr_t)h, flags);
8135 }
8136 
8137 /* License: Ruby's */
8138 int
8139 rb_w32_unwrap_io_handle(int fd)
8140 {
8141  SOCKET sock = TO_SOCKET(fd);
8142  _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
8143  if (!is_socket(sock)) {
8144  UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
8145  constat_delete((HANDLE)sock);
8146  }
8147  else {
8148  socklist_delete(&sock, NULL);
8149  }
8150  return _close(fd);
8151 }
8152 
8153 #if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8154 /*
8155  * Set floating point precision for pow() of mingw-w64 x86.
8156  * With default precision the result is not proper on WinXP.
8157  */
8158 double
8159 rb_w32_pow(double x, double y)
8160 {
8161 #undef pow
8162  double r;
8163  unsigned int default_control = _controlfp(0, 0);
8164  _controlfp(_PC_64, _MCW_PC);
8165  r = pow(x, y);
8166  /* Restore setting */
8167  _controlfp(default_control, _MCW_PC);
8168  return r;
8169 }
8170 #endif
8171 
8172 typedef struct {
8173  BOOL file_id_p;
8174  union {
8175  BY_HANDLE_FILE_INFORMATION bhfi;
8176  FILE_ID_INFO fii;
8177  } info;
8178 } w32_io_info_t;
8179 
8180 static HANDLE
8181 w32_io_info(VALUE *file, w32_io_info_t *st)
8182 {
8183  VALUE tmp;
8184  HANDLE f, ret = 0;
8185 
8186  tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
8187  if (!NIL_P(tmp)) {
8188  f = (HANDLE)rb_w32_get_osfhandle(rb_io_descriptor(tmp));
8189  if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
8190  }
8191  else {
8192  VALUE tmp;
8193  WCHAR *ptr;
8194  int len;
8195  VALUE v;
8196 
8197  FilePathValue(*file);
8198  tmp = rb_str_encode_ospath(*file);
8199  len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
8200  ptr = ALLOCV_N(WCHAR, v, len);
8201  MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
8202  f = CreateFileW(ptr, 0,
8203  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
8204  FILE_FLAG_BACKUP_SEMANTICS, NULL);
8205  ALLOCV_END(v);
8206  if (f == INVALID_HANDLE_VALUE) return f;
8207  ret = f;
8208  }
8209  if (GetFileType(f) == FILE_TYPE_DISK) {
8210  ZeroMemory(st, sizeof(*st));
8211  if (get_ino(f, &st->info.fii)) {
8212  st->file_id_p = TRUE;
8213  return ret;
8214  }
8215  else if (GetLastError() != ERROR_INVALID_PARAMETER) {
8216  CloseHandle(f);
8217  return INVALID_HANDLE_VALUE;
8218  }
8219  /* this API may not work at files on non Microsoft SMB
8220  * server, fallback to old API then. */
8221  if (GetFileInformationByHandle(f, &st->info.bhfi)) {
8222  st->file_id_p = FALSE;
8223  return ret;
8224  }
8225  }
8226  if (ret) CloseHandle(ret);
8227  return INVALID_HANDLE_VALUE;
8228 }
8229 
8230 static VALUE
8231 close_handle(VALUE h)
8232 {
8233  CloseHandle((HANDLE)h);
8234  return Qfalse;
8235 }
8236 
8238  VALUE *fname;
8239  w32_io_info_t *st;
8240 };
8241 
8242 static VALUE
8243 call_w32_io_info(VALUE arg)
8244 {
8245  struct w32_io_info_args *p = (void *)arg;
8246  return (VALUE)w32_io_info(p->fname, p->st);
8247 }
8248 
8249 VALUE
8250 rb_w32_file_identical_p(VALUE fname1, VALUE fname2)
8251 {
8252  w32_io_info_t st1, st2;
8253  HANDLE f1 = 0, f2 = 0;
8254 
8255  f1 = w32_io_info(&fname1, &st1);
8256  if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
8257  if (f1) {
8258  struct w32_io_info_args arg;
8259  arg.fname = &fname2;
8260  arg.st = &st2;
8261  f2 = (HANDLE)rb_ensure(call_w32_io_info, (VALUE)&arg, close_handle, (VALUE)f1);
8262  }
8263  else {
8264  f2 = w32_io_info(&fname2, &st2);
8265  }
8266  if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
8267  if (f2) CloseHandle(f2);
8268 
8269  if (st1.file_id_p != st2.file_id_p) return Qfalse;
8270  if (!st1.file_id_p) {
8271  if (st1.info.bhfi.dwVolumeSerialNumber == st2.info.bhfi.dwVolumeSerialNumber &&
8272  st1.info.bhfi.nFileIndexHigh == st2.info.bhfi.nFileIndexHigh &&
8273  st1.info.bhfi.nFileIndexLow == st2.info.bhfi.nFileIndexLow)
8274  return Qtrue;
8275  }
8276  else {
8277  if (st1.info.fii.VolumeSerialNumber == st2.info.fii.VolumeSerialNumber &&
8278  memcmp(&st1.info.fii.FileId, &st2.info.fii.FileId, sizeof(FILE_ID_128)) == 0)
8279  return Qtrue;
8280  }
8281  return Qfalse;
8282 }
8283 
8284 int
8285 rb_w32_set_thread_description(HANDLE th, const WCHAR *name)
8286 {
8287  int result = FALSE;
8288  typedef HRESULT (WINAPI *set_thread_description_func)(HANDLE, PCWSTR);
8289  static set_thread_description_func set_thread_description =
8290  (set_thread_description_func)-1;
8291  if (set_thread_description == (set_thread_description_func)-1) {
8292  /* Since Windows 10, version 1607 and Windows Server 2016 */
8293  set_thread_description = (set_thread_description_func)
8294  get_proc_address("kernel32", "SetThreadDescription", NULL);
8295  }
8296  if (set_thread_description) {
8297  result = set_thread_description(th, name);
8298  }
8299  return result;
8300 }
8301 
8302 int
8303 rb_w32_set_thread_description_str(HANDLE th, VALUE name)
8304 {
8305  int idx, result = FALSE;
8306  WCHAR *s;
8307 
8308  if (NIL_P(name)) {
8309  return rb_w32_set_thread_description(th, L"");
8310  }
8311  s = (WCHAR *)StringValueCStr(name);
8312  idx = rb_enc_get_index(name);
8313  if (idx == ENCINDEX_UTF_16LE) {
8314  result = rb_w32_set_thread_description(th, s);
8315  }
8316  else {
8317  name = rb_str_conv_enc(name, rb_enc_from_index(idx), rb_utf8_encoding());
8318  s = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(name), RSTRING_LEN(name)+1, NULL);
8319  result = rb_w32_set_thread_description(th, s);
8320  free(s);
8321  }
8322  RB_GC_GUARD(name);
8323  return result;
8324 }
8325 
8326 VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notimplement;
8327 
8328 #if RUBY_MSVCRT_VERSION < 120
8329 #include "missing/nextafter.c"
8330 #endif
8331 
8332 void *
8333 rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset)
8334 {
8335  void *ptr;
8336  //DWORD protect = 0;
8337  DWORD protect = PAGE_EXECUTE_READWRITE;
8338 
8339  if (fd > 0 || offset) {
8340  /* not supported */
8341  errno = EINVAL;
8342  return MAP_FAILED;
8343  }
8344 
8345 /*
8346  if (prot & PROT_EXEC) {
8347  if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8348  else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8349  else protect = PAGE_EXECUTE;
8350  }
8351  else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8352  else if (prot & PROT_READ) protect = PAGE_READONLY;
8353 */
8354  ptr = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, protect);
8355  if (!ptr) {
8356  errno = rb_w32_map_errno(GetLastError());
8357  return MAP_FAILED;
8358  }
8359 
8360  return ptr;
8361 }
8362 
8363 int
8364 rb_w32_munmap(void *addr, size_t len)
8365 {
8366  if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8367  errno = rb_w32_map_errno(GetLastError());
8368  return -1;
8369  }
8370 
8371  return 0;
8372 }
8373 
8374 inline int
8375 rb_w32_mprotect(void *addr, size_t len, int prot)
8376 {
8377 /*
8378  DWORD protect = 0;
8379  if (prot & PROT_EXEC) {
8380  if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8381  else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8382  else protect = PAGE_EXECUTE;
8383  }
8384  else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8385  else if (prot & PROT_READ) protect = PAGE_READONLY;
8386  if (!VirtualProtect(addr, len, protect, NULL)) {
8387  errno = rb_w32_map_errno(GetLastError());
8388  return -1;
8389  }
8390 */
8391  if (prot & PROT_EXEC) {
8392  if (!FlushInstructionCache(GetCurrentProcess(), addr, len)) {
8393  errno = rb_w32_map_errno(GetLastError());
8394  return -1;
8395  }
8396  }
8397  return 0;
8398 }
#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:3687
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:3443
#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:878
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:2997
void rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
Destructively overwrites an fdset with another.
Definition: win32.c:2982
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:3716
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:4472
void ruby_xfree(void *ptr)
Deallocates a storage instance.
Definition: gc.c:4594
void * ruby_xcalloc(size_t nelems, size_t elemsiz)
Identical to ruby_xmalloc2(), except it returns a zero-filled storage instance.
Definition: gc.c:4512