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