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