22 #undef __STRICT_ANSI__
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"
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);
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)
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)
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)
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);
139 static FARPROC get_proc_address(
const char *module,
const char *func, HANDLE *mh);
141 #define RUBY_CRITICAL if (0) {} else
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 },
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;
314 memset(&osver, 0,
sizeof(OSVERSIONINFO));
315 osver.dwOSVersionInfoSize =
sizeof(OSVERSIONINFO);
316 GetVersionEx(&osver);
324 return osver.dwPlatformId;
332 return osver.dwMajorVersion;
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
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));
367 LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
370 LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
372 case LOCK_SH|LOCK_NB:
373 LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
375 case LOCK_EX|LOCK_NB:
376 LK_ERR(LockFileEx(fh,
377 LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
378 0, LK_LEN, LK_LEN, &o), i);
381 case LOCK_UN|LOCK_NB:
382 LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
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,
405 static inline WCHAR *
406 translate_wchar(WCHAR *p,
int from,
int to)
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
445 get_special_folder(
int n, WCHAR *buf,
size_t len)
451 if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
452 f = SHGetPathFromIDListEx(pidl, buf,
len, 0);
454 alloc->lpVtbl->Free(alloc, pidl);
455 alloc->lpVtbl->Release(alloc);
462 regulate_path(WCHAR *path)
464 WCHAR *p = translate_wchar(path, L
'\\', L
'/');
465 if (p - path == 2 && path[1] == L
':') {
473 get_proc_address(
const char *module,
const char *func, HANDLE *mh)
479 h = LoadLibrary(module);
481 h = GetModuleHandle(module);
485 ptr = GetProcAddress(h, func);
497 rb_w32_special_folder(
int type)
499 WCHAR path[PATH_MAX];
501 if (!get_special_folder(
type, path, numberof(path)))
return Qnil;
506 #if defined _MSC_VER && _MSC_VER <= 1200
508 #define GetSystemWindowsDirectoryW GetWindowsDirectoryW
513 rb_w32_system_tmpdir(WCHAR *path, UINT
len)
515 static const WCHAR temp[] = L
"temp";
518 if (!get_special_folder(CSIDL_LOCAL_APPDATA, path,
len)) {
519 if (GetSystemWindowsDirectoryW(path,
len))
return 0;
521 p = translate_wchar(path, L
'\\', L
'/');
522 if (*(p - 1) != L
'/') *p++ = L
'/';
523 if ((UINT)(p - path + numberof(temp)) >=
len)
return 0;
524 memcpy(p, temp,
sizeof(temp));
525 return (UINT)(p - path + numberof(temp) - 1);
538 rb_w32_home_dir(
void)
540 WCHAR *buffer = NULL;
541 size_t buffer_len = MAX_PATH,
len = 0;
543 HOME_NONE, ENV_HOME, ENV_USERPROFILE, ENV_DRIVEPATH
544 } home_type = HOME_NONE;
546 if ((
len = GetEnvironmentVariableW(L
"HOME", NULL, 0)) != 0) {
548 home_type = ENV_HOME;
550 else if ((
len = GetEnvironmentVariableW(L
"USERPROFILE", NULL, 0)) != 0) {
552 home_type = ENV_USERPROFILE;
554 else if ((
len = GetEnvironmentVariableW(L
"HOMEDRIVE", NULL, 0)) != 0) {
556 if ((
len = GetEnvironmentVariableW(L
"HOMEPATH", NULL, 0)) != 0) {
558 home_type = ENV_DRIVEPATH;
563 buffer = malloc(
sizeof(WCHAR) * buffer_len);
564 if (buffer == NULL)
return NULL;
568 GetEnvironmentVariableW(L
"HOME", buffer, buffer_len);
570 case ENV_USERPROFILE:
571 GetEnvironmentVariableW(L
"USERPROFILE", buffer, buffer_len);
574 len = GetEnvironmentVariableW(L
"HOMEDRIVE", buffer, buffer_len);
575 GetEnvironmentVariableW(L
"HOMEPATH", buffer +
len, buffer_len -
len);
578 if (!get_special_folder(CSIDL_PROFILE, buffer, buffer_len) &&
579 !get_special_folder(CSIDL_PERSONAL, buffer, buffer_len)) {
583 buffer = realloc(buffer,
sizeof(WCHAR) * (lstrlenW(buffer) + 1));
588 regulate_path(buffer);
599 if (!GetEnvironmentVariableW(L
"HOME", env, numberof(env))) {
600 WCHAR *whome = rb_w32_home_dir();
602 _wputenv_s(L
"HOME", whome);
607 if (!GetEnvironmentVariableW(L
"USER", env, numberof(env))) {
609 if (!GetEnvironmentVariableW(L
"USERNAME", env, numberof(env)) &&
610 !GetUserNameW(env, (
len = numberof(env), &
len))) {
611 NTLoginName =
"<Unknown>";
614 _wputenv_s(L
"USER", env);
615 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
619 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
622 if (!GetEnvironmentVariableW(L
"TMPDIR", env, numberof(env)) &&
623 !GetEnvironmentVariableW(L
"TMP", env, numberof(env)) &&
624 !GetEnvironmentVariableW(L
"TEMP", env, numberof(env)) &&
625 rb_w32_system_tmpdir(env, numberof(env))) {
626 _wputenv_s(L
"TMPDIR", env);
630 static void init_stdhandle(
void);
632 #if RUBY_MSVCRT_VERSION >= 80
635 invalid_parameter(
const wchar_t *expr,
const wchar_t *func,
const wchar_t *file,
unsigned int line, uintptr_t dummy)
640 int ruby_w32_rtc_error;
647 rtc_error_handler(
int e, const
char *src,
int line, const
char *exe, const
char *fmt, ...)
652 if (!ruby_w32_rtc_error)
return 0;
664 static CRITICAL_SECTION select_mutex;
666 static CRITICAL_SECTION socklist_mutex;
669 static CRITICAL_SECTION conlist_mutex;
671 #define conlist_disabled ((st_table *)-1)
673 #define thread_exclusive(obj) \
674 for (bool exclusive_for_##obj = (EnterCriticalSection(&obj##_mutex), true); \
675 exclusive_for_##obj; \
676 exclusive_for_##obj = (LeaveCriticalSection(&obj##_mutex), false))
678 static CRITICAL_SECTION uenvarea_mutex;
679 static char *uenvarea;
684 int state, seq[16], reverse;
689 enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
693 free_conlist(st_data_t key, st_data_t val, st_data_t arg)
701 constat_delete(HANDLE h)
703 thread_exclusive(conlist) {
704 if (conlist && conlist != conlist_disabled) {
705 st_data_t key = (st_data_t)h, val;
706 st_delete(conlist, &key, &val);
717 DeleteCriticalSection(&select_mutex);
718 DeleteCriticalSection(&socklist_mutex);
719 DeleteCriticalSection(&conlist_mutex);
720 thread_exclusive(uenvarea) {
726 DeleteCriticalSection(&uenvarea_mutex);
733 EnterCriticalSection(&socklist_mutex);
735 st_free_table(socklist);
738 LeaveCriticalSection(&socklist_mutex);
740 EnterCriticalSection(&conlist_mutex);
741 if (conlist && conlist != conlist_disabled) {
743 st_free_table(conlist);
746 LeaveCriticalSection(&conlist_mutex);
749 #define ATOMIC_LONG_CAS(var, oldval, newval) InterlockedCompareExchange(&(var), (newval), (oldval))
753 install_vm_exit_handler(
void)
755 static LONG installed = 0;
758 while ((i = ATOMIC_LONG_CAS(installed, 0, -1)) != 1) {
764 ATOMIC_LONG_CAS(installed, -1, 1);
780 version = MAKEWORD(2, 0);
781 if (WSAStartup(version, &retdata))
782 rb_fatal(
"Unable to locate winsock library!");
783 if (LOBYTE(retdata.wVersion) != 2)
784 rb_fatal(
"could not find version 2 of winsock dll");
786 InitializeCriticalSection(&select_mutex);
787 InitializeCriticalSection(&socklist_mutex);
788 InitializeCriticalSection(&conlist_mutex);
790 atexit(exit_handler);
793 #define MAKE_SOCKDATA(af, fl) ((int)((((int)af)<<4)|((fl)&0xFFFF)))
794 #define GET_FAMILY(v) ((int)(((v)>>4)&0xFFFF))
795 #define GET_FLAGS(v) ((int)((v)&0xFFFF))
799 socklist_insert(SOCKET sock,
int flag)
803 thread_exclusive(socklist) {
805 socklist = st_init_numtable();
806 install_vm_exit_handler();
808 ret = st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
816 socklist_lookup(SOCKET sock,
int *flagp)
821 thread_exclusive(socklist) {
822 if (!socklist)
continue;
823 ret = st_lookup(socklist, (st_data_t)sock, &data);
833 socklist_delete(SOCKET *sockp,
int *flagp)
839 thread_exclusive(socklist) {
840 if (!socklist)
continue;
841 key = (st_data_t)*sockp;
843 data = (st_data_t)*flagp;
844 ret = st_delete(socklist, &key, &data);
846 *sockp = (SOCKET)key;
855 #if RUBY_MSVCRT_VERSION >= 80
857 # define _CrtSetReportMode(type,mode) ((void)0)
858 # define _RTC_SetErrorFunc(func) ((void)0)
860 static void set_pioinfo_extra(
void);
862 static int w32_cmdvector(
const WCHAR *,
char ***, UINT,
rb_encoding *);
868 rb_w32_sysinit(
int *argc,
char ***argv)
870 #if RUBY_MSVCRT_VERSION >= 80
872 _CrtSetReportMode(_CRT_ASSERT, 0);
873 _set_invalid_parameter_handler(invalid_parameter);
874 _RTC_SetErrorFunc(rtc_error_handler);
877 SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
884 *argc = w32_cmdvector(GetCommandLineW(), argv, CP_UTF8, &OnigEncodingUTF_8);
892 InitializeCriticalSection(&uenvarea_mutex);
904 return (
char *)NTLoginName;
907 #define MAXCHILDNUM 256
916 #define FOREACH_CHILD(v) do { \
917 struct ChildRecord* v; \
918 for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
919 #define END_FOREACH_CHILD } while (0)
923 FindChildSlot(rb_pid_t pid)
926 FOREACH_CHILD(child) {
927 if (child->pid == pid) {
936 FindChildSlotByHandle(HANDLE h)
939 FOREACH_CHILD(child) {
940 if (child->hProcess == h) {
951 HANDLE h = child->hProcess;
952 child->hProcess = NULL;
959 FindFreeChildSlot(
void)
961 FOREACH_CHILD(child) {
964 child->hProcess = NULL;
978 #define InternalCmdsMax 8
979 static const char szInternalCmds[][InternalCmdsMax+2] = {
1033 internal_match(
const void *key,
const void *elem)
1035 return strncmp(key, ((
const char *)elem) + 1, InternalCmdsMax);
1040 is_command_com(
const char *interp)
1042 int i = strlen(interp) - 11;
1044 if ((i == 0 || (i > 0 && isdirsep(interp[i-1]))) &&
1045 strcasecmp(interp+i,
"command.com") == 0) {
1051 static int internal_cmd_match(
const char *cmdname,
int nt);
1055 is_internal_cmd(
const char *cmd,
int nt)
1057 char cmdname[9], *b = cmdname, c;
1060 if (!(c = *cmd++))
return 0;
1061 }
while (isspace(c));
1064 while (isalpha(c)) {
1066 if (b == cmdname +
sizeof(cmdname))
return 0;
1069 if (c ==
'.') c = *cmd;
1071 case '<':
case '>':
case '|':
1073 case '\0':
case ' ':
case '\t':
case '\n':
1079 return internal_cmd_match(cmdname, nt);
1084 internal_cmd_match(
const char *cmdname,
int nt)
1088 nm = bsearch(cmdname, szInternalCmds,
1089 sizeof(szInternalCmds) /
sizeof(*szInternalCmds),
1090 sizeof(*szInternalCmds),
1092 if (!nm || !(nm[0] & (nt ? 2 : 1)))
1099 rb_w32_get_osfhandle(
int fh)
1101 return _get_osfhandle(fh);
1106 join_argv(
char *cmd,
char *
const *argv, BOOL escape, UINT cp,
int backslash)
1110 int len, n, bs, quote;
1112 for (t = argv, q = cmd,
len = 0; (p = *t) != 0; t++) {
1115 if (!*p || strpbrk(p,
" \t\"'")) {
1120 for (bs = 0; *p; ++p) {
1134 memset(q,
'\\', bs);
1139 case '<':
case '>':
case '|':
case '^':
1140 if (escape && !quote) {
1141 len += (n = p - s) + 1;
1152 p = CharNextExA(cp, p, 0) - 1;
1156 len += (n = p - s) + 1;
1160 if (backslash > 0) {
1163 translate_char(q,
'/',
'\\', cp);
1166 if (quote) *q++ =
'"';
1179 #define STRNDUPV(ptr, v, src, len) \
1180 (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
1184 check_spawn_mode(
int mode)
1206 if (mode == P_OVERLAY) {
1207 WaitForSingleObject(child->hProcess, INFINITE);
1208 GetExitCodeProcess(child->hProcess, &exitcode);
1209 CloseChildHandle(child);
1217 CreateChild(
struct ChildRecord *child,
const WCHAR *cmd,
const WCHAR *prog, HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
1220 STARTUPINFOW aStartupInfo;
1221 PROCESS_INFORMATION aProcessInformation;
1222 SECURITY_ATTRIBUTES sa;
1224 if (!cmd && !prog) {
1234 sa.nLength =
sizeof(SECURITY_ATTRIBUTES);
1235 sa.lpSecurityDescriptor = NULL;
1236 sa.bInheritHandle = TRUE;
1238 memset(&aStartupInfo, 0,
sizeof(aStartupInfo));
1239 memset(&aProcessInformation, 0,
sizeof(aProcessInformation));
1240 aStartupInfo.cb =
sizeof(aStartupInfo);
1241 aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
1243 aStartupInfo.hStdInput = hInput;
1246 aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1249 aStartupInfo.hStdOutput = hOutput;
1252 aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1255 aStartupInfo.hStdError = hError;
1258 aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1261 dwCreationFlags |= NORMAL_PRIORITY_CLASS;
1263 if (lstrlenW(cmd) > 32767) {
1270 fRet = CreateProcessW(prog, (WCHAR *)cmd, &sa, &sa,
1271 sa.bInheritHandle, dwCreationFlags, NULL, NULL,
1272 &aStartupInfo, &aProcessInformation);
1273 errno = map_errno(GetLastError());
1281 CloseHandle(aProcessInformation.hThread);
1283 child->hProcess = aProcessInformation.hProcess;
1284 child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
1291 is_batch(
const char *cmd)
1293 int len = strlen(cmd);
1294 if (
len <= 4)
return 0;
1296 if (*cmd++ !=
'.')
return 0;
1297 if (strcasecmp(cmd,
"bat") == 0)
return 1;
1298 if (strcasecmp(cmd,
"cmd") == 0)
return 1;
1302 #define filecp rb_w32_filecp
1303 #define mbstr_to_wstr rb_w32_mbstr_to_wstr
1304 #define wstr_to_mbstr rb_w32_wstr_to_mbstr
1305 #define acp_to_wstr(str, plen) mbstr_to_wstr(CP_ACP, str, -1, plen)
1306 #define wstr_to_acp(str, plen) wstr_to_mbstr(CP_ACP, str, -1, plen)
1307 #define filecp_to_wstr(str, plen) mbstr_to_wstr(filecp(), str, -1, plen)
1308 #define wstr_to_filecp(str, plen) wstr_to_mbstr(filecp(), str, -1, plen)
1309 #define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
1310 #define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
1314 rb_w32_start_process(
const char *abspath,
char *
const *argv,
int out_fd)
1323 WCHAR *wcmd = NULL, *wprog = NULL;
1324 HANDLE outHandle = NULL;
1327 outHandle = (HANDLE)rb_w32_get_osfhandle(out_fd);
1330 len = join_argv(NULL, argv, FALSE, filecp(), 1);
1331 cmd = alloca(
sizeof(
char) *
len);
1332 join_argv(cmd, argv, FALSE, filecp(), 1);
1334 if (!(wcmd = mbstr_to_wstr(filecp(), cmd, -1, NULL))) {
1338 if (!(wprog = mbstr_to_wstr(filecp(), abspath, -1, NULL))) {
1343 if (!CreateChild(&child, wcmd, wprog, NULL, outHandle, outHandle, 0)) {
1349 return child.hProcess;
1354 w32_spawn(
int mode,
const char *cmd,
const char *prog, UINT cp)
1356 char fbuf[PATH_MAX];
1358 const char *shell = NULL;
1359 WCHAR *wcmd = NULL, *wshell = NULL;
1365 char *cmd_sep = NULL;
1367 if (check_spawn_mode(mode))
return -1;
1370 if (!(p = dln_find_exe_r(prog, NULL, fbuf,
sizeof(fbuf)))) {
1375 translate_char(p,
'/',
'\\', cp);
1382 if ((shell = w32_getenv(
"RUBYSHELL", cp)) && (redir = has_redirection(cmd, cp))) {
1383 size_t shell_len = strlen(shell);
1384 size_t cmd_len = strlen(cmd) +
sizeof(
" -c ") + 2;
1385 char *tmp =
ALLOCV(v, shell_len + cmd_len);
1386 memcpy(tmp, shell, shell_len + 1);
1387 translate_char(tmp,
'/',
'\\', cp);
1388 snprintf(tmp + shell_len, cmd_len,
" -c \"%s\"", cmd);
1391 else if ((shell = w32_getenv(
"COMSPEC", cp)) &&
1392 (nt = !is_command_com(shell),
1393 (redir < 0 ? has_redirection(cmd, cp) : redir) ||
1394 is_internal_cmd(cmd, nt))) {
1395 size_t cmd_len = strlen(shell) + strlen(cmd) +
sizeof(
" /c ") + (nt ? 2 : 0);
1396 char *tmp =
ALLOCV(v, cmd_len);
1397 snprintf(tmp, cmd_len, nt ?
"%s /c \"%s\"" :
"%s /c %s", shell, cmd);
1401 int len = 0, quote = (*cmd ==
'"') ?
'"' : (*cmd ==
'\'') ?
'\'' : 0;
1403 for (prog = cmd + !!quote;; prog = CharNextExA(cp, prog, 0)) {
1404 if (*prog ==
'/') slash = 1;
1408 STRNDUPV(p, v2, cmd,
len);
1414 if ((
unsigned char)*prog == quote) {
1415 len = prog++ - cmd - 1;
1416 STRNDUPV(p, v2, cmd + 1,
len);
1420 if (quote)
continue;
1421 if (
ISSPACE(*prog) || strchr(
"<>|*?\"", *prog)) {
1423 STRNDUPV(p, v2, cmd,
len + (slash ? strlen(prog) : 0));
1426 sep = *(cmd_sep = &p[
len]);
1433 shell = dln_find_exe_r(shell, NULL, fbuf,
sizeof(fbuf));
1434 if (p && slash) translate_char(p,
'/',
'\\', cp);
1436 shell = p ? p : cmd;
1439 len = strlen(shell);
1440 if (strchr(shell,
' ')) quote = -1;
1441 if (shell == fbuf) {
1444 else if (shell != p && strchr(shell,
'/')) {
1445 STRNDUPV(p, v2, shell,
len);
1448 if (p) translate_char(p,
'/',
'\\', cp);
1449 if (is_batch(shell)) {
1450 int alen = strlen(prog);
1451 cmd = p =
ALLOCV(v,
len + alen + (quote ? 2 : 0) + 1);
1452 if (quote) *p++ =
'"';
1453 memcpy(p, shell,
len);
1455 if (quote) *p++ =
'"';
1456 memcpy(p, prog, alen + 1);
1463 if (!e && shell && !(wshell = mbstr_to_wstr(cp, shell, -1, NULL))) e = E2BIG;
1464 if (cmd_sep) *cmd_sep = sep;
1465 if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1471 if (CreateChild(child, wcmd, wshell, NULL, NULL, NULL, 0)) {
1472 ret = child_result(child, mode);
1483 rb_w32_spawn(
int mode,
const char *cmd,
const char *prog)
1486 return w32_spawn(mode, cmd, prog, filecp());
1491 rb_w32_uspawn(
int mode,
const char *cmd,
const char *prog)
1493 return w32_spawn(mode, cmd, prog, CP_UTF8);
1498 w32_spawn_process(
int mode,
const char *prog,
char *
const *argv,
1499 int in_fd,
int out_fd,
int err_fd, DWORD flags, UINT cp)
1503 BOOL ntcmd = FALSE, tmpnt;
1505 char *cmd, fbuf[PATH_MAX];
1506 WCHAR *wcmd = NULL, *wprog = NULL;
1510 HANDLE in_handle = NULL, out_handle = NULL, err_handle = NULL;
1512 if (check_spawn_mode(mode))
return -1;
1515 in_handle = (HANDLE)rb_w32_get_osfhandle(in_fd);
1518 out_handle = (HANDLE)rb_w32_get_osfhandle(out_fd);
1521 err_handle = (HANDLE)rb_w32_get_osfhandle(err_fd);
1524 if (!prog) prog = argv[0];
1525 if ((shell = w32_getenv(
"COMSPEC", cp)) &&
1526 internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
1531 else if ((cmd = dln_find_exe_r(prog, NULL, fbuf,
sizeof(fbuf)))) {
1532 if (cmd == prog) strlcpy(cmd = fbuf, prog,
sizeof(fbuf));
1533 translate_char(cmd,
'/',
'\\', cp);
1536 else if (strchr(prog,
'/')) {
1538 if (
len <
sizeof(fbuf))
1539 strlcpy(cmd = fbuf, prog,
sizeof(fbuf));
1541 STRNDUPV(cmd, v, prog,
len);
1542 translate_char(cmd,
'/',
'\\', cp);
1545 if (c_switch || is_batch(prog)) {
1547 progs[0] = (
char *)prog;
1549 len = join_argv(NULL, progs, ntcmd, cp, 1);
1550 if (c_switch)
len += 3;
1552 if (argv[0])
len += join_argv(NULL, argv, ntcmd, cp, 0);
1554 join_argv(cmd, progs, ntcmd, cp, 1);
1555 if (c_switch) strlcat(cmd,
" /c",
len);
1556 if (argv[0]) join_argv(cmd + strlcat(cmd,
" ",
len), argv, ntcmd, cp, 0);
1557 prog = c_switch ? shell : 0;
1560 len = join_argv(NULL, argv, FALSE, cp, 1);
1562 join_argv(cmd, argv, FALSE, cp, 1);
1565 if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1567 if (!e && prog && !(wprog = mbstr_to_wstr(cp, prog, -1, NULL))) e = E2BIG;
1571 if (CreateChild(child, wcmd, wprog, in_handle, out_handle, err_handle, flags)) {
1572 ret = child_result(child, mode);
1583 rb_w32_aspawn_flags(
int mode,
const char *prog,
char *
const *argv, DWORD flags)
1586 return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, filecp());
1591 rb_w32_uaspawn_flags(
int mode,
const char *prog,
char *
const *argv, DWORD flags)
1593 return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, CP_UTF8);
1598 rb_w32_aspawn(
int mode,
const char *prog,
char *
const *argv)
1600 return w32_spawn_process(mode, prog, argv, -1, -1, -1, 0, filecp());
1605 rb_w32_uaspawn(
int mode,
const char *prog,
char *
const *argv)
1607 return rb_w32_uaspawn_flags(mode, prog, argv, 0);
1612 rb_w32_uspawn_process(
int mode,
const char *prog,
char *
const *argv,
1613 int in_fd,
int out_fd,
int err_fd, DWORD flags)
1615 return w32_spawn_process(mode, prog, argv, in_fd, out_fd, err_fd,
1632 #define NTMALLOC 0x2
1633 #define NTSTRING 0x4
1637 insert(
const char *path,
VALUE vinfo,
void *enc)
1643 if (!tmpcurr)
return -1;
1645 tmpcurr->len = strlen(path);
1646 tmpcurr->str =
strdup(path);
1647 if (!tmpcurr->str)
return -1;
1648 tmpcurr->flags |= NTMALLOC;
1650 *tail = &tmpcurr->next;
1659 char buffer[PATH_MAX], *buf = buffer;
1663 if (patt->len >= PATH_MAX)
1664 if (!(buf = malloc(patt->len + 1)))
return 0;
1666 memcpy(buf, patt->str, patt->len);
1667 buf[patt->len] =
'\0';
1668 translate_char(buf,
'\\',
'/', cp);
1669 status = ruby_brace_glob_with_enc(buf, 0, insert, (
VALUE)&tail, enc);
1673 if (status || last == tail)
return 0;
1674 if (patt->flags & NTMALLOC)
1687 has_redirection(
const char *cmd, UINT cp)
1703 else if (quote == *
ptr)
1721 if (*
ptr++ ==
'%')
return TRUE;
1727 ptr = CharNextExA(cp,
ptr, 0);
1735 static inline WCHAR *
1736 skipspace(WCHAR *
ptr)
1745 w32_cmdvector(
const WCHAR *cmd,
char ***vec, UINT cp,
rb_encoding *enc)
1748 int elements, strsz, done;
1749 int slashes, escape;
1750 WCHAR *
ptr, *base, *cmdline;
1751 char *cptr, *buffer;
1767 ptr = cmdline = wcsdup(cmd);
1778 while (*(
ptr = skipspace(
ptr))) {
1780 quote = slashes = globbing = escape = 0;
1781 for (done = 0; !done && *
ptr; ) {
1790 if (quote != L
'\'') slashes++;
1830 if (!(slashes & 1)) {
1833 else if (quote == *
ptr) {
1834 if (quote == L
'"' && quote ==
ptr[1])
1867 slashes = quote = 0;
1868 while (p < base +
len) {
1872 if (quote != L
'\'') slashes++;
1877 if (!(slashes & 1) && quote && quote != c) {
1882 memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
1883 sizeof(WCHAR) * (base +
len - p));
1884 len -= ((slashes + 1) >> 1) + (~slashes & 1);
1885 p -= (slashes + 1) >> 1;
1886 if (!(slashes & 1)) {
1888 if (quote == L
'"' && quote == *p)
1909 if (!curr)
goto do_nothing;
1910 curr->str = rb_w32_wstr_to_mbstr(cp, base,
len, &curr->len);
1911 curr->flags |= NTMALLOC;
1913 if (globbing && (tail = cmdglob(curr, cmdtail, cp, enc))) {
1918 cmdtail = &curr->next;
1928 for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
1930 strsz += (curr->len + 1);
1933 len = (elements+1)*
sizeof(
char *) + strsz;
1934 buffer = (
char *)malloc(
len);
1937 while ((curr = cmdhead) != 0) {
1938 cmdhead = curr->next;
1939 if (curr->flags & NTMALLOC) free(curr->str);
1943 for (vptr = *vec; *vptr; ++vptr);
1959 vptr = (
char **) buffer;
1961 cptr = buffer + (elements+1) *
sizeof(
char *);
1963 while ((curr = cmdhead) != 0) {
1964 memcpy(cptr, curr->str, curr->len);
1965 cptr[curr->len] =
'\0';
1967 cptr += curr->len + 1;
1968 cmdhead = curr->next;
1969 if (curr->flags & NTMALLOC) free(curr->str);
1974 *vec = (
char **) buffer;
1986 open_special(
const WCHAR *path, DWORD access, DWORD flags)
1988 const DWORD share_mode =
1989 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
1990 return CreateFileW(path, access, share_mode, NULL, OPEN_EXISTING,
1991 FILE_FLAG_BACKUP_SEMANTICS|flags, NULL);
2001 #define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] & (1 << (i) % CHAR_BIT))
2002 #define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
2004 #define BitOfIsDir(n) ((n) * 2)
2005 #define BitOfIsRep(n) ((n) * 2 + 1)
2006 #define DIRENT_PER_CHAR (CHAR_BIT / 2)
2008 static const WCHAR namespace_prefix[] = {L
'\\', L
'\\', L
'?', L
'\\'};
2010 enum {FINAL_PATH_MAX = PATH_MAX + numberof(namespace_prefix)};
2014 open_dir_handle(
const WCHAR *filename, WIN32_FIND_DATAW *fd)
2025 fh = open_special(filename, 0, 0);
2026 if (fh != INVALID_HANDLE_VALUE) {
2027 len = GetFinalPathNameByHandleW(fh, fullname, FINAL_PATH_MAX, 0);
2029 if (
len >= FINAL_PATH_MAX) {
2030 errno = ENAMETOOLONG;
2031 return INVALID_HANDLE_VALUE;
2035 len = lstrlenW(filename);
2036 if (
len >= PATH_MAX) {
2037 errno = ENAMETOOLONG;
2038 return INVALID_HANDLE_VALUE;
2042 p = &fullname[
len-1];
2043 if (!(isdirsep(*p) || *p == L
':')) *++p = L
'\\';
2050 fh = FindFirstFileW(fullname, fd);
2051 if (fh == INVALID_HANDLE_VALUE) {
2052 errno = map_errno(GetLastError());
2059 w32_wopendir(
const WCHAR *wpath)
2062 WIN32_FIND_DATAW fd;
2075 if (wstati128(wpath, &sbuf, FALSE) < 0) {
2078 if (!(sbuf.st_mode & S_IFDIR) &&
2079 (!
ISALPHA(wpath[0]) || wpath[1] != L
':' || wpath[2] != L
'\0' ||
2080 ((1 << ((wpath[0] & 0x5f) -
'A')) & GetLogicalDrives()) == 0)) {
2084 fh = open_dir_handle(wpath, &fd);
2085 if (fh == INVALID_HANDLE_VALUE) {
2092 p = calloc(1,
sizeof(
DIR));
2096 pathlen = lstrlenW(wpath);
2106 len = lstrlenW(fd.cFileName) + 1;
2107 altlen = lstrlenW(fd.cAlternateFileName) + 1;
2113 tmpW = realloc(p->start, (idx +
len + altlen) *
sizeof(WCHAR));
2123 memcpy(&p->start[idx], fd.cFileName,
len *
sizeof(WCHAR));
2124 memcpy(&p->start[idx +
len], fd.cAlternateFileName, altlen *
sizeof(WCHAR));
2126 if (p->nfiles % DIRENT_PER_CHAR == 0) {
2127 tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
2131 p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
2133 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2134 SetBit(p->bits, BitOfIsDir(p->nfiles));
2135 if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2136 WCHAR *tmppath = malloc((pathlen +
len + 1) *
sizeof(WCHAR));
2137 memcpy(tmppath, wpath, pathlen *
sizeof(WCHAR));
2138 tmppath[pathlen] = L
'\\';
2139 memcpy(tmppath + pathlen + 1, fd.cFileName,
len *
sizeof(WCHAR));
2140 if (rb_w32_reparse_symlink_p(tmppath))
2141 SetBit(p->bits, BitOfIsRep(p->nfiles));
2146 idx +=
len + altlen;
2147 }
while (FindNextFileW(fh, &fd));
2158 UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
2164 rb_w32_wstr_to_mbstr(UINT cp,
const WCHAR *wstr,
int clen,
long *plen)
2167 int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL);
2168 if (!(
ptr = malloc(
len)))
return 0;
2169 WideCharToMultiByte(cp, 0, wstr, clen,
ptr,
len, NULL, NULL);
2172 if (clen == -1) --
len;
2180 rb_w32_mbstr_to_wstr(UINT cp,
const char *str,
int clen,
long *plen)
2184 int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0);
2185 if (!(
ptr = malloc(
sizeof(WCHAR) *
len)))
return 0;
2186 MultiByteToWideChar(cp, 0, str, clen,
ptr,
len);
2189 if (clen == -1) --
len;
2197 rb_w32_opendir(
const char *filename)
2200 WCHAR *wpath = filecp_to_wstr(filename, NULL);
2203 ret = w32_wopendir(wpath);
2210 rb_w32_uopendir(
const char *filename)
2213 WCHAR *wpath = utf8_to_wstr(filename, NULL);
2216 ret = w32_wopendir(wpath);
2227 move_to_next_entry(
DIR *dirp)
2231 dirp->curr += lstrlenW(dirp->curr) + 1;
2232 dirp->curr += lstrlenW(dirp->curr) + 1;
2233 if (dirp->curr >= (dirp->start + dirp->size)) {
2245 win32_direct_conv(
const WCHAR *file,
const WCHAR *alt,
struct direct *entry,
const void *enc)
2247 UINT cp = *((UINT *)enc);
2248 if (!(entry->d_name = wstr_to_mbstr(cp, file, -1, &entry->d_namlen)))
2252 entry->d_altname = wstr_to_mbstr(cp, alt, -1, &altlen);
2253 entry->d_altlen = altlen;
2260 rb_w32_conv_from_wchar(
const WCHAR *wstr,
rb_encoding *enc)
2263 long len = lstrlenW(wstr);
2266 if (encindex == ENCINDEX_UTF_16LE) {
2270 #if SIZEOF_INT < SIZEOF_LONG
2271 # error long should equal to int on Windows
2274 len = WideCharToMultiByte(CP_UTF8, 0, wstr, clen, NULL, 0, NULL, NULL);
2276 WideCharToMultiByte(CP_UTF8, 0, wstr, clen,
RSTRING_PTR(src),
len, NULL, NULL);
2279 case ENCINDEX_ASCII_8BIT:
2280 case ENCINDEX_US_ASCII:
2282 case ENCINDEX_UTF_8:
2291 rb_w32_conv_from_wstr(
const WCHAR *wstr,
long *lenp,
rb_encoding *enc)
2293 VALUE str = rb_w32_conv_from_wchar(wstr, enc);
2297 if (
NIL_P(str))
return wstr_to_utf8(wstr, lenp);
2306 ruby_direct_conv(
const WCHAR *file,
const WCHAR *alt,
struct direct *entry,
const void *enc)
2308 if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
2312 entry->d_altname = rb_w32_conv_from_wstr(alt, &altlen, enc);
2313 entry->d_altlen = altlen;
2320 readdir_internal(
DIR *dirp, BOOL (*conv)(
const WCHAR *,
const WCHAR *,
struct direct *,
const void *),
const void *enc)
2322 static long dummy_ino = 0;
2329 free(dirp->dirstr.d_name);
2330 free(dirp->dirstr.d_altname);
2331 dirp->dirstr.d_altname = 0;
2332 dirp->dirstr.d_altlen = 0;
2333 conv(dirp->curr, dirp->curr + lstrlenW(dirp->curr) + 1, &dirp->dirstr, enc);
2338 dirp->dirstr.d_ino = (ino_t)(InterlockedIncrement(&dummy_ino) - 1);
2344 if (GetBit(dirp->bits, BitOfIsRep(dirp->loc)))
2345 dirp->dirstr.d_type = DT_LNK;
2346 else if (GetBit(dirp->bits, BitOfIsDir(dirp->loc)))
2347 dirp->dirstr.d_type = DT_DIR;
2349 dirp->dirstr.d_type = DT_REG;
2355 move_to_next_entry(dirp);
2357 return &(dirp->dirstr);
2369 if (idx == ENCINDEX_ASCII_8BIT) {
2370 const UINT cp = filecp();
2371 return readdir_internal(dirp, win32_direct_conv, &cp);
2373 else if (idx == ENCINDEX_UTF_8) {
2374 const UINT cp = CP_UTF8;
2375 return readdir_internal(dirp, win32_direct_conv, &cp);
2378 return readdir_internal(dirp, ruby_direct_conv, enc);
2383 rb_w32_ureaddir(
DIR *dirp)
2385 const UINT cp = CP_UTF8;
2386 return readdir_internal(dirp, win32_direct_conv, &cp);
2395 rb_w32_telldir(
DIR *dirp)
2406 rb_w32_seekdir(
DIR *dirp,
long loc)
2408 if (dirp->loc > loc) rb_w32_rewinddir(dirp);
2410 while (dirp->curr && dirp->loc < loc) {
2411 move_to_next_entry(dirp);
2421 rb_w32_rewinddir(
DIR *dirp)
2423 dirp->curr = dirp->start;
2433 rb_w32_closedir(
DIR *dirp)
2436 free(dirp->dirstr.d_name);
2437 free(dirp->dirstr.d_altname);
2445 #if RUBY_MSVCRT_VERSION >= 140
2460 CRITICAL_SECTION _lock;
2462 #define FILE_COUNT(stream) ((vcruntime_file*)stream)->_cnt
2463 #define FILE_READPTR(stream) ((vcruntime_file*)stream)->_ptr
2464 #define FILE_FILENO(stream) ((vcruntime_file*)stream)->_file
2466 #define FILE_COUNT(stream) stream->_cnt
2467 #define FILE_READPTR(stream) stream->_ptr
2468 #define FILE_FILENO(stream) stream->_file
2472 #if RUBY_MSVCRT_VERSION >= 140
2473 typedef char lowio_text_mode;
2474 typedef char lowio_pipe_lookahead[3];
2477 CRITICAL_SECTION lock;
2480 unsigned char osfile;
2481 lowio_text_mode textmode;
2482 lowio_pipe_lookahead _pipe_lookahead;
2484 uint8_t unicode : 1;
2485 uint8_t utf8translations : 1;
2486 uint8_t dbcsBufferUsed : 1;
2495 CRITICAL_SECTION lock;
2496 #if RUBY_MSVCRT_VERSION >= 80
2503 #if !defined _CRTIMP || defined __MINGW32__
2505 #define _CRTIMP __declspec(dllimport)
2508 #if RUBY_MSVCRT_VERSION >= 140
2509 static ioinfo ** __pioinfo = NULL;
2510 #define IOINFO_L2E 6
2512 EXTERN_C _CRTIMP
ioinfo * __pioinfo[];
2513 #define IOINFO_L2E 5
2515 static inline ioinfo* _pioinfo(
int);
2518 #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
2519 #define _osfhnd(i) (_pioinfo(i)->osfhnd)
2520 #define _osfile(i) (_pioinfo(i)->osfile)
2521 #define rb_acrt_lowio_lock_fh(i) EnterCriticalSection(&_pioinfo(i)->lock)
2522 #define rb_acrt_lowio_unlock_fh(i) LeaveCriticalSection(&_pioinfo(i)->lock)
2524 #if RUBY_MSVCRT_VERSION >= 80
2525 static size_t pioinfo_extra = 0;
2529 set_pioinfo_extra(
void)
2531 #if RUBY_MSVCRT_VERSION >= 140
2532 # define FUNCTION_RET 0xc3
2534 # define UCRTBASE "ucrtbased.dll"
2536 # define UCRTBASE "ucrtbase.dll"
2551 char *p = (
char*)get_proc_address(UCRTBASE,
"_isatty", NULL);
2554 #if defined(_M_ARM64) || defined(__aarch64__)
2555 #define IS_INSN(pc, name) ((*(pc) & name##_mask) == name##_id)
2556 const int max_num_inst = 500;
2557 uint32_t *start = (uint32_t*)p;
2558 uint32_t *end_limit = (start + max_num_inst);
2559 uint32_t *pc = start;
2562 fprintf(stderr,
"_isatty proc not found in " UCRTBASE
"\n");
2567 const uint32_t ret_id = 0xd65f0000;
2568 const uint32_t ret_mask = 0xfffffc1f;
2569 for(; pc < end_limit; pc++) {
2570 if (IS_INSN(pc, ret)) {
2574 if (pc == end_limit) {
2575 fprintf(stderr,
"end of _isatty not found in " UCRTBASE
"\n");
2580 const uint32_t adrp_id = 0x90000000;
2581 const uint32_t adrp_mask = 0x9f000000;
2582 const uint32_t add_id = 0x11000000;
2583 const uint32_t add_mask = 0x3fc00000;
2584 for(; pc > start; pc--) {
2585 if (IS_INSN(pc, adrp) && IS_INSN(pc + 1, add)) {
2590 fprintf(stderr,
"pioinfo mark not found in " UCRTBASE
"\n");
2600 const uint32_t adrp_insn = *pc;
2601 const uint32_t adrp_immhi = (adrp_insn & 0x00ffffe0) >> 5;
2602 const uint32_t adrp_immlo = (adrp_insn & 0x60000000) >> (5 + 19 + 5);
2604 const uint64_t adrp_imm = ((adrp_immhi << 2) | adrp_immlo) << 12;
2606 const uint64_t adrp_base = (uint64_t)pc & 0xfffffffffffff000;
2608 const uint32_t add_insn = *(pc + 1);
2609 const uint32_t add_sh = (add_insn & 0x400000) >> (12 + 5 + 5);
2613 const uint64_t add_imm = ((add_insn & 0x3ffc00) >> (5 + 5)) << (add_sh ? 12 : 0);
2615 __pioinfo = (
ioinfo**)(adrp_base + adrp_imm + add_imm);
2623 # define FUNCTION_BEFORE_RET_MARK "\x48\x83\xc4"
2624 # define FUNCTION_SKIP_BYTES 1
2627 # define PIOINFO_MARK "\x48\x8d\x0d"
2630 # define PIOINFO_MARK "\x48\x8d\x15"
2635 # define FUNCTION_BEFORE_RET_MARK "\x5d"
2637 # define FUNCTION_BEFORE_RET_MARK_2 "\xc9"
2638 # define FUNCTION_SKIP_BYTES 0
2640 # define PIOINFO_MARK "\x8B\x04\x85"
2643 for (pend += 10; pend < p + 500; pend++) {
2645 if ((memcmp(pend, FUNCTION_BEFORE_RET_MARK,
sizeof(FUNCTION_BEFORE_RET_MARK) - 1) == 0
2646 # ifdef FUNCTION_BEFORE_RET_MARK_2
2647 || memcmp(pend, FUNCTION_BEFORE_RET_MARK_2,
sizeof(FUNCTION_BEFORE_RET_MARK_2) - 1) == 0
2650 *(pend + (
sizeof(FUNCTION_BEFORE_RET_MARK) - 1) + FUNCTION_SKIP_BYTES) == (
char)FUNCTION_RET) {
2652 for (pend -= (
sizeof(PIOINFO_MARK) - 1); pend > p; pend--) {
2653 if (memcmp(pend, PIOINFO_MARK,
sizeof(PIOINFO_MARK) - 1) == 0) {
2662 fprintf(stderr,
"unexpected " UCRTBASE
"\n");
2666 p +=
sizeof(PIOINFO_MARK) - 1;
2668 rel = *(int32_t*)(p);
2669 rip = p +
sizeof(int32_t);
2670 __pioinfo = (
ioinfo**)(rip + rel);
2672 __pioinfo = *(
ioinfo***)(p);
2678 fd = _open(
"NUL", O_RDONLY);
2679 for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra +=
sizeof(
void *)) {
2680 if (_osfhnd(fd) == _get_osfhandle(fd)) {
2686 if (pioinfo_extra > 64) {
2692 #define pioinfo_extra 0
2698 const size_t sizeof_ioinfo =
sizeof(
ioinfo) + pioinfo_extra;
2699 return (
ioinfo*)((
char*)__pioinfo[fd >> IOINFO_L2E] +
2700 (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
2703 #define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2704 #define _set_osflags(fh, flags) (_osfile(fh) = (flags))
2707 #define FEOFLAG 0x02
2709 #define FNOINHERIT 0x10
2710 #define FAPPEND 0x20
2714 static int is_socket(SOCKET);
2715 static int is_console(SOCKET);
2719 rb_w32_io_cancelable_p(
int fd)
2721 return is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd));
2726 rb_w32_open_osfhandle(intptr_t osfhandle,
int flags)
2735 if (flags & O_APPEND)
2736 fileflags |= FAPPEND;
2741 if (flags & O_NOINHERIT)
2742 fileflags |= FNOINHERIT;
2745 hF = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2746 fh = _open_osfhandle((intptr_t)hF, 0);
2754 rb_acrt_lowio_lock_fh(fh);
2756 _set_osfhnd(fh, osfhandle);
2760 _set_osflags(fh, fileflags);
2761 rb_acrt_lowio_unlock_fh(fh);
2768 init_stdhandle(
void)
2772 #define open_null(fd) \
2774 (nullfd = open("NUL", O_RDWR)) : 0), \
2775 ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)), \
2778 if (fileno(stdin) < 0) {
2779 FILE_FILENO(stdin) = open_null(0);
2782 setmode(fileno(stdin), O_BINARY);
2784 if (fileno(stdout) < 0) {
2785 FILE_FILENO(stdout) = open_null(1);
2787 if (fileno(stderr) < 0) {
2788 FILE_FILENO(stderr) = open_null(2);
2790 if (nullfd >= 0 && !keep) close(nullfd);
2791 setvbuf(stderr, NULL, _IONBF, 0);
2794 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
2796 if (GetConsoleMode(h, &m)) {
2797 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
2798 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
2800 SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
2809 is_socket(SOCKET sock)
2811 if (socklist_lookup(sock, NULL))
2819 rb_w32_is_socket(
int fd)
2821 return is_socket(TO_SOCKET(fd));
2834 rb_w32_strerror(
int e)
2836 static char buffer[512];
2840 if (e < 0 || e > sys_nerr) {
2843 #if WSAEWOULDBLOCK != EWOULDBLOCK
2844 else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
2848 for (s = 0; s < (int)(
sizeof(errmap)/
sizeof(*errmap)); s++)
2849 if (errmap[s].winerr == WSAEWOULDBLOCK)
2851 for (i = s; i < (int)(
sizeof(errmap)/
sizeof(*errmap)); i++)
2852 if (errmap[i].err == e) {
2853 e = errmap[i].winerr;
2858 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2859 FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
2860 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
2861 buffer,
sizeof(buffer), NULL) == 0 &&
2862 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2863 FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
2864 buffer,
sizeof(buffer), NULL) == 0)
2865 strlcpy(buffer,
"Unknown Error",
sizeof(buffer));
2868 strlcpy(buffer, strerror(e),
sizeof(buffer));
2871 while ((p = strpbrk(p,
"\r\n")) != NULL) {
2872 memmove(p, p + 1, strlen(p));
2921 setuid(rb_uid_t uid)
2923 return (uid == ROOT_UID ? 0 : -1);
2928 setgid(rb_gid_t gid)
2930 return (gid == ROOT_GID ? 0 : -1);
2939 ioctl(
int i,
int u, ...)
2946 rb_w32_fdset(
int fd, fd_set *set)
2955 rb_w32_fdclr(
int fd, fd_set *set)
2958 SOCKET s = TO_SOCKET(fd);
2960 for (i = 0; i < set->fd_count; i++) {
2961 if (set->fd_array[i] == s) {
2962 memmove(&set->fd_array[i], &set->fd_array[i+1],
2963 sizeof(set->fd_array[0]) * (--set->fd_count - i));
2973 rb_w32_fdisset(
int fd, fd_set *set)
2976 SOCKET s = TO_SOCKET(fd);
2977 if (s == (SOCKET)INVALID_HANDLE_VALUE)
2979 RUBY_CRITICAL {ret = __WSAFDIsSet(s, set);}
2987 max = min(src->fd_count, (UINT)max);
2988 if ((UINT)dst->
capa < (UINT)max) {
2989 dst->
capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2993 memcpy(dst->
fdset->fd_array, src->fd_array,
2994 max *
sizeof(src->fd_array[0]));
2995 dst->
fdset->fd_count = src->fd_count;
3002 if ((UINT)dst->
capa < src->
fdset->fd_count) {
3003 dst->
capa = (src->
fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
3007 memcpy(dst->
fdset->fd_array, src->
fdset->fd_array,
3008 src->
fdset->fd_count *
sizeof(src->
fdset->fd_array[0]));
3022 extract_fd(
rb_fdset_t *dst, fd_set *src,
int (*func)(SOCKET))
3028 while (s < src->fd_count) {
3029 SOCKET fd = src->fd_array[s];
3031 if (!func || (*func)(fd)) {
3035 for (d = 0; d < dst->
fdset->fd_count; d++) {
3036 if (dst->
fdset->fd_array[d] == fd)
3039 if (d == dst->
fdset->fd_count) {
3040 if ((
int)dst->
fdset->fd_count >= dst->
capa) {
3041 dst->
capa = (dst->
fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
3044 dst->
fdset->fd_array[dst->
fdset->fd_count++] = fd;
3048 &src->fd_array[s+1],
3049 sizeof(src->fd_array[0]) * (--src->fd_count - s));
3059 return dst ? dst->
fdset->fd_count : m;
3064 copy_fd(fd_set *dst, fd_set *src)
3067 if (!src || !dst)
return 0;
3069 for (s = 0; s < src->fd_count; ++s) {
3070 SOCKET fd = src->fd_array[s];
3072 for (d = 0; d < dst->fd_count; ++d) {
3073 if (dst->fd_array[d] == fd)
3076 if (d == dst->fd_count && d < FD_SETSIZE) {
3077 dst->fd_array[dst->fd_count++] = fd;
3081 return dst->fd_count;
3086 is_not_socket(SOCKET sock)
3088 return !is_socket(sock);
3093 is_pipe(SOCKET sock)
3098 ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
3106 is_readable_pipe(SOCKET sock)
3112 if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
3116 ret = (GetLastError() == ERROR_BROKEN_PIPE);
3125 is_console(SOCKET sock)
3132 ret = (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n));
3140 is_readable_console(SOCKET sock)
3147 if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) {
3148 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
3149 ir.Event.KeyEvent.uChar.UnicodeChar) {
3152 else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown &&
3153 ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU &&
3154 ir.Event.KeyEvent.uChar.UnicodeChar) {
3158 ReadConsoleInputW((HANDLE)sock, &ir, 1, &n);
3168 is_invalid_handle(SOCKET sock)
3170 return (HANDLE)sock == INVALID_HANDLE_VALUE;
3175 do_select(
int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3182 rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
3184 rb_w32_sleep(INFINITE);
3188 thread_exclusive(select) {
3189 r = select(nfds, rd, wr, ex, timeout);
3191 if (r == SOCKET_ERROR) {
3192 errno = map_errno(WSAGetLastError());
3207 rb_w32_time_subtract(
struct timeval *rest,
const struct timeval *wait)
3209 if (rest->tv_sec < wait->tv_sec) {
3212 while (rest->tv_usec < wait->tv_usec) {
3213 if (rest->tv_sec <= wait->tv_sec) {
3217 rest->tv_usec += 1000 * 1000;
3219 rest->tv_sec -= wait->tv_sec;
3220 rest->tv_usec -= wait->tv_usec;
3221 return rest->tv_sec != 0 || rest->tv_usec != 0;
3228 if (t1->tv_sec < t2->tv_sec)
3230 if (t1->tv_sec > t2->tv_sec)
3232 if (t1->tv_usec < t2->tv_usec)
3234 if (t1->tv_usec > t2->tv_usec)
3241 int rb_w32_check_interrupt(
void *);
3246 rb_w32_select_with_thread(
int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3247 struct timeval *timeout,
void *th)
3256 struct timeval limit = {0, 0};
3258 if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
3264 if (timeout->tv_sec < 0 ||
3265 timeout->tv_usec < 0 ||
3266 timeout->tv_usec >= 1000000) {
3270 gettimeofday(&limit, NULL);
3271 limit.tv_sec += timeout->tv_sec;
3272 limit.tv_usec += timeout->tv_usec;
3273 if (limit.tv_usec >= 1000000) {
3274 limit.tv_usec -= 1000000;
3286 nonsock += extract_fd(&else_rd, rd, is_not_socket);
3289 nonsock += extract_fd(&else_wr, wr, is_not_socket);
3292 if (extract_fd(NULL, else_rd.
fdset, is_invalid_handle) > 0 ||
3293 extract_fd(NULL, else_wr.
fdset, is_invalid_handle) > 0) {
3301 extract_fd(&pipe_rd, else_rd.
fdset, is_pipe);
3304 extract_fd(&cons_rd, else_rd.
fdset, is_console);
3307 extract_fd(&except, ex, is_not_socket);
3310 if (rd && (
int)rd->fd_count > r) r = (int)rd->fd_count;
3311 if (wr && (
int)wr->fd_count > r) r = (
int)wr->fd_count;
3312 if (ex && (
int)ex->fd_count > r) r = (
int)ex->fd_count;
3313 if (nfds > r) nfds = r;
3317 const struct timeval wait = {0, 10 * 1000};
3320 if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
3327 extract_fd(&else_rd, pipe_rd.
fdset, is_readable_pipe);
3328 extract_fd(&else_rd, cons_rd.
fdset, is_readable_console);
3331 if (else_rd.
fdset->fd_count || else_wr.
fdset->fd_count) {
3332 r = do_select(nfds, rd, wr, ex, &zero);
3334 r += copy_fd(rd, else_rd.
fdset);
3335 r += copy_fd(wr, else_wr.
fdset);
3341 const struct timeval *dowait = &wait;
3351 if (rd) copy_fd(&orig_rd, rd);
3352 if (wr) copy_fd(&orig_wr, wr);
3353 if (ex) copy_fd(&orig_ex, ex);
3354 r = do_select(nfds, rd, wr, ex, &zero);
3356 if (rd) copy_fd(rd, &orig_rd);
3357 if (wr) copy_fd(wr, &orig_wr);
3358 if (ex) copy_fd(ex, &orig_ex);
3362 gettimeofday(&now, NULL);
3364 if (!rb_w32_time_subtract(&rest, &now))
break;
3365 if (compare(&rest, &wait) < 0) dowait = &rest;
3367 Sleep(dowait->tv_sec * 1000 + (dowait->tv_usec + 999) / 1000);
3383 rb_w32_select(
int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3386 return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
3391 get_wsa_extension_function(SOCKET s, GUID guid)
3396 WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid,
sizeof(guid),
3397 &
ptr,
sizeof(
ptr), &dmy, NULL, NULL);
3407 rb_w32_accept(
int s,
struct sockaddr *addr,
int *addrlen)
3413 r = accept(TO_SOCKET(s), addr, addrlen);
3414 if (r != INVALID_SOCKET) {
3415 SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
3416 fd = rb_w32_open_osfhandle((intptr_t)r, O_RDWR|O_BINARY|O_NOINHERIT);
3418 socklist_insert(r, 0);
3423 errno = map_errno(WSAGetLastError());
3434 rb_w32_bind(
int s,
const struct sockaddr *addr,
int addrlen)
3439 r = bind(TO_SOCKET(s), addr, addrlen);
3440 if (r == SOCKET_ERROR)
3441 errno = map_errno(WSAGetLastError());
3450 rb_w32_connect(
int s,
const struct sockaddr *addr,
int addrlen)
3454 r = connect(TO_SOCKET(s), addr, addrlen);
3455 if (r == SOCKET_ERROR) {
3456 int err = WSAGetLastError();
3457 if (err != WSAEWOULDBLOCK)
3458 errno = map_errno(err);
3460 errno = EINPROGRESS;
3471 rb_w32_getpeername(
int s,
struct sockaddr *addr,
int *addrlen)
3475 r = getpeername(TO_SOCKET(s), addr, addrlen);
3476 if (r == SOCKET_ERROR)
3477 errno = map_errno(WSAGetLastError());
3486 rb_w32_getsockname(
int fd,
struct sockaddr *addr,
int *addrlen)
3491 sock = TO_SOCKET(fd);
3492 r = getsockname(sock, addr, addrlen);
3493 if (r == SOCKET_ERROR) {
3494 DWORD wsaerror = WSAGetLastError();
3495 if (wsaerror == WSAEINVAL) {
3497 if (socklist_lookup(sock, &flags)) {
3498 int af = GET_FAMILY(flags);
3500 memset(addr, 0, *addrlen);
3501 addr->sa_family = af;
3506 errno = map_errno(wsaerror);
3516 rb_w32_getsockopt(
int s,
int level,
int optname,
char *optval,
int *optlen)
3520 r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3521 if (r == SOCKET_ERROR)
3522 errno = map_errno(WSAGetLastError());
3531 rb_w32_ioctlsocket(
int s,
long cmd, u_long *argp)
3535 r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3536 if (r == SOCKET_ERROR)
3537 errno = map_errno(WSAGetLastError());
3546 rb_w32_listen(
int s,
int backlog)
3550 r = listen(TO_SOCKET(s), backlog);
3551 if (r == SOCKET_ERROR)
3552 errno = map_errno(WSAGetLastError());
3564 finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol,
int result, DWORD *
len, DWORD size)
3569 if (result != SOCKET_ERROR)
3571 else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
3572 switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
3575 result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg);
3582 result = SOCKET_ERROR;
3585 if ((err = WSAGetLastError()) == WSAECONNABORTED && !input)
3587 else if (err == WSAEMSGSIZE && input) {
3593 errno = map_errno(err);
3595 case WAIT_OBJECT_0 + 1:
3598 CancelIo((HANDLE)s);
3603 if (err == WSAECONNABORTED && !input)
3606 errno = map_errno(err);
3609 CloseHandle(wol->hEvent);
3616 overlapped_socket_io(BOOL input,
int fd,
char *buf,
int len,
int flags,
3617 struct sockaddr *addr,
int *addrlen)
3628 socklist_lookup(s, &mode);
3629 if (GET_FLAGS(mode) & O_NONBLOCK) {
3632 if (addr && addrlen)
3633 r = recvfrom(s, buf,
len, flags, addr, addrlen);
3635 r = recv(s, buf,
len, flags);
3636 if (r == SOCKET_ERROR)
3637 errno = map_errno(WSAGetLastError());
3640 if (addr && addrlen)
3641 r = sendto(s, buf,
len, flags, addr, *addrlen);
3643 r = send(s, buf,
len, flags);
3644 if (r == SOCKET_ERROR) {
3645 DWORD err = WSAGetLastError();
3646 if (err == WSAECONNABORTED)
3649 errno = map_errno(err);
3659 memset(&wol, 0,
sizeof(wol));
3661 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3664 if (addr && addrlen)
3665 ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3668 ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3671 if (addr && addrlen)
3672 ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3675 ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3679 finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3688 rb_w32_recv(
int fd,
char *buf,
int len,
int flags)
3690 return overlapped_socket_io(TRUE, fd, buf,
len, flags, NULL, NULL);
3695 rb_w32_recvfrom(
int fd,
char *buf,
int len,
int flags,
3696 struct sockaddr *from,
int *fromlen)
3698 return overlapped_socket_io(TRUE, fd, buf,
len, flags, from, fromlen);
3703 rb_w32_send(
int fd,
const char *buf,
int len,
int flags)
3705 return overlapped_socket_io(FALSE, fd, (
char *)buf,
len, flags, NULL, NULL);
3710 rb_w32_sendto(
int fd,
const char *buf,
int len,
int flags,
3711 const struct sockaddr *to,
int tolen)
3713 return overlapped_socket_io(FALSE, fd, (
char *)buf,
len, flags,
3714 (
struct sockaddr *)to, &tolen);
3717 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3723 DWORD dwBufferCount;
3728 #ifndef WSAID_WSARECVMSG
3729 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3731 #ifndef WSAID_WSASENDMSG
3732 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3736 #define msghdr_to_wsamsg(msg, wsamsg) \
3739 (wsamsg)->name = (msg)->msg_name; \
3740 (wsamsg)->namelen = (msg)->msg_namelen; \
3741 (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3742 (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3743 for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3744 (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3745 (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3747 (wsamsg)->Control.buf = (msg)->msg_control; \
3748 (wsamsg)->Control.len = (msg)->msg_controllen; \
3749 (wsamsg)->dwFlags = (msg)->msg_flags; \
3754 recvmsg(
int fd,
struct msghdr *msg,
int flags)
3756 typedef int (WSAAPI *WSARecvMsg_t)(SOCKET,
WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3757 static WSARecvMsg_t pWSARecvMsg = NULL;
3767 static const GUID guid = WSAID_WSARECVMSG;
3768 pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, guid);
3773 msghdr_to_wsamsg(msg, &wsamsg);
3774 wsamsg.dwFlags |= flags;
3776 socklist_lookup(s, &mode);
3777 if (GET_FLAGS(mode) & O_NONBLOCK) {
3779 if ((ret = pWSARecvMsg(s, &wsamsg, &
len, NULL, NULL)) == SOCKET_ERROR) {
3780 errno = map_errno(WSAGetLastError());
3788 memset(&wol, 0,
sizeof(wol));
3790 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3791 ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3794 ret = finish_overlapped_socket(TRUE, s, &wol, ret, &
len, size);
3796 if (ret == SOCKET_ERROR)
3800 msg->msg_name = wsamsg.name;
3801 msg->msg_namelen = wsamsg.namelen;
3802 msg->msg_flags = wsamsg.dwFlags;
3809 sendmsg(
int fd,
const struct msghdr *msg,
int flags)
3811 typedef int (WSAAPI *WSASendMsg_t)(SOCKET,
const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3812 static WSASendMsg_t pWSASendMsg = NULL;
3822 static const GUID guid = WSAID_WSASENDMSG;
3823 pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, guid);
3828 msghdr_to_wsamsg(msg, &wsamsg);
3830 socklist_lookup(s, &mode);
3831 if (GET_FLAGS(mode) & O_NONBLOCK) {
3833 if ((ret = pWSASendMsg(s, &wsamsg, flags, &
len, NULL, NULL)) == SOCKET_ERROR) {
3834 errno = map_errno(WSAGetLastError());
3842 memset(&wol, 0,
sizeof(wol));
3844 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3845 ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3848 finish_overlapped_socket(FALSE, s, &wol, ret, &
len, size);
3858 rb_w32_setsockopt(
int s,
int level,
int optname,
const char *optval,
int optlen)
3862 r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3863 if (r == SOCKET_ERROR)
3864 errno = map_errno(WSAGetLastError());
3873 rb_w32_shutdown(
int s,
int how)
3877 r = shutdown(TO_SOCKET(s), how);
3878 if (r == SOCKET_ERROR)
3879 errno = map_errno(WSAGetLastError());
3886 open_ifs_socket(
int af,
int type,
int protocol)
3888 unsigned long proto_buffers_len = 0;
3890 SOCKET out = INVALID_SOCKET;
3892 if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3893 error_code = WSAGetLastError();
3894 if (error_code == WSAENOBUFS) {
3895 WSAPROTOCOL_INFO *proto_buffers;
3896 int protocols_available = 0;
3898 proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3899 if (!proto_buffers) {
3900 WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3901 return INVALID_SOCKET;
3904 protocols_available =
3905 WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3906 if (protocols_available != SOCKET_ERROR) {
3908 for (i = 0; i < protocols_available; i++) {
3909 if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3910 (
type != proto_buffers[i].iSocketType) ||
3911 (protocol != 0 && protocol != proto_buffers[i].iProtocol))
3914 if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3917 out = WSASocket(af,
type, protocol, &(proto_buffers[i]), 0,
3918 WSA_FLAG_OVERLAPPED);
3921 if (out == INVALID_SOCKET)
3922 out = WSASocket(af,
type, protocol, NULL, 0, 0);
3923 if (out != INVALID_SOCKET)
3924 SetHandleInformation((HANDLE)out, HANDLE_FLAG_INHERIT, 0);
3927 free(proto_buffers);
3938 rb_w32_socket(
int af,
int type,
int protocol)
3944 s = open_ifs_socket(af,
type, protocol);
3945 if (s == INVALID_SOCKET) {
3946 errno = map_errno(WSAGetLastError());
3950 fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3952 socklist_insert(s, MAKE_SOCKDATA(af, 0));
3960 #undef gethostbyaddr
3963 struct hostent * WSAAPI
3964 rb_w32_gethostbyaddr(
const char *addr,
int len,
int type)
3968 r = gethostbyaddr(addr,
len,
type);
3970 errno = map_errno(WSAGetLastError());
3975 #undef gethostbyname
3978 struct hostent * WSAAPI
3979 rb_w32_gethostbyname(
const char *name)
3983 r = gethostbyname(name);
3985 errno = map_errno(WSAGetLastError());
3994 rb_w32_gethostname(
char *name,
int len)
3998 r = gethostname(name,
len);
3999 if (r == SOCKET_ERROR)
4000 errno = map_errno(WSAGetLastError());
4005 #undef getprotobyname
4008 struct protoent * WSAAPI
4009 rb_w32_getprotobyname(
const char *name)
4013 r = getprotobyname(name);
4015 errno = map_errno(WSAGetLastError());
4020 #undef getprotobynumber
4023 struct protoent * WSAAPI
4024 rb_w32_getprotobynumber(
int num)
4028 r = getprotobynumber(num);
4030 errno = map_errno(WSAGetLastError());
4035 #undef getservbyname
4038 struct servent * WSAAPI
4039 rb_w32_getservbyname(
const char *name,
const char *proto)
4043 r = getservbyname(name, proto);
4045 errno = map_errno(WSAGetLastError());
4050 #undef getservbyport
4053 struct servent * WSAAPI
4054 rb_w32_getservbyport(
int port,
const char *proto)
4058 r = getservbyport(port, proto);
4060 errno = map_errno(WSAGetLastError());
4065 #ifdef HAVE_AFUNIX_H
4069 socketpair_unix_path(
struct sockaddr_un *sock_un)
4072 WCHAR wpath[
sizeof(sock_un->sun_path)/
sizeof(*sock_un->sun_path)] = L
"";
4077 listener = socket(AF_UNIX, SOCK_STREAM, 0);
4078 if (listener == INVALID_SOCKET)
4081 memset(sock_un, 0,
sizeof(*sock_un));
4082 sock_un->sun_family = AF_UNIX;
4092 for (
int try = 0; ;
try++) {
4093 LARGE_INTEGER ticks;
4094 size_t path_len = 0;
4095 const size_t maxpath =
sizeof(sock_un->sun_path)/
sizeof(*sock_un->sun_path);
4100 path_len = GetTempPathW(maxpath, wpath);
4103 wcsncpy(wpath, L
"C:/Temp/", maxpath);
4104 path_len = lstrlenW(wpath);
4111 closesocket(listener);
4116 path_len = WideCharToMultiByte(CP_UTF8, 0, wpath, path_len, sock_un->sun_path, maxpath, NULL, NULL);
4117 QueryPerformanceCounter(&ticks);
4118 path_len += snprintf(sock_un->sun_path + path_len,
4122 GetCurrentProcessId());
4125 MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path, -1, wpath,
sizeof(wpath)/
sizeof(*wpath));
4127 if (bind(listener, (
struct sockaddr *)sock_un,
sizeof(*sock_un)) != SOCKET_ERROR)
4130 closesocket(listener);
4132 return sizeof(*sock_un);
4138 socketpair_internal(
int af,
int type,
int protocol, SOCKET *sv)
4140 SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
4141 struct sockaddr_in sock_in4;
4144 struct sockaddr_in6 sock_in6;
4147 #ifdef HAVE_AFUNIX_H
4148 struct sockaddr_un sock_un = {0, {0}};
4149 WCHAR wpath[
sizeof(sock_un.sun_path)/
sizeof(*sock_un.sun_path)] = L
"";
4152 struct sockaddr *addr;
4158 #if defined PF_INET && PF_INET != AF_INET
4161 sock_in4.sin_family = AF_INET;
4162 sock_in4.sin_port = 0;
4163 sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4164 addr = (
struct sockaddr *)&sock_in4;
4165 len =
sizeof(sock_in4);
4169 memset(&sock_in6, 0,
sizeof(sock_in6));
4170 sock_in6.sin6_family = AF_INET6;
4171 sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
4172 addr = (
struct sockaddr *)&sock_in6;
4173 len =
sizeof(sock_in6);
4176 #ifdef HAVE_AFUNIX_H
4178 addr = (
struct sockaddr *)&sock_un;
4179 len = socketpair_unix_path(&sock_un);
4180 MultiByteToWideChar(CP_UTF8, 0, sock_un.sun_path, -1, wpath,
sizeof(wpath)/
sizeof(*wpath));
4186 errno = EAFNOSUPPORT;
4189 if (
type != SOCK_STREAM) {
4194 sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
4195 sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
4198 svr = open_ifs_socket(af,
type, protocol);
4199 if (svr == INVALID_SOCKET)
4201 if (bind(svr, addr,
len) < 0)
4203 if (getsockname(svr, addr, &
len) < 0)
4205 if (
type == SOCK_STREAM)
4208 w = open_ifs_socket(af,
type, protocol);
4209 if (w == INVALID_SOCKET)
4211 if (connect(w, addr,
len) < 0)
4214 r = accept(svr, addr, &
len);
4215 if (r == INVALID_SOCKET)
4217 SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
4223 errno = map_errno(WSAGetLastError());
4224 if (r != INVALID_SOCKET)
4226 if (w != INVALID_SOCKET)
4233 if (svr != INVALID_SOCKET)
4235 #ifdef HAVE_AFUNIX_H
4236 if (sock_un.sun_family == AF_UNIX)
4246 socketpair(
int af,
int type,
int protocol,
int *sv)
4250 if (socketpair_internal(af,
type, protocol, pair) < 0)
4252 sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
4254 closesocket(pair[0]);
4255 closesocket(pair[1]);
4258 sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
4260 rb_w32_close(sv[0]);
4261 closesocket(pair[1]);
4264 socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
4265 socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
4270 #if !defined(_MSC_VER) || _MSC_VER >= 1400
4273 str2guid(
const char *str, GUID *guid)
4275 #define hex2byte(str) \
4276 ((isdigit(*(str)) ? *(str) - '0' : toupper(*(str)) - 'A' + 10) << 4 | (isdigit(*((str) + 1)) ? *((str) + 1) - '0' : toupper(*((str) + 1)) - 'A' + 10))
4279 if (*str ==
'{') str++;
4280 guid->Data1 = (long)strtoul(str, &end, 16);
4282 guid->Data2 = (
unsigned short)strtoul(str, &end, 16);
4284 guid->Data3 = (
unsigned short)strtoul(str, &end, 16);
4286 guid->Data4[0] = hex2byte(str);
4288 guid->Data4[1] = hex2byte(str);
4290 for (i = 0; i < 6; i++) {
4291 guid->Data4[i + 2] = hex2byte(str);
4297 #ifndef HAVE_TYPE_NET_LUID
4301 uint64_t Reserved :24;
4302 uint64_t NetLuidIndex :24;
4303 uint64_t IfType :16;
4309 getifaddrs(
struct ifaddrs **ifap)
4313 IP_ADAPTER_ADDRESSES *root, *addr;
4316 ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
4317 if (ret != ERROR_BUFFER_OVERFLOW) {
4318 errno = map_errno(ret);
4322 ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, root, &size);
4323 if (ret != ERROR_SUCCESS) {
4324 errno = map_errno(ret);
4329 for (prev = NULL, addr = root; addr; addr = addr->Next) {
4331 char name[IFNAMSIZ];
4336 prev->ifa_next = ifa;
4340 str2guid(addr->AdapterName, &guid);
4341 if (ConvertInterfaceGuidToLuid(&guid, &luid) == NO_ERROR &&
4342 ConvertInterfaceLuidToNameA(&luid, name,
sizeof(name)) == NO_ERROR) {
4349 if (addr->IfType & IF_TYPE_SOFTWARE_LOOPBACK)
4350 ifa->ifa_flags |= IFF_LOOPBACK;
4351 if (addr->OperStatus == IfOperStatusUp) {
4352 ifa->ifa_flags |= IFF_UP;
4354 if (addr->FirstUnicastAddress) {
4355 IP_ADAPTER_UNICAST_ADDRESS *cur;
4357 for (cur = addr->FirstUnicastAddress; cur; cur = cur->Next) {
4358 if (cur->Flags & IP_ADAPTER_ADDRESS_TRANSIENT ||
4359 cur->DadState == IpDadStateDeprecated) {
4365 prev->ifa_next = ifa;
4367 ifa->ifa_flags = prev->ifa_flags;
4369 ifa->ifa_addr =
ruby_xmalloc(cur->Address.iSockaddrLength);
4370 memcpy(ifa->ifa_addr, cur->Address.lpSockaddr,
4371 cur->Address.iSockaddrLength);
4386 freeifaddrs(
struct ifaddrs *ifp)
4389 struct ifaddrs *next = ifp->ifa_next;
4403 void endhostent(
void) {}
4404 void endnetent(
void) {}
4405 void endprotoent(
void) {}
4406 void endservent(
void) {}
4408 struct netent *getnetent (
void) {
return (
struct netent *) NULL;}
4410 struct netent *getnetbyaddr(
long net,
int type) {
return (
struct netent *)NULL;}
4412 struct netent *getnetbyname(
const char *name) {
return (
struct netent *)NULL;}
4414 struct protoent *getprotoent (
void) {
return (
struct protoent *) NULL;}
4416 struct servent *getservent (
void) {
return (
struct servent *) NULL;}
4418 void sethostent (
int stayopen) {}
4420 void setnetent (
int stayopen) {}
4422 void setprotoent (
int stayopen) {}
4424 void setservent (
int stayopen) {}
4427 int rb_w32_set_nonblock2(
int fd,
int nonblock);
4431 setfl(SOCKET sock,
int arg)
4438 socklist_lookup(sock, &flag);
4439 af = GET_FAMILY(flag);
4440 flag = GET_FLAGS(flag);
4441 if (arg & O_NONBLOCK) {
4446 flag &= ~O_NONBLOCK;
4450 ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
4452 socklist_insert(sock, MAKE_SOCKDATA(af, flag));
4454 errno = map_errno(WSAGetLastError());
4462 dupfd(HANDLE hDup,
int flags,
int minfd)
4470 ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
4472 goto close_fds_and_return;
4475 goto close_fds_and_return;
4477 fds[filled++] = ret;
4478 }
while (filled < (
int)numberof(fds));
4480 ret = dupfd(hDup, flags, minfd);
4482 close_fds_and_return:
4484 while (filled > 0) {
4485 int fd = fds[--filled];
4486 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
4496 fcntl(
int fd,
int cmd, ...)
4505 arg = va_arg(va,
int);
4507 return rb_w32_set_nonblock2(fd, arg);
4509 case F_DUPFD:
case F_DUPFD_CLOEXEC: {
4513 if (!(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
4514 GetCurrentProcess(), &hDup, 0L,
4515 cmd == F_DUPFD && !(flag & FNOINHERIT),
4516 DUPLICATE_SAME_ACCESS))) {
4517 errno = map_errno(GetLastError());
4522 arg = va_arg(va,
int);
4528 flag &= ~FNOINHERIT;
4529 if ((ret = dupfd(hDup, flag, arg)) == -1)
4535 if (h == -1)
return -1;
4536 if (!GetHandleInformation((HANDLE)h, &flag)) {
4537 errno = map_errno(GetLastError());
4540 return (flag & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
4544 if (h == -1)
return -1;
4546 arg = va_arg(va,
int);
4548 if (!SetHandleInformation((HANDLE)h, HANDLE_FLAG_INHERIT,
4549 (arg & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT)) {
4550 errno = map_errno(GetLastError());
4553 if (arg & FD_CLOEXEC)
4554 _osfile(fd) |= FNOINHERIT;
4556 _osfile(fd) &= ~FNOINHERIT;
4567 rb_w32_set_nonblock2(
int fd,
int nonblock)
4569 SOCKET sock = TO_SOCKET(fd);
4570 if (is_socket(sock)) {
4571 return setfl(sock, nonblock ? O_NONBLOCK : 0);
4573 else if (is_pipe(sock)) {
4575 if (!GetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL, NULL, NULL, 0)) {
4576 errno = map_errno(GetLastError());
4580 state |= PIPE_NOWAIT;
4583 state &= ~PIPE_NOWAIT;
4585 if (!SetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL)) {
4586 errno = map_errno(GetLastError());
4598 rb_w32_set_nonblock(
int fd)
4600 return rb_w32_set_nonblock2(fd, TRUE);
4609 poll_child_status(
struct ChildRecord *child,
int *stat_loc)
4614 if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
4616 err = GetLastError();
4618 case ERROR_INVALID_PARAMETER:
4621 case ERROR_INVALID_HANDLE:
4625 errno = map_errno(err);
4629 CloseChildHandle(child);
4632 if (exitcode != STILL_ACTIVE) {
4635 if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
4639 CloseChildHandle(child);
4641 *stat_loc = exitcode << 8;
4642 if (exitcode & 0xC0000000) {
4643 static const struct {
4647 {STATUS_ACCESS_VIOLATION, SIGSEGV},
4648 {STATUS_ILLEGAL_INSTRUCTION, SIGILL},
4649 {STATUS_PRIVILEGED_INSTRUCTION, SIGILL},
4650 {STATUS_FLOAT_DENORMAL_OPERAND, SIGFPE},
4651 {STATUS_FLOAT_DIVIDE_BY_ZERO, SIGFPE},
4652 {STATUS_FLOAT_INEXACT_RESULT, SIGFPE},
4653 {STATUS_FLOAT_INVALID_OPERATION, SIGFPE},
4654 {STATUS_FLOAT_OVERFLOW, SIGFPE},
4655 {STATUS_FLOAT_STACK_CHECK, SIGFPE},
4656 {STATUS_FLOAT_UNDERFLOW, SIGFPE},
4657 #ifdef STATUS_FLOAT_MULTIPLE_FAULTS
4658 {STATUS_FLOAT_MULTIPLE_FAULTS, SIGFPE},
4660 #ifdef STATUS_FLOAT_MULTIPLE_TRAPS
4661 {STATUS_FLOAT_MULTIPLE_TRAPS, SIGFPE},
4663 {STATUS_CONTROL_C_EXIT, SIGINT},
4666 for (i = 0; i < (int)numberof(table); i++) {
4667 if (table[i].status == exitcode) {
4668 *stat_loc |= table[i].sig;
4673 if (i >= (
int)numberof(table))
4674 *stat_loc |= SIGSEGV;
4684 waitpid(rb_pid_t pid,
int *stat_loc,
int options)
4689 if (options == WNOHANG) {
4700 HANDLE events[MAXCHILDNUM];
4703 FOREACH_CHILD(child) {
4704 if (!child->pid || child->pid < 0)
continue;
4705 if ((pid = poll_child_status(child, stat_loc)))
return pid;
4706 events[count++] = child->hProcess;
4707 } END_FOREACH_CHILD;
4713 ret = rb_w32_wait_events_blocking(events, count, timeout);
4714 if (ret == WAIT_TIMEOUT)
return 0;
4715 if ((ret -= WAIT_OBJECT_0) == count) {
4719 errno = map_errno(GetLastError());
4723 cause = FindChildSlotByHandle(events[ret]);
4728 return poll_child_status(cause, stat_loc);
4738 while (!(pid = poll_child_status(child, stat_loc))) {
4740 int ret = rb_w32_wait_events_blocking(&child->hProcess, 1, timeout);
4741 if (ret == WAIT_OBJECT_0 + 1)
return -1;
4742 if (ret != WAIT_OBJECT_0) {
4744 if (options & WNOHANG) {
4751 if (pid == -1 && retried) pid = 0;
4757 #include <sys/timeb.h>
4762 filetime_split(
const FILETIME* ft,
long *subsec)
4768 tmp.LowPart = ft->dwLowDateTime;
4769 tmp.HighPart = ft->dwHighDateTime;
4776 lt -= (
LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * subsec_unit;
4778 *subsec = (long)(lt % subsec_unit);
4779 return (time_t)(lt / subsec_unit);
4789 GetSystemTimePreciseAsFileTime(&ft);
4790 tv->tv_sec = filetime_split(&ft, &subsec);
4791 tv->tv_usec = subsec / 10;
4798 clock_gettime(clockid_t clock_id,
struct timespec *sp)
4801 case CLOCK_REALTIME:
4806 GetSystemTimePreciseAsFileTime(&ft);
4807 sp->tv_sec = filetime_split(&ft, &subsec);
4808 sp->tv_nsec = subsec * 100;
4811 case CLOCK_MONOTONIC:
4814 LARGE_INTEGER count;
4815 if (!QueryPerformanceFrequency(&freq)) {
4816 errno = map_errno(GetLastError());
4819 if (!QueryPerformanceCounter(&count)) {
4820 errno = map_errno(GetLastError());
4823 sp->tv_sec = count.QuadPart / freq.QuadPart;
4824 if (freq.QuadPart < 1000000000)
4825 sp->tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
4827 sp->tv_nsec = (
long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
4838 clock_getres(clockid_t clock_id,
struct timespec *sp)
4841 case CLOCK_REALTIME:
4847 case CLOCK_MONOTONIC:
4850 if (!QueryPerformanceFrequency(&freq)) {
4851 errno = map_errno(GetLastError());
4855 sp->tv_nsec = (long)(1000000000.0 / freq.QuadPart);
4866 w32_getcwd(
char *buffer,
int size, UINT cp,
void *alloc(
int,
void *),
void *arg)
4871 len = GetCurrentDirectoryW(0, NULL);
4873 errno = map_errno(GetLastError());
4877 if (buffer && size <
len) {
4883 if (!GetCurrentDirectoryW(
len, p)) {
4884 errno = map_errno(GetLastError());
4888 wlen = translate_wchar(p, L
'\\', L
'/') - p + 1;
4889 len = WideCharToMultiByte(cp, 0, p, wlen, NULL, 0, NULL, NULL);
4897 buffer = (*alloc)(
len, arg);
4903 WideCharToMultiByte(cp, 0, p, wlen, buffer,
len, NULL, NULL);
4910 getcwd_alloc(
int size,
void *dummy)
4912 return malloc(size);
4917 rb_w32_getcwd(
char *buffer,
int size)
4919 return w32_getcwd(buffer, size, filecp(), getcwd_alloc, NULL);
4924 rb_w32_ugetcwd(
char *buffer,
int size)
4926 return w32_getcwd(buffer, size, CP_UTF8, getcwd_alloc, NULL);
4931 getcwd_value(
int size,
void *arg)
4939 rb_dir_getwd_ospath(
void)
4942 w32_getcwd(NULL, 0, CP_UTF8, getcwd_value, &cwd);
4948 chown(
const char *path,
int owner,
int group)
4955 rb_w32_uchown(
const char *path,
int owner,
int group)
4961 lchown(
const char *path,
int owner,
int group)
4967 rb_w32_ulchown(
const char *path,
int owner,
int group)
4974 kill(rb_pid_t pid,
int sig)
4979 if (pid < 0 || (pid == 0 && sig != SIGINT)) {
4984 if ((
unsigned int)pid == GetCurrentProcessId() &&
4985 (sig != 0 && sig != SIGKILL)) {
4986 if ((ret =
raise(sig)) != 0) {
4997 OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4998 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4999 if (GetLastError() == ERROR_INVALID_PARAMETER) {
5015 DWORD ctrlEvent = CTRL_C_EVENT;
5019 ctrlEvent = CTRL_BREAK_EVENT;
5021 if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
5022 if ((err = GetLastError()) == 0)
5025 errno = map_errno(GetLastError());
5036 hProc = child->hProcess;
5039 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
5041 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
5042 if (GetLastError() == ERROR_INVALID_PARAMETER) {
5052 if (!GetExitCodeProcess(hProc, &status)) {
5053 errno = map_errno(GetLastError());
5056 else if (status == STILL_ACTIVE) {
5057 if (!TerminateProcess(hProc, 0)) {
5084 wlink(
const WCHAR *from,
const WCHAR *to)
5086 if (!CreateHardLinkW(to, from, NULL)) {
5087 errno = map_errno(GetLastError());
5096 rb_w32_ulink(
const char *from,
const char *to)
5102 if (!(wfrom = utf8_to_wstr(from, NULL)))
5104 if (!(wto = utf8_to_wstr(to, NULL))) {
5108 ret = wlink(wfrom, wto);
5116 link(
const char *from,
const char *to)
5122 if (!(wfrom = filecp_to_wstr(from, NULL)))
5124 if (!(wto = filecp_to_wstr(to, NULL))) {
5128 ret = wlink(wfrom, wto);
5135 #ifndef FILE_DEVICE_FILE_SYSTEM
5136 # define FILE_DEVICE_FILE_SYSTEM 0x00000009
5138 #ifndef FSCTL_GET_REPARSE_POINT
5139 # define FSCTL_GET_REPARSE_POINT ((0x9<<16)|(42<<2))
5141 #ifndef IO_REPARSE_TAG_SYMLINK
5142 # define IO_REPARSE_TAG_SYMLINK 0xA000000CL
5153 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5154 if (f == INVALID_HANDLE_VALUE) {
5155 return GetLastError();
5158 if (!DeviceIoControl(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
5159 rp, size, &ret, NULL)) {
5162 else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
5163 rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
5164 e = ERROR_INVALID_PARAMETER;
5172 rb_w32_reparse_symlink_p(
const WCHAR *path)
5180 e = rb_w32_read_reparse_point(path, rp,
sizeof(rbuf), &wbuf, &
len);
5181 if (e == ERROR_MORE_DATA) {
5182 size_t size = rb_w32_reparse_buffer_size(
len + 1);
5184 e = rb_w32_read_reparse_point(path, rp, size, &wbuf, &
len);
5189 case ERROR_MORE_DATA:
5198 size_t bufsize, WCHAR **result, DWORD *
len)
5200 int e = reparse_symlink(path, rp, bufsize);
5203 if (!e || e == ERROR_MORE_DATA) {
5205 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
5206 name = ((
char *)rp->SymbolicLinkReparseBuffer.PathBuffer +
5207 rp->SymbolicLinkReparseBuffer.PrintNameOffset);
5208 ret = rp->SymbolicLinkReparseBuffer.PrintNameLength;
5209 *
len = ret /
sizeof(WCHAR);
5211 else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
5212 static const WCHAR volume[] = L
"Volume{";
5214 name = ((
char *)rp->MountPointReparseBuffer.PathBuffer +
5215 rp->MountPointReparseBuffer.SubstituteNameOffset +
5216 volume_prefix_len *
sizeof(WCHAR));
5217 ret = rp->MountPointReparseBuffer.SubstituteNameLength;
5218 *
len = ret /
sizeof(WCHAR);
5219 ret -= volume_prefix_len *
sizeof(WCHAR);
5220 if (ret >
sizeof(volume) - 1 *
sizeof(WCHAR) &&
5221 memcmp(name, volume,
sizeof(volume) - 1 *
sizeof(WCHAR)) == 0)
5229 if ((
char *)name + ret +
sizeof(WCHAR) > (
char *)rp + bufsize)
5233 ((WCHAR *)name)[ret/
sizeof(WCHAR)] = L
'\0';
5234 translate_wchar(name, L
'\\', L
'/');
5244 w32_readlink(UINT cp,
const char *path,
char *buf,
size_t bufsize)
5246 VALUE rp_buf, rp_buf_bigger = 0;
5247 DWORD
len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0);
5248 size_t size = rb_w32_reparse_buffer_size(bufsize);
5250 WCHAR *wpath =
ALLOCV(rp_buf,
sizeof(WCHAR) *
len + size);
5255 MultiByteToWideChar(cp, 0, path, -1, wpath,
len);
5256 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &
len);
5257 if (e == ERROR_MORE_DATA) {
5258 size = rb_w32_reparse_buffer_size(
len + 1);
5259 rp =
ALLOCV(rp_buf_bigger, size);
5260 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &
len);
5265 errno = e == -1 ? EINVAL : map_errno(e);
5268 len = lstrlenW(wname);
5269 ret = WideCharToMultiByte(cp, 0, wname,
len, buf, bufsize, NULL, NULL);
5280 rb_w32_ureadlink(
const char *path,
char *buf,
size_t bufsize)
5282 return w32_readlink(CP_UTF8, path, buf, bufsize);
5287 readlink(
const char *path,
char *buf,
size_t bufsize)
5289 return w32_readlink(filecp(), path, buf, bufsize);
5292 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
5293 #define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
5295 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
5296 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
5301 w32_symlink(UINT cp,
const char *src,
const char *link)
5303 int atts, len1, len2;
5305 WCHAR *wsrc, *wlink;
5310 static DWORD create_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5320 len1 = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
5321 len2 = MultiByteToWideChar(cp, 0, link, -1, NULL, 0);
5322 wsrc =
ALLOCV_N(WCHAR, buf, len1+len2);
5323 wlink = wsrc + len1;
5324 MultiByteToWideChar(cp, 0, src, -1, wsrc, len1);
5325 MultiByteToWideChar(cp, 0, link, -1, wlink, len2);
5326 translate_wchar(wsrc, L
'/', L
'\\');
5328 atts = GetFileAttributesW(wsrc);
5329 if (atts != -1 && atts & FILE_ATTRIBUTE_DIRECTORY)
5330 flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
5331 ret = CreateSymbolicLinkW(wlink, wsrc, flag |= create_flag);
5333 (e = GetLastError()) == ERROR_INVALID_PARAMETER &&
5334 (flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
5336 flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5337 ret = CreateSymbolicLinkW(wlink, wsrc, flag);
5338 if (!ret) e = GetLastError();
5343 errno = map_errno(e);
5351 rb_w32_usymlink(
const char *src,
const char *link)
5353 return w32_symlink(CP_UTF8, src, link);
5358 symlink(
const char *src,
const char *link)
5360 return w32_symlink(filecp(), src, link);
5367 return waitpid(-1, status, 0);
5372 w32_getenv(
const char *name, UINT cp)
5374 WCHAR *wenvarea, *wenv;
5375 int len = strlen(name);
5376 char *env, *found = NULL;
5379 if (
len == 0)
return NULL;
5384 return getenv(name);
5387 thread_exclusive(uenvarea) {
5392 wenvarea = GetEnvironmentStringsW();
5394 map_errno(GetLastError());
5397 for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
5398 wlen += lstrlenW(wenv) + 1;
5399 uenvarea = wstr_to_mbstr(cp, wenvarea, wlen, NULL);
5400 FreeEnvironmentStringsW(wenvarea);
5404 for (env = uenvarea; *env; env += strlen(env) + 1) {
5405 if (strncasecmp(env, name,
len) == 0 && *(env +
len) ==
'=') {
5406 found = env +
len + 1;
5417 rb_w32_ugetenv(
const char *name)
5419 return w32_getenv(name, CP_UTF8);
5424 rb_w32_getenv(
const char *name)
5426 return w32_getenv(name, CP_ACP);
5431 get_attr_vsn(
const WCHAR *path, DWORD *atts, DWORD *vsn)
5433 BY_HANDLE_FILE_INFORMATION st = {0};
5435 HANDLE h = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5437 if (h == INVALID_HANDLE_VALUE) {
5442 if (!GetFileInformationByHandle(h, &st)) {
5447 *atts = st.dwFileAttributes;
5448 *vsn = st.dwVolumeSerialNumber;
5456 wrename(
const WCHAR *oldpath,
const WCHAR *newpath)
5459 DWORD oldatts = 0, newatts = (DWORD)-1;
5460 DWORD oldvsn = 0, newvsn = 0, e;
5462 e = get_attr_vsn(oldpath, &oldatts, &oldvsn);
5464 errno = map_errno(e);
5467 if (oldatts & FILE_ATTRIBUTE_REPARSE_POINT) {
5468 HANDLE fh = open_special(oldpath, 0, 0);
5469 if (fh == INVALID_HANDLE_VALUE) {
5471 if (e == ERROR_CANT_RESOLVE_FILENAME) {
5478 get_attr_vsn(newpath, &newatts, &newvsn);
5481 if (newatts != (DWORD)-1 && newatts & FILE_ATTRIBUTE_READONLY)
5482 SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
5484 if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
5488 DWORD e = GetLastError();
5489 if ((e == ERROR_ACCESS_DENIED) && (oldatts & FILE_ATTRIBUTE_DIRECTORY) &&
5493 errno = map_errno(e);
5496 SetFileAttributesW(newpath, oldatts);
5504 rb_w32_urename(
const char *from,
const char *to)
5510 if (!(wfrom = utf8_to_wstr(from, NULL)))
5512 if (!(wto = utf8_to_wstr(to, NULL))) {
5516 ret = wrename(wfrom, wto);
5524 rb_w32_rename(
const char *from,
const char *to)
5530 if (!(wfrom = filecp_to_wstr(from, NULL)))
5532 if (!(wto = filecp_to_wstr(to, NULL))) {
5536 ret = wrename(wfrom, wto);
5544 isUNCRoot(
const WCHAR *path)
5546 if (path[0] == L
'\\' && path[1] == L
'\\') {
5547 const WCHAR *p = path + 2;
5548 if (p[0] == L
'?' && p[1] == L
'\\') {
5556 for (p++; *p; p++) {
5560 if (!p[0] || !p[1] || (p[1] == L
'.' && !p[2]))
5567 #define COPY_STAT(src, dest, size_cast) do { \
5568 (dest).st_dev = (src).st_dev; \
5569 (dest).st_ino = (src).st_ino; \
5570 (dest).st_mode = (src).st_mode; \
5571 (dest).st_nlink = (src).st_nlink; \
5572 (dest).st_uid = (src).st_uid; \
5573 (dest).st_gid = (src).st_gid; \
5574 (dest).st_rdev = (src).st_rdev; \
5575 (dest).st_size = size_cast(src).st_size; \
5576 (dest).st_atime = (src).st_atime; \
5577 (dest).st_mtime = (src).st_mtime; \
5578 (dest).st_ctime = (src).st_ctime; \
5581 static time_t filetime_to_unixtime(
const FILETIME *ft);
5582 static long filetime_to_nsec(
const FILETIME *ft);
5583 static WCHAR *name_for_stat(WCHAR *buf,
const WCHAR *path);
5584 static DWORD stati128_handle(HANDLE h,
struct stati128 *st);
5589 rb_w32_fstat(
int fd,
struct stat *st)
5591 BY_HANDLE_FILE_INFORMATION info;
5592 int ret = fstat(fd, st);
5594 if (ret)
return ret;
5595 if (GetEnvironmentVariableW(L
"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND)
return ret;
5596 if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
5597 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5598 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5599 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5606 rb_w32_fstati128(
int fd,
struct stati128 *st)
5609 int ret = fstat(fd, &tmp);
5611 if (ret)
return ret;
5612 COPY_STAT(tmp, *st, +);
5613 stati128_handle((HANDLE)_get_osfhandle(fd), st);
5617 #if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5619 BYTE Identifier[16];
5623 #if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5624 #define FileIdInfo 0x12
5635 return GetFileInformationByHandleEx(h, FileIdInfo,
id,
sizeof(*
id));
5640 stati128_handle(HANDLE h,
struct stati128 *st)
5642 BY_HANDLE_FILE_INFORMATION info;
5643 DWORD attr = (DWORD)-1;
5645 if (GetFileInformationByHandle(h, &info)) {
5647 st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
5648 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5649 st->st_atimensec = filetime_to_nsec(&info.ftLastAccessTime);
5650 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5651 st->st_mtimensec = filetime_to_nsec(&info.ftLastWriteTime);
5652 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5653 st->st_ctimensec = filetime_to_nsec(&info.ftCreationTime);
5654 st->st_nlink = info.nNumberOfLinks;
5655 attr = info.dwFileAttributes;
5656 if (get_ino(h, &fii)) {
5657 st->st_ino = *((
unsigned __int64 *)&fii.FileId);
5658 st->st_inohigh = *((__int64 *)&fii.FileId + 1);
5661 st->st_ino = ((__int64)info.nFileIndexHigh << 32) | info.nFileIndexLow;
5670 filetime_to_unixtime(
const FILETIME *ft)
5673 time_t t = filetime_split(ft, &subsec);
5675 if (t < 0)
return 0;
5681 filetime_to_nsec(
const FILETIME *ft)
5684 tmp.LowPart = ft->dwLowDateTime;
5685 tmp.HighPart = ft->dwHighDateTime;
5686 return (
long)(tmp.QuadPart % 10000000) * 100;
5691 fileattr_to_unixmode(DWORD attr,
const WCHAR *path,
unsigned mode)
5693 if (attr & FILE_ATTRIBUTE_READONLY) {
5697 mode |= S_IREAD | S_IWRITE | S_IWUSR;
5700 if (mode & S_IFMT) {
5703 else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5707 else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5708 mode |= S_IFDIR | S_IEXEC;
5714 if (path && (mode & S_IFREG)) {
5715 const WCHAR *end = path + lstrlenW(path);
5716 while (path < end) {
5717 end = CharPrevW(path, end);
5719 if ((_wcsicmp(end, L
".bat") == 0) ||
5720 (_wcsicmp(end, L
".cmd") == 0) ||
5721 (_wcsicmp(end, L
".com") == 0) ||
5722 (_wcsicmp(end, L
".exe") == 0)) {
5727 if (!iswalnum(*end))
break;
5731 mode |= (mode & 0500) >> 3;
5732 mode |= (mode & 0500) >> 6;
5739 check_valid_dir(
const WCHAR *path)
5741 WIN32_FIND_DATAW fd;
5743 WCHAR full[PATH_MAX];
5749 if (!(p = wcsstr(path, L
"...")))
5751 q = p + wcsspn(p, L
".");
5752 if ((p == path || wcschr(L
":/\\", *(p - 1))) &&
5753 (!*q || wcschr(L
":/\\", *q))) {
5760 if (!GetFullPathNameW(path,
sizeof(full) /
sizeof(WCHAR), full, &dmy)) {
5761 errno = map_errno(GetLastError());
5764 if (full[1] == L
':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5767 fh = open_dir_handle(path, &fd);
5768 if (fh == INVALID_HANDLE_VALUE)
5776 stat_by_find(
const WCHAR *path,
struct stati128 *st)
5779 WIN32_FIND_DATAW wfd;
5782 h = FindFirstFileW(path, &wfd);
5783 if (h == INVALID_HANDLE_VALUE) {
5784 errno = map_errno(GetLastError());
5788 st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path, 0);
5789 st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
5790 st->st_atimensec = filetime_to_nsec(&wfd.ftLastAccessTime);
5791 st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
5792 st->st_mtimensec = filetime_to_nsec(&wfd.ftLastWriteTime);
5793 st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
5794 st->st_ctimensec = filetime_to_nsec(&wfd.ftCreationTime);
5795 st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
5802 path_drive(
const WCHAR *path)
5804 return (iswalpha(path[0]) && path[1] == L
':') ?
5805 towupper(path[0]) - L
'A' : _getdrive() - 1;
5810 winnt_stat(
const WCHAR *path,
struct stati128 *st, BOOL lstat)
5812 DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
5814 WCHAR finalname[PATH_MAX];
5817 memset(st, 0,
sizeof(*st));
5818 f = open_special(path, 0, flags);
5819 open_error = GetLastError();
5820 if (f == INVALID_HANDLE_VALUE && !lstat) {
5822 FILE_ATTRIBUTE_TAG_INFO attr_info;
5825 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5826 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5827 &attr_info,
sizeof(attr_info));
5828 if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
5830 f = INVALID_HANDLE_VALUE;
5833 if (f != INVALID_HANDLE_VALUE) {
5834 DWORD attr = stati128_handle(f, st);
5835 const DWORD
len = GetFinalPathNameByHandleW(f, finalname, numberof(finalname), 0);
5837 switch (GetFileType(f)) {
5838 case FILE_TYPE_CHAR:
5841 case FILE_TYPE_PIPE:
5845 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5846 FILE_ATTRIBUTE_TAG_INFO attr_info;
5849 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5850 &attr_info,
sizeof(attr_info));
5851 if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) {
5855 else if (rb_w32_reparse_symlink_p(path)) {
5858 mode |= S_IFLNK | S_IEXEC;
5861 mode |= S_IFDIR | S_IEXEC;
5866 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5867 if (check_valid_dir(path))
return -1;
5869 st->st_mode = fileattr_to_unixmode(attr, path, mode);
5871 finalname[min(
len, numberof(finalname)-1)] = L
'\0';
5873 if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
5874 path += numberof(namespace_prefix);
5878 if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME)
5879 || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) {
5880 errno = map_errno(open_error);
5884 if (stat_by_find(path, st))
return -1;
5887 st->st_dev = st->st_rdev = path_drive(path);
5894 rb_w32_stat(
const char *path,
struct stat *st)
5898 if (w32_stati128(path, &tmp, filecp(), FALSE))
return -1;
5899 COPY_STAT(tmp, *st, (_off_t));
5905 wstati128(
const WCHAR *path,
struct stati128 *st, BOOL lstat)
5915 size = lstrlenW(path) + 2;
5917 if (!(path = name_for_stat(buf1, path)))
5919 ret = winnt_stat(path, st, lstat);
5928 name_for_stat(WCHAR *buf1,
const WCHAR *path)
5934 for (p = path, s = buf1; *p; p++, s++) {
5942 if (!
len || L
'\"' == *(--s)) {
5946 end = buf1 +
len - 1;
5948 if (isUNCRoot(buf1)) {
5951 else if (*end != L
'\\')
5952 lstrcatW(buf1, L
"\\");
5954 else if (*end == L
'\\' || (buf1 + 1 == end && *end == L
':'))
5955 lstrcatW(buf1, L
".");
5962 rb_w32_ustati128(
const char *path,
struct stati128 *st)
5964 return w32_stati128(path, st, CP_UTF8, FALSE);
5969 rb_w32_stati128(
const char *path,
struct stati128 *st)
5971 return w32_stati128(path, st, filecp(), FALSE);
5976 w32_stati128(
const char *path,
struct stati128 *st, UINT cp, BOOL lstat)
5981 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
5983 ret = wstati128(wpath, st, lstat);
5990 rb_w32_ulstati128(
const char *path,
struct stati128 *st)
5992 return w32_stati128(path, st, CP_UTF8, TRUE);
5997 rb_w32_lstati128(
const char *path,
struct stati128 *st)
5999 return w32_stati128(path, st, filecp(), TRUE);
6004 rb_w32_lseek(
int fd, rb_off_t ofs,
int whence)
6006 SOCKET sock = TO_SOCKET(fd);
6007 if (is_socket(sock) || is_pipe(sock)) {
6011 return _lseeki64(fd, ofs, whence);
6016 w32_access(
const char *path,
int mode, UINT cp)
6019 if (w32_stati128(path, &stat, cp, FALSE) != 0)
6022 if ((stat.st_mode & mode) != mode) {
6031 rb_w32_access(
const char *path,
int mode)
6033 return w32_access(path, mode, filecp());
6038 rb_w32_uaccess(
const char *path,
int mode)
6040 return w32_access(path, mode, CP_UTF8);
6045 rb_chsize(HANDLE h, rb_off_t size)
6047 long upos, lpos, usize, lsize;
6051 if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6052 (e = GetLastError())) {
6053 errno = map_errno(e);
6056 usize = (long)(size >> 32);
6058 if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
6059 (e = GetLastError())) {
6060 errno = map_errno(e);
6062 else if (!SetEndOfFile(h)) {
6063 errno = map_errno(GetLastError());
6068 SetFilePointer(h, lpos, &upos, SEEK_SET);
6074 w32_truncate(
const char *path, rb_off_t length, UINT cp)
6080 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6082 h = CreateFileW(wpath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
6083 if (h == INVALID_HANDLE_VALUE) {
6084 errno = map_errno(GetLastError());
6089 ret = rb_chsize(h, length);
6096 rb_w32_utruncate(
const char *path, rb_off_t length)
6098 return w32_truncate(path, length, CP_UTF8);
6103 rb_w32_truncate(
const char *path, rb_off_t length)
6105 return w32_truncate(path, length, filecp());
6110 rb_w32_ftruncate(
int fd, rb_off_t length)
6114 h = (HANDLE)_get_osfhandle(fd);
6115 if (h == (HANDLE)-1)
return -1;
6116 return rb_chsize(h, length);
6121 filetime_to_clock(FILETIME *ft)
6123 __int64 qw = ft->dwHighDateTime;
6125 qw |= ft->dwLowDateTime;
6132 rb_w32_times(
struct tms *tmbuf)
6134 FILETIME create, exit, kernel, user;
6136 if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
6137 tmbuf->tms_utime = filetime_to_clock(&user);
6138 tmbuf->tms_stime = filetime_to_clock(&kernel);
6139 tmbuf->tms_cutime = 0;
6140 tmbuf->tms_cstime = 0;
6143 tmbuf->tms_utime = clock();
6144 tmbuf->tms_stime = 0;
6145 tmbuf->tms_cutime = 0;
6146 tmbuf->tms_cstime = 0;
6153 #define yield_once() Sleep(0)
6154 #define yield_until(condition) do yield_once(); while (!(condition))
6163 uintptr_t (*func)(uintptr_t
self,
int argc, uintptr_t* argv);
6171 call_asynchronous(PVOID argp)
6175 arg->stackaddr = &argp;
6176 ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
6177 arg->errnum =
errno;
6183 rb_w32_asynchronize(asynchronous_func_t func, uintptr_t
self,
6184 int argc, uintptr_t* argv, uintptr_t intrval)
6187 BOOL interrupted = FALSE;
6193 arg.stackaddr = NULL;
6200 thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6203 yield_until(arg.stackaddr);
6205 if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6208 if (TerminateThread(thr, intrval)) {
6213 GetExitCodeThread(thr, &val);
6218 MEMORY_BASIC_INFORMATION m;
6220 memset(&m, 0,
sizeof(m));
6221 if (!VirtualQuery(arg.stackaddr, &m,
sizeof(m))) {
6222 Debug(fprintf(stderr,
"couldn't get stack base:%p:%d\n",
6223 arg.stackaddr, GetLastError()));
6225 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6226 Debug(fprintf(stderr,
"couldn't release stack:%p:%d\n",
6227 m.AllocationBase, GetLastError()));
6238 rb_fatal(
"failed to launch waiter thread:%ld", GetLastError());
6246 rb_w32_get_environ(
void)
6248 WCHAR *envtop, *env;
6249 char **myenvtop, **myenv;
6262 envtop = GetEnvironmentStringsW();
6263 for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6264 if (*env !=
'=') num++;
6266 myenvtop = (
char **)malloc(
sizeof(
char *) * (num + 1));
6267 for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
6269 if (!(*myenv = wstr_to_utf8(env, NULL))) {
6276 FreeEnvironmentStringsW(envtop);
6283 rb_w32_free_environ(
char **env)
6287 while (*t) free(*t++);
6295 return GetCurrentProcessId();
6301 rb_w32_getppid(
void)
6303 typedef long (WINAPI query_func)(HANDLE, int,
void *, ULONG, ULONG *);
6304 static query_func *pNtQueryInformationProcess = (query_func *)-1;
6307 if (pNtQueryInformationProcess == (query_func *)-1)
6308 pNtQueryInformationProcess = (query_func *)get_proc_address(
"ntdll.dll",
"NtQueryInformationProcess", NULL);
6309 if (pNtQueryInformationProcess) {
6312 void* PebBaseAddress;
6313 uintptr_t AffinityMask;
6314 uintptr_t BasePriority;
6315 uintptr_t UniqueProcessId;
6316 uintptr_t ParentProcessId;
6319 long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi,
sizeof(pbi), &
len);
6321 ppid = pbi.ParentProcessId;
6328 STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
6331 #define set_new_std_handle(newfd, handle) do { \
6332 if ((unsigned)(newfd) > 2) break; \
6333 SetStdHandle(STD_INPUT_HANDLE+(STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)*(newfd), \
6336 #define set_new_std_fd(newfd) set_new_std_handle(newfd, (HANDLE)rb_w32_get_osfhandle(newfd))
6340 rb_w32_dup2(
int oldfd,
int newfd)
6344 if (oldfd == newfd)
return newfd;
6345 ret = dup2(oldfd, newfd);
6346 if (ret < 0)
return ret;
6347 set_new_std_fd(newfd);
6353 rb_w32_uopen(
const char *file,
int oflag, ...)
6360 va_start(arg, oflag);
6361 pmode = va_arg(arg,
int);
6364 if (!(wfile = utf8_to_wstr(file, NULL)))
6366 ret = w32_wopen(wfile, oflag, pmode);
6373 check_if_wdir(
const WCHAR *wfile)
6375 DWORD attr = GetFileAttributesW(wfile);
6376 if (attr == (DWORD)-1L ||
6377 !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6378 check_valid_dir(wfile)) {
6387 rb_w32_open(
const char *file,
int oflag, ...)
6394 va_start(arg, oflag);
6395 pmode = va_arg(arg,
int);
6398 if (!(wfile = filecp_to_wstr(file, NULL)))
6400 ret = w32_wopen(wfile, oflag, pmode);
6407 rb_w32_wopen(
const WCHAR *file,
int oflag, ...)
6411 if (oflag & O_CREAT) {
6413 va_start(arg, oflag);
6414 pmode = va_arg(arg,
int);
6418 return w32_wopen(file, oflag, pmode);
6422 w32_wopen(
const WCHAR *file,
int oflag,
int pmode)
6428 DWORD attr = FILE_ATTRIBUTE_NORMAL;
6429 SECURITY_ATTRIBUTES sec;
6433 share_delete = oflag & O_SHARE_DELETE ? FILE_SHARE_DELETE : 0;
6434 oflag &= ~O_SHARE_DELETE;
6435 if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
6436 fd = _wopen(file, oflag, pmode);
6440 check_if_wdir(file);
6443 errno = map_errno(GetLastError());
6450 sec.nLength =
sizeof(sec);
6451 sec.lpSecurityDescriptor = NULL;
6452 if (oflag & O_NOINHERIT) {
6453 sec.bInheritHandle = FALSE;
6454 flags |= FNOINHERIT;
6457 sec.bInheritHandle = TRUE;
6459 oflag &= ~O_NOINHERIT;
6462 oflag &= ~(O_BINARY | O_TEXT);
6464 switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
6466 access = GENERIC_READ | GENERIC_WRITE;
6469 access = GENERIC_READ;
6472 access = GENERIC_WRITE;
6478 oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
6480 switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
6482 create = OPEN_ALWAYS;
6486 create = OPEN_EXISTING;
6488 case O_CREAT | O_EXCL:
6489 case O_CREAT | O_EXCL | O_TRUNC:
6490 create = CREATE_NEW;
6493 case O_TRUNC | O_EXCL:
6494 create = TRUNCATE_EXISTING;
6496 case O_CREAT | O_TRUNC:
6497 create = CREATE_ALWAYS;
6503 if (oflag & O_CREAT) {
6505 if (!(pmode & S_IWRITE))
6506 attr = FILE_ATTRIBUTE_READONLY;
6508 oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6510 if (oflag & O_TEMPORARY) {
6511 attr |= FILE_FLAG_DELETE_ON_CLOSE;
6514 oflag &= ~O_TEMPORARY;
6516 if (oflag & _O_SHORT_LIVED)
6517 attr |= FILE_ATTRIBUTE_TEMPORARY;
6518 oflag &= ~_O_SHORT_LIVED;
6520 switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
6524 attr |= FILE_FLAG_SEQUENTIAL_SCAN;
6527 attr |= FILE_FLAG_RANDOM_ACCESS;
6533 oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6535 if (oflag & ~O_APPEND) {
6542 h = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6543 fd = _open_osfhandle((intptr_t)h, 0);
6551 rb_acrt_lowio_lock_fh(fd);
6552 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6553 _set_osflags(fd, 0);
6555 h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE | share_delete, &sec, create, attr, NULL);
6556 if (h == INVALID_HANDLE_VALUE) {
6557 DWORD e = GetLastError();
6558 if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
6559 errno = map_errno(e);
6560 rb_acrt_lowio_unlock_fh(fd);
6565 switch (GetFileType(h)) {
6566 case FILE_TYPE_CHAR:
6569 case FILE_TYPE_PIPE:
6572 case FILE_TYPE_UNKNOWN:
6573 errno = map_errno(GetLastError());
6575 rb_acrt_lowio_unlock_fh(fd);
6579 if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6582 _set_osfhnd(fd, (intptr_t)h);
6583 _set_osflags(fd, flags | FOPEN);
6585 rb_acrt_lowio_unlock_fh(fd);
6595 rb_w32_fclose(
FILE *fp)
6597 int fd = fileno(fp);
6598 SOCKET sock = TO_SOCKET(fd);
6599 int save_errno =
errno;
6601 if (fflush(fp))
return -1;
6602 if (!is_socket(sock)) {
6603 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6606 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6609 if (closesocket(sock) == SOCKET_ERROR) {
6610 errno = map_errno(WSAGetLastError());
6618 rb_w32_pipe(
int fds[2])
6620 static long serial = 0;
6621 static const char prefix[] =
"\\\\.\\pipe\\ruby";
6623 width_of_prefix = (int)
sizeof(prefix) - 1,
6624 width_of_pid = (int)
sizeof(rb_pid_t) * 2,
6625 width_of_serial = (int)
sizeof(serial) * 2,
6626 width_of_ids = width_of_pid + 1 + width_of_serial + 1
6628 char name[
sizeof(prefix) + width_of_ids];
6629 SECURITY_ATTRIBUTES sec;
6630 HANDLE hRead, hWrite, h;
6631 int fdRead, fdWrite;
6634 memcpy(name, prefix, width_of_prefix);
6635 snprintf(name + width_of_prefix, width_of_ids,
"%.*"PRI_PIDT_PREFIX"x-%.*lx",
6636 width_of_pid, rb_w32_getpid(), width_of_serial, InterlockedIncrement(&serial)-1);
6638 sec.nLength =
sizeof(sec);
6639 sec.lpSecurityDescriptor = NULL;
6640 sec.bInheritHandle = FALSE;
6643 hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6644 0, 2, 65536, 65536, 0, &sec);
6646 if (hRead == INVALID_HANDLE_VALUE) {
6647 DWORD err = GetLastError();
6648 if (err == ERROR_PIPE_BUSY)
6651 errno = map_errno(GetLastError());
6656 hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6657 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6659 if (hWrite == INVALID_HANDLE_VALUE) {
6660 errno = map_errno(GetLastError());
6667 h = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6668 fdRead = _open_osfhandle((intptr_t)h, 0);
6672 CloseHandle(hWrite);
6678 rb_acrt_lowio_lock_fh(fdRead);
6679 _set_osfhnd(fdRead, (intptr_t)hRead);
6680 _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
6681 rb_acrt_lowio_unlock_fh(fdRead);
6687 h = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6688 fdWrite = _open_osfhandle((intptr_t)h, 0);
6690 if (fdWrite == -1) {
6692 CloseHandle(hWrite);
6696 rb_acrt_lowio_lock_fh(fdWrite);
6697 _set_osfhnd(fdWrite, (intptr_t)hWrite);
6698 _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
6699 rb_acrt_lowio_unlock_fh(fdWrite);
6702 rb_w32_close(fdRead);
6714 console_emulator_p(
void)
6719 const void *
const func = WriteConsoleW;
6721 MEMORY_BASIC_INFORMATION m;
6723 memset(&m, 0,
sizeof(m));
6724 if (!VirtualQuery(func, &m,
sizeof(m))) {
6727 k = GetModuleHandle(
"kernel32.dll");
6728 if (!k)
return FALSE;
6729 return (HMODULE)m.AllocationBase != k;
6735 constat_handle(HANDLE h)
6739 thread_exclusive(conlist) {
6741 if (console_emulator_p()) {
6742 conlist = conlist_disabled;
6745 conlist = st_init_numtable();
6746 install_vm_exit_handler();
6748 else if (conlist == conlist_disabled) {
6751 if (st_lookup(conlist, (st_data_t)h, &data)) {
6755 CONSOLE_SCREEN_BUFFER_INFO csbi;
6757 p->vt100.state = constat_init;
6758 p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6759 p->vt100.reverse = 0;
6760 p->vt100.saved.X = p->vt100.saved.Y = 0;
6761 if (GetConsoleScreenBufferInfo(h, &csbi)) {
6762 p->vt100.attr = csbi.wAttributes;
6764 st_insert(conlist, (st_data_t)h, (st_data_t)p);
6772 constat_reset(HANDLE h)
6776 thread_exclusive(conlist) {
6777 if (!conlist || conlist == conlist_disabled)
continue;
6778 if (!st_lookup(conlist, (st_data_t)h, &data))
continue;
6780 p->vt100.state = constat_init;
6784 #define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6785 #define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6787 #define constat_attr_color_reverse(attr) \
6788 ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6789 (((attr) & FOREGROUND_MASK) << 4) | \
6790 (((attr) & BACKGROUND_MASK) >> 4)
6794 constat_attr(
int count,
const int *seq, WORD attr, WORD default_attr,
int *reverse)
6799 if (!count)
return attr;
6800 if (rev) attr = constat_attr_color_reverse(attr);
6801 bold = attr & FOREGROUND_INTENSITY;
6802 attr &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
6804 while (count-- > 0) {
6807 attr = default_attr;
6812 bold = FOREGROUND_INTENSITY;
6815 #ifndef COMMON_LVB_UNDERSCORE
6816 #define COMMON_LVB_UNDERSCORE 0x8000
6818 attr |= COMMON_LVB_UNDERSCORE;
6825 attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
6829 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)) | FOREGROUND_RED;
6833 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_RED)) | FOREGROUND_GREEN;
6837 attr = (attr & ~FOREGROUND_BLUE) | FOREGROUND_GREEN | FOREGROUND_RED;
6841 attr = (attr & ~(FOREGROUND_GREEN | FOREGROUND_RED)) | FOREGROUND_BLUE;
6845 attr = (attr & ~FOREGROUND_GREEN) | FOREGROUND_BLUE | FOREGROUND_RED;
6849 attr = (attr & ~FOREGROUND_RED) | FOREGROUND_BLUE | FOREGROUND_GREEN;
6853 attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6857 attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
6860 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN)) | BACKGROUND_RED;
6863 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_RED)) | BACKGROUND_GREEN;
6866 attr = (attr & ~BACKGROUND_BLUE) | BACKGROUND_GREEN | BACKGROUND_RED;
6869 attr = (attr & ~(BACKGROUND_GREEN | BACKGROUND_RED)) | BACKGROUND_BLUE;
6872 attr = (attr & ~BACKGROUND_GREEN) | BACKGROUND_BLUE | BACKGROUND_RED;
6875 attr = (attr & ~BACKGROUND_RED) | BACKGROUND_BLUE | BACKGROUND_GREEN;
6878 attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
6883 if (rev) attr = constat_attr_color_reverse(attr);
6890 constat_clear(HANDLE handle, WORD attr, DWORD
len, COORD pos)
6894 FillConsoleOutputAttribute(handle, attr,
len, pos, &written);
6895 FillConsoleOutputCharacterW(handle, L
' ',
len, pos, &written);
6900 constat_apply(HANDLE handle,
struct constat *s, WCHAR w)
6902 CONSOLE_SCREEN_BUFFER_INFO csbi;
6903 const int *seq = s->vt100.seq;
6904 int count = s->vt100.state;
6908 if (!GetConsoleScreenBufferInfo(handle, &csbi))
return;
6909 arg0 = (count > 0 && seq[0] > 0);
6910 if (arg0) arg1 = seq[0];
6913 SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr, &s->vt100.reverse));
6916 csbi.dwCursorPosition.X = 0;
6918 csbi.dwCursorPosition.Y -= arg1;
6919 if (csbi.dwCursorPosition.Y < csbi.srWindow.Top)
6920 csbi.dwCursorPosition.Y = csbi.srWindow.Top;
6921 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6924 csbi.dwCursorPosition.X = 0;
6927 csbi.dwCursorPosition.Y += arg1;
6928 if (csbi.dwCursorPosition.Y > csbi.srWindow.Bottom)
6929 csbi.dwCursorPosition.Y = csbi.srWindow.Bottom;
6930 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6933 csbi.dwCursorPosition.X += arg1;
6934 if (csbi.dwCursorPosition.X >= csbi.srWindow.Right)
6935 csbi.dwCursorPosition.X = csbi.srWindow.Right;
6936 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6939 csbi.dwCursorPosition.X -= arg1;
6940 if (csbi.dwCursorPosition.X < csbi.srWindow.Left)
6941 csbi.dwCursorPosition.X = csbi.srWindow.Left;
6942 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6946 arg1 += csbi.srWindow.Left;
6947 if (arg1 > csbi.srWindow.Right)
6948 arg1 = csbi.srWindow.Right;
6949 csbi.dwCursorPosition.X = arg1;
6950 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6953 arg1 += csbi.srWindow.Top;
6954 if (arg1 > csbi.srWindow.Bottom)
6955 arg1 = csbi.srWindow.Bottom;
6956 csbi.dwCursorPosition.Y = arg1;
6957 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6961 pos.Y = arg1 + csbi.srWindow.Top - 1;
6962 if (pos.Y > csbi.srWindow.Bottom) pos.Y = csbi.srWindow.Bottom;
6963 if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
6964 pos.X = arg1 + csbi.srWindow.Left - 1;
6965 if (pos.X > csbi.srWindow.Right) pos.X = csbi.srWindow.Right;
6966 SetConsoleCursorPosition(handle, pos);
6969 switch (arg0 ? arg1 : 0) {
6971 constat_clear(handle, csbi.wAttributes,
6972 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.dwCursorPosition.Y + 1)
6973 - csbi.dwCursorPosition.X),
6974 csbi.dwCursorPosition);
6978 pos.Y = csbi.srWindow.Top;
6979 constat_clear(handle, csbi.wAttributes,
6980 (csbi.dwSize.X * (csbi.dwCursorPosition.Y - csbi.srWindow.Top)
6981 + csbi.dwCursorPosition.X + 1),
6986 pos.Y = csbi.srWindow.Top;
6987 constat_clear(handle, csbi.wAttributes,
6988 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1)),
6994 constat_clear(handle, csbi.wAttributes,
6995 (csbi.dwSize.X * csbi.dwSize.Y),
7001 switch (arg0 ? arg1 : 0) {
7003 constat_clear(handle, csbi.wAttributes,
7004 (csbi.dwSize.X - csbi.dwCursorPosition.X),
7005 csbi.dwCursorPosition);
7009 pos.Y = csbi.dwCursorPosition.Y;
7010 constat_clear(handle, csbi.wAttributes,
7011 csbi.dwCursorPosition.X + 1, pos);
7015 pos.Y = csbi.dwCursorPosition.Y;
7016 constat_clear(handle, csbi.wAttributes,
7017 csbi.dwSize.X, pos);
7022 s->vt100.saved = csbi.dwCursorPosition;
7025 SetConsoleCursorPosition(handle, s->vt100.saved);
7028 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7029 CONSOLE_CURSOR_INFO cci;
7030 GetConsoleCursorInfo(handle, &cci);
7031 cci.bVisible = TRUE;
7032 SetConsoleCursorInfo(handle, &cci);
7036 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7037 CONSOLE_CURSOR_INFO cci;
7038 GetConsoleCursorInfo(handle, &cci);
7039 cci.bVisible = FALSE;
7040 SetConsoleCursorInfo(handle, &cci);
7048 static const long MAXSIZE_CONSOLE_WRITING = 31366;
7052 constat_parse(HANDLE h,
struct constat *s,
const WCHAR **ptrp,
long *lenp)
7054 const WCHAR *
ptr = *ptrp;
7055 long rest,
len = *lenp;
7059 rest = *lenp -
len - 1;
7060 if (s->vt100.state == constat_esc) {
7063 s->vt100.state = constat_init;
7064 if (
len > 0 && *
ptr != L
'[')
continue;
7065 s->vt100.state = constat_esc;
7067 else if (s->vt100.state == constat_esc) {
7070 s->vt100.state = constat_init;
7073 rest = *lenp -
len - 1;
7074 if (rest > 0) --rest;
7075 s->vt100.state = constat_seq;
7076 s->vt100.seq[0] = 0;
7078 else if (s->vt100.state >= constat_seq) {
7079 if (wc >= L
'0' && wc <= L
'9') {
7080 if (s->vt100.state < (
int)numberof(s->vt100.seq)) {
7081 int *seq = &s->vt100.seq[s->vt100.state];
7082 *seq = (*seq * 10) + (wc - L
'0');
7085 else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L
'?') {
7086 s->vt100.seq[s->vt100.state++] = -1;
7090 if (++s->vt100.state < (
int)numberof(s->vt100.seq)) {
7091 s->vt100.seq[s->vt100.state] = 0;
7094 s->vt100.state = (int)numberof(s->vt100.seq);
7098 constat_apply(h, s, wc);
7099 s->vt100.state = constat_init;
7104 else if ((rest = *lenp -
len) < MAXSIZE_CONSOLE_WRITING) {
7120 rb_w32_close(
int fd)
7122 SOCKET sock = TO_SOCKET(fd);
7123 int save_errno =
errno;
7125 if (!is_socket(sock)) {
7126 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7127 constat_delete((HANDLE)sock);
7130 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
7131 socklist_delete(&sock, NULL);
7134 if (closesocket(sock) == SOCKET_ERROR) {
7135 errno = map_errno(WSAGetLastError());
7141 #ifndef INVALID_SET_FILE_POINTER
7142 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
7146 setup_overlapped(OVERLAPPED *ol,
int fd,
int iswrite, rb_off_t *_offset)
7148 memset(ol, 0,
sizeof(*ol));
7153 DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
7157 uint64_t offset = *_offset;
7158 ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
7159 ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
7162 LARGE_INTEGER seek_offset = {0}, current_offset = {0};
7163 if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, ¤t_offset, seek_method)) {
7164 DWORD last_error = GetLastError();
7165 if (last_error != NO_ERROR) {
7166 errno = map_errno(last_error);
7172 *_offset = current_offset.QuadPart;
7174 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7176 DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method);
7178 if (low == INVALID_SET_FILE_POINTER) {
7179 DWORD err = GetLastError();
7180 if (err != NO_ERROR) {
7181 errno = map_errno(err);
7187 ol->OffsetHigh = high;
7190 ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7192 errno = map_errno(GetLastError());
7199 finish_overlapped(OVERLAPPED *ol,
int fd, DWORD size, rb_off_t *_offset)
7201 CloseHandle(ol->hEvent);
7205 DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN;
7207 LARGE_INTEGER seek_offset = {0};
7208 if (seek_method == FILE_BEGIN) {
7209 seek_offset.QuadPart = *_offset;
7212 SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method);
7214 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7215 LONG high = ol->OffsetHigh;
7216 DWORD low = ol->Offset + size;
7217 if (low < ol->Offset)
7219 SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
7226 rb_w32_read_internal(
int fd,
void *buf,
size_t size, rb_off_t *offset)
7228 SOCKET sock = TO_SOCKET(fd);
7236 BOOL islineinput = FALSE;
7239 if (is_socket(sock))
7240 return rb_w32_recv(fd, buf, size, 0);
7243 if (_get_osfhandle(fd) == -1) {
7247 if (!offset && _osfile(fd) & FTEXT) {
7248 return _read(fd, buf, size);
7251 rb_acrt_lowio_lock_fh(fd);
7253 if (!size || _osfile(fd) & FEOFLAG) {
7254 _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7255 rb_acrt_lowio_unlock_fh(fd);
7260 isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
7263 GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
7264 islineinput = (mode & ENABLE_LINE_INPUT) != 0;
7269 constat_reset((HANDLE)_osfhnd(fd));
7281 if (setup_overlapped(&ol, fd, FALSE, offset)) {
7282 rb_acrt_lowio_unlock_fh(fd);
7286 if (!ReadFile((HANDLE)_osfhnd(fd), buf,
len, &read, &ol)) {
7287 err = GetLastError();
7288 if (err == ERROR_NO_DATA && (_osfile(fd) & FPIPE)) {
7290 if (GetNamedPipeHandleState((HANDLE)_osfhnd(fd), &state, NULL, NULL, NULL, NULL, 0) && (state & PIPE_NOWAIT)) {
7291 errno = EWOULDBLOCK;
7294 errno = map_errno(err);
7296 rb_acrt_lowio_unlock_fh(fd);
7299 else if (err != ERROR_IO_PENDING) {
7300 CloseHandle(ol.hEvent);
7301 if (err == ERROR_ACCESS_DENIED)
7303 else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
7304 rb_acrt_lowio_unlock_fh(fd);
7308 errno = map_errno(err);
7310 rb_acrt_lowio_unlock_fh(fd);
7314 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7315 if (wait != WAIT_OBJECT_0) {
7316 if (wait == WAIT_OBJECT_0 + 1)
7319 errno = map_errno(GetLastError());
7320 CloseHandle(ol.hEvent);
7321 CancelIo((HANDLE)_osfhnd(fd));
7322 rb_acrt_lowio_unlock_fh(fd);
7326 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
7327 (err = GetLastError()) != ERROR_HANDLE_EOF) {
7329 if (err != ERROR_BROKEN_PIPE) {
7330 errno = map_errno(err);
7333 CloseHandle(ol.hEvent);
7334 CancelIo((HANDLE)_osfhnd(fd));
7335 rb_acrt_lowio_unlock_fh(fd);
7340 err = GetLastError();
7341 errno = map_errno(err);
7344 finish_overlapped(&ol, fd, read, offset);
7348 buf = (
char *)buf + read;
7349 if (err != ERROR_OPERATION_ABORTED &&
7350 !(isconsole &&
len == 1 && (!islineinput || *((
char *)buf - 1) ==
'\n')) && size > 0)
7354 _set_osflags(fd, _osfile(fd) | FEOFLAG);
7357 rb_acrt_lowio_unlock_fh(fd);
7365 rb_w32_write_internal(
int fd,
const void *buf,
size_t size, rb_off_t *offset)
7367 SOCKET sock = TO_SOCKET(fd);
7375 if (is_socket(sock))
7376 return rb_w32_send(fd, buf, size, 0);
7379 if (_get_osfhandle(fd) == -1) {
7384 if (!offset && (_osfile(fd) & FTEXT) &&
7385 (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
7386 ssize_t w = _write(fd, buf, size);
7387 if (w == (ssize_t)-1 &&
errno == EINVAL) {
7388 errno = map_errno(GetLastError());
7393 rb_acrt_lowio_lock_fh(fd);
7395 if (!size || _osfile(fd) & FEOFLAG) {
7396 rb_acrt_lowio_unlock_fh(fd);
7402 len = (_osfile(fd) & FDEV) ? min(MAXSIZE_CONSOLE_WRITING, size) : size;
7407 if (setup_overlapped(&ol, fd, TRUE, offset)) {
7408 rb_acrt_lowio_unlock_fh(fd);
7412 if (!WriteFile((HANDLE)_osfhnd(fd), buf,
len, &written, &ol)) {
7413 err = GetLastError();
7414 if (err != ERROR_IO_PENDING) {
7415 CloseHandle(ol.hEvent);
7416 if (err == ERROR_ACCESS_DENIED)
7419 errno = map_errno(err);
7421 rb_acrt_lowio_unlock_fh(fd);
7425 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7426 if (wait != WAIT_OBJECT_0) {
7427 if (wait == WAIT_OBJECT_0 + 1)
7430 errno = map_errno(GetLastError());
7431 CloseHandle(ol.hEvent);
7432 CancelIo((HANDLE)_osfhnd(fd));
7433 rb_acrt_lowio_unlock_fh(fd);
7437 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) {
7438 errno = map_errno(GetLastError());
7439 CloseHandle(ol.hEvent);
7440 CancelIo((HANDLE)_osfhnd(fd));
7441 rb_acrt_lowio_unlock_fh(fd);
7446 finish_overlapped(&ol, fd, written, offset);
7449 if (written ==
len) {
7450 buf = (
const char *)buf +
len;
7455 size_t newlen =
len / 2;
7457 size +=
len - newlen;
7462 errno = EWOULDBLOCK;
7465 rb_acrt_lowio_unlock_fh(fd);
7471 rb_w32_read(
int fd,
void *buf,
size_t size)
7473 return rb_w32_read_internal(fd, buf, size, NULL);
7477 rb_w32_write(
int fd,
const void *buf,
size_t size)
7479 return rb_w32_write_internal(fd, buf, size, NULL);
7483 rb_w32_pread(
int descriptor,
void *base,
size_t size, rb_off_t offset)
7485 return rb_w32_read_internal(descriptor, base, size, &offset);
7489 rb_w32_pwrite(
int descriptor,
const void *base,
size_t size, rb_off_t offset)
7491 return rb_w32_write_internal(descriptor, base, size, &offset);
7496 rb_w32_write_console(uintptr_t strarg,
int fd)
7499 DWORD dwMode, reslen;
7503 const WCHAR *
ptr, *next;
7507 handle = (HANDLE)_osfhnd(fd);
7508 if (!GetConsoleMode(handle, &dwMode))
7511 s = constat_handle(handle);
7521 case ENCINDEX_US_ASCII:
7522 case ENCINDEX_ASCII_8BIT:
7524 case ENCINDEX_UTF_8:
7526 if (!
ptr)
return -1L;
7528 case ENCINDEX_UTF_16LE:
7534 if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7535 if (!WriteConsoleW(handle,
ptr,
len, &reslen, NULL))
7536 reslen = (DWORD)-1L;
7540 long curlen = constat_parse(handle, s, (next =
ptr, &next), &
len);
7541 reslen += next -
ptr;
7544 if (!WriteConsoleW(handle,
ptr, curlen, &written, NULL)) {
7545 reslen = (DWORD)-1L;
7554 return (
long)reslen;
7557 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7560 unixtime_to_filetime(time_t time, FILETIME *ft)
7564 tmp.QuadPart = ((
LONG_LONG)time + (
LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7565 ft->dwLowDateTime = tmp.LowPart;
7566 ft->dwHighDateTime = tmp.HighPart;
7573 timespec_to_filetime(
const struct timespec *ts, FILETIME *ft)
7577 tmp.QuadPart = ((
LONG_LONG)ts->tv_sec + (
LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7578 tmp.QuadPart += ts->tv_nsec / 100;
7579 ft->dwLowDateTime = tmp.LowPart;
7580 ft->dwHighDateTime = tmp.HighPart;
7586 wutimensat(
int dirfd,
const WCHAR *path,
const struct timespec *times,
int flags)
7589 FILETIME atime, mtime;
7594 if (dirfd != AT_FDCWD) {
7604 if (wstati128(path, &stat, FALSE)) {
7609 if (timespec_to_filetime(×[0], &atime)) {
7612 if (timespec_to_filetime(×[1], &mtime)) {
7617 GetSystemTimePreciseAsFileTime(&atime);
7622 const DWORD attr = GetFileAttributesW(path);
7623 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7624 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7625 hFile = open_special(path, GENERIC_WRITE, 0);
7626 if (hFile == INVALID_HANDLE_VALUE) {
7627 errno = map_errno(GetLastError());
7631 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7632 errno = map_errno(GetLastError());
7637 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7638 SetFileAttributesW(path, attr);
7646 w32_utimensat(
int dirfd,
const char *path,
const struct timespec *times,
int flags, UINT cp)
7648 WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7652 ret = wutimensat(dirfd, wpath, times, flags);
7660 rb_w32_uutime(
const char *path,
const struct utimbuf *times)
7664 ts[0].tv_sec = times->actime;
7666 ts[1].tv_sec = times->modtime;
7668 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7673 rb_w32_utime(
const char *path,
const struct utimbuf *times)
7677 ts[0].tv_sec = times->actime;
7679 ts[1].tv_sec = times->modtime;
7681 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7686 rb_w32_uutimes(
const char *path,
const struct timeval *times)
7690 ts[0].tv_sec = times[0].tv_sec;
7691 ts[0].tv_nsec = times[0].tv_usec * 1000;
7692 ts[1].tv_sec = times[1].tv_sec;
7693 ts[1].tv_nsec = times[1].tv_usec * 1000;
7694 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7699 rb_w32_utimes(
const char *path,
const struct timeval *times)
7703 ts[0].tv_sec = times[0].tv_sec;
7704 ts[0].tv_nsec = times[0].tv_usec * 1000;
7705 ts[1].tv_sec = times[1].tv_sec;
7706 ts[1].tv_nsec = times[1].tv_usec * 1000;
7707 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7712 rb_w32_uutimensat(
int dirfd,
const char *path,
const struct timespec *times,
int flags)
7714 return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7719 rb_w32_utimensat(
int dirfd,
const char *path,
const struct timespec *times,
int flags)
7721 return w32_utimensat(dirfd, path, times, flags, filecp());
7726 rb_w32_uchdir(
const char *path)
7731 if (!(wpath = utf8_to_wstr(path, NULL)))
7733 ret = _wchdir(wpath);
7740 wmkdir(
const WCHAR *wpath,
int mode)
7745 if (CreateDirectoryW(wpath, NULL) == FALSE) {
7746 errno = map_errno(GetLastError());
7749 if (_wchmod(wpath, mode) == -1) {
7750 RemoveDirectoryW(wpath);
7760 rb_w32_umkdir(
const char *path,
int mode)
7765 if (!(wpath = utf8_to_wstr(path, NULL)))
7767 ret = wmkdir(wpath, mode);
7774 rb_w32_mkdir(
const char *path,
int mode)
7779 if (!(wpath = filecp_to_wstr(path, NULL)))
7781 ret = wmkdir(wpath, mode);
7788 wrmdir(
const WCHAR *wpath)
7792 const DWORD attr = GetFileAttributesW(wpath);
7793 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7794 SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
7796 if (RemoveDirectoryW(wpath) == FALSE) {
7797 errno = map_errno(GetLastError());
7799 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7800 SetFileAttributesW(wpath, attr);
7809 rb_w32_rmdir(
const char *path)
7814 if (!(wpath = filecp_to_wstr(path, NULL)))
7816 ret = wrmdir(wpath);
7823 rb_w32_urmdir(
const char *path)
7828 if (!(wpath = utf8_to_wstr(path, NULL)))
7830 ret = wrmdir(wpath);
7837 wunlink(
const WCHAR *path)
7840 const DWORD SYMLINKD = FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY;
7842 const DWORD attr = GetFileAttributesW(path);
7843 if (attr == (DWORD)-1) {
7845 else if ((attr & SYMLINKD) == SYMLINKD) {
7846 ret = RemoveDirectoryW(path);
7849 if (attr & FILE_ATTRIBUTE_READONLY) {
7850 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7852 ret = DeleteFileW(path);
7855 errno = map_errno(GetLastError());
7857 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7858 SetFileAttributesW(path, attr);
7867 rb_w32_uunlink(
const char *path)
7872 if (!(wpath = utf8_to_wstr(path, NULL)))
7874 ret = wunlink(wpath);
7881 rb_w32_unlink(
const char *path)
7886 if (!(wpath = filecp_to_wstr(path, NULL)))
7888 ret = wunlink(wpath);
7895 rb_w32_uchmod(
const char *path,
int mode)
7900 if (!(wpath = utf8_to_wstr(path, NULL)))
7902 ret = _wchmod(wpath, mode);
7909 fchmod(
int fd,
int mode)
7913 LARGE_INTEGER CreationTime;
7914 LARGE_INTEGER LastAccessTime;
7915 LARGE_INTEGER LastWriteTime;
7916 LARGE_INTEGER ChangeTime;
7917 DWORD FileAttributes;
7918 } info = {{{0}}, {{0}}, {{0}},};
7919 HANDLE h = (HANDLE)_get_osfhandle(fd);
7921 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
7922 if (!(mode & 0200)) info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
7923 if (!SetFileInformationByHandle(h, 0, &info,
sizeof(info))) {
7924 errno = map_errno(GetLastError());
7932 rb_w32_isatty(
int fd)
7937 if (_get_osfhandle(fd) == -1) {
7940 if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
7947 #if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7948 extern long _ftol(
double);
7958 _ftol2_sse(
double d)
7969 int *ip = (
int *)(&x + 1) - 1;
7976 rb_w32_inet_ntop(
int af,
const void *addr,
char *numaddr,
size_t numaddr_len)
7978 return (inet_ntop)(af, (
void *)addr, numaddr, numaddr_len);
7983 rb_w32_inet_pton(
int af,
const char *src,
void *dst)
7985 return (inet_pton)(af, src, dst);
7990 rb_w32_fd_is_text(
int fd)
7992 return _osfile(fd) & FTEXT;
7995 #if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7998 unixtime_to_systemtime(
const time_t t, SYSTEMTIME *st)
8001 if (unixtime_to_filetime(t, &ft))
return -1;
8002 if (!FileTimeToSystemTime(&ft, st))
return -1;
8008 systemtime_to_tm(
const SYSTEMTIME *st,
struct tm *t)
8010 int y = st->wYear, m = st->wMonth, d = st->wDay;
8011 t->tm_sec = st->wSecond;
8012 t->tm_min = st->wMinute;
8013 t->tm_hour = st->wHour;
8014 t->tm_mday = st->wDay;
8015 t->tm_mon = st->wMonth - 1;
8016 t->tm_year = y - 1900;
8017 t->tm_wday = st->wDayOfWeek;
8025 d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
8026 d += ((m - 3) * 153 + 2) / 5;
8034 systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
8036 TIME_ZONE_INFORMATION stdtz;
8039 if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst))
return -1;
8041 GetTimeZoneInformation(&stdtz);
8044 if (tz->StandardBias == tz->DaylightBias)
return 0;
8045 if (!tz->StandardDate.wMonth)
return 0;
8046 if (!tz->DaylightDate.wMonth)
return 0;
8047 if (tz != &stdtz) stdtz = *tz;
8049 stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
8050 if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst))
return 0;
8051 if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
8057 #ifdef HAVE__GMTIME64_S
8058 # ifndef HAVE__LOCALTIME64_S
8060 # define HAVE__LOCALTIME64_S 1
8062 # ifndef MINGW_HAS_SECURE_API
8063 _CRTIMP errno_t __cdecl _gmtime64_s(
struct tm* tm,
const __time64_t *time);
8064 _CRTIMP errno_t __cdecl _localtime64_s(
struct tm* tm,
const __time64_t *time);
8066 # define gmtime_s _gmtime64_s
8067 # define localtime_s _localtime64_s
8072 gmtime_r(
const time_t *tp,
struct tm *rp)
8080 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8081 e = gmtime_s(rp, tp);
8082 if (e != 0)
goto error;
8086 if (unixtime_to_systemtime(*tp, &st))
goto error;
8088 systemtime_to_tm(&st, rp);
8096 localtime_r(
const time_t *tp,
struct tm *rp)
8104 #if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8105 e = localtime_s(rp, tp);
8109 SYSTEMTIME gst, lst;
8110 if (unixtime_to_systemtime(*tp, &gst))
goto error;
8111 rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
8112 systemtime_to_tm(&lst, rp);
8120 rb_w32_wrap_io_handle(HANDLE h,
int flags)
8123 int len =
sizeof(tmp);
8124 int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (
char *)&tmp, &
len);
8125 if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
8127 if (flags & O_NONBLOCK) {
8128 flags &= ~O_NONBLOCK;
8131 socklist_insert((SOCKET)h, f);
8133 else if (flags & O_NONBLOCK) {
8137 return rb_w32_open_osfhandle((intptr_t)h, flags);
8142 rb_w32_unwrap_io_handle(
int fd)
8144 SOCKET sock = TO_SOCKET(fd);
8145 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
8146 if (!is_socket(sock)) {
8147 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
8148 constat_delete((HANDLE)sock);
8151 socklist_delete(&sock, NULL);
8156 #if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8162 rb_w32_pow(
double x,
double y)
8166 unsigned int default_control = _controlfp(0, 0);
8167 _controlfp(_PC_64, _MCW_PC);
8170 _controlfp(default_control, _MCW_PC);
8178 BY_HANDLE_FILE_INFORMATION bhfi;
8189 tmp = rb_check_convert_type_with_id(*file,
T_FILE,
"IO", idTo_io);
8192 if (f == (HANDLE)-1)
return INVALID_HANDLE_VALUE;
8202 len = MultiByteToWideChar(CP_UTF8, 0,
RSTRING_PTR(tmp), -1, NULL, 0);
8205 f = CreateFileW(
ptr, 0,
8206 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
8207 FILE_FLAG_BACKUP_SEMANTICS, NULL);
8209 if (f == INVALID_HANDLE_VALUE)
return f;
8212 if (GetFileType(f) == FILE_TYPE_DISK) {
8213 ZeroMemory(st,
sizeof(*st));
8214 if (get_ino(f, &st->info.fii)) {
8215 st->file_id_p = TRUE;
8218 else if (GetLastError() != ERROR_INVALID_PARAMETER) {
8220 return INVALID_HANDLE_VALUE;
8224 if (GetFileInformationByHandle(f, &st->info.bhfi)) {
8225 st->file_id_p = FALSE;
8229 if (ret) CloseHandle(ret);
8230 return INVALID_HANDLE_VALUE;
8234 close_handle(
VALUE h)
8236 CloseHandle((HANDLE)h);
8246 call_w32_io_info(
VALUE arg)
8249 return (
VALUE)w32_io_info(p->fname, p->st);
8253 rb_w32_file_identical_p(
VALUE fname1,
VALUE fname2)
8256 HANDLE f1 = 0, f2 = 0;
8258 f1 = w32_io_info(&fname1, &st1);
8259 if (f1 == INVALID_HANDLE_VALUE)
return Qfalse;
8262 arg.fname = &fname2;
8267 f2 = w32_io_info(&fname2, &st2);
8269 if (f2 == INVALID_HANDLE_VALUE)
return Qfalse;
8270 if (f2) CloseHandle(f2);
8272 if (st1.file_id_p != st2.file_id_p)
return Qfalse;
8273 if (!st1.file_id_p) {
8274 if (st1.info.bhfi.dwVolumeSerialNumber == st2.info.bhfi.dwVolumeSerialNumber &&
8275 st1.info.bhfi.nFileIndexHigh == st2.info.bhfi.nFileIndexHigh &&
8276 st1.info.bhfi.nFileIndexLow == st2.info.bhfi.nFileIndexLow)
8280 if (st1.info.fii.VolumeSerialNumber == st2.info.fii.VolumeSerialNumber &&
8281 memcmp(&st1.info.fii.FileId, &st2.info.fii.FileId,
sizeof(
FILE_ID_128)) == 0)
8288 rb_w32_set_thread_description(HANDLE th,
const WCHAR *name)
8291 typedef HRESULT (WINAPI *set_thread_description_func)(HANDLE, PCWSTR);
8292 static set_thread_description_func set_thread_description =
8293 (set_thread_description_func)-1;
8294 if (set_thread_description == (set_thread_description_func)-1) {
8296 set_thread_description = (set_thread_description_func)
8297 get_proc_address(
"kernel32",
"SetThreadDescription", NULL);
8299 if (set_thread_description) {
8300 result = set_thread_description(th, name);
8306 rb_w32_set_thread_description_str(HANDLE th,
VALUE name)
8308 int idx, result = FALSE;
8312 return rb_w32_set_thread_description(th, L
"");
8316 if (idx == ENCINDEX_UTF_16LE) {
8317 result = rb_w32_set_thread_description(th, s);
8322 result = rb_w32_set_thread_description(th, s);
8331 #if RUBY_MSVCRT_VERSION < 120
8332 #include "missing/nextafter.c"
8336 rb_w32_mmap(
void *addr,
size_t len,
int prot,
int flags,
int fd, rb_off_t offset)
8340 DWORD protect = PAGE_EXECUTE_READWRITE;
8342 if (fd > 0 || offset) {
8357 ptr = VirtualAlloc(addr,
len, MEM_RESERVE | MEM_COMMIT, protect);
8359 errno = rb_w32_map_errno(GetLastError());
8367 rb_w32_munmap(
void *addr,
size_t len)
8369 if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8370 errno = rb_w32_map_errno(GetLastError());
8378 rb_w32_mprotect(
void *addr,
size_t len,
int prot)
8394 if (prot & PROT_EXEC) {
8395 if (!FlushInstructionCache(GetCurrentProcess(), addr,
len)) {
8396 errno = rb_w32_map_errno(GetLastError());
int ruby_glob_func(const char *path, VALUE arg, void *enc)
Type of a glob callback function.
#define T_FILE
Old name of RUBY_T_FILE.
#define ALLOCV
Old name of RB_ALLOCV.
#define ISSPACE
Old name of rb_isspace.
#define ALLOC
Old name of RB_ALLOC.
#define xfree
Old name of ruby_xfree.
#define xrealloc
Old name of ruby_xrealloc.
#define ECONV_UNDEF_REPLACE
Old name of RUBY_ECONV_UNDEF_REPLACE.
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
#define ECONV_INVALID_REPLACE
Old name of RUBY_ECONV_INVALID_REPLACE.
#define ASSUME
Old name of RBIMPL_ASSUME.
#define ISALPHA
Old name of rb_isalpha.
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
#define ISALNUM
Old name of rb_isalnum.
#define ALLOCV_END
Old name of RB_ALLOCV_END.
void rb_fatal(const char *fmt,...)
Raises the unsung "fatal" exception.
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
int rb_enc_get_index(VALUE obj)
Queries the index of the encoding of the passed object, if any.
int rb_enc_to_index(rb_encoding *enc)
Queries the index of the encoding.
rb_encoding * rb_utf8_encoding(void)
Queries the encoding that represents UTF-8.
rb_encoding * rb_enc_from_index(int idx)
Identical to rb_find_encoding(), except it takes an encoding index instead of a Ruby object.
static const char * rb_enc_name(rb_encoding *enc)
Queries the (canonical) name of the passed encoding.
rb_encoding * rb_filesystem_encoding(void)
Queries the "filesystem" encoding.
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
VALUE rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts)
Identical to rb_str_conv_enc(), except it additionally takes IO encoder options.
VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
Identical to rb_str_new(), except it additionally takes an encoding.
int rb_econv_has_convpath_p(const char *from_encoding, const char *to_encoding)
Queries if there is more than one way to convert between the passed two encodings.
VALUE rb_str_encode_ospath(VALUE path)
Converts a string into an "OS Path" encoding, if any.
void rb_write_error2(const char *str, long len)
Identical to rb_write_error(), except it additionally takes the message's length.
VALUE rb_utf8_str_new(const char *ptr, long len)
Identical to rb_str_new(), except it generates a string of "UTF-8" encoding.
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
#define rb_strlen_lit(str)
Length of a string literal.
VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker)
Raises rb_eNotImpError.
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
char * ptr
Pointer to the underlying memory region, of at least capa bytes.
int len
Length of the buffer.
char * ruby_strdup(const char *str)
This is our own version of strdup(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
#define strdup(s)
Just another name of ruby_strdup.
void ruby_vm_at_exit(void(*func)(ruby_vm_t *))
ruby_vm_at_exit registers a function func to be invoked when a VM passed away.
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
VALUE rb_str_vcatf(VALUE dst, const char *fmt, va_list ap)
Identical to rb_str_catf(), except it takes a va_list.
void rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
Identical to rb_w32_fd_copy(), except it copies unlimited number of file descriptors.
void rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
Destructively overwrites an fdset with another.
void rb_fd_term(rb_fdset_t *f)
Destroys the rb_fdset_t, releasing any memory and resources it used.
#define rb_long2int
Just another name of rb_long2int_inline.
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
#define ALLOCA_N(type, n)
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
VALUE type(ANYARGS)
ANYARGS-ed function type.
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
#define RBIMPL_ATTR_NONNULL(list)
Wraps (or simulates) __attribute__((nonnull))
#define PRI_PIDT_PREFIX
A rb_sprintf() format prefix to be used for a pid_t parameter.
#define rb_fd_init
Initialises the :given :rb_fdset_t.
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
#define FilePathValue(v)
Ensures that the parameter object is a path.
#define errno
Ractor-aware version of errno.
The data structure which wraps the fd_set bitmap used by select(2).
fd_set * fdset
File descriptors buffer.
int capa
Maximum allowed number of FDs.
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
uintptr_t VALUE
Type that represents a Ruby object.
void * ruby_xmalloc(size_t size)
Allocates a storage instance.
void ruby_xfree(void *ptr)
Deallocates a storage instance.
void * ruby_xcalloc(size_t nelems, size_t elemsiz)
Identical to ruby_xmalloc2(), except it returns a zero-filled storage instance.