Ruby 3.2.5p208 (2024-07-26 revision 31d0f1a2e7dbfb60731d1f05b868e1d578cda493)
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 }
5898 else if (rb_w32_reparse_symlink_p(path)) {
5899 /* TODO: size in which encoding? */
5900 st->st_size = 0;
5901 mode |= S_IFLNK | S_IEXEC;
5902 }
5903 else {
5904 mode |= S_IFDIR | S_IEXEC;
5905 }
5906 }
5907 }
5908 CloseHandle(f);
5909 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5910 if (check_valid_dir(path)) return -1;
5911 }
5912 st->st_mode = fileattr_to_unixmode(attr, path, mode);
5913 if (len) {
5914 finalname[min(len, numberof(finalname)-1)] = L'\0';
5915 path = finalname;
5916 if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
5917 path += numberof(namespace_prefix);
5918 }
5919 }
5920 else {
5921 if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME)
5922 || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) {
5923 errno = map_errno(open_error);
5924 return -1;
5925 }
5926
5927 if (stat_by_find(path, st)) return -1;
5928 }
5929
5930 st->st_dev = st->st_rdev = path_drive(path);
5931
5932 return 0;
5933}
5934
5935/* License: Ruby's */
5936int
5937rb_w32_stat(const char *path, struct stat *st)
5938{
5939 struct stati128 tmp;
5940
5941 if (w32_stati128(path, &tmp, filecp(), FALSE)) return -1;
5942 COPY_STAT(tmp, *st, (_off_t));
5943 return 0;
5944}
5945
5946/* License: Ruby's */
5947static int
5948wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat)
5949{
5950 WCHAR *buf1;
5951 int ret, size;
5952 VALUE v;
5953
5954 if (!path || !st) {
5955 errno = EFAULT;
5956 return -1;
5957 }
5958 size = lstrlenW(path) + 2;
5959 buf1 = ALLOCV_N(WCHAR, v, size);
5960 if (!(path = name_for_stat(buf1, path)))
5961 return -1;
5962 ret = winnt_stat(path, st, lstat);
5963 if (v)
5964 ALLOCV_END(v);
5965
5966 return ret;
5967}
5968
5969/* License: Ruby's */
5970static WCHAR *
5971name_for_stat(WCHAR *buf1, const WCHAR *path)
5972{
5973 const WCHAR *p;
5974 WCHAR *s, *end;
5975 int len;
5976
5977 for (p = path, s = buf1; *p; p++, s++) {
5978 if (*p == L'/')
5979 *s = L'\\';
5980 else
5981 *s = *p;
5982 }
5983 *s = '\0';
5984 len = s - buf1;
5985 if (!len || L'\"' == *(--s)) {
5986 errno = ENOENT;
5987 return NULL;
5988 }
5989 end = buf1 + len - 1;
5990
5991 if (isUNCRoot(buf1)) {
5992 if (*end == L'.')
5993 *end = L'\0';
5994 else if (*end != L'\\')
5995 lstrcatW(buf1, L"\\");
5996 }
5997 else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
5998 lstrcatW(buf1, L".");
5999
6000 return buf1;
6001}
6002
6003/* License: Ruby's */
6004int
6005rb_w32_ustati128(const char *path, struct stati128 *st)
6006{
6007 return w32_stati128(path, st, CP_UTF8, FALSE);
6008}
6009
6010/* License: Ruby's */
6011int
6012rb_w32_stati128(const char *path, struct stati128 *st)
6013{
6014 return w32_stati128(path, st, filecp(), FALSE);
6015}
6016
6017/* License: Ruby's */
6018static int
6019w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat)
6020{
6021 WCHAR *wpath;
6022 int ret;
6023
6024 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6025 return -1;
6026 ret = wstati128(wpath, st, lstat);
6027 free(wpath);
6028 return ret;
6029}
6030
6031/* License: Ruby's */
6032int
6033rb_w32_ulstati128(const char *path, struct stati128 *st)
6034{
6035 return w32_stati128(path, st, CP_UTF8, TRUE);
6036}
6037
6038/* License: Ruby's */
6039int
6040rb_w32_lstati128(const char *path, struct stati128 *st)
6041{
6042 return w32_stati128(path, st, filecp(), TRUE);
6043}
6044
6045/* License: Ruby's */
6046rb_off_t
6047rb_w32_lseek(int fd, rb_off_t ofs, int whence)
6048{
6049 SOCKET sock = TO_SOCKET(fd);
6050 if (is_socket(sock) || is_pipe(sock)) {
6051 errno = ESPIPE;
6052 return -1;
6053 }
6054 return _lseeki64(fd, ofs, whence);
6055}
6056
6057/* License: Ruby's */
6058static int
6059w32_access(const char *path, int mode, UINT cp)
6060{
6061 struct stati128 stat;
6062 if (w32_stati128(path, &stat, cp, FALSE) != 0)
6063 return -1;
6064 mode <<= 6;
6065 if ((stat.st_mode & mode) != mode) {
6066 errno = EACCES;
6067 return -1;
6068 }
6069 return 0;
6070}
6071
6072/* License: Ruby's */
6073int
6074rb_w32_access(const char *path, int mode)
6075{
6076 return w32_access(path, mode, filecp());
6077}
6078
6079/* License: Ruby's */
6080int
6081rb_w32_uaccess(const char *path, int mode)
6082{
6083 return w32_access(path, mode, CP_UTF8);
6084}
6085
6086/* License: Ruby's */
6087static int
6088rb_chsize(HANDLE h, rb_off_t size)
6089{
6090 long upos, lpos, usize, lsize;
6091 int ret = -1;
6092 DWORD e;
6093
6094 if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6095 (e = GetLastError())) {
6096 errno = map_errno(e);
6097 return -1;
6098 }
6099 usize = (long)(size >> 32);
6100 lsize = (long)size;
6101 if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
6102 (e = GetLastError())) {
6103 errno = map_errno(e);
6104 }
6105 else if (!SetEndOfFile(h)) {
6106 errno = map_errno(GetLastError());
6107 }
6108 else {
6109 ret = 0;
6110 }
6111 SetFilePointer(h, lpos, &upos, SEEK_SET);
6112 return ret;
6113}
6114
6115/* License: Ruby's */
6116static int
6117w32_truncate(const char *path, rb_off_t length, UINT cp)
6118{
6119 HANDLE h;
6120 int ret;
6121 WCHAR *wpath;
6122
6123 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6124 return -1;
6125 h = CreateFileW(wpath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
6126 if (h == INVALID_HANDLE_VALUE) {
6127 errno = map_errno(GetLastError());
6128 free(wpath);
6129 return -1;
6130 }
6131 free(wpath);
6132 ret = rb_chsize(h, length);
6133 CloseHandle(h);
6134 return ret;
6135}
6136
6137/* License: Ruby's */
6138int
6139rb_w32_utruncate(const char *path, rb_off_t length)
6140{
6141 return w32_truncate(path, length, CP_UTF8);
6142}
6143
6144/* License: Ruby's */
6145int
6146rb_w32_truncate(const char *path, rb_off_t length)
6147{
6148 return w32_truncate(path, length, filecp());
6149}
6150
6151/* License: Ruby's */
6152int
6153rb_w32_ftruncate(int fd, rb_off_t length)
6154{
6155 HANDLE h;
6156
6157 h = (HANDLE)_get_osfhandle(fd);
6158 if (h == (HANDLE)-1) return -1;
6159 return rb_chsize(h, length);
6160}
6161
6162/* License: Ruby's */
6163static long
6164filetime_to_clock(FILETIME *ft)
6165{
6166 __int64 qw = ft->dwHighDateTime;
6167 qw <<= 32;
6168 qw |= ft->dwLowDateTime;
6169 qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
6170 return (long) qw;
6171}
6172
6173/* License: Ruby's */
6174int
6175rb_w32_times(struct tms *tmbuf)
6176{
6177 FILETIME create, exit, kernel, user;
6178
6179 if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
6180 tmbuf->tms_utime = filetime_to_clock(&user);
6181 tmbuf->tms_stime = filetime_to_clock(&kernel);
6182 tmbuf->tms_cutime = 0;
6183 tmbuf->tms_cstime = 0;
6184 }
6185 else {
6186 tmbuf->tms_utime = clock();
6187 tmbuf->tms_stime = 0;
6188 tmbuf->tms_cutime = 0;
6189 tmbuf->tms_cstime = 0;
6190 }
6191 return 0;
6192}
6193
6194
6195/* License: Ruby's */
6196#define yield_once() Sleep(0)
6197#define yield_until(condition) do yield_once(); while (!(condition))
6198
6199/* License: Ruby's */
6201 /* output field */
6202 void* stackaddr;
6203 int errnum;
6204
6205 /* input field */
6206 uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
6207 uintptr_t self;
6208 int argc;
6209 uintptr_t* argv;
6210};
6211
6212/* License: Ruby's */
6213static DWORD WINAPI
6214call_asynchronous(PVOID argp)
6215{
6216 DWORD ret;
6217 struct asynchronous_arg_t *arg = argp;
6218 arg->stackaddr = &argp;
6219 ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
6220 arg->errnum = errno;
6221 return ret;
6222}
6223
6224/* License: Ruby's */
6225uintptr_t
6226rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
6227 int argc, uintptr_t* argv, uintptr_t intrval)
6228{
6229 DWORD val;
6230 BOOL interrupted = FALSE;
6231 HANDLE thr;
6232
6233 RUBY_CRITICAL {
6234 struct asynchronous_arg_t arg;
6235
6236 arg.stackaddr = NULL;
6237 arg.errnum = 0;
6238 arg.func = func;
6239 arg.self = self;
6240 arg.argc = argc;
6241 arg.argv = argv;
6242
6243 thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6244
6245 if (thr) {
6246 yield_until(arg.stackaddr);
6247
6248 if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6249 interrupted = TRUE;
6250
6251 if (TerminateThread(thr, intrval)) {
6252 yield_once();
6253 }
6254 }
6255
6256 GetExitCodeThread(thr, &val);
6257 CloseHandle(thr);
6258
6259 if (interrupted) {
6260 /* must release stack of killed thread, why doesn't Windows? */
6261 MEMORY_BASIC_INFORMATION m;
6262
6263 memset(&m, 0, sizeof(m));
6264 if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
6265 Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
6266 arg.stackaddr, GetLastError()));
6267 }
6268 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6269 Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
6270 m.AllocationBase, GetLastError()));
6271 }
6272 errno = EINTR;
6273 }
6274 else {
6275 errno = arg.errnum;
6276 }
6277 }
6278 }
6279
6280 if (!thr) {
6281 rb_fatal("failed to launch waiter thread:%ld", GetLastError());
6282 }
6283
6284 return val;
6285}
6286
6287/* License: Ruby's */
6288char **
6289rb_w32_get_environ(void)
6290{
6291 WCHAR *envtop, *env;
6292 char **myenvtop, **myenv;
6293 int num;
6294
6295 /*
6296 * We avoid values started with `='. If you want to deal those values,
6297 * change this function, and some functions in hash.c which recognize
6298 * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
6299 * CygWin deals these values by changing first `=' to '!'. But we don't
6300 * use such trick and follow cmd.exe's way that just doesn't show these
6301 * values.
6302 *
6303 * This function returns UTF-8 strings.
6304 */
6305 envtop = GetEnvironmentStringsW();
6306 for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6307 if (*env != '=') num++;
6308
6309 myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
6310 for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
6311 if (*env != '=') {
6312 if (!(*myenv = wstr_to_utf8(env, NULL))) {
6313 break;
6314 }
6315 myenv++;
6316 }
6317 }
6318 *myenv = NULL;
6319 FreeEnvironmentStringsW(envtop);
6320
6321 return myenvtop;
6322}
6323
6324/* License: Ruby's */
6325void
6326rb_w32_free_environ(char **env)
6327{
6328 char **t = env;
6329
6330 while (*t) free(*t++);
6331 free(env);
6332}
6333
6334/* License: Ruby's */
6335rb_pid_t
6336rb_w32_getpid(void)
6337{
6338 return GetCurrentProcessId();
6339}
6340
6341
6342/* License: Ruby's */
6343rb_pid_t
6344rb_w32_getppid(void)
6345{
6346 typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
6347 static query_func *pNtQueryInformationProcess = (query_func *)-1;
6348 rb_pid_t ppid = 0;
6349
6350 if (pNtQueryInformationProcess == (query_func *)-1)
6351 pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
6352 if (pNtQueryInformationProcess) {
6353 struct {
6354 long ExitStatus;
6355 void* PebBaseAddress;
6356 uintptr_t AffinityMask;
6357 uintptr_t BasePriority;
6358 uintptr_t UniqueProcessId;
6359 uintptr_t ParentProcessId;
6360 } pbi;
6361 ULONG len;
6362 long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
6363 if (!ret) {
6364 ppid = pbi.ParentProcessId;
6365 }
6366 }
6367
6368 return ppid;
6369}
6370
6371STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
6372
6373/* License: Ruby's */
6374#define set_new_std_handle(newfd, handle) do { \
6375 if ((unsigned)(newfd) > 2) break; \
6376 SetStdHandle(STD_INPUT_HANDLE+(STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)*(newfd), \
6377 (handle)); \
6378 } while (0)
6379#define set_new_std_fd(newfd) set_new_std_handle(newfd, (HANDLE)rb_w32_get_osfhandle(newfd))
6380
6381/* License: Ruby's */
6382int
6383rb_w32_dup2(int oldfd, int newfd)
6384{
6385 int ret;
6386
6387 if (oldfd == newfd) return newfd;
6388 ret = dup2(oldfd, newfd);
6389 if (ret < 0) return ret;
6390 set_new_std_fd(newfd);
6391 return newfd;
6392}
6393
6394/* License: Ruby's */
6395int
6396rb_w32_uopen(const char *file, int oflag, ...)
6397{
6398 WCHAR *wfile;
6399 int ret;
6400 int pmode;
6401
6402 va_list arg;
6403 va_start(arg, oflag);
6404 pmode = va_arg(arg, int);
6405 va_end(arg);
6406
6407 if (!(wfile = utf8_to_wstr(file, NULL)))
6408 return -1;
6409 ret = w32_wopen(wfile, oflag, pmode);
6410 free(wfile);
6411 return ret;
6412}
6413
6414/* License: Ruby's */
6415static int
6416check_if_wdir(const WCHAR *wfile)
6417{
6418 DWORD attr = GetFileAttributesW(wfile);
6419 if (attr == (DWORD)-1L ||
6420 !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6421 check_valid_dir(wfile)) {
6422 return FALSE;
6423 }
6424 errno = EISDIR;
6425 return TRUE;
6426}
6427
6428/* License: Ruby's */
6429int
6430rb_w32_open(const char *file, int oflag, ...)
6431{
6432 WCHAR *wfile;
6433 int ret;
6434 int pmode;
6435
6436 va_list arg;
6437 va_start(arg, oflag);
6438 pmode = va_arg(arg, int);
6439 va_end(arg);
6440
6441 if (!(wfile = filecp_to_wstr(file, NULL)))
6442 return -1;
6443 ret = w32_wopen(wfile, oflag, pmode);
6444 free(wfile);
6445 return ret;
6446}
6447
6448/* License: Ruby's */
6449int
6450rb_w32_wopen(const WCHAR *file, int oflag, ...)
6451{
6452 int pmode = 0;
6453
6454 if (oflag & O_CREAT) {
6455 va_list arg;
6456 va_start(arg, oflag);
6457 pmode = va_arg(arg, int);
6458 va_end(arg);
6459 }
6460
6461 return w32_wopen(file, oflag, pmode);
6462}
6463
6464static int
6465w32_wopen(const WCHAR *file, int oflag, int pmode)
6466{
6467 char flags = 0;
6468 int fd;
6469 DWORD access;
6470 DWORD create;
6471 DWORD attr = FILE_ATTRIBUTE_NORMAL;
6472 SECURITY_ATTRIBUTES sec;
6473 HANDLE h;
6474 int share_delete;
6475
6476 share_delete = oflag & O_SHARE_DELETE ? FILE_SHARE_DELETE : 0;
6477 oflag &= ~O_SHARE_DELETE;
6478 if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
6479 fd = _wopen(file, oflag, pmode);
6480 if (fd == -1) {
6481 switch (errno) {
6482 case EACCES:
6483 check_if_wdir(file);
6484 break;
6485 case EINVAL:
6486 errno = map_errno(GetLastError());
6487 break;
6488 }
6489 }
6490 return fd;
6491 }
6492
6493 sec.nLength = sizeof(sec);
6494 sec.lpSecurityDescriptor = NULL;
6495 if (oflag & O_NOINHERIT) {
6496 sec.bInheritHandle = FALSE;
6497 flags |= FNOINHERIT;
6498 }
6499 else {
6500 sec.bInheritHandle = TRUE;
6501 }
6502 oflag &= ~O_NOINHERIT;
6503
6504 /* always open with binary mode */
6505 oflag &= ~(O_BINARY | O_TEXT);
6506
6507 switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
6508 case O_RDWR:
6509 access = GENERIC_READ | GENERIC_WRITE;
6510 break;
6511 case O_RDONLY:
6512 access = GENERIC_READ;
6513 break;
6514 case O_WRONLY:
6515 access = GENERIC_WRITE;
6516 break;
6517 default:
6518 errno = EINVAL;
6519 return -1;
6520 }
6521 oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
6522
6523 switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
6524 case O_CREAT:
6525 create = OPEN_ALWAYS;
6526 break;
6527 case 0:
6528 case O_EXCL:
6529 create = OPEN_EXISTING;
6530 break;
6531 case O_CREAT | O_EXCL:
6532 case O_CREAT | O_EXCL | O_TRUNC:
6533 create = CREATE_NEW;
6534 break;
6535 case O_TRUNC:
6536 case O_TRUNC | O_EXCL:
6537 create = TRUNCATE_EXISTING;
6538 break;
6539 case O_CREAT | O_TRUNC:
6540 create = CREATE_ALWAYS;
6541 break;
6542 default:
6543 errno = EINVAL;
6544 return -1;
6545 }
6546 if (oflag & O_CREAT) {
6547 /* TODO: we need to check umask here, but it's not exported... */
6548 if (!(pmode & S_IWRITE))
6549 attr = FILE_ATTRIBUTE_READONLY;
6550 }
6551 oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6552
6553 if (oflag & O_TEMPORARY) {
6554 attr |= FILE_FLAG_DELETE_ON_CLOSE;
6555 access |= DELETE;
6556 }
6557 oflag &= ~O_TEMPORARY;
6558
6559 if (oflag & _O_SHORT_LIVED)
6560 attr |= FILE_ATTRIBUTE_TEMPORARY;
6561 oflag &= ~_O_SHORT_LIVED;
6562
6563 switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
6564 case 0:
6565 break;
6566 case O_SEQUENTIAL:
6567 attr |= FILE_FLAG_SEQUENTIAL_SCAN;
6568 break;
6569 case O_RANDOM:
6570 attr |= FILE_FLAG_RANDOM_ACCESS;
6571 break;
6572 default:
6573 errno = EINVAL;
6574 return -1;
6575 }
6576 oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6577
6578 if (oflag & ~O_APPEND) {
6579 errno = EINVAL;
6580 return -1;
6581 }
6582
6583 /* allocate a C Runtime file handle */
6584 RUBY_CRITICAL {
6585 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6586 fd = _open_osfhandle((intptr_t)h, 0);
6587 CloseHandle(h);
6588 }
6589 if (fd == -1) {
6590 errno = EMFILE;
6591 return -1;
6592 }
6593 RUBY_CRITICAL {
6594 rb_acrt_lowio_lock_fh(fd);
6595 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6596 _set_osflags(fd, 0);
6597
6598 h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE | share_delete, &sec, create, attr, NULL);
6599 if (h == INVALID_HANDLE_VALUE) {
6600 DWORD e = GetLastError();
6601 if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
6602 errno = map_errno(e);
6603 rb_acrt_lowio_unlock_fh(fd);
6604 fd = -1;
6605 goto quit;
6606 }
6607
6608 switch (GetFileType(h)) {
6609 case FILE_TYPE_CHAR:
6610 flags |= FDEV;
6611 break;
6612 case FILE_TYPE_PIPE:
6613 flags |= FPIPE;
6614 break;
6615 case FILE_TYPE_UNKNOWN:
6616 errno = map_errno(GetLastError());
6617 CloseHandle(h);
6618 rb_acrt_lowio_unlock_fh(fd);
6619 fd = -1;
6620 goto quit;
6621 }
6622 if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6623 flags |= FAPPEND;
6624
6625 _set_osfhnd(fd, (intptr_t)h);
6626 _set_osflags(fd, flags | FOPEN);
6627
6628 rb_acrt_lowio_unlock_fh(fd);
6629 quit:
6630 ;
6631 }
6632
6633 return fd;
6634}
6635
6636/* License: Ruby's */
6637int
6638rb_w32_fclose(FILE *fp)
6639{
6640 int fd = fileno(fp);
6641 SOCKET sock = TO_SOCKET(fd);
6642 int save_errno = errno;
6643
6644 if (fflush(fp)) return -1;
6645 if (!is_socket(sock)) {
6646 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6647 return fclose(fp);
6648 }
6649 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6650 fclose(fp);
6651 errno = save_errno;
6652 if (closesocket(sock) == SOCKET_ERROR) {
6653 errno = map_errno(WSAGetLastError());
6654 return -1;
6655 }
6656 return 0;
6657}
6658
6659/* License: Ruby's */
6660int
6661rb_w32_pipe(int fds[2])
6662{
6663 static long serial = 0;
6664 static const char prefix[] = "\\\\.\\pipe\\ruby";
6665 enum {
6666 width_of_prefix = (int)sizeof(prefix) - 1,
6667 width_of_pid = (int)sizeof(rb_pid_t) * 2,
6668 width_of_serial = (int)sizeof(serial) * 2,
6669 width_of_ids = width_of_pid + 1 + width_of_serial + 1
6670 };
6671 char name[sizeof(prefix) + width_of_ids];
6672 SECURITY_ATTRIBUTES sec;
6673 HANDLE hRead, hWrite, h;
6674 int fdRead, fdWrite;
6675 int ret;
6676
6677 memcpy(name, prefix, width_of_prefix);
6678 snprintf(name + width_of_prefix, width_of_ids, "%.*"PRI_PIDT_PREFIX"x-%.*lx",
6679 width_of_pid, rb_w32_getpid(), width_of_serial, InterlockedIncrement(&serial)-1);
6680
6681 sec.nLength = sizeof(sec);
6682 sec.lpSecurityDescriptor = NULL;
6683 sec.bInheritHandle = FALSE;
6684
6685 RUBY_CRITICAL {
6686 hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6687 0, 2, 65536, 65536, 0, &sec);
6688 }
6689 if (hRead == INVALID_HANDLE_VALUE) {
6690 DWORD err = GetLastError();
6691 if (err == ERROR_PIPE_BUSY)
6692 errno = EMFILE;
6693 else
6694 errno = map_errno(GetLastError());
6695 return -1;
6696 }
6697
6698 RUBY_CRITICAL {
6699 hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6700 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6701 }
6702 if (hWrite == INVALID_HANDLE_VALUE) {
6703 errno = map_errno(GetLastError());
6704 CloseHandle(hRead);
6705 return -1;
6706 }
6707
6708 RUBY_CRITICAL do {
6709 ret = 0;
6710 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6711 fdRead = _open_osfhandle((intptr_t)h, 0);
6712 CloseHandle(h);
6713 if (fdRead == -1) {
6714 errno = EMFILE;
6715 CloseHandle(hWrite);
6716 CloseHandle(hRead);
6717 ret = -1;
6718 break;
6719 }
6720
6721 rb_acrt_lowio_lock_fh(fdRead);
6722 _set_osfhnd(fdRead, (intptr_t)hRead);
6723 _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
6724 rb_acrt_lowio_unlock_fh(fdRead);
6725 } while (0);
6726 if (ret)
6727 return ret;
6728
6729 RUBY_CRITICAL do {
6730 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6731 fdWrite = _open_osfhandle((intptr_t)h, 0);
6732 CloseHandle(h);
6733 if (fdWrite == -1) {
6734 errno = EMFILE;
6735 CloseHandle(hWrite);
6736 ret = -1;
6737 break;
6738 }
6739 rb_acrt_lowio_lock_fh(fdWrite);
6740 _set_osfhnd(fdWrite, (intptr_t)hWrite);
6741 _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
6742 rb_acrt_lowio_unlock_fh(fdWrite);
6743 } while (0);
6744 if (ret) {
6745 rb_w32_close(fdRead);
6746 return ret;
6747 }
6748
6749 fds[0] = fdRead;
6750 fds[1] = fdWrite;
6751
6752 return 0;
6753}
6754
6755/* License: Ruby's */
6756static int
6757console_emulator_p(void)
6758{
6759#ifdef _WIN32_WCE
6760 return FALSE;
6761#else
6762 const void *const func = WriteConsoleW;
6763 HMODULE k;
6764 MEMORY_BASIC_INFORMATION m;
6765
6766 memset(&m, 0, sizeof(m));
6767 if (!VirtualQuery(func, &m, sizeof(m))) {
6768 return FALSE;
6769 }
6770 k = GetModuleHandle("kernel32.dll");
6771 if (!k) return FALSE;
6772 return (HMODULE)m.AllocationBase != k;
6773#endif
6774}
6775
6776/* License: Ruby's */
6777static struct constat *
6778constat_handle(HANDLE h)
6779{
6780 st_data_t data;
6781 struct constat *p = NULL;
6782 thread_exclusive(conlist) {
6783 if (!conlist) {
6784 if (console_emulator_p()) {
6785 conlist = conlist_disabled;
6786 continue;
6787 }
6788 conlist = st_init_numtable();
6789 install_vm_exit_handler();
6790 }
6791 else if (conlist == conlist_disabled) {
6792 continue;
6793 }
6794 if (st_lookup(conlist, (st_data_t)h, &data)) {
6795 p = (struct constat *)data;
6796 }
6797 else {
6798 CONSOLE_SCREEN_BUFFER_INFO csbi;
6799 p = ALLOC(struct constat);
6800 p->vt100.state = constat_init;
6801 p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6802 p->vt100.reverse = 0;
6803 p->vt100.saved.X = p->vt100.saved.Y = 0;
6804 if (GetConsoleScreenBufferInfo(h, &csbi)) {
6805 p->vt100.attr = csbi.wAttributes;
6806 }
6807 st_insert(conlist, (st_data_t)h, (st_data_t)p);
6808 }
6809 }
6810 return p;
6811}
6812
6813/* License: Ruby's */
6814static void
6815constat_reset(HANDLE h)
6816{
6817 st_data_t data;
6818 struct constat *p;
6819 thread_exclusive(conlist) {
6820 if (!conlist || conlist == conlist_disabled) continue;
6821 if (!st_lookup(conlist, (st_data_t)h, &data)) continue;
6822 p = (struct constat *)data;
6823 p->vt100.state = constat_init;
6824 }
6825}
6826
6827#define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6828#define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6829
6830#define constat_attr_color_reverse(attr) \
6831 ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6832 (((attr) & FOREGROUND_MASK) << 4) | \
6833 (((attr) & BACKGROUND_MASK) >> 4)
6834
6835/* License: Ruby's */
6836static WORD
6837constat_attr(int count, const int *seq, WORD attr, WORD default_attr, int *reverse)
6838{
6839 int rev = *reverse;
6840 WORD bold;
6841
6842 if (!count) return attr;
6843 if (rev) attr = constat_attr_color_reverse(attr);
6844 bold = attr & FOREGROUND_INTENSITY;
6845 attr &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
6846
6847 while (count-- > 0) {
6848 switch (*seq++) {
6849 case 0:
6850 attr = default_attr;
6851 rev = 0;
6852 bold = 0;
6853 break;
6854 case 1:
6855 bold = FOREGROUND_INTENSITY;
6856 break;
6857 case 4:
6858#ifndef COMMON_LVB_UNDERSCORE
6859#define COMMON_LVB_UNDERSCORE 0x8000
6860#endif
6861 attr |= COMMON_LVB_UNDERSCORE;
6862 break;
6863 case 7:
6864 rev = 1;
6865 break;
6866
6867 case 30:
6868 attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
6869 break;
6870 case 17:
6871 case 31:
6872 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)) | FOREGROUND_RED;
6873 break;
6874 case 18:
6875 case 32:
6876 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_RED)) | FOREGROUND_GREEN;
6877 break;
6878 case 19:
6879 case 33:
6880 attr = (attr & ~FOREGROUND_BLUE) | FOREGROUND_GREEN | FOREGROUND_RED;
6881 break;
6882 case 20:
6883 case 34:
6884 attr = (attr & ~(FOREGROUND_GREEN | FOREGROUND_RED)) | FOREGROUND_BLUE;
6885 break;
6886 case 21:
6887 case 35:
6888 attr = (attr & ~FOREGROUND_GREEN) | FOREGROUND_BLUE | FOREGROUND_RED;
6889 break;
6890 case 22:
6891 case 36:
6892 attr = (attr & ~FOREGROUND_RED) | FOREGROUND_BLUE | FOREGROUND_GREEN;
6893 break;
6894 case 23:
6895 case 37:
6896 attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6897 break;
6898
6899 case 40:
6900 attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
6901 break;
6902 case 41:
6903 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN)) | BACKGROUND_RED;
6904 break;
6905 case 42:
6906 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_RED)) | BACKGROUND_GREEN;
6907 break;
6908 case 43:
6909 attr = (attr & ~BACKGROUND_BLUE) | BACKGROUND_GREEN | BACKGROUND_RED;
6910 break;
6911 case 44:
6912 attr = (attr & ~(BACKGROUND_GREEN | BACKGROUND_RED)) | BACKGROUND_BLUE;
6913 break;
6914 case 45:
6915 attr = (attr & ~BACKGROUND_GREEN) | BACKGROUND_BLUE | BACKGROUND_RED;
6916 break;
6917 case 46:
6918 attr = (attr & ~BACKGROUND_RED) | BACKGROUND_BLUE | BACKGROUND_GREEN;
6919 break;
6920 case 47:
6921 attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
6922 break;
6923 }
6924 }
6925 attr |= bold;
6926 if (rev) attr = constat_attr_color_reverse(attr);
6927 *reverse = rev;
6928 return attr;
6929}
6930
6931/* License: Ruby's */
6932static void
6933constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
6934{
6935 DWORD written;
6936
6937 FillConsoleOutputAttribute(handle, attr, len, pos, &written);
6938 FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
6939}
6940
6941/* License: Ruby's */
6942static void
6943constat_apply(HANDLE handle, struct constat *s, WCHAR w)
6944{
6945 CONSOLE_SCREEN_BUFFER_INFO csbi;
6946 const int *seq = s->vt100.seq;
6947 int count = s->vt100.state;
6948 int arg0, arg1 = 1;
6949 COORD pos;
6950
6951 if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
6952 arg0 = (count > 0 && seq[0] > 0);
6953 if (arg0) arg1 = seq[0];
6954 switch (w) {
6955 case L'm':
6956 SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr, &s->vt100.reverse));
6957 break;
6958 case L'F':
6959 csbi.dwCursorPosition.X = 0;
6960 case L'A':
6961 csbi.dwCursorPosition.Y -= arg1;
6962 if (csbi.dwCursorPosition.Y < csbi.srWindow.Top)
6963 csbi.dwCursorPosition.Y = csbi.srWindow.Top;
6964 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6965 break;
6966 case L'E':
6967 csbi.dwCursorPosition.X = 0;
6968 case L'B':
6969 case L'e':
6970 csbi.dwCursorPosition.Y += arg1;
6971 if (csbi.dwCursorPosition.Y > csbi.srWindow.Bottom)
6972 csbi.dwCursorPosition.Y = csbi.srWindow.Bottom;
6973 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6974 break;
6975 case L'C':
6976 csbi.dwCursorPosition.X += arg1;
6977 if (csbi.dwCursorPosition.X >= csbi.srWindow.Right)
6978 csbi.dwCursorPosition.X = csbi.srWindow.Right;
6979 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6980 break;
6981 case L'D':
6982 csbi.dwCursorPosition.X -= arg1;
6983 if (csbi.dwCursorPosition.X < csbi.srWindow.Left)
6984 csbi.dwCursorPosition.X = csbi.srWindow.Left;
6985 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6986 break;
6987 case L'G':
6988 case L'`':
6989 arg1 += csbi.srWindow.Left;
6990 if (arg1 > csbi.srWindow.Right)
6991 arg1 = csbi.srWindow.Right;
6992 csbi.dwCursorPosition.X = arg1;
6993 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6994 break;
6995 case L'd':
6996 arg1 += csbi.srWindow.Top;
6997 if (arg1 > csbi.srWindow.Bottom)
6998 arg1 = csbi.srWindow.Bottom;
6999 csbi.dwCursorPosition.Y = arg1;
7000 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
7001 break;
7002 case L'H':
7003 case L'f':
7004 pos.Y = arg1 + csbi.srWindow.Top - 1;
7005 if (pos.Y > csbi.srWindow.Bottom) pos.Y = csbi.srWindow.Bottom;
7006 if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
7007 pos.X = arg1 + csbi.srWindow.Left - 1;
7008 if (pos.X > csbi.srWindow.Right) pos.X = csbi.srWindow.Right;
7009 SetConsoleCursorPosition(handle, pos);
7010 break;
7011 case L'J':
7012 switch (arg0 ? arg1 : 0) {
7013 case 0: /* erase after cursor */
7014 constat_clear(handle, csbi.wAttributes,
7015 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.dwCursorPosition.Y + 1)
7016 - csbi.dwCursorPosition.X),
7017 csbi.dwCursorPosition);
7018 break;
7019 case 1: /* erase before *and* cursor */
7020 pos.X = 0;
7021 pos.Y = csbi.srWindow.Top;
7022 constat_clear(handle, csbi.wAttributes,
7023 (csbi.dwSize.X * (csbi.dwCursorPosition.Y - csbi.srWindow.Top)
7024 + csbi.dwCursorPosition.X + 1),
7025 pos);
7026 break;
7027 case 2: /* erase entire screen */
7028 pos.X = 0;
7029 pos.Y = csbi.srWindow.Top;
7030 constat_clear(handle, csbi.wAttributes,
7031 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1)),
7032 pos);
7033 break;
7034 case 3: /* erase entire screen */
7035 pos.X = 0;
7036 pos.Y = 0;
7037 constat_clear(handle, csbi.wAttributes,
7038 (csbi.dwSize.X * csbi.dwSize.Y),
7039 pos);
7040 break;
7041 }
7042 break;
7043 case L'K':
7044 switch (arg0 ? arg1 : 0) {
7045 case 0: /* erase after cursor */
7046 constat_clear(handle, csbi.wAttributes,
7047 (csbi.dwSize.X - csbi.dwCursorPosition.X),
7048 csbi.dwCursorPosition);
7049 break;
7050 case 1: /* erase before *and* cursor */
7051 pos.X = 0;
7052 pos.Y = csbi.dwCursorPosition.Y;
7053 constat_clear(handle, csbi.wAttributes,
7054 csbi.dwCursorPosition.X + 1, pos);
7055 break;
7056 case 2: /* erase entire line */
7057 pos.X = 0;
7058 pos.Y = csbi.dwCursorPosition.Y;
7059 constat_clear(handle, csbi.wAttributes,
7060 csbi.dwSize.X, pos);
7061 break;
7062 }
7063 break;
7064 case L's':
7065 s->vt100.saved = csbi.dwCursorPosition;
7066 break;
7067 case L'u':
7068 SetConsoleCursorPosition(handle, s->vt100.saved);
7069 break;
7070 case L'h':
7071 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7072 CONSOLE_CURSOR_INFO cci;
7073 GetConsoleCursorInfo(handle, &cci);
7074 cci.bVisible = TRUE;
7075 SetConsoleCursorInfo(handle, &cci);
7076 }
7077 break;
7078 case L'l':
7079 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7080 CONSOLE_CURSOR_INFO cci;
7081 GetConsoleCursorInfo(handle, &cci);
7082 cci.bVisible = FALSE;
7083 SetConsoleCursorInfo(handle, &cci);
7084 }
7085 break;
7086 }
7087}
7088
7089/* get rid of console writing bug; assume WriteConsole and WriteFile
7090 * on a console share the same limit. */
7091static const long MAXSIZE_CONSOLE_WRITING = 31366;
7092
7093/* License: Ruby's */
7094static long
7095constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
7096{
7097 const WCHAR *ptr = *ptrp;
7098 long rest, len = *lenp;
7099 while (len-- > 0) {
7100 WCHAR wc = *ptr++;
7101 if (wc == 0x1b) {
7102 rest = *lenp - len - 1;
7103 if (s->vt100.state == constat_esc) {
7104 rest++; /* reuse this ESC */
7105 }
7106 s->vt100.state = constat_init;
7107 if (len > 0 && *ptr != L'[') continue;
7108 s->vt100.state = constat_esc;
7109 }
7110 else if (s->vt100.state == constat_esc) {
7111 if (wc != L'[') {
7112 /* TODO: supply dropped ESC at beginning */
7113 s->vt100.state = constat_init;
7114 continue;
7115 }
7116 rest = *lenp - len - 1;
7117 if (rest > 0) --rest;
7118 s->vt100.state = constat_seq;
7119 s->vt100.seq[0] = 0;
7120 }
7121 else if (s->vt100.state >= constat_seq) {
7122 if (wc >= L'0' && wc <= L'9') {
7123 if (s->vt100.state < (int)numberof(s->vt100.seq)) {
7124 int *seq = &s->vt100.seq[s->vt100.state];
7125 *seq = (*seq * 10) + (wc - L'0');
7126 }
7127 }
7128 else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
7129 s->vt100.seq[s->vt100.state++] = -1;
7130 }
7131 else {
7132 do {
7133 if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
7134 s->vt100.seq[s->vt100.state] = 0;
7135 }
7136 else {
7137 s->vt100.state = (int)numberof(s->vt100.seq);
7138 }
7139 } while (0);
7140 if (wc != L';') {
7141 constat_apply(h, s, wc);
7142 s->vt100.state = constat_init;
7143 }
7144 }
7145 rest = 0;
7146 }
7147 else if ((rest = *lenp - len) < MAXSIZE_CONSOLE_WRITING) {
7148 continue;
7149 }
7150 *ptrp = ptr;
7151 *lenp = len;
7152 return rest;
7153 }
7154 len = *lenp;
7155 *ptrp = ptr;
7156 *lenp = 0;
7157 return len;
7158}
7159
7160
7161/* License: Ruby's */
7162int
7163rb_w32_close(int fd)
7164{
7165 SOCKET sock = TO_SOCKET(fd);
7166 int save_errno = errno;
7167
7168 if (!is_socket(sock)) {
7169 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7170 constat_delete((HANDLE)sock);
7171 return _close(fd);
7172 }
7173 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
7174 socklist_delete(&sock, NULL);
7175 _close(fd);
7176 errno = save_errno;
7177 if (closesocket(sock) == SOCKET_ERROR) {
7178 errno = map_errno(WSAGetLastError());
7179 return -1;
7180 }
7181 return 0;
7182}
7183
7184static int
7185setup_overlapped(OVERLAPPED *ol, int fd, int iswrite)
7186{
7187 memset(ol, 0, sizeof(*ol));
7188 if (!(_osfile(fd) & (FDEV | FPIPE))) {
7189 LONG high = 0;
7190 /* On mode:a, it can write only FILE_END.
7191 * On mode:a+, though it can write only FILE_END,
7192 * it can read from everywhere.
7193 */
7194 DWORD method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
7195 DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method);
7196#ifndef INVALID_SET_FILE_POINTER
7197#define INVALID_SET_FILE_POINTER ((DWORD)-1)
7198#endif
7199 if (low == INVALID_SET_FILE_POINTER) {
7200 DWORD err = GetLastError();
7201 if (err != NO_ERROR) {
7202 errno = map_errno(err);
7203 return -1;
7204 }
7205 }
7206 ol->Offset = low;
7207 ol->OffsetHigh = high;
7208 }
7209 ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7210 if (!ol->hEvent) {
7211 errno = map_errno(GetLastError());
7212 return -1;
7213 }
7214 return 0;
7215}
7216
7217static void
7218finish_overlapped(OVERLAPPED *ol, int fd, DWORD size)
7219{
7220 CloseHandle(ol->hEvent);
7221
7222 if (!(_osfile(fd) & (FDEV | FPIPE))) {
7223 LONG high = ol->OffsetHigh;
7224 DWORD low = ol->Offset + size;
7225 if (low < ol->Offset)
7226 ++high;
7227 SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
7228 }
7229}
7230
7231#undef read
7232/* License: Ruby's */
7233ssize_t
7234rb_w32_read(int fd, void *buf, size_t size)
7235{
7236 SOCKET sock = TO_SOCKET(fd);
7237 DWORD read;
7238 DWORD wait;
7239 DWORD err;
7240 size_t len;
7241 size_t ret;
7242 OVERLAPPED ol;
7243 BOOL isconsole;
7244 BOOL islineinput = FALSE;
7245 int start = 0;
7246
7247 if (is_socket(sock))
7248 return rb_w32_recv(fd, buf, size, 0);
7249
7250 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7251 if (_get_osfhandle(fd) == -1) {
7252 return -1;
7253 }
7254
7255 if (_osfile(fd) & FTEXT) {
7256 return _read(fd, buf, size);
7257 }
7258
7259 rb_acrt_lowio_lock_fh(fd);
7260
7261 if (!size || _osfile(fd) & FEOFLAG) {
7262 _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7263 rb_acrt_lowio_unlock_fh(fd);
7264 return 0;
7265 }
7266
7267 ret = 0;
7268 isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
7269 if (isconsole) {
7270 DWORD mode;
7271 GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
7272 islineinput = (mode & ENABLE_LINE_INPUT) != 0;
7273 }
7274 retry:
7275 /* get rid of console reading bug */
7276 if (isconsole) {
7277 constat_reset((HANDLE)_osfhnd(fd));
7278 if (start)
7279 len = 1;
7280 else {
7281 len = 0;
7282 start = 1;
7283 }
7284 }
7285 else
7286 len = size;
7287 size -= len;
7288
7289 if (setup_overlapped(&ol, fd, FALSE)) {
7290 rb_acrt_lowio_unlock_fh(fd);
7291 return -1;
7292 }
7293
7294 if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, &ol)) {
7295 err = GetLastError();
7296 if (err == ERROR_NO_DATA && (_osfile(fd) & FPIPE)) {
7297 DWORD state;
7298 if (GetNamedPipeHandleState((HANDLE)_osfhnd(fd), &state, NULL, NULL, NULL, NULL, 0) && (state & PIPE_NOWAIT)) {
7299 errno = EWOULDBLOCK;
7300 }
7301 else {
7302 errno = map_errno(err);
7303 }
7304 rb_acrt_lowio_unlock_fh(fd);
7305 return -1;
7306 }
7307 else if (err != ERROR_IO_PENDING) {
7308 CloseHandle(ol.hEvent);
7309 if (err == ERROR_ACCESS_DENIED)
7310 errno = EBADF;
7311 else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
7312 rb_acrt_lowio_unlock_fh(fd);
7313 return 0;
7314 }
7315 else
7316 errno = map_errno(err);
7317
7318 rb_acrt_lowio_unlock_fh(fd);
7319 return -1;
7320 }
7321
7322 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7323 if (wait != WAIT_OBJECT_0) {
7324 if (wait == WAIT_OBJECT_0 + 1)
7325 errno = EINTR;
7326 else
7327 errno = map_errno(GetLastError());
7328 CloseHandle(ol.hEvent);
7329 CancelIo((HANDLE)_osfhnd(fd));
7330 rb_acrt_lowio_unlock_fh(fd);
7331 return -1;
7332 }
7333
7334 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
7335 (err = GetLastError()) != ERROR_HANDLE_EOF) {
7336 int ret = 0;
7337 if (err != ERROR_BROKEN_PIPE) {
7338 errno = map_errno(err);
7339 ret = -1;
7340 }
7341 CloseHandle(ol.hEvent);
7342 CancelIo((HANDLE)_osfhnd(fd));
7343 rb_acrt_lowio_unlock_fh(fd);
7344 return ret;
7345 }
7346 }
7347 else {
7348 err = GetLastError();
7349 errno = map_errno(err);
7350 }
7351
7352 finish_overlapped(&ol, fd, read);
7353
7354 ret += read;
7355 if (read >= len) {
7356 buf = (char *)buf + read;
7357 if (err != ERROR_OPERATION_ABORTED &&
7358 !(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
7359 goto retry;
7360 }
7361 if (read == 0)
7362 _set_osflags(fd, _osfile(fd) | FEOFLAG);
7363
7364
7365 rb_acrt_lowio_unlock_fh(fd);
7366
7367 return ret;
7368}
7369
7370#undef write
7371/* License: Ruby's */
7372ssize_t
7373rb_w32_write(int fd, const void *buf, size_t size)
7374{
7375 SOCKET sock = TO_SOCKET(fd);
7376 DWORD written;
7377 DWORD wait;
7378 DWORD err;
7379 size_t len;
7380 size_t ret;
7381 OVERLAPPED ol;
7382
7383 if (is_socket(sock))
7384 return rb_w32_send(fd, buf, size, 0);
7385
7386 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7387 if (_get_osfhandle(fd) == -1) {
7388 return -1;
7389 }
7390
7391 if ((_osfile(fd) & FTEXT) &&
7392 (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
7393 ssize_t w = _write(fd, buf, size);
7394 if (w == (ssize_t)-1 && errno == EINVAL) {
7395 errno = map_errno(GetLastError());
7396 }
7397 return w;
7398 }
7399
7400 rb_acrt_lowio_lock_fh(fd);
7401
7402 if (!size || _osfile(fd) & FEOFLAG) {
7403 rb_acrt_lowio_unlock_fh(fd);
7404 return 0;
7405 }
7406
7407 ret = 0;
7408 retry:
7409 len = (_osfile(fd) & FDEV) ? min(MAXSIZE_CONSOLE_WRITING, size) : size;
7410 size -= len;
7411 retry2:
7412
7413 if (setup_overlapped(&ol, fd, TRUE)) {
7414 rb_acrt_lowio_unlock_fh(fd);
7415 return -1;
7416 }
7417
7418 if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, &ol)) {
7419 err = GetLastError();
7420 if (err != ERROR_IO_PENDING) {
7421 CloseHandle(ol.hEvent);
7422 if (err == ERROR_ACCESS_DENIED)
7423 errno = EBADF;
7424 else
7425 errno = map_errno(err);
7426
7427 rb_acrt_lowio_unlock_fh(fd);
7428 return -1;
7429 }
7430
7431 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7432 if (wait != WAIT_OBJECT_0) {
7433 if (wait == WAIT_OBJECT_0 + 1)
7434 errno = EINTR;
7435 else
7436 errno = map_errno(GetLastError());
7437 CloseHandle(ol.hEvent);
7438 CancelIo((HANDLE)_osfhnd(fd));
7439 rb_acrt_lowio_unlock_fh(fd);
7440 return -1;
7441 }
7442
7443 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) {
7444 errno = map_errno(GetLastError());
7445 CloseHandle(ol.hEvent);
7446 CancelIo((HANDLE)_osfhnd(fd));
7447 rb_acrt_lowio_unlock_fh(fd);
7448 return -1;
7449 }
7450 }
7451
7452 finish_overlapped(&ol, fd, written);
7453
7454 ret += written;
7455 if (written == len) {
7456 buf = (const char *)buf + len;
7457 if (size > 0)
7458 goto retry;
7459 }
7460 if (ret == 0) {
7461 size_t newlen = len / 2;
7462 if (newlen > 0) {
7463 size += len - newlen;
7464 len = newlen;
7465 goto retry2;
7466 }
7467 ret = -1;
7468 errno = EWOULDBLOCK;
7469 }
7470
7471 rb_acrt_lowio_unlock_fh(fd);
7472
7473 return ret;
7474}
7475
7476/* License: Ruby's */
7477long
7478rb_w32_write_console(uintptr_t strarg, int fd)
7479{
7480 HANDLE handle;
7481 DWORD dwMode, reslen;
7482 VALUE str = strarg;
7483 int encindex;
7484 WCHAR *wbuffer = 0;
7485 const WCHAR *ptr, *next;
7486 struct constat *s;
7487 long len;
7488
7489 handle = (HANDLE)_osfhnd(fd);
7490 if (!GetConsoleMode(handle, &dwMode))
7491 return -1L;
7492
7493 s = constat_handle(handle);
7494 if (!s) return -1L;
7495 encindex = ENCODING_GET(str);
7496 switch (encindex) {
7497 default:
7498 if (!rb_econv_has_convpath_p(rb_enc_name(rb_enc_from_index(encindex)), "UTF-8"))
7499 return -1L;
7500 str = rb_str_conv_enc_opts(str, NULL, rb_enc_from_index(ENCINDEX_UTF_8),
7502 /* fall through */
7503 case ENCINDEX_US_ASCII:
7504 case ENCINDEX_ASCII_8BIT:
7505 /* assume UTF-8 */
7506 case ENCINDEX_UTF_8:
7507 ptr = wbuffer = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(str), RSTRING_LEN(str), &len);
7508 if (!ptr) return -1L;
7509 break;
7510 case ENCINDEX_UTF_16LE:
7511 ptr = (const WCHAR *)RSTRING_PTR(str);
7512 len = RSTRING_LEN(str) / sizeof(WCHAR);
7513 break;
7514 }
7515 reslen = 0;
7516 if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7517 if (!WriteConsoleW(handle, ptr, len, &reslen, NULL))
7518 reslen = (DWORD)-1L;
7519 }
7520 else {
7521 while (len > 0) {
7522 long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
7523 reslen += next - ptr;
7524 if (curlen > 0) {
7525 DWORD written;
7526 if (!WriteConsoleW(handle, ptr, curlen, &written, NULL)) {
7527 reslen = (DWORD)-1L;
7528 break;
7529 }
7530 }
7531 ptr = next;
7532 }
7533 }
7534 RB_GC_GUARD(str);
7535 if (wbuffer) free(wbuffer);
7536 return (long)reslen;
7537}
7538
7539#if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7540/* License: Ruby's */
7541static int
7542unixtime_to_filetime(time_t time, FILETIME *ft)
7543{
7544 ULARGE_INTEGER tmp;
7545
7546 tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7547 ft->dwLowDateTime = tmp.LowPart;
7548 ft->dwHighDateTime = tmp.HighPart;
7549 return 0;
7550}
7551#endif
7552
7553/* License: Ruby's */
7554static int
7555timespec_to_filetime(const struct timespec *ts, FILETIME *ft)
7556{
7557 ULARGE_INTEGER tmp;
7558
7559 tmp.QuadPart = ((LONG_LONG)ts->tv_sec + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7560 tmp.QuadPart += ts->tv_nsec / 100;
7561 ft->dwLowDateTime = tmp.LowPart;
7562 ft->dwHighDateTime = tmp.HighPart;
7563 return 0;
7564}
7565
7566/* License: Ruby's */
7567static int
7568wutimensat(int dirfd, const WCHAR *path, const struct timespec *times, int flags)
7569{
7570 HANDLE hFile;
7571 FILETIME atime, mtime;
7572 struct stati128 stat;
7573 int ret = 0;
7574
7575 /* TODO: When path is absolute, dirfd should be ignored. */
7576 if (dirfd != AT_FDCWD) {
7577 errno = ENOSYS;
7578 return -1;
7579 }
7580
7581 if (flags != 0) {
7582 errno = EINVAL; /* AT_SYMLINK_NOFOLLOW isn't supported. */
7583 return -1;
7584 }
7585
7586 if (wstati128(path, &stat, FALSE)) {
7587 return -1;
7588 }
7589
7590 if (times) {
7591 if (timespec_to_filetime(&times[0], &atime)) {
7592 return -1;
7593 }
7594 if (timespec_to_filetime(&times[1], &mtime)) {
7595 return -1;
7596 }
7597 }
7598 else {
7599 get_systemtime(&atime);
7600 mtime = atime;
7601 }
7602
7603 RUBY_CRITICAL {
7604 const DWORD attr = GetFileAttributesW(path);
7605 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7606 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7607 hFile = open_special(path, GENERIC_WRITE, 0);
7608 if (hFile == INVALID_HANDLE_VALUE) {
7609 errno = map_errno(GetLastError());
7610 ret = -1;
7611 }
7612 else {
7613 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7614 errno = map_errno(GetLastError());
7615 ret = -1;
7616 }
7617 CloseHandle(hFile);
7618 }
7619 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7620 SetFileAttributesW(path, attr);
7621 }
7622
7623 return ret;
7624}
7625
7626/* License: Ruby's */
7627static int
7628w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags, UINT cp)
7629{
7630 WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7631 int ret = -1;
7632
7633 if (wpath) {
7634 ret = wutimensat(dirfd, wpath, times, flags);
7635 free(wpath);
7636 }
7637 return ret;
7638}
7639
7640/* License: Ruby's */
7641int
7642rb_w32_uutime(const char *path, const struct utimbuf *times)
7643{
7644 struct timespec ts[2];
7645
7646 ts[0].tv_sec = times->actime;
7647 ts[0].tv_nsec = 0;
7648 ts[1].tv_sec = times->modtime;
7649 ts[1].tv_nsec = 0;
7650 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7651}
7652
7653/* License: Ruby's */
7654int
7655rb_w32_utime(const char *path, const struct utimbuf *times)
7656{
7657 struct timespec ts[2];
7658
7659 ts[0].tv_sec = times->actime;
7660 ts[0].tv_nsec = 0;
7661 ts[1].tv_sec = times->modtime;
7662 ts[1].tv_nsec = 0;
7663 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7664}
7665
7666/* License: Ruby's */
7667int
7668rb_w32_uutimes(const char *path, const struct timeval *times)
7669{
7670 struct timespec ts[2];
7671
7672 ts[0].tv_sec = times[0].tv_sec;
7673 ts[0].tv_nsec = times[0].tv_usec * 1000;
7674 ts[1].tv_sec = times[1].tv_sec;
7675 ts[1].tv_nsec = times[1].tv_usec * 1000;
7676 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7677}
7678
7679/* License: Ruby's */
7680int
7681rb_w32_utimes(const char *path, const struct timeval *times)
7682{
7683 struct timespec ts[2];
7684
7685 ts[0].tv_sec = times[0].tv_sec;
7686 ts[0].tv_nsec = times[0].tv_usec * 1000;
7687 ts[1].tv_sec = times[1].tv_sec;
7688 ts[1].tv_nsec = times[1].tv_usec * 1000;
7689 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7690}
7691
7692/* License: Ruby's */
7693int
7694rb_w32_uutimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7695{
7696 return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7697}
7698
7699/* License: Ruby's */
7700int
7701rb_w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7702{
7703 return w32_utimensat(dirfd, path, times, flags, filecp());
7704}
7705
7706/* License: Ruby's */
7707int
7708rb_w32_uchdir(const char *path)
7709{
7710 WCHAR *wpath;
7711 int ret;
7712
7713 if (!(wpath = utf8_to_wstr(path, NULL)))
7714 return -1;
7715 ret = _wchdir(wpath);
7716 free(wpath);
7717 return ret;
7718}
7719
7720/* License: Ruby's */
7721static int
7722wmkdir(const WCHAR *wpath, int mode)
7723{
7724 int ret = -1;
7725
7726 RUBY_CRITICAL do {
7727 if (CreateDirectoryW(wpath, NULL) == FALSE) {
7728 errno = map_errno(GetLastError());
7729 break;
7730 }
7731 if (_wchmod(wpath, mode) == -1) {
7732 RemoveDirectoryW(wpath);
7733 break;
7734 }
7735 ret = 0;
7736 } while (0);
7737 return ret;
7738}
7739
7740/* License: Ruby's */
7741int
7742rb_w32_umkdir(const char *path, int mode)
7743{
7744 WCHAR *wpath;
7745 int ret;
7746
7747 if (!(wpath = utf8_to_wstr(path, NULL)))
7748 return -1;
7749 ret = wmkdir(wpath, mode);
7750 free(wpath);
7751 return ret;
7752}
7753
7754/* License: Ruby's */
7755int
7756rb_w32_mkdir(const char *path, int mode)
7757{
7758 WCHAR *wpath;
7759 int ret;
7760
7761 if (!(wpath = filecp_to_wstr(path, NULL)))
7762 return -1;
7763 ret = wmkdir(wpath, mode);
7764 free(wpath);
7765 return ret;
7766}
7767
7768/* License: Ruby's */
7769static int
7770wrmdir(const WCHAR *wpath)
7771{
7772 int ret = 0;
7773 RUBY_CRITICAL {
7774 const DWORD attr = GetFileAttributesW(wpath);
7775 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7776 SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
7777 }
7778 if (RemoveDirectoryW(wpath) == FALSE) {
7779 errno = map_errno(GetLastError());
7780 ret = -1;
7781 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7782 SetFileAttributesW(wpath, attr);
7783 }
7784 }
7785 }
7786 return ret;
7787}
7788
7789/* License: Ruby's */
7790int
7791rb_w32_rmdir(const char *path)
7792{
7793 WCHAR *wpath;
7794 int ret;
7795
7796 if (!(wpath = filecp_to_wstr(path, NULL)))
7797 return -1;
7798 ret = wrmdir(wpath);
7799 free(wpath);
7800 return ret;
7801}
7802
7803/* License: Ruby's */
7804int
7805rb_w32_urmdir(const char *path)
7806{
7807 WCHAR *wpath;
7808 int ret;
7809
7810 if (!(wpath = utf8_to_wstr(path, NULL)))
7811 return -1;
7812 ret = wrmdir(wpath);
7813 free(wpath);
7814 return ret;
7815}
7816
7817/* License: Ruby's */
7818static int
7819wunlink(const WCHAR *path)
7820{
7821 int ret = 0;
7822 const DWORD SYMLINKD = FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY;
7823 RUBY_CRITICAL {
7824 const DWORD attr = GetFileAttributesW(path);
7825 if (attr == (DWORD)-1) {
7826 }
7827 else if ((attr & SYMLINKD) == SYMLINKD) {
7828 ret = RemoveDirectoryW(path);
7829 }
7830 else {
7831 if (attr & FILE_ATTRIBUTE_READONLY) {
7832 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7833 }
7834 ret = DeleteFileW(path);
7835 }
7836 if (!ret) {
7837 errno = map_errno(GetLastError());
7838 ret = -1;
7839 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7840 SetFileAttributesW(path, attr);
7841 }
7842 }
7843 }
7844 return ret;
7845}
7846
7847/* License: Ruby's */
7848int
7849rb_w32_uunlink(const char *path)
7850{
7851 WCHAR *wpath;
7852 int ret;
7853
7854 if (!(wpath = utf8_to_wstr(path, NULL)))
7855 return -1;
7856 ret = wunlink(wpath);
7857 free(wpath);
7858 return ret;
7859}
7860
7861/* License: Ruby's */
7862int
7863rb_w32_unlink(const char *path)
7864{
7865 WCHAR *wpath;
7866 int ret;
7867
7868 if (!(wpath = filecp_to_wstr(path, NULL)))
7869 return -1;
7870 ret = wunlink(wpath);
7871 free(wpath);
7872 return ret;
7873}
7874
7875/* License: Ruby's */
7876int
7877rb_w32_uchmod(const char *path, int mode)
7878{
7879 WCHAR *wpath;
7880 int ret;
7881
7882 if (!(wpath = utf8_to_wstr(path, NULL)))
7883 return -1;
7884 ret = _wchmod(wpath, mode);
7885 free(wpath);
7886 return ret;
7887}
7888
7889/* License: Ruby's */
7890int
7891fchmod(int fd, int mode)
7892{
7893 typedef BOOL (WINAPI *set_file_information_by_handle_func)
7894 (HANDLE, int, void*, DWORD);
7895 static set_file_information_by_handle_func set_file_info =
7896 (set_file_information_by_handle_func)-1;
7897
7898 /* from winbase.h of the mingw-w64 runtime package. */
7899 struct {
7900 LARGE_INTEGER CreationTime;
7901 LARGE_INTEGER LastAccessTime;
7902 LARGE_INTEGER LastWriteTime;
7903 LARGE_INTEGER ChangeTime;
7904 DWORD FileAttributes;
7905 } info = {{{0}}, {{0}}, {{0}},}; /* fields with 0 are unchanged */
7906 HANDLE h = (HANDLE)_get_osfhandle(fd);
7907
7908 if (h == INVALID_HANDLE_VALUE) {
7909 errno = EBADF;
7910 return -1;
7911 }
7912 if (set_file_info == (set_file_information_by_handle_func)-1) {
7913 /* Since Windows Vista and Windows Server 2008 */
7914 set_file_info = (set_file_information_by_handle_func)
7915 get_proc_address("kernel32", "SetFileInformationByHandle", NULL);
7916 }
7917 if (!set_file_info) {
7918 errno = ENOSYS;
7919 return -1;
7920 }
7921
7922 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
7923 if (!(mode & 0200)) info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
7924 if (!set_file_info(h, 0, &info, sizeof(info))) {
7925 errno = map_errno(GetLastError());
7926 return -1;
7927 }
7928 return 0;
7929}
7930
7931/* License: Ruby's */
7932int
7933rb_w32_isatty(int fd)
7934{
7935 DWORD mode;
7936
7937 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7938 if (_get_osfhandle(fd) == -1) {
7939 return 0;
7940 }
7941 if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
7942 errno = ENOTTY;
7943 return 0;
7944 }
7945 return 1;
7946}
7947
7948#if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7949extern long _ftol(double);
7950/* License: Ruby's */
7951long
7952_ftol2(double d)
7953{
7954 return _ftol(d);
7955}
7956
7957/* License: Ruby's */
7958long
7959_ftol2_sse(double d)
7960{
7961 return _ftol(d);
7962}
7963#endif
7964
7965#ifndef signbit
7966/* License: Ruby's */
7967int
7968signbit(double x)
7969{
7970 int *ip = (int *)(&x + 1) - 1;
7971 return *ip < 0;
7972}
7973#endif
7974
7975/* License: Ruby's */
7976const char * WSAAPI
7977rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
7978{
7979 typedef char *(WSAAPI inet_ntop_t)(int, void *, char *, size_t);
7980 static inet_ntop_t *pInetNtop = (inet_ntop_t *)-1;
7981 if (pInetNtop == (inet_ntop_t *)-1)
7982 pInetNtop = (inet_ntop_t *)get_proc_address("ws2_32", "inet_ntop", NULL);
7983 if (pInetNtop) {
7984 return pInetNtop(af, (void *)addr, numaddr, numaddr_len);
7985 }
7986 else {
7987 struct in_addr in;
7988 memcpy(&in.s_addr, addr, sizeof(in.s_addr));
7989 snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
7990 }
7991 return numaddr;
7992}
7993
7994/* License: Ruby's */
7995int WSAAPI
7996rb_w32_inet_pton(int af, const char *src, void *dst)
7997{
7998 typedef int (WSAAPI inet_pton_t)(int, const char*, void *);
7999 static inet_pton_t *pInetPton = (inet_pton_t *)-1;
8000 if (pInetPton == (inet_pton_t *)-1)
8001 pInetPton = (inet_pton_t *)get_proc_address("ws2_32", "inet_pton", NULL);
8002 if (pInetPton) {
8003 return pInetPton(af, src, dst);
8004 }
8005 return 0;
8006}
8007
8008/* License: Ruby's */
8009char
8010rb_w32_fd_is_text(int fd)
8011{
8012 return _osfile(fd) & FTEXT;
8013}
8014
8015#if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
8016/* License: Ruby's */
8017static int
8018unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
8019{
8020 FILETIME ft;
8021 if (unixtime_to_filetime(t, &ft)) return -1;
8022 if (!FileTimeToSystemTime(&ft, st)) return -1;
8023 return 0;
8024}
8025
8026/* License: Ruby's */
8027static void
8028systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
8029{
8030 int y = st->wYear, m = st->wMonth, d = st->wDay;
8031 t->tm_sec = st->wSecond;
8032 t->tm_min = st->wMinute;
8033 t->tm_hour = st->wHour;
8034 t->tm_mday = st->wDay;
8035 t->tm_mon = st->wMonth - 1;
8036 t->tm_year = y - 1900;
8037 t->tm_wday = st->wDayOfWeek;
8038 switch (m) {
8039 case 1:
8040 break;
8041 case 2:
8042 d += 31;
8043 break;
8044 default:
8045 d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
8046 d += ((m - 3) * 153 + 2) / 5;
8047 break;
8048 }
8049 t->tm_yday = d - 1;
8050}
8051
8052/* License: Ruby's */
8053static int
8054systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
8055{
8056 TIME_ZONE_INFORMATION stdtz;
8057 SYSTEMTIME sst;
8058
8059 if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
8060 if (!tz) {
8061 GetTimeZoneInformation(&stdtz);
8062 tz = &stdtz;
8063 }
8064 if (tz->StandardBias == tz->DaylightBias) return 0;
8065 if (!tz->StandardDate.wMonth) return 0;
8066 if (!tz->DaylightDate.wMonth) return 0;
8067 if (tz != &stdtz) stdtz = *tz;
8068
8069 stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
8070 if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst)) return 0;
8071 if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
8072 return 0;
8073 return 1;
8074}
8075#endif
8076
8077#ifdef HAVE__GMTIME64_S
8078# ifndef HAVE__LOCALTIME64_S
8079/* assume same as _gmtime64_s() */
8080# define HAVE__LOCALTIME64_S 1
8081# endif
8082# ifndef MINGW_HAS_SECURE_API
8083 _CRTIMP errno_t __cdecl _gmtime64_s(struct tm* tm, const __time64_t *time);
8084 _CRTIMP errno_t __cdecl _localtime64_s(struct tm* tm, const __time64_t *time);
8085# endif
8086# define gmtime_s _gmtime64_s
8087# define localtime_s _localtime64_s
8088#endif
8089
8090/* License: Ruby's */
8091struct tm *
8092gmtime_r(const time_t *tp, struct tm *rp)
8093{
8094 int e = EINVAL;
8095 if (!tp || !rp) {
8096 error:
8097 errno = e;
8098 return NULL;
8099 }
8100#if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8101 e = gmtime_s(rp, tp);
8102 if (e != 0) goto error;
8103#else
8104 {
8105 SYSTEMTIME st;
8106 if (unixtime_to_systemtime(*tp, &st)) goto error;
8107 rp->tm_isdst = 0;
8108 systemtime_to_tm(&st, rp);
8109 }
8110#endif
8111 return rp;
8112}
8113
8114/* License: Ruby's */
8115struct tm *
8116localtime_r(const time_t *tp, struct tm *rp)
8117{
8118 int e = EINVAL;
8119 if (!tp || !rp) {
8120 error:
8121 errno = e;
8122 return NULL;
8123 }
8124#if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8125 e = localtime_s(rp, tp);
8126 if (e) goto error;
8127#else
8128 {
8129 SYSTEMTIME gst, lst;
8130 if (unixtime_to_systemtime(*tp, &gst)) goto error;
8131 rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
8132 systemtime_to_tm(&lst, rp);
8133 }
8134#endif
8135 return rp;
8136}
8137
8138/* License: Ruby's */
8139int
8140rb_w32_wrap_io_handle(HANDLE h, int flags)
8141{
8142 BOOL tmp;
8143 int len = sizeof(tmp);
8144 int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (char *)&tmp, &len);
8145 if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
8146 int f = 0;
8147 if (flags & O_NONBLOCK) {
8148 flags &= ~O_NONBLOCK;
8149 f = O_NONBLOCK;
8150 }
8151 socklist_insert((SOCKET)h, f);
8152 }
8153 else if (flags & O_NONBLOCK) {
8154 errno = EINVAL;
8155 return -1;
8156 }
8157 return rb_w32_open_osfhandle((intptr_t)h, flags);
8158}
8159
8160/* License: Ruby's */
8161int
8162rb_w32_unwrap_io_handle(int fd)
8163{
8164 SOCKET sock = TO_SOCKET(fd);
8165 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
8166 if (!is_socket(sock)) {
8167 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
8168 constat_delete((HANDLE)sock);
8169 }
8170 else {
8171 socklist_delete(&sock, NULL);
8172 }
8173 return _close(fd);
8174}
8175
8176#if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8177/*
8178 * Set floating point precision for pow() of mingw-w64 x86.
8179 * With default precision the result is not proper on WinXP.
8180 */
8181double
8182rb_w32_pow(double x, double y)
8183{
8184#undef pow
8185 double r;
8186 unsigned int default_control = _controlfp(0, 0);
8187 _controlfp(_PC_64, _MCW_PC);
8188 r = pow(x, y);
8189 /* Restore setting */
8190 _controlfp(default_control, _MCW_PC);
8191 return r;
8192}
8193#endif
8194
8195typedef struct {
8196 BOOL file_id_p;
8197 union {
8198 BY_HANDLE_FILE_INFORMATION bhfi;
8199 FILE_ID_INFO fii;
8200 } info;
8202
8203static HANDLE
8204w32_io_info(VALUE *file, w32_io_info_t *st)
8205{
8206 VALUE tmp;
8207 HANDLE f, ret = 0;
8208
8209 tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
8210 if (!NIL_P(tmp)) {
8211 rb_io_t *fptr;
8212
8213 GetOpenFile(tmp, fptr);
8214 f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
8215 if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
8216 }
8217 else {
8218 VALUE tmp;
8219 WCHAR *ptr;
8220 int len;
8221 VALUE v;
8222
8223 FilePathValue(*file);
8224 tmp = rb_str_encode_ospath(*file);
8225 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
8226 ptr = ALLOCV_N(WCHAR, v, len);
8227 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
8228 f = CreateFileW(ptr, 0,
8229 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
8230 FILE_FLAG_BACKUP_SEMANTICS, NULL);
8231 ALLOCV_END(v);
8232 if (f == INVALID_HANDLE_VALUE) return f;
8233 ret = f;
8234 }
8235 if (GetFileType(f) == FILE_TYPE_DISK) {
8236 DWORD err;
8237 ZeroMemory(st, sizeof(*st));
8238 err = get_ino(f, &st->info.fii);
8239 if (!err) {
8240 st->file_id_p = TRUE;
8241 return ret;
8242 }
8243 else if (err != ERROR_INVALID_PARAMETER) {
8244 CloseHandle(f);
8245 return INVALID_HANDLE_VALUE;
8246 }
8247 /* this API may not work at files on non Microsoft SMB
8248 * server, fallback to old API then. */
8249 if (GetFileInformationByHandle(f, &st->info.bhfi)) {
8250 st->file_id_p = FALSE;
8251 return ret;
8252 }
8253 }
8254 if (ret) CloseHandle(ret);
8255 return INVALID_HANDLE_VALUE;
8256}
8257
8258static VALUE
8259close_handle(VALUE h)
8260{
8261 CloseHandle((HANDLE)h);
8262 return Qfalse;
8263}
8264
8266 VALUE *fname;
8267 w32_io_info_t *st;
8268};
8269
8270static VALUE
8271call_w32_io_info(VALUE arg)
8272{
8273 struct w32_io_info_args *p = (void *)arg;
8274 return (VALUE)w32_io_info(p->fname, p->st);
8275}
8276
8277VALUE
8278rb_w32_file_identical_p(VALUE fname1, VALUE fname2)
8279{
8280 w32_io_info_t st1, st2;
8281 HANDLE f1 = 0, f2 = 0;
8282
8283 f1 = w32_io_info(&fname1, &st1);
8284 if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
8285 if (f1) {
8286 struct w32_io_info_args arg;
8287 arg.fname = &fname2;
8288 arg.st = &st2;
8289 f2 = (HANDLE)rb_ensure(call_w32_io_info, (VALUE)&arg, close_handle, (VALUE)f1);
8290 }
8291 else {
8292 f2 = w32_io_info(&fname2, &st2);
8293 }
8294 if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
8295 if (f2) CloseHandle(f2);
8296
8297 if (st1.file_id_p != st2.file_id_p) return Qfalse;
8298 if (!st1.file_id_p) {
8299 if (st1.info.bhfi.dwVolumeSerialNumber == st2.info.bhfi.dwVolumeSerialNumber &&
8300 st1.info.bhfi.nFileIndexHigh == st2.info.bhfi.nFileIndexHigh &&
8301 st1.info.bhfi.nFileIndexLow == st2.info.bhfi.nFileIndexLow)
8302 return Qtrue;
8303 }
8304 else {
8305 if (st1.info.fii.VolumeSerialNumber == st2.info.fii.VolumeSerialNumber &&
8306 memcmp(&st1.info.fii.FileId, &st2.info.fii.FileId, sizeof(FILE_ID_128)) == 0)
8307 return Qtrue;
8308 }
8309 return Qfalse;
8310}
8311
8312int
8313rb_w32_set_thread_description(HANDLE th, const WCHAR *name)
8314{
8315 int result = FALSE;
8316 typedef HRESULT (WINAPI *set_thread_description_func)(HANDLE, PCWSTR);
8317 static set_thread_description_func set_thread_description =
8318 (set_thread_description_func)-1;
8319 if (set_thread_description == (set_thread_description_func)-1) {
8320 /* Since Windows 10, version 1607 and Windows Server 2016 */
8321 set_thread_description = (set_thread_description_func)
8322 get_proc_address("kernel32", "SetThreadDescription", NULL);
8323 }
8324 if (set_thread_description) {
8325 result = set_thread_description(th, name);
8326 }
8327 return result;
8328}
8329
8330int
8331rb_w32_set_thread_description_str(HANDLE th, VALUE name)
8332{
8333 int idx, result = FALSE;
8334 WCHAR *s;
8335
8336 if (NIL_P(name)) {
8337 return rb_w32_set_thread_description(th, L"");
8338 }
8339 s = (WCHAR *)StringValueCStr(name);
8340 idx = rb_enc_get_index(name);
8341 if (idx == ENCINDEX_UTF_16LE) {
8342 result = rb_w32_set_thread_description(th, s);
8343 }
8344 else {
8345 name = rb_str_conv_enc(name, rb_enc_from_index(idx), rb_utf8_encoding());
8346 s = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(name), RSTRING_LEN(name)+1, NULL);
8347 result = rb_w32_set_thread_description(th, s);
8348 free(s);
8349 }
8350 RB_GC_GUARD(name);
8351 return result;
8352}
8353
8354VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notimplement;
8355
8356#if RUBY_MSVCRT_VERSION < 120
8357#include "missing/nextafter.c"
8358#endif
8359
8360void *
8361rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset)
8362{
8363 void *ptr;
8364 //DWORD protect = 0;
8365 DWORD protect = PAGE_EXECUTE_READWRITE;
8366
8367 if (fd > 0 || offset) {
8368 /* not supported */
8369 errno = EINVAL;
8370 return MAP_FAILED;
8371 }
8372
8373/*
8374 if (prot & PROT_EXEC) {
8375 if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8376 else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8377 else protect = PAGE_EXECUTE;
8378 }
8379 else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8380 else if (prot & PROT_READ) protect = PAGE_READONLY;
8381*/
8382 ptr = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, protect);
8383 if (!ptr) {
8384 errno = rb_w32_map_errno(GetLastError());
8385 return MAP_FAILED;
8386 }
8387
8388 return ptr;
8389}
8390
8391int
8392rb_w32_munmap(void *addr, size_t len)
8393{
8394 if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8395 errno = rb_w32_map_errno(GetLastError());
8396 return -1;
8397 }
8398
8399 return 0;
8400}
8401
8402inline int
8403rb_w32_mprotect(void *addr, size_t len, int prot)
8404{
8405/*
8406 DWORD protect = 0;
8407 if (prot & PROT_EXEC) {
8408 if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8409 else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8410 else protect = PAGE_EXECUTE;
8411 }
8412 else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8413 else if (prot & PROT_READ) protect = PAGE_READONLY;
8414 if (!VirtualProtect(addr, len, protect, NULL)) {
8415 errno = rb_w32_map_errno(GetLastError());
8416 return -1;
8417 }
8418*/
8419 if (prot & PROT_EXEC) {
8420 if (!FlushInstructionCache(GetCurrentProcess(), addr, len)) {
8421 errno = rb_w32_map_errno(GetLastError());
8422 return -1;
8423 }
8424 }
8425 return 0;
8426}
#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:9075
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