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