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