59#include "ruby/win32.h"
62#include "win32/file.h"
65#include "internal/enc.h"
66#include "internal/object.h"
67#include "internal/static_assert.h"
70#define isdirsep(x) ((x) == '/' || (x) == '\\')
72static int w32_wopen(
const WCHAR *file,
int oflag,
int perm);
73static int w32_stati128(
const char *path,
struct stati128 *st, UINT cp, BOOL lstat);
74static char *w32_getenv(
const char *name, UINT cp);
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)
84#define DLN_CHAR_NEXT(p) CharNextExA(cp, (p), 0)
85#define dln_find_exe_r rb_w32_udln_find_exe_r
86#define dln_find_file_r rb_w32_udln_find_file_r
93#define dln_find_exe_r(fname, path, buf, size) rb_w32_udln_find_exe_r(fname, path, buf, size, cp)
94#define dln_find_file_r(fname, path, buf, size) rb_w32_udln_find_file_r(fname, path, buf, size, cp)
100# define PATH_MAX MAX_PATH
101# elif defined HAVE_SYS_PARAM_H
102# include <sys/param.h>
103# define PATH_MAX MAXPATHLEN
115#define _filbuf _fgetc_nolock
116#define _flsbuf _fputc_nolock
117#define enough_to_get(n) (--(n) >= 0)
118#define enough_to_put(n) (--(n) >= 0)
121#define Debug(something) something
123#define Debug(something)
126#define TO_SOCKET(x) _get_osfhandle(x)
128int rb_w32_reparse_symlink_p(
const WCHAR *path);
130static int has_redirection(
const char *, UINT);
131int rb_w32_wait_events(HANDLE *events,
int num, DWORD timeout);
132static int rb_w32_open_osfhandle(intptr_t osfhandle,
int flags);
133static int wstati128(
const WCHAR *path,
struct stati128 *st, BOOL lstat);
136static FARPROC get_proc_address(
const char *module,
const char *func, HANDLE *mh);
138#define RUBY_CRITICAL if (0) {} else
145 { ERROR_INVALID_FUNCTION, EINVAL },
146 { ERROR_FILE_NOT_FOUND, ENOENT },
147 { ERROR_PATH_NOT_FOUND, ENOENT },
148 { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
149 { ERROR_ACCESS_DENIED, EACCES },
150 { ERROR_INVALID_HANDLE, EBADF },
151 { ERROR_ARENA_TRASHED, ENOMEM },
152 { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
153 { ERROR_INVALID_BLOCK, ENOMEM },
154 { ERROR_BAD_ENVIRONMENT, E2BIG },
155 { ERROR_BAD_FORMAT, ENOEXEC },
156 { ERROR_INVALID_ACCESS, EINVAL },
157 { ERROR_INVALID_DATA, EINVAL },
158 { ERROR_INVALID_DRIVE, ENOENT },
159 { ERROR_CURRENT_DIRECTORY, EACCES },
160 { ERROR_NOT_SAME_DEVICE, EXDEV },
161 { ERROR_NO_MORE_FILES, ENOENT },
162 { ERROR_WRITE_PROTECT, EROFS },
163 { ERROR_BAD_UNIT, ENODEV },
164 { ERROR_NOT_READY, ENXIO },
165 { ERROR_BAD_COMMAND, EACCES },
166 { ERROR_CRC, EACCES },
167 { ERROR_BAD_LENGTH, EACCES },
169 { ERROR_NOT_DOS_DISK, EACCES },
170 { ERROR_SECTOR_NOT_FOUND, EACCES },
171 { ERROR_OUT_OF_PAPER, EACCES },
172 { ERROR_WRITE_FAULT, EIO },
173 { ERROR_READ_FAULT, EIO },
174 { ERROR_GEN_FAILURE, EACCES },
175 { ERROR_LOCK_VIOLATION, EACCES },
176 { ERROR_SHARING_VIOLATION, EACCES },
177 { ERROR_WRONG_DISK, EACCES },
178 { ERROR_SHARING_BUFFER_EXCEEDED, EACCES },
179 { ERROR_BAD_NETPATH, ENOENT },
180 { ERROR_NETWORK_ACCESS_DENIED, EACCES },
181 { ERROR_BAD_NET_NAME, ENOENT },
182 { ERROR_FILE_EXISTS, EEXIST },
183 { ERROR_CANNOT_MAKE, EACCES },
184 { ERROR_FAIL_I24, EACCES },
185 { ERROR_INVALID_PARAMETER, EINVAL },
186 { ERROR_NO_PROC_SLOTS, EAGAIN },
187 { ERROR_DRIVE_LOCKED, EACCES },
188 { ERROR_BROKEN_PIPE, EPIPE },
189 { ERROR_DISK_FULL, ENOSPC },
190 { ERROR_INVALID_TARGET_HANDLE, EBADF },
191 { ERROR_INVALID_HANDLE, EINVAL },
192 { ERROR_WAIT_NO_CHILDREN, ECHILD },
193 { ERROR_CHILD_NOT_COMPLETE, ECHILD },
194 { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
195 { ERROR_NEGATIVE_SEEK, EINVAL },
196 { ERROR_SEEK_ON_DEVICE, EACCES },
197 { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
198 { ERROR_DIRECTORY, ENOTDIR },
199 { ERROR_NOT_LOCKED, EACCES },
200 { ERROR_BAD_PATHNAME, ENOENT },
201 { ERROR_MAX_THRDS_REACHED, EAGAIN },
202 { ERROR_LOCK_FAILED, EACCES },
203 { ERROR_ALREADY_EXISTS, EEXIST },
204 { ERROR_INVALID_STARTING_CODESEG, ENOEXEC },
205 { ERROR_INVALID_STACKSEG, ENOEXEC },
206 { ERROR_INVALID_MODULETYPE, ENOEXEC },
207 { ERROR_INVALID_EXE_SIGNATURE, ENOEXEC },
208 { ERROR_EXE_MARKED_INVALID, ENOEXEC },
209 { ERROR_BAD_EXE_FORMAT, ENOEXEC },
210 { ERROR_ITERATED_DATA_EXCEEDS_64k,ENOEXEC },
211 { ERROR_INVALID_MINALLOCSIZE, ENOEXEC },
212 { ERROR_DYNLINK_FROM_INVALID_RING,ENOEXEC },
213 { ERROR_IOPL_NOT_ENABLED, ENOEXEC },
214 { ERROR_INVALID_SEGDPL, ENOEXEC },
215 { ERROR_AUTODATASEG_EXCEEDS_64k, ENOEXEC },
216 { ERROR_RING2SEG_MUST_BE_MOVABLE, ENOEXEC },
217 { ERROR_RELOC_CHAIN_XEEDS_SEGLIM, ENOEXEC },
218 { ERROR_INFLOOP_IN_RELOC_CHAIN, ENOEXEC },
219 { ERROR_FILENAME_EXCED_RANGE, ENOENT },
220 { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
221#ifndef ERROR_PIPE_LOCAL
222#define ERROR_PIPE_LOCAL 229L
224 { ERROR_PIPE_LOCAL, EPIPE },
225 { ERROR_BAD_PIPE, EPIPE },
226 { ERROR_PIPE_BUSY, EAGAIN },
227 { ERROR_NO_DATA, EPIPE },
228 { ERROR_PIPE_NOT_CONNECTED, EPIPE },
229 { ERROR_OPERATION_ABORTED, EINTR },
230 { ERROR_NOT_ENOUGH_QUOTA, ENOMEM },
231 { ERROR_MOD_NOT_FOUND, ENOENT },
232 { ERROR_PRIVILEGE_NOT_HELD, EACCES, },
233 { ERROR_CANT_RESOLVE_FILENAME, ELOOP, },
236 { WSAEACCES, EACCES },
237 { WSAEFAULT, EFAULT },
238 { WSAEINVAL, EINVAL },
239 { WSAEMFILE, EMFILE },
240 { WSAEWOULDBLOCK, EWOULDBLOCK },
241 { WSAEINPROGRESS, EINPROGRESS },
242 { WSAEALREADY, EALREADY },
243 { WSAENOTSOCK, ENOTSOCK },
244 { WSAEDESTADDRREQ, EDESTADDRREQ },
245 { WSAEMSGSIZE, EMSGSIZE },
246 { WSAEPROTOTYPE, EPROTOTYPE },
247 { WSAENOPROTOOPT, ENOPROTOOPT },
248 { WSAEPROTONOSUPPORT, EPROTONOSUPPORT },
249 { WSAESOCKTNOSUPPORT, ESOCKTNOSUPPORT },
250 { WSAEOPNOTSUPP, EOPNOTSUPP },
251 { WSAEPFNOSUPPORT, EPFNOSUPPORT },
252 { WSAEAFNOSUPPORT, EAFNOSUPPORT },
253 { WSAEADDRINUSE, EADDRINUSE },
254 { WSAEADDRNOTAVAIL, EADDRNOTAVAIL },
255 { WSAENETDOWN, ENETDOWN },
256 { WSAENETUNREACH, ENETUNREACH },
257 { WSAENETRESET, ENETRESET },
258 { WSAECONNABORTED, ECONNABORTED },
259 { WSAECONNRESET, ECONNRESET },
260 { WSAENOBUFS, ENOBUFS },
261 { WSAEISCONN, EISCONN },
262 { WSAENOTCONN, ENOTCONN },
263 { WSAESHUTDOWN, ESHUTDOWN },
264 { WSAETOOMANYREFS, ETOOMANYREFS },
265 { WSAETIMEDOUT, ETIMEDOUT },
266 { WSAECONNREFUSED, ECONNREFUSED },
268 { WSAENAMETOOLONG, ENAMETOOLONG },
269 { WSAEHOSTDOWN, EHOSTDOWN },
270 { WSAEHOSTUNREACH, EHOSTUNREACH },
271 { WSAEPROCLIM, EPROCLIM },
272 { WSAENOTEMPTY, ENOTEMPTY },
273 { WSAEUSERS, EUSERS },
274 { WSAEDQUOT, EDQUOT },
275 { WSAESTALE, ESTALE },
276 { WSAEREMOTE, EREMOTE },
281rb_w32_map_errno(DWORD winerr)
289 for (i = 0; i < (int)(
sizeof(errmap) /
sizeof(*errmap)); i++) {
290 if (errmap[i].winerr == winerr) {
291 return errmap[i].err;
295 if (winerr >= WSABASEERR) {
301#define map_errno rb_w32_map_errno
303static const char *NTLoginName;
305static OSVERSIONINFO osver;
311 memset(&osver, 0,
sizeof(OSVERSIONINFO));
312 osver.dwOSVersionInfoSize =
sizeof(OSVERSIONINFO);
313 GetVersionEx(&osver);
320 return osver.dwMajorVersion;
331 DWORD err = GetLastError(); \
332 if (err == ERROR_LOCK_VIOLATION || err == ERROR_IO_PENDING) \
333 errno = EWOULDBLOCK; \
334 else if (err == ERROR_NOT_LOCKED) \
337 errno = map_errno(err); \
340#define LK_LEN ULONG_MAX
344flock_winnt(uintptr_t self,
int argc, uintptr_t* argv)
348 const HANDLE fh = (HANDLE)self;
349 const int oper = argc;
351 memset(&o, 0,
sizeof(o));
355 LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
358 LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
360 case LOCK_SH|LOCK_NB:
361 LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
363 case LOCK_EX|LOCK_NB:
364 LK_ERR(LockFileEx(fh,
365 LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
366 0, LK_LEN, LK_LEN, &o), i);
369 case LOCK_UN|LOCK_NB:
370 LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
383flock(
int fd,
int oper)
385 const asynchronous_func_t locker = flock_winnt;
387 return rb_w32_asynchronize(locker,
388 (
VALUE)_get_osfhandle(fd), oper, NULL,
394translate_wchar(WCHAR *p,
int from,
int to)
405translate_char(
char *p,
int from,
int to, UINT cp)
408 if ((
unsigned char)*p == from)
410 p = CharNextExA(cp, p, 0);
415#ifndef CSIDL_LOCAL_APPDATA
416#define CSIDL_LOCAL_APPDATA 28
418#ifndef CSIDL_COMMON_APPDATA
419#define CSIDL_COMMON_APPDATA 35
422#define CSIDL_WINDOWS 36
425#define CSIDL_SYSTEM 37
428#define CSIDL_PROFILE 40
433get_special_folder(
int n, WCHAR *buf,
size_t len)
439 if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
440 f = SHGetPathFromIDListEx(pidl, buf,
len, 0);
442 alloc->lpVtbl->Free(alloc, pidl);
443 alloc->lpVtbl->Release(alloc);
450regulate_path(WCHAR *path)
452 WCHAR *p = translate_wchar(path, L
'\\', L
'/');
453 if (p - path == 2 && path[1] == L
':') {
461get_proc_address(
const char *module,
const char *func, HANDLE *mh)
467 h = LoadLibrary(module);
469 h = GetModuleHandle(module);
473 ptr = GetProcAddress(h, func);
485rb_w32_special_folder(
int type)
487 WCHAR path[PATH_MAX];
489 if (!get_special_folder(
type, path, numberof(path)))
return Qnil;
491 return rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
496rb_w32_system_tmpdir(WCHAR *path, UINT
len)
498 static const WCHAR temp[] = L
"temp";
501 if (!get_special_folder(CSIDL_LOCAL_APPDATA, path,
len)) {
502 if (GetSystemWindowsDirectoryW(path,
len))
return 0;
504 p = translate_wchar(path, L
'\\', L
'/');
505 if (p == path || *(p - 1) != L
'/') *p++ = L
'/';
506 if ((UINT)(p - path + numberof(temp)) >=
len)
return 0;
507 memcpy(p, temp,
sizeof(temp));
508 return (UINT)(p - path + numberof(temp) - 1);
523 WCHAR *buffer = NULL;
524 size_t len = 0, len2;
526#define alloc_buffer(len) \
527 if ((buffer = malloc(sizeof(WCHAR) * len)) != NULL) ; \
530 if ((
len = GetEnvironmentVariableW(L
"HOME", NULL, 0)) != 0) {
532 GetEnvironmentVariableW(L
"HOME", buffer,
len);
534 else if ((
len = GetEnvironmentVariableW(L
"USERPROFILE", NULL, 0)) != 0) {
536 GetEnvironmentVariableW(L
"USERPROFILE", buffer,
len);
538 else if ((
len = GetEnvironmentVariableW(L
"HOMEDRIVE", NULL, 0)) != 0 &&
539 (len2 = GetEnvironmentVariableW(L
"HOMEPATH", NULL, 0)) != 0) {
542 len = GetEnvironmentVariableW(L
"HOMEDRIVE", buffer,
len);
543 GetEnvironmentVariableW(L
"HOMEPATH", buffer +
len, len2 -
len);
546 alloc_buffer(MAX_PATH);
547 if (!get_special_folder(CSIDL_PROFILE, buffer, MAX_PATH) &&
548 !get_special_folder(CSIDL_PERSONAL, buffer, MAX_PATH)) {
552 buffer = realloc(buffer,
sizeof(WCHAR) * (lstrlenW(buffer) + 1));
557 regulate_path(buffer);
568 if (!GetEnvironmentVariableW(L
"HOME", env, numberof(env))) {
569 WCHAR *whome = rb_w32_home_dir();
571 _wputenv_s(L
"HOME", whome);
576 if (!GetEnvironmentVariableW(L
"USER", env, numberof(env))) {
578 if (!GetEnvironmentVariableW(L
"USERNAME", env, numberof(env)) &&
579 !GetUserNameW(env, (
len = numberof(env), &
len))) {
580 NTLoginName =
"<Unknown>";
583 _wputenv_s(L
"USER", env);
584 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
588 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
591 if (!GetEnvironmentVariableW(L
"TMPDIR", env, numberof(env)) &&
592 !GetEnvironmentVariableW(L
"TMP", env, numberof(env)) &&
593 !GetEnvironmentVariableW(L
"TEMP", env, numberof(env)) &&
594 rb_w32_system_tmpdir(env, numberof(env))) {
595 _wputenv_s(L
"TMPDIR", env);
599static void init_stdhandle(
void);
603invalid_parameter(
const wchar_t *expr,
const wchar_t *func,
const wchar_t *file,
unsigned int line, uintptr_t dummy)
608int ruby_w32_rtc_error;
616rtc_error_handler(
int e, const
char *src,
int line, const
char *exe, const
char *fmt, ...)
621 if (!ruby_w32_rtc_error)
return 0;
622 str = rb_sprintf(
"%s:%d: ", src, line);
624 rb_str_vcatf(str, fmt, ap);
632static CRITICAL_SECTION select_mutex;
634static CRITICAL_SECTION socklist_mutex;
637static CRITICAL_SECTION conlist_mutex;
639#define conlist_disabled ((st_table *)-1)
641#define thread_exclusive(obj) \
642 for (bool exclusive_for_##obj = (EnterCriticalSection(&obj##_mutex), true); \
643 exclusive_for_##obj; \
644 exclusive_for_##obj = (LeaveCriticalSection(&obj##_mutex), false))
646static CRITICAL_SECTION uenvarea_mutex;
647static char *uenvarea;
652 int state, seq[16], reverse;
657enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
661free_conlist(st_data_t key, st_data_t val, st_data_t arg)
669constat_delete(HANDLE h)
671 thread_exclusive(conlist) {
672 if (conlist && conlist != conlist_disabled) {
673 st_data_t key = (st_data_t)h, val;
674 st_delete(conlist, &key, &val);
685 DeleteCriticalSection(&select_mutex);
686 DeleteCriticalSection(&socklist_mutex);
687 DeleteCriticalSection(&conlist_mutex);
688 thread_exclusive(uenvarea) {
694 DeleteCriticalSection(&uenvarea_mutex);
701 EnterCriticalSection(&socklist_mutex);
703 st_free_table(socklist);
706 LeaveCriticalSection(&socklist_mutex);
708 EnterCriticalSection(&conlist_mutex);
709 if (conlist && conlist != conlist_disabled) {
710 st_foreach(conlist, free_conlist, 0);
711 st_free_table(conlist);
714 LeaveCriticalSection(&conlist_mutex);
717#define ATOMIC_LONG_CAS(var, oldval, newval) InterlockedCompareExchange(&(var), (newval), (oldval))
721install_vm_exit_handler(
void)
723 static LONG installed = 0;
726 while ((i = ATOMIC_LONG_CAS(installed, 0, -1)) != 1) {
732 ATOMIC_LONG_CAS(installed, -1, 1);
748 version = MAKEWORD(2, 0);
749 if (WSAStartup(version, &retdata))
750 rb_fatal(
"Unable to locate winsock library!");
751 if (LOBYTE(retdata.wVersion) != 2)
752 rb_fatal(
"could not find version 2 of winsock dll");
754 InitializeCriticalSection(&select_mutex);
755 InitializeCriticalSection(&socklist_mutex);
756 InitializeCriticalSection(&conlist_mutex);
758 atexit(exit_handler);
761#define MAKE_SOCKDATA(af, fl) ((int)((((int)af)<<4)|((fl)&0xFFFF)))
762#define GET_FAMILY(v) ((int)(((v)>>4)&0xFFFF))
763#define GET_FLAGS(v) ((int)((v)&0xFFFF))
767socklist_insert(SOCKET sock,
int flag)
771 thread_exclusive(socklist) {
773 socklist = st_init_numtable();
774 install_vm_exit_handler();
776 ret = st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
784socklist_lookup(SOCKET sock,
int *flagp)
789 thread_exclusive(socklist) {
790 if (!socklist)
continue;
791 ret = st_lookup(socklist, (st_data_t)sock, &data);
801socklist_delete(SOCKET *sockp,
int *flagp)
807 thread_exclusive(socklist) {
808 if (!socklist)
continue;
809 key = (st_data_t)*sockp;
811 data = (st_data_t)*flagp;
812 ret = st_delete(socklist, &key, &data);
814 *sockp = (SOCKET)key;
824# define _CrtSetReportMode(type,mode) ((void)0)
825# define _RTC_SetErrorFunc(func) ((void)0)
827static void set_pioinfo_extra(
void);
828static int w32_cmdvector(
const WCHAR *,
char ***, UINT,
rb_encoding *);
845rb_w32_init_long_paths(
void)
848 enum {peb_bit_field_offset = 3, is_long_path_aware_process = 0x80};
849 typedef long (WINAPI version_func)(OSVERSIONINFOW *);
850 typedef void *(WINAPI peb_func)(
void);
851 version_func *pRtlGetVersion;
852 peb_func *pRtlGetCurrentPeb;
856 pRtlGetVersion = (version_func *)get_proc_address(
"ntdll.dll",
"RtlGetVersion", NULL);
857 if (!pRtlGetVersion)
return;
858 memset(&osvi, 0,
sizeof(osvi));
859 osvi.dwOSVersionInfoSize =
sizeof(osvi);
860 if (pRtlGetVersion(&osvi))
return;
863 if (osvi.dwMajorVersion < 10)
return;
864 if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 &&
865 osvi.dwBuildNumber < 15063)
return;
867 pRtlGetCurrentPeb = (peb_func *)get_proc_address(
"ntdll.dll",
"RtlGetCurrentPeb", NULL);
868 if (!pRtlGetCurrentPeb)
return;
869 bit_field = (BYTE *)pRtlGetCurrentPeb() + peb_bit_field_offset;
870 *bit_field |= is_long_path_aware_process;
875rb_w32_sysinit(
int *argc,
char ***argv)
877 _CrtSetReportMode(_CRT_ASSERT, 0);
878 _set_invalid_parameter_handler(invalid_parameter);
879 _RTC_SetErrorFunc(rtc_error_handler);
883 SetErrorMode(SEM_FAILCRITICALERRORS|(ruby_w32_wer ? 0 : SEM_NOGPFAULTERRORBOX));
890 *argc = w32_cmdvector(GetCommandLineW(), argv, CP_UTF8, &OnigEncodingUTF_8);
898 InitializeCriticalSection(&uenvarea_mutex);
910 return (
char *)NTLoginName;
913#define MAXCHILDNUM 256
922#define FOREACH_CHILD(v) do { \
923 struct ChildRecord* v; \
924 for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
925#define END_FOREACH_CHILD } while (0)
929FindChildSlot(rb_pid_t pid)
932 FOREACH_CHILD(child) {
933 if (child->pid == pid) {
942FindChildSlotByHandle(HANDLE h)
945 FOREACH_CHILD(child) {
946 if (child->hProcess == h) {
957 HANDLE h = child->hProcess;
958 child->hProcess = NULL;
965FindFreeChildSlot(
void)
967 FOREACH_CHILD(child) {
970 child->hProcess = NULL;
978#define InternalCmdsMax 8
979static const char szInternalCmds[][InternalCmdsMax+1] = {
1027internal_match(
const void *key,
const void *elem)
1029 return strncmp(key, elem, InternalCmdsMax);
1032static int internal_cmd_match(
const char *cmdname);
1036is_internal_cmd(
const char *cmd)
1038 char cmdname[InternalCmdsMax+1], *b = cmdname, c;
1041 if (!(c = *cmd++))
return 0;
1042 }
while (isspace(c));
1045 while (isalpha(c)) {
1047 if (b == cmdname +
sizeof(cmdname))
return 0;
1050 if (c ==
'.') c = *cmd;
1052 case '<':
case '>':
case '|':
1054 case '\0':
case ' ':
case '\t':
case '\n':
1060 return internal_cmd_match(cmdname);
1065internal_cmd_match(
const char *cmdname)
1069 nm = bsearch(cmdname, szInternalCmds,
1070 numberof(szInternalCmds),
1071 sizeof(*szInternalCmds),
1079rb_w32_get_osfhandle(
int fh)
1081 return _get_osfhandle(fh);
1086join_argv(
char *cmd,
char *
const *argv, BOOL escape, UINT cp,
int backslash, BOOL quote_bs)
1090 int len, n, bs, quote;
1092 for (t = argv, q = cmd,
len = 0; (p = *t) != 0; t++) {
1095 if (!*p || strpbrk(p,
" \t\"'")) {
1100 for (bs = 0; *p; ++p) {
1114 memset(q,
'\\', bs);
1119 case '<':
case '>':
case '|':
case '^':
1120 if (escape && !quote) {
1121 len += (n = p - s) + 1;
1132 p = CharNextExA(cp, p, 0) - 1;
1136 len += (n = p - s) + 1;
1137 if (quote)
len += (quote_bs ? bs : 0) + 1;
1140 if (backslash > 0) {
1143 translate_char(q,
'/',
'\\', cp);
1148 if (quote_bs && bs) {
1149 memset(q,
'\\', bs);
1166#define STRNDUPV(ptr, v, src, len) \
1167 (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
1171check_spawn_mode(
int mode)
1193 if (mode == P_OVERLAY) {
1194 WaitForSingleObject(child->hProcess, INFINITE);
1195 GetExitCodeProcess(child->hProcess, &exitcode);
1196 CloseChildHandle(child);
1204CreateChild(
struct ChildRecord *child,
const WCHAR *cmd,
const WCHAR *prog, HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
1207 STARTUPINFOW aStartupInfo;
1208 PROCESS_INFORMATION aProcessInformation;
1209 SECURITY_ATTRIBUTES sa;
1211 if (!cmd && !prog) {
1221 sa.nLength =
sizeof(SECURITY_ATTRIBUTES);
1222 sa.lpSecurityDescriptor = NULL;
1223 sa.bInheritHandle = TRUE;
1225 memset(&aStartupInfo, 0,
sizeof(aStartupInfo));
1226 memset(&aProcessInformation, 0,
sizeof(aProcessInformation));
1227 aStartupInfo.cb =
sizeof(aStartupInfo);
1228 aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
1230 aStartupInfo.hStdInput = hInput;
1233 aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1236 aStartupInfo.hStdOutput = hOutput;
1239 aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1242 aStartupInfo.hStdError = hError;
1245 aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1248 dwCreationFlags |= NORMAL_PRIORITY_CLASS;
1250 if (lstrlenW(cmd) > 32767) {
1257 fRet = CreateProcessW(prog, (WCHAR *)cmd, &sa, &sa,
1258 sa.bInheritHandle, dwCreationFlags, NULL, NULL,
1259 &aStartupInfo, &aProcessInformation);
1260 errno = map_errno(GetLastError());
1268 CloseHandle(aProcessInformation.hThread);
1270 child->hProcess = aProcessInformation.hProcess;
1271 child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
1278is_batch(
const char *cmd)
1280 int len = strlen(cmd);
1281 if (
len <= 4)
return 0;
1283 if (*cmd++ !=
'.')
return 0;
1284 if (strcasecmp(cmd,
"bat") == 0)
return 1;
1285 if (strcasecmp(cmd,
"cmd") == 0)
return 1;
1289#define filecp rb_w32_filecp
1290#define mbstr_to_wstr rb_w32_mbstr_to_wstr
1291#define wstr_to_mbstr rb_w32_wstr_to_mbstr
1292#define acp_to_wstr(str, plen) mbstr_to_wstr(CP_ACP, str, -1, plen)
1293#define wstr_to_acp(str, plen) wstr_to_mbstr(CP_ACP, str, -1, plen)
1294#define filecp_to_wstr(str, plen) mbstr_to_wstr(filecp(), str, -1, plen)
1295#define wstr_to_filecp(str, plen) wstr_to_mbstr(filecp(), str, -1, plen)
1296#define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
1297#define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
1301w32_spawn(
int mode,
const char *cmd,
const char *prog, UINT cp)
1303 char fbuf[PATH_MAX];
1305 const char *shell = NULL;
1306 WCHAR *wcmd = NULL, *wshell = NULL;
1312 char *cmd_sep = NULL;
1314 if (check_spawn_mode(mode))
return -1;
1317 if (!(p = dln_find_exe_r(prog, NULL, fbuf,
sizeof(fbuf)))) {
1322 translate_char(p,
'/',
'\\', cp);
1328 if ((shell = w32_getenv(
"RUBYSHELL", cp)) && (redir = has_redirection(cmd, cp))) {
1329 size_t shell_len = strlen(shell);
1330 size_t cmd_len = strlen(cmd) +
sizeof(
" -c ") + 2;
1331 char *tmp =
ALLOCV(v, shell_len + cmd_len);
1332 memcpy(tmp, shell, shell_len + 1);
1333 translate_char(tmp,
'/',
'\\', cp);
1334 snprintf(tmp + shell_len, cmd_len,
" -c \"%s\"", cmd);
1337 else if ((shell = w32_getenv(
"COMSPEC", cp)) &&
1338 ((redir < 0 ? has_redirection(cmd, cp) : redir) || is_internal_cmd(cmd))) {
1339 size_t cmd_len = strlen(shell) + strlen(cmd) +
sizeof(
" /c \"\"");
1340 char *tmp =
ALLOCV(v, cmd_len);
1341 snprintf(tmp, cmd_len,
"%s /c \"%s\"", shell, cmd);
1345 int len = 0, quote = (*cmd ==
'"') ?
'"' : (*cmd ==
'\'') ?
'\'' : 0;
1347 for (prog = cmd + !!quote;; prog = CharNextExA(cp, prog, 0)) {
1348 if (*prog ==
'/') slash = 1;
1352 STRNDUPV(p, v2, cmd,
len);
1358 if ((
unsigned char)*prog == quote) {
1359 len = prog++ - cmd - 1;
1360 STRNDUPV(p, v2, cmd + 1,
len);
1364 if (quote)
continue;
1365 if (
ISSPACE(*prog) || strchr(
"<>|*?\"", *prog)) {
1367 STRNDUPV(p, v2, cmd,
len + (slash ? strlen(prog) : 0));
1370 sep = *(cmd_sep = &p[
len]);
1377 shell = dln_find_exe_r(shell, NULL, fbuf,
sizeof(fbuf));
1378 if (p && slash) translate_char(p,
'/',
'\\', cp);
1380 shell = p ? p : cmd;
1383 len = strlen(shell);
1384 if (strchr(shell,
' ')) quote = -1;
1385 if (shell == fbuf) {
1388 else if (shell != p && strchr(shell,
'/')) {
1389 STRNDUPV(p, v2, shell,
len);
1392 if (p) translate_char(p,
'/',
'\\', cp);
1393 if (is_batch(shell)) {
1394 int alen = strlen(prog);
1395 cmd = p =
ALLOCV(v,
len + alen + (quote ? 2 : 0) + 1);
1396 if (quote) *p++ =
'"';
1397 memcpy(p, shell,
len);
1399 if (quote) *p++ =
'"';
1400 memcpy(p, prog, alen + 1);
1407 if (!e && shell && !(wshell = mbstr_to_wstr(cp, shell, -1, NULL))) e = E2BIG;
1408 if (cmd_sep) *cmd_sep = sep;
1409 if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1415 if (CreateChild(child, wcmd, wshell, NULL, NULL, NULL, 0)) {
1416 ret = child_result(child, mode);
1427rb_w32_spawn(
int mode,
const char *cmd,
const char *prog)
1430 return w32_spawn(mode, cmd, prog, filecp());
1435rb_w32_uspawn(
int mode,
const char *cmd,
const char *prog)
1437 return w32_spawn(mode, cmd, prog, CP_UTF8);
1442w32_spawn_process(
int mode,
const char *prog,
char *
const *argv,
1443 int in_fd,
int out_fd,
int err_fd, DWORD flags, UINT cp)
1448 char *cmd, fbuf[PATH_MAX];
1449 WCHAR *wcmd = NULL, *wprog = NULL;
1453 HANDLE in_handle = NULL, out_handle = NULL, err_handle = NULL;
1455 if (check_spawn_mode(mode))
return -1;
1458 in_handle = (HANDLE)rb_w32_get_osfhandle(in_fd);
1461 out_handle = (HANDLE)rb_w32_get_osfhandle(out_fd);
1464 err_handle = (HANDLE)rb_w32_get_osfhandle(err_fd);
1467 if (!prog) prog = argv[0];
1468 if ((shell = w32_getenv(
"COMSPEC", cp)) && internal_cmd_match(prog)) {
1472 else if ((cmd = dln_find_exe_r(prog, NULL, fbuf,
sizeof(fbuf)))) {
1473 if (cmd == prog) strlcpy(cmd = fbuf, prog,
sizeof(fbuf));
1474 translate_char(cmd,
'/',
'\\', cp);
1477 else if (strchr(prog,
'/')) {
1479 if (
len <
sizeof(fbuf))
1480 strlcpy(cmd = fbuf, prog,
sizeof(fbuf));
1482 STRNDUPV(cmd, v, prog,
len);
1483 translate_char(cmd,
'/',
'\\', cp);
1486 if (c_switch || is_batch(prog)) {
1488 progs[0] = (
char *)prog;
1490 len = join_argv(NULL, progs, TRUE, cp, 1, FALSE);
1491 if (c_switch)
len += 3;
1493 if (argv[0])
len += join_argv(NULL, argv, TRUE, cp, 0, FALSE);
1495 join_argv(cmd, progs, TRUE, cp, 1, FALSE);
1496 if (c_switch) strlcat(cmd,
" /c",
len);
1497 if (argv[0]) join_argv(cmd + strlcat(cmd,
" ",
len), argv, TRUE, cp, 0, FALSE);
1498 prog = c_switch ? shell : 0;
1501 len = join_argv(NULL, argv, FALSE, cp, 1, TRUE);
1503 join_argv(cmd, argv, FALSE, cp, 1, TRUE);
1506 if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1508 if (!e && prog && !(wprog = mbstr_to_wstr(cp, prog, -1, NULL))) e = E2BIG;
1512 if (CreateChild(child, wcmd, wprog, in_handle, out_handle, err_handle, flags)) {
1513 ret = child_result(child, mode);
1524rb_w32_aspawn_flags(
int mode,
const char *prog,
char *
const *argv, DWORD flags)
1527 return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, filecp());
1532rb_w32_uaspawn_flags(
int mode,
const char *prog,
char *
const *argv, DWORD flags)
1534 return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, CP_UTF8);
1539rb_w32_aspawn(
int mode,
const char *prog,
char *
const *argv)
1541 return w32_spawn_process(mode, prog, argv, -1, -1, -1, 0, filecp());
1546rb_w32_uaspawn(
int mode,
const char *prog,
char *
const *argv)
1548 return rb_w32_uaspawn_flags(mode, prog, argv, 0);
1553rb_w32_uspawn_process(
int mode,
const char *prog,
char *
const *argv,
1554 int in_fd,
int out_fd,
int err_fd, DWORD flags)
1556 return w32_spawn_process(mode, prog, argv, in_fd, out_fd, err_fd,
1578insert(
const char *path,
VALUE vinfo,
void *enc)
1584 if (!tmpcurr)
return -1;
1586 tmpcurr->len = strlen(path);
1587 tmpcurr->str =
strdup(path);
1588 if (!tmpcurr->str)
return -1;
1589 tmpcurr->flags |= NTMALLOC;
1591 *tail = &tmpcurr->next;
1600 char buffer[PATH_MAX], *buf = buffer;
1604 if (patt->len >= PATH_MAX)
1605 if (!(buf = malloc(patt->len + 1)))
return 0;
1607 memcpy(buf, patt->str, patt->len);
1608 buf[patt->len] =
'\0';
1609 translate_char(buf,
'\\',
'/', cp);
1610 status = ruby_brace_glob_with_enc(buf, 0, insert, (
VALUE)&tail, enc);
1614 if (status || last == tail)
return 0;
1615 if (patt->flags & NTMALLOC)
1628has_redirection(
const char *cmd, UINT cp)
1638 for (ptr = cmd; *ptr;) {
1644 else if (quote == *ptr)
1660 if (*++ptr !=
'_' && !
ISALPHA(*ptr))
break;
1661 while (*++ptr ==
'_' ||
ISALNUM(*ptr));
1662 if (*ptr && *ptr++ ==
'%')
return TRUE;
1668 ptr = CharNextExA(cp, ptr, 0);
1676static inline WCHAR *
1677skipspace(WCHAR *ptr)
1686w32_cmdvector(
const WCHAR *cmd,
char ***vec, UINT cp,
rb_encoding *enc)
1689 int elements, strsz, done;
1690 int slashes, escape;
1691 WCHAR *ptr, *base, *cmdline;
1692 char *cptr, *buffer;
1708 ptr = cmdline = wcsdup(cmd);
1719 while (*(ptr = skipspace(ptr))) {
1721 quote = slashes = globbing = escape = 0;
1722 for (done = 0; !done && *ptr; ) {
1731 if (quote != L
'\'') slashes++;
1772 if (!(slashes & 1)) {
1775 else if (quote == *ptr) {
1776 if (quote == L
'"' && quote == ptr[1])
1786 ptr = CharNextW(ptr);
1809 slashes = quote = 0;
1810 while (p < base +
len) {
1814 if (quote != L
'\'') slashes++;
1819 if (!(slashes & 1) && quote && quote != c) {
1824 memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
1825 sizeof(WCHAR) * (base +
len - p));
1826 len -= ((slashes + 1) >> 1) + (~slashes & 1);
1827 p -= (slashes + 1) >> 1;
1828 if (!(slashes & 1)) {
1830 if (quote == L
'"' && quote == *p)
1851 if (!curr)
goto do_nothing;
1852 curr->str = rb_w32_wstr_to_mbstr(cp, base,
len, &curr->len);
1853 curr->flags |= NTMALLOC;
1855 if (globbing && (tail = cmdglob(curr, cmdtail, cp, enc))) {
1860 cmdtail = &curr->next;
1870 for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
1872 strsz += (curr->len + 1);
1875 len = (elements+1)*
sizeof(
char *) + strsz;
1876 buffer = (
char *)malloc(
len);
1879 while ((curr = cmdhead) != 0) {
1880 cmdhead = curr->next;
1881 if (curr->flags & NTMALLOC) free(curr->str);
1885 for (vptr = *vec; *vptr; ++vptr);
1901 vptr = (
char **) buffer;
1903 cptr = buffer + (elements+1) *
sizeof(
char *);
1905 while ((curr = cmdhead) != 0) {
1906 memcpy(cptr, curr->str, curr->len);
1907 cptr[curr->len] =
'\0';
1909 cptr += curr->len + 1;
1910 cmdhead = curr->next;
1911 if (curr->flags & NTMALLOC) free(curr->str);
1916 *vec = (
char **) buffer;
1928open_special(
const WCHAR *path, DWORD access, DWORD flags)
1930 const DWORD share_mode =
1931 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
1932 return CreateFileW(path, access, share_mode, NULL, OPEN_EXISTING,
1933 FILE_FLAG_BACKUP_SEMANTICS|flags, NULL);
1943#define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] & (1 << (i) % CHAR_BIT))
1944#define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
1946#define BitOfIsDir(n) ((n) * 2)
1947#define BitOfIsRep(n) ((n) * 2 + 1)
1948#define DIRENT_PER_CHAR (CHAR_BIT / 2)
1950static const WCHAR namespace_prefix[] = {L
'\\', L
'\\', L
'?', L
'\\'};
1956get_handle_pathname(HANDLE fh, WCHAR **pathptr, DWORD add)
1958 DWORD
len = GetFinalPathNameByHandleW(fh, NULL, 0, 0);
1960 WCHAR *path = malloc((
len + add + 1) *
sizeof(WCHAR));
1961 if (!(*pathptr = path))
return 0;
1962 len = GetFinalPathNameByHandleW(fh, path,
len + 1, 0);
1972open_dir_handle(
const WCHAR *filename, WIN32_FIND_DATAW *fd)
1976 WCHAR *fullname = 0;
1984 fh = open_special(filename, 0, 0);
1985 if (fh != INVALID_HANDLE_VALUE) {
1986 len = get_handle_pathname(fh, &fullname, wildcard_len);
1990 len = lstrlenW(filename);
1991 fullname = malloc((
len + wildcard_len + 1) *
sizeof(WCHAR));
1992 if (!fullname)
return INVALID_HANDLE_VALUE;
1998 p = &fullname[
len-1];
1999 if (!(isdirsep(*p) || *p == L
':')) *++p = L
'\\';
2006 fh = FindFirstFileW(fullname, fd);
2007 if (fh == INVALID_HANDLE_VALUE) {
2008 errno = map_errno(GetLastError());
2016w32_wopendir(
const WCHAR *wpath)
2019 WIN32_FIND_DATAW fd;
2032 if (wstati128(wpath, &sbuf, FALSE) < 0) {
2035 if (!(sbuf.st_mode & S_IFDIR) &&
2036 (!
ISALPHA(wpath[0]) || wpath[1] != L
':' || wpath[2] != L
'\0' ||
2037 ((1 << ((wpath[0] & 0x5f) -
'A')) & GetLogicalDrives()) == 0)) {
2041 fh = open_dir_handle(wpath, &fd);
2042 if (fh == INVALID_HANDLE_VALUE) {
2049 p = calloc(1,
sizeof(
DIR));
2053 pathlen = lstrlenW(wpath);
2063 len = lstrlenW(fd.cFileName) + 1;
2064 altlen = lstrlenW(fd.cAlternateFileName) + 1;
2070 tmpW = realloc(p->start, (idx +
len + altlen) *
sizeof(WCHAR));
2080 memcpy(&p->start[idx], fd.cFileName,
len *
sizeof(WCHAR));
2081 memcpy(&p->start[idx +
len], fd.cAlternateFileName, altlen *
sizeof(WCHAR));
2083 if (p->nfiles % DIRENT_PER_CHAR == 0) {
2084 tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
2088 p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
2090 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2091 SetBit(p->bits, BitOfIsDir(p->nfiles));
2092 if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2093 WCHAR *tmppath = malloc((pathlen +
len + 1) *
sizeof(WCHAR));
2094 memcpy(tmppath, wpath, pathlen *
sizeof(WCHAR));
2095 tmppath[pathlen] = L
'\\';
2096 memcpy(tmppath + pathlen + 1, fd.cFileName,
len *
sizeof(WCHAR));
2097 if (rb_w32_reparse_symlink_p(tmppath))
2098 SetBit(p->bits, BitOfIsRep(p->nfiles));
2103 idx +=
len + altlen;
2104 }
while (FindNextFileW(fh, &fd));
2115 UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
2121rb_w32_wstr_to_mbstr(UINT cp,
const WCHAR *wstr,
int clen,
long *plen)
2124 int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL);
2125 if (!(ptr = malloc(
len)))
return 0;
2126 WideCharToMultiByte(cp, 0, wstr, clen, ptr,
len, NULL, NULL);
2129 if (clen == -1) --
len;
2137rb_w32_mbstr_to_wstr(UINT cp,
const char *str,
int clen,
long *plen)
2140 int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0);
2141 if (!(ptr = malloc(
sizeof(WCHAR) *
len)))
return 0;
2142 MultiByteToWideChar(cp, 0, str, clen, ptr,
len);
2145 if (clen == -1) --
len;
2153rb_w32_opendir(
const char *filename)
2156 WCHAR *wpath = filecp_to_wstr(filename, NULL);
2159 ret = w32_wopendir(wpath);
2166rb_w32_uopendir(
const char *filename)
2169 WCHAR *wpath = utf8_to_wstr(filename, NULL);
2172 ret = w32_wopendir(wpath);
2183move_to_next_entry(
DIR *dirp)
2187 dirp->curr += lstrlenW(dirp->curr) + 1;
2188 dirp->curr += lstrlenW(dirp->curr) + 1;
2189 if (dirp->curr >= (dirp->start + dirp->size)) {
2201win32_direct_conv(
const WCHAR *file,
const WCHAR *alt,
struct direct *entry,
const void *enc)
2203 UINT cp = *((UINT *)enc);
2204 if (!(entry->d_name = wstr_to_mbstr(cp, file, -1, &entry->d_namlen)))
2208 entry->d_altname = wstr_to_mbstr(cp, alt, -1, &altlen);
2209 entry->d_altlen = altlen;
2216rb_w32_conv_from_wchar(
const WCHAR *wstr,
rb_encoding *enc)
2219 long len = lstrlenW(wstr);
2220 int encindex = rb_enc_to_index(enc);
2222 if (encindex == ENCINDEX_UTF_16LE) {
2223 return rb_enc_str_new((
char *)wstr,
len *
sizeof(WCHAR), enc);
2226#if SIZEOF_INT < SIZEOF_LONG
2227# error long should equal to int on Windows
2230 len = WideCharToMultiByte(CP_UTF8, 0, wstr, clen, NULL, 0, NULL, NULL);
2231 src = rb_enc_str_new(0,
len, rb_enc_from_index(ENCINDEX_UTF_8));
2232 WideCharToMultiByte(CP_UTF8, 0, wstr, clen, RSTRING_PTR(src),
len, NULL, NULL);
2235 case ENCINDEX_ASCII_8BIT:
2236 case ENCINDEX_US_ASCII:
2238 case ENCINDEX_UTF_8:
2247rb_w32_conv_from_wstr(
const WCHAR *wstr,
long *lenp,
rb_encoding *enc)
2249 VALUE str = rb_w32_conv_from_wchar(wstr, enc);
2253 if (
NIL_P(str))
return wstr_to_utf8(wstr, lenp);
2254 *lenp =
len = RSTRING_LEN(str);
2255 memcpy(ptr = malloc(
len + 1), RSTRING_PTR(str),
len);
2262ruby_direct_conv(
const WCHAR *file,
const WCHAR *alt,
struct direct *entry,
const void *enc)
2264 if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
2268 entry->d_altname = rb_w32_conv_from_wstr(alt, &altlen, enc);
2269 entry->d_altlen = altlen;
2276readdir_internal(
DIR *dirp, BOOL (*conv)(
const WCHAR *,
const WCHAR *,
struct direct *,
const void *),
const void *enc)
2278 static long dummy_ino = 0;
2285 free(dirp->dirstr.d_name);
2286 free(dirp->dirstr.d_altname);
2287 dirp->dirstr.d_altname = 0;
2288 dirp->dirstr.d_altlen = 0;
2289 conv(dirp->curr, dirp->curr + lstrlenW(dirp->curr) + 1, &dirp->dirstr, enc);
2294 dirp->dirstr.d_ino = (ino_t)(InterlockedIncrement(&dummy_ino) - 1);
2300 if (GetBit(dirp->bits, BitOfIsRep(dirp->loc)))
2301 dirp->dirstr.d_type = DT_LNK;
2302 else if (GetBit(dirp->bits, BitOfIsDir(dirp->loc)))
2303 dirp->dirstr.d_type = DT_DIR;
2305 dirp->dirstr.d_type = DT_REG;
2311 move_to_next_entry(dirp);
2313 return &(dirp->dirstr);
2324 int idx = rb_enc_to_index(enc);
2325 if (idx == ENCINDEX_ASCII_8BIT) {
2326 const UINT cp = filecp();
2327 return readdir_internal(dirp, win32_direct_conv, &cp);
2329 else if (idx == ENCINDEX_UTF_8) {
2330 const UINT cp = CP_UTF8;
2331 return readdir_internal(dirp, win32_direct_conv, &cp);
2334 return readdir_internal(dirp, ruby_direct_conv, enc);
2339rb_w32_ureaddir(
DIR *dirp)
2341 const UINT cp = CP_UTF8;
2342 return readdir_internal(dirp, win32_direct_conv, &cp);
2351rb_w32_telldir(
DIR *dirp)
2362rb_w32_seekdir(
DIR *dirp,
long loc)
2364 if (dirp->loc > loc) rb_w32_rewinddir(dirp);
2366 while (dirp->curr && dirp->loc < loc) {
2367 move_to_next_entry(dirp);
2377rb_w32_rewinddir(
DIR *dirp)
2379 dirp->curr = dirp->start;
2389rb_w32_closedir(
DIR *dirp)
2392 free(dirp->dirstr.d_name);
2393 free(dirp->dirstr.d_altname);
2415 CRITICAL_SECTION _lock;
2417#define FILE_COUNT(stream) ((vcruntime_file*)stream)->_cnt
2418#define FILE_READPTR(stream) ((vcruntime_file*)stream)->_ptr
2419#define FILE_FILENO(stream) ((vcruntime_file*)stream)->_file
2422typedef char lowio_text_mode;
2423typedef char lowio_pipe_lookahead[3];
2426 CRITICAL_SECTION lock;
2429 unsigned char osfile;
2430 lowio_text_mode textmode;
2431 lowio_pipe_lookahead _pipe_lookahead;
2433 uint8_t unicode : 1;
2434 uint8_t utf8translations : 1;
2435 uint8_t dbcsBufferUsed : 1;
2439#if !defined _CRTIMP || defined __MINGW32__
2441#define _CRTIMP __declspec(dllimport)
2444static ioinfo ** __pioinfo = NULL;
2446static inline ioinfo* _pioinfo(
int);
2449#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
2450#define _osfhnd(i) (_pioinfo(i)->osfhnd)
2451#define _osfile(i) (_pioinfo(i)->osfile)
2452#define rb_acrt_lowio_lock_fh(i) EnterCriticalSection(&_pioinfo(i)->lock)
2453#define rb_acrt_lowio_unlock_fh(i) LeaveCriticalSection(&_pioinfo(i)->lock)
2455static size_t pioinfo_extra = 0;
2459set_pioinfo_extra(
void)
2461#define FUNCTION_RET 0xc3
2463# define UCRTBASE "ucrtbased.dll"
2465# define UCRTBASE "ucrtbase.dll"
2480 char *p = (
char*)get_proc_address(UCRTBASE,
"_isatty", NULL);
2483#if defined(_M_ARM64) || defined(__aarch64__)
2484#define IS_INSN(pc, name) ((*(pc) & name##_mask) == name##_id)
2485 const int max_num_inst = 500;
2486 uint32_t *start = (uint32_t*)p;
2487 uint32_t *end_limit = (start + max_num_inst);
2488 uint32_t *pc = start;
2491 fprintf(stderr,
"_isatty proc not found in " UCRTBASE
"\n");
2496 const uint32_t ret_id = 0xd65f0000;
2497 const uint32_t ret_mask = 0xfffffc1f;
2498 for(; pc < end_limit; pc++) {
2499 if (IS_INSN(pc, ret)) {
2503 if (pc == end_limit) {
2504 fprintf(stderr,
"end of _isatty not found in " UCRTBASE
"\n");
2509 const uint32_t adrp_id = 0x90000000;
2510 const uint32_t adrp_mask = 0x9f000000;
2511 const uint32_t add_id = 0x11000000;
2512 const uint32_t add_mask = 0x7fc00000;
2513 for(; pc > start; pc--) {
2514 if (IS_INSN(pc, adrp) && IS_INSN(pc + 1, add)) {
2519 fprintf(stderr,
"pioinfo mark not found in " UCRTBASE
"\n");
2529 const uint32_t adrp_insn = *pc;
2530 const uint32_t adrp_immhi = (adrp_insn & 0x00ffffe0) >> 5;
2531 const uint32_t adrp_immlo = (adrp_insn & 0x60000000) >> (5 + 19 + 5);
2532 const int64_t adrp_sign = (adrp_insn & 0x00800000) ? ~0x001fffff : 0;
2534 const int64_t adrp_imm = (adrp_sign | (adrp_immhi << 2) | adrp_immlo) << 12;
2536 const uint64_t adrp_base = (uint64_t)pc & 0xfffffffffffff000;
2538 const uint32_t add_insn = *(pc + 1);
2539 const uint64_t add_imm = (add_insn & 0x3ffc00) >> (5 + 5);
2541 __pioinfo = (
ioinfo**)(adrp_base + adrp_imm + add_imm);
2549# define FUNCTION_BEFORE_RET_MARK "\x48\x83\xc4"
2550# define FUNCTION_SKIP_BYTES 1
2553# define PIOINFO_MARK "\x48\x8d\x0d"
2556# define PIOINFO_MARK "\x48\x8d\x15"
2561# define FUNCTION_BEFORE_RET_MARK "\x5d"
2563# define FUNCTION_BEFORE_RET_MARK_2 "\xc9"
2564# define FUNCTION_SKIP_BYTES 0
2566# define PIOINFO_MARK "\x8B\x04\x85"
2569 for (pend += 10; pend < p + 500; pend++) {
2571 if ((memcmp(pend, FUNCTION_BEFORE_RET_MARK,
sizeof(FUNCTION_BEFORE_RET_MARK) - 1) == 0
2572# ifdef FUNCTION_BEFORE_RET_MARK_2
2573 || memcmp(pend, FUNCTION_BEFORE_RET_MARK_2,
sizeof(FUNCTION_BEFORE_RET_MARK_2) - 1) == 0
2576 *(pend + (
sizeof(FUNCTION_BEFORE_RET_MARK) - 1) + FUNCTION_SKIP_BYTES) == (
char)FUNCTION_RET) {
2578 for (pend -= (
sizeof(PIOINFO_MARK) - 1); pend > p; pend--) {
2579 if (memcmp(pend, PIOINFO_MARK,
sizeof(PIOINFO_MARK) - 1) == 0) {
2588 fprintf(stderr,
"unexpected " UCRTBASE
"\n");
2592 p +=
sizeof(PIOINFO_MARK) - 1;
2594 rel = *(int32_t*)(p);
2595 rip = p +
sizeof(int32_t);
2596 __pioinfo = (
ioinfo**)(rip + rel);
2598 __pioinfo = *(
ioinfo***)(p);
2603 fd = _open(
"NUL", O_RDONLY);
2604 for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra +=
sizeof(
void *)) {
2605 if (_osfhnd(fd) == _get_osfhandle(fd)) {
2611 if (pioinfo_extra > 64) {
2620 const size_t sizeof_ioinfo =
sizeof(
ioinfo) + pioinfo_extra;
2621 return (
ioinfo*)((
char*)__pioinfo[fd >> IOINFO_L2E] +
2622 (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
2625#define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2626#define _set_osflags(fh, flags) (_osfile(fh) = (flags))
2631#define FNOINHERIT 0x10
2636static int is_socket(SOCKET);
2637static int is_console(SOCKET);
2641rb_w32_io_cancelable_p(
int fd)
2643 return is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd));
2648rb_w32_open_osfhandle(intptr_t osfhandle,
int flags)
2657 if (flags & O_APPEND)
2658 fileflags |= FAPPEND;
2663 if (flags & O_NOINHERIT)
2664 fileflags |= FNOINHERIT;
2667 hF = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2668 fh = _open_osfhandle((intptr_t)hF, 0);
2676 rb_acrt_lowio_lock_fh(fh);
2678 _set_osfhnd(fh, osfhandle);
2682 _set_osflags(fh, fileflags);
2683 rb_acrt_lowio_unlock_fh(fh);
2694#define open_null(fd) \
2696 (nullfd = open("NUL", O_RDWR)) : 0), \
2697 ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)), \
2700 if (fileno(stdin) < 0) {
2701 FILE_FILENO(stdin) = open_null(0);
2704 setmode(fileno(stdin), O_BINARY);
2706 if (fileno(stdout) < 0) {
2707 FILE_FILENO(stdout) = open_null(1);
2709 if (fileno(stderr) < 0) {
2710 FILE_FILENO(stderr) = open_null(2);
2712 if (nullfd >= 0 && !keep) close(nullfd);
2713 setvbuf(stderr, NULL, _IONBF, 0);
2716 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
2718 if (GetConsoleMode(h, &m)) {
2719#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
2720#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
2722 SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
2731is_socket(SOCKET sock)
2733 if (socklist_lookup(sock, NULL))
2741rb_w32_is_socket(
int fd)
2743 return is_socket(TO_SOCKET(fd));
2756rb_w32_strerror(
int e)
2758 static char buffer[512];
2763 strlcpy(buffer,
"Unknown Error",
sizeof(buffer));
2764 else if (e > sys_nerr) {
2765#if WSAEWOULDBLOCK != EWOULDBLOCK
2766 if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
2770 for (s = 0; s < (int)(
sizeof(errmap)/
sizeof(*errmap)); s++)
2771 if (errmap[s].winerr == WSAEWOULDBLOCK)
2773 for (i = s; i < (int)(
sizeof(errmap)/
sizeof(*errmap)); i++)
2774 if (errmap[i].err == e) {
2775 e = errmap[i].winerr;
2780 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2781 FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
2782 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
2783 buffer,
sizeof(buffer), NULL) == 0 &&
2784 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2785 FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
2786 buffer,
sizeof(buffer), NULL) == 0)
2787 strlcpy(buffer,
"Unknown Error",
sizeof(buffer));
2790 strlcpy(buffer, strerror(e),
sizeof(buffer));
2793 while ((p = strpbrk(p,
"\r\n")) != NULL) {
2794 memmove(p, p + 1, strlen(p));
2845 return (uid == ROOT_UID ? 0 : -1);
2852 return (gid == ROOT_GID ? 0 : -1);
2861ioctl(
int i,
int u, ...)
2868rb_w32_fdset(
int fd, fd_set *set)
2877rb_w32_fdclr(
int fd, fd_set *set)
2880 SOCKET s = TO_SOCKET(fd);
2882 for (i = 0; i < set->fd_count; i++) {
2883 if (set->fd_array[i] == s) {
2884 memmove(&set->fd_array[i], &set->fd_array[i+1],
2885 sizeof(set->fd_array[0]) * (--set->fd_count - i));
2895rb_w32_fdisset(
int fd, fd_set *set)
2898 SOCKET s = TO_SOCKET(fd);
2899 if (s == (SOCKET)INVALID_HANDLE_VALUE)
2901 RUBY_CRITICAL {ret = __WSAFDIsSet(s, set);}
2909 max = min(src->fd_count, (UINT)max);
2910 if ((UINT)dst->
capa < (UINT)max) {
2911 dst->
capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2915 memcpy(dst->
fdset->fd_array, src->fd_array,
2916 max *
sizeof(src->fd_array[0]));
2917 dst->
fdset->fd_count = src->fd_count;
2924 if ((UINT)dst->
capa < src->
fdset->fd_count) {
2925 dst->
capa = (src->
fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2929 memcpy(dst->
fdset->fd_array, src->
fdset->fd_array,
2930 src->
fdset->fd_count *
sizeof(src->
fdset->fd_array[0]));
2944extract_fd(
rb_fdset_t *dst, fd_set *src,
int (*func)(SOCKET))
2950 while (s < src->fd_count) {
2951 SOCKET fd = src->fd_array[s];
2953 if (!func || (*func)(fd)) {
2957 for (d = 0; d < dst->
fdset->fd_count; d++) {
2958 if (dst->
fdset->fd_array[d] == fd)
2961 if (d == dst->
fdset->fd_count) {
2962 if ((
int)dst->
fdset->fd_count >= dst->
capa) {
2963 dst->
capa = (dst->
fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2966 dst->
fdset->fd_array[dst->
fdset->fd_count++] = fd;
2970 &src->fd_array[s+1],
2971 sizeof(src->fd_array[0]) * (--src->fd_count - s));
2981 return dst ? dst->
fdset->fd_count : m;
2986copy_fd(fd_set *dst, fd_set *src)
2989 if (!src || !dst)
return 0;
2991 for (s = 0; s < src->fd_count; ++s) {
2992 SOCKET fd = src->fd_array[s];
2994 for (d = 0; d < dst->fd_count; ++d) {
2995 if (dst->fd_array[d] == fd)
2998 if (d == dst->fd_count) {
2999 dst->fd_array[dst->fd_count++] = fd;
3003 return dst->fd_count;
3008save_fd(SOCKET *dst,
const fd_set *src)
3010 if (src) memcpy(dst, src->fd_array, src->fd_count *
sizeof(SOCKET));
3015restore_fd(fd_set *dst,
const SOCKET *src, UINT count)
3018 memcpy(dst->fd_array, src, count *
sizeof(SOCKET));
3019 dst->fd_count = count;
3024is_not_socket(SOCKET sock)
3026 return !is_socket(sock);
3036 ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
3044is_readable_pipe(SOCKET sock)
3050 if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
3054 ret = (GetLastError() == ERROR_BROKEN_PIPE);
3063is_console(SOCKET sock)
3070 ret = (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n));
3078is_readable_console(SOCKET sock)
3085 if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) {
3086 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
3087 ir.Event.KeyEvent.uChar.UnicodeChar) {
3090 else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown &&
3091 ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU &&
3092 ir.Event.KeyEvent.uChar.UnicodeChar) {
3096 ReadConsoleInputW((HANDLE)sock, &ir, 1, &n);
3106is_invalid_handle(SOCKET sock)
3108 return (HANDLE)sock == INVALID_HANDLE_VALUE;
3113do_select(
int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3120 rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
3122 rb_w32_sleep(INFINITE);
3126 thread_exclusive(select) {
3127 r = select(nfds, rd, wr, ex, timeout);
3129 if (r == SOCKET_ERROR) {
3130 errno = map_errno(WSAGetLastError());
3145rb_w32_time_subtract(
struct timeval *rest,
const struct timeval *wait)
3147 if (rest->tv_sec < wait->tv_sec) {
3150 while (rest->tv_usec < wait->tv_usec) {
3151 if (rest->tv_sec <= wait->tv_sec) {
3155 rest->tv_usec += 1000 * 1000;
3157 rest->tv_sec -= wait->tv_sec;
3158 rest->tv_usec -= wait->tv_usec;
3159 return rest->tv_sec != 0 || rest->tv_usec != 0;
3166 if (t1->tv_sec < t2->tv_sec)
3168 if (t1->tv_sec > t2->tv_sec)
3170 if (t1->tv_usec < t2->tv_usec)
3172 if (t1->tv_usec > t2->tv_usec)
3179int rb_w32_check_interrupt(
void *);
3184rb_w32_select_with_thread(
int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3185 struct timeval *timeout,
void *th)
3194 struct timeval limit = {0, 0};
3196 if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
3202 if (timeout->tv_sec < 0 ||
3203 timeout->tv_usec < 0 ||
3204 timeout->tv_usec >= 1000000) {
3208 gettimeofday(&limit, NULL);
3209 limit.tv_sec += timeout->tv_sec;
3210 limit.tv_usec += timeout->tv_usec;
3211 if (limit.tv_usec >= 1000000) {
3212 limit.tv_usec -= 1000000;
3224 nonsock += extract_fd(&else_rd, rd, is_not_socket);
3227 nonsock += extract_fd(&else_wr, wr, is_not_socket);
3230 if (extract_fd(NULL, else_rd.
fdset, is_invalid_handle) > 0 ||
3231 extract_fd(NULL, else_wr.
fdset, is_invalid_handle) > 0) {
3239 extract_fd(&pipe_rd, else_rd.
fdset, is_pipe);
3242 extract_fd(&cons_rd, else_rd.
fdset, is_console);
3245 extract_fd(&except, ex, is_not_socket);
3248 if (rd && (
int)rd->fd_count > r) r = (int)rd->fd_count;
3249 if (wr && (
int)wr->fd_count > r) r = (int)wr->fd_count;
3250 if (ex && (
int)ex->fd_count > r) r = (int)ex->fd_count;
3251 if (nfds > r) nfds = r;
3255 const struct timeval wait = {0, 10 * 1000};
3258 UINT nrd = rd ? rd->fd_count : 0;
3259 UINT nwr = wr ? wr->fd_count : 0;
3260 UINT nex = ex ? ex->fd_count : 0;
3261 SOCKET *orig =
ALLOC_N(SOCKET, nrd + nwr + nex);
3264 save_fd(orig + nrd, wr);
3265 save_fd(orig + nrd + nwr, ex);
3267 if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
3274 extract_fd(&else_rd, pipe_rd.
fdset, is_readable_pipe);
3275 extract_fd(&else_rd, cons_rd.
fdset, is_readable_console);
3278 if (else_rd.
fdset->fd_count || else_wr.
fdset->fd_count) {
3279 r = do_select(nfds, rd, wr, ex, &zero);
3282 r += copy_fd(rd, else_rd.
fdset);
3283 r += copy_fd(wr, else_wr.
fdset);
3289 const struct timeval *dowait = &wait;
3291 r = do_select(nfds, rd, wr, ex, &zero);
3296 gettimeofday(&now, NULL);
3298 if (!rb_w32_time_subtract(&rest, &now))
break;
3299 if (compare(&rest, &wait) < 0) dowait = &rest;
3301 restore_fd(rd, orig, nrd);
3302 restore_fd(wr, orig + nrd, nwr);
3303 restore_fd(ex, orig + nrd + nwr, nex);
3304 Sleep(dowait->tv_sec * 1000 + (dowait->tv_usec + 999) / 1000);
3307 ruby_xfree_sized(orig,
sizeof(SOCKET) * (nrd + nwr + nex));
3321rb_w32_select(
int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3324 return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
3329get_wsa_extension_function(SOCKET s, GUID guid)
3334 WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid,
sizeof(guid),
3335 &ptr,
sizeof(ptr), &dmy, NULL, NULL);
3345rb_w32_accept(
int s,
struct sockaddr *addr,
int *addrlen)
3351 r = accept(TO_SOCKET(s), addr, addrlen);
3352 if (r != INVALID_SOCKET) {
3353 SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
3354 fd = rb_w32_open_osfhandle((intptr_t)r, O_RDWR|O_BINARY|O_NOINHERIT);
3356 socklist_insert(r, 0);
3361 errno = map_errno(WSAGetLastError());
3372rb_w32_bind(
int s,
const struct sockaddr *addr,
int addrlen)
3377 r = bind(TO_SOCKET(s), addr, addrlen);
3378 if (r == SOCKET_ERROR)
3379 errno = map_errno(WSAGetLastError());
3388rb_w32_connect(
int s,
const struct sockaddr *addr,
int addrlen)
3392 r = connect(TO_SOCKET(s), addr, addrlen);
3393 if (r == SOCKET_ERROR) {
3394 int err = WSAGetLastError();
3395 if (err != WSAEWOULDBLOCK)
3396 errno = map_errno(err);
3398 errno = EINPROGRESS;
3409rb_w32_getpeername(
int s,
struct sockaddr *addr,
int *addrlen)
3413 r = getpeername(TO_SOCKET(s), addr, addrlen);
3414 if (r == SOCKET_ERROR)
3415 errno = map_errno(WSAGetLastError());
3424rb_w32_getsockname(
int fd,
struct sockaddr *addr,
int *addrlen)
3429 sock = TO_SOCKET(fd);
3430 r = getsockname(sock, addr, addrlen);
3431 if (r == SOCKET_ERROR) {
3432 DWORD wsaerror = WSAGetLastError();
3433 if (wsaerror == WSAEINVAL) {
3435 if (socklist_lookup(sock, &flags)) {
3436 int af = GET_FAMILY(flags);
3438 memset(addr, 0, *addrlen);
3439 addr->sa_family = af;
3444 errno = map_errno(wsaerror);
3454rb_w32_getsockopt(
int s,
int level,
int optname,
char *optval,
int *optlen)
3458 r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3459 if (r == SOCKET_ERROR)
3460 errno = map_errno(WSAGetLastError());
3464 if (r == 0 && level == SOL_SOCKET && optname == SO_ERROR &&
3465 *optlen == (
int)
sizeof(
int)) {
3467 memcpy(&sockerr, optval,
sizeof(sockerr));
3468 sockerr = map_errno(sockerr);
3469 memcpy(optval, &sockerr,
sizeof(sockerr));
3478rb_w32_ioctlsocket(
int s,
long cmd, u_long *argp)
3482 r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3483 if (r == SOCKET_ERROR)
3484 errno = map_errno(WSAGetLastError());
3493rb_w32_listen(
int s,
int backlog)
3497 r = listen(TO_SOCKET(s), backlog);
3498 if (r == SOCKET_ERROR)
3499 errno = map_errno(WSAGetLastError());
3511finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol,
int result, DWORD *
len, DWORD size)
3516 if (result != SOCKET_ERROR)
3518 else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
3519 switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
3522 result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg);
3529 result = SOCKET_ERROR;
3532 if ((err = WSAGetLastError()) == WSAECONNABORTED && !input)
3534 else if (err == WSAEMSGSIZE && input) {
3540 errno = map_errno(err);
3542 case WAIT_OBJECT_0 + 1:
3545 CancelIo((HANDLE)s);
3550 if (err == WSAECONNABORTED && !input)
3553 errno = map_errno(err);
3556 CloseHandle(wol->hEvent);
3563overlapped_socket_io(BOOL input,
int fd,
char *buf,
int len,
int flags,
3564 struct sockaddr *addr,
int *addrlen)
3575 socklist_lookup(s, &mode);
3576 if (GET_FLAGS(mode) & O_NONBLOCK) {
3579 if (addr && addrlen)
3580 r = recvfrom(s, buf,
len, flags, addr, addrlen);
3582 r = recv(s, buf,
len, flags);
3583 if (r == SOCKET_ERROR)
3584 errno = map_errno(WSAGetLastError());
3587 if (addr && addrlen)
3588 r = sendto(s, buf,
len, flags, addr, *addrlen);
3590 r = send(s, buf,
len, flags);
3591 if (r == SOCKET_ERROR) {
3592 DWORD err = WSAGetLastError();
3593 if (err == WSAECONNABORTED)
3596 errno = map_errno(err);
3606 memset(&wol, 0,
sizeof(wol));
3608 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3611 if (addr && addrlen)
3612 ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3615 ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3618 if (addr && addrlen)
3619 ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3622 ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3626 finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3635rb_w32_recv(
int fd,
char *buf,
int len,
int flags)
3637 return overlapped_socket_io(TRUE, fd, buf,
len, flags, NULL, NULL);
3642rb_w32_recvfrom(
int fd,
char *buf,
int len,
int flags,
3643 struct sockaddr *from,
int *fromlen)
3645 return overlapped_socket_io(TRUE, fd, buf,
len, flags, from, fromlen);
3650rb_w32_send(
int fd,
const char *buf,
int len,
int flags)
3652 return overlapped_socket_io(FALSE, fd, (
char *)buf,
len, flags, NULL, NULL);
3657rb_w32_sendto(
int fd,
const char *buf,
int len,
int flags,
3658 const struct sockaddr *to,
int tolen)
3660 return overlapped_socket_io(FALSE, fd, (
char *)buf,
len, flags,
3661 (
struct sockaddr *)to, &tolen);
3664#if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3670 DWORD dwBufferCount;
3675#ifndef WSAID_WSARECVMSG
3676#define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3678#ifndef WSAID_WSASENDMSG
3679#define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3683#define msghdr_to_wsamsg(msg, wsamsg) \
3686 (wsamsg)->name = (msg)->msg_name; \
3687 (wsamsg)->namelen = (msg)->msg_namelen; \
3688 (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3689 (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3690 for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3691 (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3692 (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3694 (wsamsg)->Control.buf = (msg)->msg_control; \
3695 (wsamsg)->Control.len = (msg)->msg_controllen; \
3696 (wsamsg)->dwFlags = (msg)->msg_flags; \
3701recvmsg(
int fd,
struct msghdr *msg,
int flags)
3703 typedef int (WSAAPI *WSARecvMsg_t)(SOCKET,
WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3704 static WSARecvMsg_t pWSARecvMsg = NULL;
3714 static const GUID guid = WSAID_WSARECVMSG;
3715 pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, guid);
3720 msghdr_to_wsamsg(msg, &wsamsg);
3721 wsamsg.dwFlags |= flags;
3723 socklist_lookup(s, &mode);
3724 if (GET_FLAGS(mode) & O_NONBLOCK) {
3726 if ((ret = pWSARecvMsg(s, &wsamsg, &
len, NULL, NULL)) == SOCKET_ERROR) {
3727 errno = map_errno(WSAGetLastError());
3735 memset(&wol, 0,
sizeof(wol));
3737 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3738 ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3741 ret = finish_overlapped_socket(TRUE, s, &wol, ret, &
len, size);
3743 if (ret == SOCKET_ERROR)
3747 msg->msg_name = wsamsg.name;
3748 msg->msg_namelen = wsamsg.namelen;
3749 msg->msg_flags = wsamsg.dwFlags;
3756sendmsg(
int fd,
const struct msghdr *msg,
int flags)
3758 typedef int (WSAAPI *WSASendMsg_t)(SOCKET,
const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3759 static WSASendMsg_t pWSASendMsg = NULL;
3769 static const GUID guid = WSAID_WSASENDMSG;
3770 pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, guid);
3775 msghdr_to_wsamsg(msg, &wsamsg);
3777 socklist_lookup(s, &mode);
3778 if (GET_FLAGS(mode) & O_NONBLOCK) {
3780 if ((ret = pWSASendMsg(s, &wsamsg, flags, &
len, NULL, NULL)) == SOCKET_ERROR) {
3781 errno = map_errno(WSAGetLastError());
3789 memset(&wol, 0,
sizeof(wol));
3791 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3792 ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3795 finish_overlapped_socket(FALSE, s, &wol, ret, &
len, size);
3805rb_w32_setsockopt(
int s,
int level,
int optname,
const char *optval,
int optlen)
3809 r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3810 if (r == SOCKET_ERROR)
3811 errno = map_errno(WSAGetLastError());
3820rb_w32_shutdown(
int s,
int how)
3824 r = shutdown(TO_SOCKET(s), how);
3825 if (r == SOCKET_ERROR)
3826 errno = map_errno(WSAGetLastError());
3833open_ifs_socket(
int af,
int type,
int protocol)
3835 unsigned long proto_buffers_len = 0;
3837 SOCKET out = INVALID_SOCKET;
3839 if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3840 error_code = WSAGetLastError();
3841 if (error_code == WSAENOBUFS) {
3842 WSAPROTOCOL_INFO *proto_buffers;
3843 int protocols_available = 0;
3845 proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3846 if (!proto_buffers) {
3847 WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3848 return INVALID_SOCKET;
3851 protocols_available =
3852 WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3853 if (protocols_available != SOCKET_ERROR) {
3855 for (i = 0; i < protocols_available; i++) {
3856 if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3857 (
type != proto_buffers[i].iSocketType) ||
3858 (protocol != 0 && protocol != proto_buffers[i].iProtocol))
3861 if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3864 out = WSASocket(af,
type, protocol, &(proto_buffers[i]), 0,
3865 WSA_FLAG_OVERLAPPED);
3868 if (out == INVALID_SOCKET)
3869 out = WSASocket(af,
type, protocol, NULL, 0, 0);
3870 if (out != INVALID_SOCKET)
3871 SetHandleInformation((HANDLE)out, HANDLE_FLAG_INHERIT, 0);
3874 free(proto_buffers);
3885rb_w32_socket(
int af,
int type,
int protocol)
3891 s = open_ifs_socket(af,
type, protocol);
3892 if (s == INVALID_SOCKET) {
3893 errno = map_errno(WSAGetLastError());
3897 fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3899 socklist_insert(s, MAKE_SOCKDATA(af, 0));
3910struct hostent * WSAAPI
3911rb_w32_gethostbyaddr(
const char *addr,
int len,
int type)
3915 r = gethostbyaddr(addr,
len,
type);
3917 errno = map_errno(WSAGetLastError());
3925struct hostent * WSAAPI
3926rb_w32_gethostbyname(
const char *name)
3930 r = gethostbyname(name);
3932 errno = map_errno(WSAGetLastError());
3941rb_w32_gethostname(
char *name,
int len)
3945 r = gethostname(name,
len);
3946 if (r == SOCKET_ERROR)
3947 errno = map_errno(WSAGetLastError());
3952#undef getprotobyname
3955struct protoent * WSAAPI
3956rb_w32_getprotobyname(
const char *name)
3960 r = getprotobyname(name);
3962 errno = map_errno(WSAGetLastError());
3967#undef getprotobynumber
3970struct protoent * WSAAPI
3971rb_w32_getprotobynumber(
int num)
3975 r = getprotobynumber(num);
3977 errno = map_errno(WSAGetLastError());
3985struct servent * WSAAPI
3986rb_w32_getservbyname(
const char *name,
const char *proto)
3990 r = getservbyname(name, proto);
3992 errno = map_errno(WSAGetLastError());
4000struct servent * WSAAPI
4001rb_w32_getservbyport(
int port,
const char *proto)
4005 r = getservbyport(port, proto);
4007 errno = map_errno(WSAGetLastError());
4016socketpair_unix_path(
struct sockaddr_un *sock_un, WCHAR *wpath,
const int maxpath)
4023 listener = socket(AF_UNIX, SOCK_STREAM, 0);
4024 if (listener == INVALID_SOCKET) {
4025 errno = EAFNOSUPPORT;
4029 memset(sock_un, 0,
sizeof(*sock_un));
4030 sock_un->sun_family = AF_UNIX;
4040 for (
int try = 0; ;
try++) {
4041 LARGE_INTEGER ticks;
4043 static const WCHAR FALLBACK_TEMP[] = L
"C:/Temp/";
4044 static const int FALLBACK_TEMP_SIZE = numberof(FALLBACK_TEMP) - 1;
4049 path_len = GetTempPathW(maxpath, wpath);
4050 if (path_len == 0 || path_len >= maxpath) {
4059 if (FALLBACK_TEMP_SIZE >= maxpath)
continue;
4060 wcsncpy(wpath, FALLBACK_TEMP, maxpath);
4061 path_len = FALLBACK_TEMP_SIZE;
4068 closesocket(listener);
4069 errno = EADDRNOTAVAIL;
4074 int un_len = WideCharToMultiByte(CP_UTF8, 0, wpath, path_len, sock_un->sun_path, maxpath, NULL, NULL);
4075 QueryPerformanceCounter(&ticks);
4076 int base_len = snprintf(sock_un->sun_path + un_len,
4080 GetCurrentProcessId());
4081 if (un_len + base_len >= maxpath)
continue;
4084 MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path + un_len, base_len + 1, wpath + path_len, maxpath - path_len);
4086 if (bind(listener, (
struct sockaddr *)sock_un,
sizeof(*sock_un)) != SOCKET_ERROR)
4089 closesocket(listener);
4091 return sizeof(*sock_un);
4097socketpair_internal(
int af,
int type,
int protocol, SOCKET *sv)
4099 SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
4100 struct sockaddr_in sock_in4;
4103 struct sockaddr_in6 sock_in6;
4107 struct sockaddr_un sock_un = {0, {0}};
4108 WCHAR wpath[
sizeof(sock_un.sun_path)/
sizeof(*sock_un.sun_path)] = L
"";
4111 struct sockaddr *addr;
4117#if defined PF_INET && PF_INET != AF_INET
4120 sock_in4.sin_family = AF_INET;
4121 sock_in4.sin_port = 0;
4122 sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4123 addr = (
struct sockaddr *)&sock_in4;
4124 len =
sizeof(sock_in4);
4128 memset(&sock_in6, 0,
sizeof(sock_in6));
4129 sock_in6.sin6_family = AF_INET6;
4130 sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
4131 addr = (
struct sockaddr *)&sock_in6;
4132 len =
sizeof(sock_in6);
4137 addr = (
struct sockaddr *)&sock_un;
4138 len = socketpair_unix_path(&sock_un, wpath, numberof(wpath));
4139 if (!
len)
return -1;
4143 errno = EAFNOSUPPORT;
4146 if (
type != SOCK_STREAM) {
4151 sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
4152 sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
4155 svr = open_ifs_socket(af,
type, protocol);
4156 if (svr == INVALID_SOCKET)
4158 if (bind(svr, addr,
len) < 0)
4160 if (getsockname(svr, addr, &
len) < 0)
4162 if (
type == SOCK_STREAM)
4165 w = open_ifs_socket(af,
type, protocol);
4166 if (w == INVALID_SOCKET)
4168 if (connect(w, addr,
len) < 0)
4171 r = accept(svr, addr, &
len);
4172 if (r == INVALID_SOCKET)
4174 SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
4180 errno = map_errno(WSAGetLastError());
4181 if (r != INVALID_SOCKET)
4183 if (w != INVALID_SOCKET)
4190 if (svr != INVALID_SOCKET)
4193 if (sock_un.sun_family == AF_UNIX)
4203socketpair(
int af,
int type,
int protocol,
int *sv)
4207 if (socketpair_internal(af,
type, protocol, pair) < 0)
4209 sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
4211 closesocket(pair[0]);
4212 closesocket(pair[1]);
4215 sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
4217 rb_w32_close(sv[0]);
4218 closesocket(pair[1]);
4221 socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
4222 socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
4229str2guid(
const char *str, GUID *guid)
4231#define hex2byte(str) \
4232 ((isdigit(*(str)) ? *(str) - '0' : toupper(*(str)) - 'A' + 10) << 4 | (isdigit(*((str) + 1)) ? *((str) + 1) - '0' : toupper(*((str) + 1)) - 'A' + 10))
4235 if (*str ==
'{') str++;
4236 guid->Data1 = (long)strtoul(str, &end, 16);
4238 guid->Data2 = (
unsigned short)strtoul(str, &end, 16);
4240 guid->Data3 = (
unsigned short)strtoul(str, &end, 16);
4242 guid->Data4[0] = hex2byte(str);
4244 guid->Data4[1] = hex2byte(str);
4246 for (i = 0; i < 6; i++) {
4247 guid->Data4[i + 2] = hex2byte(str);
4253#ifndef HAVE_TYPE_NET_LUID
4257 uint64_t Reserved :24;
4258 uint64_t NetLuidIndex :24;
4259 uint64_t IfType :16;
4265getifaddrs(
struct ifaddrs **ifap)
4269 IP_ADAPTER_ADDRESSES *root, *addr;
4272 ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
4273 if (ret != ERROR_BUFFER_OVERFLOW) {
4274 errno = map_errno(ret);
4277 root = ruby_xmalloc(size);
4278 ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, root, &size);
4279 if (ret != ERROR_SUCCESS) {
4280 errno = map_errno(ret);
4285 for (prev = NULL, addr = root; addr; addr = addr->Next) {
4286 struct ifaddrs *ifa = ruby_xcalloc(1,
sizeof(*ifa));
4287 char name[IFNAMSIZ];
4292 prev->ifa_next = ifa;
4296 str2guid(addr->AdapterName, &guid);
4297 if (ConvertInterfaceGuidToLuid(&guid, &luid) == NO_ERROR &&
4298 ConvertInterfaceLuidToNameA(&luid, name,
sizeof(name)) == NO_ERROR) {
4305 if (addr->IfType & IF_TYPE_SOFTWARE_LOOPBACK)
4306 ifa->ifa_flags |= IFF_LOOPBACK;
4307 if (addr->OperStatus == IfOperStatusUp) {
4308 ifa->ifa_flags |= IFF_UP;
4310 if (addr->FirstUnicastAddress) {
4311 IP_ADAPTER_UNICAST_ADDRESS *cur;
4313 for (cur = addr->FirstUnicastAddress; cur; cur = cur->Next) {
4314 if (cur->Flags & IP_ADAPTER_ADDRESS_TRANSIENT ||
4315 cur->DadState == IpDadStateDeprecated) {
4320 ifa = ruby_xcalloc(1,
sizeof(*ifa));
4321 prev->ifa_next = ifa;
4323 ifa->ifa_flags = prev->ifa_flags;
4325 ifa->ifa_addr = ruby_xmalloc(cur->Address.iSockaddrLength);
4326 memcpy(ifa->ifa_addr, cur->Address.lpSockaddr,
4327 cur->Address.iSockaddrLength);
4342freeifaddrs(
struct ifaddrs *ifp)
4345 struct ifaddrs *next = ifp->ifa_next;
4346 ruby_xfree(ifp->ifa_addr);
4347 ruby_xfree(ifp->ifa_name);
4358void endhostent(
void) {}
4359void endnetent(
void) {}
4360void endprotoent(
void) {}
4361void endservent(
void) {}
4363struct netent *getnetent (
void) {
return (
struct netent *) NULL;}
4365struct netent *getnetbyaddr(
long net,
int type) {
return (
struct netent *)NULL;}
4367struct netent *getnetbyname(
const char *name) {
return (
struct netent *)NULL;}
4369struct protoent *getprotoent (
void) {
return (
struct protoent *) NULL;}
4371struct servent *getservent (
void) {
return (
struct servent *) NULL;}
4373void sethostent (
int stayopen) {}
4375void setnetent (
int stayopen) {}
4377void setprotoent (
int stayopen) {}
4379void setservent (
int stayopen) {}
4382int rb_w32_set_nonblock2(
int fd,
int nonblock);
4386setfl(SOCKET sock,
int arg)
4393 socklist_lookup(sock, &flag);
4394 af = GET_FAMILY(flag);
4395 flag = GET_FLAGS(flag);
4396 if (arg & O_NONBLOCK) {
4401 flag &= ~O_NONBLOCK;
4405 ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
4407 socklist_insert(sock, MAKE_SOCKDATA(af, flag));
4409 errno = map_errno(WSAGetLastError());
4417dupfd(HANDLE hDup,
int flags,
int minfd)
4425 ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
4427 goto close_fds_and_return;
4430 goto close_fds_and_return;
4432 fds[filled++] = ret;
4433 }
while (filled < (
int)numberof(fds));
4435 ret = dupfd(hDup, flags, minfd);
4437 close_fds_and_return:
4439 while (filled > 0) {
4440 int fd = fds[--filled];
4441 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
4451fcntl(
int fd,
int cmd, ...)
4460 arg = va_arg(va,
int);
4462 return rb_w32_set_nonblock2(fd, arg);
4464 case F_DUPFD:
case F_DUPFD_CLOEXEC: {
4468 if (!(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
4469 GetCurrentProcess(), &hDup, 0L,
4470 cmd == F_DUPFD && !(flag & FNOINHERIT),
4471 DUPLICATE_SAME_ACCESS))) {
4472 errno = map_errno(GetLastError());
4477 arg = va_arg(va,
int);
4483 flag &= ~FNOINHERIT;
4484 if ((ret = dupfd(hDup, flag, arg)) == -1)
4490 if (h == -1)
return -1;
4491 if (!GetHandleInformation((HANDLE)h, &flag)) {
4492 errno = map_errno(GetLastError());
4495 return (flag & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
4499 if (h == -1)
return -1;
4501 arg = va_arg(va,
int);
4503 if (!SetHandleInformation((HANDLE)h, HANDLE_FLAG_INHERIT,
4504 (arg & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT)) {
4505 errno = map_errno(GetLastError());
4508 if (arg & FD_CLOEXEC)
4509 _osfile(fd) |= FNOINHERIT;
4511 _osfile(fd) &= ~FNOINHERIT;
4522rb_w32_set_nonblock2(
int fd,
int nonblock)
4524 SOCKET sock = TO_SOCKET(fd);
4525 if (is_socket(sock)) {
4526 return setfl(sock, nonblock ? O_NONBLOCK : 0);
4528 else if (is_pipe(sock)) {
4530 if (!GetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL, NULL, NULL, 0)) {
4531 errno = map_errno(GetLastError());
4535 state |= PIPE_NOWAIT;
4538 state &= ~PIPE_NOWAIT;
4540 if (!SetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL)) {
4541 errno = map_errno(GetLastError());
4553rb_w32_set_nonblock(
int fd)
4555 return rb_w32_set_nonblock2(fd, TRUE);
4564poll_child_status(
struct ChildRecord *child,
int *stat_loc)
4569 if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
4571 err = GetLastError();
4573 case ERROR_INVALID_PARAMETER:
4576 case ERROR_INVALID_HANDLE:
4580 errno = map_errno(err);
4584 CloseChildHandle(child);
4587 if (exitcode != STILL_ACTIVE) {
4590 if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
4594 CloseChildHandle(child);
4596 *stat_loc = exitcode << 8;
4597 if (exitcode & 0xC0000000) {
4598 static const struct {
4602 {STATUS_ACCESS_VIOLATION, SIGSEGV},
4603 {STATUS_ILLEGAL_INSTRUCTION, SIGILL},
4604 {STATUS_PRIVILEGED_INSTRUCTION, SIGILL},
4605 {STATUS_FLOAT_DENORMAL_OPERAND, SIGFPE},
4606 {STATUS_FLOAT_DIVIDE_BY_ZERO, SIGFPE},
4607 {STATUS_FLOAT_INEXACT_RESULT, SIGFPE},
4608 {STATUS_FLOAT_INVALID_OPERATION, SIGFPE},
4609 {STATUS_FLOAT_OVERFLOW, SIGFPE},
4610 {STATUS_FLOAT_STACK_CHECK, SIGFPE},
4611 {STATUS_FLOAT_UNDERFLOW, SIGFPE},
4612#ifdef STATUS_FLOAT_MULTIPLE_FAULTS
4613 {STATUS_FLOAT_MULTIPLE_FAULTS, SIGFPE},
4615#ifdef STATUS_FLOAT_MULTIPLE_TRAPS
4616 {STATUS_FLOAT_MULTIPLE_TRAPS, SIGFPE},
4618 {STATUS_CONTROL_C_EXIT, SIGINT},
4621 for (i = 0; i < (int)numberof(table); i++) {
4622 if (table[i].status == exitcode) {
4623 *stat_loc |= table[i].sig;
4628 if (i >= (
int)numberof(table))
4629 *stat_loc |= SIGSEGV;
4639waitpid(rb_pid_t pid,
int *stat_loc,
int options)
4644 if (options == WNOHANG) {
4655 HANDLE events[MAXCHILDNUM];
4658 FOREACH_CHILD(child) {
4659 if (!child->pid || child->pid < 0)
continue;
4660 if ((pid = poll_child_status(child, stat_loc)))
return pid;
4661 events[count++] = child->hProcess;
4662 } END_FOREACH_CHILD;
4668 ret = rb_w32_wait_events_blocking(events, count, timeout);
4669 if (ret == WAIT_TIMEOUT)
return 0;
4670 if ((ret -= WAIT_OBJECT_0) == count) {
4674 errno = map_errno(GetLastError());
4678 cause = FindChildSlotByHandle(events[ret]);
4683 return poll_child_status(cause, stat_loc);
4693 while (!(pid = poll_child_status(child, stat_loc))) {
4695 int ret = rb_w32_wait_events_blocking(&child->hProcess, 1, timeout);
4696 if (ret == WAIT_OBJECT_0 + 1)
return -1;
4697 if (ret != WAIT_OBJECT_0) {
4699 if (options & WNOHANG) {
4706 if (pid == -1 && retried) pid = 0;
4712#include <sys/timeb.h>
4715#define filetime_unit (10UL * 1000 * 1000)
4716#define filetime_diff_days ((1970-1601)*3652425UL/10000)
4717#define filetime_diff_secs (filetime_diff_days * (24ULL * 60 * 60))
4718#define unix_to_filetime(sec) (((sec) + filetime_diff_secs) * filetime_unit)
4719static const ULONGLONG filetime_unix_offset = unix_to_filetime(0ULL);
4720static const unsigned long secs_in_ns = 1000000000UL;
4732li_time_split(ULONGLONG lt,
long *subsec)
4738 lt -= filetime_unix_offset;
4740 *subsec = (long)(lt % filetime_unit);
4741 return (time_t)(lt / filetime_unit);
4746li_time_split_ns(ULONGLONG lt,
long *ns)
4749 time_t t = li_time_split(lt, &subsec);
4752 if (t < 0)
return 0;
4758filetime_to_li_time(
const FILETIME* ft)
4761 return fi.i.QuadPart;
4771 GetSystemTimePreciseAsFileTime(&ft);
4772 tv->tv_sec = li_time_split(filetime_to_li_time(&ft), &subsec);
4773 tv->tv_usec = subsec / 10;
4780filetime_to_unixtime(
const FILETIME *ft)
4783 time_t t = li_time_split(filetime_to_li_time(ft), &subsec);
4785 if (t < 0)
return 0;
4791filetime_split_ns(
const FILETIME *ft,
long *ns)
4793 return li_time_split_ns(filetime_to_li_time(ft), ns);
4798filetime_to_timespec(FILETIME ft,
struct timespec *sp)
4800 sp->tv_sec = filetime_split_ns(&ft, &sp->tv_nsec);
4805clock_gettime(clockid_t clock_id,
struct timespec *sp)
4808 case CLOCK_REALTIME:
4809 case CLOCK_REALTIME_COARSE:
4813 GetSystemTimePreciseAsFileTime(&ft);
4814 filetime_to_timespec(ft, sp);
4817 case CLOCK_MONOTONIC:
4820 LARGE_INTEGER count;
4821 if (UNLIKELY(!QueryPerformanceFrequency(&freq))) {
4822 errno = map_errno(GetLastError());
4825 if (UNLIKELY(!QueryPerformanceCounter(&count))) {
4826 errno = map_errno(GetLastError());
4829 sp->tv_sec = count.QuadPart / freq.QuadPart;
4830 if (freq.QuadPart < secs_in_ns)
4831 sp->tv_nsec = (count.QuadPart % freq.QuadPart) * secs_in_ns / freq.QuadPart;
4833 sp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * ((double)secs_in_ns / freq.QuadPart));
4836 case CLOCK_PROCESS_CPUTIME_ID:
4837 case CLOCK_THREAD_CPUTIME_ID:
4841 if (clock_id == CLOCK_PROCESS_CPUTIME_ID) {
4842 ok = GetProcessTimes(GetCurrentProcess(), &c.ft, &e.ft, &k.ft, &u.ft);
4845 ok = GetThreadTimes(GetCurrentThread(), &c.ft, &e.ft, &k.ft, &u.ft);
4847 if (UNLIKELY(!ok)) {
4848 errno = map_errno(GetLastError());
4851 total.i.QuadPart = k.i.QuadPart + u.i.QuadPart;
4852 filetime_to_timespec(total.ft, sp);
4863clock_getres(clockid_t clock_id,
struct timespec *sp)
4866 case CLOCK_REALTIME:
4867 case CLOCK_REALTIME_COARSE:
4873 case CLOCK_MONOTONIC:
4876 if (!QueryPerformanceFrequency(&freq)) {
4877 errno = map_errno(GetLastError());
4881 sp->tv_nsec = (long)((
double)secs_in_ns / freq.QuadPart);
4884 case CLOCK_PROCESS_CPUTIME_ID:
4885 case CLOCK_THREAD_CPUTIME_ID:
4887 const int frames_in_sec = 60;
4889 sp->tv_nsec = (long)(secs_in_ns / frames_in_sec);
4900w32_getcwd(
char *buffer,
int size, UINT cp,
void *alloc(
int,
void *),
void *arg)
4905 len = GetCurrentDirectoryW(0, NULL);
4907 errno = map_errno(GetLastError());
4911 if (buffer && size <
len) {
4917 if (!GetCurrentDirectoryW(
len, p)) {
4918 errno = map_errno(GetLastError());
4922 wlen = translate_wchar(p, L
'\\', L
'/') - p + 1;
4923 len = WideCharToMultiByte(cp, 0, p, wlen, NULL, 0, NULL, NULL);
4931 buffer = (*alloc)(
len, arg);
4937 WideCharToMultiByte(cp, 0, p, wlen, buffer,
len, NULL, NULL);
4944getcwd_alloc(
int size,
void *dummy)
4946 return malloc(size);
4951rb_w32_getcwd(
char *buffer,
int size)
4953 return w32_getcwd(buffer, size, filecp(), getcwd_alloc, NULL);
4958rb_w32_ugetcwd(
char *buffer,
int size)
4960 return w32_getcwd(buffer, size, CP_UTF8, getcwd_alloc, NULL);
4965getcwd_value(
int size,
void *arg)
4968 return RSTRING_PTR(str);
4973rb_dir_getwd_ospath(
void)
4976 w32_getcwd(NULL, 0, CP_UTF8, getcwd_value, &cwd);
4982chown(
const char *path,
int owner,
int group)
4989rb_w32_uchown(
const char *path,
int owner,
int group)
4995lchown(
const char *path,
int owner,
int group)
5001rb_w32_ulchown(
const char *path,
int owner,
int group)
5008kill(rb_pid_t pid,
int sig)
5013 if (pid < 0 || (pid == 0 && sig != SIGINT)) {
5018 if ((
unsigned int)pid == GetCurrentProcessId() &&
5019 (sig != 0 && sig != SIGKILL)) {
5020 if ((ret = raise(sig)) != 0) {
5031 OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
5032 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
5033 if (GetLastError() == ERROR_INVALID_PARAMETER) {
5049 DWORD ctrlEvent = CTRL_C_EVENT;
5053 ctrlEvent = CTRL_BREAK_EVENT;
5055 if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
5056 if ((err = GetLastError()) == 0)
5059 errno = map_errno(GetLastError());
5070 hProc = child->hProcess;
5073 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
5075 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
5076 if (GetLastError() == ERROR_INVALID_PARAMETER) {
5086 if (!GetExitCodeProcess(hProc, &status)) {
5087 errno = map_errno(GetLastError());
5090 else if (status == STILL_ACTIVE) {
5091 if (!TerminateProcess(hProc, 0)) {
5118wlink(
const WCHAR *from,
const WCHAR *to)
5120 if (!CreateHardLinkW(to, from, NULL)) {
5121 errno = map_errno(GetLastError());
5130rb_w32_ulink(
const char *from,
const char *to)
5136 if (!(wfrom = utf8_to_wstr(from, NULL)))
5138 if (!(wto = utf8_to_wstr(to, NULL))) {
5142 ret = wlink(wfrom, wto);
5150link(
const char *from,
const char *to)
5156 if (!(wfrom = filecp_to_wstr(from, NULL)))
5158 if (!(wto = filecp_to_wstr(to, NULL))) {
5162 ret = wlink(wfrom, wto);
5169#ifndef FILE_DEVICE_FILE_SYSTEM
5170# define FILE_DEVICE_FILE_SYSTEM 0x00000009
5172#ifndef FSCTL_GET_REPARSE_POINT
5173# define FSCTL_GET_REPARSE_POINT ((0x9<<16)|(42<<2))
5175#ifndef IO_REPARSE_TAG_SYMLINK
5176# define IO_REPARSE_TAG_SYMLINK 0xA000000CL
5187 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5188 if (f == INVALID_HANDLE_VALUE) {
5189 return GetLastError();
5192 if (!DeviceIoControl(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
5193 rp, size, &ret, NULL)) {
5196 else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
5197 rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
5198 e = ERROR_INVALID_PARAMETER;
5206rb_w32_reparse_symlink_p(
const WCHAR *path)
5214 e = rb_w32_read_reparse_point(path, rp,
sizeof(rbuf), &wbuf, &
len);
5215 if (e == ERROR_MORE_DATA) {
5216 size_t size = rb_w32_reparse_buffer_size(
len + 1);
5218 e = rb_w32_read_reparse_point(path, rp, size, &wbuf, &
len);
5223 case ERROR_MORE_DATA:
5232 size_t bufsize, WCHAR **result, DWORD *
len)
5234 int e = reparse_symlink(path, rp, bufsize);
5237 if (!e || e == ERROR_MORE_DATA) {
5239 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
5240 name = ((
char *)rp->SymbolicLinkReparseBuffer.PathBuffer +
5241 rp->SymbolicLinkReparseBuffer.PrintNameOffset);
5242 ret = rp->SymbolicLinkReparseBuffer.PrintNameLength;
5243 *
len = ret /
sizeof(WCHAR);
5245 else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
5246 static const WCHAR volume[] = L
"Volume{";
5248 name = ((
char *)rp->MountPointReparseBuffer.PathBuffer +
5249 rp->MountPointReparseBuffer.SubstituteNameOffset +
5250 volume_prefix_len *
sizeof(WCHAR));
5251 ret = rp->MountPointReparseBuffer.SubstituteNameLength;
5252 *
len = ret /
sizeof(WCHAR);
5253 ret -= volume_prefix_len *
sizeof(WCHAR);
5254 if (ret >
sizeof(volume) - 1 *
sizeof(WCHAR) &&
5255 memcmp(name, volume,
sizeof(volume) - 1 *
sizeof(WCHAR)) == 0)
5263 if ((
char *)name + ret +
sizeof(WCHAR) > (
char *)rp + bufsize)
5267 ((WCHAR *)name)[ret/
sizeof(WCHAR)] = L
'\0';
5268 translate_wchar(name, L
'\\', L
'/');
5278w32_readlink(UINT cp,
const char *path,
char *buf,
size_t bufsize)
5280 VALUE rp_buf, rp_buf_bigger = 0;
5281 DWORD
len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0);
5282 size_t size = rb_w32_reparse_buffer_size(bufsize);
5284 WCHAR *wpath =
ALLOCV(rp_buf,
sizeof(WCHAR) *
len + size);
5289 MultiByteToWideChar(cp, 0, path, -1, wpath,
len);
5290 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &
len);
5291 if (e == ERROR_MORE_DATA) {
5292 size = rb_w32_reparse_buffer_size(
len + 1);
5293 rp =
ALLOCV(rp_buf_bigger, size);
5294 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &
len);
5299 errno = e == -1 ? EINVAL : map_errno(e);
5302 len = lstrlenW(wname);
5303 ret = WideCharToMultiByte(cp, 0, wname,
len, buf, bufsize, NULL, NULL);
5314rb_w32_ureadlink(
const char *path,
char *buf,
size_t bufsize)
5316 return w32_readlink(CP_UTF8, path, buf, bufsize);
5321readlink(
const char *path,
char *buf,
size_t bufsize)
5323 return w32_readlink(filecp(), path, buf, bufsize);
5326#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
5327#define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
5329#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
5330#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
5335w32_symlink(UINT cp,
const char *src,
const char *link)
5337 int atts, len1, len2;
5339 WCHAR *wsrc, *wlink;
5344 static DWORD create_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5354 len1 = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
5355 len2 = MultiByteToWideChar(cp, 0, link, -1, NULL, 0);
5356 wsrc =
ALLOCV_N(WCHAR, buf, len1+len2);
5357 wlink = wsrc + len1;
5358 MultiByteToWideChar(cp, 0, src, -1, wsrc, len1);
5359 MultiByteToWideChar(cp, 0, link, -1, wlink, len2);
5360 translate_wchar(wsrc, L
'/', L
'\\');
5361 translate_wchar(wlink, L
'/', L
'\\');
5371 (((wsrc[0] >= L
'A' && wsrc[0] <= L
'Z') ||
5372 (wsrc[0] >= L
'a' && wsrc[0] <= L
'z')) && wsrc[1] == L
':') ||
5374 if (!independent && (sep = wcsrchr(wlink, L
'\\')) != NULL) {
5376 size_t dirlen = sep - wlink + 1;
5377 size_t srclen = wcslen(wsrc) + 1;
5378 WCHAR *fullsrc =
ALLOCV_N(WCHAR, buf2, dirlen + srclen);
5379 MEMCPY(fullsrc, wlink, WCHAR, dirlen);
5380 MEMCPY(fullsrc + dirlen, wsrc, WCHAR, srclen);
5381 atts = GetFileAttributesW(fullsrc);
5385 atts = GetFileAttributesW(wsrc);
5388 if (atts != -1 && atts & FILE_ATTRIBUTE_DIRECTORY)
5389 flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
5390 ret = CreateSymbolicLinkW(wlink, wsrc, flag |= create_flag);
5392 (e = GetLastError()) == ERROR_INVALID_PARAMETER &&
5393 (flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
5395 flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5396 ret = CreateSymbolicLinkW(wlink, wsrc, flag);
5397 if (!ret) e = GetLastError();
5402 errno = map_errno(e);
5410rb_w32_usymlink(
const char *src,
const char *link)
5412 return w32_symlink(CP_UTF8, src, link);
5417symlink(
const char *src,
const char *link)
5419 return w32_symlink(filecp(), src, link);
5426 return waitpid(-1, status, 0);
5431w32_getenv(
const char *name, UINT cp)
5433 WCHAR *wenvarea, *wenv;
5434 int len = strlen(name);
5435 char *env, *found = NULL;
5438 if (
len == 0)
return NULL;
5443 return getenv(name);
5446 thread_exclusive(uenvarea) {
5451 wenvarea = GetEnvironmentStringsW();
5453 map_errno(GetLastError());
5456 for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
5457 wlen += lstrlenW(wenv) + 1;
5458 uenvarea = wstr_to_mbstr(cp, wenvarea, wlen, NULL);
5459 FreeEnvironmentStringsW(wenvarea);
5463 for (env = uenvarea; *env; env += strlen(env) + 1) {
5464 if (strncasecmp(env, name,
len) == 0 && *(env +
len) ==
'=') {
5465 found = env +
len + 1;
5476rb_w32_ugetenv(
const char *name)
5478 return w32_getenv(name, CP_UTF8);
5483rb_w32_getenv(
const char *name)
5485 return w32_getenv(name, CP_ACP);
5490get_attr_vsn(
const WCHAR *path, DWORD *atts, DWORD *vsn)
5492 BY_HANDLE_FILE_INFORMATION st = {0};
5494 HANDLE h = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5496 if (h == INVALID_HANDLE_VALUE) {
5501 if (!GetFileInformationByHandle(h, &st)) {
5506 *atts = st.dwFileAttributes;
5507 *vsn = st.dwVolumeSerialNumber;
5515wrename(
const WCHAR *oldpath,
const WCHAR *newpath)
5518 DWORD oldatts = 0, newatts = (DWORD)-1;
5519 DWORD oldvsn = 0, newvsn = 0, e;
5521 e = get_attr_vsn(oldpath, &oldatts, &oldvsn);
5523 errno = map_errno(e);
5526 if (oldatts & FILE_ATTRIBUTE_REPARSE_POINT) {
5527 HANDLE fh = open_special(oldpath, 0, 0);
5528 if (fh == INVALID_HANDLE_VALUE) {
5530 if (e == ERROR_CANT_RESOLVE_FILENAME) {
5537 get_attr_vsn(newpath, &newatts, &newvsn);
5540 if (newatts != (DWORD)-1 && newatts & FILE_ATTRIBUTE_READONLY)
5541 SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
5543 if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
5547 DWORD e = GetLastError();
5548 if ((e == ERROR_ACCESS_DENIED) && (oldatts & FILE_ATTRIBUTE_DIRECTORY) &&
5552 errno = map_errno(e);
5555 SetFileAttributesW(newpath, oldatts);
5563rb_w32_urename(
const char *from,
const char *to)
5569 if (!(wfrom = utf8_to_wstr(from, NULL)))
5571 if (!(wto = utf8_to_wstr(to, NULL))) {
5575 ret = wrename(wfrom, wto);
5583rb_w32_rename(
const char *from,
const char *to)
5589 if (!(wfrom = filecp_to_wstr(from, NULL)))
5591 if (!(wto = filecp_to_wstr(to, NULL))) {
5595 ret = wrename(wfrom, wto);
5603isUNCRoot(
const WCHAR *path)
5605 if (path[0] == L
'\\' && path[1] == L
'\\') {
5606 const WCHAR *p = path + 2;
5607 if (p[0] == L
'?' && p[1] == L
'\\') {
5615 for (p++; *p; p++) {
5619 if (!p[0] || !p[1] || (p[1] == L
'.' && !p[2]))
5626#define COPY_STAT(src, dest, size_cast) do { \
5627 (dest).st_dev = (src).st_dev; \
5628 (dest).st_ino = (src).st_ino; \
5629 (dest).st_mode = (src).st_mode; \
5630 (dest).st_nlink = (src).st_nlink; \
5631 (dest).st_uid = (src).st_uid; \
5632 (dest).st_gid = (src).st_gid; \
5633 (dest).st_rdev = (src).st_rdev; \
5634 (dest).st_size = size_cast(src).st_size; \
5635 (dest).st_atime = (src).st_atime; \
5636 (dest).st_mtime = (src).st_mtime; \
5637 (dest).st_ctime = (src).st_ctime; \
5640static WCHAR *name_for_stat(WCHAR *buf,
const WCHAR *path);
5641static DWORD stati128_handle(HANDLE h,
struct stati128 *st);
5646rb_w32_fstat(
int fd,
struct stat *st)
5648 BY_HANDLE_FILE_INFORMATION info;
5649 int ret = fstat(fd, st);
5651 if (ret)
return ret;
5652 if (GetEnvironmentVariableW(L
"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND)
return ret;
5653 if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
5654 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5655 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5656 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5663rb_w32_fstati128(
int fd,
struct stati128 *st)
5666 int ret = fstat(fd, &tmp);
5668 if (ret)
return ret;
5669 COPY_STAT(tmp, *st, +);
5670 stati128_handle((HANDLE)_get_osfhandle(fd), st);
5674#if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5676 BYTE Identifier[16];
5680#if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5681#define FileIdInfo 0x12
5692 return GetFileInformationByHandleEx(h, FileIdInfo,
id,
sizeof(*
id));
5697stati128_handle(HANDLE h,
struct stati128 *st)
5699 BY_HANDLE_FILE_INFORMATION info;
5700 DWORD attr = (DWORD)-1;
5702 if (GetFileInformationByHandle(h, &info)) {
5704 st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
5705 st->st_atime = filetime_split_ns(&info.ftLastAccessTime, &st->st_atimensec);
5706 st->st_mtime = filetime_split_ns(&info.ftLastWriteTime, &st->st_mtimensec);
5707 st->st_ctime = filetime_split_ns(&info.ftCreationTime, &st->st_ctimensec);
5708 st->st_nlink = info.nNumberOfLinks;
5709 attr = info.dwFileAttributes;
5710 if (get_ino(h, &fii)) {
5711 st->st_ino = *((
unsigned __int64 *)&fii.FileId);
5712 st->st_inohigh = *((__int64 *)&fii.FileId + 1);
5715 st->st_ino = ((__int64)info.nFileIndexHigh << 32) | info.nFileIndexLow;
5724fileattr_to_unixmode(DWORD attr,
const WCHAR *path,
unsigned mode)
5726 if (attr & FILE_ATTRIBUTE_READONLY) {
5730 mode |= S_IREAD | S_IWRITE | S_IWUSR;
5733 if (mode & S_IFMT) {
5736 else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5740 else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5741 mode |= S_IFDIR | S_IEXEC;
5747 if (path && (mode & S_IFREG)) {
5748 const WCHAR *end = path + lstrlenW(path);
5749 while (path < end) {
5750 end = CharPrevW(path, end);
5752 if ((_wcsicmp(end, L
".bat") == 0) ||
5753 (_wcsicmp(end, L
".cmd") == 0) ||
5754 (_wcsicmp(end, L
".com") == 0) ||
5755 (_wcsicmp(end, L
".exe") == 0)) {
5760 if (!iswalnum(*end))
break;
5764 mode |= (mode & 0500) >> 3;
5765 mode |= (mode & 0500) >> 6;
5772check_valid_dir(
const WCHAR *path)
5774 WIN32_FIND_DATAW fd;
5776 WCHAR full[PATH_MAX];
5781 if (!(p = wcsstr(path, L
"...")))
5783 q = p + wcsspn(p, L
".");
5784 if ((p == path || wcschr(L
":/\\", *(p - 1))) &&
5785 (!*q || wcschr(L
":/\\", *q))) {
5792 DWORD
len = GetFullPathNameW(path, numberof(full), full, NULL);
5793 if (
len >= numberof(full)) {
5794 WCHAR *fullpath = malloc(
len *
sizeof(WCHAR));
5795 if (!fullpath)
return -1;
5796 len = GetFullPathNameW(path,
len, fullpath, NULL);
5801 errno = map_errno(GetLastError());
5804 if (
len == 3 && full[1] == L
':' && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5807 fh = open_dir_handle(path, &fd);
5808 if (fh == INVALID_HANDLE_VALUE)
5816stat_by_find(
const WCHAR *path,
struct stati128 *st)
5819 WIN32_FIND_DATAW wfd;
5822 h = FindFirstFileW(path, &wfd);
5823 if (h == INVALID_HANDLE_VALUE) {
5824 errno = map_errno(GetLastError());
5828 st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path, 0);
5829 st->st_atime = filetime_split_ns(&wfd.ftLastAccessTime, &st->st_atimensec);
5830 st->st_mtime = filetime_split_ns(&wfd.ftLastWriteTime, &st->st_mtimensec);
5831 st->st_ctime = filetime_split_ns(&wfd.ftCreationTime, &st->st_ctimensec);
5832 st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
5839path_drive(
const WCHAR *path)
5841 if (path[0] && path[1] == L
':') {
5842 if (iswalpha(path[0]))
return towupper(path[0]) - L
'A';
5843 return (
int)path[0];
5845 return _getdrive() - 1;
5848#if !defined(NTDDI_WIN11_ZN) || NTDDI_VERSION < NTDDI_WIN11_ZN
5851#define FileStatBasicByNameInfo 3
5854 LARGE_INTEGER FileId;
5855 LARGE_INTEGER CreationTime;
5856 LARGE_INTEGER LastAccessTime;
5857 LARGE_INTEGER LastWriteTime;
5858 LARGE_INTEGER ChangeTime;
5859 LARGE_INTEGER AllocationSize;
5860 LARGE_INTEGER EndOfFile;
5861 DWORD FileAttributes;
5863 DWORD NumberOfLinks;
5865 DWORD DeviceCharacteristics;
5867 LARGE_INTEGER VolumeSerialNumber;
5872#ifndef FILE_DEVICE_DISK
5873#define FILE_DEVICE_DISK 7
5876typedef BOOL (WINAPI *get_file_information_by_name_func)
5877 (PCWSTR,
int , PVOID, ULONG);
5878static get_file_information_by_name_func get_file_information_by_name =
5879 (get_file_information_by_name_func)-1;
5883large_integer_to_unixtime(
const LARGE_INTEGER *at,
long *nsecp)
5885 time_t t = li_time_split_ns(at->QuadPart, nsecp);
5886 if (t < 0)
return 0;
5892path_drive_serial(
const WCHAR *path)
5897 if (path[0] && path[1] == L
':') {
5898 if (!iswalpha(path[0]))
return 0;
5899 drive = towupper(path[0]) - L
'A';
5902 drive = _getdrive() - 1;
5904 if (drive < 0 || (
int)numberof(serials) <= drive)
return 0;
5905 if (!serials[drive]) {
5907 WCHAR root[] = L
"_:\\";
5908 root[0] = L
'A' + drive;
5909 if (get_file_information_by_name(root, FileStatBasicByNameInfo,
5910 &info,
sizeof(info)))
5911 serials[drive] = info.VolumeSerialNumber.QuadPart;
5913 return serials[drive];
5918stat_by_name(
const WCHAR *path,
struct stati128 *st)
5924 unsigned __int64 ino;
5927 if (get_file_information_by_name == (get_file_information_by_name_func)-1) {
5929 get_file_information_by_name = (get_file_information_by_name_func)
5930 get_proc_address(
"kernel32",
"GetFileInformationByName", NULL);
5932 if (!get_file_information_by_name)
return 1;
5933 if (!get_file_information_by_name(path, FileStatBasicByNameInfo,
5934 &info,
sizeof(info))) {
5935 DWORD e = GetLastError();
5937 case ERROR_FILE_NOT_FOUND:
5938 case ERROR_INVALID_NAME:
5939 case ERROR_PATH_NOT_FOUND:
5940 case ERROR_BAD_NETPATH:
5941 errno = map_errno(e);
5946 if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
5948 if (info.DeviceType != FILE_DEVICE_DISK)
5950 if (info.VolumeSerialNumber.QuadPart != path_drive_serial(path))
5952 ino = *((
unsigned __int64 *)&info.FileId128);
5953 inohigh = *((__int64 *)&info.FileId128 + 1);
5954 if (!ino && !inohigh)
5956 if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
5957 if (check_valid_dir(path))
return -1;
5960 st->st_inohigh = inohigh;
5961 st->st_size = info.EndOfFile.QuadPart;
5962 st->st_atime = large_integer_to_unixtime(&info.LastAccessTime, &st->st_atimensec);
5963 st->st_mtime = large_integer_to_unixtime(&info.LastWriteTime, &st->st_mtimensec);
5964 st->st_ctime = large_integer_to_unixtime(&info.CreationTime, &st->st_ctimensec);
5965 st->st_nlink = info.NumberOfLinks;
5966 st->st_mode = fileattr_to_unixmode(info.FileAttributes, path, 0);
5967 st->st_dev = st->st_rdev = path_drive(path);
5973winnt_stat(
const WCHAR *path,
struct stati128 *st, BOOL lstat)
5975 DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
5977 WCHAR *finalname = 0;
5980 memset(st, 0,
sizeof(*st));
5981 switch (stat_by_name(path, st)) {
5987 f = open_special(path, 0, flags);
5988 open_error = GetLastError();
5989 if (f == INVALID_HANDLE_VALUE && !lstat) {
5991 FILE_ATTRIBUTE_TAG_INFO attr_info;
5994 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5995 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5996 &attr_info,
sizeof(attr_info));
5997 if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
5999 f = INVALID_HANDLE_VALUE;
6002 if (f != INVALID_HANDLE_VALUE) {
6003 DWORD attr = stati128_handle(f, st);
6005 switch (GetFileType(f)) {
6006 case FILE_TYPE_CHAR:
6009 case FILE_TYPE_PIPE:
6013 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
6014 if (check_valid_dir(path))
return -1;
6016 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
6017 FILE_ATTRIBUTE_TAG_INFO attr_info;
6020 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
6021 &attr_info,
sizeof(attr_info));
6022 if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) {
6026 else if (rb_w32_reparse_symlink_p(path)) {
6029 mode |= S_IFLNK | S_IEXEC;
6032 mode |= S_IFDIR | S_IEXEC;
6036 const DWORD
len = get_handle_pathname(f, &finalname, 0);
6038 st->st_mode = fileattr_to_unixmode(attr, path, mode);
6041 if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
6042 path += numberof(namespace_prefix);
6046 switch (open_error) {
6047 case ERROR_FILE_NOT_FOUND:
6048 case ERROR_INVALID_NAME:
6049 case ERROR_PATH_NOT_FOUND:
6050 case ERROR_BAD_NETPATH:
6051 case ERROR_CANT_RESOLVE_FILENAME:
6052 errno = map_errno(open_error);
6056 if (stat_by_find(path, st))
return -1;
6059 st->st_dev = st->st_rdev = path_drive(path);
6060 if (finalname) free(finalname);
6067rb_w32_stat(
const char *path,
struct stat *st)
6071 if (w32_stati128(path, &tmp, filecp(), FALSE))
return -1;
6072 COPY_STAT(tmp, *st, (_off_t));
6078wstati128(
const WCHAR *path,
struct stati128 *st, BOOL lstat)
6088 size = lstrlenW(path) + 2;
6090 if (!(path = name_for_stat(buf1, path)))
6092 ret = winnt_stat(path, st, lstat);
6101name_for_stat(WCHAR *buf1,
const WCHAR *path)
6107 for (p = path, s = buf1; *p; p++, s++) {
6115 if (!
len || L
'\"' == *(--s)) {
6119 end = buf1 +
len - 1;
6121 if (isUNCRoot(buf1)) {
6124 else if (*end != L
'\\')
6125 lstrcatW(buf1, L
"\\");
6127 else if (*end == L
'\\' || (buf1 + 1 == end && *end == L
':'))
6128 lstrcatW(buf1, L
".");
6135rb_w32_ustati128(
const char *path,
struct stati128 *st)
6137 return w32_stati128(path, st, CP_UTF8, FALSE);
6142rb_w32_stati128(
const char *path,
struct stati128 *st)
6144 return w32_stati128(path, st, filecp(), FALSE);
6149w32_stati128(
const char *path,
struct stati128 *st, UINT cp, BOOL lstat)
6154 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6156 ret = wstati128(wpath, st, lstat);
6163rb_w32_ulstati128(
const char *path,
struct stati128 *st)
6165 return w32_stati128(path, st, CP_UTF8, TRUE);
6170rb_w32_lstati128(
const char *path,
struct stati128 *st)
6172 return w32_stati128(path, st, filecp(), TRUE);
6177rb_w32_lseek(
int fd, rb_off_t ofs,
int whence)
6179 SOCKET sock = TO_SOCKET(fd);
6180 if (is_socket(sock) || is_pipe(sock)) {
6184 return _lseeki64(fd, ofs, whence);
6189w32_access(
const char *path,
int mode, UINT cp)
6192 if (w32_stati128(path, &stat, cp, FALSE) != 0)
6195 if ((stat.st_mode & mode) != mode) {
6204rb_w32_access(
const char *path,
int mode)
6206 return w32_access(path, mode, filecp());
6211rb_w32_uaccess(
const char *path,
int mode)
6213 return w32_access(path, mode, CP_UTF8);
6218rb_chsize(HANDLE h, rb_off_t size)
6220 long upos, lpos, usize, lsize;
6224 if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6225 (e = GetLastError())) {
6226 errno = map_errno(e);
6229 usize = (long)(size >> 32);
6231 if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
6232 (e = GetLastError())) {
6233 errno = map_errno(e);
6235 else if (!SetEndOfFile(h)) {
6236 errno = map_errno(GetLastError());
6241 SetFilePointer(h, lpos, &upos, SEEK_SET);
6247w32_truncate(
const char *path, rb_off_t length, UINT cp)
6253 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6255 h = CreateFileW(wpath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
6256 if (h == INVALID_HANDLE_VALUE) {
6257 errno = map_errno(GetLastError());
6262 ret = rb_chsize(h, length);
6269rb_w32_utruncate(
const char *path, rb_off_t length)
6271 return w32_truncate(path, length, CP_UTF8);
6276rb_w32_truncate(
const char *path, rb_off_t length)
6278 return w32_truncate(path, length, filecp());
6283rb_w32_ftruncate(
int fd, rb_off_t length)
6287 h = (HANDLE)_get_osfhandle(fd);
6288 if (h == (HANDLE)-1)
return -1;
6289 return rb_chsize(h, length);
6294filetime_to_clock(FILETIME *ft)
6296 __int64 qw = ft->dwHighDateTime;
6298 qw |= ft->dwLowDateTime;
6305rb_w32_times(
struct tms *tmbuf)
6307 FILETIME create, exit, kernel, user;
6309 if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
6310 tmbuf->tms_utime = filetime_to_clock(&user);
6311 tmbuf->tms_stime = filetime_to_clock(&kernel);
6312 tmbuf->tms_cutime = 0;
6313 tmbuf->tms_cstime = 0;
6316 tmbuf->tms_utime = clock();
6317 tmbuf->tms_stime = 0;
6318 tmbuf->tms_cutime = 0;
6319 tmbuf->tms_cstime = 0;
6326#define yield_once() Sleep(0)
6327#define yield_until(condition) do yield_once(); while (!(condition))
6336 uintptr_t (*func)(uintptr_t self,
int argc, uintptr_t* argv);
6344call_asynchronous(PVOID argp)
6348 arg->stackaddr = &argp;
6349 ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
6350 arg->errnum =
errno;
6356rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
6357 int argc, uintptr_t* argv, uintptr_t intrval)
6360 BOOL interrupted = FALSE;
6366 arg.stackaddr = NULL;
6373 thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6376 yield_until(arg.stackaddr);
6378 if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6381 if (TerminateThread(thr, intrval)) {
6386 GetExitCodeThread(thr, &val);
6391 MEMORY_BASIC_INFORMATION m;
6393 memset(&m, 0,
sizeof(m));
6394 if (!VirtualQuery(arg.stackaddr, &m,
sizeof(m))) {
6395 Debug(fprintf(stderr,
"couldn't get stack base:%p:%d\n",
6396 arg.stackaddr, GetLastError()));
6398 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6399 Debug(fprintf(stderr,
"couldn't release stack:%p:%d\n",
6400 m.AllocationBase, GetLastError()));
6411 rb_fatal(
"failed to launch waiter thread:%ld", GetLastError());
6419rb_w32_get_environ(
void)
6421 WCHAR *envtop, *env;
6422 char **myenvtop, **myenv;
6435 envtop = GetEnvironmentStringsW();
6436 for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6437 if (*env !=
'=') num++;
6439 myenvtop = (
char **)malloc(
sizeof(
char *) * (num + 1));
6440 for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
6442 if (!(*myenv = wstr_to_utf8(env, NULL))) {
6449 FreeEnvironmentStringsW(envtop);
6456rb_w32_free_environ(
char **env)
6460 while (*t) free(*t++);
6468 return GetCurrentProcessId();
6476 typedef long (WINAPI query_func)(HANDLE, int,
void *, ULONG, ULONG *);
6477 static query_func *pNtQueryInformationProcess = (query_func *)-1;
6480 if (pNtQueryInformationProcess == (query_func *)-1)
6481 pNtQueryInformationProcess = (query_func *)get_proc_address(
"ntdll.dll",
"NtQueryInformationProcess", NULL);
6482 if (pNtQueryInformationProcess) {
6485 void* PebBaseAddress;
6486 uintptr_t AffinityMask;
6487 uintptr_t BasePriority;
6488 uintptr_t UniqueProcessId;
6489 uintptr_t ParentProcessId;
6492 long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi,
sizeof(pbi), &
len);
6494 ppid = pbi.ParentProcessId;
6501STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
6504#define set_new_std_handle(newfd, handle) do { \
6505 if ((unsigned)(newfd) > 2) break; \
6506 SetStdHandle(STD_INPUT_HANDLE+(STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)*(newfd), \
6509#define set_new_std_fd(newfd) set_new_std_handle(newfd, (HANDLE)rb_w32_get_osfhandle(newfd))
6513rb_w32_dup2(
int oldfd,
int newfd)
6517 if (oldfd == newfd)
return newfd;
6518 ret = dup2(oldfd, newfd);
6519 if (ret < 0)
return ret;
6520 set_new_std_fd(newfd);
6526rb_w32_uopen(
const char *file,
int oflag, ...)
6533 va_start(arg, oflag);
6534 pmode = va_arg(arg,
int);
6537 if (!(wfile = utf8_to_wstr(file, NULL)))
6539 ret = w32_wopen(wfile, oflag, pmode);
6546check_if_wdir(
const WCHAR *wfile)
6548 DWORD attr = GetFileAttributesW(wfile);
6549 if (attr == (DWORD)-1L ||
6550 !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6551 check_valid_dir(wfile)) {
6560rb_w32_open(
const char *file,
int oflag, ...)
6567 va_start(arg, oflag);
6568 pmode = va_arg(arg,
int);
6571 if (!(wfile = filecp_to_wstr(file, NULL)))
6573 ret = w32_wopen(wfile, oflag, pmode);
6580rb_w32_wopen(
const WCHAR *file,
int oflag, ...)
6584 if (oflag & O_CREAT) {
6586 va_start(arg, oflag);
6587 pmode = va_arg(arg,
int);
6591 return w32_wopen(file, oflag, pmode);
6595w32_wopen(
const WCHAR *file,
int oflag,
int pmode)
6601 DWORD attr = FILE_ATTRIBUTE_NORMAL;
6602 SECURITY_ATTRIBUTES sec;
6606 share_delete = oflag & O_SHARE_DELETE ? FILE_SHARE_DELETE : 0;
6607 oflag &= ~O_SHARE_DELETE;
6608 if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
6609 fd = _wopen(file, oflag, pmode);
6613 check_if_wdir(file);
6616 errno = map_errno(GetLastError());
6623 sec.nLength =
sizeof(sec);
6624 sec.lpSecurityDescriptor = NULL;
6625 if (oflag & O_NOINHERIT) {
6626 sec.bInheritHandle = FALSE;
6627 flags |= FNOINHERIT;
6630 sec.bInheritHandle = TRUE;
6632 oflag &= ~O_NOINHERIT;
6635 oflag &= ~(O_BINARY | O_TEXT);
6637 switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
6639 access = GENERIC_READ | GENERIC_WRITE;
6642 access = GENERIC_READ;
6645 access = GENERIC_WRITE;
6651 oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
6653 switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
6655 create = OPEN_ALWAYS;
6659 create = OPEN_EXISTING;
6661 case O_CREAT | O_EXCL:
6662 case O_CREAT | O_EXCL | O_TRUNC:
6663 create = CREATE_NEW;
6666 case O_TRUNC | O_EXCL:
6667 create = TRUNCATE_EXISTING;
6669 case O_CREAT | O_TRUNC:
6670 create = CREATE_ALWAYS;
6676 if (oflag & O_CREAT) {
6678 if (!(pmode & S_IWRITE))
6679 attr = FILE_ATTRIBUTE_READONLY;
6681 oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6683 if (oflag & O_TEMPORARY) {
6684 attr |= FILE_FLAG_DELETE_ON_CLOSE;
6687 oflag &= ~O_TEMPORARY;
6689 if (oflag & _O_SHORT_LIVED)
6690 attr |= FILE_ATTRIBUTE_TEMPORARY;
6691 oflag &= ~_O_SHORT_LIVED;
6693 switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
6697 attr |= FILE_FLAG_SEQUENTIAL_SCAN;
6700 attr |= FILE_FLAG_RANDOM_ACCESS;
6706 oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6708 if (oflag & ~O_APPEND) {
6715 h = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6716 fd = _open_osfhandle((intptr_t)h, 0);
6724 rb_acrt_lowio_lock_fh(fd);
6725 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6726 _set_osflags(fd, 0);
6728 h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE | share_delete, &sec, create, attr, NULL);
6729 if (h == INVALID_HANDLE_VALUE) {
6730 DWORD e = GetLastError();
6731 if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
6732 errno = map_errno(e);
6733 rb_acrt_lowio_unlock_fh(fd);
6738 switch (GetFileType(h)) {
6739 case FILE_TYPE_CHAR:
6742 case FILE_TYPE_PIPE:
6745 case FILE_TYPE_UNKNOWN:
6746 errno = map_errno(GetLastError());
6748 rb_acrt_lowio_unlock_fh(fd);
6752 if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6755 _set_osfhnd(fd, (intptr_t)h);
6756 _set_osflags(fd, flags | FOPEN);
6758 rb_acrt_lowio_unlock_fh(fd);
6768rb_w32_fclose(
FILE *fp)
6770 int fd = fileno(fp);
6771 SOCKET sock = TO_SOCKET(fd);
6772 int save_errno =
errno;
6774 if (fflush(fp))
return -1;
6775 if (!is_socket(sock)) {
6776 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6779 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6782 if (closesocket(sock) == SOCKET_ERROR) {
6783 errno = map_errno(WSAGetLastError());
6791rb_w32_pipe(
int fds[2])
6793 static long serial = 0;
6794 static const char prefix[] =
"\\\\.\\pipe\\ruby";
6796 width_of_prefix = (int)
sizeof(prefix) - 1,
6797 width_of_pid = (int)
sizeof(rb_pid_t) * 2,
6798 width_of_serial = (int)
sizeof(serial) * 2,
6799 width_of_ids = width_of_pid + 1 + width_of_serial + 1
6801 char name[
sizeof(prefix) + width_of_ids];
6802 SECURITY_ATTRIBUTES sec;
6803 HANDLE hRead, hWrite, h;
6804 int fdRead, fdWrite;
6807 memcpy(name, prefix, width_of_prefix);
6808 snprintf(name + width_of_prefix, width_of_ids,
"%.*"PRI_PIDT_PREFIX"x-%.*lx",
6809 width_of_pid, rb_w32_getpid(), width_of_serial, (
unsigned long)(InterlockedIncrement(&serial)-1));
6811 sec.nLength =
sizeof(sec);
6812 sec.lpSecurityDescriptor = NULL;
6813 sec.bInheritHandle = FALSE;
6816 hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6817 0, 2, 65536, 65536, 0, &sec);
6819 if (hRead == INVALID_HANDLE_VALUE) {
6820 DWORD err = GetLastError();
6821 if (err == ERROR_PIPE_BUSY)
6824 errno = map_errno(GetLastError());
6829 hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6830 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6832 if (hWrite == INVALID_HANDLE_VALUE) {
6833 errno = map_errno(GetLastError());
6840 h = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6841 fdRead = _open_osfhandle((intptr_t)h, 0);
6845 CloseHandle(hWrite);
6851 rb_acrt_lowio_lock_fh(fdRead);
6852 _set_osfhnd(fdRead, (intptr_t)hRead);
6853 _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
6854 rb_acrt_lowio_unlock_fh(fdRead);
6860 h = CreateFile(
"NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6861 fdWrite = _open_osfhandle((intptr_t)h, 0);
6863 if (fdWrite == -1) {
6865 CloseHandle(hWrite);
6869 rb_acrt_lowio_lock_fh(fdWrite);
6870 _set_osfhnd(fdWrite, (intptr_t)hWrite);
6871 _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
6872 rb_acrt_lowio_unlock_fh(fdWrite);
6875 rb_w32_close(fdRead);
6887console_emulator_p(
void)
6889 const void *
const func = WriteConsoleW;
6891 MEMORY_BASIC_INFORMATION m;
6893 memset(&m, 0,
sizeof(m));
6894 if (!VirtualQuery(func, &m,
sizeof(m))) {
6897 k = GetModuleHandle(
"kernel32.dll");
6898 if (!k)
return FALSE;
6899 return (HMODULE)m.AllocationBase != k;
6904constat_handle(HANDLE h)
6908 thread_exclusive(conlist) {
6910 if (console_emulator_p()) {
6911 conlist = conlist_disabled;
6914 conlist = st_init_numtable();
6915 install_vm_exit_handler();
6917 else if (conlist == conlist_disabled) {
6920 if (st_lookup(conlist, (st_data_t)h, &data)) {
6924 CONSOLE_SCREEN_BUFFER_INFO csbi;
6926 p->vt100.state = constat_init;
6927 p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6928 p->vt100.reverse = 0;
6929 p->vt100.saved.X = p->vt100.saved.Y = 0;
6930 if (GetConsoleScreenBufferInfo(h, &csbi)) {
6931 p->vt100.attr = csbi.wAttributes;
6933 st_insert(conlist, (st_data_t)h, (st_data_t)p);
6939#define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6940#define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6942#define constat_attr_color_reverse(attr) \
6943 ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6944 (((attr) & FOREGROUND_MASK) << 4) | \
6945 (((attr) & BACKGROUND_MASK) >> 4)
6949constat_attr(
int count,
const int *seq, WORD attr, WORD default_attr,
int *reverse)
6954 if (!count)
return attr;
6955 if (rev) attr = constat_attr_color_reverse(attr);
6956 bold = attr & FOREGROUND_INTENSITY;
6957 attr &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
6959 while (count-- > 0) {
6962 attr = default_attr;
6967 bold = FOREGROUND_INTENSITY;
6973#ifndef COMMON_LVB_UNDERSCORE
6974#define COMMON_LVB_UNDERSCORE 0x8000
6976 attr |= COMMON_LVB_UNDERSCORE;
6979 attr &= ~COMMON_LVB_UNDERSCORE;
6989 attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
6992 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)) | FOREGROUND_RED;
6995 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_RED)) | FOREGROUND_GREEN;
6998 attr = (attr & ~FOREGROUND_BLUE) | FOREGROUND_GREEN | FOREGROUND_RED;
7001 attr = (attr & ~(FOREGROUND_GREEN | FOREGROUND_RED)) | FOREGROUND_BLUE;
7004 attr = (attr & ~FOREGROUND_GREEN) | FOREGROUND_BLUE | FOREGROUND_RED;
7007 attr = (attr & ~FOREGROUND_RED) | FOREGROUND_BLUE | FOREGROUND_GREEN;
7010 attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
7015 attr = (attr & ~FOREGROUND_MASK) | (default_attr & FOREGROUND_MASK);
7019 attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
7022 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN)) | BACKGROUND_RED;
7025 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_RED)) | BACKGROUND_GREEN;
7028 attr = (attr & ~BACKGROUND_BLUE) | BACKGROUND_GREEN | BACKGROUND_RED;
7031 attr = (attr & ~(BACKGROUND_GREEN | BACKGROUND_RED)) | BACKGROUND_BLUE;
7034 attr = (attr & ~BACKGROUND_GREEN) | BACKGROUND_BLUE | BACKGROUND_RED;
7037 attr = (attr & ~BACKGROUND_RED) | BACKGROUND_BLUE | BACKGROUND_GREEN;
7040 attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
7045 attr = (attr & ~BACKGROUND_MASK) | (default_attr & BACKGROUND_MASK);
7050 if (rev) attr = constat_attr_color_reverse(attr);
7057constat_clear(HANDLE handle, WORD attr, DWORD
len, COORD pos)
7061 FillConsoleOutputAttribute(handle, attr,
len, pos, &written);
7062 FillConsoleOutputCharacterW(handle, L
' ',
len, pos, &written);
7067constat_apply(HANDLE handle,
struct constat *s, WCHAR w)
7069 CONSOLE_SCREEN_BUFFER_INFO csbi;
7070 const int *seq = s->vt100.seq;
7071 int count = s->vt100.state;
7075 if (!GetConsoleScreenBufferInfo(handle, &csbi))
return;
7076 arg0 = (count > 0 && seq[0] > 0);
7077 if (arg0) arg1 = seq[0];
7080 SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr, &s->vt100.reverse));
7083 csbi.dwCursorPosition.X = 0;
7085 csbi.dwCursorPosition.Y -= arg1;
7086 if (csbi.dwCursorPosition.Y < csbi.srWindow.Top)
7087 csbi.dwCursorPosition.Y = csbi.srWindow.Top;
7088 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
7091 csbi.dwCursorPosition.X = 0;
7094 csbi.dwCursorPosition.Y += arg1;
7095 if (csbi.dwCursorPosition.Y > csbi.srWindow.Bottom)
7096 csbi.dwCursorPosition.Y = csbi.srWindow.Bottom;
7097 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
7100 csbi.dwCursorPosition.X += arg1;
7101 if (csbi.dwCursorPosition.X >= csbi.srWindow.Right)
7102 csbi.dwCursorPosition.X = csbi.srWindow.Right;
7103 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
7106 csbi.dwCursorPosition.X -= arg1;
7107 if (csbi.dwCursorPosition.X < csbi.srWindow.Left)
7108 csbi.dwCursorPosition.X = csbi.srWindow.Left;
7109 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
7113 arg1 += csbi.srWindow.Left;
7114 if (arg1 > csbi.srWindow.Right)
7115 arg1 = csbi.srWindow.Right;
7116 csbi.dwCursorPosition.X = arg1;
7117 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
7120 arg1 += csbi.srWindow.Top;
7121 if (arg1 > csbi.srWindow.Bottom)
7122 arg1 = csbi.srWindow.Bottom;
7123 csbi.dwCursorPosition.Y = arg1;
7124 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
7128 pos.Y = arg1 + csbi.srWindow.Top - 1;
7129 if (pos.Y > csbi.srWindow.Bottom) pos.Y = csbi.srWindow.Bottom;
7130 if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
7131 pos.X = arg1 + csbi.srWindow.Left - 1;
7132 if (pos.X > csbi.srWindow.Right) pos.X = csbi.srWindow.Right;
7133 SetConsoleCursorPosition(handle, pos);
7136 switch (arg0 ? arg1 : 0) {
7138 constat_clear(handle, csbi.wAttributes,
7139 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.dwCursorPosition.Y + 1)
7140 - csbi.dwCursorPosition.X),
7141 csbi.dwCursorPosition);
7145 pos.Y = csbi.srWindow.Top;
7146 constat_clear(handle, csbi.wAttributes,
7147 (csbi.dwSize.X * (csbi.dwCursorPosition.Y - csbi.srWindow.Top)
7148 + csbi.dwCursorPosition.X + 1),
7153 pos.Y = csbi.srWindow.Top;
7154 constat_clear(handle, csbi.wAttributes,
7155 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1)),
7161 constat_clear(handle, csbi.wAttributes,
7162 (csbi.dwSize.X * csbi.dwSize.Y),
7168 switch (arg0 ? arg1 : 0) {
7170 constat_clear(handle, csbi.wAttributes,
7171 (csbi.dwSize.X - csbi.dwCursorPosition.X),
7172 csbi.dwCursorPosition);
7176 pos.Y = csbi.dwCursorPosition.Y;
7177 constat_clear(handle, csbi.wAttributes,
7178 csbi.dwCursorPosition.X + 1, pos);
7182 pos.Y = csbi.dwCursorPosition.Y;
7183 constat_clear(handle, csbi.wAttributes,
7184 csbi.dwSize.X, pos);
7189 s->vt100.saved = csbi.dwCursorPosition;
7192 SetConsoleCursorPosition(handle, s->vt100.saved);
7195 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7196 CONSOLE_CURSOR_INFO cci;
7197 GetConsoleCursorInfo(handle, &cci);
7198 cci.bVisible = TRUE;
7199 SetConsoleCursorInfo(handle, &cci);
7203 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
7204 CONSOLE_CURSOR_INFO cci;
7205 GetConsoleCursorInfo(handle, &cci);
7206 cci.bVisible = FALSE;
7207 SetConsoleCursorInfo(handle, &cci);
7215constat_parse(HANDLE h,
struct constat *s,
const WCHAR **ptrp,
long *lenp)
7217 const WCHAR *ptr = *ptrp;
7218 long rest,
len = *lenp;
7222 rest = *lenp -
len - 1;
7223 if (s->vt100.state == constat_esc) {
7226 s->vt100.state = constat_init;
7227 if (
len > 0 && *ptr != L
'[')
continue;
7228 s->vt100.state = constat_esc;
7230 else if (s->vt100.state == constat_esc) {
7233 s->vt100.state = constat_init;
7236 rest = *lenp -
len - 1;
7237 if (rest > 0) --rest;
7238 s->vt100.state = constat_seq;
7239 s->vt100.seq[0] = 0;
7241 else if (s->vt100.state >= constat_seq) {
7242 if (wc >= L
'0' && wc <= L
'9') {
7243 if (s->vt100.state < (
int)numberof(s->vt100.seq)) {
7244 int *seq = &s->vt100.seq[s->vt100.state];
7245 *seq = (*seq * 10) + (wc - L
'0');
7248 else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L
'?') {
7249 s->vt100.seq[s->vt100.state++] = -1;
7253 if (++s->vt100.state < (
int)numberof(s->vt100.seq)) {
7254 s->vt100.seq[s->vt100.state] = 0;
7257 s->vt100.state = (int)numberof(s->vt100.seq);
7261 constat_apply(h, s, wc);
7262 s->vt100.state = constat_init;
7285 SOCKET sock = TO_SOCKET(fd);
7286 int save_errno =
errno;
7288 if (!is_socket(sock)) {
7289 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7290 constat_delete((HANDLE)sock);
7293 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
7294 socklist_delete(&sock, NULL);
7297 if (closesocket(sock) == SOCKET_ERROR) {
7298 errno = map_errno(WSAGetLastError());
7304#ifndef INVALID_SET_FILE_POINTER
7305#define INVALID_SET_FILE_POINTER ((DWORD)-1)
7309setup_overlapped(OVERLAPPED *ol,
int fd,
int iswrite, rb_off_t *_offset)
7311 memset(ol, 0,
sizeof(*ol));
7316 DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
7320 uint64_t offset = *_offset;
7321 ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
7322 ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
7325 LARGE_INTEGER seek_offset = {0}, current_offset = {0};
7326 if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, ¤t_offset, seek_method)) {
7327 DWORD last_error = GetLastError();
7328 if (last_error != NO_ERROR) {
7329 errno = map_errno(last_error);
7335 *_offset = current_offset.QuadPart;
7337 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7339 DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method);
7341 if (low == INVALID_SET_FILE_POINTER) {
7342 DWORD err = GetLastError();
7343 if (err != NO_ERROR) {
7344 errno = map_errno(err);
7350 ol->OffsetHigh = high;
7353 ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7355 errno = map_errno(GetLastError());
7362finish_overlapped(OVERLAPPED *ol,
int fd, DWORD size, rb_off_t *_offset)
7364 CloseHandle(ol->hEvent);
7368 DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN;
7370 LARGE_INTEGER seek_offset = {0};
7371 if (seek_method == FILE_BEGIN) {
7372 seek_offset.QuadPart = *_offset;
7375 SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method);
7377 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7378 LONG high = ol->OffsetHigh;
7379 DWORD low = ol->Offset + size;
7380 if (low < ol->Offset)
7382 SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
7389rb_w32_read_internal(
int fd,
void *buf,
size_t size, rb_off_t *offset)
7391 SOCKET sock = TO_SOCKET(fd);
7399 if (is_socket(sock))
7400 return rb_w32_recv(fd, buf, size, 0);
7403 if (_get_osfhandle(fd) == -1) {
7407 if (!offset && _osfile(fd) & FTEXT) {
7408 return _read(fd, buf, size);
7411 rb_acrt_lowio_lock_fh(fd);
7413 if (!size || _osfile(fd) & FEOFLAG) {
7414 _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7415 rb_acrt_lowio_unlock_fh(fd);
7424 if (setup_overlapped(&ol, fd, FALSE, offset)) {
7425 rb_acrt_lowio_unlock_fh(fd);
7429 if (!ReadFile((HANDLE)_osfhnd(fd), buf,
len, &read, &ol)) {
7430 err = GetLastError();
7431 if (err == ERROR_NO_DATA && (_osfile(fd) & FPIPE)) {
7433 if (GetNamedPipeHandleState((HANDLE)_osfhnd(fd), &state, NULL, NULL, NULL, NULL, 0) && (state & PIPE_NOWAIT)) {
7434 errno = EWOULDBLOCK;
7437 errno = map_errno(err);
7439 rb_acrt_lowio_unlock_fh(fd);
7442 else if (err != ERROR_IO_PENDING) {
7443 CloseHandle(ol.hEvent);
7444 if (err == ERROR_ACCESS_DENIED)
7446 else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
7447 rb_acrt_lowio_unlock_fh(fd);
7451 errno = map_errno(err);
7453 rb_acrt_lowio_unlock_fh(fd);
7457 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7458 if (wait != WAIT_OBJECT_0) {
7459 if (wait == WAIT_OBJECT_0 + 1)
7462 errno = map_errno(GetLastError());
7463 CloseHandle(ol.hEvent);
7464 CancelIo((HANDLE)_osfhnd(fd));
7465 rb_acrt_lowio_unlock_fh(fd);
7469 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
7470 (err = GetLastError()) != ERROR_HANDLE_EOF) {
7472 if (err != ERROR_BROKEN_PIPE) {
7473 errno = map_errno(err);
7476 CloseHandle(ol.hEvent);
7477 CancelIo((HANDLE)_osfhnd(fd));
7478 rb_acrt_lowio_unlock_fh(fd);
7483 err = GetLastError();
7484 errno = map_errno(err);
7487 finish_overlapped(&ol, fd, read, offset);
7491 buf = (
char *)buf + read;
7492 if (err != ERROR_OPERATION_ABORTED && size > 0)
7496 _set_osflags(fd, _osfile(fd) | FEOFLAG);
7499 rb_acrt_lowio_unlock_fh(fd);
7507rb_w32_write_internal(
int fd,
const void *buf,
size_t size, rb_off_t *offset)
7509 SOCKET sock = TO_SOCKET(fd);
7517 if (is_socket(sock))
7518 return rb_w32_send(fd, buf, size, 0);
7521 if (_get_osfhandle(fd) == -1) {
7526 if (!offset && (_osfile(fd) & FTEXT) &&
7527 (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
7528 ssize_t w = _write(fd, buf, size);
7529 if (w == (ssize_t)-1 &&
errno == EINVAL) {
7530 errno = map_errno(GetLastError());
7535 rb_acrt_lowio_lock_fh(fd);
7537 if (!size || _osfile(fd) & FEOFLAG) {
7538 rb_acrt_lowio_unlock_fh(fd);
7549 if (setup_overlapped(&ol, fd, TRUE, offset)) {
7550 rb_acrt_lowio_unlock_fh(fd);
7554 if (!WriteFile((HANDLE)_osfhnd(fd), buf,
len, &written, &ol)) {
7555 err = GetLastError();
7556 if (err != ERROR_IO_PENDING) {
7557 CloseHandle(ol.hEvent);
7558 if (err == ERROR_ACCESS_DENIED)
7561 errno = map_errno(err);
7563 rb_acrt_lowio_unlock_fh(fd);
7567 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7568 if (wait != WAIT_OBJECT_0) {
7569 if (wait == WAIT_OBJECT_0 + 1)
7572 errno = map_errno(GetLastError());
7573 CloseHandle(ol.hEvent);
7574 CancelIo((HANDLE)_osfhnd(fd));
7575 rb_acrt_lowio_unlock_fh(fd);
7579 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) {
7580 errno = map_errno(GetLastError());
7581 CloseHandle(ol.hEvent);
7582 CancelIo((HANDLE)_osfhnd(fd));
7583 rb_acrt_lowio_unlock_fh(fd);
7588 finish_overlapped(&ol, fd, written, offset);
7591 if (written ==
len) {
7592 buf = (
const char *)buf +
len;
7597 size_t newlen =
len / 2;
7599 size +=
len - newlen;
7604 errno = EWOULDBLOCK;
7607 rb_acrt_lowio_unlock_fh(fd);
7613rb_w32_read(
int fd,
void *buf,
size_t size)
7615 return rb_w32_read_internal(fd, buf, size, NULL);
7619rb_w32_write(
int fd,
const void *buf,
size_t size)
7621 return rb_w32_write_internal(fd, buf, size, NULL);
7625rb_w32_pread(
int descriptor,
void *base,
size_t size, rb_off_t offset)
7627 return rb_w32_read_internal(descriptor, base, size, &offset);
7631rb_w32_pwrite(
int descriptor,
const void *base,
size_t size, rb_off_t offset)
7633 return rb_w32_write_internal(descriptor, base, size, &offset);
7638rb_w32_write_console(uintptr_t strarg,
int fd)
7641 DWORD dwMode, reslen;
7645 const WCHAR *ptr, *next;
7649 handle = (HANDLE)_osfhnd(fd);
7650 if (!GetConsoleMode(handle, &dwMode))
7653 s = constat_handle(handle);
7663 case ENCINDEX_US_ASCII:
7664 case ENCINDEX_ASCII_8BIT:
7666 case ENCINDEX_UTF_8:
7667 ptr = wbuffer = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(str), RSTRING_LEN(str), &
len);
7668 if (!ptr)
return -1L;
7670 case ENCINDEX_UTF_16LE:
7671 ptr = (
const WCHAR *)RSTRING_PTR(str);
7672 len = RSTRING_LEN(str) /
sizeof(WCHAR);
7676 if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7677 if (!WriteConsoleW(handle, ptr,
len, &reslen, NULL))
7678 reslen = (DWORD)-1L;
7682 long curlen = constat_parse(handle, s, (next = ptr, &next), &
len);
7683 reslen += next - ptr;
7686 if (!WriteConsoleW(handle, ptr, curlen, &written, NULL)) {
7687 reslen = (DWORD)-1L;
7696 return (
long)reslen;
7701timespec_to_filetime(
const struct timespec *ts, FILETIME *ft)
7705 tmp.QuadPart = unix_to_filetime((ULONGLONG)ts->tv_sec);
7706 tmp.QuadPart += ts->tv_nsec / 100;
7707 ft->dwLowDateTime = tmp.LowPart;
7708 ft->dwHighDateTime = tmp.HighPart;
7714wutimensat(
int dirfd,
const WCHAR *path,
const struct timespec *times,
int flags)
7717 FILETIME atime, mtime;
7722 if (dirfd != AT_FDCWD) {
7732 if (wstati128(path, &stat, FALSE)) {
7737 if (timespec_to_filetime(×[0], &atime)) {
7740 if (timespec_to_filetime(×[1], &mtime)) {
7745 GetSystemTimePreciseAsFileTime(&atime);
7750 const DWORD attr = GetFileAttributesW(path);
7751 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7752 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7753 hFile = open_special(path, GENERIC_WRITE, 0);
7754 if (hFile == INVALID_HANDLE_VALUE) {
7755 errno = map_errno(GetLastError());
7759 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7760 errno = map_errno(GetLastError());
7765 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7766 SetFileAttributesW(path, attr);
7774w32_utimensat(
int dirfd,
const char *path,
const struct timespec *times,
int flags, UINT cp)
7776 WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7780 ret = wutimensat(dirfd, wpath, times, flags);
7788rb_w32_uutime(
const char *path,
const struct utimbuf *times)
7792 ts[0].tv_sec = times->actime;
7794 ts[1].tv_sec = times->modtime;
7796 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7801rb_w32_utime(
const char *path,
const struct utimbuf *times)
7805 ts[0].tv_sec = times->actime;
7807 ts[1].tv_sec = times->modtime;
7809 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7814rb_w32_uutimes(
const char *path,
const struct timeval *times)
7818 ts[0].tv_sec = times[0].tv_sec;
7819 ts[0].tv_nsec = times[0].tv_usec * 1000;
7820 ts[1].tv_sec = times[1].tv_sec;
7821 ts[1].tv_nsec = times[1].tv_usec * 1000;
7822 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7827rb_w32_utimes(
const char *path,
const struct timeval *times)
7831 ts[0].tv_sec = times[0].tv_sec;
7832 ts[0].tv_nsec = times[0].tv_usec * 1000;
7833 ts[1].tv_sec = times[1].tv_sec;
7834 ts[1].tv_nsec = times[1].tv_usec * 1000;
7835 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7840rb_w32_uutimensat(
int dirfd,
const char *path,
const struct timespec *times,
int flags)
7842 return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7847rb_w32_utimensat(
int dirfd,
const char *path,
const struct timespec *times,
int flags)
7849 return w32_utimensat(dirfd, path, times, flags, filecp());
7854rb_w32_uchdir(
const char *path)
7859 if (!(wpath = utf8_to_wstr(path, NULL)))
7861 ret = _wchdir(wpath);
7868wmkdir(
const WCHAR *wpath,
int mode)
7873 if (CreateDirectoryW(wpath, NULL) == FALSE) {
7874 errno = map_errno(GetLastError());
7877 if (_wchmod(wpath, mode) == -1) {
7878 RemoveDirectoryW(wpath);
7888rb_w32_umkdir(
const char *path,
int mode)
7893 if (!(wpath = utf8_to_wstr(path, NULL)))
7895 ret = wmkdir(wpath, mode);
7902rb_w32_mkdir(
const char *path,
int mode)
7907 if (!(wpath = filecp_to_wstr(path, NULL)))
7909 ret = wmkdir(wpath, mode);
7916wrmdir(
const WCHAR *wpath)
7920 const DWORD attr = GetFileAttributesW(wpath);
7921 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7922 SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
7924 if (RemoveDirectoryW(wpath) == FALSE) {
7925 errno = map_errno(GetLastError());
7927 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7928 SetFileAttributesW(wpath, attr);
7937rb_w32_rmdir(
const char *path)
7942 if (!(wpath = filecp_to_wstr(path, NULL)))
7944 ret = wrmdir(wpath);
7951rb_w32_urmdir(
const char *path)
7956 if (!(wpath = utf8_to_wstr(path, NULL)))
7958 ret = wrmdir(wpath);
7965wunlink(
const WCHAR *path)
7968 const DWORD SYMLINKD = FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY;
7970 const DWORD attr = GetFileAttributesW(path);
7971 if (attr == (DWORD)-1) {
7973 else if ((attr & SYMLINKD) == SYMLINKD) {
7974 ret = RemoveDirectoryW(path);
7977 if (attr & FILE_ATTRIBUTE_READONLY) {
7978 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7980 ret = DeleteFileW(path);
7983 errno = map_errno(GetLastError());
7985 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7986 SetFileAttributesW(path, attr);
7995rb_w32_uunlink(
const char *path)
8000 if (!(wpath = utf8_to_wstr(path, NULL)))
8002 ret = wunlink(wpath);
8009rb_w32_unlink(
const char *path)
8014 if (!(wpath = filecp_to_wstr(path, NULL)))
8016 ret = wunlink(wpath);
8023rb_w32_uchmod(
const char *path,
int mode)
8028 if (!(wpath = utf8_to_wstr(path, NULL)))
8030 ret = _wchmod(wpath, mode);
8037fchmod(
int fd,
int mode)
8041 LARGE_INTEGER CreationTime;
8042 LARGE_INTEGER LastAccessTime;
8043 LARGE_INTEGER LastWriteTime;
8044 LARGE_INTEGER ChangeTime;
8045 DWORD FileAttributes;
8046 } info = {{{0}}, {{0}}, {{0}},};
8047 HANDLE h = (HANDLE)_get_osfhandle(fd);
8049 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
8050 if (!(mode & 0200)) info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
8051 if (!SetFileInformationByHandle(h, 0, &info,
sizeof(info))) {
8052 errno = map_errno(GetLastError());
8060rb_w32_isatty(
int fd)
8065 if (_get_osfhandle(fd) == -1) {
8068 if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
8080 int *ip = (
int *)(&x + 1) - 1;
8087rb_w32_inet_ntop(
int af,
const void *addr,
char *numaddr,
size_t numaddr_len)
8089 return (inet_ntop)(af, (
void *)addr, numaddr, numaddr_len);
8094rb_w32_inet_pton(
int af,
const char *src,
void *dst)
8096 return (inet_pton)(af, src, dst);
8101rb_w32_fd_is_text(
int fd)
8103 return _osfile(fd) & FTEXT;
8106#ifdef HAVE__GMTIME64_S
8107# ifndef HAVE__LOCALTIME64_S
8109# define HAVE__LOCALTIME64_S 1
8111# ifndef MINGW_HAS_SECURE_API
8112 _CRTIMP errno_t __cdecl _gmtime64_s(
struct tm* tm,
const __time64_t *time);
8113 _CRTIMP errno_t __cdecl _localtime64_s(
struct tm* tm,
const __time64_t *time);
8115# define gmtime_s _gmtime64_s
8116# define localtime_s _localtime64_s
8121gmtime_r(
const time_t *tp,
struct tm *rp)
8129 e = gmtime_s(rp, tp);
8130 if (e != 0)
goto error;
8136localtime_r(
const time_t *tp,
struct tm *rp)
8144 e = localtime_s(rp, tp);
8151rb_w32_wrap_io_handle(HANDLE h,
int flags)
8154 int len =
sizeof(tmp);
8155 int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (
char *)&tmp, &
len);
8156 if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
8158 if (flags & O_NONBLOCK) {
8159 flags &= ~O_NONBLOCK;
8162 socklist_insert((SOCKET)h, f);
8164 else if (flags & O_NONBLOCK) {
8168 return rb_w32_open_osfhandle((intptr_t)h, flags);
8173rb_w32_unwrap_io_handle(
int fd)
8175 SOCKET sock = TO_SOCKET(fd);
8176 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
8177 if (!is_socket(sock)) {
8178 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
8179 constat_delete((HANDLE)sock);
8182 socklist_delete(&sock, NULL);
8187#if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8193rb_w32_pow(
double x,
double y)
8197 unsigned int default_control = _controlfp(0, 0);
8198 _controlfp(_PC_64, _MCW_PC);
8201 _controlfp(default_control, _MCW_PC);
8209 BY_HANDLE_FILE_INFORMATION bhfi;
8220 tmp = rb_check_convert_type_with_id(*file,
T_FILE,
"IO", idTo_io);
8223 if (f == (HANDLE)-1)
return INVALID_HANDLE_VALUE;
8232 tmp = rb_str_encode_ospath(*file);
8233 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
8235 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr,
len);
8236 f = CreateFileW(ptr, 0,
8237 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
8238 FILE_FLAG_BACKUP_SEMANTICS, NULL);
8240 if (f == INVALID_HANDLE_VALUE)
return f;
8243 if (GetFileType(f) == FILE_TYPE_DISK) {
8244 ZeroMemory(st,
sizeof(*st));
8245 if (get_ino(f, &st->info.fii)) {
8246 st->file_id_p = TRUE;
8249 else if (GetLastError() != ERROR_INVALID_PARAMETER) {
8251 return INVALID_HANDLE_VALUE;
8255 if (GetFileInformationByHandle(f, &st->info.bhfi)) {
8256 st->file_id_p = FALSE;
8260 if (ret) CloseHandle(ret);
8261 return INVALID_HANDLE_VALUE;
8265close_handle(
VALUE h)
8267 CloseHandle((HANDLE)h);
8277call_w32_io_info(
VALUE arg)
8280 return (
VALUE)w32_io_info(p->fname, p->st);
8284rb_w32_file_identical_p(
VALUE fname1,
VALUE fname2)
8287 HANDLE f1 = 0, f2 = 0;
8289 f1 = w32_io_info(&fname1, &st1);
8290 if (f1 == INVALID_HANDLE_VALUE)
return Qfalse;
8293 arg.fname = &fname2;
8298 f2 = w32_io_info(&fname2, &st2);
8300 if (f2 == INVALID_HANDLE_VALUE)
return Qfalse;
8301 if (f2) CloseHandle(f2);
8303 if (st1.file_id_p != st2.file_id_p)
return Qfalse;
8304 if (!st1.file_id_p) {
8305 if (st1.info.bhfi.dwVolumeSerialNumber == st2.info.bhfi.dwVolumeSerialNumber &&
8306 st1.info.bhfi.nFileIndexHigh == st2.info.bhfi.nFileIndexHigh &&
8307 st1.info.bhfi.nFileIndexLow == st2.info.bhfi.nFileIndexLow)
8311 if (st1.info.fii.VolumeSerialNumber == st2.info.fii.VolumeSerialNumber &&
8312 memcmp(&st1.info.fii.FileId, &st2.info.fii.FileId,
sizeof(
FILE_ID_128)) == 0)
8319rb_w32_set_thread_description(HANDLE th,
const WCHAR *name)
8322 typedef HRESULT (WINAPI *set_thread_description_func)(HANDLE, PCWSTR);
8323 static set_thread_description_func set_thread_description =
8324 (set_thread_description_func)-1;
8325 if (set_thread_description == (set_thread_description_func)-1) {
8327 set_thread_description = (set_thread_description_func)
8328 get_proc_address(
"kernel32",
"SetThreadDescription", NULL);
8330 if (set_thread_description) {
8331 result = set_thread_description(th, name);
8337rb_w32_set_thread_description_str(HANDLE th,
VALUE name)
8339 int idx, result = FALSE;
8343 return rb_w32_set_thread_description(th, L
"");
8346 idx = rb_enc_get_index(name);
8347 if (idx == ENCINDEX_UTF_16LE) {
8348 result = rb_w32_set_thread_description(th, s);
8351 name =
rb_str_conv_enc(name, rb_enc_from_index(idx), rb_utf8_encoding());
8352 s = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(name), RSTRING_LEN(name)+1, NULL);
8353 result = rb_w32_set_thread_description(th, s);
8363rb_w32_mmap(
void *addr,
size_t len,
int prot,
int flags,
int fd, rb_off_t offset)
8367 DWORD protect = PAGE_EXECUTE_READWRITE;
8369 if (fd > 0 || offset) {
8384 ptr = VirtualAlloc(addr,
len, MEM_RESERVE | MEM_COMMIT, protect);
8386 errno = rb_w32_map_errno(GetLastError());
8394rb_w32_munmap(
void *addr,
size_t len)
8396 if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8397 errno = rb_w32_map_errno(GetLastError());
8405rb_w32_mprotect(
void *addr,
size_t len,
int prot)
8421 if (prot & PROT_EXEC) {
8422 if (!FlushInstructionCache(GetCurrentProcess(), addr,
len)) {
8423 errno = rb_w32_map_errno(GetLastError());
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
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 ALLOC_N
Old name of RB_ALLOC_N.
#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.
#define RBIMPL_ATTR_NONNULL(list)
Wraps (or simulates) __attribute__((nonnull))
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.
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.
void rb_write_error2(const char *str, long len)
Identical to rb_write_error(), except it additionally takes the message's length.
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.
#define rb_utf8_str_new(str, len)
Identical to rb_str_new, except it generates a string of "UTF-8" encoding.
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.
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.
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.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#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.
#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.