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