e1741c05221336a28e6c59d2ec0c787f8cf6f9cb
[ruby.git] / win32 / win32.c
blobe1741c05221336a28e6c59d2ec0c787f8cf6f9cb
1 /*
2 * Copyright (c) 1993, Intergraph Corporation
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.
7 * Various Unix compatibility functions and NT specific functions.
9 * Some of this code was derived from the MSDOS port(s) and the OS/2 port.
13 The parts licensed under above copyright notice are marked as "Artistic or
14 GPL".
15 Another parts are licensed under Ruby's License.
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
22 #undef __STRICT_ANSI__
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>
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"
64 #include "ruby/internal/stdbool.h"
65 #include "encindex.h"
66 #define isdirsep(x) ((x) == '/' || (x) == '\\')
68 #if defined _MSC_VER && _MSC_VER <= 1200
69 # define CharNextExA(cp, p, flags) CharNextExA((WORD)(cp), (p), (flags))
70 #endif
72 static int w32_wopen(const WCHAR *file, int oflag, int perm);
73 static int w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat);
74 static char *w32_getenv(const char *name, UINT cp);
76 #undef getenv
78 * Do not remove the macros to substitute functions in dln_find.c.
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
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
109 #undef stat
110 #undef fclose
111 #undef close
112 #undef setsockopt
113 #undef dup2
114 #undef strdup
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)
123 #ifdef WIN32_DEBUG
124 #define Debug(something) something
125 #else
126 #define Debug(something) /* nothing */
127 #endif
129 #define TO_SOCKET(x) _get_osfhandle(x)
131 int rb_w32_reparse_symlink_p(const WCHAR *path);
133 static int has_redirection(const char *, UINT);
134 int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
135 static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
136 static int wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat);
137 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
138 int ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc);
139 static FARPROC get_proc_address(const char *module, const char *func, HANDLE *mh);
141 #define RUBY_CRITICAL if (0) {} else /* just remark */
143 /* errno mapping */
144 static 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 },
282 /* License: Ruby's */
284 rb_w32_map_errno(DWORD winerr)
286 int i;
288 if (winerr == 0) {
289 return 0;
292 for (i = 0; i < (int)(sizeof(errmap) / sizeof(*errmap)); i++) {
293 if (errmap[i].winerr == winerr) {
294 return errmap[i].err;
298 if (winerr >= WSABASEERR) {
299 return winerr;
301 return EINVAL;
304 #define map_errno rb_w32_map_errno
306 static const char *NTLoginName;
308 static OSVERSIONINFO osver;
310 /* License: Artistic or GPL */
311 static void
312 get_version(void)
314 memset(&osver, 0, sizeof(OSVERSIONINFO));
315 osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
316 GetVersionEx(&osver);
319 #ifdef _M_IX86
320 /* License: Artistic or GPL */
321 DWORD
322 rb_w32_osid(void)
324 return osver.dwPlatformId;
326 #endif
328 /* License: Artistic or GPL */
329 DWORD
330 rb_w32_osver(void)
332 return osver.dwMajorVersion;
335 /* simulate flock by locking a range on the file */
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); \
351 } while (0)
352 #define LK_LEN ULONG_MAX
354 /* License: Artistic or GPL */
355 static uintptr_t
356 flock_winnt(uintptr_t self, int argc, uintptr_t* argv)
358 OVERLAPPED o;
359 int i = -1;
360 const HANDLE fh = (HANDLE)self;
361 const int oper = argc;
363 memset(&o, 0, sizeof(o));
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;
388 return i;
391 #undef LK_ERR
393 /* License: Artistic or GPL */
395 flock(int fd, int oper)
397 const asynchronous_func_t locker = flock_winnt;
399 return rb_w32_asynchronize(locker,
400 (VALUE)_get_osfhandle(fd), oper, NULL,
401 (DWORD)-1);
404 /* License: Ruby's */
405 static inline WCHAR *
406 translate_wchar(WCHAR *p, int from, int to)
408 for (; *p; p++) {
409 if (*p == from)
410 *p = to;
412 return p;
415 /* License: Ruby's */
416 static inline char *
417 translate_char(char *p, int from, int to, UINT cp)
419 while (*p) {
420 if ((unsigned char)*p == from)
421 *p = to;
422 p = CharNextExA(cp, p, 0);
424 return p;
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
443 /* License: Ruby's */
444 static BOOL
445 get_special_folder(int n, WCHAR *buf, size_t len)
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;
453 if (func == (get_path_func)-1) {
454 func = (get_path_func)
455 get_proc_address("shell32", "SHGetPathFromIDListEx", NULL);
457 if (!func && len < MAX_PATH) return FALSE;
459 if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
460 if (func) {
461 f = func(pidl, buf, len, 0);
463 else {
464 f = SHGetPathFromIDListW(pidl, buf);
466 SHGetMalloc(&alloc);
467 alloc->lpVtbl->Free(alloc, pidl);
468 alloc->lpVtbl->Release(alloc);
470 return f;
473 /* License: Ruby's */
474 static void
475 regulate_path(WCHAR *path)
477 WCHAR *p = translate_wchar(path, L'\\', L'/');
478 if (p - path == 2 && path[1] == L':') {
479 *p++ = L'/';
480 *p = L'\0';
484 /* License: Ruby's */
485 static FARPROC
486 get_proc_address(const char *module, const char *func, HANDLE *mh)
488 HANDLE h;
489 FARPROC ptr;
491 if (mh)
492 h = LoadLibrary(module);
493 else
494 h = GetModuleHandle(module);
495 if (!h)
496 return NULL;
498 ptr = GetProcAddress(h, func);
499 if (mh) {
500 if (ptr)
501 *mh = h;
502 else
503 FreeLibrary(h);
505 return ptr;
508 /* License: Ruby's */
509 VALUE
510 rb_w32_special_folder(int type)
512 WCHAR path[PATH_MAX];
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());
519 #if defined _MSC_VER && _MSC_VER <= 1200
520 /* License: Ruby's */
521 #define GetSystemWindowsDirectoryW GetWindowsDirectoryW
522 #endif
524 /* License: Ruby's */
525 UINT
526 rb_w32_system_tmpdir(WCHAR *path, UINT len)
528 static const WCHAR temp[] = L"temp";
529 WCHAR *p;
531 if (!get_special_folder(CSIDL_LOCAL_APPDATA, path, len)) {
532 if (GetSystemWindowsDirectoryW(path, len)) return 0;
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);
542 Return user's home directory using environment variables combinations.
543 Memory allocated by this function should be manually freed
544 afterwards with xfree.
546 Try:
547 HOME, USERPROFILE, HOMEDRIVE + HOMEPATH environment variables
548 Special Folders - Profile and Personal
550 WCHAR *
551 rb_w32_home_dir(void)
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;
559 if ((len = GetEnvironmentVariableW(L"HOME", NULL, 0)) != 0) {
560 buffer_len = len;
561 home_type = ENV_HOME;
563 else if ((len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) != 0) {
564 buffer_len = len;
565 home_type = ENV_USERPROFILE;
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;
575 /* allocate buffer */
576 buffer = ALLOC_N(WCHAR, buffer_len);
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;
595 REALLOC_N(buffer, WCHAR, lstrlenW(buffer) + 1);
596 break;
599 /* sanitize backslashes with forwardslashes */
600 regulate_path(buffer);
602 return buffer;
605 /* License: Ruby's */
606 static void
607 init_env(void)
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)
621 wk.eq = L'=';
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;
632 else if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
633 f = TRUE;
635 else if (get_special_folder(CSIDL_PROFILE, env, numberof(env))) {
636 f = TRUE;
638 else if (get_special_folder(CSIDL_PERSONAL, env, numberof(env))) {
639 f = TRUE;
641 if (f) {
642 regulate_path(env);
643 set_env_val(L"HOME");
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>";
652 else {
653 set_env_val(L"USER");
654 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
657 else {
658 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
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);
668 #undef env
669 #undef set_env_val
672 static void init_stdhandle(void);
674 #if RUBY_MSVCRT_VERSION >= 80
675 /* License: Ruby's */
676 static void
677 invalid_parameter(const wchar_t *expr, const wchar_t *func, const wchar_t *file, unsigned int line, uintptr_t dummy)
679 // nothing to do
682 int ruby_w32_rtc_error;
684 # ifndef __MINGW32__
685 /* License: Ruby's */
686 RBIMPL_ATTR_NONNULL((5))
687 RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6)
688 static int __cdecl
689 rtc_error_handler(int e, const char *src, int line, const char *exe, const char *fmt, ...)
691 va_list ap;
692 VALUE str;
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);
700 rb_write_error2(RSTRING_PTR(str), RSTRING_LEN(str));
701 return 0;
703 # endif
704 #endif
706 static CRITICAL_SECTION select_mutex;
708 static CRITICAL_SECTION socklist_mutex;
709 static st_table *socklist = NULL;
711 static CRITICAL_SECTION conlist_mutex;
712 static st_table *conlist = NULL;
713 #define conlist_disabled ((st_table *)-1)
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))
720 static CRITICAL_SECTION uenvarea_mutex;
721 static char *uenvarea;
723 /* License: Ruby's */
724 struct constat {
725 struct {
726 int state, seq[16], reverse;
727 WORD attr;
728 COORD saved;
729 } vt100;
731 enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
733 /* License: Ruby's */
734 static int
735 free_conlist(st_data_t key, st_data_t val, st_data_t arg)
737 xfree((struct constat *)val);
738 return ST_DELETE;
741 /* License: Ruby's */
742 static void
743 constat_delete(HANDLE h)
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);
754 /* License: Ruby's */
755 static void
756 exit_handler(void)
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;
768 DeleteCriticalSection(&uenvarea_mutex);
771 /* License: Ruby's */
772 static void
773 vm_exit_handler(ruby_vm_t *vm)
775 EnterCriticalSection(&socklist_mutex);
776 if (socklist) {
777 st_free_table(socklist);
778 socklist = NULL;
780 LeaveCriticalSection(&socklist_mutex);
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;
788 LeaveCriticalSection(&conlist_mutex);
791 #define ATOMIC_LONG_CAS(var, oldval, newval) InterlockedCompareExchange(&(var), (newval), (oldval))
793 /* License: Ruby's */
794 static void
795 install_vm_exit_handler(void)
797 static LONG installed = 0;
798 LONG i;
800 while ((i = ATOMIC_LONG_CAS(installed, 0, -1)) != 1) {
801 if (i != 0) {
802 Sleep(1);
803 continue;
805 ruby_vm_at_exit(vm_exit_handler);
806 ATOMIC_LONG_CAS(installed, -1, 1);
807 break;
811 /* License: Artistic or GPL */
812 static void
813 StartSockets(void)
815 WORD version;
816 WSADATA retdata;
819 // initialize the winsock interface and insure that it's
820 // cleaned up at exit.
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");
828 InitializeCriticalSection(&select_mutex);
829 InitializeCriticalSection(&socklist_mutex);
830 InitializeCriticalSection(&conlist_mutex);
832 atexit(exit_handler);
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))
839 /* License: Ruby's */
840 static inline int
841 socklist_insert(SOCKET sock, int flag)
843 int ret;
845 thread_exclusive(socklist) {
846 if (!socklist) {
847 socklist = st_init_numtable();
848 install_vm_exit_handler();
850 ret = st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
853 return ret;
856 /* License: Ruby's */
857 static inline int
858 socklist_lookup(SOCKET sock, int *flagp)
860 st_data_t data;
861 int ret = 0;
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;
870 return ret;
873 /* License: Ruby's */
874 static inline int
875 socklist_delete(SOCKET *sockp, int *flagp)
877 st_data_t key;
878 st_data_t data;
879 int ret = 0;
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;
894 return ret;
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
902 static void set_pioinfo_extra(void);
903 #endif
904 static int w32_cmdvector(const WCHAR *, char ***, UINT, rb_encoding *);
906 // Initialization stuff
908 /* License: Ruby's */
909 void
910 rb_w32_sysinit(int *argc, char ***argv)
912 #if RUBY_MSVCRT_VERSION >= 80
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);
921 get_version();
924 // subvert cmd.exe's feeble attempt at command line parsing
926 *argc = w32_cmdvector(GetCommandLineW(), argv, CP_UTF8, &OnigEncodingUTF_8);
929 // Now set up the correct time stuff
932 tzset();
934 InitializeCriticalSection(&uenvarea_mutex);
935 init_env();
937 init_stdhandle();
939 // Initialize Winsock
940 StartSockets();
943 char *
944 getlogin(void)
946 return (char *)NTLoginName;
949 #define MAXCHILDNUM 256 /* max num of child processes */
951 /* License: Ruby's */
952 static struct ChildRecord {
953 HANDLE hProcess; /* process handle */
954 rb_pid_t pid; /* process id */
955 } ChildRecord[MAXCHILDNUM];
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)
963 /* License: Ruby's */
964 static struct ChildRecord *
965 FindChildSlot(rb_pid_t pid)
968 FOREACH_CHILD(child) {
969 if (child->pid == pid) {
970 return child;
972 } END_FOREACH_CHILD;
973 return NULL;
976 /* License: Ruby's */
977 static struct ChildRecord *
978 FindChildSlotByHandle(HANDLE h)
981 FOREACH_CHILD(child) {
982 if (child->hProcess == h) {
983 return child;
985 } END_FOREACH_CHILD;
986 return NULL;
989 /* License: Ruby's */
990 static void
991 CloseChildHandle(struct ChildRecord *child)
993 HANDLE h = child->hProcess;
994 child->hProcess = NULL;
995 child->pid = 0;
996 CloseHandle(h);
999 /* License: Ruby's */
1000 static struct ChildRecord *
1001 FindFreeChildSlot(void)
1003 FOREACH_CHILD(child) {
1004 if (!child->pid) {
1005 child->pid = -1; /* lock the slot */
1006 child->hProcess = NULL;
1007 return child;
1009 } END_FOREACH_CHILD;
1010 return NULL;
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
1020 #define InternalCmdsMax 8
1021 static 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",
1073 /* License: Ruby's */
1074 static int
1075 internal_match(const void *key, const void *elem)
1077 return strncmp(key, ((const char *)elem) + 1, InternalCmdsMax);
1080 /* License: Ruby's */
1081 static int
1082 is_command_com(const char *interp)
1084 int i = strlen(interp) - 11;
1086 if ((i == 0 || (i > 0 && isdirsep(interp[i-1]))) &&
1087 strcasecmp(interp+i, "command.com") == 0) {
1088 return 1;
1090 return 0;
1093 static int internal_cmd_match(const char *cmdname, int nt);
1095 /* License: Ruby's */
1096 static int
1097 is_internal_cmd(const char *cmd, int nt)
1099 char cmdname[9], *b = cmdname, c;
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++;
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;
1120 *b = 0;
1121 return internal_cmd_match(cmdname, nt);
1124 /* License: Ruby's */
1125 static int
1126 internal_cmd_match(const char *cmdname, int nt)
1128 char *nm;
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;
1139 /* License: Ruby's */
1140 SOCKET
1141 rb_w32_get_osfhandle(int fh)
1143 return _get_osfhandle(fh);
1146 /* License: Ruby's */
1147 static int
1148 join_argv(char *cmd, char *const *argv, BOOL escape, UINT cp, int backslash)
1150 const char *p, *s;
1151 char *q, *const *t;
1152 int len, n, bs, quote;
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++ = '"';
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;
1173 s = p;
1174 len += ++bs;
1175 if (q) {
1176 memset(q, '\\', bs);
1177 q += bs;
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++ = '^';
1189 s = p;
1190 break;
1192 default:
1193 bs = 0;
1194 p = CharNextExA(cp, p, 0) - 1;
1195 break;
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);
1207 q += n;
1208 if (quote) *q++ = '"';
1209 *q++ = ' ';
1212 if (q > cmd) --len;
1213 if (q) {
1214 if (q > cmd) --q;
1215 *q = '\0';
1217 return len;
1220 /* License: Ruby's */
1221 #define STRNDUPV(ptr, v, src, len) \
1222 (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
1224 /* License: Ruby's */
1225 static int
1226 check_spawn_mode(int mode)
1228 switch (mode) {
1229 case P_NOWAIT:
1230 case P_OVERLAY:
1231 return 0;
1232 default:
1233 errno = EINVAL;
1234 return -1;
1238 /* License: Ruby's */
1239 static rb_pid_t
1240 child_result(struct ChildRecord *child, int mode)
1242 DWORD exitcode;
1244 if (!child) {
1245 return -1;
1248 if (mode == P_OVERLAY) {
1249 WaitForSingleObject(child->hProcess, INFINITE);
1250 GetExitCodeProcess(child->hProcess, &exitcode);
1251 CloseChildHandle(child);
1252 _exit(exitcode);
1254 return child->pid;
1257 /* License: Ruby's */
1258 static int
1259 CreateChild(struct ChildRecord *child, const WCHAR *cmd, const WCHAR *prog, HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
1261 BOOL fRet;
1262 STARTUPINFOW aStartupInfo;
1263 PROCESS_INFORMATION aProcessInformation;
1264 SECURITY_ATTRIBUTES sa;
1266 if (!cmd && !prog) {
1267 errno = EFAULT;
1268 return FALSE;
1271 if (!child) {
1272 errno = EAGAIN;
1273 return FALSE;
1276 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
1277 sa.lpSecurityDescriptor = NULL;
1278 sa.bInheritHandle = TRUE;
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;
1287 else {
1288 aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1290 if (hOutput) {
1291 aStartupInfo.hStdOutput = hOutput;
1293 else {
1294 aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1296 if (hError) {
1297 aStartupInfo.hStdError = hError;
1299 else {
1300 aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1303 dwCreationFlags |= NORMAL_PRIORITY_CLASS;
1305 if (lstrlenW(cmd) > 32767) {
1306 child->pid = 0; /* release the slot */
1307 errno = E2BIG;
1308 return FALSE;
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());
1318 if (!fRet) {
1319 child->pid = 0; /* release the slot */
1320 return FALSE;
1323 CloseHandle(aProcessInformation.hThread);
1325 child->hProcess = aProcessInformation.hProcess;
1326 child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
1328 return TRUE;
1331 /* License: Ruby's */
1332 static int
1333 is_batch(const char *cmd)
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;
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)
1354 /* License: Ruby's */
1355 MJIT_FUNC_EXPORTED HANDLE
1356 rb_w32_start_process(const char *abspath, char *const *argv, int out_fd)
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;
1368 if (out_fd) {
1369 outHandle = (HANDLE)rb_w32_get_osfhandle(out_fd);
1372 len = join_argv(NULL, argv, FALSE, filecp(), 1);
1373 cmd = alloca(sizeof(char) * len);
1374 join_argv(cmd, argv, FALSE, filecp(), 1);
1376 if (!(wcmd = mbstr_to_wstr(filecp(), cmd, -1, NULL))) {
1377 errno = E2BIG;
1378 return NULL;
1380 if (!(wprog = mbstr_to_wstr(filecp(), abspath, -1, NULL))) {
1381 errno = E2BIG;
1382 return NULL;
1385 if (!CreateChild(&child, wcmd, wprog, NULL, outHandle, outHandle, 0)) {
1386 return NULL;
1389 free(wcmd);
1390 free(wprog);
1391 return child.hProcess;
1394 /* License: Artistic or GPL */
1395 static rb_pid_t
1396 w32_spawn(int mode, const char *cmd, const char *prog, UINT cp)
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;
1409 if (check_spawn_mode(mode)) return -1;
1411 if (prog) {
1412 if (!(p = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1413 shell = prog;
1415 else {
1416 shell = p;
1417 translate_char(p, '/', '\\', cp);
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;
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;
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;
1453 shell = cmd;
1454 break;
1456 if ((unsigned char)*prog == quote) {
1457 len = prog++ - cmd - 1;
1458 STRNDUPV(p, v2, cmd + 1, len);
1459 shell = p;
1460 break;
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';
1471 shell = p;
1472 break;
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;
1480 else {
1481 len = strlen(shell);
1482 if (strchr(shell, ' ')) quote = -1;
1483 if (shell == fbuf) {
1484 p = fbuf;
1486 else if (shell != p && strchr(shell, '/')) {
1487 STRNDUPV(p, v2, shell, len);
1488 shell = p;
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;
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);
1511 if (!e) {
1512 struct ChildRecord *child = FindFreeChildSlot();
1513 if (CreateChild(child, wcmd, wshell, NULL, NULL, NULL, 0)) {
1514 ret = child_result(child, mode);
1517 free(wshell);
1518 free(wcmd);
1519 if (e) errno = e;
1520 return ret;
1523 /* License: Ruby's */
1524 rb_pid_t
1525 rb_w32_spawn(int mode, const char *cmd, const char *prog)
1527 /* assume ACP */
1528 return w32_spawn(mode, cmd, prog, filecp());
1531 /* License: Ruby's */
1532 rb_pid_t
1533 rb_w32_uspawn(int mode, const char *cmd, const char *prog)
1535 return w32_spawn(mode, cmd, prog, CP_UTF8);
1538 /* License: Artistic or GPL */
1539 static rb_pid_t
1540 w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags, UINT cp)
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;
1552 if (check_spawn_mode(mode)) return -1;
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;
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;
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;
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;
1589 else {
1590 len = join_argv(NULL, argv, FALSE, cp, 1);
1591 cmd = ALLOCV(v, len);
1592 join_argv(cmd, argv, FALSE, cp, 1);
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;
1599 if (!e) {
1600 struct ChildRecord *child = FindFreeChildSlot();
1601 if (CreateChild(child, wcmd, wprog, NULL, NULL, NULL, flags)) {
1602 ret = child_result(child, mode);
1605 free(wprog);
1606 free(wcmd);
1607 if (e) errno = e;
1608 return ret;
1611 /* License: Ruby's */
1612 rb_pid_t
1613 rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1615 /* assume ACP */
1616 return w32_aspawn_flags(mode, prog, argv, flags, filecp());
1619 /* License: Ruby's */
1620 rb_pid_t
1621 rb_w32_uaspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1623 return w32_aspawn_flags(mode, prog, argv, flags, CP_UTF8);
1626 /* License: Ruby's */
1627 rb_pid_t
1628 rb_w32_aspawn(int mode, const char *prog, char *const *argv)
1630 return w32_aspawn_flags(mode, prog, argv, 0, filecp());
1633 /* License: Ruby's */
1634 rb_pid_t
1635 rb_w32_uaspawn(int mode, const char *prog, char *const *argv)
1637 return rb_w32_uaspawn_flags(mode, prog, argv, 0);
1640 /* License: Artistic or GPL */
1641 typedef struct _NtCmdLineElement {
1642 struct _NtCmdLineElement *next;
1643 char *str;
1644 long len;
1645 int flags;
1646 } NtCmdLineElement;
1649 // Possible values for flags
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
1656 /* License: Ruby's */
1657 static int
1658 insert(const char *path, VALUE vinfo, void *enc)
1660 NtCmdLineElement *tmpcurr;
1661 NtCmdLineElement ***tail = (NtCmdLineElement ***)vinfo;
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;
1673 return 0;
1676 /* License: Artistic or GPL */
1677 static NtCmdLineElement **
1678 cmdglob(NtCmdLineElement *patt, NtCmdLineElement **tail, UINT cp, rb_encoding *enc)
1680 char buffer[PATH_MAX], *buf = buffer;
1681 NtCmdLineElement **last = tail;
1682 int status;
1684 if (patt->len >= PATH_MAX)
1685 if (!(buf = malloc(patt->len + 1))) return 0;
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);
1694 if (status || last == tail) return 0;
1695 if (patt->flags & NTMALLOC)
1696 free(patt->str);
1697 free(patt);
1698 return tail;
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
1706 /* License: Artistic or GPL */
1707 static int
1708 has_redirection(const char *cmd, UINT cp)
1710 char quote = '\0';
1711 const char *ptr;
1714 // Scan the string, looking for redirection characters (< or >), pipe
1715 // character (|) or newline (\n) that are not in a quoted string
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;
1729 case '>':
1730 case '<':
1731 case '|':
1732 case '&':
1733 case '\n':
1734 if (!quote)
1735 return TRUE;
1736 ptr++;
1737 break;
1739 case '%':
1740 if (*++ptr != '_' && !ISALPHA(*ptr)) break;
1741 while (*++ptr == '_' || ISALNUM(*ptr));
1742 if (*ptr++ == '%') return TRUE;
1743 break;
1745 case '\\':
1746 ptr++;
1747 default:
1748 ptr = CharNextExA(cp, ptr, 0);
1749 break;
1752 return FALSE;
1755 /* License: Ruby's */
1756 static inline WCHAR *
1757 skipspace(WCHAR *ptr)
1759 while (ISSPACE(*ptr))
1760 ptr++;
1761 return ptr;
1764 /* License: Artistic or GPL */
1765 static int
1766 w32_cmdvector(const WCHAR *cmd, char ***vec, UINT cp, rb_encoding *enc)
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;
1779 // just return if we don't have a command line
1781 while (ISSPACE(*cmd))
1782 cmd++;
1783 if (!*cmd) {
1784 *vec = NULL;
1785 return 0;
1788 ptr = cmdline = wcsdup(cmd);
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.
1799 while (*(ptr = skipspace(ptr))) {
1800 base = ptr;
1801 quote = slashes = globbing = escape = 0;
1802 for (done = 0; !done && *ptr; ) {
1804 // Switch on the current character. We only care about the
1805 // white-space characters, the wild-card characters, and the
1806 // quote characters.
1809 switch (*ptr) {
1810 case L'\\':
1811 if (quote != L'\'') slashes++;
1812 break;
1814 case L' ':
1815 case L'\t':
1816 case L'\n':
1818 // if we're not in a string, then we're finished with this
1819 // element
1822 if (!quote) {
1823 *ptr = 0;
1824 done = 1;
1826 break;
1828 case L'*':
1829 case L'?':
1830 case L'[':
1831 case L'{':
1833 // record the fact that this element has a wildcard character
1834 // N.B. Don't glob if inside a single quoted string
1837 if (quote != L'\'')
1838 globbing++;
1839 slashes = 0;
1840 break;
1842 case L'\'':
1843 case L'\"':
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.
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';
1860 escape++;
1861 slashes = 0;
1862 break;
1864 default:
1865 ptr = CharNextW(ptr);
1866 slashes = 0;
1867 continue;
1869 ptr++;
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.
1878 len = ptr - base;
1879 if (done) --len;
1882 // if it's an input vector element and it's enclosed by quotes,
1883 // we can remove them.
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;
1896 case L'\'':
1897 case L'"':
1898 if (!(slashes & 1) && quote && quote != c) {
1899 p++;
1900 slashes = 0;
1901 break;
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';
1913 else
1914 quote = c;
1916 else
1917 p++;
1918 slashes = 0;
1919 break;
1921 default:
1922 p = CharNextW(p);
1923 slashes = 0;
1924 break;
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;
1934 if (globbing && (tail = cmdglob(curr, cmdtail, cp, enc))) {
1935 cmdtail = tail;
1937 else {
1938 *cmdtail = curr;
1939 cmdtail = &curr->next;
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.
1949 for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
1950 elements++;
1951 strsz += (curr->len + 1);
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);
1963 free(cmdline);
1964 for (vptr = *vec; *vptr; ++vptr);
1965 return vptr - *vec;
1969 // make vptr point to the start of the buffer
1970 // and cptr point to the area we'll consider the string table.
1972 // buffer (*vec)
1973 // |
1974 // V ^---------------------V
1975 // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1976 // | | | .... | NULL | | ..... |\0 | | ..... |\0 |...
1977 // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1978 // |- elements+1 -| ^ 1st element ^ 2nd element
1980 vptr = (char **) buffer;
1982 cptr = buffer + (elements+1) * sizeof(char *);
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);
1993 *vptr = 0;
1995 *vec = (char **) buffer;
1996 free(cmdline);
1997 return elements;
2001 // UNIX compatible directory access functions for NT
2004 typedef DWORD (WINAPI *get_final_path_func)(HANDLE, WCHAR*, DWORD, DWORD);
2005 static get_final_path_func get_final_path;
2007 static DWORD WINAPI
2008 get_final_path_fail(HANDLE f, WCHAR *buf, DWORD len, DWORD flag)
2010 return 0;
2013 static DWORD WINAPI
2014 get_final_path_unknown(HANDLE f, WCHAR *buf, DWORD len, DWORD flag)
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);
2024 static get_final_path_func get_final_path = get_final_path_unknown;
2026 /* License: Ruby's */
2027 /* TODO: better name */
2028 static HANDLE
2029 open_special(const WCHAR *path, DWORD access, DWORD flags)
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);
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.
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))
2047 #define BitOfIsDir(n) ((n) * 2)
2048 #define BitOfIsRep(n) ((n) * 2 + 1)
2049 #define DIRENT_PER_CHAR (CHAR_BIT / 2)
2051 static const WCHAR namespace_prefix[] = {L'\\', L'\\', L'?', L'\\'};
2053 enum {FINAL_PATH_MAX = PATH_MAX + numberof(namespace_prefix)};
2055 /* License: Artistic or GPL */
2056 static HANDLE
2057 open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
2059 HANDLE fh;
2060 WCHAR fullname[FINAL_PATH_MAX + rb_strlen_lit("\\*")];
2061 WCHAR *p;
2062 int len = 0;
2065 // Create the search pattern
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;
2077 if (!len) {
2078 len = lstrlenW(filename);
2079 if (len >= PATH_MAX) {
2080 errno = ENAMETOOLONG;
2081 return INVALID_HANDLE_VALUE;
2083 MEMCPY(fullname, filename, WCHAR, len);
2085 p = &fullname[len-1];
2086 if (!(isdirsep(*p) || *p == L':')) *++p = L'\\';
2087 *++p = L'*';
2088 *++p = L'\0';
2091 // do the FindFirstFile call
2093 fh = FindFirstFileW(fullname, fd);
2094 if (fh == INVALID_HANDLE_VALUE) {
2095 errno = map_errno(GetLastError());
2097 return fh;
2100 /* License: Artistic or GPL */
2101 static DIR *
2102 w32_wopendir(const WCHAR *wpath)
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;
2116 // check to see if we've got a directory
2118 if (wstati128(wpath, &sbuf, FALSE) < 0) {
2119 return NULL;
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;
2127 fh = open_dir_handle(wpath, &fd);
2128 if (fh == INVALID_HANDLE_VALUE) {
2129 return NULL;
2133 // Get us a DIR structure
2135 p = calloc(sizeof(DIR), 1);
2136 if (p == NULL)
2137 return NULL;
2139 pathlen = lstrlenW(wpath);
2140 idx = 0;
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.
2148 do {
2149 len = lstrlenW(fd.cFileName) + 1;
2150 altlen = lstrlenW(fd.cAlternateFileName) + 1;
2153 // bump the string table size by enough for the
2154 // new name and it's null terminator
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;
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));
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;
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);
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;
2197 /* License: Ruby's */
2198 UINT
2199 filecp(void)
2201 UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
2202 return cp;
2205 /* License: Ruby's */
2206 char *
2207 rb_w32_wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
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;
2218 return ptr;
2221 /* License: Ruby's */
2222 WCHAR *
2223 rb_w32_mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
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;
2235 return ptr;
2238 /* License: Ruby's */
2239 DIR *
2240 rb_w32_opendir(const char *filename)
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;
2251 /* License: Ruby's */
2252 DIR *
2253 rb_w32_uopendir(const char *filename)
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;
2265 // Move to next entry
2268 /* License: Artistic or GPL */
2269 static void
2270 move_to_next_entry(DIR *dirp)
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;
2283 // Readdir just returns the current string pointer and bumps the
2284 // string pointer to the next entry.
2286 /* License: Ruby's */
2287 static BOOL
2288 win32_direct_conv(const WCHAR *file, const WCHAR *alt, struct direct *entry, const void *enc)
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;
2298 return TRUE;
2301 /* License: Ruby's */
2302 VALUE
2303 rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
2305 VALUE src;
2306 long len = lstrlenW(wstr);
2307 int encindex = rb_enc_to_index(enc);
2309 if (encindex == ENCINDEX_UTF_16LE) {
2310 return rb_enc_str_new((char *)wstr, len * sizeof(WCHAR), enc);
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);
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;
2329 return rb_str_conv_enc_opts(src, NULL, enc, ECONV_UNDEF_REPLACE, Qnil);
2332 /* License: Ruby's */
2333 char *
2334 rb_w32_conv_from_wstr(const WCHAR *wstr, long *lenp, rb_encoding *enc)
2336 VALUE str = rb_w32_conv_from_wchar(wstr, enc);
2337 long len;
2338 char *ptr;
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;
2347 /* License: Ruby's */
2348 static BOOL
2349 ruby_direct_conv(const WCHAR *file, const WCHAR *alt, struct direct *entry, const void *enc)
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;
2358 return TRUE;
2361 /* License: Artistic or GPL */
2362 static struct direct *
2363 readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, const WCHAR *, struct direct *, const void *), const void *enc)
2365 static long dummy_ino = 0;
2367 if (dirp->curr) {
2370 // first set up the structure to return
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);
2381 // Fake inode
2383 dirp->dirstr.d_ino = (ino_t)(InterlockedIncrement(&dummy_ino) - 1);
2386 // Attributes
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;
2397 // Now set up for the next call to readdir
2400 move_to_next_entry(dirp);
2402 return &(dirp->dirstr);
2405 else
2406 return NULL;
2409 /* License: Ruby's */
2410 struct direct *
2411 rb_w32_readdir(DIR *dirp, rb_encoding *enc)
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);
2418 else if (idx == ENCINDEX_UTF_8) {
2419 const UINT cp = CP_UTF8;
2420 return readdir_internal(dirp, win32_direct_conv, &cp);
2422 else
2423 return readdir_internal(dirp, ruby_direct_conv, enc);
2426 /* License: Ruby's */
2427 struct direct *
2428 rb_w32_ureaddir(DIR *dirp)
2430 const UINT cp = CP_UTF8;
2431 return readdir_internal(dirp, win32_direct_conv, &cp);
2435 // Telldir returns the current string pointer position
2438 /* License: Artistic or GPL */
2439 long
2440 rb_w32_telldir(DIR *dirp)
2442 return dirp->loc;
2446 // Seekdir moves the string pointer to a previously saved position
2447 // (Saved by telldir).
2449 /* License: Ruby's */
2450 void
2451 rb_w32_seekdir(DIR *dirp, long loc)
2453 if (dirp->loc > loc) rb_w32_rewinddir(dirp);
2455 while (dirp->curr && dirp->loc < loc) {
2456 move_to_next_entry(dirp);
2461 // Rewinddir resets the string pointer to the start
2464 /* License: Artistic or GPL */
2465 void
2466 rb_w32_rewinddir(DIR *dirp)
2468 dirp->curr = dirp->start;
2469 dirp->loc = 0;
2473 // This just free's the memory allocated by opendir
2476 /* License: Artistic or GPL */
2477 void
2478 rb_w32_closedir(DIR *dirp)
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);
2493 #if RUBY_MSVCRT_VERSION >= 140
2494 typedef struct {
2495 union
2497 FILE _public_file;
2498 char* _ptr;
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
2519 /* License: Ruby's */
2520 #if RUBY_MSVCRT_VERSION >= 140
2521 typedef char lowio_text_mode;
2522 typedef char lowio_pipe_lookahead[3];
2524 typedef 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;
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
2538 typedef 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
2551 #if !defined _CRTIMP || defined __MINGW32__
2552 #undef _CRTIMP
2553 #define _CRTIMP __declspec(dllimport)
2554 #endif
2556 #if RUBY_MSVCRT_VERSION >= 140
2557 static ioinfo ** __pioinfo = NULL;
2558 #define IOINFO_L2E 6
2559 #else
2560 EXTERN_C _CRTIMP ioinfo * __pioinfo[];
2561 #define IOINFO_L2E 5
2562 #endif
2563 static inline ioinfo* _pioinfo(int);
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)
2572 #if RUBY_MSVCRT_VERSION >= 80
2573 static size_t pioinfo_extra = 0; /* workaround for VC++8 SP1 */
2575 /* License: Ruby's */
2576 static void
2577 set_pioinfo_extra(void)
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 */
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)
2595 * see also
2596 * * https://bugs.ruby-lang.org/issues/11118
2597 * * https://bugs.ruby-lang.org/issues/18605
2599 char *p = (char*)get_proc_address(UCRTBASE, "_isatty", NULL);
2600 char *pend = p;
2601 /* _osfile(fh) & FDEV */
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
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;
2636 break;
2640 fprintf(stderr, "unexpected " UCRTBASE "\n");
2641 _exit(1);
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;
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;
2661 _close(fd);
2663 if (pioinfo_extra > 64) {
2664 /* not found, maybe something wrong... */
2665 pioinfo_extra = 0;
2668 #else
2669 #define pioinfo_extra 0
2670 #endif
2672 static inline ioinfo*
2673 _pioinfo(int fd)
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);
2680 #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2681 #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
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 */
2691 static int is_socket(SOCKET);
2692 static int is_console(SOCKET);
2694 /* License: Ruby's */
2696 rb_w32_io_cancelable_p(int fd)
2698 return is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd));
2701 /* License: Ruby's */
2702 static int
2703 rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2705 int fh;
2706 char fileflags; /* _osfile flags */
2707 HANDLE hF;
2709 /* copy relevant flags from second parameter */
2710 fileflags = FDEV;
2712 if (flags & O_APPEND)
2713 fileflags |= FAPPEND;
2715 if (flags & O_TEXT)
2716 fileflags |= FTEXT;
2718 if (flags & O_NOINHERIT)
2719 fileflags |= FNOINHERIT;
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 */
2729 else {
2731 rb_acrt_lowio_lock_fh(fh);
2732 /* the file is open. now, set the info in _osfhnd array */
2733 _set_osfhnd(fh, osfhandle);
2735 fileflags |= FOPEN; /* mark as open */
2737 _set_osflags(fh, fileflags); /* set osfile entry */
2738 rb_acrt_lowio_unlock_fh(fh);
2740 return fh; /* return handle */
2743 /* License: Ruby's */
2744 static void
2745 init_stdhandle(void)
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))
2755 if (fileno(stdin) < 0) {
2756 FILE_FILENO(stdin) = open_null(0);
2758 else {
2759 setmode(fileno(stdin), O_BINARY);
2761 if (fileno(stdout) < 0) {
2762 FILE_FILENO(stdout) = open_null(1);
2764 if (fileno(stderr) < 0) {
2765 FILE_FILENO(stderr) = open_null(2);
2767 if (nullfd >= 0 && !keep) close(nullfd);
2768 setvbuf(stderr, NULL, _IONBF, 0);
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);
2782 #undef getsockopt
2784 /* License: Ruby's */
2785 static int
2786 is_socket(SOCKET sock)
2788 if (socklist_lookup(sock, NULL))
2789 return TRUE;
2790 else
2791 return FALSE;
2794 /* License: Ruby's */
2796 rb_w32_is_socket(int fd)
2798 return is_socket(TO_SOCKET(fd));
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.
2807 #undef strerror
2809 /* License: Artistic or GPL */
2810 char *
2811 rb_w32_strerror(int e)
2813 static char buffer[512];
2814 DWORD source = 0;
2815 char *p;
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;
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));
2844 else
2845 strlcpy(buffer, strerror(e), sizeof(buffer));
2847 p = buffer;
2848 while ((p = strpbrk(p, "\r\n")) != NULL) {
2849 memmove(p, p + 1, strlen(p));
2851 return buffer;
2855 // various stubs
2859 // Ownership
2861 // Just pretend that everyone is a superuser. NT will let us know if
2862 // we don't really have permission to do something.
2865 #define ROOT_UID 0
2866 #define ROOT_GID 0
2868 /* License: Artistic or GPL */
2869 rb_uid_t
2870 getuid(void)
2872 return ROOT_UID;
2875 /* License: Artistic or GPL */
2876 rb_uid_t
2877 geteuid(void)
2879 return ROOT_UID;
2882 /* License: Artistic or GPL */
2883 rb_gid_t
2884 getgid(void)
2886 return ROOT_GID;
2889 /* License: Artistic or GPL */
2890 rb_gid_t
2891 getegid(void)
2893 return ROOT_GID;
2896 /* License: Artistic or GPL */
2898 setuid(rb_uid_t uid)
2900 return (uid == ROOT_UID ? 0 : -1);
2903 /* License: Artistic or GPL */
2905 setgid(rb_gid_t gid)
2907 return (gid == ROOT_GID ? 0 : -1);
2911 // File system stuff
2914 /* License: Artistic or GPL */
2916 ioctl(int i, int u, ...)
2918 errno = EINVAL;
2919 return -1;
2922 void
2923 rb_w32_fdset(int fd, fd_set *set)
2925 FD_SET(fd, set);
2928 #undef FD_CLR
2930 /* License: Ruby's */
2931 void
2932 rb_w32_fdclr(int fd, fd_set *set)
2934 unsigned int i;
2935 SOCKET s = TO_SOCKET(fd);
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;
2946 #undef FD_ISSET
2948 /* License: Ruby's */
2950 rb_w32_fdisset(int fd, fd_set *set)
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;
2960 /* License: Ruby's */
2961 void
2962 rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
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);
2970 memcpy(dst->fdset->fd_array, src->fd_array,
2971 max * sizeof(src->fd_array[0]));
2972 dst->fdset->fd_count = src->fd_count;
2975 /* License: Ruby's */
2976 void
2977 rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
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);
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;
2990 // Networking trampolines
2991 // These are used to avoid socket startup/shutdown overhead in case
2992 // the socket routines aren't used.
2995 #undef select
2997 /* License: Ruby's */
2998 static int
2999 extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
3001 unsigned int s = 0;
3002 unsigned int m = 0;
3003 if (!src) return 0;
3005 while (s < src->fd_count) {
3006 SOCKET fd = src->fd_array[s];
3008 if (!func || (*func)(fd)) {
3009 if (dst) { /* move it to dst */
3010 unsigned int d;
3012 for (d = 0; d < dst->fdset->fd_count; d++) {
3013 if (dst->fdset->fd_array[d] == fd)
3014 break;
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);
3021 dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
3023 memmove(
3024 &src->fd_array[s],
3025 &src->fd_array[s+1],
3026 sizeof(src->fd_array[0]) * (--src->fd_count - s));
3028 else {
3029 m++;
3030 s++;
3033 else s++;
3036 return dst ? dst->fdset->fd_count : m;
3039 /* License: Ruby's */
3040 static int
3041 copy_fd(fd_set *dst, fd_set *src)
3043 unsigned int s;
3044 if (!src || !dst) return 0;
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;
3053 if (d == dst->fd_count && d < FD_SETSIZE) {
3054 dst->fd_array[dst->fd_count++] = fd;
3058 return dst->fd_count;
3061 /* License: Ruby's */
3062 static int
3063 is_not_socket(SOCKET sock)
3065 return !is_socket(sock);
3068 /* License: Ruby's */
3069 static int
3070 is_pipe(SOCKET sock) /* DONT call this for SOCKET! it claims it is PIPE. */
3072 int ret;
3074 RUBY_CRITICAL {
3075 ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
3078 return ret;
3081 /* License: Ruby's */
3082 static int
3083 is_readable_pipe(SOCKET sock) /* call this for pipe only */
3085 int ret;
3086 DWORD n = 0;
3088 RUBY_CRITICAL {
3089 if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
3090 ret = (n > 0);
3092 else {
3093 ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
3097 return ret;
3100 /* License: Ruby's */
3101 static int
3102 is_console(SOCKET sock) /* DONT call this for SOCKET! */
3104 int ret;
3105 DWORD n = 0;
3106 INPUT_RECORD ir;
3108 RUBY_CRITICAL {
3109 ret = (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n));
3112 return ret;
3115 /* License: Ruby's */
3116 static int
3117 is_readable_console(SOCKET sock) /* call this for console only */
3119 int ret = 0;
3120 DWORD n = 0;
3121 INPUT_RECORD ir;
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;
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;
3134 else {
3135 ReadConsoleInputW((HANDLE)sock, &ir, 1, &n);
3140 return ret;
3143 /* License: Ruby's */
3144 static int
3145 is_invalid_handle(SOCKET sock)
3147 return (HANDLE)sock == INVALID_HANDLE_VALUE;
3150 /* License: Artistic or GPL */
3151 static int
3152 do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3153 struct timeval *timeout)
3155 int r = 0;
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);
3163 else {
3164 RUBY_CRITICAL {
3165 thread_exclusive(select) {
3166 r = select(nfds, rd, wr, ex, timeout);
3168 if (r == SOCKET_ERROR) {
3169 errno = map_errno(WSAGetLastError());
3170 r = -1;
3175 return r;
3179 * rest -= wait
3180 * return 0 if rest is smaller than wait.
3182 /* License: Ruby's */
3184 rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
3186 if (rest->tv_sec < wait->tv_sec) {
3187 return 0;
3189 while (rest->tv_usec < wait->tv_usec) {
3190 if (rest->tv_sec <= wait->tv_sec) {
3191 return 0;
3193 rest->tv_sec -= 1;
3194 rest->tv_usec += 1000 * 1000;
3196 rest->tv_sec -= wait->tv_sec;
3197 rest->tv_usec -= wait->tv_usec;
3198 return rest->tv_sec != 0 || rest->tv_usec != 0;
3201 /* License: Ruby's */
3202 static inline int
3203 compare(const struct timeval *t1, const struct timeval *t2)
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;
3216 #undef Sleep
3218 int rb_w32_check_interrupt(void *); /* @internal */
3220 /* @internal */
3221 /* License: Ruby's */
3223 rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3224 struct timeval *timeout, void *th)
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};
3235 if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
3236 errno = EINVAL;
3237 return -1;
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;
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++;
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.
3262 rb_fd_init(&else_rd);
3263 nonsock += extract_fd(&else_rd, rd, is_not_socket);
3265 rb_fd_init(&else_wr);
3266 nonsock += extract_fd(&else_wr, wr, is_not_socket);
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;
3277 rb_fd_init(&pipe_rd);
3278 extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
3280 rb_fd_init(&cons_rd);
3281 extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
3283 rb_fd_init(&except);
3284 extract_fd(&except, ex, is_not_socket); // drop only
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;
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;
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);
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;
3317 else {
3318 const struct timeval *dowait = &wait;
3320 fd_set orig_rd;
3321 fd_set orig_wr;
3322 fd_set orig_ex;
3324 FD_ZERO(&orig_rd);
3325 FD_ZERO(&orig_wr);
3326 FD_ZERO(&orig_ex);
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);
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;
3344 Sleep(dowait->tv_sec * 1000 + (dowait->tv_usec + 999) / 1000);
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);
3355 return r;
3358 /* License: Ruby's */
3359 int WSAAPI
3360 rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3361 struct timeval *timeout)
3363 return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
3366 /* License: Ruby's */
3367 static FARPROC
3368 get_wsa_extension_function(SOCKET s, GUID guid)
3370 DWORD dmy;
3371 FARPROC ptr = NULL;
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;
3380 #undef accept
3382 /* License: Artistic or GPL */
3383 int WSAAPI
3384 rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
3386 SOCKET r;
3387 int fd;
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);
3399 else {
3400 errno = map_errno(WSAGetLastError());
3401 fd = -1;
3404 return fd;
3407 #undef bind
3409 /* License: Artistic or GPL */
3410 int WSAAPI
3411 rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
3413 int r;
3415 RUBY_CRITICAL {
3416 r = bind(TO_SOCKET(s), addr, addrlen);
3417 if (r == SOCKET_ERROR)
3418 errno = map_errno(WSAGetLastError());
3420 return r;
3423 #undef connect
3425 /* License: Artistic or GPL */
3426 int WSAAPI
3427 rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
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;
3440 return r;
3444 #undef getpeername
3446 /* License: Artistic or GPL */
3447 int WSAAPI
3448 rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
3450 int r;
3451 RUBY_CRITICAL {
3452 r = getpeername(TO_SOCKET(s), addr, addrlen);
3453 if (r == SOCKET_ERROR)
3454 errno = map_errno(WSAGetLastError());
3456 return r;
3459 #undef getsockname
3461 /* License: Artistic or GPL */
3462 int WSAAPI
3463 rb_w32_getsockname(int fd, struct sockaddr *addr, int *addrlen)
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;
3483 errno = map_errno(wsaerror);
3486 return r;
3489 #undef getsockopt
3491 /* License: Artistic or GPL */
3492 int WSAAPI
3493 rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
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());
3501 return r;
3504 #undef ioctlsocket
3506 /* License: Artistic or GPL */
3507 int WSAAPI
3508 rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
3510 int r;
3511 RUBY_CRITICAL {
3512 r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3513 if (r == SOCKET_ERROR)
3514 errno = map_errno(WSAGetLastError());
3516 return r;
3519 #undef listen
3521 /* License: Artistic or GPL */
3522 int WSAAPI
3523 rb_w32_listen(int s, int backlog)
3525 int r;
3526 RUBY_CRITICAL {
3527 r = listen(TO_SOCKET(s), backlog);
3528 if (r == SOCKET_ERROR)
3529 errno = map_errno(WSAGetLastError());
3531 return r;
3534 #undef recv
3535 #undef recvfrom
3536 #undef send
3537 #undef sendto
3539 /* License: Ruby's */
3540 static int
3541 finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
3543 DWORD flg;
3544 int err;
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);
3554 if (result) {
3555 result = 0;
3556 *len = size;
3557 break;
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;
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;
3579 else {
3580 if (err == WSAECONNABORTED && !input)
3581 errno = EPIPE;
3582 else
3583 errno = map_errno(err);
3584 *len = -1;
3586 CloseHandle(wol->hEvent);
3588 return result;
3591 /* License: Artistic or GPL */
3592 static int
3593 overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
3594 struct sockaddr *addr, int *addrlen)
3596 int r;
3597 int ret;
3598 int mode = 0;
3599 DWORD flg;
3600 WSAOVERLAPPED wol;
3601 WSABUF wbuf;
3602 SOCKET s;
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());
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);
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);
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);
3656 finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3657 r = (int)rlen;
3660 return r;
3663 /* License: Ruby's */
3664 int WSAAPI
3665 rb_w32_recv(int fd, char *buf, int len, int flags)
3667 return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
3670 /* License: Ruby's */
3671 int WSAAPI
3672 rb_w32_recvfrom(int fd, char *buf, int len, int flags,
3673 struct sockaddr *from, int *fromlen)
3675 return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
3678 /* License: Ruby's */
3679 int WSAAPI
3680 rb_w32_send(int fd, const char *buf, int len, int flags)
3682 return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
3685 /* License: Ruby's */
3686 int WSAAPI
3687 rb_w32_sendto(int fd, const char *buf, int len, int flags,
3688 const struct sockaddr *to, int tolen)
3690 return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
3691 (struct sockaddr *)to, &tolen);
3694 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3695 /* License: Ruby's */
3696 typedef 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
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; \
3724 (wsamsg)->Control.buf = (msg)->msg_control; \
3725 (wsamsg)->Control.len = (msg)->msg_controllen; \
3726 (wsamsg)->dwFlags = (msg)->msg_flags; \
3727 } while (0)
3729 /* License: Ruby's */
3731 recvmsg(int fd, struct msghdr *msg, int flags)
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;
3741 s = TO_SOCKET(fd);
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;
3750 msghdr_to_wsamsg(msg, &wsamsg);
3751 wsamsg.dwFlags |= flags;
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;
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);
3771 ret = finish_overlapped_socket(TRUE, s, &wol, ret, &len, size);
3773 if (ret == SOCKET_ERROR)
3774 return -1;
3776 /* WSAMSG to msghdr */
3777 msg->msg_name = wsamsg.name;
3778 msg->msg_namelen = wsamsg.namelen;
3779 msg->msg_flags = wsamsg.dwFlags;
3781 return len;
3784 /* License: Ruby's */
3786 sendmsg(int fd, const struct msghdr *msg, int flags)
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;
3796 s = TO_SOCKET(fd);
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;
3805 msghdr_to_wsamsg(msg, &wsamsg);
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;
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);
3825 finish_overlapped_socket(FALSE, s, &wol, ret, &len, size);
3828 return len;
3831 #undef setsockopt
3833 /* License: Artistic or GPL */
3834 int WSAAPI
3835 rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
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());
3843 return r;
3846 #undef shutdown
3848 /* License: Artistic or GPL */
3849 int WSAAPI
3850 rb_w32_shutdown(int s, int how)
3852 int r;
3853 RUBY_CRITICAL {
3854 r = shutdown(TO_SOCKET(s), how);
3855 if (r == SOCKET_ERROR)
3856 errno = map_errno(WSAGetLastError());
3858 return r;
3861 /* License: Ruby's */
3862 static SOCKET
3863 open_ifs_socket(int af, int type, int protocol)
3865 unsigned long proto_buffers_len = 0;
3866 int error_code;
3867 SOCKET out = INVALID_SOCKET;
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;
3875 proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3876 if (!proto_buffers) {
3877 WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3878 return INVALID_SOCKET;
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;
3891 if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3892 continue;
3894 out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
3895 WSA_FLAG_OVERLAPPED);
3896 break;
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);
3904 free(proto_buffers);
3908 return out;
3911 #undef socket
3913 /* License: Artistic or GPL */
3914 int WSAAPI
3915 rb_w32_socket(int af, int type, int protocol)
3917 SOCKET s;
3918 int fd;
3920 RUBY_CRITICAL {
3921 s = open_ifs_socket(af, type, protocol);
3922 if (s == INVALID_SOCKET) {
3923 errno = map_errno(WSAGetLastError());
3924 fd = -1;
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);
3934 return fd;
3937 #undef gethostbyaddr
3939 /* License: Artistic or GPL */
3940 struct hostent * WSAAPI
3941 rb_w32_gethostbyaddr(const char *addr, int len, int type)
3943 struct hostent *r;
3944 RUBY_CRITICAL {
3945 r = gethostbyaddr(addr, len, type);
3946 if (r == NULL)
3947 errno = map_errno(WSAGetLastError());
3949 return r;
3952 #undef gethostbyname
3954 /* License: Artistic or GPL */
3955 struct hostent * WSAAPI
3956 rb_w32_gethostbyname(const char *name)
3958 struct hostent *r;
3959 RUBY_CRITICAL {
3960 r = gethostbyname(name);
3961 if (r == NULL)
3962 errno = map_errno(WSAGetLastError());
3964 return r;
3967 #undef gethostname
3969 /* License: Artistic or GPL */
3970 int WSAAPI
3971 rb_w32_gethostname(char *name, int len)
3973 int r;
3974 RUBY_CRITICAL {
3975 r = gethostname(name, len);
3976 if (r == SOCKET_ERROR)
3977 errno = map_errno(WSAGetLastError());
3979 return r;
3982 #undef getprotobyname
3984 /* License: Artistic or GPL */
3985 struct protoent * WSAAPI
3986 rb_w32_getprotobyname(const char *name)
3988 struct protoent *r;
3989 RUBY_CRITICAL {
3990 r = getprotobyname(name);
3991 if (r == NULL)
3992 errno = map_errno(WSAGetLastError());
3994 return r;
3997 #undef getprotobynumber
3999 /* License: Artistic or GPL */
4000 struct protoent * WSAAPI
4001 rb_w32_getprotobynumber(int num)
4003 struct protoent *r;
4004 RUBY_CRITICAL {
4005 r = getprotobynumber(num);
4006 if (r == NULL)
4007 errno = map_errno(WSAGetLastError());
4009 return r;
4012 #undef getservbyname
4014 /* License: Artistic or GPL */
4015 struct servent * WSAAPI
4016 rb_w32_getservbyname(const char *name, const char *proto)
4018 struct servent *r;
4019 RUBY_CRITICAL {
4020 r = getservbyname(name, proto);
4021 if (r == NULL)
4022 errno = map_errno(WSAGetLastError());
4024 return r;
4027 #undef getservbyport
4029 /* License: Artistic or GPL */
4030 struct servent * WSAAPI
4031 rb_w32_getservbyport(int port, const char *proto)
4033 struct servent *r;
4034 RUBY_CRITICAL {
4035 r = getservbyport(port, proto);
4036 if (r == NULL)
4037 errno = map_errno(WSAGetLastError());
4039 return r;
4042 #ifdef HAVE_AFUNIX_H
4044 /* License: Ruby's */
4045 static size_t
4046 socketpair_unix_path(struct sockaddr_un *sock_un)
4048 SOCKET listener;
4049 WCHAR wpath[sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path)] = L"";
4051 /* AF_UNIX/SOCK_STREAM became available in Windows 10
4052 * See https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows
4054 listener = socket(AF_UNIX, SOCK_STREAM, 0);
4055 if (listener == INVALID_SOCKET)
4056 return 0;
4058 memset(sock_un, 0, sizeof(*sock_un));
4059 sock_un->sun_family = AF_UNIX;
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
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.
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);
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;
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());
4101 /* Convert to UTF16 for DeleteFileW */
4102 MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
4104 if (bind(listener, (struct sockaddr *)sock_un, sizeof(*sock_un)) != SOCKET_ERROR)
4105 break;
4107 closesocket(listener);
4108 DeleteFileW(wpath);
4109 return sizeof(*sock_un);
4111 #endif
4113 /* License: Ruby's */
4114 static int
4115 socketpair_internal(int af, int type, int protocol, SOCKET *sv)
4117 SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
4118 struct sockaddr_in sock_in4;
4120 #ifdef INET6
4121 struct sockaddr_in6 sock_in6;
4122 #endif
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
4129 struct sockaddr *addr;
4130 int ret = -1;
4131 int len;
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;
4166 if (type != SOCK_STREAM) {
4167 errno = EPROTOTYPE;
4168 return -1;
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);
4185 w = open_ifs_socket(af, type, protocol);
4186 if (w == INVALID_SOCKET)
4187 break;
4188 if (connect(w, addr, len) < 0)
4189 break;
4191 r = accept(svr, addr, &len);
4192 if (r == INVALID_SOCKET)
4193 break;
4194 SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
4196 ret = 0;
4197 } while (0);
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);
4206 else {
4207 sv[0] = r;
4208 sv[1] = w;
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
4218 return ret;
4221 /* License: Ruby's */
4223 socketpair(int af, int type, int protocol, int *sv)
4225 SOCKET pair[2];
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;
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;
4241 socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
4242 socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
4244 return 0;
4247 #if !defined(_MSC_VER) || _MSC_VER >= 1400
4248 /* License: Ruby's */
4249 static void
4250 str2guid(const char *str, GUID *guid)
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;
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
4284 typedef DWORD (WINAPI *cigl_t)(const GUID *, NET_LUID *);
4285 typedef DWORD (WINAPI *cilnA_t)(const NET_LUID *, char *, size_t);
4286 static cigl_t pConvertInterfaceGuidToLuid = (cigl_t)-1;
4287 static cilnA_t pConvertInterfaceLuidToNameA = (cilnA_t)-1;
4290 getifaddrs(struct ifaddrs **ifap)
4292 ULONG size = 0;
4293 ULONG ret;
4294 IP_ADAPTER_ADDRESSES *root, *addr;
4295 struct ifaddrs *prev;
4297 ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
4298 if (ret != ERROR_BUFFER_OVERFLOW) {
4299 errno = map_errno(ret);
4300 return -1;
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;
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);
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;
4325 if (prev)
4326 prev->ifa_next = ifa;
4327 else
4328 *ifap = ifa;
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);
4336 else {
4337 ifa->ifa_name = ruby_strdup(addr->AdapterName);
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;
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;
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;
4360 ifa->ifa_addr = ruby_xmalloc(cur->Address.iSockaddrLength);
4361 memcpy(ifa->ifa_addr, cur->Address.lpSockaddr,
4362 cur->Address.iSockaddrLength);
4363 added = 1;
4368 prev = ifa;
4371 ruby_xfree(root);
4372 return 0;
4375 /* License: Ruby's */
4376 void
4377 freeifaddrs(struct ifaddrs *ifp)
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;
4387 #endif
4389 #if 0 // Have never been used
4391 // Networking stubs
4394 void endhostent(void) {}
4395 void endnetent(void) {}
4396 void endprotoent(void) {}
4397 void endservent(void) {}
4399 struct netent *getnetent (void) {return (struct netent *) NULL;}
4401 struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
4403 struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
4405 struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
4407 struct servent *getservent (void) {return (struct servent *) NULL;}
4409 void sethostent (int stayopen) {}
4411 void setnetent (int stayopen) {}
4413 void setprotoent (int stayopen) {}
4415 void setservent (int stayopen) {}
4416 #endif
4418 int rb_w32_set_nonblock2(int fd, int nonblock);
4420 /* License: Ruby's */
4421 static int
4422 setfl(SOCKET sock, int arg)
4424 int ret;
4425 int af = 0;
4426 int flag = 0;
4427 u_long ioctlArg;
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;
4436 else {
4437 flag &= ~O_NONBLOCK;
4438 ioctlArg = 0;
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());
4448 return ret;
4451 /* License: Ruby's */
4452 static int
4453 dupfd(HANDLE hDup, int flags, int minfd)
4455 int save_errno;
4456 int ret;
4457 int fds[32];
4458 int filled = 0;
4460 do {
4461 ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
4462 if (ret == -1) {
4463 goto close_fds_and_return;
4465 if (ret >= minfd) {
4466 goto close_fds_and_return;
4468 fds[filled++] = ret;
4469 } while (filled < (int)numberof(fds));
4471 ret = dupfd(hDup, flags, minfd);
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);
4480 errno = save_errno;
4482 return ret;
4485 /* License: Ruby's */
4487 fcntl(int fd, int cmd, ...)
4489 va_list va;
4490 int arg;
4491 DWORD flag;
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);
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;
4512 va_start(va, cmd);
4513 arg = va_arg(va, int);
4514 va_end(va);
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;
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;
4531 return (flag & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
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;
4544 if (arg & FD_CLOEXEC)
4545 _osfile(fd) |= FNOINHERIT;
4546 else
4547 _osfile(fd) &= ~FNOINHERIT;
4548 return 0;
4550 default:
4551 errno = EINVAL;
4552 return -1;
4556 /* License: Ruby's */
4558 rb_w32_set_nonblock2(int fd, int nonblock)
4560 SOCKET sock = TO_SOCKET(fd);
4561 if (is_socket(sock)) {
4562 return setfl(sock, nonblock ? O_NONBLOCK : 0);
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;
4570 if (nonblock) {
4571 state |= PIPE_NOWAIT;
4573 else {
4574 state &= ~PIPE_NOWAIT;
4576 if (!SetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL)) {
4577 errno = map_errno(GetLastError());
4578 return -1;
4580 return 0;
4582 else {
4583 errno = EBADF;
4584 return -1;
4589 rb_w32_set_nonblock(int fd)
4591 return rb_w32_set_nonblock2(fd, TRUE);
4594 #ifndef WNOHANG
4595 #define WNOHANG -1
4596 #endif
4598 /* License: Ruby's */
4599 static rb_pid_t
4600 poll_child_status(struct ChildRecord *child, int *stat_loc)
4602 DWORD exitcode;
4603 DWORD err;
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;
4619 error_exit:
4620 CloseChildHandle(child);
4621 return -1;
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;
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},
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;
4663 // if unknown status, assume SEGV
4664 if (i >= (int)numberof(table))
4665 *stat_loc |= SIGSEGV;
4668 return pid;
4670 return 0;
4673 /* License: Artistic or GPL */
4674 rb_pid_t
4675 waitpid(rb_pid_t pid, int *stat_loc, int options)
4677 DWORD timeout;
4679 /* Artistic or GPL part start */
4680 if (options == WNOHANG) {
4681 timeout = 0;
4683 else {
4684 timeout = INFINITE;
4686 /* Artistic or GPL part end */
4688 if (pid == -1) {
4689 int count = 0;
4690 int ret;
4691 HANDLE events[MAXCHILDNUM];
4692 struct ChildRecord* cause;
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;
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;
4709 if (ret > count) {
4710 errno = map_errno(GetLastError());
4711 return -1;
4714 cause = FindChildSlotByHandle(events[ret]);
4715 if (!cause) {
4716 errno = ECHILD;
4717 return -1;
4719 return poll_child_status(cause, stat_loc);
4721 else {
4722 struct ChildRecord* child = FindChildSlot(pid);
4723 int retried = 0;
4724 if (!child) {
4725 errno = ECHILD;
4726 return -1;
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;
4739 ++retried;
4742 if (pid == -1 && retried) pid = 0;
4745 return pid;
4748 #include <sys/timeb.h>
4750 static int have_precisetime = -1;
4752 static void
4753 get_systemtime(FILETIME *ft)
4755 typedef void (WINAPI *get_time_func)(FILETIME *ft);
4756 static get_time_func func = (get_time_func)-1;
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;
4765 else
4766 have_precisetime = 1;
4768 if (!ft) return;
4769 func(ft);
4772 /* License: Ruby's */
4773 /* split FILETIME value into UNIX time and sub-seconds in NT ticks */
4774 static time_t
4775 filetime_split(const FILETIME* ft, long *subsec)
4777 ULARGE_INTEGER tmp;
4778 unsigned LONG_LONG lt;
4779 const unsigned LONG_LONG subsec_unit = (unsigned LONG_LONG)10 * 1000 * 1000;
4781 tmp.LowPart = ft->dwLowDateTime;
4782 tmp.HighPart = ft->dwHighDateTime;
4783 lt = tmp.QuadPart;
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;
4791 *subsec = (long)(lt % subsec_unit);
4792 return (time_t)(lt / subsec_unit);
4795 /* License: Ruby's */
4796 int __cdecl
4797 gettimeofday(struct timeval *tv, struct timezone *tz)
4799 FILETIME ft;
4800 long subsec;
4802 get_systemtime(&ft);
4803 tv->tv_sec = filetime_split(&ft, &subsec);
4804 tv->tv_usec = subsec / 10;
4806 return 0;
4809 /* License: Ruby's */
4811 clock_gettime(clockid_t clock_id, struct timespec *sp)
4813 switch (clock_id) {
4814 case CLOCK_REALTIME:
4816 FILETIME ft;
4817 long subsec;
4819 get_systemtime(&ft);
4820 sp->tv_sec = filetime_split(&ft, &subsec);
4821 sp->tv_nsec = subsec * 100;
4822 return 0;
4824 case CLOCK_MONOTONIC:
4826 LARGE_INTEGER freq;
4827 LARGE_INTEGER count;
4828 if (!QueryPerformanceFrequency(&freq)) {
4829 errno = map_errno(GetLastError());
4830 return -1;
4832 if (!QueryPerformanceCounter(&count)) {
4833 errno = map_errno(GetLastError());
4834 return -1;
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;
4843 default:
4844 errno = EINVAL;
4845 return -1;
4849 /* License: Ruby's */
4851 clock_getres(clockid_t clock_id, struct timespec *sp)
4853 switch (clock_id) {
4854 case CLOCK_REALTIME:
4856 sp->tv_sec = 0;
4857 sp->tv_nsec = 1000;
4858 return 0;
4860 case CLOCK_MONOTONIC:
4862 LARGE_INTEGER freq;
4863 if (!QueryPerformanceFrequency(&freq)) {
4864 errno = map_errno(GetLastError());
4865 return -1;
4867 sp->tv_sec = 0;
4868 sp->tv_nsec = (long)(1000000000.0 / freq.QuadPart);
4869 return 0;
4871 default:
4872 errno = EINVAL;
4873 return -1;
4877 /* License: Ruby's */
4878 static char *
4879 w32_getcwd(char *buffer, int size, UINT cp, void *alloc(int, void *), void *arg)
4881 WCHAR *p;
4882 int wlen, len;
4884 len = GetCurrentDirectoryW(0, NULL);
4885 if (!len) {
4886 errno = map_errno(GetLastError());
4887 return NULL;
4890 if (buffer && size < len) {
4891 errno = ERANGE;
4892 return NULL;
4895 p = ALLOCA_N(WCHAR, len);
4896 if (!GetCurrentDirectoryW(len, p)) {
4897 errno = map_errno(GetLastError());
4898 return NULL;
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;
4909 else {
4910 buffer = (*alloc)(len, arg);
4911 if (!buffer) {
4912 errno = ENOMEM;
4913 return NULL;
4916 WideCharToMultiByte(cp, 0, p, wlen, buffer, len, NULL, NULL);
4918 return buffer;
4921 /* License: Ruby's */
4922 static void *
4923 getcwd_alloc(int size, void *dummy)
4925 return malloc(size);
4928 /* License: Ruby's */
4929 char *
4930 rb_w32_getcwd(char *buffer, int size)
4932 return w32_getcwd(buffer, size, filecp(), getcwd_alloc, NULL);
4935 /* License: Ruby's */
4936 char *
4937 rb_w32_ugetcwd(char *buffer, int size)
4939 return w32_getcwd(buffer, size, CP_UTF8, getcwd_alloc, NULL);
4942 /* License: Ruby's */
4943 static void *
4944 getcwd_value(int size, void *arg)
4946 VALUE str = *(VALUE *)arg = rb_utf8_str_new(0, size - 1);
4947 return RSTRING_PTR(str);
4950 /* License: Ruby's */
4951 VALUE
4952 rb_dir_getwd_ospath(void)
4954 VALUE cwd = Qnil;
4955 w32_getcwd(NULL, 0, CP_UTF8, getcwd_value, &cwd);
4956 return cwd;
4959 /* License: Artistic or GPL */
4961 chown(const char *path, int owner, int group)
4963 return 0;
4966 /* License: Artistic or GPL */
4968 rb_w32_uchown(const char *path, int owner, int group)
4970 return 0;
4974 lchown(const char *path, int owner, int group)
4976 return 0;
4980 rb_w32_ulchown(const char *path, int owner, int group)
4982 return 0;
4985 /* License: Ruby's */
4987 kill(rb_pid_t pid, int sig)
4989 int ret = 0;
4990 DWORD err;
4992 if (pid < 0 || (pid == 0 && sig != SIGINT)) {
4993 errno = EINVAL;
4994 return -1;
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;
5003 return ret;
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;
5015 else {
5016 errno = EPERM;
5018 ret = -1;
5020 else {
5021 CloseHandle(hProc);
5024 break;
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;
5034 if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
5035 if ((err = GetLastError()) == 0)
5036 errno = EPERM;
5037 else
5038 errno = map_errno(GetLastError());
5039 ret = -1;
5042 break;
5044 case SIGKILL:
5045 RUBY_CRITICAL {
5046 HANDLE hProc;
5047 struct ChildRecord* child = FindChildSlot(pid);
5048 if (child) {
5049 hProc = child->hProcess;
5051 else {
5052 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
5054 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
5055 if (GetLastError() == ERROR_INVALID_PARAMETER) {
5056 errno = ESRCH;
5058 else {
5059 errno = EPERM;
5061 ret = -1;
5063 else {
5064 DWORD status;
5065 if (!GetExitCodeProcess(hProc, &status)) {
5066 errno = map_errno(GetLastError());
5067 ret = -1;
5069 else if (status == STILL_ACTIVE) {
5070 if (!TerminateProcess(hProc, 0)) {
5071 errno = EPERM;
5072 ret = -1;
5075 else {
5076 errno = ESRCH;
5077 ret = -1;
5079 if (!child) {
5080 CloseHandle(hProc);
5084 break;
5086 default:
5087 errno = EINVAL;
5088 ret = -1;
5089 break;
5092 return ret;
5095 /* License: Ruby's */
5096 static int
5097 wlink(const WCHAR *from, const WCHAR *to)
5099 if (!CreateHardLinkW(to, from, NULL)) {
5100 errno = map_errno(GetLastError());
5101 return -1;
5104 return 0;
5107 /* License: Ruby's */
5109 rb_w32_ulink(const char *from, const char *to)
5111 WCHAR *wfrom;
5112 WCHAR *wto;
5113 int ret;
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;
5121 ret = wlink(wfrom, wto);
5122 free(wto);
5123 free(wfrom);
5124 return ret;
5127 /* License: Ruby's */
5129 link(const char *from, const char *to)
5131 WCHAR *wfrom;
5132 WCHAR *wto;
5133 int ret;
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;
5141 ret = wlink(wfrom, wto);
5142 free(wto);
5143 free(wfrom);
5144 return ret;
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
5158 /* License: Ruby's */
5159 static int
5160 reparse_symlink(const WCHAR *path, rb_w32_reparse_buffer_t *rp, size_t size)
5162 HANDLE f;
5163 DWORD ret;
5164 int e = 0;
5166 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5167 if (f == INVALID_HANDLE_VALUE) {
5168 return GetLastError();
5171 if (!DeviceIoControl(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
5172 rp, size, &ret, NULL)) {
5173 e = GetLastError();
5175 else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
5176 rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
5177 e = ERROR_INVALID_PARAMETER;
5179 CloseHandle(f);
5180 return e;
5183 /* License: Ruby's */
5185 rb_w32_reparse_symlink_p(const WCHAR *path)
5187 VALUE wtmp = 0;
5188 rb_w32_reparse_buffer_t rbuf, *rp = &rbuf;
5189 WCHAR *wbuf;
5190 DWORD len;
5191 int e;
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);
5200 switch (e) {
5201 case 0:
5202 case ERROR_MORE_DATA:
5203 return TRUE;
5205 return FALSE;
5208 /* License: Ruby's */
5210 rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
5211 size_t bufsize, WCHAR **result, DWORD *len)
5213 int e = reparse_symlink(path, rp, bufsize);
5214 DWORD ret = 0;
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);
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;
5237 else {
5238 return -1;
5240 *result = name;
5241 if (e) {
5242 if ((char *)name + ret + sizeof(WCHAR) > (char *)rp + bufsize)
5243 return e;
5244 /* SubstituteName is not used */
5246 ((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0';
5247 translate_wchar(name, L'\\', L'/');
5248 return 0;
5250 else {
5251 return e;
5255 /* License: Ruby's */
5256 static ssize_t
5257 w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize)
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;
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);
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;
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;
5288 return ret;
5291 /* License: Ruby's */
5292 ssize_t
5293 rb_w32_ureadlink(const char *path, char *buf, size_t bufsize)
5295 return w32_readlink(CP_UTF8, path, buf, bufsize);
5298 /* License: Ruby's */
5299 ssize_t
5300 readlink(const char *path, char *buf, size_t bufsize)
5302 return w32_readlink(filecp(), path, buf, bufsize);
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
5312 /* License: Ruby's */
5313 static int
5314 w32_symlink(UINT cp, const char *src, const char *link)
5316 int atts, len1, len2;
5317 VALUE buf;
5318 WCHAR *wsrc, *wlink;
5319 DWORD flag = 0;
5320 BOOLEAN ret;
5321 int e;
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;
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);
5333 if (!create_symbolic_link) {
5334 errno = ENOSYS;
5335 return -1;
5338 if (!*link) {
5339 errno = ENOENT;
5340 return -1;
5342 if (!*src) {
5343 errno = EINVAL;
5344 return -1;
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'\\');
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();
5366 ALLOCV_END(buf);
5368 if (!ret) {
5369 errno = map_errno(e);
5370 return -1;
5372 return 0;
5375 /* License: Ruby's */
5377 rb_w32_usymlink(const char *src, const char *link)
5379 return w32_symlink(CP_UTF8, src, link);
5382 /* License: Ruby's */
5384 symlink(const char *src, const char *link)
5386 return w32_symlink(filecp(), src, link);
5389 /* License: Ruby's */
5390 rb_pid_t
5391 wait(int *status)
5393 return waitpid(-1, status, 0);
5396 /* License: Ruby's */
5397 static char *
5398 w32_getenv(const char *name, UINT cp)
5400 WCHAR *wenvarea, *wenv;
5401 int len = strlen(name);
5402 char *env, *found = NULL;
5403 int wlen;
5405 if (len == 0) return NULL;
5407 if (!NTLoginName) {
5408 /* initialized in init_env, uenvarea_mutex should have been
5409 * initialized before it */
5410 return getenv(name);
5413 thread_exclusive(uenvarea) {
5414 if (uenvarea) {
5415 free(uenvarea);
5416 uenvarea = NULL;
5418 wenvarea = GetEnvironmentStringsW();
5419 if (!wenvarea) {
5420 map_errno(GetLastError());
5421 continue;
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;
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;
5438 return found;
5441 /* License: Ruby's */
5442 char *
5443 rb_w32_ugetenv(const char *name)
5445 return w32_getenv(name, CP_UTF8);
5448 /* License: Ruby's */
5449 char *
5450 rb_w32_getenv(const char *name)
5452 return w32_getenv(name, CP_ACP);
5455 /* License: Ruby's */
5456 static DWORD
5457 get_attr_vsn(const WCHAR *path, DWORD *atts, DWORD *vsn)
5459 BY_HANDLE_FILE_INFORMATION st = {0};
5460 DWORD e = 0;
5461 HANDLE h = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5463 if (h == INVALID_HANDLE_VALUE) {
5464 e = GetLastError();
5465 ASSUME(e);
5466 return e;
5468 if (!GetFileInformationByHandle(h, &st)) {
5469 e = GetLastError();
5470 ASSUME(e);
5472 else {
5473 *atts = st.dwFileAttributes;
5474 *vsn = st.dwVolumeSerialNumber;
5476 CloseHandle(h);
5477 return e;
5480 /* License: Artistic or GPL */
5481 static int
5482 wrename(const WCHAR *oldpath, const WCHAR *newpath)
5484 int res = 0;
5485 DWORD oldatts = 0, newatts = (DWORD)-1;
5486 DWORD oldvsn = 0, newvsn = 0, e;
5488 e = get_attr_vsn(oldpath, &oldatts, &oldvsn);
5489 if (e) {
5490 errno = map_errno(e);
5491 return -1;
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;
5502 CloseHandle(fh);
5504 get_attr_vsn(newpath, &newatts, &newvsn);
5506 RUBY_CRITICAL {
5507 if (newatts != (DWORD)-1 && newatts & FILE_ATTRIBUTE_READONLY)
5508 SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
5510 if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
5511 res = -1;
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);
5521 else
5522 SetFileAttributesW(newpath, oldatts);
5525 return res;
5528 /* License: Ruby's */
5530 rb_w32_urename(const char *from, const char *to)
5532 WCHAR *wfrom;
5533 WCHAR *wto;
5534 int ret = -1;
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;
5542 ret = wrename(wfrom, wto);
5543 free(wto);
5544 free(wfrom);
5545 return ret;
5548 /* License: Ruby's */
5550 rb_w32_rename(const char *from, const char *to)
5552 WCHAR *wfrom;
5553 WCHAR *wto;
5554 int ret = -1;
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;
5562 ret = wrename(wfrom, wto);
5563 free(wto);
5564 free(wfrom);
5565 return ret;
5568 /* License: Ruby's */
5569 static int
5570 isUNCRoot(const WCHAR *path)
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;
5577 for (; *p; p++) {
5578 if (*p == L'\\')
5579 break;
5581 if (p[0] && p[1]) {
5582 for (p++; *p; p++) {
5583 if (*p == L'\\')
5584 break;
5586 if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
5587 return 1;
5590 return 0;
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)
5607 static time_t filetime_to_unixtime(const FILETIME *ft);
5608 static long filetime_to_nsec(const FILETIME *ft);
5609 static WCHAR *name_for_stat(WCHAR *buf, const WCHAR *path);
5610 static DWORD stati128_handle(HANDLE h, struct stati128 *st);
5612 #undef fstat
5613 /* License: Ruby's */
5615 rb_w32_fstat(int fd, struct stat *st)
5617 BY_HANDLE_FILE_INFORMATION info;
5618 int ret = fstat(fd, st);
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);
5627 return ret;
5630 /* License: Ruby's */
5632 rb_w32_fstati128(int fd, struct stati128 *st)
5634 struct stat tmp;
5635 int ret = fstat(fd, &tmp);
5637 if (ret) return ret;
5638 COPY_STAT(tmp, *st, +);
5639 stati128_handle((HANDLE)_get_osfhandle(fd), st);
5640 return ret;
5643 #if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5644 typedef struct {
5645 BYTE Identifier[16];
5646 } FILE_ID_128;
5647 #endif
5649 #if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5650 #define FileIdInfo 0x12
5652 typedef struct {
5653 unsigned LONG_LONG VolumeSerialNumber;
5654 FILE_ID_128 FileId;
5655 } FILE_ID_INFO;
5656 #endif
5658 static DWORD
5659 get_ino(HANDLE h, FILE_ID_INFO *id)
5661 typedef BOOL (WINAPI *gfibhe_t)(HANDLE, int, void *, DWORD);
5662 static gfibhe_t pGetFileInformationByHandleEx = (gfibhe_t)-1;
5664 if (pGetFileInformationByHandleEx == (gfibhe_t)-1)
5665 /* Since Windows Vista and Windows Server 2008 */
5666 pGetFileInformationByHandleEx = (gfibhe_t)get_proc_address("kernel32", "GetFileInformationByHandleEx", NULL);
5668 if (pGetFileInformationByHandleEx) {
5669 if (pGetFileInformationByHandleEx(h, FileIdInfo, id, sizeof(*id)))
5670 return 0;
5671 else
5672 return GetLastError();
5674 return ERROR_INVALID_PARAMETER;
5677 /* License: Ruby's */
5678 static DWORD
5679 stati128_handle(HANDLE h, struct stati128 *st)
5681 BY_HANDLE_FILE_INFORMATION info;
5682 DWORD attr = (DWORD)-1;
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);
5699 else {
5700 st->st_ino = ((__int64)info.nFileIndexHigh << 32) | info.nFileIndexLow;
5701 st->st_inohigh = 0;
5704 return attr;
5707 /* License: Ruby's */
5708 static time_t
5709 filetime_to_unixtime(const FILETIME *ft)
5711 long subsec;
5712 time_t t = filetime_split(ft, &subsec);
5714 if (t < 0) return 0;
5715 return t;
5718 /* License: Ruby's */
5719 static long
5720 filetime_to_nsec(const FILETIME *ft)
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;
5732 /* License: Ruby's */
5733 static unsigned
5734 fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode)
5736 if (attr & FILE_ATTRIBUTE_READONLY) {
5737 mode |= S_IREAD;
5739 else {
5740 mode |= S_IREAD | S_IWRITE | S_IWUSR;
5743 if (mode & S_IFMT) {
5744 /* format is already set */
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. */
5750 else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5751 mode |= S_IFDIR | S_IEXEC;
5753 else {
5754 mode |= S_IFREG;
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;
5768 break;
5770 if (!iswalnum(*end)) break;
5774 mode |= (mode & 0500) >> 3;
5775 mode |= (mode & 0500) >> 6;
5777 return mode;
5780 /* License: Ruby's */
5781 static int
5782 check_valid_dir(const WCHAR *path)
5784 WIN32_FIND_DATAW fd;
5785 HANDLE fh;
5786 WCHAR full[PATH_MAX];
5787 WCHAR *dmy;
5788 WCHAR *p, *q;
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;
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;
5807 if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5808 return 0;
5810 fh = open_dir_handle(path, &fd);
5811 if (fh == INVALID_HANDLE_VALUE)
5812 return -1;
5813 FindClose(fh);
5814 return 0;
5817 /* License: Ruby's */
5818 static int
5819 stat_by_find(const WCHAR *path, struct stati128 *st)
5821 HANDLE h;
5822 WIN32_FIND_DATAW wfd;
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;
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;
5843 /* License: Ruby's */
5844 static int
5845 path_drive(const WCHAR *path)
5847 return (iswalpha(path[0]) && path[1] == L':') ?
5848 towupper(path[0]) - L'A' : _getdrive() - 1;
5851 /* License: Ruby's */
5852 static int
5853 winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
5855 DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
5856 HANDLE f;
5857 WCHAR finalname[PATH_MAX];
5858 int open_error;
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;
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;
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;
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;
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;
5903 else {
5904 mode |= S_IFDIR | S_IEXEC;
5908 CloseHandle(f);
5909 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5910 if (check_valid_dir(path)) return -1;
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);
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;
5927 if (stat_by_find(path, st)) return -1;
5930 st->st_dev = st->st_rdev = path_drive(path);
5932 return 0;
5935 /* License: Ruby's */
5937 rb_w32_stat(const char *path, struct stat *st)
5939 struct stati128 tmp;
5941 if (w32_stati128(path, &tmp, filecp(), FALSE)) return -1;
5942 COPY_STAT(tmp, *st, (_off_t));
5943 return 0;
5946 /* License: Ruby's */
5947 static int
5948 wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat)
5950 WCHAR *buf1;
5951 int ret, size;
5952 VALUE v;
5954 if (!path || !st) {
5955 errno = EFAULT;
5956 return -1;
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);
5966 return ret;
5969 /* License: Ruby's */
5970 static WCHAR *
5971 name_for_stat(WCHAR *buf1, const WCHAR *path)
5973 const WCHAR *p;
5974 WCHAR *s, *end;
5975 int len;
5977 for (p = path, s = buf1; *p; p++, s++) {
5978 if (*p == L'/')
5979 *s = L'\\';
5980 else
5981 *s = *p;
5983 *s = '\0';
5984 len = s - buf1;
5985 if (!len || L'\"' == *(--s)) {
5986 errno = ENOENT;
5987 return NULL;
5989 end = buf1 + len - 1;
5991 if (isUNCRoot(buf1)) {
5992 if (*end == L'.')
5993 *end = L'\0';
5994 else if (*end != L'\\')
5995 lstrcatW(buf1, L"\\");
5997 else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
5998 lstrcatW(buf1, L".");
6000 return buf1;
6003 /* License: Ruby's */
6005 rb_w32_ustati128(const char *path, struct stati128 *st)
6007 return w32_stati128(path, st, CP_UTF8, FALSE);
6010 /* License: Ruby's */
6012 rb_w32_stati128(const char *path, struct stati128 *st)
6014 return w32_stati128(path, st, filecp(), FALSE);
6017 /* License: Ruby's */
6018 static int
6019 w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat)
6021 WCHAR *wpath;
6022 int ret;
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;
6031 /* License: Ruby's */
6033 rb_w32_ulstati128(const char *path, struct stati128 *st)
6035 return w32_stati128(path, st, CP_UTF8, TRUE);
6038 /* License: Ruby's */
6040 rb_w32_lstati128(const char *path, struct stati128 *st)
6042 return w32_stati128(path, st, filecp(), TRUE);
6045 /* License: Ruby's */
6046 rb_off_t
6047 rb_w32_lseek(int fd, rb_off_t ofs, int whence)
6049 SOCKET sock = TO_SOCKET(fd);
6050 if (is_socket(sock) || is_pipe(sock)) {
6051 errno = ESPIPE;
6052 return -1;
6054 return _lseeki64(fd, ofs, whence);
6057 /* License: Ruby's */
6058 static int
6059 w32_access(const char *path, int mode, UINT cp)
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;
6069 return 0;
6072 /* License: Ruby's */
6074 rb_w32_access(const char *path, int mode)
6076 return w32_access(path, mode, filecp());
6079 /* License: Ruby's */
6081 rb_w32_uaccess(const char *path, int mode)
6083 return w32_access(path, mode, CP_UTF8);
6086 /* License: Ruby's */
6087 static int
6088 rb_chsize(HANDLE h, rb_off_t size)
6090 long upos, lpos, usize, lsize;
6091 int ret = -1;
6092 DWORD e;
6094 if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6095 (e = GetLastError())) {
6096 errno = map_errno(e);
6097 return -1;
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);
6105 else if (!SetEndOfFile(h)) {
6106 errno = map_errno(GetLastError());
6108 else {
6109 ret = 0;
6111 SetFilePointer(h, lpos, &upos, SEEK_SET);
6112 return ret;
6115 /* License: Ruby's */
6116 static int
6117 w32_truncate(const char *path, rb_off_t length, UINT cp)
6119 HANDLE h;
6120 int ret;
6121 WCHAR *wpath;
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;
6131 free(wpath);
6132 ret = rb_chsize(h, length);
6133 CloseHandle(h);
6134 return ret;
6137 /* License: Ruby's */
6139 rb_w32_utruncate(const char *path, rb_off_t length)
6141 return w32_truncate(path, length, CP_UTF8);
6144 /* License: Ruby's */
6146 rb_w32_truncate(const char *path, rb_off_t length)
6148 return w32_truncate(path, length, filecp());
6151 /* License: Ruby's */
6153 rb_w32_ftruncate(int fd, rb_off_t length)
6155 HANDLE h;
6157 h = (HANDLE)_get_osfhandle(fd);
6158 if (h == (HANDLE)-1) return -1;
6159 return rb_chsize(h, length);
6162 /* License: Ruby's */
6163 static long
6164 filetime_to_clock(FILETIME *ft)
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;
6173 /* License: Ruby's */
6175 rb_w32_times(struct tms *tmbuf)
6177 FILETIME create, exit, kernel, user;
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;
6185 else {
6186 tmbuf->tms_utime = clock();
6187 tmbuf->tms_stime = 0;
6188 tmbuf->tms_cutime = 0;
6189 tmbuf->tms_cstime = 0;
6191 return 0;
6195 /* License: Ruby's */
6196 #define yield_once() Sleep(0)
6197 #define yield_until(condition) do yield_once(); while (!(condition))
6199 /* License: Ruby's */
6200 struct asynchronous_arg_t {
6201 /* output field */
6202 void* stackaddr;
6203 int errnum;
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;
6212 /* License: Ruby's */
6213 static DWORD WINAPI
6214 call_asynchronous(PVOID argp)
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;
6224 /* License: Ruby's */
6225 uintptr_t
6226 rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
6227 int argc, uintptr_t* argv, uintptr_t intrval)
6229 DWORD val;
6230 BOOL interrupted = FALSE;
6231 HANDLE thr;
6233 RUBY_CRITICAL {
6234 struct asynchronous_arg_t arg;
6236 arg.stackaddr = NULL;
6237 arg.errnum = 0;
6238 arg.func = func;
6239 arg.self = self;
6240 arg.argc = argc;
6241 arg.argv = argv;
6243 thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6245 if (thr) {
6246 yield_until(arg.stackaddr);
6248 if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6249 interrupted = TRUE;
6251 if (TerminateThread(thr, intrval)) {
6252 yield_once();
6256 GetExitCodeThread(thr, &val);
6257 CloseHandle(thr);
6259 if (interrupted) {
6260 /* must release stack of killed thread, why doesn't Windows? */
6261 MEMORY_BASIC_INFORMATION m;
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()));
6268 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6269 Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
6270 m.AllocationBase, GetLastError()));
6272 errno = EINTR;
6274 else {
6275 errno = arg.errnum;
6280 if (!thr) {
6281 rb_fatal("failed to launch waiter thread:%ld", GetLastError());
6284 return val;
6287 /* License: Ruby's */
6288 char **
6289 rb_w32_get_environ(void)
6291 WCHAR *envtop, *env;
6292 char **myenvtop, **myenv;
6293 int num;
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.
6303 * This function returns UTF-8 strings.
6305 envtop = GetEnvironmentStringsW();
6306 for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6307 if (*env != '=') num++;
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;
6315 myenv++;
6318 *myenv = NULL;
6319 FreeEnvironmentStringsW(envtop);
6321 return myenvtop;
6324 /* License: Ruby's */
6325 void
6326 rb_w32_free_environ(char **env)
6328 char **t = env;
6330 while (*t) free(*t++);
6331 free(env);
6334 /* License: Ruby's */
6335 rb_pid_t
6336 rb_w32_getpid(void)
6338 return GetCurrentProcessId();
6342 /* License: Ruby's */
6343 rb_pid_t
6344 rb_w32_getppid(void)
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;
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;
6368 return ppid;
6371 STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
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))
6381 /* License: Ruby's */
6383 rb_w32_dup2(int oldfd, int newfd)
6385 int ret;
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;
6394 /* License: Ruby's */
6396 rb_w32_uopen(const char *file, int oflag, ...)
6398 WCHAR *wfile;
6399 int ret;
6400 int pmode;
6402 va_list arg;
6403 va_start(arg, oflag);
6404 pmode = va_arg(arg, int);
6405 va_end(arg);
6407 if (!(wfile = utf8_to_wstr(file, NULL)))
6408 return -1;
6409 ret = w32_wopen(wfile, oflag, pmode);
6410 free(wfile);
6411 return ret;
6414 /* License: Ruby's */
6415 static int
6416 check_if_wdir(const WCHAR *wfile)
6418 DWORD attr = GetFileAttributesW(wfile);
6419 if (attr == (DWORD)-1L ||
6420 !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6421 check_valid_dir(wfile)) {
6422 return FALSE;
6424 errno = EISDIR;
6425 return TRUE;
6428 /* License: Ruby's */
6430 rb_w32_open(const char *file, int oflag, ...)
6432 WCHAR *wfile;
6433 int ret;
6434 int pmode;
6436 va_list arg;
6437 va_start(arg, oflag);
6438 pmode = va_arg(arg, int);
6439 va_end(arg);
6441 if (!(wfile = filecp_to_wstr(file, NULL)))
6442 return -1;
6443 ret = w32_wopen(wfile, oflag, pmode);
6444 free(wfile);
6445 return ret;
6448 /* License: Ruby's */
6450 rb_w32_wopen(const WCHAR *file, int oflag, ...)
6452 int pmode = 0;
6454 if (oflag & O_CREAT) {
6455 va_list arg;
6456 va_start(arg, oflag);
6457 pmode = va_arg(arg, int);
6458 va_end(arg);
6461 return w32_wopen(file, oflag, pmode);
6464 static int
6465 w32_wopen(const WCHAR *file, int oflag, int pmode)
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;
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;
6490 return fd;
6493 sec.nLength = sizeof(sec);
6494 sec.lpSecurityDescriptor = NULL;
6495 if (oflag & O_NOINHERIT) {
6496 sec.bInheritHandle = FALSE;
6497 flags |= FNOINHERIT;
6499 else {
6500 sec.bInheritHandle = TRUE;
6502 oflag &= ~O_NOINHERIT;
6504 /* always open with binary mode */
6505 oflag &= ~(O_BINARY | O_TEXT);
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;
6521 oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
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;
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;
6551 oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6553 if (oflag & O_TEMPORARY) {
6554 attr |= FILE_FLAG_DELETE_ON_CLOSE;
6555 access |= DELETE;
6557 oflag &= ~O_TEMPORARY;
6559 if (oflag & _O_SHORT_LIVED)
6560 attr |= FILE_ATTRIBUTE_TEMPORARY;
6561 oflag &= ~_O_SHORT_LIVED;
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;
6576 oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6578 if (oflag & ~O_APPEND) {
6579 errno = EINVAL;
6580 return -1;
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);
6589 if (fd == -1) {
6590 errno = EMFILE;
6591 return -1;
6593 RUBY_CRITICAL {
6594 rb_acrt_lowio_lock_fh(fd);
6595 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6596 _set_osflags(fd, 0);
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;
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;
6622 if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6623 flags |= FAPPEND;
6625 _set_osfhnd(fd, (intptr_t)h);
6626 _set_osflags(fd, flags | FOPEN);
6628 rb_acrt_lowio_unlock_fh(fd);
6629 quit:
6633 return fd;
6636 /* License: Ruby's */
6638 rb_w32_fclose(FILE *fp)
6640 int fd = fileno(fp);
6641 SOCKET sock = TO_SOCKET(fd);
6642 int save_errno = errno;
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);
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;
6656 return 0;
6659 /* License: Ruby's */
6661 rb_w32_pipe(int fds[2])
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
6671 char name[sizeof(prefix) + width_of_ids];
6672 SECURITY_ATTRIBUTES sec;
6673 HANDLE hRead, hWrite, h;
6674 int fdRead, fdWrite;
6675 int ret;
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);
6681 sec.nLength = sizeof(sec);
6682 sec.lpSecurityDescriptor = NULL;
6683 sec.bInheritHandle = FALSE;
6685 RUBY_CRITICAL {
6686 hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6687 0, 2, 65536, 65536, 0, &sec);
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;
6698 RUBY_CRITICAL {
6699 hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6700 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6702 if (hWrite == INVALID_HANDLE_VALUE) {
6703 errno = map_errno(GetLastError());
6704 CloseHandle(hRead);
6705 return -1;
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;
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;
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;
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;
6749 fds[0] = fdRead;
6750 fds[1] = fdWrite;
6752 return 0;
6755 /* License: Ruby's */
6756 static int
6757 console_emulator_p(void)
6759 #ifdef _WIN32_WCE
6760 return FALSE;
6761 #else
6762 const void *const func = WriteConsoleW;
6763 HMODULE k;
6764 MEMORY_BASIC_INFORMATION m;
6766 memset(&m, 0, sizeof(m));
6767 if (!VirtualQuery(func, &m, sizeof(m))) {
6768 return FALSE;
6770 k = GetModuleHandle("kernel32.dll");
6771 if (!k) return FALSE;
6772 return (HMODULE)m.AllocationBase != k;
6773 #endif
6776 /* License: Ruby's */
6777 static struct constat *
6778 constat_handle(HANDLE h)
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;
6788 conlist = st_init_numtable();
6789 install_vm_exit_handler();
6791 else if (conlist == conlist_disabled) {
6792 continue;
6794 if (st_lookup(conlist, (st_data_t)h, &data)) {
6795 p = (struct constat *)data;
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;
6807 st_insert(conlist, (st_data_t)h, (st_data_t)p);
6810 return p;
6813 /* License: Ruby's */
6814 static void
6815 constat_reset(HANDLE h)
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;
6827 #define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6828 #define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6830 #define constat_attr_color_reverse(attr) \
6831 ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6832 (((attr) & FOREGROUND_MASK) << 4) | \
6833 (((attr) & BACKGROUND_MASK) >> 4)
6835 /* License: Ruby's */
6836 static WORD
6837 constat_attr(int count, const int *seq, WORD attr, WORD default_attr, int *reverse)
6839 int rev = *reverse;
6840 WORD bold;
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);
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;
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;
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;
6925 attr |= bold;
6926 if (rev) attr = constat_attr_color_reverse(attr);
6927 *reverse = rev;
6928 return attr;
6931 /* License: Ruby's */
6932 static void
6933 constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
6935 DWORD written;
6937 FillConsoleOutputAttribute(handle, attr, len, pos, &written);
6938 FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
6941 /* License: Ruby's */
6942 static void
6943 constat_apply(HANDLE handle, struct constat *s, WCHAR w)
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;
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;
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;
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);
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);
7085 break;
7089 /* get rid of console writing bug; assume WriteConsole and WriteFile
7090 * on a console share the same limit. */
7091 static const long MAXSIZE_CONSOLE_WRITING = 31366;
7093 /* License: Ruby's */
7094 static long
7095 constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
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 */
7106 s->vt100.state = constat_init;
7107 if (len > 0 && *ptr != L'[') continue;
7108 s->vt100.state = constat_esc;
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;
7116 rest = *lenp - len - 1;
7117 if (rest > 0) --rest;
7118 s->vt100.state = constat_seq;
7119 s->vt100.seq[0] = 0;
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');
7128 else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
7129 s->vt100.seq[s->vt100.state++] = -1;
7131 else {
7132 do {
7133 if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
7134 s->vt100.seq[s->vt100.state] = 0;
7136 else {
7137 s->vt100.state = (int)numberof(s->vt100.seq);
7139 } while (0);
7140 if (wc != L';') {
7141 constat_apply(h, s, wc);
7142 s->vt100.state = constat_init;
7145 rest = 0;
7147 else if ((rest = *lenp - len) < MAXSIZE_CONSOLE_WRITING) {
7148 continue;
7150 *ptrp = ptr;
7151 *lenp = len;
7152 return rest;
7154 len = *lenp;
7155 *ptrp = ptr;
7156 *lenp = 0;
7157 return len;
7161 /* License: Ruby's */
7163 rb_w32_close(int fd)
7165 SOCKET sock = TO_SOCKET(fd);
7166 int save_errno = errno;
7168 if (!is_socket(sock)) {
7169 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7170 constat_delete((HANDLE)sock);
7171 return _close(fd);
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;
7181 return 0;
7184 static int
7185 setup_overlapped(OVERLAPPED *ol, int fd, int iswrite)
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.
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;
7206 ol->Offset = low;
7207 ol->OffsetHigh = high;
7209 ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7210 if (!ol->hEvent) {
7211 errno = map_errno(GetLastError());
7212 return -1;
7214 return 0;
7217 static void
7218 finish_overlapped(OVERLAPPED *ol, int fd, DWORD size)
7220 CloseHandle(ol->hEvent);
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);
7231 #undef read
7232 /* License: Ruby's */
7233 ssize_t
7234 rb_w32_read(int fd, void *buf, size_t size)
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;
7247 if (is_socket(sock))
7248 return rb_w32_recv(fd, buf, size, 0);
7250 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7251 if (_get_osfhandle(fd) == -1) {
7252 return -1;
7255 if (_osfile(fd) & FTEXT) {
7256 return _read(fd, buf, size);
7259 rb_acrt_lowio_lock_fh(fd);
7261 if (!size || _osfile(fd) & FEOFLAG) {
7262 _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7263 rb_acrt_lowio_unlock_fh(fd);
7264 return 0;
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;
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;
7285 else
7286 len = size;
7287 size -= len;
7289 if (setup_overlapped(&ol, fd, FALSE)) {
7290 rb_acrt_lowio_unlock_fh(fd);
7291 return -1;
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;
7301 else {
7302 errno = map_errno(err);
7304 rb_acrt_lowio_unlock_fh(fd);
7305 return -1;
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;
7315 else
7316 errno = map_errno(err);
7318 rb_acrt_lowio_unlock_fh(fd);
7319 return -1;
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;
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;
7341 CloseHandle(ol.hEvent);
7342 CancelIo((HANDLE)_osfhnd(fd));
7343 rb_acrt_lowio_unlock_fh(fd);
7344 return ret;
7347 else {
7348 err = GetLastError();
7349 errno = map_errno(err);
7352 finish_overlapped(&ol, fd, read);
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;
7361 if (read == 0)
7362 _set_osflags(fd, _osfile(fd) | FEOFLAG);
7365 rb_acrt_lowio_unlock_fh(fd);
7367 return ret;
7370 #undef write
7371 /* License: Ruby's */
7372 ssize_t
7373 rb_w32_write(int fd, const void *buf, size_t size)
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;
7383 if (is_socket(sock))
7384 return rb_w32_send(fd, buf, size, 0);
7386 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7387 if (_get_osfhandle(fd) == -1) {
7388 return -1;
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());
7397 return w;
7400 rb_acrt_lowio_lock_fh(fd);
7402 if (!size || _osfile(fd) & FEOFLAG) {
7403 rb_acrt_lowio_unlock_fh(fd);
7404 return 0;
7407 ret = 0;
7408 retry:
7409 len = (_osfile(fd) & FDEV) ? min(MAXSIZE_CONSOLE_WRITING, size) : size;
7410 size -= len;
7411 retry2:
7413 if (setup_overlapped(&ol, fd, TRUE)) {
7414 rb_acrt_lowio_unlock_fh(fd);
7415 return -1;
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);
7427 rb_acrt_lowio_unlock_fh(fd);
7428 return -1;
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;
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;
7452 finish_overlapped(&ol, fd, written);
7454 ret += written;
7455 if (written == len) {
7456 buf = (const char *)buf + len;
7457 if (size > 0)
7458 goto retry;
7460 if (ret == 0) {
7461 size_t newlen = len / 2;
7462 if (newlen > 0) {
7463 size += len - newlen;
7464 len = newlen;
7465 goto retry2;
7467 ret = -1;
7468 errno = EWOULDBLOCK;
7471 rb_acrt_lowio_unlock_fh(fd);
7473 return ret;
7476 /* License: Ruby's */
7477 long
7478 rb_w32_write_console(uintptr_t strarg, int fd)
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;
7489 handle = (HANDLE)_osfhnd(fd);
7490 if (!GetConsoleMode(handle, &dwMode))
7491 return -1L;
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),
7501 ECONV_INVALID_REPLACE|ECONV_UNDEF_REPLACE, Qnil);
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;
7515 reslen = 0;
7516 if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7517 if (!WriteConsoleW(handle, ptr, len, &reslen, NULL))
7518 reslen = (DWORD)-1L;
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;
7531 ptr = next;
7534 RB_GC_GUARD(str);
7535 if (wbuffer) free(wbuffer);
7536 return (long)reslen;
7539 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7540 /* License: Ruby's */
7541 static int
7542 unixtime_to_filetime(time_t time, FILETIME *ft)
7544 ULARGE_INTEGER tmp;
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;
7551 #endif
7553 /* License: Ruby's */
7554 static int
7555 timespec_to_filetime(const struct timespec *ts, FILETIME *ft)
7557 ULARGE_INTEGER tmp;
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;
7566 /* License: Ruby's */
7567 static int
7568 wutimensat(int dirfd, const WCHAR *path, const struct timespec *times, int flags)
7570 HANDLE hFile;
7571 FILETIME atime, mtime;
7572 struct stati128 stat;
7573 int ret = 0;
7575 /* TODO: When path is absolute, dirfd should be ignored. */
7576 if (dirfd != AT_FDCWD) {
7577 errno = ENOSYS;
7578 return -1;
7581 if (flags != 0) {
7582 errno = EINVAL; /* AT_SYMLINK_NOFOLLOW isn't supported. */
7583 return -1;
7586 if (wstati128(path, &stat, FALSE)) {
7587 return -1;
7590 if (times) {
7591 if (timespec_to_filetime(&times[0], &atime)) {
7592 return -1;
7594 if (timespec_to_filetime(&times[1], &mtime)) {
7595 return -1;
7598 else {
7599 get_systemtime(&atime);
7600 mtime = atime;
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;
7612 else {
7613 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7614 errno = map_errno(GetLastError());
7615 ret = -1;
7617 CloseHandle(hFile);
7619 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7620 SetFileAttributesW(path, attr);
7623 return ret;
7626 /* License: Ruby's */
7627 static int
7628 w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags, UINT cp)
7630 WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7631 int ret = -1;
7633 if (wpath) {
7634 ret = wutimensat(dirfd, wpath, times, flags);
7635 free(wpath);
7637 return ret;
7640 /* License: Ruby's */
7642 rb_w32_uutime(const char *path, const struct utimbuf *times)
7644 struct timespec ts[2];
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);
7653 /* License: Ruby's */
7655 rb_w32_utime(const char *path, const struct utimbuf *times)
7657 struct timespec ts[2];
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());
7666 /* License: Ruby's */
7668 rb_w32_uutimes(const char *path, const struct timeval *times)
7670 struct timespec ts[2];
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);
7679 /* License: Ruby's */
7681 rb_w32_utimes(const char *path, const struct timeval *times)
7683 struct timespec ts[2];
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());
7692 /* License: Ruby's */
7694 rb_w32_uutimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7696 return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7699 /* License: Ruby's */
7701 rb_w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7703 return w32_utimensat(dirfd, path, times, flags, filecp());
7706 /* License: Ruby's */
7708 rb_w32_uchdir(const char *path)
7710 WCHAR *wpath;
7711 int ret;
7713 if (!(wpath = utf8_to_wstr(path, NULL)))
7714 return -1;
7715 ret = _wchdir(wpath);
7716 free(wpath);
7717 return ret;
7720 /* License: Ruby's */
7721 static int
7722 wmkdir(const WCHAR *wpath, int mode)
7724 int ret = -1;
7726 RUBY_CRITICAL do {
7727 if (CreateDirectoryW(wpath, NULL) == FALSE) {
7728 errno = map_errno(GetLastError());
7729 break;
7731 if (_wchmod(wpath, mode) == -1) {
7732 RemoveDirectoryW(wpath);
7733 break;
7735 ret = 0;
7736 } while (0);
7737 return ret;
7740 /* License: Ruby's */
7742 rb_w32_umkdir(const char *path, int mode)
7744 WCHAR *wpath;
7745 int ret;
7747 if (!(wpath = utf8_to_wstr(path, NULL)))
7748 return -1;
7749 ret = wmkdir(wpath, mode);
7750 free(wpath);
7751 return ret;
7754 /* License: Ruby's */
7756 rb_w32_mkdir(const char *path, int mode)
7758 WCHAR *wpath;
7759 int ret;
7761 if (!(wpath = filecp_to_wstr(path, NULL)))
7762 return -1;
7763 ret = wmkdir(wpath, mode);
7764 free(wpath);
7765 return ret;
7768 /* License: Ruby's */
7769 static int
7770 wrmdir(const WCHAR *wpath)
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);
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);
7786 return ret;
7789 /* License: Ruby's */
7791 rb_w32_rmdir(const char *path)
7793 WCHAR *wpath;
7794 int ret;
7796 if (!(wpath = filecp_to_wstr(path, NULL)))
7797 return -1;
7798 ret = wrmdir(wpath);
7799 free(wpath);
7800 return ret;
7803 /* License: Ruby's */
7805 rb_w32_urmdir(const char *path)
7807 WCHAR *wpath;
7808 int ret;
7810 if (!(wpath = utf8_to_wstr(path, NULL)))
7811 return -1;
7812 ret = wrmdir(wpath);
7813 free(wpath);
7814 return ret;
7817 /* License: Ruby's */
7818 static int
7819 wunlink(const WCHAR *path)
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) {
7827 else if ((attr & SYMLINKD) == SYMLINKD) {
7828 ret = RemoveDirectoryW(path);
7830 else {
7831 if (attr & FILE_ATTRIBUTE_READONLY) {
7832 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7834 ret = DeleteFileW(path);
7836 if (!ret) {
7837 errno = map_errno(GetLastError());
7838 ret = -1;
7839 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7840 SetFileAttributesW(path, attr);
7844 return ret;
7847 /* License: Ruby's */
7849 rb_w32_uunlink(const char *path)
7851 WCHAR *wpath;
7852 int ret;
7854 if (!(wpath = utf8_to_wstr(path, NULL)))
7855 return -1;
7856 ret = wunlink(wpath);
7857 free(wpath);
7858 return ret;
7861 /* License: Ruby's */
7863 rb_w32_unlink(const char *path)
7865 WCHAR *wpath;
7866 int ret;
7868 if (!(wpath = filecp_to_wstr(path, NULL)))
7869 return -1;
7870 ret = wunlink(wpath);
7871 free(wpath);
7872 return ret;
7875 /* License: Ruby's */
7877 rb_w32_uchmod(const char *path, int mode)
7879 WCHAR *wpath;
7880 int ret;
7882 if (!(wpath = utf8_to_wstr(path, NULL)))
7883 return -1;
7884 ret = _wchmod(wpath, mode);
7885 free(wpath);
7886 return ret;
7889 /* License: Ruby's */
7891 fchmod(int fd, int mode)
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;
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);
7908 if (h == INVALID_HANDLE_VALUE) {
7909 errno = EBADF;
7910 return -1;
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);
7917 if (!set_file_info) {
7918 errno = ENOSYS;
7919 return -1;
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;
7928 return 0;
7931 /* License: Ruby's */
7933 rb_w32_isatty(int fd)
7935 DWORD mode;
7937 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7938 if (_get_osfhandle(fd) == -1) {
7939 return 0;
7941 if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
7942 errno = ENOTTY;
7943 return 0;
7945 return 1;
7948 #if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7949 extern long _ftol(double);
7950 /* License: Ruby's */
7951 long
7952 _ftol2(double d)
7954 return _ftol(d);
7957 /* License: Ruby's */
7958 long
7959 _ftol2_sse(double d)
7961 return _ftol(d);
7963 #endif
7965 #ifndef signbit
7966 /* License: Ruby's */
7968 signbit(double x)
7970 int *ip = (int *)(&x + 1) - 1;
7971 return *ip < 0;
7973 #endif
7975 /* License: Ruby's */
7976 const char * WSAAPI
7977 rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
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);
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));
7991 return numaddr;
7994 /* License: Ruby's */
7995 int WSAAPI
7996 rb_w32_inet_pton(int af, const char *src, void *dst)
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);
8005 return 0;
8008 /* License: Ruby's */
8009 char
8010 rb_w32_fd_is_text(int fd)
8012 return _osfile(fd) & FTEXT;
8015 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
8016 /* License: Ruby's */
8017 static int
8018 unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
8020 FILETIME ft;
8021 if (unixtime_to_filetime(t, &ft)) return -1;
8022 if (!FileTimeToSystemTime(&ft, st)) return -1;
8023 return 0;
8026 /* License: Ruby's */
8027 static void
8028 systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
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;
8049 t->tm_yday = d - 1;
8052 /* License: Ruby's */
8053 static int
8054 systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
8056 TIME_ZONE_INFORMATION stdtz;
8057 SYSTEMTIME sst;
8059 if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
8060 if (!tz) {
8061 GetTimeZoneInformation(&stdtz);
8062 tz = &stdtz;
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;
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;
8075 #endif
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
8090 /* License: Ruby's */
8091 struct tm *
8092 gmtime_r(const time_t *tp, struct tm *rp)
8094 int e = EINVAL;
8095 if (!tp || !rp) {
8096 error:
8097 errno = e;
8098 return NULL;
8100 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8101 e = gmtime_s(rp, tp);
8102 if (e != 0) goto error;
8103 #else
8105 SYSTEMTIME st;
8106 if (unixtime_to_systemtime(*tp, &st)) goto error;
8107 rp->tm_isdst = 0;
8108 systemtime_to_tm(&st, rp);
8110 #endif
8111 return rp;
8114 /* License: Ruby's */
8115 struct tm *
8116 localtime_r(const time_t *tp, struct tm *rp)
8118 int e = EINVAL;
8119 if (!tp || !rp) {
8120 error:
8121 errno = e;
8122 return NULL;
8124 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8125 e = localtime_s(rp, tp);
8126 if (e) goto error;
8127 #else
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);
8134 #endif
8135 return rp;
8138 /* License: Ruby's */
8140 rb_w32_wrap_io_handle(HANDLE h, int flags)
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;
8151 socklist_insert((SOCKET)h, f);
8153 else if (flags & O_NONBLOCK) {
8154 errno = EINVAL;
8155 return -1;
8157 return rb_w32_open_osfhandle((intptr_t)h, flags);
8160 /* License: Ruby's */
8162 rb_w32_unwrap_io_handle(int fd)
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);
8170 else {
8171 socklist_delete(&sock, NULL);
8173 return _close(fd);
8176 #if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8178 * Set floating point precision for pow() of mingw-w64 x86.
8179 * With default precision the result is not proper on WinXP.
8181 double
8182 rb_w32_pow(double x, double y)
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;
8193 #endif
8195 typedef struct {
8196 BOOL file_id_p;
8197 union {
8198 BY_HANDLE_FILE_INFORMATION bhfi;
8199 FILE_ID_INFO fii;
8200 } info;
8201 } w32_io_info_t;
8203 static HANDLE
8204 w32_io_info(VALUE *file, w32_io_info_t *st)
8206 VALUE tmp;
8207 HANDLE f, ret = 0;
8209 tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
8210 if (!NIL_P(tmp)) {
8211 rb_io_t *fptr;
8213 GetOpenFile(tmp, fptr);
8214 f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
8215 if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
8217 else {
8218 VALUE tmp;
8219 WCHAR *ptr;
8220 int len;
8221 VALUE v;
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;
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;
8243 else if (err != ERROR_INVALID_PARAMETER) {
8244 CloseHandle(f);
8245 return INVALID_HANDLE_VALUE;
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;
8254 if (ret) CloseHandle(ret);
8255 return INVALID_HANDLE_VALUE;
8258 static VALUE
8259 close_handle(VALUE h)
8261 CloseHandle((HANDLE)h);
8262 return Qfalse;
8265 struct w32_io_info_args {
8266 VALUE *fname;
8267 w32_io_info_t *st;
8270 static VALUE
8271 call_w32_io_info(VALUE arg)
8273 struct w32_io_info_args *p = (void *)arg;
8274 return (VALUE)w32_io_info(p->fname, p->st);
8277 VALUE
8278 rb_w32_file_identical_p(VALUE fname1, VALUE fname2)
8280 w32_io_info_t st1, st2;
8281 HANDLE f1 = 0, f2 = 0;
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);
8291 else {
8292 f2 = w32_io_info(&fname2, &st2);
8294 if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
8295 if (f2) CloseHandle(f2);
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;
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;
8309 return Qfalse;
8313 rb_w32_set_thread_description(HANDLE th, const WCHAR *name)
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);
8324 if (set_thread_description) {
8325 result = set_thread_description(th, name);
8327 return result;
8331 rb_w32_set_thread_description_str(HANDLE th, VALUE name)
8333 int idx, result = FALSE;
8334 WCHAR *s;
8336 if (NIL_P(name)) {
8337 return rb_w32_set_thread_description(th, L"");
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);
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);
8350 RB_GC_GUARD(name);
8351 return result;
8354 VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notimplement;
8356 #if RUBY_MSVCRT_VERSION < 120
8357 #include "missing/nextafter.c"
8358 #endif
8360 void *
8361 rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset)
8363 void *ptr;
8364 //DWORD protect = 0;
8365 DWORD protect = PAGE_EXECUTE_READWRITE;
8367 if (fd > 0 || offset) {
8368 /* not supported */
8369 errno = EINVAL;
8370 return MAP_FAILED;
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;
8379 else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8380 else if (prot & PROT_READ) protect = PAGE_READONLY;
8382 ptr = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, protect);
8383 if (!ptr) {
8384 errno = rb_w32_map_errno(GetLastError());
8385 return MAP_FAILED;
8388 return ptr;
8392 rb_w32_munmap(void *addr, size_t len)
8394 if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8395 errno = rb_w32_map_errno(GetLastError());
8396 return -1;
8399 return 0;
8402 inline int
8403 rb_w32_mprotect(void *addr, size_t len, int prot)
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;
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;
8419 if (prot & PROT_EXEC) {
8420 if (!FlushInstructionCache(GetCurrentProcess(), addr, len)) {
8421 errno = rb_w32_map_errno(GetLastError());
8422 return -1;
8425 return 0;