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