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