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