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