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
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"
27 #include "ruby/util.h"
31 /* #include <sys/wait.h> */
45 #if defined _MSC_VER && _MSC_VER >= 1400
55 #include "ruby/win32.h"
57 #include "win32/dir.h"
58 #include "win32/file.h"
61 #include "internal/enc.h"
62 #include "internal/object.h"
63 #include "internal/static_assert.h"
64 #include "ruby/internal/stdbool.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))
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
);
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 */
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
91 #undef rb_w32_stati128
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 */
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
116 #if RUBY_MSVCRT_VERSION >= 140
117 # define _filbuf _fgetc_nolock
118 # define _flsbuf _fputc_nolock
120 #define enough_to_get(n) (--(n) >= 0)
121 #define enough_to_put(n) (--(n) >= 0)
124 #define Debug(something) something
126 #define Debug(something) /* nothing */
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 */
144 static const struct {
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
},
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
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
, },
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
},
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
)
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
) {
304 #define map_errno rb_w32_map_errno
306 static const char *NTLoginName
;
308 static OSVERSIONINFO osver
;
310 /* License: Artistic or GPL */
314 memset(&osver
, 0, sizeof(OSVERSIONINFO
));
315 osver
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
316 GetVersionEx(&osver
);
320 /* License: Artistic or GPL */
324 return osver
.dwPlatformId
;
328 /* License: Artistic or GPL */
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) \
343 DWORD err = GetLastError(); \
344 if (err == ERROR_LOCK_VIOLATION || err == ERROR_IO_PENDING) \
345 errno = EWOULDBLOCK; \
346 else if (err == ERROR_NOT_LOCKED) \
349 errno = map_errno(err); \
352 #define LK_LEN ULONG_MAX
354 /* License: Artistic or GPL */
356 flock_winnt(uintptr_t self
, int argc
, uintptr_t* argv
)
360 const HANDLE fh
= (HANDLE
)self
;
361 const int oper
= argc
;
363 memset(&o
, 0, sizeof(o
));
366 case LOCK_SH
: /* shared lock */
367 LK_ERR(LockFileEx(fh
, 0, 0, LK_LEN
, LK_LEN
, &o
), i
);
369 case LOCK_EX
: /* exclusive lock */
370 LK_ERR(LockFileEx(fh
, LOCKFILE_EXCLUSIVE_LOCK
, 0, LK_LEN
, LK_LEN
, &o
), i
);
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
);
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
);
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
);
384 default: /* unknown */
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
,
404 /* License: Ruby's */
405 static inline WCHAR
*
406 translate_wchar(WCHAR
*p
, int from
, int to
)
415 /* License: Ruby's */
417 translate_char(char *p
, int from
, int to
, UINT cp
)
420 if ((unsigned char)*p
== from
)
422 p
= CharNextExA(cp
, p
, 0);
427 #ifndef CSIDL_LOCAL_APPDATA
428 #define CSIDL_LOCAL_APPDATA 28
430 #ifndef CSIDL_COMMON_APPDATA
431 #define CSIDL_COMMON_APPDATA 35
433 #ifndef CSIDL_WINDOWS
434 #define CSIDL_WINDOWS 36
437 #define CSIDL_SYSTEM 37
439 #ifndef CSIDL_PROFILE
440 #define CSIDL_PROFILE 40
443 /* License: Ruby's */
445 get_special_folder(int n
, WCHAR
*buf
, size_t len
)
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) {
461 f
= func(pidl
, buf
, len
, 0);
464 f
= SHGetPathFromIDListW(pidl
, buf
);
467 alloc
->lpVtbl
->Free(alloc
, pidl
);
468 alloc
->lpVtbl
->Release(alloc
);
473 /* License: Ruby's */
475 regulate_path(WCHAR
*path
)
477 WCHAR
*p
= translate_wchar(path
, L
'\\', L
'/');
478 if (p
- path
== 2 && path
[1] == L
':') {
484 /* License: Ruby's */
486 get_proc_address(const char *module
, const char *func
, HANDLE
*mh
)
492 h
= LoadLibrary(module
);
494 h
= GetModuleHandle(module
);
498 ptr
= GetProcAddress(h
, func
);
508 /* License: Ruby's */
510 rb_w32_special_folder(int type
)
512 WCHAR path
[PATH_MAX
];
514 if (!get_special_folder(type
, path
, numberof(path
))) return Qnil
;
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
524 /* License: Ruby's */
526 rb_w32_system_tmpdir(WCHAR
*path
, UINT len
)
528 static const WCHAR temp
[] = L
"temp";
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.
547 HOME, USERPROFILE, HOMEDRIVE + HOMEPATH environment variables
548 Special Folders - Profile and Personal
551 rb_w32_home_dir(void)
553 WCHAR
*buffer
= NULL
;
554 size_t buffer_len
= MAX_PATH
, len
= 0;
556 HOME_NONE
, ENV_HOME
, ENV_USERPROFILE
, ENV_DRIVEPATH
557 } home_type
= HOME_NONE
;
559 if ((len
= GetEnvironmentVariableW(L
"HOME", NULL
, 0)) != 0) {
561 home_type
= ENV_HOME
;
563 else if ((len
= GetEnvironmentVariableW(L
"USERPROFILE", NULL
, 0)) != 0) {
565 home_type
= ENV_USERPROFILE
;
567 else if ((len
= GetEnvironmentVariableW(L
"HOMEDRIVE", NULL
, 0)) != 0) {
569 if ((len
= GetEnvironmentVariableW(L
"HOMEPATH", NULL
, 0)) != 0) {
571 home_type
= ENV_DRIVEPATH
;
575 /* allocate buffer */
576 buffer
= ALLOC_N(WCHAR
, buffer_len
);
580 GetEnvironmentVariableW(L
"HOME", buffer
, buffer_len
);
582 case ENV_USERPROFILE
:
583 GetEnvironmentVariableW(L
"USERPROFILE", buffer
, buffer_len
);
586 len
= GetEnvironmentVariableW(L
"HOMEDRIVE", buffer
, buffer_len
);
587 GetEnvironmentVariableW(L
"HOMEPATH", buffer
+ len
, buffer_len
- len
);
590 if (!get_special_folder(CSIDL_PROFILE
, buffer
, buffer_len
) &&
591 !get_special_folder(CSIDL_PERSONAL
, buffer
, buffer_len
)) {
595 REALLOC_N(buffer
, WCHAR
, lstrlenW(buffer
) + 1);
599 /* sanitize backslashes with forwardslashes */
600 regulate_path(buffer
);
605 /* License: Ruby's */
609 static const WCHAR TMPDIR
[] = L
"TMPDIR";
610 struct {WCHAR name
[6], eq
, val
[ENV_MAX
];} wk
;
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); \
623 if (!GetEnvironmentVariableW(L
"HOME", env
, numberof(env
))) {
625 if (GetEnvironmentVariableW(L
"HOMEDRIVE", env
, numberof(env
)))
629 if (GetEnvironmentVariableW(L
"HOMEPATH", env
+ len
, numberof(env
) - len
) || len
) {
632 else if (GetEnvironmentVariableW(L
"USERPROFILE", env
, numberof(env
))) {
635 else if (get_special_folder(CSIDL_PROFILE
, env
, numberof(env
))) {
638 else if (get_special_folder(CSIDL_PERSONAL
, env
, numberof(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>";
653 set_env_val(L
"USER");
654 NTLoginName
= rb_w32_wstr_to_mbstr(CP_UTF8
, env
, -1, NULL
);
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
))) {
672 static void init_stdhandle(void);
674 #if RUBY_MSVCRT_VERSION >= 80
675 /* License: Ruby's */
677 invalid_parameter(const wchar_t *expr
, const wchar_t *func
, const wchar_t *file
, unsigned int line
, uintptr_t dummy
)
682 int ruby_w32_rtc_error
;
685 /* License: Ruby's */
686 RBIMPL_ATTR_NONNULL((5))
687 RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT
, 5, 6)
689 rtc_error_handler(int e
, const char *src
, int line
, const char *exe
, const char *fmt
, ...)
694 if (!ruby_w32_rtc_error
) return 0;
695 str
= rb_sprintf("%s:%d: ", src
, line
);
697 rb_str_vcatf(str
, fmt
, ap
);
699 rb_str_cat(str
, "\n", 1);
700 rb_write_error2(RSTRING_PTR(str
), RSTRING_LEN(str
));
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 */
726 int state
, seq
[16], reverse
;
731 enum {constat_init
= -2, constat_esc
= -1, constat_seq
= 0};
733 /* License: Ruby's */
735 free_conlist(st_data_t key
, st_data_t val
, st_data_t arg
)
737 xfree((struct constat
*)val
);
741 /* License: Ruby's */
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 */
759 DeleteCriticalSection(&select_mutex
);
760 DeleteCriticalSection(&socklist_mutex
);
761 DeleteCriticalSection(&conlist_mutex
);
762 thread_exclusive(uenvarea
) {
768 DeleteCriticalSection(&uenvarea_mutex
);
771 /* License: Ruby's */
773 vm_exit_handler(ruby_vm_t
*vm
)
775 EnterCriticalSection(&socklist_mutex
);
777 st_free_table(socklist
);
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
);
788 LeaveCriticalSection(&conlist_mutex
);
791 #define ATOMIC_LONG_CAS(var, oldval, newval) InterlockedCompareExchange(&(var), (newval), (oldval))
793 /* License: Ruby's */
795 install_vm_exit_handler(void)
797 static LONG installed
= 0;
800 while ((i
= ATOMIC_LONG_CAS(installed
, 0, -1)) != 1) {
805 ruby_vm_at_exit(vm_exit_handler
);
806 ATOMIC_LONG_CAS(installed
, -1, 1);
811 /* License: Artistic or GPL */
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 */
841 socklist_insert(SOCKET sock
, int flag
)
845 thread_exclusive(socklist
) {
847 socklist
= st_init_numtable();
848 install_vm_exit_handler();
850 ret
= st_insert(socklist
, (st_data_t
)sock
, (st_data_t
)flag
);
856 /* License: Ruby's */
858 socklist_lookup(SOCKET sock
, int *flagp
)
863 thread_exclusive(socklist
) {
864 if (!socklist
) continue;
865 ret
= st_lookup(socklist
, (st_data_t
)sock
, &data
);
873 /* License: Ruby's */
875 socklist_delete(SOCKET
*sockp
, int *flagp
)
881 thread_exclusive(socklist
) {
882 if (!socklist
) continue;
883 key
= (st_data_t
)*sockp
;
885 data
= (st_data_t
)*flagp
;
886 ret
= st_delete(socklist
, &key
, &data
);
888 *sockp
= (SOCKET
)key
;
897 #if RUBY_MSVCRT_VERSION >= 80
899 # define _CrtSetReportMode(type,mode) ((void)0)
900 # define _RTC_SetErrorFunc(func) ((void)0)
902 static void set_pioinfo_extra(void);
904 static int w32_cmdvector(const WCHAR
*, char ***, UINT
, rb_encoding
*);
906 // Initialization stuff
908 /* License: Ruby's */
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
);
919 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOGPFAULTERRORBOX
);
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
934 InitializeCriticalSection(&uenvarea_mutex
);
939 // Initialize Winsock
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
) {
976 /* License: Ruby's */
977 static struct ChildRecord
*
978 FindChildSlotByHandle(HANDLE h
)
981 FOREACH_CHILD(child
) {
982 if (child
->hProcess
== h
) {
989 /* License: Ruby's */
991 CloseChildHandle(struct ChildRecord
*child
)
993 HANDLE h
= child
->hProcess
;
994 child
->hProcess
= NULL
;
999 /* License: Ruby's */
1000 static struct ChildRecord
*
1001 FindFreeChildSlot(void)
1003 FOREACH_CHILD(child
) {
1005 child
->pid
= -1; /* lock the slot */
1006 child
->hProcess
= NULL
;
1009 } END_FOREACH_CHILD
;
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,"}}'
1020 #define InternalCmdsMax 8
1021 static const char szInternalCmds
[][InternalCmdsMax
+2] = {
1073 /* License: Ruby's */
1075 internal_match(const void *key
, const void *elem
)
1077 return strncmp(key
, ((const char *)elem
) + 1, InternalCmdsMax
);
1080 /* License: Ruby's */
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) {
1093 static int internal_cmd_match(const char *cmdname
, int nt
);
1095 /* License: Ruby's */
1097 is_internal_cmd(const char *cmd
, int nt
)
1099 char cmdname
[9], *b
= cmdname
, c
;
1102 if (!(c
= *cmd
++)) return 0;
1103 } while (isspace(c
));
1106 while (isalpha(c
)) {
1108 if (b
== cmdname
+ sizeof(cmdname
)) return 0;
1111 if (c
== '.') c
= *cmd
;
1113 case '<': case '>': case '|':
1115 case '\0': case ' ': case '\t': case '\n':
1121 return internal_cmd_match(cmdname
, nt
);
1124 /* License: Ruby's */
1126 internal_cmd_match(const char *cmdname
, int nt
)
1130 nm
= bsearch(cmdname
, szInternalCmds
,
1131 sizeof(szInternalCmds
) / sizeof(*szInternalCmds
),
1132 sizeof(*szInternalCmds
),
1134 if (!nm
|| !(nm
[0] & (nt
? 2 : 1)))
1139 /* License: Ruby's */
1141 rb_w32_get_osfhandle(int fh
)
1143 return _get_osfhandle(fh
);
1146 /* License: Ruby's */
1148 join_argv(char *cmd
, char *const *argv
, BOOL escape
, UINT cp
, int backslash
)
1152 int len
, n
, bs
, quote
;
1154 for (t
= argv
, q
= cmd
, len
= 0; (p
= *t
) != 0; t
++) {
1157 if (!*p
|| strpbrk(p
, " \t\"'")) {
1162 for (bs
= 0; *p
; ++p
) {
1176 memset(q
, '\\', bs
);
1181 case '<': case '>': case '|': case '^':
1182 if (escape
&& !quote
) {
1183 len
+= (n
= p
- s
) + 1;
1194 p
= CharNextExA(cp
, p
, 0) - 1;
1198 len
+= (n
= p
- s
) + 1;
1202 if (backslash
> 0) {
1205 translate_char(q
, '/', '\\', cp
);
1208 if (quote
) *q
++ = '"';
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 */
1226 check_spawn_mode(int mode
)
1238 /* License: Ruby's */
1240 child_result(struct ChildRecord
*child
, int mode
)
1248 if (mode
== P_OVERLAY
) {
1249 WaitForSingleObject(child
->hProcess
, INFINITE
);
1250 GetExitCodeProcess(child
->hProcess
, &exitcode
);
1251 CloseChildHandle(child
);
1257 /* License: Ruby's */
1259 CreateChild(struct ChildRecord
*child
, const WCHAR
*cmd
, const WCHAR
*prog
, HANDLE hInput
, HANDLE hOutput
, HANDLE hError
, DWORD dwCreationFlags
)
1262 STARTUPINFOW aStartupInfo
;
1263 PROCESS_INFORMATION aProcessInformation
;
1264 SECURITY_ATTRIBUTES sa
;
1266 if (!cmd
&& !prog
) {
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
;
1285 aStartupInfo
.hStdInput
= hInput
;
1288 aStartupInfo
.hStdInput
= GetStdHandle(STD_INPUT_HANDLE
);
1291 aStartupInfo
.hStdOutput
= hOutput
;
1294 aStartupInfo
.hStdOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
1297 aStartupInfo
.hStdError
= hError
;
1300 aStartupInfo
.hStdError
= GetStdHandle(STD_ERROR_HANDLE
);
1303 dwCreationFlags
|= NORMAL_PRIORITY_CLASS
;
1305 if (lstrlenW(cmd
) > 32767) {
1306 child
->pid
= 0; /* release the slot */
1312 fRet
= CreateProcessW(prog
, (WCHAR
*)cmd
, &sa
, &sa
,
1313 sa
.bInheritHandle
, dwCreationFlags
, NULL
, NULL
,
1314 &aStartupInfo
, &aProcessInformation
);
1315 errno
= map_errno(GetLastError());
1319 child
->pid
= 0; /* release the slot */
1323 CloseHandle(aProcessInformation
.hThread
);
1325 child
->hProcess
= aProcessInformation
.hProcess
;
1326 child
->pid
= (rb_pid_t
)aProcessInformation
.dwProcessId
;
1331 /* License: Ruby's */
1333 is_batch(const char *cmd
)
1335 int len
= strlen(cmd
);
1336 if (len
<= 4) return 0;
1338 if (*cmd
++ != '.') return 0;
1339 if (strcasecmp(cmd
, "bat") == 0) return 1;
1340 if (strcasecmp(cmd
, "cmd") == 0) return 1;
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
;
1365 WCHAR
*wcmd
= NULL
, *wprog
= NULL
;
1366 HANDLE outHandle
= NULL
;
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
))) {
1380 if (!(wprog
= mbstr_to_wstr(filecp(), abspath
, -1, NULL
))) {
1385 if (!CreateChild(&child
, wcmd
, wprog
, NULL
, outHandle
, outHandle
, 0)) {
1391 return child
.hProcess
;
1394 /* License: Artistic or GPL */
1396 w32_spawn(int mode
, const char *cmd
, const char *prog
, UINT cp
)
1398 char fbuf
[PATH_MAX
];
1400 const char *shell
= NULL
;
1401 WCHAR
*wcmd
= NULL
, *wshell
= NULL
;
1407 char *cmd_sep
= NULL
;
1409 if (check_spawn_mode(mode
)) return -1;
1412 if (!(p
= dln_find_exe_r(prog
, NULL
, fbuf
, sizeof(fbuf
)))) {
1417 translate_char(p
, '/', '\\', cp
);
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
);
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
);
1443 int len
= 0, quote
= (*cmd
== '"') ? '"' : (*cmd
== '\'') ? '\'' : 0;
1445 for (prog
= cmd
+ !!quote
;; prog
= CharNextExA(cp
, prog
, 0)) {
1446 if (*prog
== '/') slash
= 1;
1450 STRNDUPV(p
, v2
, cmd
, len
);
1456 if ((unsigned char)*prog
== quote
) {
1457 len
= prog
++ - cmd
- 1;
1458 STRNDUPV(p
, v2
, cmd
+ 1, len
);
1462 if (quote
) continue;
1463 if (ISSPACE(*prog
) || strchr("<>|*?\"", *prog
)) {
1465 STRNDUPV(p
, v2
, cmd
, len
+ (slash
? strlen(prog
) : 0));
1468 sep
= *(cmd_sep
= &p
[len
]);
1475 shell
= dln_find_exe_r(shell
, NULL
, fbuf
, sizeof(fbuf
));
1476 if (p
&& slash
) translate_char(p
, '/', '\\', cp
);
1478 shell
= p
? p
: cmd
;
1481 len
= strlen(shell
);
1482 if (strchr(shell
, ' ')) quote
= -1;
1483 if (shell
== fbuf
) {
1486 else if (shell
!= p
&& strchr(shell
, '/')) {
1487 STRNDUPV(p
, v2
, shell
, len
);
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
);
1497 if (quote
) *p
++ = '"';
1498 memcpy(p
, prog
, alen
+ 1);
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
);
1512 struct ChildRecord
*child
= FindFreeChildSlot();
1513 if (CreateChild(child
, wcmd
, wshell
, NULL
, NULL
, NULL
, 0)) {
1514 ret
= child_result(child
, mode
);
1523 /* License: Ruby's */
1525 rb_w32_spawn(int mode
, const char *cmd
, const char *prog
)
1528 return w32_spawn(mode
, cmd
, prog
, filecp());
1531 /* License: Ruby's */
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 */
1540 w32_aspawn_flags(int mode
, const char *prog
, char *const *argv
, DWORD flags
, UINT cp
)
1544 BOOL ntcmd
= FALSE
, tmpnt
;
1546 char *cmd
, fbuf
[PATH_MAX
];
1547 WCHAR
*wcmd
= NULL
, *wprog
= NULL
;
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
))) {
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
);
1566 else if (strchr(prog
, '/')) {
1568 if (len
< sizeof(fbuf
))
1569 strlcpy(cmd
= fbuf
, prog
, sizeof(fbuf
));
1571 STRNDUPV(cmd
, v
, prog
, len
);
1572 translate_char(cmd
, '/', '\\', cp
);
1575 if (c_switch
|| is_batch(prog
)) {
1577 progs
[0] = (char *)prog
;
1579 len
= join_argv(NULL
, progs
, ntcmd
, cp
, 1);
1580 if (c_switch
) len
+= 3;
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;
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
;
1600 struct ChildRecord
*child
= FindFreeChildSlot();
1601 if (CreateChild(child
, wcmd
, wprog
, NULL
, NULL
, NULL
, flags
)) {
1602 ret
= child_result(child
, mode
);
1611 /* License: Ruby's */
1613 rb_w32_aspawn_flags(int mode
, const char *prog
, char *const *argv
, DWORD flags
)
1616 return w32_aspawn_flags(mode
, prog
, argv
, flags
, filecp());
1619 /* License: Ruby's */
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 */
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 */
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
;
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 */
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
;
1671 *tail
= &tmpcurr
->next
;
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
;
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
);
1694 if (status
|| last
== tail
) return 0;
1695 if (patt
->flags
& NTMALLOC
)
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 */
1708 has_redirection(const char *cmd
, UINT cp
)
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
;) {
1724 else if (quote
== *ptr
)
1740 if (*++ptr
!= '_' && !ISALPHA(*ptr
)) break;
1741 while (*++ptr
== '_' || ISALNUM(*ptr
));
1742 if (*ptr
++ == '%') return TRUE
;
1748 ptr
= CharNextExA(cp
, ptr
, 0);
1755 /* License: Ruby's */
1756 static inline WCHAR
*
1757 skipspace(WCHAR
*ptr
)
1759 while (ISSPACE(*ptr
))
1764 /* License: Artistic or GPL */
1766 w32_cmdvector(const WCHAR
*cmd
, char ***vec
, UINT cp
, rb_encoding
*enc
)
1769 int elements
, strsz
, done
;
1770 int slashes
, escape
;
1771 WCHAR
*ptr
, *base
, *cmdline
;
1772 char *cptr
, *buffer
;
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
))
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
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
))) {
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.
1811 if (quote
!= L
'\'') slashes
++;
1818 // if we're not in a string, then we're finished with this
1833 // record the fact that this element has a wildcard character
1834 // N.B. Don't glob if inside a single quoted string
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)) {
1854 else if (quote
== *ptr
) {
1855 if (quote
== L
'"' && quote
== ptr
[1])
1865 ptr
= CharNextW(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.
1882 // if it's an input vector element and it's enclosed by quotes,
1883 // we can remove them.
1888 slashes
= quote
= 0;
1889 while (p
< base
+ len
) {
1893 if (quote
!= L
'\'') slashes
++;
1898 if (!(slashes
& 1) && quote
&& quote
!= c
) {
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)) {
1909 if (quote
== L
'"' && quote
== *p
)
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
))) {
1939 cmdtail
= &curr
->next
;
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
) {
1951 strsz
+= (curr
->len
+ 1);
1954 len
= (elements
+1)*sizeof(char *) + strsz
;
1955 buffer
= (char *)malloc(len
);
1958 while ((curr
= cmdhead
) != 0) {
1959 cmdhead
= curr
->next
;
1960 if (curr
->flags
& NTMALLOC
) free(curr
->str
);
1964 for (vptr
= *vec
; *vptr
; ++vptr
);
1969 // make vptr point to the start of the buffer
1970 // and cptr point to the area we'll consider the string table.
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';
1988 cptr
+= curr
->len
+ 1;
1989 cmdhead
= curr
->next
;
1990 if (curr
->flags
& NTMALLOC
) free(curr
->str
);
1995 *vec
= (char **) buffer
;
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
;
2008 get_final_path_fail(HANDLE f
, WCHAR
*buf
, DWORD len
, DWORD flag
)
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 */
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 */
2057 open_dir_handle(const WCHAR
*filename
, WIN32_FIND_DATAW
*fd
)
2060 WCHAR fullname
[FINAL_PATH_MAX
+ rb_strlen_lit("\\*")];
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);
2072 if (len
>= FINAL_PATH_MAX
) {
2073 errno
= ENAMETOOLONG
;
2074 return INVALID_HANDLE_VALUE
;
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
'\\';
2091 // do the FindFirstFile call
2093 fh
= FindFirstFileW(fullname
, fd
);
2094 if (fh
== INVALID_HANDLE_VALUE
) {
2095 errno
= map_errno(GetLastError());
2100 /* License: Artistic or GPL */
2102 w32_wopendir(const WCHAR
*wpath
)
2104 struct stati128 sbuf
;
2105 WIN32_FIND_DATAW fd
;
2116 // check to see if we've got a directory
2118 if (wstati128(wpath
, &sbuf
, FALSE
) < 0) {
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)) {
2127 fh
= open_dir_handle(wpath
, &fd
);
2128 if (fh
== INVALID_HANDLE_VALUE
) {
2133 // Get us a DIR structure
2135 p
= calloc(sizeof(DIR), 1);
2139 pathlen
= lstrlenW(wpath
);
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.
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
));
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);
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
));
2189 idx
+= len
+ altlen
;
2190 } while (FindNextFileW(fh
, &fd
));
2197 /* License: Ruby's */
2201 UINT cp
= AreFileApisANSI() ? CP_ACP
: CP_OEMCP
;
2205 /* License: Ruby's */
2207 rb_w32_wstr_to_mbstr(UINT cp
, const WCHAR
*wstr
, int clen
, long *plen
)
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
);
2214 /* exclude NUL only if NUL-terminated string */
2215 if (clen
== -1) --len
;
2221 /* License: Ruby's */
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. */
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
);
2231 /* exclude NUL only if NUL-terminated string */
2232 if (clen
== -1) --len
;
2238 /* License: Ruby's */
2240 rb_w32_opendir(const char *filename
)
2243 WCHAR
*wpath
= filecp_to_wstr(filename
, NULL
);
2246 ret
= w32_wopendir(wpath
);
2251 /* License: Ruby's */
2253 rb_w32_uopendir(const char *filename
)
2256 WCHAR
*wpath
= utf8_to_wstr(filename
, NULL
);
2259 ret
= w32_wopendir(wpath
);
2265 // Move to next entry
2268 /* License: Artistic or GPL */
2270 move_to_next_entry(DIR *dirp
)
2274 dirp
->curr
+= lstrlenW(dirp
->curr
) + 1;
2275 dirp
->curr
+= lstrlenW(dirp
->curr
) + 1;
2276 if (dirp
->curr
>= (dirp
->start
+ dirp
->size
)) {
2283 // Readdir just returns the current string pointer and bumps the
2284 // string pointer to the next entry.
2286 /* License: Ruby's */
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
)))
2295 entry
->d_altname
= wstr_to_mbstr(cp
, alt
, -1, &altlen
);
2296 entry
->d_altlen
= altlen
;
2301 /* License: Ruby's */
2303 rb_w32_conv_from_wchar(const WCHAR
*wstr
, rb_encoding
*enc
)
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
);
2313 #if SIZEOF_INT < SIZEOF_LONG
2314 # error long should equal to int on Windows
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
);
2322 case ENCINDEX_ASCII_8BIT
:
2323 case ENCINDEX_US_ASCII
:
2325 case ENCINDEX_UTF_8
:
2329 return rb_str_conv_enc_opts(src
, NULL
, enc
, ECONV_UNDEF_REPLACE
, Qnil
);
2332 /* License: Ruby's */
2334 rb_w32_conv_from_wstr(const WCHAR
*wstr
, long *lenp
, rb_encoding
*enc
)
2336 VALUE str
= rb_w32_conv_from_wchar(wstr
, enc
);
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
);
2347 /* License: Ruby's */
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
)))
2355 entry
->d_altname
= rb_w32_conv_from_wstr(alt
, &altlen
, enc
);
2356 entry
->d_altlen
= altlen
;
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;
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
);
2383 dirp
->dirstr
.d_ino
= (ino_t
)(InterlockedIncrement(&dummy_ino
) - 1);
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
;
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
);
2409 /* License: Ruby's */
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
);
2423 return readdir_internal(dirp
, ruby_direct_conv
, enc
);
2426 /* License: Ruby's */
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 */
2440 rb_w32_telldir(DIR *dirp
)
2446 // Seekdir moves the string pointer to a previously saved position
2447 // (Saved by telldir).
2449 /* License: Ruby's */
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 */
2466 rb_w32_rewinddir(DIR *dirp
)
2468 dirp
->curr
= dirp
->start
;
2473 // This just free's the memory allocated by opendir
2476 /* License: Artistic or GPL */
2478 rb_w32_closedir(DIR *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
);
2493 #if RUBY_MSVCRT_VERSION >= 140
2508 CRITICAL_SECTION _lock
;
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
2514 #define FILE_COUNT(stream) stream->_cnt
2515 #define FILE_READPTR(stream) stream->_ptr
2516 #define FILE_FILENO(stream) stream->_file
2519 /* License: Ruby's */
2520 #if RUBY_MSVCRT_VERSION >= 140
2521 typedef char lowio_text_mode
;
2522 typedef char lowio_pipe_lookahead
[3];
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
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 */
2543 CRITICAL_SECTION lock
;
2544 #if RUBY_MSVCRT_VERSION >= 80
2551 #if !defined _CRTIMP || defined __MINGW32__
2553 #define _CRTIMP __declspec(dllimport)
2556 #if RUBY_MSVCRT_VERSION >= 140
2557 static ioinfo
** __pioinfo
= NULL
;
2558 #define IOINFO_L2E 6
2560 EXTERN_C _CRTIMP ioinfo
* __pioinfo
[];
2561 #define IOINFO_L2E 5
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 */
2577 set_pioinfo_extra(void)
2579 #if RUBY_MSVCRT_VERSION >= 140
2580 # define FUNCTION_RET 0xc3 /* ret */
2582 # define UCRTBASE "ucrtbased.dll"
2584 # define UCRTBASE "ucrtbase.dll"
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)
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
);
2601 /* _osfile(fh) & FDEV */
2607 # define FUNCTION_BEFORE_RET_MARK "\x48\x83\xc4"
2608 # define FUNCTION_SKIP_BYTES 1
2610 /* lea rcx,[__pioinfo's addr in RIP-relative 32bit addr] */
2611 # define PIOINFO_MARK "\x48\x8d\x0d"
2613 /* lea rdx,[__pioinfo's addr in RIP-relative 32bit addr] */
2614 # define PIOINFO_MARK "\x48\x8d\x15"
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"
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) {
2640 fprintf(stderr
, "unexpected " UCRTBASE
"\n");
2644 p
+= sizeof(PIOINFO_MARK
) - 1;
2646 rel
= *(int32_t*)(p
);
2647 rip
= p
+ sizeof(int32_t);
2648 __pioinfo
= (ioinfo
**)(rip
+ rel
);
2650 __pioinfo
= *(ioinfo
***)(p
);
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
)) {
2663 if (pioinfo_extra
> 64) {
2664 /* not found, maybe something wrong... */
2669 #define pioinfo_extra 0
2672 static inline ioinfo
*
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 */
2703 rb_w32_open_osfhandle(intptr_t osfhandle
, int flags
)
2706 char fileflags
; /* _osfile flags */
2709 /* copy relevant flags from second parameter */
2712 if (flags
& O_APPEND
)
2713 fileflags
|= FAPPEND
;
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);
2726 errno
= EMFILE
; /* too many open files */
2727 _doserrno
= 0L; /* not an OS error */
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 */
2745 init_stdhandle(void)
2749 #define open_null(fd) \
2751 (nullfd = open("NUL", O_RDWR)) : 0), \
2752 ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)), \
2755 if (fileno(stdin
) < 0) {
2756 FILE_FILENO(stdin
) = open_null(0);
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
);
2773 if (GetConsoleMode(h
, &m
)) {
2774 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
2775 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
2777 SetConsoleMode(h
, m
| ENABLE_VIRTUAL_TERMINAL_PROCESSING
);
2784 /* License: Ruby's */
2786 is_socket(SOCKET sock
)
2788 if (socklist_lookup(sock
, NULL
))
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.
2809 /* License: Artistic or GPL */
2811 rb_w32_strerror(int e
)
2813 static char buffer
[512];
2817 if (e
< 0 || e
> sys_nerr
) {
2820 #if WSAEWOULDBLOCK != EWOULDBLOCK
2821 else if (e
>= EADDRINUSE
&& e
<= EWOULDBLOCK
) {
2825 for (s
= 0; s
< (int)(sizeof(errmap
)/sizeof(*errmap
)); s
++)
2826 if (errmap
[s
].winerr
== WSAEWOULDBLOCK
)
2828 for (i
= s
; i
< (int)(sizeof(errmap
)/sizeof(*errmap
)); i
++)
2829 if (errmap
[i
].err
== e
) {
2830 e
= errmap
[i
].winerr
;
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
));
2845 strlcpy(buffer
, strerror(e
), sizeof(buffer
));
2848 while ((p
= strpbrk(p
, "\r\n")) != NULL
) {
2849 memmove(p
, p
+ 1, strlen(p
));
2861 // Just pretend that everyone is a superuser. NT will let us know if
2862 // we don't really have permission to do something.
2868 /* License: Artistic or GPL */
2875 /* License: Artistic or GPL */
2882 /* License: Artistic or GPL */
2889 /* License: Artistic or GPL */
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
, ...)
2923 rb_w32_fdset(int fd
, fd_set
*set
)
2930 /* License: Ruby's */
2932 rb_w32_fdclr(int fd
, fd_set
*set
)
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
));
2948 /* License: Ruby's */
2950 rb_w32_fdisset(int fd
, fd_set
*set
)
2953 SOCKET s
= TO_SOCKET(fd
);
2954 if (s
== (SOCKET
)INVALID_HANDLE_VALUE
)
2956 RUBY_CRITICAL
{ret
= __WSAFDIsSet(s
, set
);}
2960 /* License: Ruby's */
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 */
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.
2997 /* License: Ruby's */
2999 extract_fd(rb_fdset_t
*dst
, fd_set
*src
, int (*func
)(SOCKET
))
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 */
3012 for (d
= 0; d
< dst
->fdset
->fd_count
; d
++) {
3013 if (dst
->fdset
->fd_array
[d
] == fd
)
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
;
3025 &src
->fd_array
[s
+1],
3026 sizeof(src
->fd_array
[0]) * (--src
->fd_count
- s
));
3036 return dst
? dst
->fdset
->fd_count
: m
;
3039 /* License: Ruby's */
3041 copy_fd(fd_set
*dst
, fd_set
*src
)
3044 if (!src
|| !dst
) return 0;
3046 for (s
= 0; s
< src
->fd_count
; ++s
) {
3047 SOCKET fd
= src
->fd_array
[s
];
3049 for (d
= 0; d
< dst
->fd_count
; ++d
) {
3050 if (dst
->fd_array
[d
] == fd
)
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 */
3063 is_not_socket(SOCKET sock
)
3065 return !is_socket(sock
);
3068 /* License: Ruby's */
3070 is_pipe(SOCKET sock
) /* DONT call this for SOCKET! it claims it is PIPE. */
3075 ret
= (GetFileType((HANDLE
)sock
) == FILE_TYPE_PIPE
);
3081 /* License: Ruby's */
3083 is_readable_pipe(SOCKET sock
) /* call this for pipe only */
3089 if (PeekNamedPipe((HANDLE
)sock
, NULL
, 0, NULL
, &n
, NULL
)) {
3093 ret
= (GetLastError() == ERROR_BROKEN_PIPE
); /* pipe was closed */
3100 /* License: Ruby's */
3102 is_console(SOCKET sock
) /* DONT call this for SOCKET! */
3109 ret
= (PeekConsoleInputW((HANDLE
)sock
, &ir
, 1, &n
));
3115 /* License: Ruby's */
3117 is_readable_console(SOCKET sock
) /* call this for console only */
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
) {
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
) {
3135 ReadConsoleInputW((HANDLE
)sock
, &ir
, 1, &n
);
3143 /* License: Ruby's */
3145 is_invalid_handle(SOCKET sock
)
3147 return (HANDLE
)sock
== INVALID_HANDLE_VALUE
;
3150 /* License: Artistic or GPL */
3152 do_select(int nfds
, fd_set
*rd
, fd_set
*wr
, fd_set
*ex
,
3153 struct timeval
*timeout
)
3159 rb_w32_sleep(timeout
->tv_sec
* 1000 + timeout
->tv_usec
/ 1000);
3161 rb_w32_sleep(INFINITE
);
3165 thread_exclusive(select
) {
3166 r
= select(nfds
, rd
, wr
, ex
, timeout
);
3168 if (r
== SOCKET_ERROR
) {
3169 errno
= map_errno(WSAGetLastError());
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
) {
3189 while (rest
->tv_usec
< wait
->tv_usec
) {
3190 if (rest
->tv_sec
<= wait
->tv_sec
) {
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 */
3203 compare(const struct timeval
*t1
, const struct timeval
*t2
)
3205 if (t1
->tv_sec
< t2
->tv_sec
)
3207 if (t1
->tv_sec
> t2
->tv_sec
)
3209 if (t1
->tv_usec
< t2
->tv_usec
)
3211 if (t1
->tv_usec
> t2
->tv_usec
)
3218 int rb_w32_check_interrupt(void *); /* @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
)
3233 struct timeval limit
= {0, 0};
3235 if (nfds
< 0 || (timeout
&& (timeout
->tv_sec
< 0 || timeout
->tv_usec
< 0))) {
3241 if (timeout
->tv_sec
< 0 ||
3242 timeout
->tv_usec
< 0 ||
3243 timeout
->tv_usec
>= 1000000) {
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;
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
);
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
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
3297 if (th
&& rb_w32_check_interrupt(th
) != WAIT_TIMEOUT
) {
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
);
3318 const struct timeval
*dowait
= &wait
;
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
);
3339 gettimeofday(&now
, NULL
);
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
);
3358 /* License: Ruby's */
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 */
3368 get_wsa_extension_function(SOCKET s
, GUID guid
)
3373 WSAIoctl(s
, SIO_GET_EXTENSION_FUNCTION_POINTER
, &guid
, sizeof(guid
),
3374 &ptr
, sizeof(ptr
), &dmy
, NULL
, NULL
);
3382 /* License: Artistic or GPL */
3384 rb_w32_accept(int s
, struct sockaddr
*addr
, int *addrlen
)
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
);
3395 socklist_insert(r
, 0);
3400 errno
= map_errno(WSAGetLastError());
3409 /* License: Artistic or GPL */
3411 rb_w32_bind(int s
, const struct sockaddr
*addr
, int addrlen
)
3416 r
= bind(TO_SOCKET(s
), addr
, addrlen
);
3417 if (r
== SOCKET_ERROR
)
3418 errno
= map_errno(WSAGetLastError());
3425 /* License: Artistic or GPL */
3427 rb_w32_connect(int s
, const struct sockaddr
*addr
, int addrlen
)
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
);
3437 errno
= EINPROGRESS
;
3446 /* License: Artistic or GPL */
3448 rb_w32_getpeername(int s
, struct sockaddr
*addr
, int *addrlen
)
3452 r
= getpeername(TO_SOCKET(s
), addr
, addrlen
);
3453 if (r
== SOCKET_ERROR
)
3454 errno
= map_errno(WSAGetLastError());
3461 /* License: Artistic or GPL */
3463 rb_w32_getsockname(int fd
, struct sockaddr
*addr
, int *addrlen
)
3468 sock
= TO_SOCKET(fd
);
3469 r
= getsockname(sock
, addr
, addrlen
);
3470 if (r
== SOCKET_ERROR
) {
3471 DWORD wsaerror
= WSAGetLastError();
3472 if (wsaerror
== WSAEINVAL
) {
3474 if (socklist_lookup(sock
, &flags
)) {
3475 int af
= GET_FAMILY(flags
);
3477 memset(addr
, 0, *addrlen
);
3478 addr
->sa_family
= af
;
3483 errno
= map_errno(wsaerror
);
3491 /* License: Artistic or GPL */
3493 rb_w32_getsockopt(int s
, int level
, int optname
, char *optval
, int *optlen
)
3497 r
= getsockopt(TO_SOCKET(s
), level
, optname
, optval
, optlen
);
3498 if (r
== SOCKET_ERROR
)
3499 errno
= map_errno(WSAGetLastError());
3506 /* License: Artistic or GPL */
3508 rb_w32_ioctlsocket(int s
, long cmd
, u_long
*argp
)
3512 r
= ioctlsocket(TO_SOCKET(s
), cmd
, argp
);
3513 if (r
== SOCKET_ERROR
)
3514 errno
= map_errno(WSAGetLastError());
3521 /* License: Artistic or GPL */
3523 rb_w32_listen(int s
, int backlog
)
3527 r
= listen(TO_SOCKET(s
), backlog
);
3528 if (r
== SOCKET_ERROR
)
3529 errno
= map_errno(WSAGetLastError());
3539 /* License: Ruby's */
3541 finish_overlapped_socket(BOOL input
, SOCKET s
, WSAOVERLAPPED
*wol
, int result
, DWORD
*len
, DWORD size
)
3546 if (result
!= SOCKET_ERROR
)
3548 else if ((err
= WSAGetLastError()) == WSA_IO_PENDING
) {
3549 switch (rb_w32_wait_events_blocking(&wol
->hEvent
, 1, INFINITE
)) {
3552 result
= WSAGetOverlappedResult(s
, wol
, &size
, TRUE
, &flg
);
3559 result
= SOCKET_ERROR
;
3562 if ((err
= WSAGetLastError()) == WSAECONNABORTED
&& !input
)
3564 else if (err
== WSAEMSGSIZE
&& input
) {
3570 errno
= map_errno(err
);
3572 case WAIT_OBJECT_0
+ 1:
3575 CancelIo((HANDLE
)s
);
3580 if (err
== WSAECONNABORTED
&& !input
)
3583 errno
= map_errno(err
);
3586 CloseHandle(wol
->hEvent
);
3591 /* License: Artistic or GPL */
3593 overlapped_socket_io(BOOL input
, int fd
, char *buf
, int len
, int flags
,
3594 struct sockaddr
*addr
, int *addrlen
)
3605 socklist_lookup(s
, &mode
);
3606 if (GET_FLAGS(mode
) & O_NONBLOCK
) {
3609 if (addr
&& addrlen
)
3610 r
= recvfrom(s
, buf
, len
, flags
, addr
, addrlen
);
3612 r
= recv(s
, buf
, len
, flags
);
3613 if (r
== SOCKET_ERROR
)
3614 errno
= map_errno(WSAGetLastError());
3617 if (addr
&& addrlen
)
3618 r
= sendto(s
, buf
, len
, flags
, addr
, *addrlen
);
3620 r
= send(s
, buf
, len
, flags
);
3621 if (r
== SOCKET_ERROR
) {
3622 DWORD err
= WSAGetLastError();
3623 if (err
== WSAECONNABORTED
)
3626 errno
= map_errno(err
);
3636 memset(&wol
, 0, sizeof(wol
));
3638 wol
.hEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
3641 if (addr
&& addrlen
)
3642 ret
= WSARecvFrom(s
, &wbuf
, 1, &size
, &flg
, addr
, addrlen
,
3645 ret
= WSARecv(s
, &wbuf
, 1, &size
, &flg
, &wol
, NULL
);
3648 if (addr
&& addrlen
)
3649 ret
= WSASendTo(s
, &wbuf
, 1, &size
, flags
, addr
, *addrlen
,
3652 ret
= WSASend(s
, &wbuf
, 1, &size
, flags
, &wol
, NULL
);
3656 finish_overlapped_socket(input
, s
, &wol
, ret
, &rlen
, size
);
3663 /* License: Ruby's */
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 */
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 */
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 */
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 */
3700 DWORD dwBufferCount
;
3705 #ifndef WSAID_WSARECVMSG
3706 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3708 #ifndef WSAID_WSASENDMSG
3709 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3712 /* License: Ruby's */
3713 #define msghdr_to_wsamsg(msg, wsamsg) \
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; \
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
;
3744 static const GUID guid
= WSAID_WSARECVMSG
;
3745 pWSARecvMsg
= (WSARecvMsg_t
)get_wsa_extension_function(s
, guid
);
3750 msghdr_to_wsamsg(msg
, &wsamsg
);
3751 wsamsg
.dwFlags
|= flags
;
3753 socklist_lookup(s
, &mode
);
3754 if (GET_FLAGS(mode
) & O_NONBLOCK
) {
3756 if ((ret
= pWSARecvMsg(s
, &wsamsg
, &len
, NULL
, NULL
)) == SOCKET_ERROR
) {
3757 errno
= map_errno(WSAGetLastError());
3765 memset(&wol
, 0, sizeof(wol
));
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
)
3776 /* WSAMSG to msghdr */
3777 msg
->msg_name
= wsamsg
.name
;
3778 msg
->msg_namelen
= wsamsg
.namelen
;
3779 msg
->msg_flags
= wsamsg
.dwFlags
;
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
;
3799 static const GUID guid
= WSAID_WSASENDMSG
;
3800 pWSASendMsg
= (WSASendMsg_t
)get_wsa_extension_function(s
, guid
);
3805 msghdr_to_wsamsg(msg
, &wsamsg
);
3807 socklist_lookup(s
, &mode
);
3808 if (GET_FLAGS(mode
) & O_NONBLOCK
) {
3810 if ((ret
= pWSASendMsg(s
, &wsamsg
, flags
, &len
, NULL
, NULL
)) == SOCKET_ERROR
) {
3811 errno
= map_errno(WSAGetLastError());
3819 memset(&wol
, 0, sizeof(wol
));
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
);
3833 /* License: Artistic or GPL */
3835 rb_w32_setsockopt(int s
, int level
, int optname
, const char *optval
, int optlen
)
3839 r
= setsockopt(TO_SOCKET(s
), level
, optname
, optval
, optlen
);
3840 if (r
== SOCKET_ERROR
)
3841 errno
= map_errno(WSAGetLastError());
3848 /* License: Artistic or GPL */
3850 rb_w32_shutdown(int s
, int how
)
3854 r
= shutdown(TO_SOCKET(s
), how
);
3855 if (r
== SOCKET_ERROR
)
3856 errno
= map_errno(WSAGetLastError());
3861 /* License: Ruby's */
3863 open_ifs_socket(int af
, int type
, int protocol
)
3865 unsigned long proto_buffers_len
= 0;
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
) {
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
))
3891 if ((proto_buffers
[i
].dwServiceFlags1
& XP1_IFS_HANDLES
) == 0)
3894 out
= WSASocket(af
, type
, protocol
, &(proto_buffers
[i
]), 0,
3895 WSA_FLAG_OVERLAPPED
);
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
);
3913 /* License: Artistic or GPL */
3915 rb_w32_socket(int af
, int type
, int protocol
)
3921 s
= open_ifs_socket(af
, type
, protocol
);
3922 if (s
== INVALID_SOCKET
) {
3923 errno
= map_errno(WSAGetLastError());
3927 fd
= rb_w32_open_osfhandle(s
, O_RDWR
|O_BINARY
|O_NOINHERIT
);
3929 socklist_insert(s
, MAKE_SOCKDATA(af
, 0));
3937 #undef gethostbyaddr
3939 /* License: Artistic or GPL */
3940 struct hostent
* WSAAPI
3941 rb_w32_gethostbyaddr(const char *addr
, int len
, int type
)
3945 r
= gethostbyaddr(addr
, len
, type
);
3947 errno
= map_errno(WSAGetLastError());
3952 #undef gethostbyname
3954 /* License: Artistic or GPL */
3955 struct hostent
* WSAAPI
3956 rb_w32_gethostbyname(const char *name
)
3960 r
= gethostbyname(name
);
3962 errno
= map_errno(WSAGetLastError());
3969 /* License: Artistic or GPL */
3971 rb_w32_gethostname(char *name
, int len
)
3975 r
= gethostname(name
, len
);
3976 if (r
== SOCKET_ERROR
)
3977 errno
= map_errno(WSAGetLastError());
3982 #undef getprotobyname
3984 /* License: Artistic or GPL */
3985 struct protoent
* WSAAPI
3986 rb_w32_getprotobyname(const char *name
)
3990 r
= getprotobyname(name
);
3992 errno
= map_errno(WSAGetLastError());
3997 #undef getprotobynumber
3999 /* License: Artistic or GPL */
4000 struct protoent
* WSAAPI
4001 rb_w32_getprotobynumber(int num
)
4005 r
= getprotobynumber(num
);
4007 errno
= map_errno(WSAGetLastError());
4012 #undef getservbyname
4014 /* License: Artistic or GPL */
4015 struct servent
* WSAAPI
4016 rb_w32_getservbyname(const char *name
, const char *proto
)
4020 r
= getservbyname(name
, proto
);
4022 errno
= map_errno(WSAGetLastError());
4027 #undef getservbyport
4029 /* License: Artistic or GPL */
4030 struct servent
* WSAAPI
4031 rb_w32_getservbyport(int port
, const char *proto
)
4035 r
= getservbyport(port
, proto
);
4037 errno
= map_errno(WSAGetLastError());
4042 #ifdef HAVE_AFUNIX_H
4044 /* License: Ruby's */
4046 socketpair_unix_path(struct sockaddr_un
*sock_un
)
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
)
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
);
4076 /* user temp dir from TMP or TEMP env var, it ends with a backslash */
4077 path_len
= GetTempPathW(maxpath
, wpath
);
4080 wcsncpy(wpath
, L
"C:/Temp/", maxpath
);
4081 path_len
= lstrlenW(wpath
);
4084 /* Current directory */
4088 closesocket(listener
);
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
,
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
)
4107 closesocket(listener
);
4109 return sizeof(*sock_un
);
4113 /* License: Ruby's */
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
;
4121 struct sockaddr_in6 sock_in6
;
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
"";
4129 struct sockaddr
*addr
;
4135 #if defined PF_INET && PF_INET != AF_INET
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
);
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
);
4153 #ifdef HAVE_AFUNIX_H
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
));
4163 errno
= EAFNOSUPPORT
;
4166 if (type
!= SOCK_STREAM
) {
4171 sv
[0] = (SOCKET
)INVALID_HANDLE_VALUE
;
4172 sv
[1] = (SOCKET
)INVALID_HANDLE_VALUE
;
4175 svr
= open_ifs_socket(af
, type
, protocol
);
4176 if (svr
== INVALID_SOCKET
)
4178 if (bind(svr
, addr
, len
) < 0)
4180 if (getsockname(svr
, addr
, &len
) < 0)
4182 if (type
== SOCK_STREAM
)
4185 w
= open_ifs_socket(af
, type
, protocol
);
4186 if (w
== INVALID_SOCKET
)
4188 if (connect(w
, addr
, len
) < 0)
4191 r
= accept(svr
, addr
, &len
);
4192 if (r
== INVALID_SOCKET
)
4194 SetHandleInformation((HANDLE
)r
, HANDLE_FLAG_INHERIT
, 0);
4200 errno
= map_errno(WSAGetLastError());
4201 if (r
!= INVALID_SOCKET
)
4203 if (w
!= INVALID_SOCKET
)
4210 if (svr
!= INVALID_SOCKET
)
4212 #ifdef HAVE_AFUNIX_H
4213 if (sock_un
.sun_family
== AF_UNIX
)
4221 /* License: Ruby's */
4223 socketpair(int af
, int type
, int protocol
, int *sv
)
4227 if (socketpair_internal(af
, type
, protocol
, pair
) < 0)
4229 sv
[0] = rb_w32_open_osfhandle(pair
[0], O_RDWR
|O_BINARY
|O_NOINHERIT
);
4231 closesocket(pair
[0]);
4232 closesocket(pair
[1]);
4235 sv
[1] = rb_w32_open_osfhandle(pair
[1], O_RDWR
|O_BINARY
|O_NOINHERIT
);
4237 rb_w32_close(sv
[0]);
4238 closesocket(pair
[1]);
4241 socklist_insert(pair
[0], MAKE_SOCKDATA(af
, 0));
4242 socklist_insert(pair
[1], MAKE_SOCKDATA(af
, 0));
4247 #if !defined(_MSC_VER) || _MSC_VER >= 1400
4248 /* License: Ruby's */
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))
4256 if (*str
== '{') str
++;
4257 guid
->Data1
= (long)strtoul(str
, &end
, 16);
4259 guid
->Data2
= (unsigned short)strtoul(str
, &end
, 16);
4261 guid
->Data3
= (unsigned short)strtoul(str
, &end
, 16);
4263 guid
->Data4
[0] = hex2byte(str
);
4265 guid
->Data4
[1] = hex2byte(str
);
4267 for (i
= 0; i
< 6; i
++) {
4268 guid
->Data4
[i
+ 2] = hex2byte(str
);
4273 /* License: Ruby's */
4274 #ifndef HAVE_TYPE_NET_LUID
4278 uint64_t Reserved
:24;
4279 uint64_t NetLuidIndex
:24;
4280 uint64_t IfType
:16;
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
)
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
);
4302 root
= ruby_xmalloc(size
);
4303 ret
= GetAdaptersAddresses(AF_UNSPEC
, 0, NULL
, root
, &size
);
4304 if (ret
!= ERROR_SUCCESS
) {
4305 errno
= map_errno(ret
);
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
];
4326 prev
->ifa_next
= 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
);
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
;
4348 for (cur
= addr
->FirstUnicastAddress
; cur
; cur
= cur
->Next
) {
4349 if (cur
->Flags
& IP_ADAPTER_ADDRESS_TRANSIENT
||
4350 cur
->DadState
== IpDadStateDeprecated
) {
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
);
4375 /* License: Ruby's */
4377 freeifaddrs(struct ifaddrs
*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
);
4389 #if 0 // Have never been used
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
) {}
4418 int rb_w32_set_nonblock2(int fd
, int nonblock
);
4420 /* License: Ruby's */
4422 setfl(SOCKET sock
, int arg
)
4429 socklist_lookup(sock
, &flag
);
4430 af
= GET_FAMILY(flag
);
4431 flag
= GET_FLAGS(flag
);
4432 if (arg
& O_NONBLOCK
) {
4437 flag
&= ~O_NONBLOCK
;
4441 ret
= ioctlsocket(sock
, FIONBIO
, &ioctlArg
);
4443 socklist_insert(sock
, MAKE_SOCKDATA(af
, flag
));
4445 errno
= map_errno(WSAGetLastError());
4451 /* License: Ruby's */
4453 dupfd(HANDLE hDup
, int flags
, int minfd
)
4461 ret
= _open_osfhandle((intptr_t)hDup
, flags
| FOPEN
);
4463 goto close_fds_and_return
;
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
:
4475 while (filled
> 0) {
4476 int fd
= fds
[--filled
];
4477 _set_osfhnd(fd
, (intptr_t)INVALID_HANDLE_VALUE
);
4485 /* License: Ruby's */
4487 fcntl(int fd
, int cmd
, ...)
4496 arg
= va_arg(va
, int);
4498 return rb_w32_set_nonblock2(fd
, arg
);
4500 case F_DUPFD
: case F_DUPFD_CLOEXEC
: {
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());
4513 arg
= va_arg(va
, int);
4519 flag
&= ~FNOINHERIT
;
4520 if ((ret
= dupfd(hDup
, flag
, arg
)) == -1)
4525 SIGNED_VALUE h
= _get_osfhandle(fd
);
4526 if (h
== -1) return -1;
4527 if (!GetHandleInformation((HANDLE
)h
, &flag
)) {
4528 errno
= map_errno(GetLastError());
4531 return (flag
& HANDLE_FLAG_INHERIT
) ? 0 : FD_CLOEXEC
;
4534 SIGNED_VALUE h
= _get_osfhandle(fd
);
4535 if (h
== -1) return -1;
4537 arg
= va_arg(va
, int);
4539 if (!SetHandleInformation((HANDLE
)h
, HANDLE_FLAG_INHERIT
,
4540 (arg
& FD_CLOEXEC
) ? 0 : HANDLE_FLAG_INHERIT
)) {
4541 errno
= map_errno(GetLastError());
4544 if (arg
& FD_CLOEXEC
)
4545 _osfile(fd
) |= FNOINHERIT
;
4547 _osfile(fd
) &= ~FNOINHERIT
;
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
)) {
4566 if (!GetNamedPipeHandleState((HANDLE
)sock
, &state
, NULL
, NULL
, NULL
, NULL
, 0)) {
4567 errno
= map_errno(GetLastError());
4571 state
|= PIPE_NOWAIT
;
4574 state
&= ~PIPE_NOWAIT
;
4576 if (!SetNamedPipeHandleState((HANDLE
)sock
, &state
, NULL
, NULL
)) {
4577 errno
= map_errno(GetLastError());
4589 rb_w32_set_nonblock(int fd
)
4591 return rb_w32_set_nonblock2(fd
, TRUE
);
4598 /* License: Ruby's */
4600 poll_child_status(struct ChildRecord
*child
, int *stat_loc
)
4605 if (!GetExitCodeProcess(child
->hProcess
, &exitcode
)) {
4606 /* If an error occurred, return immediately. */
4607 err
= GetLastError();
4609 case ERROR_INVALID_PARAMETER
:
4612 case ERROR_INVALID_HANDLE
:
4616 errno
= map_errno(err
);
4620 CloseChildHandle(child
);
4623 if (exitcode
!= STILL_ACTIVE
) {
4625 /* If already died, wait process's real termination. */
4626 if (rb_w32_wait_events_blocking(&child
->hProcess
, 1, INFINITE
) != WAIT_OBJECT_0
) {
4630 CloseChildHandle(child
);
4632 *stat_loc
= exitcode
<< 8;
4633 if (exitcode
& 0xC0000000) {
4634 static const struct {
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
},
4651 #ifdef STATUS_FLOAT_MULTIPLE_TRAPS
4652 {STATUS_FLOAT_MULTIPLE_TRAPS
, SIGFPE
},
4654 {STATUS_CONTROL_C_EXIT
, SIGINT
},
4657 for (i
= 0; i
< (int)numberof(table
); i
++) {
4658 if (table
[i
].status
== exitcode
) {
4659 *stat_loc
|= table
[i
].sig
;
4663 // if unknown status, assume SEGV
4664 if (i
>= (int)numberof(table
))
4665 *stat_loc
|= SIGSEGV
;
4673 /* License: Artistic or GPL */
4675 waitpid(rb_pid_t pid
, int *stat_loc
, int options
)
4679 /* Artistic or GPL part start */
4680 if (options
== WNOHANG
) {
4686 /* Artistic or GPL part end */
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
;
4704 ret
= rb_w32_wait_events_blocking(events
, count
, timeout
);
4705 if (ret
== WAIT_TIMEOUT
) return 0;
4706 if ((ret
-= WAIT_OBJECT_0
) == count
) {
4710 errno
= map_errno(GetLastError());
4714 cause
= FindChildSlotByHandle(events
[ret
]);
4719 return poll_child_status(cause
, stat_loc
);
4722 struct ChildRecord
* child
= FindChildSlot(pid
);
4729 while (!(pid
= poll_child_status(child
, stat_loc
))) {
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
) {
4735 if (options
& WNOHANG
) {
4742 if (pid
== -1 && retried
) pid
= 0;
4748 #include <sys/timeb.h>
4750 static int have_precisetime
= -1;
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
);
4762 func
= GetSystemTimeAsFileTime
;
4763 have_precisetime
= 0;
4766 have_precisetime
= 1;
4772 /* License: Ruby's */
4773 /* split FILETIME value into UNIX time and sub-seconds in NT ticks */
4775 filetime_split(const FILETIME
* ft
, long *subsec
)
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
;
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
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 */
4797 gettimeofday(struct timeval
*tv
, struct timezone
*tz
)
4802 get_systemtime(&ft
);
4803 tv
->tv_sec
= filetime_split(&ft
, &subsec
);
4804 tv
->tv_usec
= subsec
/ 10;
4809 /* License: Ruby's */
4811 clock_gettime(clockid_t clock_id
, struct timespec
*sp
)
4814 case CLOCK_REALTIME
:
4819 get_systemtime(&ft
);
4820 sp
->tv_sec
= filetime_split(&ft
, &subsec
);
4821 sp
->tv_nsec
= subsec
* 100;
4824 case CLOCK_MONOTONIC
:
4827 LARGE_INTEGER count
;
4828 if (!QueryPerformanceFrequency(&freq
)) {
4829 errno
= map_errno(GetLastError());
4832 if (!QueryPerformanceCounter(&count
)) {
4833 errno
= map_errno(GetLastError());
4836 sp
->tv_sec
= count
.QuadPart
/ freq
.QuadPart
;
4837 if (freq
.QuadPart
< 1000000000)
4838 sp
->tv_nsec
= (count
.QuadPart
% freq
.QuadPart
) * 1000000000 / freq
.QuadPart
;
4840 sp
->tv_nsec
= (long)((count
.QuadPart
% freq
.QuadPart
) * (1000000000.0 / freq
.QuadPart
));
4849 /* License: Ruby's */
4851 clock_getres(clockid_t clock_id
, struct timespec
*sp
)
4854 case CLOCK_REALTIME
:
4860 case CLOCK_MONOTONIC
:
4863 if (!QueryPerformanceFrequency(&freq
)) {
4864 errno
= map_errno(GetLastError());
4868 sp
->tv_nsec
= (long)(1000000000.0 / freq
.QuadPart
);
4877 /* License: Ruby's */
4879 w32_getcwd(char *buffer
, int size
, UINT cp
, void *alloc(int, void *), void *arg
)
4884 len
= GetCurrentDirectoryW(0, NULL
);
4886 errno
= map_errno(GetLastError());
4890 if (buffer
&& size
< len
) {
4895 p
= ALLOCA_N(WCHAR
, len
);
4896 if (!GetCurrentDirectoryW(len
, p
)) {
4897 errno
= map_errno(GetLastError());
4901 wlen
= translate_wchar(p
, L
'\\', L
'/') - p
+ 1;
4902 len
= WideCharToMultiByte(cp
, 0, p
, wlen
, NULL
, 0, NULL
, NULL
);
4910 buffer
= (*alloc
)(len
, arg
);
4916 WideCharToMultiByte(cp
, 0, p
, wlen
, buffer
, len
, NULL
, NULL
);
4921 /* License: Ruby's */
4923 getcwd_alloc(int size
, void *dummy
)
4925 return malloc(size
);
4928 /* License: Ruby's */
4930 rb_w32_getcwd(char *buffer
, int size
)
4932 return w32_getcwd(buffer
, size
, filecp(), getcwd_alloc
, NULL
);
4935 /* License: Ruby's */
4937 rb_w32_ugetcwd(char *buffer
, int size
)
4939 return w32_getcwd(buffer
, size
, CP_UTF8
, getcwd_alloc
, NULL
);
4942 /* License: Ruby's */
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 */
4952 rb_dir_getwd_ospath(void)
4955 w32_getcwd(NULL
, 0, CP_UTF8
, getcwd_value
, &cwd
);
4959 /* License: Artistic or GPL */
4961 chown(const char *path
, int owner
, int group
)
4966 /* License: Artistic or GPL */
4968 rb_w32_uchown(const char *path
, int owner
, int group
)
4974 lchown(const char *path
, int owner
, int group
)
4980 rb_w32_ulchown(const char *path
, int owner
, int group
)
4985 /* License: Ruby's */
4987 kill(rb_pid_t pid
, int sig
)
4992 if (pid
< 0 || (pid
== 0 && sig
!= SIGINT
)) {
4997 if ((unsigned int)pid
== GetCurrentProcessId() &&
4998 (sig
!= 0 && sig
!= SIGKILL
)) {
4999 if ((ret
= raise(sig
)) != 0) {
5000 /* MSVCRT doesn't set errno... */
5010 OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, (DWORD
)pid
);
5011 if (hProc
== NULL
|| hProc
== INVALID_HANDLE_VALUE
) {
5012 if (GetLastError() == ERROR_INVALID_PARAMETER
) {
5028 DWORD ctrlEvent
= CTRL_C_EVENT
;
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)
5038 errno
= map_errno(GetLastError());
5047 struct ChildRecord
* child
= FindChildSlot(pid
);
5049 hProc
= child
->hProcess
;
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
) {
5065 if (!GetExitCodeProcess(hProc
, &status
)) {
5066 errno
= map_errno(GetLastError());
5069 else if (status
== STILL_ACTIVE
) {
5070 if (!TerminateProcess(hProc
, 0)) {
5095 /* License: Ruby's */
5097 wlink(const WCHAR
*from
, const WCHAR
*to
)
5099 if (!CreateHardLinkW(to
, from
, NULL
)) {
5100 errno
= map_errno(GetLastError());
5107 /* License: Ruby's */
5109 rb_w32_ulink(const char *from
, const char *to
)
5115 if (!(wfrom
= utf8_to_wstr(from
, NULL
)))
5117 if (!(wto
= utf8_to_wstr(to
, NULL
))) {
5121 ret
= wlink(wfrom
, wto
);
5127 /* License: Ruby's */
5129 link(const char *from
, const char *to
)
5135 if (!(wfrom
= filecp_to_wstr(from
, NULL
)))
5137 if (!(wto
= filecp_to_wstr(to
, NULL
))) {
5141 ret
= wlink(wfrom
, wto
);
5147 /* License: Public Domain, copied from mingw headers */
5148 #ifndef FILE_DEVICE_FILE_SYSTEM
5149 # define FILE_DEVICE_FILE_SYSTEM 0x00000009
5151 #ifndef FSCTL_GET_REPARSE_POINT
5152 # define FSCTL_GET_REPARSE_POINT ((0x9<<16)|(42<<2))
5154 #ifndef IO_REPARSE_TAG_SYMLINK
5155 # define IO_REPARSE_TAG_SYMLINK 0xA000000CL
5158 /* License: Ruby's */
5160 reparse_symlink(const WCHAR
*path
, rb_w32_reparse_buffer_t
*rp
, size_t size
)
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
)) {
5175 else if (rp
->ReparseTag
!= IO_REPARSE_TAG_SYMLINK
&&
5176 rp
->ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
) {
5177 e
= ERROR_INVALID_PARAMETER
;
5183 /* License: Ruby's */
5185 rb_w32_reparse_symlink_p(const WCHAR
*path
)
5188 rb_w32_reparse_buffer_t rbuf
, *rp
= &rbuf
;
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
);
5202 case ERROR_MORE_DATA
:
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
);
5216 if (!e
|| e
== ERROR_MORE_DATA
) {
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)
5242 if ((char *)name
+ ret
+ sizeof(WCHAR
) > (char *)rp
+ bufsize
)
5244 /* SubstituteName is not used */
5246 ((WCHAR
*)name
)[ret
/sizeof(WCHAR
)] = L
'\0';
5247 translate_wchar(name
, L
'\\', L
'/');
5255 /* License: Ruby's */
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
);
5263 WCHAR
*wpath
= ALLOCV(rp_buf
, sizeof(WCHAR
) * len
+ size
);
5264 rb_w32_reparse_buffer_t
*rp
= (void *)(wpath
+ len
);
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
);
5277 ALLOCV_END(rp_buf_bigger
);
5278 errno
= e
== -1 ? EINVAL
: map_errno(e
);
5281 len
= lstrlenW(wname
);
5282 ret
= WideCharToMultiByte(cp
, 0, wname
, len
, buf
, bufsize
, NULL
, NULL
);
5284 ALLOCV_END(rp_buf_bigger
);
5291 /* License: Ruby's */
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 */
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)
5308 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
5309 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
5312 /* License: Ruby's */
5314 w32_symlink(UINT cp
, const char *src
, const char *link
)
5316 int atts
, len1
, len2
;
5318 WCHAR
*wsrc
, *wlink
;
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
) {
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
);
5359 (e
= GetLastError()) == ERROR_INVALID_PARAMETER
&&
5360 (flag
& SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
)) {
5362 flag
&= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
;
5363 ret
= create_symbolic_link(wlink
, wsrc
, flag
);
5364 if (!ret
) e
= GetLastError();
5369 errno
= map_errno(e
);
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 */
5393 return waitpid(-1, status
, 0);
5396 /* License: Ruby's */
5398 w32_getenv(const char *name
, UINT cp
)
5400 WCHAR
*wenvarea
, *wenv
;
5401 int len
= strlen(name
);
5402 char *env
, *found
= NULL
;
5405 if (len
== 0) return NULL
;
5408 /* initialized in init_env, uenvarea_mutex should have been
5409 * initialized before it */
5410 return getenv(name
);
5413 thread_exclusive(uenvarea
) {
5418 wenvarea
= GetEnvironmentStringsW();
5420 map_errno(GetLastError());
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
);
5430 for (env
= uenvarea
; *env
; env
+= strlen(env
) + 1) {
5431 if (strncasecmp(env
, name
, len
) == 0 && *(env
+ len
) == '=') {
5432 found
= env
+ len
+ 1;
5441 /* License: Ruby's */
5443 rb_w32_ugetenv(const char *name
)
5445 return w32_getenv(name
, CP_UTF8
);
5448 /* License: Ruby's */
5450 rb_w32_getenv(const char *name
)
5452 return w32_getenv(name
, CP_ACP
);
5455 /* License: Ruby's */
5457 get_attr_vsn(const WCHAR
*path
, DWORD
*atts
, DWORD
*vsn
)
5459 BY_HANDLE_FILE_INFORMATION st
= {0};
5461 HANDLE h
= open_special(path
, 0, FILE_FLAG_OPEN_REPARSE_POINT
);
5463 if (h
== INVALID_HANDLE_VALUE
) {
5468 if (!GetFileInformationByHandle(h
, &st
)) {
5473 *atts
= st
.dwFileAttributes
;
5474 *vsn
= st
.dwVolumeSerialNumber
;
5480 /* License: Artistic or GPL */
5482 wrename(const WCHAR
*oldpath
, const WCHAR
*newpath
)
5485 DWORD oldatts
= 0, newatts
= (DWORD
)-1;
5486 DWORD oldvsn
= 0, newvsn
= 0, e
;
5488 e
= get_attr_vsn(oldpath
, &oldatts
, &oldvsn
);
5490 errno
= map_errno(e
);
5493 if (oldatts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
5494 HANDLE fh
= open_special(oldpath
, 0, 0);
5495 if (fh
== INVALID_HANDLE_VALUE
) {
5497 if (e
== ERROR_CANT_RESOLVE_FILENAME
) {
5504 get_attr_vsn(newpath
, &newatts
, &newvsn
);
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
))
5514 DWORD e
= GetLastError();
5515 if ((e
== ERROR_ACCESS_DENIED
) && (oldatts
& FILE_ATTRIBUTE_DIRECTORY
) &&
5519 errno
= map_errno(e
);
5522 SetFileAttributesW(newpath
, oldatts
);
5528 /* License: Ruby's */
5530 rb_w32_urename(const char *from
, const char *to
)
5536 if (!(wfrom
= utf8_to_wstr(from
, NULL
)))
5538 if (!(wto
= utf8_to_wstr(to
, NULL
))) {
5542 ret
= wrename(wfrom
, wto
);
5548 /* License: Ruby's */
5550 rb_w32_rename(const char *from
, const char *to
)
5556 if (!(wfrom
= filecp_to_wstr(from
, NULL
)))
5558 if (!(wto
= filecp_to_wstr(to
, NULL
))) {
5562 ret
= wrename(wfrom
, wto
);
5568 /* License: Ruby's */
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
'\\') {
5582 for (p
++; *p
; p
++) {
5586 if (!p
[0] || !p
[1] || (p
[1] == L
'.' && !p
[2]))
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; \
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
);
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
);
5630 /* License: Ruby's */
5632 rb_w32_fstati128(int fd
, struct stati128
*st
)
5635 int ret
= fstat(fd
, &tmp
);
5637 if (ret
) return ret
;
5638 COPY_STAT(tmp
, *st
, +);
5639 stati128_handle((HANDLE
)_get_osfhandle(fd
), st
);
5643 #if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5645 BYTE Identifier
[16];
5649 #if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5650 #define FileIdInfo 0x12
5653 unsigned LONG_LONG VolumeSerialNumber
;
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
)))
5672 return GetLastError();
5674 return ERROR_INVALID_PARAMETER
;
5677 /* License: Ruby's */
5679 stati128_handle(HANDLE h
, struct stati128
*st
)
5681 BY_HANDLE_FILE_INFORMATION info
;
5682 DWORD attr
= (DWORD
)-1;
5684 if (GetFileInformationByHandle(h
, &info
)) {
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);
5700 st
->st_ino
= ((__int64
)info
.nFileIndexHigh
<< 32) | info
.nFileIndexLow
;
5707 /* License: Ruby's */
5709 filetime_to_unixtime(const FILETIME
*ft
)
5712 time_t t
= filetime_split(ft
, &subsec
);
5714 if (t
< 0) return 0;
5718 /* License: Ruby's */
5720 filetime_to_nsec(const FILETIME
*ft
)
5722 if (have_precisetime
<= 0)
5726 tmp
.LowPart
= ft
->dwLowDateTime
;
5727 tmp
.HighPart
= ft
->dwHighDateTime
;
5728 return (long)(tmp
.QuadPart
% 10000000) * 100;
5732 /* License: Ruby's */
5734 fileattr_to_unixmode(DWORD attr
, const WCHAR
*path
, unsigned mode
)
5736 if (attr
& FILE_ATTRIBUTE_READONLY
) {
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
;
5757 if (path
&& (mode
& S_IFREG
)) {
5758 const WCHAR
*end
= path
+ lstrlenW(path
);
5759 while (path
< end
) {
5760 end
= CharPrevW(path
, end
);
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)) {
5770 if (!iswalnum(*end
)) break;
5774 mode
|= (mode
& 0500) >> 3;
5775 mode
|= (mode
& 0500) >> 6;
5780 /* License: Ruby's */
5782 check_valid_dir(const WCHAR
*path
)
5784 WIN32_FIND_DATAW fd
;
5786 WCHAR full
[PATH_MAX
];
5790 /* GetFileAttributes() determines "..." as directory. */
5791 /* We recheck it by FindFirstFile(). */
5792 if (!(p
= wcsstr(path
, L
"...")))
5794 q
= p
+ wcsspn(p
, L
".");
5795 if ((p
== path
|| wcschr(L
":/\\", *(p
- 1))) &&
5796 (!*q
|| wcschr(L
":/\\", *q
))) {
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());
5807 if (full
[1] == L
':' && !full
[3] && GetDriveTypeW(full
) != DRIVE_NO_ROOT_DIR
)
5810 fh
= open_dir_handle(path
, &fd
);
5811 if (fh
== INVALID_HANDLE_VALUE
)
5817 /* License: Ruby's */
5819 stat_by_find(const WCHAR
*path
, struct stati128
*st
)
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());
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
;
5843 /* License: Ruby's */
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 */
5853 winnt_stat(const WCHAR
*path
, struct stati128
*st
, BOOL lstat
)
5855 DWORD flags
= lstat
? FILE_FLAG_OPEN_REPARSE_POINT
: 0;
5857 WCHAR finalname
[PATH_MAX
];
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
;
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
) {
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);
5880 switch (GetFileType(f
)) {
5881 case FILE_TYPE_CHAR
:
5884 case FILE_TYPE_PIPE
:
5888 if (attr
& FILE_ATTRIBUTE_REPARSE_POINT
) {
5889 FILE_ATTRIBUTE_TAG_INFO attr_info
;
5892 e
= GetFileInformationByHandleEx( f
, FileAttributeTagInfo
,
5893 &attr_info
, sizeof(attr_info
));
5894 if (e
&& attr_info
.ReparseTag
== IO_REPARSE_TAG_AF_UNIX
) {
5898 else if (rb_w32_reparse_symlink_p(path
)) {
5899 /* TODO: size in which encoding? */
5901 mode
|= S_IFLNK
| S_IEXEC
;
5904 mode
|= S_IFDIR
| S_IEXEC
;
5909 if (attr
& FILE_ATTRIBUTE_DIRECTORY
) {
5910 if (check_valid_dir(path
)) return -1;
5912 st
->st_mode
= fileattr_to_unixmode(attr
, path
, mode
);
5914 finalname
[min(len
, numberof(finalname
)-1)] = L
'\0';
5916 if (wcsncmp(path
, namespace_prefix
, numberof(namespace_prefix
)) == 0)
5917 path
+= numberof(namespace_prefix
);
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
);
5927 if (stat_by_find(path
, st
)) return -1;
5930 st
->st_dev
= st
->st_rdev
= path_drive(path
);
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
));
5946 /* License: Ruby's */
5948 wstati128(const WCHAR
*path
, struct stati128
*st
, BOOL lstat
)
5958 size
= lstrlenW(path
) + 2;
5959 buf1
= ALLOCV_N(WCHAR
, v
, size
);
5960 if (!(path
= name_for_stat(buf1
, path
)))
5962 ret
= winnt_stat(path
, st
, lstat
);
5969 /* License: Ruby's */
5971 name_for_stat(WCHAR
*buf1
, const WCHAR
*path
)
5977 for (p
= path
, s
= buf1
; *p
; p
++, s
++) {
5985 if (!len
|| L
'\"' == *(--s
)) {
5989 end
= buf1
+ len
- 1;
5991 if (isUNCRoot(buf1
)) {
5994 else if (*end
!= L
'\\')
5995 lstrcatW(buf1
, L
"\\");
5997 else if (*end
== L
'\\' || (buf1
+ 1 == end
&& *end
== L
':'))
5998 lstrcatW(buf1
, L
".");
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 */
6019 w32_stati128(const char *path
, struct stati128
*st
, UINT cp
, BOOL lstat
)
6024 if (!(wpath
= mbstr_to_wstr(cp
, path
, -1, NULL
)))
6026 ret
= wstati128(wpath
, st
, lstat
);
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 */
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
)) {
6054 return _lseeki64(fd
, ofs
, whence
);
6057 /* License: Ruby's */
6059 w32_access(const char *path
, int mode
, UINT cp
)
6061 struct stati128 stat
;
6062 if (w32_stati128(path
, &stat
, cp
, FALSE
) != 0)
6065 if ((stat
.st_mode
& mode
) != mode
) {
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 */
6088 rb_chsize(HANDLE h
, rb_off_t size
)
6090 long upos
, lpos
, usize
, lsize
;
6094 if ((lpos
= SetFilePointer(h
, 0, (upos
= 0, &upos
), SEEK_CUR
)) == -1L &&
6095 (e
= GetLastError())) {
6096 errno
= map_errno(e
);
6099 usize
= (long)(size
>> 32);
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());
6111 SetFilePointer(h
, lpos
, &upos
, SEEK_SET
);
6115 /* License: Ruby's */
6117 w32_truncate(const char *path
, rb_off_t length
, UINT cp
)
6123 if (!(wpath
= mbstr_to_wstr(cp
, path
, -1, NULL
)))
6125 h
= CreateFileW(wpath
, GENERIC_WRITE
, 0, 0, OPEN_EXISTING
, 0, 0);
6126 if (h
== INVALID_HANDLE_VALUE
) {
6127 errno
= map_errno(GetLastError());
6132 ret
= rb_chsize(h
, length
);
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
)
6157 h
= (HANDLE
)_get_osfhandle(fd
);
6158 if (h
== (HANDLE
)-1) return -1;
6159 return rb_chsize(h
, length
);
6162 /* License: Ruby's */
6164 filetime_to_clock(FILETIME
*ft
)
6166 __int64 qw
= ft
->dwHighDateTime
;
6168 qw
|= ft
->dwLowDateTime
;
6169 qw
/= 10000; /* File time ticks at 0.1uS, clock at 1mS */
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;
6186 tmbuf
->tms_utime
= clock();
6187 tmbuf
->tms_stime
= 0;
6188 tmbuf
->tms_cutime
= 0;
6189 tmbuf
->tms_cstime
= 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
{
6206 uintptr_t (*func
)(uintptr_t self
, int argc
, uintptr_t* argv
);
6212 /* License: Ruby's */
6214 call_asynchronous(PVOID argp
)
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
;
6224 /* License: Ruby's */
6226 rb_w32_asynchronize(asynchronous_func_t func
, uintptr_t self
,
6227 int argc
, uintptr_t* argv
, uintptr_t intrval
)
6230 BOOL interrupted
= FALSE
;
6234 struct asynchronous_arg_t arg
;
6236 arg
.stackaddr
= NULL
;
6243 thr
= CreateThread(NULL
, 0, call_asynchronous
, &arg
, 0, &val
);
6246 yield_until(arg
.stackaddr
);
6248 if (rb_w32_wait_events_blocking(&thr
, 1, INFINITE
) != WAIT_OBJECT_0
) {
6251 if (TerminateThread(thr
, intrval
)) {
6256 GetExitCodeThread(thr
, &val
);
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()));
6281 rb_fatal("failed to launch waiter thread:%ld", GetLastError());
6287 /* License: Ruby's */
6289 rb_w32_get_environ(void)
6291 WCHAR
*envtop
, *env
;
6292 char **myenvtop
, **myenv
;
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
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) {
6312 if (!(*myenv
= wstr_to_utf8(env
, NULL
))) {
6319 FreeEnvironmentStringsW(envtop
);
6324 /* License: Ruby's */
6326 rb_w32_free_environ(char **env
)
6330 while (*t
) free(*t
++);
6334 /* License: Ruby's */
6338 return GetCurrentProcessId();
6342 /* License: Ruby's */
6344 rb_w32_getppid(void)
6346 typedef long (WINAPI query_func
)(HANDLE
, int, void *, ULONG
, ULONG
*);
6347 static query_func
*pNtQueryInformationProcess
= (query_func
*)-1;
6350 if (pNtQueryInformationProcess
== (query_func
*)-1)
6351 pNtQueryInformationProcess
= (query_func
*)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL
);
6352 if (pNtQueryInformationProcess
) {
6355 void* PebBaseAddress
;
6356 uintptr_t AffinityMask
;
6357 uintptr_t BasePriority
;
6358 uintptr_t UniqueProcessId
;
6359 uintptr_t ParentProcessId
;
6362 long ret
= pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi
, sizeof(pbi
), &len
);
6364 ppid
= pbi
.ParentProcessId
;
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), \
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
)
6387 if (oldfd
== newfd
) return newfd
;
6388 ret
= dup2(oldfd
, newfd
);
6389 if (ret
< 0) return ret
;
6390 set_new_std_fd(newfd
);
6394 /* License: Ruby's */
6396 rb_w32_uopen(const char *file
, int oflag
, ...)
6403 va_start(arg
, oflag
);
6404 pmode
= va_arg(arg
, int);
6407 if (!(wfile
= utf8_to_wstr(file
, NULL
)))
6409 ret
= w32_wopen(wfile
, oflag
, pmode
);
6414 /* License: Ruby's */
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
)) {
6428 /* License: Ruby's */
6430 rb_w32_open(const char *file
, int oflag
, ...)
6437 va_start(arg
, oflag
);
6438 pmode
= va_arg(arg
, int);
6441 if (!(wfile
= filecp_to_wstr(file
, NULL
)))
6443 ret
= w32_wopen(wfile
, oflag
, pmode
);
6448 /* License: Ruby's */
6450 rb_w32_wopen(const WCHAR
*file
, int oflag
, ...)
6454 if (oflag
& O_CREAT
) {
6456 va_start(arg
, oflag
);
6457 pmode
= va_arg(arg
, int);
6461 return w32_wopen(file
, oflag
, pmode
);
6465 w32_wopen(const WCHAR
*file
, int oflag
, int pmode
)
6471 DWORD attr
= FILE_ATTRIBUTE_NORMAL
;
6472 SECURITY_ATTRIBUTES sec
;
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
);
6483 check_if_wdir(file
);
6486 errno
= map_errno(GetLastError());
6493 sec
.nLength
= sizeof(sec
);
6494 sec
.lpSecurityDescriptor
= NULL
;
6495 if (oflag
& O_NOINHERIT
) {
6496 sec
.bInheritHandle
= FALSE
;
6497 flags
|= FNOINHERIT
;
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
)) {
6509 access
= GENERIC_READ
| GENERIC_WRITE
;
6512 access
= GENERIC_READ
;
6515 access
= GENERIC_WRITE
;
6521 oflag
&= ~(O_RDWR
| O_RDONLY
| O_WRONLY
);
6523 switch (oflag
& (O_CREAT
| O_EXCL
| O_TRUNC
)) {
6525 create
= OPEN_ALWAYS
;
6529 create
= OPEN_EXISTING
;
6531 case O_CREAT
| O_EXCL
:
6532 case O_CREAT
| O_EXCL
| O_TRUNC
:
6533 create
= CREATE_NEW
;
6536 case O_TRUNC
| O_EXCL
:
6537 create
= TRUNCATE_EXISTING
;
6539 case O_CREAT
| O_TRUNC
:
6540 create
= CREATE_ALWAYS
;
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
;
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
)) {
6567 attr
|= FILE_FLAG_SEQUENTIAL_SCAN
;
6570 attr
|= FILE_FLAG_RANDOM_ACCESS
;
6576 oflag
&= ~(O_SEQUENTIAL
| O_RANDOM
);
6578 if (oflag
& ~O_APPEND
) {
6583 /* allocate a C Runtime file handle */
6585 h
= CreateFile("NUL", 0, 0, NULL
, OPEN_ALWAYS
, 0, NULL
);
6586 fd
= _open_osfhandle((intptr_t)h
, 0);
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
);
6608 switch (GetFileType(h
)) {
6609 case FILE_TYPE_CHAR
:
6612 case FILE_TYPE_PIPE
:
6615 case FILE_TYPE_UNKNOWN
:
6616 errno
= map_errno(GetLastError());
6618 rb_acrt_lowio_unlock_fh(fd
);
6622 if (!(flags
& (FDEV
| FPIPE
)) && (oflag
& O_APPEND
))
6625 _set_osfhnd(fd
, (intptr_t)h
);
6626 _set_osflags(fd
, flags
| FOPEN
);
6628 rb_acrt_lowio_unlock_fh(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
);
6649 _set_osfhnd(fd
, (SOCKET
)INVALID_HANDLE_VALUE
);
6652 if (closesocket(sock
) == SOCKET_ERROR
) {
6653 errno
= map_errno(WSAGetLastError());
6659 /* License: Ruby's */
6661 rb_w32_pipe(int fds
[2])
6663 static long serial
= 0;
6664 static const char prefix
[] = "\\\\.\\pipe\\ruby";
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
;
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
;
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
)
6694 errno
= map_errno(GetLastError());
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());
6710 h
= CreateFile("NUL", 0, 0, NULL
, OPEN_ALWAYS
, 0, NULL
);
6711 fdRead
= _open_osfhandle((intptr_t)h
, 0);
6715 CloseHandle(hWrite
);
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
);
6730 h
= CreateFile("NUL", 0, 0, NULL
, OPEN_ALWAYS
, 0, NULL
);
6731 fdWrite
= _open_osfhandle((intptr_t)h
, 0);
6733 if (fdWrite
== -1) {
6735 CloseHandle(hWrite
);
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
);
6745 rb_w32_close(fdRead
);
6755 /* License: Ruby's */
6757 console_emulator_p(void)
6762 const void *const func
= WriteConsoleW
;
6764 MEMORY_BASIC_INFORMATION m
;
6766 memset(&m
, 0, sizeof(m
));
6767 if (!VirtualQuery(func
, &m
, sizeof(m
))) {
6770 k
= GetModuleHandle("kernel32.dll");
6771 if (!k
) return FALSE
;
6772 return (HMODULE
)m
.AllocationBase
!= k
;
6776 /* License: Ruby's */
6777 static struct constat
*
6778 constat_handle(HANDLE h
)
6781 struct constat
*p
= NULL
;
6782 thread_exclusive(conlist
) {
6784 if (console_emulator_p()) {
6785 conlist
= conlist_disabled
;
6788 conlist
= st_init_numtable();
6789 install_vm_exit_handler();
6791 else if (conlist
== conlist_disabled
) {
6794 if (st_lookup(conlist
, (st_data_t
)h
, &data
)) {
6795 p
= (struct constat
*)data
;
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
);
6813 /* License: Ruby's */
6815 constat_reset(HANDLE h
)
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 */
6837 constat_attr(int count
, const int *seq
, WORD attr
, WORD default_attr
, int *reverse
)
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) {
6850 attr
= default_attr
;
6855 bold
= FOREGROUND_INTENSITY
;
6858 #ifndef COMMON_LVB_UNDERSCORE
6859 #define COMMON_LVB_UNDERSCORE 0x8000
6861 attr
|= COMMON_LVB_UNDERSCORE
;
6868 attr
&= ~(FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
);
6872 attr
= (attr
& ~(FOREGROUND_BLUE
| FOREGROUND_GREEN
)) | FOREGROUND_RED
;
6876 attr
= (attr
& ~(FOREGROUND_BLUE
| FOREGROUND_RED
)) | FOREGROUND_GREEN
;
6880 attr
= (attr
& ~FOREGROUND_BLUE
) | FOREGROUND_GREEN
| FOREGROUND_RED
;
6884 attr
= (attr
& ~(FOREGROUND_GREEN
| FOREGROUND_RED
)) | FOREGROUND_BLUE
;
6888 attr
= (attr
& ~FOREGROUND_GREEN
) | FOREGROUND_BLUE
| FOREGROUND_RED
;
6892 attr
= (attr
& ~FOREGROUND_RED
) | FOREGROUND_BLUE
| FOREGROUND_GREEN
;
6896 attr
|= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
6900 attr
&= ~(BACKGROUND_BLUE
| BACKGROUND_GREEN
| BACKGROUND_RED
);
6903 attr
= (attr
& ~(BACKGROUND_BLUE
| BACKGROUND_GREEN
)) | BACKGROUND_RED
;
6906 attr
= (attr
& ~(BACKGROUND_BLUE
| BACKGROUND_RED
)) | BACKGROUND_GREEN
;
6909 attr
= (attr
& ~BACKGROUND_BLUE
) | BACKGROUND_GREEN
| BACKGROUND_RED
;
6912 attr
= (attr
& ~(BACKGROUND_GREEN
| BACKGROUND_RED
)) | BACKGROUND_BLUE
;
6915 attr
= (attr
& ~BACKGROUND_GREEN
) | BACKGROUND_BLUE
| BACKGROUND_RED
;
6918 attr
= (attr
& ~BACKGROUND_RED
) | BACKGROUND_BLUE
| BACKGROUND_GREEN
;
6921 attr
|= BACKGROUND_BLUE
| BACKGROUND_GREEN
| BACKGROUND_RED
;
6926 if (rev
) attr
= constat_attr_color_reverse(attr
);
6931 /* License: Ruby's */
6933 constat_clear(HANDLE handle
, WORD attr
, DWORD len
, COORD pos
)
6937 FillConsoleOutputAttribute(handle
, attr
, len
, pos
, &written
);
6938 FillConsoleOutputCharacterW(handle
, L
' ', len
, pos
, &written
);
6941 /* License: Ruby's */
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
;
6951 if (!GetConsoleScreenBufferInfo(handle
, &csbi
)) return;
6952 arg0
= (count
> 0 && seq
[0] > 0);
6953 if (arg0
) arg1
= seq
[0];
6956 SetConsoleTextAttribute(handle
, constat_attr(count
, seq
, csbi
.wAttributes
, s
->vt100
.attr
, &s
->vt100
.reverse
));
6959 csbi
.dwCursorPosition
.X
= 0;
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
);
6967 csbi
.dwCursorPosition
.X
= 0;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
7019 case 1: /* erase before *and* cursor */
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),
7027 case 2: /* erase entire screen */
7029 pos
.Y
= csbi
.srWindow
.Top
;
7030 constat_clear(handle
, csbi
.wAttributes
,
7031 (csbi
.dwSize
.X
* (csbi
.srWindow
.Bottom
- csbi
.srWindow
.Top
+ 1)),
7034 case 3: /* erase entire screen */
7037 constat_clear(handle
, csbi
.wAttributes
,
7038 (csbi
.dwSize
.X
* csbi
.dwSize
.Y
),
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
);
7050 case 1: /* erase before *and* cursor */
7052 pos
.Y
= csbi
.dwCursorPosition
.Y
;
7053 constat_clear(handle
, csbi
.wAttributes
,
7054 csbi
.dwCursorPosition
.X
+ 1, pos
);
7056 case 2: /* erase entire line */
7058 pos
.Y
= csbi
.dwCursorPosition
.Y
;
7059 constat_clear(handle
, csbi
.wAttributes
,
7060 csbi
.dwSize
.X
, pos
);
7065 s
->vt100
.saved
= csbi
.dwCursorPosition
;
7068 SetConsoleCursorPosition(handle
, s
->vt100
.saved
);
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
);
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
);
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 */
7095 constat_parse(HANDLE h
, struct constat
*s
, const WCHAR
**ptrp
, long *lenp
)
7097 const WCHAR
*ptr
= *ptrp
;
7098 long rest
, len
= *lenp
;
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
) {
7112 /* TODO: supply dropped ESC at beginning */
7113 s
->vt100
.state
= constat_init
;
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;
7133 if (++s
->vt100
.state
< (int)numberof(s
->vt100
.seq
)) {
7134 s
->vt100
.seq
[s
->vt100
.state
] = 0;
7137 s
->vt100
.state
= (int)numberof(s
->vt100
.seq
);
7141 constat_apply(h
, s
, wc
);
7142 s
->vt100
.state
= constat_init
;
7147 else if ((rest
= *lenp
- len
) < MAXSIZE_CONSOLE_WRITING
) {
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
);
7173 _set_osfhnd(fd
, (SOCKET
)INVALID_HANDLE_VALUE
);
7174 socklist_delete(&sock
, NULL
);
7177 if (closesocket(sock
) == SOCKET_ERROR
) {
7178 errno
= map_errno(WSAGetLastError());
7185 setup_overlapped(OVERLAPPED
*ol
, int fd
, int iswrite
)
7187 memset(ol
, 0, sizeof(*ol
));
7188 if (!(_osfile(fd
) & (FDEV
| FPIPE
))) {
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)
7199 if (low
== INVALID_SET_FILE_POINTER
) {
7200 DWORD err
= GetLastError();
7201 if (err
!= NO_ERROR
) {
7202 errno
= map_errno(err
);
7207 ol
->OffsetHigh
= high
;
7209 ol
->hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
7211 errno
= map_errno(GetLastError());
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
)
7227 SetFilePointer((HANDLE
)_osfhnd(fd
), low
, &high
, FILE_BEGIN
);
7232 /* License: Ruby's */
7234 rb_w32_read(int fd
, void *buf
, size_t size
)
7236 SOCKET sock
= TO_SOCKET(fd
);
7244 BOOL islineinput
= FALSE
;
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) {
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
);
7268 isconsole
= is_console(_osfhnd(fd
)) && (osver
.dwMajorVersion
< 6 || (osver
.dwMajorVersion
== 6 && osver
.dwMinorVersion
< 2));
7271 GetConsoleMode((HANDLE
)_osfhnd(fd
),&mode
);
7272 islineinput
= (mode
& ENABLE_LINE_INPUT
) != 0;
7275 /* get rid of console reading bug */
7277 constat_reset((HANDLE
)_osfhnd(fd
));
7289 if (setup_overlapped(&ol
, fd
, FALSE
)) {
7290 rb_acrt_lowio_unlock_fh(fd
);
7294 if (!ReadFile((HANDLE
)_osfhnd(fd
), buf
, len
, &read
, &ol
)) {
7295 err
= GetLastError();
7296 if (err
== ERROR_NO_DATA
&& (_osfile(fd
) & FPIPE
)) {
7298 if (GetNamedPipeHandleState((HANDLE
)_osfhnd(fd
), &state
, NULL
, NULL
, NULL
, NULL
, 0) && (state
& PIPE_NOWAIT
)) {
7299 errno
= EWOULDBLOCK
;
7302 errno
= map_errno(err
);
7304 rb_acrt_lowio_unlock_fh(fd
);
7307 else if (err
!= ERROR_IO_PENDING
) {
7308 CloseHandle(ol
.hEvent
);
7309 if (err
== ERROR_ACCESS_DENIED
)
7311 else if (err
== ERROR_BROKEN_PIPE
|| err
== ERROR_HANDLE_EOF
) {
7312 rb_acrt_lowio_unlock_fh(fd
);
7316 errno
= map_errno(err
);
7318 rb_acrt_lowio_unlock_fh(fd
);
7322 wait
= rb_w32_wait_events_blocking(&ol
.hEvent
, 1, INFINITE
);
7323 if (wait
!= WAIT_OBJECT_0
) {
7324 if (wait
== WAIT_OBJECT_0
+ 1)
7327 errno
= map_errno(GetLastError());
7328 CloseHandle(ol
.hEvent
);
7329 CancelIo((HANDLE
)_osfhnd(fd
));
7330 rb_acrt_lowio_unlock_fh(fd
);
7334 if (!GetOverlappedResult((HANDLE
)_osfhnd(fd
), &ol
, &read
, TRUE
) &&
7335 (err
= GetLastError()) != ERROR_HANDLE_EOF
) {
7337 if (err
!= ERROR_BROKEN_PIPE
) {
7338 errno
= map_errno(err
);
7341 CloseHandle(ol
.hEvent
);
7342 CancelIo((HANDLE
)_osfhnd(fd
));
7343 rb_acrt_lowio_unlock_fh(fd
);
7348 err
= GetLastError();
7349 errno
= map_errno(err
);
7352 finish_overlapped(&ol
, fd
, read
);
7356 buf
= (char *)buf
+ read
;
7357 if (err
!= ERROR_OPERATION_ABORTED
&&
7358 !(isconsole
&& len
== 1 && (!islineinput
|| *((char *)buf
- 1) == '\n')) && size
> 0)
7362 _set_osflags(fd
, _osfile(fd
) | FEOFLAG
);
7365 rb_acrt_lowio_unlock_fh(fd
);
7371 /* License: Ruby's */
7373 rb_w32_write(int fd
, const void *buf
, size_t size
)
7375 SOCKET sock
= TO_SOCKET(fd
);
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) {
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());
7400 rb_acrt_lowio_lock_fh(fd
);
7402 if (!size
|| _osfile(fd
) & FEOFLAG
) {
7403 rb_acrt_lowio_unlock_fh(fd
);
7409 len
= (_osfile(fd
) & FDEV
) ? min(MAXSIZE_CONSOLE_WRITING
, size
) : size
;
7413 if (setup_overlapped(&ol
, fd
, TRUE
)) {
7414 rb_acrt_lowio_unlock_fh(fd
);
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
)
7425 errno
= map_errno(err
);
7427 rb_acrt_lowio_unlock_fh(fd
);
7431 wait
= rb_w32_wait_events_blocking(&ol
.hEvent
, 1, INFINITE
);
7432 if (wait
!= WAIT_OBJECT_0
) {
7433 if (wait
== WAIT_OBJECT_0
+ 1)
7436 errno
= map_errno(GetLastError());
7437 CloseHandle(ol
.hEvent
);
7438 CancelIo((HANDLE
)_osfhnd(fd
));
7439 rb_acrt_lowio_unlock_fh(fd
);
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
);
7452 finish_overlapped(&ol
, fd
, written
);
7455 if (written
== len
) {
7456 buf
= (const char *)buf
+ len
;
7461 size_t newlen
= len
/ 2;
7463 size
+= len
- newlen
;
7468 errno
= EWOULDBLOCK
;
7471 rb_acrt_lowio_unlock_fh(fd
);
7476 /* License: Ruby's */
7478 rb_w32_write_console(uintptr_t strarg
, int fd
)
7481 DWORD dwMode
, reslen
;
7485 const WCHAR
*ptr
, *next
;
7489 handle
= (HANDLE
)_osfhnd(fd
);
7490 if (!GetConsoleMode(handle
, &dwMode
))
7493 s
= constat_handle(handle
);
7495 encindex
= ENCODING_GET(str
);
7498 if (!rb_econv_has_convpath_p(rb_enc_name(rb_enc_from_index(encindex
)), "UTF-8"))
7500 str
= rb_str_conv_enc_opts(str
, NULL
, rb_enc_from_index(ENCINDEX_UTF_8
),
7501 ECONV_INVALID_REPLACE
|ECONV_UNDEF_REPLACE
, Qnil
);
7503 case ENCINDEX_US_ASCII
:
7504 case ENCINDEX_ASCII_8BIT
:
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;
7510 case ENCINDEX_UTF_16LE
:
7511 ptr
= (const WCHAR
*)RSTRING_PTR(str
);
7512 len
= RSTRING_LEN(str
) / sizeof(WCHAR
);
7516 if (dwMode
& ENABLE_VIRTUAL_TERMINAL_PROCESSING
) {
7517 if (!WriteConsoleW(handle
, ptr
, len
, &reslen
, NULL
))
7518 reslen
= (DWORD
)-1L;
7522 long curlen
= constat_parse(handle
, s
, (next
= ptr
, &next
), &len
);
7523 reslen
+= next
- ptr
;
7526 if (!WriteConsoleW(handle
, ptr
, curlen
, &written
, NULL
)) {
7527 reslen
= (DWORD
)-1L;
7535 if (wbuffer
) free(wbuffer
);
7536 return (long)reslen
;
7539 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7540 /* License: Ruby's */
7542 unixtime_to_filetime(time_t time
, FILETIME
*ft
)
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
;
7553 /* License: Ruby's */
7555 timespec_to_filetime(const struct timespec
*ts
, FILETIME
*ft
)
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
;
7566 /* License: Ruby's */
7568 wutimensat(int dirfd
, const WCHAR
*path
, const struct timespec
*times
, int flags
)
7571 FILETIME atime
, mtime
;
7572 struct stati128 stat
;
7575 /* TODO: When path is absolute, dirfd should be ignored. */
7576 if (dirfd
!= AT_FDCWD
) {
7582 errno
= EINVAL
; /* AT_SYMLINK_NOFOLLOW isn't supported. */
7586 if (wstati128(path
, &stat
, FALSE
)) {
7591 if (timespec_to_filetime(×
[0], &atime
)) {
7594 if (timespec_to_filetime(×
[1], &mtime
)) {
7599 get_systemtime(&atime
);
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());
7613 if (!SetFileTime(hFile
, NULL
, &atime
, &mtime
)) {
7614 errno
= map_errno(GetLastError());
7619 if (attr
!= (DWORD
)-1 && (attr
& FILE_ATTRIBUTE_READONLY
))
7620 SetFileAttributesW(path
, attr
);
7626 /* License: Ruby's */
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
);
7634 ret
= wutimensat(dirfd
, wpath
, times
, flags
);
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
;
7648 ts
[1].tv_sec
= times
->modtime
;
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
;
7661 ts
[1].tv_sec
= times
->modtime
;
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
)
7713 if (!(wpath
= utf8_to_wstr(path
, NULL
)))
7715 ret
= _wchdir(wpath
);
7720 /* License: Ruby's */
7722 wmkdir(const WCHAR
*wpath
, int mode
)
7727 if (CreateDirectoryW(wpath
, NULL
) == FALSE
) {
7728 errno
= map_errno(GetLastError());
7731 if (_wchmod(wpath
, mode
) == -1) {
7732 RemoveDirectoryW(wpath
);
7740 /* License: Ruby's */
7742 rb_w32_umkdir(const char *path
, int mode
)
7747 if (!(wpath
= utf8_to_wstr(path
, NULL
)))
7749 ret
= wmkdir(wpath
, mode
);
7754 /* License: Ruby's */
7756 rb_w32_mkdir(const char *path
, int mode
)
7761 if (!(wpath
= filecp_to_wstr(path
, NULL
)))
7763 ret
= wmkdir(wpath
, mode
);
7768 /* License: Ruby's */
7770 wrmdir(const WCHAR
*wpath
)
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());
7781 if (attr
!= (DWORD
)-1 && (attr
& FILE_ATTRIBUTE_READONLY
)) {
7782 SetFileAttributesW(wpath
, attr
);
7789 /* License: Ruby's */
7791 rb_w32_rmdir(const char *path
)
7796 if (!(wpath
= filecp_to_wstr(path
, NULL
)))
7798 ret
= wrmdir(wpath
);
7803 /* License: Ruby's */
7805 rb_w32_urmdir(const char *path
)
7810 if (!(wpath
= utf8_to_wstr(path
, NULL
)))
7812 ret
= wrmdir(wpath
);
7817 /* License: Ruby's */
7819 wunlink(const WCHAR
*path
)
7822 const DWORD SYMLINKD
= FILE_ATTRIBUTE_REPARSE_POINT
|FILE_ATTRIBUTE_DIRECTORY
;
7824 const DWORD attr
= GetFileAttributesW(path
);
7825 if (attr
== (DWORD
)-1) {
7827 else if ((attr
& SYMLINKD
) == SYMLINKD
) {
7828 ret
= RemoveDirectoryW(path
);
7831 if (attr
& FILE_ATTRIBUTE_READONLY
) {
7832 SetFileAttributesW(path
, attr
& ~FILE_ATTRIBUTE_READONLY
);
7834 ret
= DeleteFileW(path
);
7837 errno
= map_errno(GetLastError());
7839 if (attr
!= (DWORD
)-1 && (attr
& FILE_ATTRIBUTE_READONLY
)) {
7840 SetFileAttributesW(path
, attr
);
7847 /* License: Ruby's */
7849 rb_w32_uunlink(const char *path
)
7854 if (!(wpath
= utf8_to_wstr(path
, NULL
)))
7856 ret
= wunlink(wpath
);
7861 /* License: Ruby's */
7863 rb_w32_unlink(const char *path
)
7868 if (!(wpath
= filecp_to_wstr(path
, NULL
)))
7870 ret
= wunlink(wpath
);
7875 /* License: Ruby's */
7877 rb_w32_uchmod(const char *path
, int mode
)
7882 if (!(wpath
= utf8_to_wstr(path
, NULL
)))
7884 ret
= _wchmod(wpath
, mode
);
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. */
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
) {
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
) {
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());
7931 /* License: Ruby's */
7933 rb_w32_isatty(int fd
)
7937 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7938 if (_get_osfhandle(fd
) == -1) {
7941 if (!GetConsoleMode((HANDLE
)_osfhnd(fd
), &mode
)) {
7948 #if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7949 extern long _ftol(double);
7950 /* License: Ruby's */
7957 /* License: Ruby's */
7959 _ftol2_sse(double d
)
7966 /* License: Ruby's */
7970 int *ip
= (int *)(&x
+ 1) - 1;
7975 /* License: Ruby's */
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
);
7984 return pInetNtop(af
, (void *)addr
, numaddr
, numaddr_len
);
7988 memcpy(&in
.s_addr
, addr
, sizeof(in
.s_addr
));
7989 snprintf(numaddr
, numaddr_len
, "%s", inet_ntoa(in
));
7994 /* License: Ruby's */
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
);
8003 return pInetPton(af
, src
, dst
);
8008 /* License: Ruby's */
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 */
8018 unixtime_to_systemtime(const time_t t
, SYSTEMTIME
*st
)
8021 if (unixtime_to_filetime(t
, &ft
)) return -1;
8022 if (!FileTimeToSystemTime(&ft
, st
)) return -1;
8026 /* License: Ruby's */
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
;
8045 d
+= 31 + 28 + (!(y
% 4) && ((y
% 100) || !(y
% 400)));
8046 d
+= ((m
- 3) * 153 + 2) / 5;
8052 /* License: Ruby's */
8054 systemtime_to_localtime(TIME_ZONE_INFORMATION
*tz
, SYSTEMTIME
*gst
, SYSTEMTIME
*lst
)
8056 TIME_ZONE_INFORMATION stdtz
;
8059 if (!SystemTimeToTzSpecificLocalTime(tz
, gst
, lst
)) return -1;
8061 GetTimeZoneInformation(&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
)
8077 #ifdef HAVE__GMTIME64_S
8078 # ifndef HAVE__LOCALTIME64_S
8079 /* assume same as _gmtime64_s() */
8080 # define HAVE__LOCALTIME64_S 1
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
);
8086 # define gmtime_s _gmtime64_s
8087 # define localtime_s _localtime64_s
8090 /* License: Ruby's */
8092 gmtime_r(const time_t *tp
, struct tm
*rp
)
8100 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8101 e
= gmtime_s(rp
, tp
);
8102 if (e
!= 0) goto error
;
8106 if (unixtime_to_systemtime(*tp
, &st
)) goto error
;
8108 systemtime_to_tm(&st
, rp
);
8114 /* License: Ruby's */
8116 localtime_r(const time_t *tp
, struct tm
*rp
)
8124 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8125 e
= localtime_s(rp
, tp
);
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
);
8138 /* License: Ruby's */
8140 rb_w32_wrap_io_handle(HANDLE h
, int flags
)
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
) {
8147 if (flags
& O_NONBLOCK
) {
8148 flags
&= ~O_NONBLOCK
;
8151 socklist_insert((SOCKET
)h
, f
);
8153 else if (flags
& O_NONBLOCK
) {
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
);
8171 socklist_delete(&sock
, NULL
);
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.
8182 rb_w32_pow(double x
, double y
)
8186 unsigned int default_control
= _controlfp(0, 0);
8187 _controlfp(_PC_64
, _MCW_PC
);
8189 /* Restore setting */
8190 _controlfp(default_control
, _MCW_PC
);
8198 BY_HANDLE_FILE_INFORMATION bhfi
;
8204 w32_io_info(VALUE
*file
, w32_io_info_t
*st
)
8209 tmp
= rb_check_convert_type_with_id(*file
, T_FILE
, "IO", idTo_io
);
8213 GetOpenFile(tmp
, fptr
);
8214 f
= (HANDLE
)rb_w32_get_osfhandle(fptr
->fd
);
8215 if (f
== (HANDLE
)-1) return INVALID_HANDLE_VALUE
;
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
);
8232 if (f
== INVALID_HANDLE_VALUE
) return f
;
8235 if (GetFileType(f
) == FILE_TYPE_DISK
) {
8237 ZeroMemory(st
, sizeof(*st
));
8238 err
= get_ino(f
, &st
->info
.fii
);
8240 st
->file_id_p
= TRUE
;
8243 else if (err
!= ERROR_INVALID_PARAMETER
) {
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
;
8254 if (ret
) CloseHandle(ret
);
8255 return INVALID_HANDLE_VALUE
;
8259 close_handle(VALUE h
)
8261 CloseHandle((HANDLE
)h
);
8265 struct w32_io_info_args
{
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
);
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
;
8286 struct w32_io_info_args arg
;
8287 arg
.fname
= &fname2
;
8289 f2
= (HANDLE
)rb_ensure(call_w32_io_info
, (VALUE
)&arg
, close_handle
, (VALUE
)f1
);
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
)
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)
8313 rb_w32_set_thread_description(HANDLE th
, const WCHAR
*name
)
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
);
8331 rb_w32_set_thread_description_str(HANDLE th
, VALUE name
)
8333 int idx
, result
= FALSE
;
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
);
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
);
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"
8361 rb_w32_mmap(void *addr
, size_t len
, int prot
, int flags
, int fd
, rb_off_t offset
)
8364 //DWORD protect = 0;
8365 DWORD protect
= PAGE_EXECUTE_READWRITE
;
8367 if (fd
> 0 || offset
) {
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
);
8384 errno
= rb_w32_map_errno(GetLastError());
8392 rb_w32_munmap(void *addr
, size_t len
)
8394 if (!VirtualFree(addr
, 0, MEM_RELEASE
)) {
8395 errno
= rb_w32_map_errno(GetLastError());
8403 rb_w32_mprotect(void *addr
, size_t len
, int prot
)
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());
8419 if (prot
& PROT_EXEC
) {
8420 if (!FlushInstructionCache(GetCurrentProcess(), addr
, len
)) {
8421 errno
= rb_w32_map_errno(GetLastError());