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