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