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