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