Ruby 3.4.3p32 (2025-04-14 revision d0b7e5b6a04bde21ca483d20a1546b28b401c2d4)
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/* License: Ruby's */
4794int
4795clock_gettime(clockid_t clock_id, struct timespec *sp)
4796{
4797 switch (clock_id) {
4798 case CLOCK_REALTIME:
4799 {
4800 FILETIME ft;
4801 long subsec;
4802
4803 GetSystemTimePreciseAsFileTime(&ft);
4804 sp->tv_sec = filetime_split(&ft, &subsec);
4805 sp->tv_nsec = subsec * 100;
4806 return 0;
4807 }
4808 case CLOCK_MONOTONIC:
4809 {
4810 LARGE_INTEGER freq;
4811 LARGE_INTEGER count;
4812 if (!QueryPerformanceFrequency(&freq)) {
4813 errno = map_errno(GetLastError());
4814 return -1;
4815 }
4816 if (!QueryPerformanceCounter(&count)) {
4817 errno = map_errno(GetLastError());
4818 return -1;
4819 }
4820 sp->tv_sec = count.QuadPart / freq.QuadPart;
4821 if (freq.QuadPart < 1000000000)
4822 sp->tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
4823 else
4824 sp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
4825 return 0;
4826 }
4827 default:
4828 errno = EINVAL;
4829 return -1;
4830 }
4831}
4832
4833/* License: Ruby's */
4834int
4835clock_getres(clockid_t clock_id, struct timespec *sp)
4836{
4837 switch (clock_id) {
4838 case CLOCK_REALTIME:
4839 {
4840 sp->tv_sec = 0;
4841 sp->tv_nsec = 1000;
4842 return 0;
4843 }
4844 case CLOCK_MONOTONIC:
4845 {
4846 LARGE_INTEGER freq;
4847 if (!QueryPerformanceFrequency(&freq)) {
4848 errno = map_errno(GetLastError());
4849 return -1;
4850 }
4851 sp->tv_sec = 0;
4852 sp->tv_nsec = (long)(1000000000.0 / freq.QuadPart);
4853 return 0;
4854 }
4855 default:
4856 errno = EINVAL;
4857 return -1;
4858 }
4859}
4860
4861/* License: Ruby's */
4862static char *
4863w32_getcwd(char *buffer, int size, UINT cp, void *alloc(int, void *), void *arg)
4864{
4865 WCHAR *p;
4866 int wlen, len;
4867
4868 len = GetCurrentDirectoryW(0, NULL);
4869 if (!len) {
4870 errno = map_errno(GetLastError());
4871 return NULL;
4872 }
4873
4874 if (buffer && size < len) {
4875 errno = ERANGE;
4876 return NULL;
4877 }
4878
4879 p = ALLOCA_N(WCHAR, len);
4880 if (!GetCurrentDirectoryW(len, p)) {
4881 errno = map_errno(GetLastError());
4882 return NULL;
4883 }
4884
4885 wlen = translate_wchar(p, L'\\', L'/') - p + 1;
4886 len = WideCharToMultiByte(cp, 0, p, wlen, NULL, 0, NULL, NULL);
4887 if (buffer) {
4888 if (size < len) {
4889 errno = ERANGE;
4890 return NULL;
4891 }
4892 }
4893 else {
4894 buffer = (*alloc)(len, arg);
4895 if (!buffer) {
4896 errno = ENOMEM;
4897 return NULL;
4898 }
4899 }
4900 WideCharToMultiByte(cp, 0, p, wlen, buffer, len, NULL, NULL);
4901
4902 return buffer;
4903}
4904
4905/* License: Ruby's */
4906static void *
4907getcwd_alloc(int size, void *dummy)
4908{
4909 return malloc(size);
4910}
4911
4912/* License: Ruby's */
4913char *
4914rb_w32_getcwd(char *buffer, int size)
4915{
4916 return w32_getcwd(buffer, size, filecp(), getcwd_alloc, NULL);
4917}
4918
4919/* License: Ruby's */
4920char *
4921rb_w32_ugetcwd(char *buffer, int size)
4922{
4923 return w32_getcwd(buffer, size, CP_UTF8, getcwd_alloc, NULL);
4924}
4925
4926/* License: Ruby's */
4927static void *
4928getcwd_value(int size, void *arg)
4929{
4930 VALUE str = *(VALUE *)arg = rb_utf8_str_new(0, size - 1);
4931 return RSTRING_PTR(str);
4932}
4933
4934/* License: Ruby's */
4935VALUE
4936rb_dir_getwd_ospath(void)
4937{
4938 VALUE cwd = Qnil;
4939 w32_getcwd(NULL, 0, CP_UTF8, getcwd_value, &cwd);
4940 return cwd;
4941}
4942
4943/* License: Artistic or GPL */
4944int
4945chown(const char *path, int owner, int group)
4946{
4947 return 0;
4948}
4949
4950/* License: Artistic or GPL */
4951int
4952rb_w32_uchown(const char *path, int owner, int group)
4953{
4954 return 0;
4955}
4956
4957int
4958lchown(const char *path, int owner, int group)
4959{
4960 return 0;
4961}
4962
4963int
4964rb_w32_ulchown(const char *path, int owner, int group)
4965{
4966 return 0;
4967}
4968
4969/* License: Ruby's */
4970int
4971kill(rb_pid_t pid, int sig)
4972{
4973 int ret = 0;
4974 DWORD err;
4975
4976 if (pid < 0 || (pid == 0 && sig != SIGINT)) {
4977 errno = EINVAL;
4978 return -1;
4979 }
4980
4981 if ((unsigned int)pid == GetCurrentProcessId() &&
4982 (sig != 0 && sig != SIGKILL)) {
4983 if ((ret = raise(sig)) != 0) {
4984 /* MSVCRT doesn't set errno... */
4985 errno = EINVAL;
4986 }
4987 return ret;
4988 }
4989
4990 switch (sig) {
4991 case 0:
4992 RUBY_CRITICAL {
4993 HANDLE hProc =
4994 OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4995 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4996 if (GetLastError() == ERROR_INVALID_PARAMETER) {
4997 errno = ESRCH;
4998 }
4999 else {
5000 errno = EPERM;
5001 }
5002 ret = -1;
5003 }
5004 else {
5005 CloseHandle(hProc);
5006 }
5007 }
5008 break;
5009
5010 case SIGINT:
5011 RUBY_CRITICAL {
5012 DWORD ctrlEvent = CTRL_C_EVENT;
5013 if (pid != 0) {
5014 /* CTRL+C signal cannot be generated for process groups.
5015 * Instead, we use CTRL+BREAK signal. */
5016 ctrlEvent = CTRL_BREAK_EVENT;
5017 }
5018 if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
5019 if ((err = GetLastError()) == 0)
5020 errno = EPERM;
5021 else
5022 errno = map_errno(GetLastError());
5023 ret = -1;
5024 }
5025 }
5026 break;
5027
5028 case SIGKILL:
5029 RUBY_CRITICAL {
5030 HANDLE hProc;
5031 struct ChildRecord* child = FindChildSlot(pid);
5032 if (child) {
5033 hProc = child->hProcess;
5034 }
5035 else {
5036 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
5037 }
5038 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
5039 if (GetLastError() == ERROR_INVALID_PARAMETER) {
5040 errno = ESRCH;
5041 }
5042 else {
5043 errno = EPERM;
5044 }
5045 ret = -1;
5046 }
5047 else {
5048 DWORD status;
5049 if (!GetExitCodeProcess(hProc, &status)) {
5050 errno = map_errno(GetLastError());
5051 ret = -1;
5052 }
5053 else if (status == STILL_ACTIVE) {
5054 if (!TerminateProcess(hProc, 0)) {
5055 errno = EPERM;
5056 ret = -1;
5057 }
5058 }
5059 else {
5060 errno = ESRCH;
5061 ret = -1;
5062 }
5063 if (!child) {
5064 CloseHandle(hProc);
5065 }
5066 }
5067 }
5068 break;
5069
5070 default:
5071 errno = EINVAL;
5072 ret = -1;
5073 break;
5074 }
5075
5076 return ret;
5077}
5078
5079/* License: Ruby's */
5080static int
5081wlink(const WCHAR *from, const WCHAR *to)
5082{
5083 if (!CreateHardLinkW(to, from, NULL)) {
5084 errno = map_errno(GetLastError());
5085 return -1;
5086 }
5087
5088 return 0;
5089}
5090
5091/* License: Ruby's */
5092int
5093rb_w32_ulink(const char *from, const char *to)
5094{
5095 WCHAR *wfrom;
5096 WCHAR *wto;
5097 int ret;
5098
5099 if (!(wfrom = utf8_to_wstr(from, NULL)))
5100 return -1;
5101 if (!(wto = utf8_to_wstr(to, NULL))) {
5102 free(wfrom);
5103 return -1;
5104 }
5105 ret = wlink(wfrom, wto);
5106 free(wto);
5107 free(wfrom);
5108 return ret;
5109}
5110
5111/* License: Ruby's */
5112int
5113link(const char *from, const char *to)
5114{
5115 WCHAR *wfrom;
5116 WCHAR *wto;
5117 int ret;
5118
5119 if (!(wfrom = filecp_to_wstr(from, NULL)))
5120 return -1;
5121 if (!(wto = filecp_to_wstr(to, NULL))) {
5122 free(wfrom);
5123 return -1;
5124 }
5125 ret = wlink(wfrom, wto);
5126 free(wto);
5127 free(wfrom);
5128 return ret;
5129}
5130
5131/* License: Public Domain, copied from mingw headers */
5132#ifndef FILE_DEVICE_FILE_SYSTEM
5133# define FILE_DEVICE_FILE_SYSTEM 0x00000009
5134#endif
5135#ifndef FSCTL_GET_REPARSE_POINT
5136# define FSCTL_GET_REPARSE_POINT ((0x9<<16)|(42<<2))
5137#endif
5138#ifndef IO_REPARSE_TAG_SYMLINK
5139# define IO_REPARSE_TAG_SYMLINK 0xA000000CL
5140#endif
5141
5142/* License: Ruby's */
5143static int
5144reparse_symlink(const WCHAR *path, rb_w32_reparse_buffer_t *rp, size_t size)
5145{
5146 HANDLE f;
5147 DWORD ret;
5148 int e = 0;
5149
5150 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5151 if (f == INVALID_HANDLE_VALUE) {
5152 return GetLastError();
5153 }
5154
5155 if (!DeviceIoControl(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
5156 rp, size, &ret, NULL)) {
5157 e = GetLastError();
5158 }
5159 else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
5160 rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
5161 e = ERROR_INVALID_PARAMETER;
5162 }
5163 CloseHandle(f);
5164 return e;
5165}
5166
5167/* License: Ruby's */
5168int
5169rb_w32_reparse_symlink_p(const WCHAR *path)
5170{
5171 VALUE wtmp = 0;
5172 rb_w32_reparse_buffer_t rbuf, *rp = &rbuf;
5173 WCHAR *wbuf;
5174 DWORD len;
5175 int e;
5176
5177 e = rb_w32_read_reparse_point(path, rp, sizeof(rbuf), &wbuf, &len);
5178 if (e == ERROR_MORE_DATA) {
5179 size_t size = rb_w32_reparse_buffer_size(len + 1);
5180 rp = ALLOCV(wtmp, size);
5181 e = rb_w32_read_reparse_point(path, rp, size, &wbuf, &len);
5182 ALLOCV_END(wtmp);
5183 }
5184 switch (e) {
5185 case 0:
5186 case ERROR_MORE_DATA:
5187 return TRUE;
5188 }
5189 return FALSE;
5190}
5191
5192/* License: Ruby's */
5193int
5194rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
5195 size_t bufsize, WCHAR **result, DWORD *len)
5196{
5197 int e = reparse_symlink(path, rp, bufsize);
5198 DWORD ret = 0;
5199
5200 if (!e || e == ERROR_MORE_DATA) {
5201 void *name;
5202 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
5203 name = ((char *)rp->SymbolicLinkReparseBuffer.PathBuffer +
5204 rp->SymbolicLinkReparseBuffer.PrintNameOffset);
5205 ret = rp->SymbolicLinkReparseBuffer.PrintNameLength;
5206 *len = ret / sizeof(WCHAR);
5207 }
5208 else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
5209 static const WCHAR volume[] = L"Volume{";
5210 enum {volume_prefix_len = rb_strlen_lit("\\??\\")};
5211 name = ((char *)rp->MountPointReparseBuffer.PathBuffer +
5212 rp->MountPointReparseBuffer.SubstituteNameOffset +
5213 volume_prefix_len * sizeof(WCHAR));
5214 ret = rp->MountPointReparseBuffer.SubstituteNameLength;
5215 *len = ret / sizeof(WCHAR);
5216 ret -= volume_prefix_len * sizeof(WCHAR);
5217 if (ret > sizeof(volume) - 1 * sizeof(WCHAR) &&
5218 memcmp(name, volume, sizeof(volume) - 1 * sizeof(WCHAR)) == 0)
5219 return -1;
5220 }
5221 else {
5222 return -1;
5223 }
5224 *result = name;
5225 if (e) {
5226 if ((char *)name + ret + sizeof(WCHAR) > (char *)rp + bufsize)
5227 return e;
5228 /* SubstituteName is not used */
5229 }
5230 ((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0';
5231 translate_wchar(name, L'\\', L'/');
5232 return 0;
5233 }
5234 else {
5235 return e;
5236 }
5237}
5238
5239/* License: Ruby's */
5240static ssize_t
5241w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize)
5242{
5243 VALUE rp_buf, rp_buf_bigger = 0;
5244 DWORD len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0);
5245 size_t size = rb_w32_reparse_buffer_size(bufsize);
5246 WCHAR *wname;
5247 WCHAR *wpath = ALLOCV(rp_buf, sizeof(WCHAR) * len + size);
5248 rb_w32_reparse_buffer_t *rp = (void *)(wpath + len);
5249 ssize_t ret;
5250 int e;
5251
5252 MultiByteToWideChar(cp, 0, path, -1, wpath, len);
5253 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5254 if (e == ERROR_MORE_DATA) {
5255 size = rb_w32_reparse_buffer_size(len + 1);
5256 rp = ALLOCV(rp_buf_bigger, size);
5257 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5258 }
5259 if (e) {
5260 ALLOCV_END(rp_buf);
5261 ALLOCV_END(rp_buf_bigger);
5262 errno = e == -1 ? EINVAL : map_errno(e);
5263 return -1;
5264 }
5265 len = lstrlenW(wname);
5266 ret = WideCharToMultiByte(cp, 0, wname, len, buf, bufsize, NULL, NULL);
5267 ALLOCV_END(rp_buf);
5268 ALLOCV_END(rp_buf_bigger);
5269 if (!ret) {
5270 ret = bufsize;
5271 }
5272 return ret;
5273}
5274
5275/* License: Ruby's */
5276ssize_t
5277rb_w32_ureadlink(const char *path, char *buf, size_t bufsize)
5278{
5279 return w32_readlink(CP_UTF8, path, buf, bufsize);
5280}
5281
5282/* License: Ruby's */
5283ssize_t
5284readlink(const char *path, char *buf, size_t bufsize)
5285{
5286 return w32_readlink(filecp(), path, buf, bufsize);
5287}
5288
5289#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
5290#define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
5291#endif
5292#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
5293#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
5294#endif
5295
5296/* License: Ruby's */
5297static int
5298w32_symlink(UINT cp, const char *src, const char *link)
5299{
5300 int atts, len1, len2;
5301 VALUE buf;
5302 WCHAR *wsrc, *wlink;
5303 DWORD flag = 0;
5304 BOOLEAN ret;
5305 int e;
5306
5307 static DWORD create_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5308
5309 if (!*link) {
5310 errno = ENOENT;
5311 return -1;
5312 }
5313 if (!*src) {
5314 errno = EINVAL;
5315 return -1;
5316 }
5317 len1 = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
5318 len2 = MultiByteToWideChar(cp, 0, link, -1, NULL, 0);
5319 wsrc = ALLOCV_N(WCHAR, buf, len1+len2);
5320 wlink = wsrc + len1;
5321 MultiByteToWideChar(cp, 0, src, -1, wsrc, len1);
5322 MultiByteToWideChar(cp, 0, link, -1, wlink, len2);
5323 translate_wchar(wsrc, L'/', L'\\');
5324
5325 atts = GetFileAttributesW(wsrc);
5326 if (atts != -1 && atts & FILE_ATTRIBUTE_DIRECTORY)
5327 flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
5328 ret = CreateSymbolicLinkW(wlink, wsrc, flag |= create_flag);
5329 if (!ret &&
5330 (e = GetLastError()) == ERROR_INVALID_PARAMETER &&
5331 (flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
5332 create_flag = 0;
5333 flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5334 ret = CreateSymbolicLinkW(wlink, wsrc, flag);
5335 if (!ret) e = GetLastError();
5336 }
5337 ALLOCV_END(buf);
5338
5339 if (!ret) {
5340 errno = map_errno(e);
5341 return -1;
5342 }
5343 return 0;
5344}
5345
5346/* License: Ruby's */
5347int
5348rb_w32_usymlink(const char *src, const char *link)
5349{
5350 return w32_symlink(CP_UTF8, src, link);
5351}
5352
5353/* License: Ruby's */
5354int
5355symlink(const char *src, const char *link)
5356{
5357 return w32_symlink(filecp(), src, link);
5358}
5359
5360/* License: Ruby's */
5361rb_pid_t
5362wait(int *status)
5363{
5364 return waitpid(-1, status, 0);
5365}
5366
5367/* License: Ruby's */
5368static char *
5369w32_getenv(const char *name, UINT cp)
5370{
5371 WCHAR *wenvarea, *wenv;
5372 int len = strlen(name);
5373 char *env, *found = NULL;
5374 int wlen;
5375
5376 if (len == 0) return NULL;
5377
5378 if (!NTLoginName) {
5379 /* initialized in init_env, uenvarea_mutex should have been
5380 * initialized before it */
5381 return getenv(name);
5382 }
5383
5384 thread_exclusive(uenvarea) {
5385 if (uenvarea) {
5386 free(uenvarea);
5387 uenvarea = NULL;
5388 }
5389 wenvarea = GetEnvironmentStringsW();
5390 if (!wenvarea) {
5391 map_errno(GetLastError());
5392 continue;
5393 }
5394 for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
5395 wlen += lstrlenW(wenv) + 1;
5396 uenvarea = wstr_to_mbstr(cp, wenvarea, wlen, NULL);
5397 FreeEnvironmentStringsW(wenvarea);
5398 if (!uenvarea)
5399 continue;
5400
5401 for (env = uenvarea; *env; env += strlen(env) + 1) {
5402 if (strncasecmp(env, name, len) == 0 && *(env + len) == '=') {
5403 found = env + len + 1;
5404 break;
5405 }
5406 }
5407 }
5408
5409 return found;
5410}
5411
5412/* License: Ruby's */
5413char *
5414rb_w32_ugetenv(const char *name)
5415{
5416 return w32_getenv(name, CP_UTF8);
5417}
5418
5419/* License: Ruby's */
5420char *
5421rb_w32_getenv(const char *name)
5422{
5423 return w32_getenv(name, CP_ACP);
5424}
5425
5426/* License: Ruby's */
5427static DWORD
5428get_attr_vsn(const WCHAR *path, DWORD *atts, DWORD *vsn)
5429{
5430 BY_HANDLE_FILE_INFORMATION st = {0};
5431 DWORD e = 0;
5432 HANDLE h = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5433
5434 if (h == INVALID_HANDLE_VALUE) {
5435 e = GetLastError();
5436 ASSUME(e);
5437 return e;
5438 }
5439 if (!GetFileInformationByHandle(h, &st)) {
5440 e = GetLastError();
5441 ASSUME(e);
5442 }
5443 else {
5444 *atts = st.dwFileAttributes;
5445 *vsn = st.dwVolumeSerialNumber;
5446 }
5447 CloseHandle(h);
5448 return e;
5449}
5450
5451/* License: Artistic or GPL */
5452static int
5453wrename(const WCHAR *oldpath, const WCHAR *newpath)
5454{
5455 int res = 0;
5456 DWORD oldatts = 0, newatts = (DWORD)-1;
5457 DWORD oldvsn = 0, newvsn = 0, e;
5458
5459 e = get_attr_vsn(oldpath, &oldatts, &oldvsn);
5460 if (e) {
5461 errno = map_errno(e);
5462 return -1;
5463 }
5464 if (oldatts & FILE_ATTRIBUTE_REPARSE_POINT) {
5465 HANDLE fh = open_special(oldpath, 0, 0);
5466 if (fh == INVALID_HANDLE_VALUE) {
5467 e = GetLastError();
5468 if (e == ERROR_CANT_RESOLVE_FILENAME) {
5469 errno = ELOOP;
5470 return -1;
5471 }
5472 }
5473 CloseHandle(fh);
5474 }
5475 get_attr_vsn(newpath, &newatts, &newvsn);
5476
5477 RUBY_CRITICAL {
5478 if (newatts != (DWORD)-1 && newatts & FILE_ATTRIBUTE_READONLY)
5479 SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
5480
5481 if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
5482 res = -1;
5483
5484 if (res) {
5485 DWORD e = GetLastError();
5486 if ((e == ERROR_ACCESS_DENIED) && (oldatts & FILE_ATTRIBUTE_DIRECTORY) &&
5487 oldvsn != newvsn)
5488 errno = EXDEV;
5489 else
5490 errno = map_errno(e);
5491 }
5492 else
5493 SetFileAttributesW(newpath, oldatts);
5494 }
5495
5496 return res;
5497}
5498
5499/* License: Ruby's */
5500int
5501rb_w32_urename(const char *from, const char *to)
5502{
5503 WCHAR *wfrom;
5504 WCHAR *wto;
5505 int ret = -1;
5506
5507 if (!(wfrom = utf8_to_wstr(from, NULL)))
5508 return -1;
5509 if (!(wto = utf8_to_wstr(to, NULL))) {
5510 free(wfrom);
5511 return -1;
5512 }
5513 ret = wrename(wfrom, wto);
5514 free(wto);
5515 free(wfrom);
5516 return ret;
5517}
5518
5519/* License: Ruby's */
5520int
5521rb_w32_rename(const char *from, const char *to)
5522{
5523 WCHAR *wfrom;
5524 WCHAR *wto;
5525 int ret = -1;
5526
5527 if (!(wfrom = filecp_to_wstr(from, NULL)))
5528 return -1;
5529 if (!(wto = filecp_to_wstr(to, NULL))) {
5530 free(wfrom);
5531 return -1;
5532 }
5533 ret = wrename(wfrom, wto);
5534 free(wto);
5535 free(wfrom);
5536 return ret;
5537}
5538
5539/* License: Ruby's */
5540static int
5541isUNCRoot(const WCHAR *path)
5542{
5543 if (path[0] == L'\\' && path[1] == L'\\') {
5544 const WCHAR *p = path + 2;
5545 if (p[0] == L'?' && p[1] == L'\\') {
5546 p += 2;
5547 }
5548 for (; *p; p++) {
5549 if (*p == L'\\')
5550 break;
5551 }
5552 if (p[0] && p[1]) {
5553 for (p++; *p; p++) {
5554 if (*p == L'\\')
5555 break;
5556 }
5557 if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
5558 return 1;
5559 }
5560 }
5561 return 0;
5562}
5563
5564#define COPY_STAT(src, dest, size_cast) do { \
5565 (dest).st_dev = (src).st_dev; \
5566 (dest).st_ino = (src).st_ino; \
5567 (dest).st_mode = (src).st_mode; \
5568 (dest).st_nlink = (src).st_nlink; \
5569 (dest).st_uid = (src).st_uid; \
5570 (dest).st_gid = (src).st_gid; \
5571 (dest).st_rdev = (src).st_rdev; \
5572 (dest).st_size = size_cast(src).st_size; \
5573 (dest).st_atime = (src).st_atime; \
5574 (dest).st_mtime = (src).st_mtime; \
5575 (dest).st_ctime = (src).st_ctime; \
5576 } while (0)
5577
5578static time_t filetime_to_unixtime(const FILETIME *ft);
5579static long filetime_to_nsec(const FILETIME *ft);
5580static WCHAR *name_for_stat(WCHAR *buf, const WCHAR *path);
5581static DWORD stati128_handle(HANDLE h, struct stati128 *st);
5582
5583#undef fstat
5584/* License: Ruby's */
5585int
5586rb_w32_fstat(int fd, struct stat *st)
5587{
5588 BY_HANDLE_FILE_INFORMATION info;
5589 int ret = fstat(fd, st);
5590
5591 if (ret) return ret;
5592 if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) return ret;
5593 if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
5594 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5595 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5596 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5597 }
5598 return ret;
5599}
5600
5601/* License: Ruby's */
5602int
5603rb_w32_fstati128(int fd, struct stati128 *st)
5604{
5605 struct stat tmp;
5606 int ret = fstat(fd, &tmp);
5607
5608 if (ret) return ret;
5609 COPY_STAT(tmp, *st, +);
5610 stati128_handle((HANDLE)_get_osfhandle(fd), st);
5611 return ret;
5612}
5613
5614#if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5615typedef struct {
5616 BYTE Identifier[16];
5617} FILE_ID_128;
5618#endif
5619
5620#if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5621#define FileIdInfo 0x12
5622
5623typedef struct {
5624 unsigned LONG_LONG VolumeSerialNumber;
5625 FILE_ID_128 FileId;
5626} FILE_ID_INFO;
5627#endif
5628
5629static BOOL
5630get_ino(HANDLE h, FILE_ID_INFO *id)
5631{
5632 return GetFileInformationByHandleEx(h, FileIdInfo, id, sizeof(*id));
5633}
5634
5635/* License: Ruby's */
5636static DWORD
5637stati128_handle(HANDLE h, struct stati128 *st)
5638{
5639 BY_HANDLE_FILE_INFORMATION info;
5640 DWORD attr = (DWORD)-1;
5641
5642 if (GetFileInformationByHandle(h, &info)) {
5643 FILE_ID_INFO fii;
5644 st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
5645 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5646 st->st_atimensec = filetime_to_nsec(&info.ftLastAccessTime);
5647 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5648 st->st_mtimensec = filetime_to_nsec(&info.ftLastWriteTime);
5649 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5650 st->st_ctimensec = filetime_to_nsec(&info.ftCreationTime);
5651 st->st_nlink = info.nNumberOfLinks;
5652 attr = info.dwFileAttributes;
5653 if (get_ino(h, &fii)) {
5654 st->st_ino = *((unsigned __int64 *)&fii.FileId);
5655 st->st_inohigh = *((__int64 *)&fii.FileId + 1);
5656 }
5657 else {
5658 st->st_ino = ((__int64)info.nFileIndexHigh << 32) | info.nFileIndexLow;
5659 st->st_inohigh = 0;
5660 }
5661 }
5662 return attr;
5663}
5664
5665/* License: Ruby's */
5666static time_t
5667filetime_to_unixtime(const FILETIME *ft)
5668{
5669 long subsec;
5670 time_t t = filetime_split(ft, &subsec);
5671
5672 if (t < 0) return 0;
5673 return t;
5674}
5675
5676/* License: Ruby's */
5677static long
5678filetime_to_nsec(const FILETIME *ft)
5679{
5680 ULARGE_INTEGER tmp;
5681 tmp.LowPart = ft->dwLowDateTime;
5682 tmp.HighPart = ft->dwHighDateTime;
5683 return (long)(tmp.QuadPart % 10000000) * 100;
5684}
5685
5686/* License: Ruby's */
5687static unsigned
5688fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode)
5689{
5690 if (attr & FILE_ATTRIBUTE_READONLY) {
5691 mode |= S_IREAD;
5692 }
5693 else {
5694 mode |= S_IREAD | S_IWRITE | S_IWUSR;
5695 }
5696
5697 if (mode & S_IFMT) {
5698 /* format is already set */
5699 }
5700 else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5701 /* Only used by stat_by_find in the case the file can not be opened.
5702 * In this case we can't get more details. */
5703 }
5704 else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5705 mode |= S_IFDIR | S_IEXEC;
5706 }
5707 else {
5708 mode |= S_IFREG;
5709 }
5710
5711 if (path && (mode & S_IFREG)) {
5712 const WCHAR *end = path + lstrlenW(path);
5713 while (path < end) {
5714 end = CharPrevW(path, end);
5715 if (*end == L'.') {
5716 if ((_wcsicmp(end, L".bat") == 0) ||
5717 (_wcsicmp(end, L".cmd") == 0) ||
5718 (_wcsicmp(end, L".com") == 0) ||
5719 (_wcsicmp(end, L".exe") == 0)) {
5720 mode |= S_IEXEC;
5721 }
5722 break;
5723 }
5724 if (!iswalnum(*end)) break;
5725 }
5726 }
5727
5728 mode |= (mode & 0500) >> 3;
5729 mode |= (mode & 0500) >> 6;
5730
5731 return mode;
5732}
5733
5734/* License: Ruby's */
5735static int
5736check_valid_dir(const WCHAR *path)
5737{
5738 WIN32_FIND_DATAW fd;
5739 HANDLE fh;
5740 WCHAR full[PATH_MAX];
5741 WCHAR *dmy;
5742 WCHAR *p, *q;
5743
5744 /* GetFileAttributes() determines "..." as directory. */
5745 /* We recheck it by FindFirstFile(). */
5746 if (!(p = wcsstr(path, L"...")))
5747 return 0;
5748 q = p + wcsspn(p, L".");
5749 if ((p == path || wcschr(L":/\\", *(p - 1))) &&
5750 (!*q || wcschr(L":/\\", *q))) {
5751 errno = ENOENT;
5752 return -1;
5753 }
5754
5755 /* if the specified path is the root of a drive and the drive is empty, */
5756 /* FindFirstFile() returns INVALID_HANDLE_VALUE. */
5757 if (!GetFullPathNameW(path, sizeof(full) / sizeof(WCHAR), full, &dmy)) {
5758 errno = map_errno(GetLastError());
5759 return -1;
5760 }
5761 if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5762 return 0;
5763
5764 fh = open_dir_handle(path, &fd);
5765 if (fh == INVALID_HANDLE_VALUE)
5766 return -1;
5767 FindClose(fh);
5768 return 0;
5769}
5770
5771/* License: Ruby's */
5772static int
5773stat_by_find(const WCHAR *path, struct stati128 *st)
5774{
5775 HANDLE h;
5776 WIN32_FIND_DATAW wfd;
5777
5778 /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
5779 h = FindFirstFileW(path, &wfd);
5780 if (h == INVALID_HANDLE_VALUE) {
5781 errno = map_errno(GetLastError());
5782 return -1;
5783 }
5784 FindClose(h);
5785 st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path, 0);
5786 st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
5787 st->st_atimensec = filetime_to_nsec(&wfd.ftLastAccessTime);
5788 st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
5789 st->st_mtimensec = filetime_to_nsec(&wfd.ftLastWriteTime);
5790 st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
5791 st->st_ctimensec = filetime_to_nsec(&wfd.ftCreationTime);
5792 st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
5793 st->st_nlink = 1;
5794 return 0;
5795}
5796
5797/* License: Ruby's */
5798static int
5799path_drive(const WCHAR *path)
5800{
5801 return (iswalpha(path[0]) && path[1] == L':') ?
5802 towupper(path[0]) - L'A' : _getdrive() - 1;
5803}
5804
5805/* License: Ruby's */
5806static int
5807winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
5808{
5809 DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
5810 HANDLE f;
5811 WCHAR finalname[PATH_MAX];
5812 int open_error;
5813
5814 memset(st, 0, sizeof(*st));
5815 f = open_special(path, 0, flags);
5816 open_error = GetLastError();
5817 if (f == INVALID_HANDLE_VALUE && !lstat) {
5818 /* Support stat (not only lstat) of UNIXSocket */
5819 FILE_ATTRIBUTE_TAG_INFO attr_info;
5820 DWORD e;
5821
5822 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5823 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5824 &attr_info, sizeof(attr_info));
5825 if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
5826 CloseHandle(f);
5827 f = INVALID_HANDLE_VALUE;
5828 }
5829 }
5830 if (f != INVALID_HANDLE_VALUE) {
5831 DWORD attr = stati128_handle(f, st);
5832 const DWORD len = GetFinalPathNameByHandleW(f, finalname, numberof(finalname), 0);
5833 unsigned mode = 0;
5834 switch (GetFileType(f)) {
5835 case FILE_TYPE_CHAR:
5836 mode = S_IFCHR;
5837 break;
5838 case FILE_TYPE_PIPE:
5839 mode = S_IFIFO;
5840 break;
5841 default:
5842 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5843 FILE_ATTRIBUTE_TAG_INFO attr_info;
5844 DWORD e;
5845
5846 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5847 &attr_info, sizeof(attr_info));
5848 if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) {
5849 st->st_size = 0;
5850 mode |= S_IFSOCK;
5851 }
5852 else if (rb_w32_reparse_symlink_p(path)) {
5853 /* TODO: size in which encoding? */
5854 st->st_size = 0;
5855 mode |= S_IFLNK | S_IEXEC;
5856 }
5857 else {
5858 mode |= S_IFDIR | S_IEXEC;
5859 }
5860 }
5861 }
5862 CloseHandle(f);
5863 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5864 if (check_valid_dir(path)) return -1;
5865 }
5866 st->st_mode = fileattr_to_unixmode(attr, path, mode);
5867 if (len) {
5868 finalname[min(len, numberof(finalname)-1)] = L'\0';
5869 path = finalname;
5870 if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
5871 path += numberof(namespace_prefix);
5872 }
5873 }
5874 else {
5875 if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME)
5876 || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) {
5877 errno = map_errno(open_error);
5878 return -1;
5879 }
5880
5881 if (stat_by_find(path, st)) return -1;
5882 }
5883
5884 st->st_dev = st->st_rdev = path_drive(path);
5885
5886 return 0;
5887}
5888
5889/* License: Ruby's */
5890int
5891rb_w32_stat(const char *path, struct stat *st)
5892{
5893 struct stati128 tmp;
5894
5895 if (w32_stati128(path, &tmp, filecp(), FALSE)) return -1;
5896 COPY_STAT(tmp, *st, (_off_t));
5897 return 0;
5898}
5899
5900/* License: Ruby's */
5901static int
5902wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat)
5903{
5904 WCHAR *buf1;
5905 int ret, size;
5906 VALUE v;
5907
5908 if (!path || !st) {
5909 errno = EFAULT;
5910 return -1;
5911 }
5912 size = lstrlenW(path) + 2;
5913 buf1 = ALLOCV_N(WCHAR, v, size);
5914 if (!(path = name_for_stat(buf1, path)))
5915 return -1;
5916 ret = winnt_stat(path, st, lstat);
5917 if (v)
5918 ALLOCV_END(v);
5919
5920 return ret;
5921}
5922
5923/* License: Ruby's */
5924static WCHAR *
5925name_for_stat(WCHAR *buf1, const WCHAR *path)
5926{
5927 const WCHAR *p;
5928 WCHAR *s, *end;
5929 int len;
5930
5931 for (p = path, s = buf1; *p; p++, s++) {
5932 if (*p == L'/')
5933 *s = L'\\';
5934 else
5935 *s = *p;
5936 }
5937 *s = '\0';
5938 len = s - buf1;
5939 if (!len || L'\"' == *(--s)) {
5940 errno = ENOENT;
5941 return NULL;
5942 }
5943 end = buf1 + len - 1;
5944
5945 if (isUNCRoot(buf1)) {
5946 if (*end == L'.')
5947 *end = L'\0';
5948 else if (*end != L'\\')
5949 lstrcatW(buf1, L"\\");
5950 }
5951 else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
5952 lstrcatW(buf1, L".");
5953
5954 return buf1;
5955}
5956
5957/* License: Ruby's */
5958int
5959rb_w32_ustati128(const char *path, struct stati128 *st)
5960{
5961 return w32_stati128(path, st, CP_UTF8, FALSE);
5962}
5963
5964/* License: Ruby's */
5965int
5966rb_w32_stati128(const char *path, struct stati128 *st)
5967{
5968 return w32_stati128(path, st, filecp(), FALSE);
5969}
5970
5971/* License: Ruby's */
5972static int
5973w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat)
5974{
5975 WCHAR *wpath;
5976 int ret;
5977
5978 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
5979 return -1;
5980 ret = wstati128(wpath, st, lstat);
5981 free(wpath);
5982 return ret;
5983}
5984
5985/* License: Ruby's */
5986int
5987rb_w32_ulstati128(const char *path, struct stati128 *st)
5988{
5989 return w32_stati128(path, st, CP_UTF8, TRUE);
5990}
5991
5992/* License: Ruby's */
5993int
5994rb_w32_lstati128(const char *path, struct stati128 *st)
5995{
5996 return w32_stati128(path, st, filecp(), TRUE);
5997}
5998
5999/* License: Ruby's */
6000rb_off_t
6001rb_w32_lseek(int fd, rb_off_t ofs, int whence)
6002{
6003 SOCKET sock = TO_SOCKET(fd);
6004 if (is_socket(sock) || is_pipe(sock)) {
6005 errno = ESPIPE;
6006 return -1;
6007 }
6008 return _lseeki64(fd, ofs, whence);
6009}
6010
6011/* License: Ruby's */
6012static int
6013w32_access(const char *path, int mode, UINT cp)
6014{
6015 struct stati128 stat;
6016 if (w32_stati128(path, &stat, cp, FALSE) != 0)
6017 return -1;
6018 mode <<= 6;
6019 if ((stat.st_mode & mode) != mode) {
6020 errno = EACCES;
6021 return -1;
6022 }
6023 return 0;
6024}
6025
6026/* License: Ruby's */
6027int
6028rb_w32_access(const char *path, int mode)
6029{
6030 return w32_access(path, mode, filecp());
6031}
6032
6033/* License: Ruby's */
6034int
6035rb_w32_uaccess(const char *path, int mode)
6036{
6037 return w32_access(path, mode, CP_UTF8);
6038}
6039
6040/* License: Ruby's */
6041static int
6042rb_chsize(HANDLE h, rb_off_t size)
6043{
6044 long upos, lpos, usize, lsize;
6045 int ret = -1;
6046 DWORD e;
6047
6048 if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6049 (e = GetLastError())) {
6050 errno = map_errno(e);
6051 return -1;
6052 }
6053 usize = (long)(size >> 32);
6054 lsize = (long)size;
6055 if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
6056 (e = GetLastError())) {
6057 errno = map_errno(e);
6058 }
6059 else if (!SetEndOfFile(h)) {
6060 errno = map_errno(GetLastError());
6061 }
6062 else {
6063 ret = 0;
6064 }
6065 SetFilePointer(h, lpos, &upos, SEEK_SET);
6066 return ret;
6067}
6068
6069/* License: Ruby's */
6070static int
6071w32_truncate(const char *path, rb_off_t length, UINT cp)
6072{
6073 HANDLE h;
6074 int ret;
6075 WCHAR *wpath;
6076
6077 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6078 return -1;
6079 h = CreateFileW(wpath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
6080 if (h == INVALID_HANDLE_VALUE) {
6081 errno = map_errno(GetLastError());
6082 free(wpath);
6083 return -1;
6084 }
6085 free(wpath);
6086 ret = rb_chsize(h, length);
6087 CloseHandle(h);
6088 return ret;
6089}
6090
6091/* License: Ruby's */
6092int
6093rb_w32_utruncate(const char *path, rb_off_t length)
6094{
6095 return w32_truncate(path, length, CP_UTF8);
6096}
6097
6098/* License: Ruby's */
6099int
6100rb_w32_truncate(const char *path, rb_off_t length)
6101{
6102 return w32_truncate(path, length, filecp());
6103}
6104
6105/* License: Ruby's */
6106int
6107rb_w32_ftruncate(int fd, rb_off_t length)
6108{
6109 HANDLE h;
6110
6111 h = (HANDLE)_get_osfhandle(fd);
6112 if (h == (HANDLE)-1) return -1;
6113 return rb_chsize(h, length);
6114}
6115
6116/* License: Ruby's */
6117static long
6118filetime_to_clock(FILETIME *ft)
6119{
6120 __int64 qw = ft->dwHighDateTime;
6121 qw <<= 32;
6122 qw |= ft->dwLowDateTime;
6123 qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
6124 return (long) qw;
6125}
6126
6127/* License: Ruby's */
6128int
6129rb_w32_times(struct tms *tmbuf)
6130{
6131 FILETIME create, exit, kernel, user;
6132
6133 if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
6134 tmbuf->tms_utime = filetime_to_clock(&user);
6135 tmbuf->tms_stime = filetime_to_clock(&kernel);
6136 tmbuf->tms_cutime = 0;
6137 tmbuf->tms_cstime = 0;
6138 }
6139 else {
6140 tmbuf->tms_utime = clock();
6141 tmbuf->tms_stime = 0;
6142 tmbuf->tms_cutime = 0;
6143 tmbuf->tms_cstime = 0;
6144 }
6145 return 0;
6146}
6147
6148
6149/* License: Ruby's */
6150#define yield_once() Sleep(0)
6151#define yield_until(condition) do yield_once(); while (!(condition))
6152
6153/* License: Ruby's */
6155 /* output field */
6156 void* stackaddr;
6157 int errnum;
6158
6159 /* input field */
6160 uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
6161 uintptr_t self;
6162 int argc;
6163 uintptr_t* argv;
6164};
6165
6166/* License: Ruby's */
6167static DWORD WINAPI
6168call_asynchronous(PVOID argp)
6169{
6170 DWORD ret;
6171 struct asynchronous_arg_t *arg = argp;
6172 arg->stackaddr = &argp;
6173 ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
6174 arg->errnum = errno;
6175 return ret;
6176}
6177
6178/* License: Ruby's */
6179uintptr_t
6180rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
6181 int argc, uintptr_t* argv, uintptr_t intrval)
6182{
6183 DWORD val;
6184 BOOL interrupted = FALSE;
6185 HANDLE thr;
6186
6187 RUBY_CRITICAL {
6188 struct asynchronous_arg_t arg;
6189
6190 arg.stackaddr = NULL;
6191 arg.errnum = 0;
6192 arg.func = func;
6193 arg.self = self;
6194 arg.argc = argc;
6195 arg.argv = argv;
6196
6197 thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6198
6199 if (thr) {
6200 yield_until(arg.stackaddr);
6201
6202 if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6203 interrupted = TRUE;
6204
6205 if (TerminateThread(thr, intrval)) {
6206 yield_once();
6207 }
6208 }
6209
6210 GetExitCodeThread(thr, &val);
6211 CloseHandle(thr);
6212
6213 if (interrupted) {
6214 /* must release stack of killed thread, why doesn't Windows? */
6215 MEMORY_BASIC_INFORMATION m;
6216
6217 memset(&m, 0, sizeof(m));
6218 if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
6219 Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
6220 arg.stackaddr, GetLastError()));
6221 }
6222 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6223 Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
6224 m.AllocationBase, GetLastError()));
6225 }
6226 errno = EINTR;
6227 }
6228 else {
6229 errno = arg.errnum;
6230 }
6231 }
6232 }
6233
6234 if (!thr) {
6235 rb_fatal("failed to launch waiter thread:%ld", GetLastError());
6236 }
6237
6238 return val;
6239}
6240
6241/* License: Ruby's */
6242char **
6243rb_w32_get_environ(void)
6244{
6245 WCHAR *envtop, *env;
6246 char **myenvtop, **myenv;
6247 int num;
6248
6249 /*
6250 * We avoid values started with `='. If you want to deal those values,
6251 * change this function, and some functions in hash.c which recognize
6252 * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
6253 * CygWin deals these values by changing first `=' to '!'. But we don't
6254 * use such trick and follow cmd.exe's way that just doesn't show these
6255 * values.
6256 *
6257 * This function returns UTF-8 strings.
6258 */
6259 envtop = GetEnvironmentStringsW();
6260 for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6261 if (*env != '=') num++;
6262
6263 myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
6264 for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
6265 if (*env != '=') {
6266 if (!(*myenv = wstr_to_utf8(env, NULL))) {
6267 break;
6268 }
6269 myenv++;
6270 }
6271 }
6272 *myenv = NULL;
6273 FreeEnvironmentStringsW(envtop);
6274
6275 return myenvtop;
6276}
6277
6278/* License: Ruby's */
6279void
6280rb_w32_free_environ(char **env)
6281{
6282 char **t = env;
6283
6284 while (*t) free(*t++);
6285 free(env);
6286}
6287
6288/* License: Ruby's */
6289rb_pid_t
6290rb_w32_getpid(void)
6291{
6292 return GetCurrentProcessId();
6293}
6294
6295
6296/* License: Ruby's */
6297rb_pid_t
6298rb_w32_getppid(void)
6299{
6300 typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
6301 static query_func *pNtQueryInformationProcess = (query_func *)-1;
6302 rb_pid_t ppid = 0;
6303
6304 if (pNtQueryInformationProcess == (query_func *)-1)
6305 pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
6306 if (pNtQueryInformationProcess) {
6307 struct {
6308 long ExitStatus;
6309 void* PebBaseAddress;
6310 uintptr_t AffinityMask;
6311 uintptr_t BasePriority;
6312 uintptr_t UniqueProcessId;
6313 uintptr_t ParentProcessId;
6314 } pbi;
6315 ULONG len;
6316 long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
6317 if (!ret) {
6318 ppid = pbi.ParentProcessId;
6319 }
6320 }
6321
6322 return ppid;
6323}
6324
6325STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
6326
6327/* License: Ruby's */
6328#define set_new_std_handle(newfd, handle) do { \
6329 if ((unsigned)(newfd) > 2) break; \
6330 SetStdHandle(STD_INPUT_HANDLE+(STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)*(newfd), \
6331 (handle)); \
6332 } while (0)
6333#define set_new_std_fd(newfd) set_new_std_handle(newfd, (HANDLE)rb_w32_get_osfhandle(newfd))
6334
6335/* License: Ruby's */
6336int
6337rb_w32_dup2(int oldfd, int newfd)
6338{
6339 int ret;
6340
6341 if (oldfd == newfd) return newfd;
6342 ret = dup2(oldfd, newfd);
6343 if (ret < 0) return ret;
6344 set_new_std_fd(newfd);
6345 return newfd;
6346}
6347
6348/* License: Ruby's */
6349int
6350rb_w32_uopen(const char *file, int oflag, ...)
6351{
6352 WCHAR *wfile;
6353 int ret;
6354 int pmode;
6355
6356 va_list arg;
6357 va_start(arg, oflag);
6358 pmode = va_arg(arg, int);
6359 va_end(arg);
6360
6361 if (!(wfile = utf8_to_wstr(file, NULL)))
6362 return -1;
6363 ret = w32_wopen(wfile, oflag, pmode);
6364 free(wfile);
6365 return ret;
6366}
6367
6368/* License: Ruby's */
6369static int
6370check_if_wdir(const WCHAR *wfile)
6371{
6372 DWORD attr = GetFileAttributesW(wfile);
6373 if (attr == (DWORD)-1L ||
6374 !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6375 check_valid_dir(wfile)) {
6376 return FALSE;
6377 }
6378 errno = EISDIR;
6379 return TRUE;
6380}
6381
6382/* License: Ruby's */
6383int
6384rb_w32_open(const char *file, int oflag, ...)
6385{
6386 WCHAR *wfile;
6387 int ret;
6388 int pmode;
6389
6390 va_list arg;
6391 va_start(arg, oflag);
6392 pmode = va_arg(arg, int);
6393 va_end(arg);
6394
6395 if (!(wfile = filecp_to_wstr(file, NULL)))
6396 return -1;
6397 ret = w32_wopen(wfile, oflag, pmode);
6398 free(wfile);
6399 return ret;
6400}
6401
6402/* License: Ruby's */
6403int
6404rb_w32_wopen(const WCHAR *file, int oflag, ...)
6405{
6406 int pmode = 0;
6407
6408 if (oflag & O_CREAT) {
6409 va_list arg;
6410 va_start(arg, oflag);
6411 pmode = va_arg(arg, int);
6412 va_end(arg);
6413 }
6414
6415 return w32_wopen(file, oflag, pmode);
6416}
6417
6418static int
6419w32_wopen(const WCHAR *file, int oflag, int pmode)
6420{
6421 char flags = 0;
6422 int fd;
6423 DWORD access;
6424 DWORD create;
6425 DWORD attr = FILE_ATTRIBUTE_NORMAL;
6426 SECURITY_ATTRIBUTES sec;
6427 HANDLE h;
6428 int share_delete;
6429
6430 share_delete = oflag & O_SHARE_DELETE ? FILE_SHARE_DELETE : 0;
6431 oflag &= ~O_SHARE_DELETE;
6432 if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
6433 fd = _wopen(file, oflag, pmode);
6434 if (fd == -1) {
6435 switch (errno) {
6436 case EACCES:
6437 check_if_wdir(file);
6438 break;
6439 case EINVAL:
6440 errno = map_errno(GetLastError());
6441 break;
6442 }
6443 }
6444 return fd;
6445 }
6446
6447 sec.nLength = sizeof(sec);
6448 sec.lpSecurityDescriptor = NULL;
6449 if (oflag & O_NOINHERIT) {
6450 sec.bInheritHandle = FALSE;
6451 flags |= FNOINHERIT;
6452 }
6453 else {
6454 sec.bInheritHandle = TRUE;
6455 }
6456 oflag &= ~O_NOINHERIT;
6457
6458 /* always open with binary mode */
6459 oflag &= ~(O_BINARY | O_TEXT);
6460
6461 switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
6462 case O_RDWR:
6463 access = GENERIC_READ | GENERIC_WRITE;
6464 break;
6465 case O_RDONLY:
6466 access = GENERIC_READ;
6467 break;
6468 case O_WRONLY:
6469 access = GENERIC_WRITE;
6470 break;
6471 default:
6472 errno = EINVAL;
6473 return -1;
6474 }
6475 oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
6476
6477 switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
6478 case O_CREAT:
6479 create = OPEN_ALWAYS;
6480 break;
6481 case 0:
6482 case O_EXCL:
6483 create = OPEN_EXISTING;
6484 break;
6485 case O_CREAT | O_EXCL:
6486 case O_CREAT | O_EXCL | O_TRUNC:
6487 create = CREATE_NEW;
6488 break;
6489 case O_TRUNC:
6490 case O_TRUNC | O_EXCL:
6491 create = TRUNCATE_EXISTING;
6492 break;
6493 case O_CREAT | O_TRUNC:
6494 create = CREATE_ALWAYS;
6495 break;
6496 default:
6497 errno = EINVAL;
6498 return -1;
6499 }
6500 if (oflag & O_CREAT) {
6501 /* TODO: we need to check umask here, but it's not exported... */
6502 if (!(pmode & S_IWRITE))
6503 attr = FILE_ATTRIBUTE_READONLY;
6504 }
6505 oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6506
6507 if (oflag & O_TEMPORARY) {
6508 attr |= FILE_FLAG_DELETE_ON_CLOSE;
6509 access |= DELETE;
6510 }
6511 oflag &= ~O_TEMPORARY;
6512
6513 if (oflag & _O_SHORT_LIVED)
6514 attr |= FILE_ATTRIBUTE_TEMPORARY;
6515 oflag &= ~_O_SHORT_LIVED;
6516
6517 switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
6518 case 0:
6519 break;
6520 case O_SEQUENTIAL:
6521 attr |= FILE_FLAG_SEQUENTIAL_SCAN;
6522 break;
6523 case O_RANDOM:
6524 attr |= FILE_FLAG_RANDOM_ACCESS;
6525 break;
6526 default:
6527 errno = EINVAL;
6528 return -1;
6529 }
6530 oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6531
6532 if (oflag & ~O_APPEND) {
6533 errno = EINVAL;
6534 return -1;
6535 }
6536
6537 /* allocate a C Runtime file handle */
6538 RUBY_CRITICAL {
6539 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6540 fd = _open_osfhandle((intptr_t)h, 0);
6541 CloseHandle(h);
6542 }
6543 if (fd == -1) {
6544 errno = EMFILE;
6545 return -1;
6546 }
6547 RUBY_CRITICAL {
6548 rb_acrt_lowio_lock_fh(fd);
6549 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6550 _set_osflags(fd, 0);
6551
6552 h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE | share_delete, &sec, create, attr, NULL);
6553 if (h == INVALID_HANDLE_VALUE) {
6554 DWORD e = GetLastError();
6555 if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
6556 errno = map_errno(e);
6557 rb_acrt_lowio_unlock_fh(fd);
6558 fd = -1;
6559 goto quit;
6560 }
6561
6562 switch (GetFileType(h)) {
6563 case FILE_TYPE_CHAR:
6564 flags |= FDEV;
6565 break;
6566 case FILE_TYPE_PIPE:
6567 flags |= FPIPE;
6568 break;
6569 case FILE_TYPE_UNKNOWN:
6570 errno = map_errno(GetLastError());
6571 CloseHandle(h);
6572 rb_acrt_lowio_unlock_fh(fd);
6573 fd = -1;
6574 goto quit;
6575 }
6576 if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6577 flags |= FAPPEND;
6578
6579 _set_osfhnd(fd, (intptr_t)h);
6580 _set_osflags(fd, flags | FOPEN);
6581
6582 rb_acrt_lowio_unlock_fh(fd);
6583 quit:
6584 ;
6585 }
6586
6587 return fd;
6588}
6589
6590/* License: Ruby's */
6591int
6592rb_w32_fclose(FILE *fp)
6593{
6594 int fd = fileno(fp);
6595 SOCKET sock = TO_SOCKET(fd);
6596 int save_errno = errno;
6597
6598 if (fflush(fp)) return -1;
6599 if (!is_socket(sock)) {
6600 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6601 return fclose(fp);
6602 }
6603 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6604 fclose(fp);
6605 errno = save_errno;
6606 if (closesocket(sock) == SOCKET_ERROR) {
6607 errno = map_errno(WSAGetLastError());
6608 return -1;
6609 }
6610 return 0;
6611}
6612
6613/* License: Ruby's */
6614int
6615rb_w32_pipe(int fds[2])
6616{
6617 static long serial = 0;
6618 static const char prefix[] = "\\\\.\\pipe\\ruby";
6619 enum {
6620 width_of_prefix = (int)sizeof(prefix) - 1,
6621 width_of_pid = (int)sizeof(rb_pid_t) * 2,
6622 width_of_serial = (int)sizeof(serial) * 2,
6623 width_of_ids = width_of_pid + 1 + width_of_serial + 1
6624 };
6625 char name[sizeof(prefix) + width_of_ids];
6626 SECURITY_ATTRIBUTES sec;
6627 HANDLE hRead, hWrite, h;
6628 int fdRead, fdWrite;
6629 int ret;
6630
6631 memcpy(name, prefix, width_of_prefix);
6632 snprintf(name + width_of_prefix, width_of_ids, "%.*"PRI_PIDT_PREFIX"x-%.*lx",
6633 width_of_pid, rb_w32_getpid(), width_of_serial, InterlockedIncrement(&serial)-1);
6634
6635 sec.nLength = sizeof(sec);
6636 sec.lpSecurityDescriptor = NULL;
6637 sec.bInheritHandle = FALSE;
6638
6639 RUBY_CRITICAL {
6640 hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6641 0, 2, 65536, 65536, 0, &sec);
6642 }
6643 if (hRead == INVALID_HANDLE_VALUE) {
6644 DWORD err = GetLastError();
6645 if (err == ERROR_PIPE_BUSY)
6646 errno = EMFILE;
6647 else
6648 errno = map_errno(GetLastError());
6649 return -1;
6650 }
6651
6652 RUBY_CRITICAL {
6653 hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6654 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6655 }
6656 if (hWrite == INVALID_HANDLE_VALUE) {
6657 errno = map_errno(GetLastError());
6658 CloseHandle(hRead);
6659 return -1;
6660 }
6661
6662 RUBY_CRITICAL do {
6663 ret = 0;
6664 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6665 fdRead = _open_osfhandle((intptr_t)h, 0);
6666 CloseHandle(h);
6667 if (fdRead == -1) {
6668 errno = EMFILE;
6669 CloseHandle(hWrite);
6670 CloseHandle(hRead);
6671 ret = -1;
6672 break;
6673 }
6674
6675 rb_acrt_lowio_lock_fh(fdRead);
6676 _set_osfhnd(fdRead, (intptr_t)hRead);
6677 _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
6678 rb_acrt_lowio_unlock_fh(fdRead);
6679 } while (0);
6680 if (ret)
6681 return ret;
6682
6683 RUBY_CRITICAL do {
6684 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6685 fdWrite = _open_osfhandle((intptr_t)h, 0);
6686 CloseHandle(h);
6687 if (fdWrite == -1) {
6688 errno = EMFILE;
6689 CloseHandle(hWrite);
6690 ret = -1;
6691 break;
6692 }
6693 rb_acrt_lowio_lock_fh(fdWrite);
6694 _set_osfhnd(fdWrite, (intptr_t)hWrite);
6695 _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
6696 rb_acrt_lowio_unlock_fh(fdWrite);
6697 } while (0);
6698 if (ret) {
6699 rb_w32_close(fdRead);
6700 return ret;
6701 }
6702
6703 fds[0] = fdRead;
6704 fds[1] = fdWrite;
6705
6706 return 0;
6707}
6708
6709/* License: Ruby's */
6710static int
6711console_emulator_p(void)
6712{
6713#ifdef _WIN32_WCE
6714 return FALSE;
6715#else
6716 const void *const func = WriteConsoleW;
6717 HMODULE k;
6718 MEMORY_BASIC_INFORMATION m;
6719
6720 memset(&m, 0, sizeof(m));
6721 if (!VirtualQuery(func, &m, sizeof(m))) {
6722 return FALSE;
6723 }
6724 k = GetModuleHandle("kernel32.dll");
6725 if (!k) return FALSE;
6726 return (HMODULE)m.AllocationBase != k;
6727#endif
6728}
6729
6730/* License: Ruby's */
6731static struct constat *
6732constat_handle(HANDLE h)
6733{
6734 st_data_t data;
6735 struct constat *p = NULL;
6736 thread_exclusive(conlist) {
6737 if (!conlist) {
6738 if (console_emulator_p()) {
6739 conlist = conlist_disabled;
6740 continue;
6741 }
6742 conlist = st_init_numtable();
6743 install_vm_exit_handler();
6744 }
6745 else if (conlist == conlist_disabled) {
6746 continue;
6747 }
6748 if (st_lookup(conlist, (st_data_t)h, &data)) {
6749 p = (struct constat *)data;
6750 }
6751 else {
6752 CONSOLE_SCREEN_BUFFER_INFO csbi;
6753 p = ALLOC(struct constat);
6754 p->vt100.state = constat_init;
6755 p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6756 p->vt100.reverse = 0;
6757 p->vt100.saved.X = p->vt100.saved.Y = 0;
6758 if (GetConsoleScreenBufferInfo(h, &csbi)) {
6759 p->vt100.attr = csbi.wAttributes;
6760 }
6761 st_insert(conlist, (st_data_t)h, (st_data_t)p);
6762 }
6763 }
6764 return p;
6765}
6766
6767/* License: Ruby's */
6768static void
6769constat_reset(HANDLE h)
6770{
6771 st_data_t data;
6772 struct constat *p;
6773 thread_exclusive(conlist) {
6774 if (!conlist || conlist == conlist_disabled) continue;
6775 if (!st_lookup(conlist, (st_data_t)h, &data)) continue;
6776 p = (struct constat *)data;
6777 p->vt100.state = constat_init;
6778 }
6779}
6780
6781#define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6782#define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6783
6784#define constat_attr_color_reverse(attr) \
6785 ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6786 (((attr) & FOREGROUND_MASK) << 4) | \
6787 (((attr) & BACKGROUND_MASK) >> 4)
6788
6789/* License: Ruby's */
6790static WORD
6791constat_attr(int count, const int *seq, WORD attr, WORD default_attr, int *reverse)
6792{
6793 int rev = *reverse;
6794 WORD bold;
6795
6796 if (!count) return attr;
6797 if (rev) attr = constat_attr_color_reverse(attr);
6798 bold = attr & FOREGROUND_INTENSITY;
6799 attr &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
6800
6801 while (count-- > 0) {
6802 switch (*seq++) {
6803 case 0:
6804 attr = default_attr;
6805 rev = 0;
6806 bold = 0;
6807 break;
6808 case 1:
6809 bold = FOREGROUND_INTENSITY;
6810 break;
6811 case 4:
6812#ifndef COMMON_LVB_UNDERSCORE
6813#define COMMON_LVB_UNDERSCORE 0x8000
6814#endif
6815 attr |= COMMON_LVB_UNDERSCORE;
6816 break;
6817 case 7:
6818 rev = 1;
6819 break;
6820
6821 case 30:
6822 attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
6823 break;
6824 case 17:
6825 case 31:
6826 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)) | FOREGROUND_RED;
6827 break;
6828 case 18:
6829 case 32:
6830 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_RED)) | FOREGROUND_GREEN;
6831 break;
6832 case 19:
6833 case 33:
6834 attr = (attr & ~FOREGROUND_BLUE) | FOREGROUND_GREEN | FOREGROUND_RED;
6835 break;
6836 case 20:
6837 case 34:
6838 attr = (attr & ~(FOREGROUND_GREEN | FOREGROUND_RED)) | FOREGROUND_BLUE;
6839 break;
6840 case 21:
6841 case 35:
6842 attr = (attr & ~FOREGROUND_GREEN) | FOREGROUND_BLUE | FOREGROUND_RED;
6843 break;
6844 case 22:
6845 case 36:
6846 attr = (attr & ~FOREGROUND_RED) | FOREGROUND_BLUE | FOREGROUND_GREEN;
6847 break;
6848 case 23:
6849 case 37:
6850 attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6851 break;
6852
6853 case 40:
6854 attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
6855 break;
6856 case 41:
6857 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN)) | BACKGROUND_RED;
6858 break;
6859 case 42:
6860 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_RED)) | BACKGROUND_GREEN;
6861 break;
6862 case 43:
6863 attr = (attr & ~BACKGROUND_BLUE) | BACKGROUND_GREEN | BACKGROUND_RED;
6864 break;
6865 case 44:
6866 attr = (attr & ~(BACKGROUND_GREEN | BACKGROUND_RED)) | BACKGROUND_BLUE;
6867 break;
6868 case 45:
6869 attr = (attr & ~BACKGROUND_GREEN) | BACKGROUND_BLUE | BACKGROUND_RED;
6870 break;
6871 case 46:
6872 attr = (attr & ~BACKGROUND_RED) | BACKGROUND_BLUE | BACKGROUND_GREEN;
6873 break;
6874 case 47:
6875 attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
6876 break;
6877 }
6878 }
6879 attr |= bold;
6880 if (rev) attr = constat_attr_color_reverse(attr);
6881 *reverse = rev;
6882 return attr;
6883}
6884
6885/* License: Ruby's */
6886static void
6887constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
6888{
6889 DWORD written;
6890
6891 FillConsoleOutputAttribute(handle, attr, len, pos, &written);
6892 FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
6893}
6894
6895/* License: Ruby's */
6896static void
6897constat_apply(HANDLE handle, struct constat *s, WCHAR w)
6898{
6899 CONSOLE_SCREEN_BUFFER_INFO csbi;
6900 const int *seq = s->vt100.seq;
6901 int count = s->vt100.state;
6902 int arg0, arg1 = 1;
6903 COORD pos;
6904
6905 if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
6906 arg0 = (count > 0 && seq[0] > 0);
6907 if (arg0) arg1 = seq[0];
6908 switch (w) {
6909 case L'm':
6910 SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr, &s->vt100.reverse));
6911 break;
6912 case L'F':
6913 csbi.dwCursorPosition.X = 0;
6914 case L'A':
6915 csbi.dwCursorPosition.Y -= arg1;
6916 if (csbi.dwCursorPosition.Y < csbi.srWindow.Top)
6917 csbi.dwCursorPosition.Y = csbi.srWindow.Top;
6918 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6919 break;
6920 case L'E':
6921 csbi.dwCursorPosition.X = 0;
6922 case L'B':
6923 case L'e':
6924 csbi.dwCursorPosition.Y += arg1;
6925 if (csbi.dwCursorPosition.Y > csbi.srWindow.Bottom)
6926 csbi.dwCursorPosition.Y = csbi.srWindow.Bottom;
6927 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6928 break;
6929 case L'C':
6930 csbi.dwCursorPosition.X += arg1;
6931 if (csbi.dwCursorPosition.X >= csbi.srWindow.Right)
6932 csbi.dwCursorPosition.X = csbi.srWindow.Right;
6933 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6934 break;
6935 case L'D':
6936 csbi.dwCursorPosition.X -= arg1;
6937 if (csbi.dwCursorPosition.X < csbi.srWindow.Left)
6938 csbi.dwCursorPosition.X = csbi.srWindow.Left;
6939 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6940 break;
6941 case L'G':
6942 case L'`':
6943 arg1 += csbi.srWindow.Left;
6944 if (arg1 > csbi.srWindow.Right)
6945 arg1 = csbi.srWindow.Right;
6946 csbi.dwCursorPosition.X = arg1;
6947 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6948 break;
6949 case L'd':
6950 arg1 += csbi.srWindow.Top;
6951 if (arg1 > csbi.srWindow.Bottom)
6952 arg1 = csbi.srWindow.Bottom;
6953 csbi.dwCursorPosition.Y = arg1;
6954 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6955 break;
6956 case L'H':
6957 case L'f':
6958 pos.Y = arg1 + csbi.srWindow.Top - 1;
6959 if (pos.Y > csbi.srWindow.Bottom) pos.Y = csbi.srWindow.Bottom;
6960 if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
6961 pos.X = arg1 + csbi.srWindow.Left - 1;
6962 if (pos.X > csbi.srWindow.Right) pos.X = csbi.srWindow.Right;
6963 SetConsoleCursorPosition(handle, pos);
6964 break;
6965 case L'J':
6966 switch (arg0 ? arg1 : 0) {
6967 case 0: /* erase after cursor */
6968 constat_clear(handle, csbi.wAttributes,
6969 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.dwCursorPosition.Y + 1)
6970 - csbi.dwCursorPosition.X),
6971 csbi.dwCursorPosition);
6972 break;
6973 case 1: /* erase before *and* cursor */
6974 pos.X = 0;
6975 pos.Y = csbi.srWindow.Top;
6976 constat_clear(handle, csbi.wAttributes,
6977 (csbi.dwSize.X * (csbi.dwCursorPosition.Y - csbi.srWindow.Top)
6978 + csbi.dwCursorPosition.X + 1),
6979 pos);
6980 break;
6981 case 2: /* erase entire screen */
6982 pos.X = 0;
6983 pos.Y = csbi.srWindow.Top;
6984 constat_clear(handle, csbi.wAttributes,
6985 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1)),
6986 pos);
6987 break;
6988 case 3: /* erase entire screen */
6989 pos.X = 0;
6990 pos.Y = 0;
6991 constat_clear(handle, csbi.wAttributes,
6992 (csbi.dwSize.X * csbi.dwSize.Y),
6993 pos);
6994 break;
6995 }
6996 break;
6997 case L'K':
6998 switch (arg0 ? arg1 : 0) {
6999 case 0: /* erase after cursor */
7000 constat_clear(handle, csbi.wAttributes,
7001 (csbi.dwSize.X - csbi.dwCursorPosition.X),
7002 csbi.dwCursorPosition);
7003 break;
7004 case 1: /* erase before *and* cursor */
7005 pos.X = 0;
7006 pos.Y = csbi.dwCursorPosition.Y;
7007 constat_clear(handle, csbi.wAttributes,
7008 csbi.dwCursorPosition.X + 1, pos);
7009 break;
7010 case 2: /* erase entire line */
7011 pos.X = 0;
7012 pos.Y = csbi.dwCursorPosition.Y;
7013 constat_clear(handle, csbi.wAttributes,
7014 csbi.dwSize.X, pos);
7015 break;
7016 }
7017 break;
7018 case L's':
7019 s->vt100.saved = csbi.dwCursorPosition;
7020 break;
7021 case L'u':
7022 SetConsoleCursorPosition(handle, s->vt100.saved);
7023 break;
7024 case L'h':
7025 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7026 CONSOLE_CURSOR_INFO cci;
7027 GetConsoleCursorInfo(handle, &cci);
7028 cci.bVisible = TRUE;
7029 SetConsoleCursorInfo(handle, &cci);
7030 }
7031 break;
7032 case L'l':
7033 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7034 CONSOLE_CURSOR_INFO cci;
7035 GetConsoleCursorInfo(handle, &cci);
7036 cci.bVisible = FALSE;
7037 SetConsoleCursorInfo(handle, &cci);
7038 }
7039 break;
7040 }
7041}
7042
7043/* get rid of console writing bug; assume WriteConsole and WriteFile
7044 * on a console share the same limit. */
7045static const long MAXSIZE_CONSOLE_WRITING = 31366;
7046
7047/* License: Ruby's */
7048static long
7049constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
7050{
7051 const WCHAR *ptr = *ptrp;
7052 long rest, len = *lenp;
7053 while (len-- > 0) {
7054 WCHAR wc = *ptr++;
7055 if (wc == 0x1b) {
7056 rest = *lenp - len - 1;
7057 if (s->vt100.state == constat_esc) {
7058 rest++; /* reuse this ESC */
7059 }
7060 s->vt100.state = constat_init;
7061 if (len > 0 && *ptr != L'[') continue;
7062 s->vt100.state = constat_esc;
7063 }
7064 else if (s->vt100.state == constat_esc) {
7065 if (wc != L'[') {
7066 /* TODO: supply dropped ESC at beginning */
7067 s->vt100.state = constat_init;
7068 continue;
7069 }
7070 rest = *lenp - len - 1;
7071 if (rest > 0) --rest;
7072 s->vt100.state = constat_seq;
7073 s->vt100.seq[0] = 0;
7074 }
7075 else if (s->vt100.state >= constat_seq) {
7076 if (wc >= L'0' && wc <= L'9') {
7077 if (s->vt100.state < (int)numberof(s->vt100.seq)) {
7078 int *seq = &s->vt100.seq[s->vt100.state];
7079 *seq = (*seq * 10) + (wc - L'0');
7080 }
7081 }
7082 else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
7083 s->vt100.seq[s->vt100.state++] = -1;
7084 }
7085 else {
7086 do {
7087 if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
7088 s->vt100.seq[s->vt100.state] = 0;
7089 }
7090 else {
7091 s->vt100.state = (int)numberof(s->vt100.seq);
7092 }
7093 } while (0);
7094 if (wc != L';') {
7095 constat_apply(h, s, wc);
7096 s->vt100.state = constat_init;
7097 }
7098 }
7099 rest = 0;
7100 }
7101 else if ((rest = *lenp - len) < MAXSIZE_CONSOLE_WRITING) {
7102 continue;
7103 }
7104 *ptrp = ptr;
7105 *lenp = len;
7106 return rest;
7107 }
7108 len = *lenp;
7109 *ptrp = ptr;
7110 *lenp = 0;
7111 return len;
7112}
7113
7114
7115/* License: Ruby's */
7116int
7117rb_w32_close(int fd)
7118{
7119 SOCKET sock = TO_SOCKET(fd);
7120 int save_errno = errno;
7121
7122 if (!is_socket(sock)) {
7123 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7124 constat_delete((HANDLE)sock);
7125 return _close(fd);
7126 }
7127 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
7128 socklist_delete(&sock, NULL);
7129 _close(fd);
7130 errno = save_errno;
7131 if (closesocket(sock) == SOCKET_ERROR) {
7132 errno = map_errno(WSAGetLastError());
7133 return -1;
7134 }
7135 return 0;
7136}
7137
7138#ifndef INVALID_SET_FILE_POINTER
7139#define INVALID_SET_FILE_POINTER ((DWORD)-1)
7140#endif
7141
7142static int
7143setup_overlapped(OVERLAPPED *ol, int fd, int iswrite, rb_off_t *_offset)
7144{
7145 memset(ol, 0, sizeof(*ol));
7146
7147 // On mode:a, it can write only FILE_END.
7148 // On mode:a+, though it can write only FILE_END,
7149 // it can read from everywhere.
7150 DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
7151
7152 if (_offset) {
7153 // Explicit offset was provided (pread/pwrite) - use it:
7154 uint64_t offset = *_offset;
7155 ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
7156 ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
7157
7158 // Update _offset with the current offset:
7159 LARGE_INTEGER seek_offset = {0}, current_offset = {0};
7160 if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, &current_offset, seek_method)) {
7161 DWORD last_error = GetLastError();
7162 if (last_error != NO_ERROR) {
7163 errno = map_errno(last_error);
7164 return -1;
7165 }
7166 }
7167
7168 // As we need to restore the current offset later, we save it here:
7169 *_offset = current_offset.QuadPart;
7170 }
7171 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7172 LONG high = 0;
7173 DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method);
7174
7175 if (low == INVALID_SET_FILE_POINTER) {
7176 DWORD err = GetLastError();
7177 if (err != NO_ERROR) {
7178 errno = map_errno(err);
7179 return -1;
7180 }
7181 }
7182
7183 ol->Offset = low;
7184 ol->OffsetHigh = high;
7185 }
7186
7187 ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7188 if (!ol->hEvent) {
7189 errno = map_errno(GetLastError());
7190 return -1;
7191 }
7192 return 0;
7193}
7194
7195static void
7196finish_overlapped(OVERLAPPED *ol, int fd, DWORD size, rb_off_t *_offset)
7197{
7198 CloseHandle(ol->hEvent);
7199
7200 if (_offset) {
7201 // If we were doing a `pread`/`pwrite`, we need to restore the current that was saved in setup_overlapped:
7202 DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN;
7203
7204 LARGE_INTEGER seek_offset = {0};
7205 if (seek_method == FILE_BEGIN) {
7206 seek_offset.QuadPart = *_offset;
7207 }
7208
7209 SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method);
7210 }
7211 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7212 LONG high = ol->OffsetHigh;
7213 DWORD low = ol->Offset + size;
7214 if (low < ol->Offset)
7215 ++high;
7216 SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
7217 }
7218}
7219
7220#undef read
7221/* License: Ruby's */
7222static ssize_t
7223rb_w32_read_internal(int fd, void *buf, size_t size, rb_off_t *offset)
7224{
7225 SOCKET sock = TO_SOCKET(fd);
7226 DWORD read;
7227 DWORD wait;
7228 DWORD err;
7229 size_t len;
7230 size_t ret;
7231 OVERLAPPED ol;
7232 BOOL isconsole;
7233 BOOL islineinput = FALSE;
7234 int start = 0;
7235
7236 if (is_socket(sock))
7237 return rb_w32_recv(fd, buf, size, 0);
7238
7239 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7240 if (_get_osfhandle(fd) == -1) {
7241 return -1;
7242 }
7243
7244 if (!offset && _osfile(fd) & FTEXT) {
7245 return _read(fd, buf, size);
7246 }
7247
7248 rb_acrt_lowio_lock_fh(fd);
7249
7250 if (!size || _osfile(fd) & FEOFLAG) {
7251 _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7252 rb_acrt_lowio_unlock_fh(fd);
7253 return 0;
7254 }
7255
7256 ret = 0;
7257 isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
7258 if (isconsole) {
7259 DWORD mode;
7260 GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
7261 islineinput = (mode & ENABLE_LINE_INPUT) != 0;
7262 }
7263 retry:
7264 /* get rid of console reading bug */
7265 if (isconsole) {
7266 constat_reset((HANDLE)_osfhnd(fd));
7267 if (start)
7268 len = 1;
7269 else {
7270 len = 0;
7271 start = 1;
7272 }
7273 }
7274 else
7275 len = size;
7276 size -= len;
7277
7278 if (setup_overlapped(&ol, fd, FALSE, offset)) {
7279 rb_acrt_lowio_unlock_fh(fd);
7280 return -1;
7281 }
7282
7283 if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, &ol)) {
7284 err = GetLastError();
7285 if (err == ERROR_NO_DATA && (_osfile(fd) & FPIPE)) {
7286 DWORD state;
7287 if (GetNamedPipeHandleState((HANDLE)_osfhnd(fd), &state, NULL, NULL, NULL, NULL, 0) && (state & PIPE_NOWAIT)) {
7288 errno = EWOULDBLOCK;
7289 }
7290 else {
7291 errno = map_errno(err);
7292 }
7293 rb_acrt_lowio_unlock_fh(fd);
7294 return -1;
7295 }
7296 else if (err != ERROR_IO_PENDING) {
7297 CloseHandle(ol.hEvent);
7298 if (err == ERROR_ACCESS_DENIED)
7299 errno = EBADF;
7300 else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
7301 rb_acrt_lowio_unlock_fh(fd);
7302 return 0;
7303 }
7304 else
7305 errno = map_errno(err);
7306
7307 rb_acrt_lowio_unlock_fh(fd);
7308 return -1;
7309 }
7310
7311 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7312 if (wait != WAIT_OBJECT_0) {
7313 if (wait == WAIT_OBJECT_0 + 1)
7314 errno = EINTR;
7315 else
7316 errno = map_errno(GetLastError());
7317 CloseHandle(ol.hEvent);
7318 CancelIo((HANDLE)_osfhnd(fd));
7319 rb_acrt_lowio_unlock_fh(fd);
7320 return -1;
7321 }
7322
7323 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
7324 (err = GetLastError()) != ERROR_HANDLE_EOF) {
7325 int ret = 0;
7326 if (err != ERROR_BROKEN_PIPE) {
7327 errno = map_errno(err);
7328 ret = -1;
7329 }
7330 CloseHandle(ol.hEvent);
7331 CancelIo((HANDLE)_osfhnd(fd));
7332 rb_acrt_lowio_unlock_fh(fd);
7333 return ret;
7334 }
7335 }
7336 else {
7337 err = GetLastError();
7338 errno = map_errno(err);
7339 }
7340
7341 finish_overlapped(&ol, fd, read, offset);
7342
7343 ret += read;
7344 if (read >= len) {
7345 buf = (char *)buf + read;
7346 if (err != ERROR_OPERATION_ABORTED &&
7347 !(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
7348 goto retry;
7349 }
7350 if (read == 0)
7351 _set_osflags(fd, _osfile(fd) | FEOFLAG);
7352
7353
7354 rb_acrt_lowio_unlock_fh(fd);
7355
7356 return ret;
7357}
7358
7359#undef write
7360/* License: Ruby's */
7361static ssize_t
7362rb_w32_write_internal(int fd, const void *buf, size_t size, rb_off_t *offset)
7363{
7364 SOCKET sock = TO_SOCKET(fd);
7365 DWORD written;
7366 DWORD wait;
7367 DWORD err;
7368 size_t len;
7369 size_t ret;
7370 OVERLAPPED ol;
7371
7372 if (is_socket(sock))
7373 return rb_w32_send(fd, buf, size, 0);
7374
7375 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7376 if (_get_osfhandle(fd) == -1) {
7377 return -1;
7378 }
7379
7380 // If an offset is given, we can't use `_write`.
7381 if (!offset && (_osfile(fd) & FTEXT) &&
7382 (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
7383 ssize_t w = _write(fd, buf, size);
7384 if (w == (ssize_t)-1 && errno == EINVAL) {
7385 errno = map_errno(GetLastError());
7386 }
7387 return w;
7388 }
7389
7390 rb_acrt_lowio_lock_fh(fd);
7391
7392 if (!size || _osfile(fd) & FEOFLAG) {
7393 rb_acrt_lowio_unlock_fh(fd);
7394 return 0;
7395 }
7396
7397 ret = 0;
7398 retry:
7399 len = (_osfile(fd) & FDEV) ? min(MAXSIZE_CONSOLE_WRITING, size) : size;
7400 size -= len;
7401 retry2:
7402
7403 // Provide the requested offset.
7404 if (setup_overlapped(&ol, fd, TRUE, offset)) {
7405 rb_acrt_lowio_unlock_fh(fd);
7406 return -1;
7407 }
7408
7409 if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, &ol)) {
7410 err = GetLastError();
7411 if (err != ERROR_IO_PENDING) {
7412 CloseHandle(ol.hEvent);
7413 if (err == ERROR_ACCESS_DENIED)
7414 errno = EBADF;
7415 else
7416 errno = map_errno(err);
7417
7418 rb_acrt_lowio_unlock_fh(fd);
7419 return -1;
7420 }
7421
7422 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7423 if (wait != WAIT_OBJECT_0) {
7424 if (wait == WAIT_OBJECT_0 + 1)
7425 errno = EINTR;
7426 else
7427 errno = map_errno(GetLastError());
7428 CloseHandle(ol.hEvent);
7429 CancelIo((HANDLE)_osfhnd(fd));
7430 rb_acrt_lowio_unlock_fh(fd);
7431 return -1;
7432 }
7433
7434 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) {
7435 errno = map_errno(GetLastError());
7436 CloseHandle(ol.hEvent);
7437 CancelIo((HANDLE)_osfhnd(fd));
7438 rb_acrt_lowio_unlock_fh(fd);
7439 return -1;
7440 }
7441 }
7442
7443 finish_overlapped(&ol, fd, written, offset);
7444
7445 ret += written;
7446 if (written == len) {
7447 buf = (const char *)buf + len;
7448 if (size > 0)
7449 goto retry;
7450 }
7451 if (ret == 0) {
7452 size_t newlen = len / 2;
7453 if (newlen > 0) {
7454 size += len - newlen;
7455 len = newlen;
7456 goto retry2;
7457 }
7458 ret = -1;
7459 errno = EWOULDBLOCK;
7460 }
7461
7462 rb_acrt_lowio_unlock_fh(fd);
7463
7464 return ret;
7465}
7466
7467ssize_t
7468rb_w32_read(int fd, void *buf, size_t size)
7469{
7470 return rb_w32_read_internal(fd, buf, size, NULL);
7471}
7472
7473ssize_t
7474rb_w32_write(int fd, const void *buf, size_t size)
7475{
7476 return rb_w32_write_internal(fd, buf, size, NULL);
7477}
7478
7479ssize_t
7480rb_w32_pread(int descriptor, void *base, size_t size, rb_off_t offset)
7481{
7482 return rb_w32_read_internal(descriptor, base, size, &offset);
7483}
7484
7485ssize_t
7486rb_w32_pwrite(int descriptor, const void *base, size_t size, rb_off_t offset)
7487{
7488 return rb_w32_write_internal(descriptor, base, size, &offset);
7489}
7490
7491/* License: Ruby's */
7492long
7493rb_w32_write_console(uintptr_t strarg, int fd)
7494{
7495 HANDLE handle;
7496 DWORD dwMode, reslen;
7497 VALUE str = strarg;
7498 int encindex;
7499 WCHAR *wbuffer = 0;
7500 const WCHAR *ptr, *next;
7501 struct constat *s;
7502 long len;
7503
7504 handle = (HANDLE)_osfhnd(fd);
7505 if (!GetConsoleMode(handle, &dwMode))
7506 return -1L;
7507
7508 s = constat_handle(handle);
7509 if (!s) return -1L;
7510 encindex = ENCODING_GET(str);
7511 switch (encindex) {
7512 default:
7513 if (!rb_econv_has_convpath_p(rb_enc_name(rb_enc_from_index(encindex)), "UTF-8"))
7514 return -1L;
7515 str = rb_str_conv_enc_opts(str, NULL, rb_enc_from_index(ENCINDEX_UTF_8),
7517 /* fall through */
7518 case ENCINDEX_US_ASCII:
7519 case ENCINDEX_ASCII_8BIT:
7520 /* assume UTF-8 */
7521 case ENCINDEX_UTF_8:
7522 ptr = wbuffer = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(str), RSTRING_LEN(str), &len);
7523 if (!ptr) return -1L;
7524 break;
7525 case ENCINDEX_UTF_16LE:
7526 ptr = (const WCHAR *)RSTRING_PTR(str);
7527 len = RSTRING_LEN(str) / sizeof(WCHAR);
7528 break;
7529 }
7530 reslen = 0;
7531 if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7532 if (!WriteConsoleW(handle, ptr, len, &reslen, NULL))
7533 reslen = (DWORD)-1L;
7534 }
7535 else {
7536 while (len > 0) {
7537 long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
7538 reslen += next - ptr;
7539 if (curlen > 0) {
7540 DWORD written;
7541 if (!WriteConsoleW(handle, ptr, curlen, &written, NULL)) {
7542 reslen = (DWORD)-1L;
7543 break;
7544 }
7545 }
7546 ptr = next;
7547 }
7548 }
7549 RB_GC_GUARD(str);
7550 free(wbuffer);
7551 return (long)reslen;
7552}
7553
7554#if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7555/* License: Ruby's */
7556static int
7557unixtime_to_filetime(time_t time, FILETIME *ft)
7558{
7559 ULARGE_INTEGER tmp;
7560
7561 tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7562 ft->dwLowDateTime = tmp.LowPart;
7563 ft->dwHighDateTime = tmp.HighPart;
7564 return 0;
7565}
7566#endif
7567
7568/* License: Ruby's */
7569static int
7570timespec_to_filetime(const struct timespec *ts, FILETIME *ft)
7571{
7572 ULARGE_INTEGER tmp;
7573
7574 tmp.QuadPart = ((LONG_LONG)ts->tv_sec + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7575 tmp.QuadPart += ts->tv_nsec / 100;
7576 ft->dwLowDateTime = tmp.LowPart;
7577 ft->dwHighDateTime = tmp.HighPart;
7578 return 0;
7579}
7580
7581/* License: Ruby's */
7582static int
7583wutimensat(int dirfd, const WCHAR *path, const struct timespec *times, int flags)
7584{
7585 HANDLE hFile;
7586 FILETIME atime, mtime;
7587 struct stati128 stat;
7588 int ret = 0;
7589
7590 /* TODO: When path is absolute, dirfd should be ignored. */
7591 if (dirfd != AT_FDCWD) {
7592 errno = ENOSYS;
7593 return -1;
7594 }
7595
7596 if (flags != 0) {
7597 errno = EINVAL; /* AT_SYMLINK_NOFOLLOW isn't supported. */
7598 return -1;
7599 }
7600
7601 if (wstati128(path, &stat, FALSE)) {
7602 return -1;
7603 }
7604
7605 if (times) {
7606 if (timespec_to_filetime(&times[0], &atime)) {
7607 return -1;
7608 }
7609 if (timespec_to_filetime(&times[1], &mtime)) {
7610 return -1;
7611 }
7612 }
7613 else {
7614 GetSystemTimePreciseAsFileTime(&atime);
7615 mtime = atime;
7616 }
7617
7618 RUBY_CRITICAL {
7619 const DWORD attr = GetFileAttributesW(path);
7620 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7621 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7622 hFile = open_special(path, GENERIC_WRITE, 0);
7623 if (hFile == INVALID_HANDLE_VALUE) {
7624 errno = map_errno(GetLastError());
7625 ret = -1;
7626 }
7627 else {
7628 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7629 errno = map_errno(GetLastError());
7630 ret = -1;
7631 }
7632 CloseHandle(hFile);
7633 }
7634 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7635 SetFileAttributesW(path, attr);
7636 }
7637
7638 return ret;
7639}
7640
7641/* License: Ruby's */
7642static int
7643w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags, UINT cp)
7644{
7645 WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7646 int ret = -1;
7647
7648 if (wpath) {
7649 ret = wutimensat(dirfd, wpath, times, flags);
7650 free(wpath);
7651 }
7652 return ret;
7653}
7654
7655/* License: Ruby's */
7656int
7657rb_w32_uutime(const char *path, const struct utimbuf *times)
7658{
7659 struct timespec ts[2];
7660
7661 ts[0].tv_sec = times->actime;
7662 ts[0].tv_nsec = 0;
7663 ts[1].tv_sec = times->modtime;
7664 ts[1].tv_nsec = 0;
7665 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7666}
7667
7668/* License: Ruby's */
7669int
7670rb_w32_utime(const char *path, const struct utimbuf *times)
7671{
7672 struct timespec ts[2];
7673
7674 ts[0].tv_sec = times->actime;
7675 ts[0].tv_nsec = 0;
7676 ts[1].tv_sec = times->modtime;
7677 ts[1].tv_nsec = 0;
7678 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7679}
7680
7681/* License: Ruby's */
7682int
7683rb_w32_uutimes(const char *path, const struct timeval *times)
7684{
7685 struct timespec ts[2];
7686
7687 ts[0].tv_sec = times[0].tv_sec;
7688 ts[0].tv_nsec = times[0].tv_usec * 1000;
7689 ts[1].tv_sec = times[1].tv_sec;
7690 ts[1].tv_nsec = times[1].tv_usec * 1000;
7691 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7692}
7693
7694/* License: Ruby's */
7695int
7696rb_w32_utimes(const char *path, const struct timeval *times)
7697{
7698 struct timespec ts[2];
7699
7700 ts[0].tv_sec = times[0].tv_sec;
7701 ts[0].tv_nsec = times[0].tv_usec * 1000;
7702 ts[1].tv_sec = times[1].tv_sec;
7703 ts[1].tv_nsec = times[1].tv_usec * 1000;
7704 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7705}
7706
7707/* License: Ruby's */
7708int
7709rb_w32_uutimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7710{
7711 return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7712}
7713
7714/* License: Ruby's */
7715int
7716rb_w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7717{
7718 return w32_utimensat(dirfd, path, times, flags, filecp());
7719}
7720
7721/* License: Ruby's */
7722int
7723rb_w32_uchdir(const char *path)
7724{
7725 WCHAR *wpath;
7726 int ret;
7727
7728 if (!(wpath = utf8_to_wstr(path, NULL)))
7729 return -1;
7730 ret = _wchdir(wpath);
7731 free(wpath);
7732 return ret;
7733}
7734
7735/* License: Ruby's */
7736static int
7737wmkdir(const WCHAR *wpath, int mode)
7738{
7739 int ret = -1;
7740
7741 RUBY_CRITICAL do {
7742 if (CreateDirectoryW(wpath, NULL) == FALSE) {
7743 errno = map_errno(GetLastError());
7744 break;
7745 }
7746 if (_wchmod(wpath, mode) == -1) {
7747 RemoveDirectoryW(wpath);
7748 break;
7749 }
7750 ret = 0;
7751 } while (0);
7752 return ret;
7753}
7754
7755/* License: Ruby's */
7756int
7757rb_w32_umkdir(const char *path, int mode)
7758{
7759 WCHAR *wpath;
7760 int ret;
7761
7762 if (!(wpath = utf8_to_wstr(path, NULL)))
7763 return -1;
7764 ret = wmkdir(wpath, mode);
7765 free(wpath);
7766 return ret;
7767}
7768
7769/* License: Ruby's */
7770int
7771rb_w32_mkdir(const char *path, int mode)
7772{
7773 WCHAR *wpath;
7774 int ret;
7775
7776 if (!(wpath = filecp_to_wstr(path, NULL)))
7777 return -1;
7778 ret = wmkdir(wpath, mode);
7779 free(wpath);
7780 return ret;
7781}
7782
7783/* License: Ruby's */
7784static int
7785wrmdir(const WCHAR *wpath)
7786{
7787 int ret = 0;
7788 RUBY_CRITICAL {
7789 const DWORD attr = GetFileAttributesW(wpath);
7790 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7791 SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
7792 }
7793 if (RemoveDirectoryW(wpath) == FALSE) {
7794 errno = map_errno(GetLastError());
7795 ret = -1;
7796 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7797 SetFileAttributesW(wpath, attr);
7798 }
7799 }
7800 }
7801 return ret;
7802}
7803
7804/* License: Ruby's */
7805int
7806rb_w32_rmdir(const char *path)
7807{
7808 WCHAR *wpath;
7809 int ret;
7810
7811 if (!(wpath = filecp_to_wstr(path, NULL)))
7812 return -1;
7813 ret = wrmdir(wpath);
7814 free(wpath);
7815 return ret;
7816}
7817
7818/* License: Ruby's */
7819int
7820rb_w32_urmdir(const char *path)
7821{
7822 WCHAR *wpath;
7823 int ret;
7824
7825 if (!(wpath = utf8_to_wstr(path, NULL)))
7826 return -1;
7827 ret = wrmdir(wpath);
7828 free(wpath);
7829 return ret;
7830}
7831
7832/* License: Ruby's */
7833static int
7834wunlink(const WCHAR *path)
7835{
7836 int ret = 0;
7837 const DWORD SYMLINKD = FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY;
7838 RUBY_CRITICAL {
7839 const DWORD attr = GetFileAttributesW(path);
7840 if (attr == (DWORD)-1) {
7841 }
7842 else if ((attr & SYMLINKD) == SYMLINKD) {
7843 ret = RemoveDirectoryW(path);
7844 }
7845 else {
7846 if (attr & FILE_ATTRIBUTE_READONLY) {
7847 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7848 }
7849 ret = DeleteFileW(path);
7850 }
7851 if (!ret) {
7852 errno = map_errno(GetLastError());
7853 ret = -1;
7854 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7855 SetFileAttributesW(path, attr);
7856 }
7857 }
7858 }
7859 return ret;
7860}
7861
7862/* License: Ruby's */
7863int
7864rb_w32_uunlink(const char *path)
7865{
7866 WCHAR *wpath;
7867 int ret;
7868
7869 if (!(wpath = utf8_to_wstr(path, NULL)))
7870 return -1;
7871 ret = wunlink(wpath);
7872 free(wpath);
7873 return ret;
7874}
7875
7876/* License: Ruby's */
7877int
7878rb_w32_unlink(const char *path)
7879{
7880 WCHAR *wpath;
7881 int ret;
7882
7883 if (!(wpath = filecp_to_wstr(path, NULL)))
7884 return -1;
7885 ret = wunlink(wpath);
7886 free(wpath);
7887 return ret;
7888}
7889
7890/* License: Ruby's */
7891int
7892rb_w32_uchmod(const char *path, int mode)
7893{
7894 WCHAR *wpath;
7895 int ret;
7896
7897 if (!(wpath = utf8_to_wstr(path, NULL)))
7898 return -1;
7899 ret = _wchmod(wpath, mode);
7900 free(wpath);
7901 return ret;
7902}
7903
7904/* License: Ruby's */
7905int
7906fchmod(int fd, int mode)
7907{
7908 /* from winbase.h of the mingw-w64 runtime package. */
7909 struct {
7910 LARGE_INTEGER CreationTime;
7911 LARGE_INTEGER LastAccessTime;
7912 LARGE_INTEGER LastWriteTime;
7913 LARGE_INTEGER ChangeTime;
7914 DWORD FileAttributes;
7915 } info = {{{0}}, {{0}}, {{0}},}; /* fields with 0 are unchanged */
7916 HANDLE h = (HANDLE)_get_osfhandle(fd);
7917
7918 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
7919 if (!(mode & 0200)) info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
7920 if (!SetFileInformationByHandle(h, 0, &info, sizeof(info))) {
7921 errno = map_errno(GetLastError());
7922 return -1;
7923 }
7924 return 0;
7925}
7926
7927/* License: Ruby's */
7928int
7929rb_w32_isatty(int fd)
7930{
7931 DWORD mode;
7932
7933 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7934 if (_get_osfhandle(fd) == -1) {
7935 return 0;
7936 }
7937 if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
7938 errno = ENOTTY;
7939 return 0;
7940 }
7941 return 1;
7942}
7943
7944#if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7945extern long _ftol(double);
7946/* License: Ruby's */
7947long
7948_ftol2(double d)
7949{
7950 return _ftol(d);
7951}
7952
7953/* License: Ruby's */
7954long
7955_ftol2_sse(double d)
7956{
7957 return _ftol(d);
7958}
7959#endif
7960
7961#ifndef signbit
7962/* License: Ruby's */
7963int
7964signbit(double x)
7965{
7966 int *ip = (int *)(&x + 1) - 1;
7967 return *ip < 0;
7968}
7969#endif
7970
7971/* License: Ruby's */
7972const char * WSAAPI
7973rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
7974{
7975 return (inet_ntop)(af, (void *)addr, numaddr, numaddr_len);
7976}
7977
7978/* License: Ruby's */
7979int WSAAPI
7980rb_w32_inet_pton(int af, const char *src, void *dst)
7981{
7982 return (inet_pton)(af, src, dst);
7983}
7984
7985/* License: Ruby's */
7986char
7987rb_w32_fd_is_text(int fd)
7988{
7989 return _osfile(fd) & FTEXT;
7990}
7991
7992#if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7993/* License: Ruby's */
7994static int
7995unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
7996{
7997 FILETIME ft;
7998 if (unixtime_to_filetime(t, &ft)) return -1;
7999 if (!FileTimeToSystemTime(&ft, st)) return -1;
8000 return 0;
8001}
8002
8003/* License: Ruby's */
8004static void
8005systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
8006{
8007 int y = st->wYear, m = st->wMonth, d = st->wDay;
8008 t->tm_sec = st->wSecond;
8009 t->tm_min = st->wMinute;
8010 t->tm_hour = st->wHour;
8011 t->tm_mday = st->wDay;
8012 t->tm_mon = st->wMonth - 1;
8013 t->tm_year = y - 1900;
8014 t->tm_wday = st->wDayOfWeek;
8015 switch (m) {
8016 case 1:
8017 break;
8018 case 2:
8019 d += 31;
8020 break;
8021 default:
8022 d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
8023 d += ((m - 3) * 153 + 2) / 5;
8024 break;
8025 }
8026 t->tm_yday = d - 1;
8027}
8028
8029/* License: Ruby's */
8030static int
8031systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
8032{
8033 TIME_ZONE_INFORMATION stdtz;
8034 SYSTEMTIME sst;
8035
8036 if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
8037 if (!tz) {
8038 GetTimeZoneInformation(&stdtz);
8039 tz = &stdtz;
8040 }
8041 if (tz->StandardBias == tz->DaylightBias) return 0;
8042 if (!tz->StandardDate.wMonth) return 0;
8043 if (!tz->DaylightDate.wMonth) return 0;
8044 if (tz != &stdtz) stdtz = *tz;
8045
8046 stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
8047 if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst)) return 0;
8048 if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
8049 return 0;
8050 return 1;
8051}
8052#endif
8053
8054#ifdef HAVE__GMTIME64_S
8055# ifndef HAVE__LOCALTIME64_S
8056/* assume same as _gmtime64_s() */
8057# define HAVE__LOCALTIME64_S 1
8058# endif
8059# ifndef MINGW_HAS_SECURE_API
8060 _CRTIMP errno_t __cdecl _gmtime64_s(struct tm* tm, const __time64_t *time);
8061 _CRTIMP errno_t __cdecl _localtime64_s(struct tm* tm, const __time64_t *time);
8062# endif
8063# define gmtime_s _gmtime64_s
8064# define localtime_s _localtime64_s
8065#endif
8066
8067/* License: Ruby's */
8068struct tm *
8069gmtime_r(const time_t *tp, struct tm *rp)
8070{
8071 int e = EINVAL;
8072 if (!tp || !rp) {
8073 error:
8074 errno = e;
8075 return NULL;
8076 }
8077#if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8078 e = gmtime_s(rp, tp);
8079 if (e != 0) goto error;
8080#else
8081 {
8082 SYSTEMTIME st;
8083 if (unixtime_to_systemtime(*tp, &st)) goto error;
8084 rp->tm_isdst = 0;
8085 systemtime_to_tm(&st, rp);
8086 }
8087#endif
8088 return rp;
8089}
8090
8091/* License: Ruby's */
8092struct tm *
8093localtime_r(const time_t *tp, struct tm *rp)
8094{
8095 int e = EINVAL;
8096 if (!tp || !rp) {
8097 error:
8098 errno = e;
8099 return NULL;
8100 }
8101#if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8102 e = localtime_s(rp, tp);
8103 if (e) goto error;
8104#else
8105 {
8106 SYSTEMTIME gst, lst;
8107 if (unixtime_to_systemtime(*tp, &gst)) goto error;
8108 rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
8109 systemtime_to_tm(&lst, rp);
8110 }
8111#endif
8112 return rp;
8113}
8114
8115/* License: Ruby's */
8116int
8117rb_w32_wrap_io_handle(HANDLE h, int flags)
8118{
8119 BOOL tmp;
8120 int len = sizeof(tmp);
8121 int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (char *)&tmp, &len);
8122 if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
8123 int f = 0;
8124 if (flags & O_NONBLOCK) {
8125 flags &= ~O_NONBLOCK;
8126 f = O_NONBLOCK;
8127 }
8128 socklist_insert((SOCKET)h, f);
8129 }
8130 else if (flags & O_NONBLOCK) {
8131 errno = EINVAL;
8132 return -1;
8133 }
8134 return rb_w32_open_osfhandle((intptr_t)h, flags);
8135}
8136
8137/* License: Ruby's */
8138int
8139rb_w32_unwrap_io_handle(int fd)
8140{
8141 SOCKET sock = TO_SOCKET(fd);
8142 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
8143 if (!is_socket(sock)) {
8144 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
8145 constat_delete((HANDLE)sock);
8146 }
8147 else {
8148 socklist_delete(&sock, NULL);
8149 }
8150 return _close(fd);
8151}
8152
8153#if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8154/*
8155 * Set floating point precision for pow() of mingw-w64 x86.
8156 * With default precision the result is not proper on WinXP.
8157 */
8158double
8159rb_w32_pow(double x, double y)
8160{
8161#undef pow
8162 double r;
8163 unsigned int default_control = _controlfp(0, 0);
8164 _controlfp(_PC_64, _MCW_PC);
8165 r = pow(x, y);
8166 /* Restore setting */
8167 _controlfp(default_control, _MCW_PC);
8168 return r;
8169}
8170#endif
8171
8172typedef struct {
8173 BOOL file_id_p;
8174 union {
8175 BY_HANDLE_FILE_INFORMATION bhfi;
8176 FILE_ID_INFO fii;
8177 } info;
8179
8180static HANDLE
8181w32_io_info(VALUE *file, w32_io_info_t *st)
8182{
8183 VALUE tmp;
8184 HANDLE f, ret = 0;
8185
8186 tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
8187 if (!NIL_P(tmp)) {
8188 f = (HANDLE)rb_w32_get_osfhandle(rb_io_descriptor(tmp));
8189 if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
8190 }
8191 else {
8192 VALUE tmp;
8193 WCHAR *ptr;
8194 int len;
8195 VALUE v;
8196
8197 FilePathValue(*file);
8198 tmp = rb_str_encode_ospath(*file);
8199 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
8200 ptr = ALLOCV_N(WCHAR, v, len);
8201 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
8202 f = CreateFileW(ptr, 0,
8203 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
8204 FILE_FLAG_BACKUP_SEMANTICS, NULL);
8205 ALLOCV_END(v);
8206 if (f == INVALID_HANDLE_VALUE) return f;
8207 ret = f;
8208 }
8209 if (GetFileType(f) == FILE_TYPE_DISK) {
8210 ZeroMemory(st, sizeof(*st));
8211 if (get_ino(f, &st->info.fii)) {
8212 st->file_id_p = TRUE;
8213 return ret;
8214 }
8215 else if (GetLastError() != ERROR_INVALID_PARAMETER) {
8216 CloseHandle(f);
8217 return INVALID_HANDLE_VALUE;
8218 }
8219 /* this API may not work at files on non Microsoft SMB
8220 * server, fallback to old API then. */
8221 if (GetFileInformationByHandle(f, &st->info.bhfi)) {
8222 st->file_id_p = FALSE;
8223 return ret;
8224 }
8225 }
8226 if (ret) CloseHandle(ret);
8227 return INVALID_HANDLE_VALUE;
8228}
8229
8230static VALUE
8231close_handle(VALUE h)
8232{
8233 CloseHandle((HANDLE)h);
8234 return Qfalse;
8235}
8236
8238 VALUE *fname;
8239 w32_io_info_t *st;
8240};
8241
8242static VALUE
8243call_w32_io_info(VALUE arg)
8244{
8245 struct w32_io_info_args *p = (void *)arg;
8246 return (VALUE)w32_io_info(p->fname, p->st);
8247}
8248
8249VALUE
8250rb_w32_file_identical_p(VALUE fname1, VALUE fname2)
8251{
8252 w32_io_info_t st1, st2;
8253 HANDLE f1 = 0, f2 = 0;
8254
8255 f1 = w32_io_info(&fname1, &st1);
8256 if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
8257 if (f1) {
8258 struct w32_io_info_args arg;
8259 arg.fname = &fname2;
8260 arg.st = &st2;
8261 f2 = (HANDLE)rb_ensure(call_w32_io_info, (VALUE)&arg, close_handle, (VALUE)f1);
8262 }
8263 else {
8264 f2 = w32_io_info(&fname2, &st2);
8265 }
8266 if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
8267 if (f2) CloseHandle(f2);
8268
8269 if (st1.file_id_p != st2.file_id_p) return Qfalse;
8270 if (!st1.file_id_p) {
8271 if (st1.info.bhfi.dwVolumeSerialNumber == st2.info.bhfi.dwVolumeSerialNumber &&
8272 st1.info.bhfi.nFileIndexHigh == st2.info.bhfi.nFileIndexHigh &&
8273 st1.info.bhfi.nFileIndexLow == st2.info.bhfi.nFileIndexLow)
8274 return Qtrue;
8275 }
8276 else {
8277 if (st1.info.fii.VolumeSerialNumber == st2.info.fii.VolumeSerialNumber &&
8278 memcmp(&st1.info.fii.FileId, &st2.info.fii.FileId, sizeof(FILE_ID_128)) == 0)
8279 return Qtrue;
8280 }
8281 return Qfalse;
8282}
8283
8284int
8285rb_w32_set_thread_description(HANDLE th, const WCHAR *name)
8286{
8287 int result = FALSE;
8288 typedef HRESULT (WINAPI *set_thread_description_func)(HANDLE, PCWSTR);
8289 static set_thread_description_func set_thread_description =
8290 (set_thread_description_func)-1;
8291 if (set_thread_description == (set_thread_description_func)-1) {
8292 /* Since Windows 10, version 1607 and Windows Server 2016 */
8293 set_thread_description = (set_thread_description_func)
8294 get_proc_address("kernel32", "SetThreadDescription", NULL);
8295 }
8296 if (set_thread_description) {
8297 result = set_thread_description(th, name);
8298 }
8299 return result;
8300}
8301
8302int
8303rb_w32_set_thread_description_str(HANDLE th, VALUE name)
8304{
8305 int idx, result = FALSE;
8306 WCHAR *s;
8307
8308 if (NIL_P(name)) {
8309 return rb_w32_set_thread_description(th, L"");
8310 }
8311 s = (WCHAR *)StringValueCStr(name);
8312 idx = rb_enc_get_index(name);
8313 if (idx == ENCINDEX_UTF_16LE) {
8314 result = rb_w32_set_thread_description(th, s);
8315 }
8316 else {
8317 name = rb_str_conv_enc(name, rb_enc_from_index(idx), rb_utf8_encoding());
8318 s = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(name), RSTRING_LEN(name)+1, NULL);
8319 result = rb_w32_set_thread_description(th, s);
8320 free(s);
8321 }
8322 RB_GC_GUARD(name);
8323 return result;
8324}
8325
8326VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notimplement;
8327
8328#if RUBY_MSVCRT_VERSION < 120
8329#include "missing/nextafter.c"
8330#endif
8331
8332void *
8333rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset)
8334{
8335 void *ptr;
8336 //DWORD protect = 0;
8337 DWORD protect = PAGE_EXECUTE_READWRITE;
8338
8339 if (fd > 0 || offset) {
8340 /* not supported */
8341 errno = EINVAL;
8342 return MAP_FAILED;
8343 }
8344
8345/*
8346 if (prot & PROT_EXEC) {
8347 if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8348 else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8349 else protect = PAGE_EXECUTE;
8350 }
8351 else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8352 else if (prot & PROT_READ) protect = PAGE_READONLY;
8353*/
8354 ptr = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, protect);
8355 if (!ptr) {
8356 errno = rb_w32_map_errno(GetLastError());
8357 return MAP_FAILED;
8358 }
8359
8360 return ptr;
8361}
8362
8363int
8364rb_w32_munmap(void *addr, size_t len)
8365{
8366 if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8367 errno = rb_w32_map_errno(GetLastError());
8368 return -1;
8369 }
8370
8371 return 0;
8372}
8373
8374inline int
8375rb_w32_mprotect(void *addr, size_t len, int prot)
8376{
8377/*
8378 DWORD protect = 0;
8379 if (prot & PROT_EXEC) {
8380 if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8381 else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8382 else protect = PAGE_EXECUTE;
8383 }
8384 else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8385 else if (prot & PROT_READ) protect = PAGE_READONLY;
8386 if (!VirtualProtect(addr, len, protect, NULL)) {
8387 errno = rb_w32_map_errno(GetLastError());
8388 return -1;
8389 }
8390*/
8391 if (prot & PROT_EXEC) {
8392 if (!FlushInstructionCache(GetCurrentProcess(), addr, len)) {
8393 errno = rb_w32_map_errno(GetLastError());
8394 return -1;
8395 }
8396 }
8397 return 0;
8398}
#define LONG_LONG
Definition long_long.h:38
#define RBIMPL_ATTR_FORMAT(x, y, z)
Wraps (or simulates) __attribute__((format))
Definition format.h:29
int ruby_glob_func(const char *path, VALUE arg, void *enc)
Type of a glob callback function.
Definition glob.h:49
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
#define ISSPACE
Old name of rb_isspace.
Definition ctype.h:88
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define xrealloc
Old name of ruby_xrealloc.
Definition xmalloc.h:56
#define ECONV_UNDEF_REPLACE
Old name of RUBY_ECONV_UNDEF_REPLACE.
Definition transcode.h:526
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
Definition encoding.h:109
#define ECONV_INVALID_REPLACE
Old name of RUBY_ECONV_INVALID_REPLACE.
Definition transcode.h:524
#define ASSUME
Old name of RBIMPL_ASSUME.
Definition assume.h:27
#define ISALPHA
Old name of rb_isalpha.
Definition ctype.h:92
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition memory.h:405
#define ISALNUM
Old name of rb_isalnum.
Definition ctype.h:91
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:406
Encoding relates APIs.
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
Definition string.c:1289
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:1173
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:3448
#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.
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:701
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