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