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