Ruby 3.5.0dev (2025-02-22 revision d6f44535c6482e895483c0c28c9a35bcf5e4fd88)
win32.c
1/*
2 * Copyright (c) 1993, Intergraph Corporation
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Artistic License, as specified in the perl README file.
6 *
7 * Various Unix compatibility functions and NT specific functions.
8 *
9 * Some of this code was derived from the MSDOS port(s) and the OS/2 port.
10 *
11 */
12/*
13 The parts licensed under above copyright notice are marked as "Artistic or
14 GPL".
15 Another parts are licensed under Ruby's License.
16
17 Copyright (C) 1993-2011 Yukihiro Matsumoto
18 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
19 Copyright (C) 2000 Information-technology Promotion Agency, Japan
20 */
21
22#undef __STRICT_ANSI__
23
24#include "ruby/ruby.h"
25#include "ruby/encoding.h"
26#include "ruby/io.h"
27#include "ruby/util.h"
28#include <fcntl.h>
29#include <process.h>
30#include <sys/stat.h>
31/* #include <sys/wait.h> */
32#include <stdio.h>
33#include <stdlib.h>
34#include <errno.h>
35#include <assert.h>
36#include <ctype.h>
37
38#include <windows.h>
39#include <winbase.h>
40#include <wincon.h>
41#include <share.h>
42#include <shlobj.h>
43#include <mbstring.h>
44#include <shlwapi.h>
45#if defined _MSC_VER && _MSC_VER >= 1400
46#include <crtdbg.h>
47#include <rtcapi.h>
48#endif
49#ifdef __MINGW32__
50#include <mswsock.h>
51#endif
52#ifdef HAVE_AFUNIX_H
53# include <afunix.h>
54#endif
55#include "ruby/win32.h"
56#include "ruby/vm.h"
57#include "win32/dir.h"
58#include "win32/file.h"
59#include "id.h"
60#include "internal.h"
61#include "internal/enc.h"
62#include "internal/object.h"
63#include "internal/static_assert.h"
65#include "encindex.h"
66#define isdirsep(x) ((x) == '/' || (x) == '\\')
67
68#if defined _MSC_VER && _MSC_VER <= 1200
69# define CharNextExA(cp, p, flags) CharNextExA((WORD)(cp), (p), (flags))
70#endif
71
72static int w32_wopen(const WCHAR *file, int oflag, int perm);
73static int w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat);
74static char *w32_getenv(const char *name, UINT cp);
75
76#undef getenv
77/*
78 * Do not remove the macros to substitute functions in dln_find.c.
79 */
80#define DLN_FIND_EXTRA_ARG_DECL ,UINT cp
81#define DLN_FIND_EXTRA_ARG ,cp
82#define rb_w32_stati128(path, st) w32_stati128(path, st, cp, FALSE)
83#define getenv(name) w32_getenv(name, cp) /* Necessarily For dln.c */
84#undef CharNext
85#define CharNext(p) CharNextExA(cp, (p), 0)
86#define dln_find_exe_r rb_w32_udln_find_exe_r
87#define dln_find_file_r rb_w32_udln_find_file_r
88#include "dln.h"
89#include "dln_find.c"
90#undef MAXPATHLEN
91#undef rb_w32_stati128
92#undef dln_find_exe_r
93#undef dln_find_file_r
94#define dln_find_exe_r(fname, path, buf, size) rb_w32_udln_find_exe_r(fname, path, buf, size, cp)
95#define dln_find_file_r(fname, path, buf, size) rb_w32_udln_find_file_r(fname, path, buf, size, cp)
96#undef CharNext /* no default cp version */
97#undef getenv
98
99#ifndef PATH_MAX
100# if defined MAX_PATH
101# define PATH_MAX MAX_PATH
102# elif defined HAVE_SYS_PARAM_H
103# include <sys/param.h>
104# define PATH_MAX MAXPATHLEN
105# endif
106#endif
107#define ENV_MAX 512
108
109#undef stat
110#undef fclose
111#undef close
112#undef setsockopt
113#undef dup2
114#undef strdup
115
116#if RUBY_MSVCRT_VERSION >= 140
117# define _filbuf _fgetc_nolock
118# define _flsbuf _fputc_nolock
119#endif
120#define enough_to_get(n) (--(n) >= 0)
121#define enough_to_put(n) (--(n) >= 0)
122
123#ifdef WIN32_DEBUG
124#define Debug(something) something
125#else
126#define Debug(something) /* nothing */
127#endif
128
129#define TO_SOCKET(x) _get_osfhandle(x)
130
131int rb_w32_reparse_symlink_p(const WCHAR *path);
132
133static int has_redirection(const char *, UINT);
134int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
135static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
136static int wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat);
137VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
138int ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc);
139static FARPROC get_proc_address(const char *module, const char *func, HANDLE *mh);
140
141#define RUBY_CRITICAL if (0) {} else /* just remark */
142
143/* errno mapping */
144static const struct {
145 DWORD winerr;
146 int err;
147} errmap[] = {
148 { ERROR_INVALID_FUNCTION, EINVAL },
149 { ERROR_FILE_NOT_FOUND, ENOENT },
150 { ERROR_PATH_NOT_FOUND, ENOENT },
151 { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
152 { ERROR_ACCESS_DENIED, EACCES },
153 { ERROR_INVALID_HANDLE, EBADF },
154 { ERROR_ARENA_TRASHED, ENOMEM },
155 { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
156 { ERROR_INVALID_BLOCK, ENOMEM },
157 { ERROR_BAD_ENVIRONMENT, E2BIG },
158 { ERROR_BAD_FORMAT, ENOEXEC },
159 { ERROR_INVALID_ACCESS, EINVAL },
160 { ERROR_INVALID_DATA, EINVAL },
161 { ERROR_INVALID_DRIVE, ENOENT },
162 { ERROR_CURRENT_DIRECTORY, EACCES },
163 { ERROR_NOT_SAME_DEVICE, EXDEV },
164 { ERROR_NO_MORE_FILES, ENOENT },
165 { ERROR_WRITE_PROTECT, EROFS },
166 { ERROR_BAD_UNIT, ENODEV },
167 { ERROR_NOT_READY, ENXIO },
168 { ERROR_BAD_COMMAND, EACCES },
169 { ERROR_CRC, EACCES },
170 { ERROR_BAD_LENGTH, EACCES },
171 { ERROR_SEEK, EIO },
172 { ERROR_NOT_DOS_DISK, EACCES },
173 { ERROR_SECTOR_NOT_FOUND, EACCES },
174 { ERROR_OUT_OF_PAPER, EACCES },
175 { ERROR_WRITE_FAULT, EIO },
176 { ERROR_READ_FAULT, EIO },
177 { ERROR_GEN_FAILURE, EACCES },
178 { ERROR_LOCK_VIOLATION, EACCES },
179 { ERROR_SHARING_VIOLATION, EACCES },
180 { ERROR_WRONG_DISK, EACCES },
181 { ERROR_SHARING_BUFFER_EXCEEDED, EACCES },
182 { ERROR_BAD_NETPATH, ENOENT },
183 { ERROR_NETWORK_ACCESS_DENIED, EACCES },
184 { ERROR_BAD_NET_NAME, ENOENT },
185 { ERROR_FILE_EXISTS, EEXIST },
186 { ERROR_CANNOT_MAKE, EACCES },
187 { ERROR_FAIL_I24, EACCES },
188 { ERROR_INVALID_PARAMETER, EINVAL },
189 { ERROR_NO_PROC_SLOTS, EAGAIN },
190 { ERROR_DRIVE_LOCKED, EACCES },
191 { ERROR_BROKEN_PIPE, EPIPE },
192 { ERROR_DISK_FULL, ENOSPC },
193 { ERROR_INVALID_TARGET_HANDLE, EBADF },
194 { ERROR_INVALID_HANDLE, EINVAL },
195 { ERROR_WAIT_NO_CHILDREN, ECHILD },
196 { ERROR_CHILD_NOT_COMPLETE, ECHILD },
197 { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
198 { ERROR_NEGATIVE_SEEK, EINVAL },
199 { ERROR_SEEK_ON_DEVICE, EACCES },
200 { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
201 { ERROR_DIRECTORY, ENOTDIR },
202 { ERROR_NOT_LOCKED, EACCES },
203 { ERROR_BAD_PATHNAME, ENOENT },
204 { ERROR_MAX_THRDS_REACHED, EAGAIN },
205 { ERROR_LOCK_FAILED, EACCES },
206 { ERROR_ALREADY_EXISTS, EEXIST },
207 { ERROR_INVALID_STARTING_CODESEG, ENOEXEC },
208 { ERROR_INVALID_STACKSEG, ENOEXEC },
209 { ERROR_INVALID_MODULETYPE, ENOEXEC },
210 { ERROR_INVALID_EXE_SIGNATURE, ENOEXEC },
211 { ERROR_EXE_MARKED_INVALID, ENOEXEC },
212 { ERROR_BAD_EXE_FORMAT, ENOEXEC },
213 { ERROR_ITERATED_DATA_EXCEEDS_64k,ENOEXEC },
214 { ERROR_INVALID_MINALLOCSIZE, ENOEXEC },
215 { ERROR_DYNLINK_FROM_INVALID_RING,ENOEXEC },
216 { ERROR_IOPL_NOT_ENABLED, ENOEXEC },
217 { ERROR_INVALID_SEGDPL, ENOEXEC },
218 { ERROR_AUTODATASEG_EXCEEDS_64k, ENOEXEC },
219 { ERROR_RING2SEG_MUST_BE_MOVABLE, ENOEXEC },
220 { ERROR_RELOC_CHAIN_XEEDS_SEGLIM, ENOEXEC },
221 { ERROR_INFLOOP_IN_RELOC_CHAIN, ENOEXEC },
222 { ERROR_FILENAME_EXCED_RANGE, ENOENT },
223 { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
224#ifndef ERROR_PIPE_LOCAL
225#define ERROR_PIPE_LOCAL 229L
226#endif
227 { ERROR_PIPE_LOCAL, EPIPE },
228 { ERROR_BAD_PIPE, EPIPE },
229 { ERROR_PIPE_BUSY, EAGAIN },
230 { ERROR_NO_DATA, EPIPE },
231 { ERROR_PIPE_NOT_CONNECTED, EPIPE },
232 { ERROR_OPERATION_ABORTED, EINTR },
233 { ERROR_NOT_ENOUGH_QUOTA, ENOMEM },
234 { ERROR_MOD_NOT_FOUND, ENOENT },
235 { ERROR_PRIVILEGE_NOT_HELD, EACCES, },
236 { ERROR_CANT_RESOLVE_FILENAME, ELOOP, },
237 { WSAEINTR, EINTR },
238 { WSAEBADF, EBADF },
239 { WSAEACCES, EACCES },
240 { WSAEFAULT, EFAULT },
241 { WSAEINVAL, EINVAL },
242 { WSAEMFILE, EMFILE },
243 { WSAEWOULDBLOCK, EWOULDBLOCK },
244 { WSAEINPROGRESS, EINPROGRESS },
245 { WSAEALREADY, EALREADY },
246 { WSAENOTSOCK, ENOTSOCK },
247 { WSAEDESTADDRREQ, EDESTADDRREQ },
248 { WSAEMSGSIZE, EMSGSIZE },
249 { WSAEPROTOTYPE, EPROTOTYPE },
250 { WSAENOPROTOOPT, ENOPROTOOPT },
251 { WSAEPROTONOSUPPORT, EPROTONOSUPPORT },
252 { WSAESOCKTNOSUPPORT, ESOCKTNOSUPPORT },
253 { WSAEOPNOTSUPP, EOPNOTSUPP },
254 { WSAEPFNOSUPPORT, EPFNOSUPPORT },
255 { WSAEAFNOSUPPORT, EAFNOSUPPORT },
256 { WSAEADDRINUSE, EADDRINUSE },
257 { WSAEADDRNOTAVAIL, EADDRNOTAVAIL },
258 { WSAENETDOWN, ENETDOWN },
259 { WSAENETUNREACH, ENETUNREACH },
260 { WSAENETRESET, ENETRESET },
261 { WSAECONNABORTED, ECONNABORTED },
262 { WSAECONNRESET, ECONNRESET },
263 { WSAENOBUFS, ENOBUFS },
264 { WSAEISCONN, EISCONN },
265 { WSAENOTCONN, ENOTCONN },
266 { WSAESHUTDOWN, ESHUTDOWN },
267 { WSAETOOMANYREFS, ETOOMANYREFS },
268 { WSAETIMEDOUT, ETIMEDOUT },
269 { WSAECONNREFUSED, ECONNREFUSED },
270 { WSAELOOP, ELOOP },
271 { WSAENAMETOOLONG, ENAMETOOLONG },
272 { WSAEHOSTDOWN, EHOSTDOWN },
273 { WSAEHOSTUNREACH, EHOSTUNREACH },
274 { WSAEPROCLIM, EPROCLIM },
275 { WSAENOTEMPTY, ENOTEMPTY },
276 { WSAEUSERS, EUSERS },
277 { WSAEDQUOT, EDQUOT },
278 { WSAESTALE, ESTALE },
279 { WSAEREMOTE, EREMOTE },
280};
281
282/* License: Ruby's */
283int
284rb_w32_map_errno(DWORD winerr)
285{
286 int i;
287
288 if (winerr == 0) {
289 return 0;
290 }
291
292 for (i = 0; i < (int)(sizeof(errmap) / sizeof(*errmap)); i++) {
293 if (errmap[i].winerr == winerr) {
294 return errmap[i].err;
295 }
296 }
297
298 if (winerr >= WSABASEERR) {
299 return winerr;
300 }
301 return EINVAL;
302}
303
304#define map_errno rb_w32_map_errno
305
306static const char *NTLoginName;
307
308static OSVERSIONINFO osver;
309
310/* License: Artistic or GPL */
311static void
312get_version(void)
313{
314 memset(&osver, 0, sizeof(OSVERSIONINFO));
315 osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
316 GetVersionEx(&osver);
317}
318
319#ifdef _M_IX86
320/* License: Artistic or GPL */
321DWORD
322rb_w32_osid(void)
323{
324 return osver.dwPlatformId;
325}
326#endif
327
328/* License: Artistic or GPL */
329DWORD
330rb_w32_osver(void)
331{
332 return osver.dwMajorVersion;
333}
334
335/* simulate flock by locking a range on the file */
336
337/* License: Artistic or GPL */
338#define LK_ERR(f,i) \
339 do { \
340 if (f) \
341 i = 0; \
342 else { \
343 DWORD err = GetLastError(); \
344 if (err == ERROR_LOCK_VIOLATION || err == ERROR_IO_PENDING) \
345 errno = EWOULDBLOCK; \
346 else if (err == ERROR_NOT_LOCKED) \
347 i = 0; \
348 else \
349 errno = map_errno(err); \
350 } \
351 } while (0)
352#define LK_LEN ULONG_MAX
353
354/* License: Artistic or GPL */
355static uintptr_t
356flock_winnt(uintptr_t self, int argc, uintptr_t* argv)
357{
358 OVERLAPPED o;
359 int i = -1;
360 const HANDLE fh = (HANDLE)self;
361 const int oper = argc;
362
363 memset(&o, 0, sizeof(o));
364
365 switch (oper) {
366 case LOCK_SH: /* shared lock */
367 LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
368 break;
369 case LOCK_EX: /* exclusive lock */
370 LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
371 break;
372 case LOCK_SH|LOCK_NB: /* non-blocking shared lock */
373 LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
374 break;
375 case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */
376 LK_ERR(LockFileEx(fh,
377 LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
378 0, LK_LEN, LK_LEN, &o), i);
379 break;
380 case LOCK_UN: /* unlock lock */
381 case LOCK_UN|LOCK_NB: /* unlock is always non-blocking, I hope */
382 LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
383 break;
384 default: /* unknown */
385 errno = EINVAL;
386 break;
387 }
388 return i;
389}
390
391#undef LK_ERR
392
393/* License: Artistic or GPL */
394int
395flock(int fd, int oper)
396{
397 const asynchronous_func_t locker = flock_winnt;
398
399 return rb_w32_asynchronize(locker,
400 (VALUE)_get_osfhandle(fd), oper, NULL,
401 (DWORD)-1);
402}
403
404/* License: Ruby's */
405static inline WCHAR *
406translate_wchar(WCHAR *p, int from, int to)
407{
408 for (; *p; p++) {
409 if (*p == from)
410 *p = to;
411 }
412 return p;
413}
414
415/* License: Ruby's */
416static inline char *
417translate_char(char *p, int from, int to, UINT cp)
418{
419 while (*p) {
420 if ((unsigned char)*p == from)
421 *p = to;
422 p = CharNextExA(cp, p, 0);
423 }
424 return p;
425}
426
427#ifndef CSIDL_LOCAL_APPDATA
428#define CSIDL_LOCAL_APPDATA 28
429#endif
430#ifndef CSIDL_COMMON_APPDATA
431#define CSIDL_COMMON_APPDATA 35
432#endif
433#ifndef CSIDL_WINDOWS
434#define CSIDL_WINDOWS 36
435#endif
436#ifndef CSIDL_SYSTEM
437#define CSIDL_SYSTEM 37
438#endif
439#ifndef CSIDL_PROFILE
440#define CSIDL_PROFILE 40
441#endif
442
443/* License: Ruby's */
444static BOOL
445get_special_folder(int n, WCHAR *buf, size_t len)
446{
447 LPITEMIDLIST pidl;
448 LPMALLOC alloc;
449 BOOL f = FALSE;
450
451 if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
452 f = SHGetPathFromIDListEx(pidl, buf, len, 0);
453 SHGetMalloc(&alloc);
454 alloc->lpVtbl->Free(alloc, pidl);
455 alloc->lpVtbl->Release(alloc);
456 }
457 return f;
458}
459
460/* License: Ruby's */
461static void
462regulate_path(WCHAR *path)
463{
464 WCHAR *p = translate_wchar(path, L'\\', L'/');
465 if (p - path == 2 && path[1] == L':') {
466 *p++ = L'/';
467 *p = L'\0';
468 }
469}
470
471/* License: Ruby's */
472static FARPROC
473get_proc_address(const char *module, const char *func, HANDLE *mh)
474{
475 HANDLE h;
476 FARPROC ptr;
477
478 if (mh)
479 h = LoadLibrary(module);
480 else
481 h = GetModuleHandle(module);
482 if (!h)
483 return NULL;
484
485 ptr = GetProcAddress(h, func);
486 if (mh) {
487 if (ptr)
488 *mh = h;
489 else
490 FreeLibrary(h);
491 }
492 return ptr;
493}
494
495/* License: Ruby's */
496VALUE
497rb_w32_special_folder(int type)
498{
499 WCHAR path[PATH_MAX];
500
501 if (!get_special_folder(type, path, numberof(path))) return Qnil;
502 regulate_path(path);
503 return rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
504}
505
506#if defined _MSC_VER && _MSC_VER <= 1200
507/* License: Ruby's */
508#define GetSystemWindowsDirectoryW GetWindowsDirectoryW
509#endif
510
511/* License: Ruby's */
512UINT
513rb_w32_system_tmpdir(WCHAR *path, UINT len)
514{
515 static const WCHAR temp[] = L"temp";
516 WCHAR *p;
517
518 if (!get_special_folder(CSIDL_LOCAL_APPDATA, path, len)) {
519 if (GetSystemWindowsDirectoryW(path, len)) return 0;
520 }
521 p = translate_wchar(path, L'\\', L'/');
522 if (*(p - 1) != L'/') *p++ = L'/';
523 if ((UINT)(p - path + numberof(temp)) >= len) return 0;
524 memcpy(p, temp, sizeof(temp));
525 return (UINT)(p - path + numberof(temp) - 1);
526}
527
528/*
529 Return user's home directory using environment variables combinations.
530 Memory allocated by this function should be manually freed
531 afterwards with xfree.
532
533 Try:
534 HOME, USERPROFILE, HOMEDRIVE + HOMEPATH environment variables
535 Special Folders - Profile and Personal
536*/
537WCHAR *
538rb_w32_home_dir(void)
539{
540 WCHAR *buffer = NULL;
541 size_t buffer_len = MAX_PATH, len = 0;
542 enum {
543 HOME_NONE, ENV_HOME, ENV_USERPROFILE, ENV_DRIVEPATH
544 } home_type = HOME_NONE;
545
546 if ((len = GetEnvironmentVariableW(L"HOME", NULL, 0)) != 0) {
547 buffer_len = len;
548 home_type = ENV_HOME;
549 }
550 else if ((len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) != 0) {
551 buffer_len = len;
552 home_type = ENV_USERPROFILE;
553 }
554 else if ((len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) != 0) {
555 buffer_len = len;
556 if ((len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) != 0) {
557 buffer_len += len;
558 home_type = ENV_DRIVEPATH;
559 }
560 }
561
562 /* can't use xmalloc here, since it's called too early from init_env() */
563 buffer = malloc(sizeof(WCHAR) * buffer_len);
564 if (buffer == NULL) return NULL;
565
566 switch (home_type) {
567 case ENV_HOME:
568 GetEnvironmentVariableW(L"HOME", buffer, buffer_len);
569 break;
570 case ENV_USERPROFILE:
571 GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len);
572 break;
573 case ENV_DRIVEPATH:
574 len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len);
575 GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len);
576 break;
577 default:
578 if (!get_special_folder(CSIDL_PROFILE, buffer, buffer_len) &&
579 !get_special_folder(CSIDL_PERSONAL, buffer, buffer_len)) {
580 free(buffer);
581 return NULL;
582 }
583 buffer = realloc(buffer, sizeof(WCHAR) * (lstrlenW(buffer) + 1));
584 break;
585 }
586
587 /* sanitize backslashes with forwardslashes */
588 regulate_path(buffer);
589
590 return buffer;
591}
592
593/* License: Ruby's */
594static void
595init_env(void)
596{
597 WCHAR env[ENV_MAX];
598
599 if (!GetEnvironmentVariableW(L"HOME", env, numberof(env))) {
600 WCHAR *whome = rb_w32_home_dir();
601 if (whome) {
602 _wputenv_s(L"HOME", whome);
603 free(whome);
604 }
605 }
606
607 if (!GetEnvironmentVariableW(L"USER", env, numberof(env))) {
608 DWORD len;
609 if (!GetEnvironmentVariableW(L"USERNAME", env, numberof(env)) &&
610 !GetUserNameW(env, (len = numberof(env), &len))) {
611 NTLoginName = "<Unknown>";
612 }
613 else {
614 _wputenv_s(L"USER", env);
615 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
616 }
617 }
618 else {
619 NTLoginName = rb_w32_wstr_to_mbstr(CP_UTF8, env, -1, NULL);
620 }
621
622 if (!GetEnvironmentVariableW(L"TMPDIR", env, numberof(env)) &&
623 !GetEnvironmentVariableW(L"TMP", env, numberof(env)) &&
624 !GetEnvironmentVariableW(L"TEMP", env, numberof(env)) &&
625 rb_w32_system_tmpdir(env, numberof(env))) {
626 _wputenv_s(L"TMPDIR", env);
627 }
628}
629
630static void init_stdhandle(void);
631
632#if RUBY_MSVCRT_VERSION >= 80
633/* License: Ruby's */
634static void
635invalid_parameter(const wchar_t *expr, const wchar_t *func, const wchar_t *file, unsigned int line, uintptr_t dummy)
636{
637 // nothing to do
638}
639
640int ruby_w32_rtc_error;
641
642# ifndef __MINGW32__
643/* License: Ruby's */
645RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6)
646static int __cdecl
647rtc_error_handler(int e, const char *src, int line, const char *exe, const char *fmt, ...)
648{
649 va_list ap;
650 VALUE str;
651
652 if (!ruby_w32_rtc_error) return 0;
653 str = rb_sprintf("%s:%d: ", src, line);
654 va_start(ap, fmt);
655 rb_str_vcatf(str, fmt, ap);
656 va_end(ap);
657 rb_str_cat(str, "\n", 1);
658 rb_write_error2(RSTRING_PTR(str), RSTRING_LEN(str));
659 return 0;
660}
661# endif
662#endif
663
664static CRITICAL_SECTION select_mutex;
665
666static CRITICAL_SECTION socklist_mutex;
667static st_table *socklist = NULL;
668
669static CRITICAL_SECTION conlist_mutex;
670static st_table *conlist = NULL;
671#define conlist_disabled ((st_table *)-1)
672
673#define thread_exclusive(obj) \
674 for (bool exclusive_for_##obj = (EnterCriticalSection(&obj##_mutex), true); \
675 exclusive_for_##obj; \
676 exclusive_for_##obj = (LeaveCriticalSection(&obj##_mutex), false))
677
678static CRITICAL_SECTION uenvarea_mutex;
679static char *uenvarea;
680
681/* License: Ruby's */
682struct constat {
683 struct {
684 int state, seq[16], reverse;
685 WORD attr;
686 COORD saved;
687 } vt100;
688};
689enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
690
691/* License: Ruby's */
692static int
693free_conlist(st_data_t key, st_data_t val, st_data_t arg)
694{
695 xfree((struct constat *)val);
696 return ST_DELETE;
697}
698
699/* License: Ruby's */
700static void
701constat_delete(HANDLE h)
702{
703 thread_exclusive(conlist) {
704 if (conlist && conlist != conlist_disabled) {
705 st_data_t key = (st_data_t)h, val;
706 st_delete(conlist, &key, &val);
707 xfree((struct constat *)val);
708 }
709 }
710}
711
712/* License: Ruby's */
713static void
714exit_handler(void)
715{
716 WSACleanup();
717 DeleteCriticalSection(&select_mutex);
718 DeleteCriticalSection(&socklist_mutex);
719 DeleteCriticalSection(&conlist_mutex);
720 thread_exclusive(uenvarea) {
721 if (uenvarea) {
722 free(uenvarea);
723 uenvarea = NULL;
724 }
725 }
726 DeleteCriticalSection(&uenvarea_mutex);
727}
728
729/* License: Ruby's */
730static void
731vm_exit_handler(ruby_vm_t *vm)
732{
733 EnterCriticalSection(&socklist_mutex);
734 if (socklist) {
735 st_free_table(socklist);
736 socklist = NULL;
737 }
738 LeaveCriticalSection(&socklist_mutex);
739
740 EnterCriticalSection(&conlist_mutex);
741 if (conlist && conlist != conlist_disabled) {
742 st_foreach(conlist, free_conlist, 0);
743 st_free_table(conlist);
744 conlist = NULL;
745 }
746 LeaveCriticalSection(&conlist_mutex);
747}
748
749#define ATOMIC_LONG_CAS(var, oldval, newval) InterlockedCompareExchange(&(var), (newval), (oldval))
750
751/* License: Ruby's */
752static void
753install_vm_exit_handler(void)
754{
755 static LONG installed = 0;
756 LONG i;
757
758 while ((i = ATOMIC_LONG_CAS(installed, 0, -1)) != 1) {
759 if (i != 0) {
760 Sleep(1);
761 continue;
762 }
763 ruby_vm_at_exit(vm_exit_handler);
764 ATOMIC_LONG_CAS(installed, -1, 1);
765 break;
766 }
767}
768
769/* License: Artistic or GPL */
770static void
771StartSockets(void)
772{
773 WORD version;
774 WSADATA retdata;
775
776 //
777 // initialize the winsock interface and insure that it's
778 // cleaned up at exit.
779 //
780 version = MAKEWORD(2, 0);
781 if (WSAStartup(version, &retdata))
782 rb_fatal("Unable to locate winsock library!");
783 if (LOBYTE(retdata.wVersion) != 2)
784 rb_fatal("could not find version 2 of winsock dll");
785
786 InitializeCriticalSection(&select_mutex);
787 InitializeCriticalSection(&socklist_mutex);
788 InitializeCriticalSection(&conlist_mutex);
789
790 atexit(exit_handler);
791}
792
793#define MAKE_SOCKDATA(af, fl) ((int)((((int)af)<<4)|((fl)&0xFFFF)))
794#define GET_FAMILY(v) ((int)(((v)>>4)&0xFFFF))
795#define GET_FLAGS(v) ((int)((v)&0xFFFF))
796
797/* License: Ruby's */
798static inline int
799socklist_insert(SOCKET sock, int flag)
800{
801 int ret;
802
803 thread_exclusive(socklist) {
804 if (!socklist) {
805 socklist = st_init_numtable();
806 install_vm_exit_handler();
807 }
808 ret = st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
809 }
810
811 return ret;
812}
813
814/* License: Ruby's */
815static inline int
816socklist_lookup(SOCKET sock, int *flagp)
817{
818 st_data_t data;
819 int ret = 0;
820
821 thread_exclusive(socklist) {
822 if (!socklist) continue;
823 ret = st_lookup(socklist, (st_data_t)sock, &data);
824 if (ret && flagp)
825 *flagp = (int)data;
826 }
827
828 return ret;
829}
830
831/* License: Ruby's */
832static inline int
833socklist_delete(SOCKET *sockp, int *flagp)
834{
835 st_data_t key;
836 st_data_t data;
837 int ret = 0;
838
839 thread_exclusive(socklist) {
840 if (!socklist) continue;
841 key = (st_data_t)*sockp;
842 if (flagp)
843 data = (st_data_t)*flagp;
844 ret = st_delete(socklist, &key, &data);
845 if (ret) {
846 *sockp = (SOCKET)key;
847 if (flagp)
848 *flagp = (int)data;
849 }
850 }
851
852 return ret;
853}
854
855#if RUBY_MSVCRT_VERSION >= 80
856# ifdef __MINGW32__
857# define _CrtSetReportMode(type,mode) ((void)0)
858# define _RTC_SetErrorFunc(func) ((void)0)
859# endif
860static void set_pioinfo_extra(void);
861#endif
862static int w32_cmdvector(const WCHAR *, char ***, UINT, rb_encoding *);
863//
864// Initialization stuff
865//
866/* License: Ruby's */
867void
868rb_w32_sysinit(int *argc, char ***argv)
869{
870#if RUBY_MSVCRT_VERSION >= 80
871
872 _CrtSetReportMode(_CRT_ASSERT, 0);
873 _set_invalid_parameter_handler(invalid_parameter);
874 _RTC_SetErrorFunc(rtc_error_handler);
875 set_pioinfo_extra();
876#endif
877 SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
878
879 get_version();
880
881 //
882 // subvert cmd.exe's feeble attempt at command line parsing
883 //
884 *argc = w32_cmdvector(GetCommandLineW(), argv, CP_UTF8, &OnigEncodingUTF_8);
885
886 //
887 // Now set up the correct time stuff
888 //
889
890 tzset();
891
892 InitializeCriticalSection(&uenvarea_mutex);
893 init_env();
894
895 init_stdhandle();
896
897 // Initialize Winsock
898 StartSockets();
899}
900
901char *
902getlogin(void)
903{
904 return (char *)NTLoginName;
905}
906
907#define MAXCHILDNUM 256 /* max num of child processes */
908
909/* License: Ruby's */
910static struct ChildRecord {
911 HANDLE hProcess; /* process handle */
912 rb_pid_t pid; /* process id */
913} ChildRecord[MAXCHILDNUM];
914
915/* License: Ruby's */
916#define FOREACH_CHILD(v) do { \
917 struct ChildRecord* v; \
918 for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
919#define END_FOREACH_CHILD } while (0)
920
921/* License: Ruby's */
922static struct ChildRecord *
923FindChildSlot(rb_pid_t pid)
924{
925
926 FOREACH_CHILD(child) {
927 if (child->pid == pid) {
928 return child;
929 }
930 } END_FOREACH_CHILD;
931 return NULL;
932}
933
934/* License: Ruby's */
935static struct ChildRecord *
936FindChildSlotByHandle(HANDLE h)
937{
938
939 FOREACH_CHILD(child) {
940 if (child->hProcess == h) {
941 return child;
942 }
943 } END_FOREACH_CHILD;
944 return NULL;
945}
946
947/* License: Ruby's */
948static void
949CloseChildHandle(struct ChildRecord *child)
950{
951 HANDLE h = child->hProcess;
952 child->hProcess = NULL;
953 child->pid = 0;
954 CloseHandle(h);
955}
956
957/* License: Ruby's */
958static struct ChildRecord *
959FindFreeChildSlot(void)
960{
961 FOREACH_CHILD(child) {
962 if (!child->pid) {
963 child->pid = -1; /* lock the slot */
964 child->hProcess = NULL;
965 return child;
966 }
967 } END_FOREACH_CHILD;
968 return NULL;
969}
970
971
972/*
973 ruby -lne 'BEGIN{$cmds = Hash.new(0); $mask = 1}'
974 -e '$cmds[$_.downcase] |= $mask' -e '$mask <<= 1 if ARGF.eof'
975 -e 'END{$cmds.sort.each{|n,f|puts " \"\\#{f.to_s(8)}\" #{n.dump} + 1,"}}'
976 98cmd ntcmd
977 */
978#define InternalCmdsMax 8
979static const char szInternalCmds[][InternalCmdsMax+2] = {
980 "\2" "assoc",
981 "\3" "break",
982 "\3" "call",
983 "\3" "cd",
984 "\1" "chcp",
985 "\3" "chdir",
986 "\3" "cls",
987 "\2" "color",
988 "\3" "copy",
989 "\1" "ctty",
990 "\3" "date",
991 "\3" "del",
992 "\3" "dir",
993 "\3" "echo",
994 "\2" "endlocal",
995 "\3" "erase",
996 "\3" "exit",
997 "\3" "for",
998 "\2" "ftype",
999 "\3" "goto",
1000 "\3" "if",
1001 "\1" "lfnfor",
1002 "\1" "lh",
1003 "\1" "lock",
1004 "\3" "md",
1005 "\3" "mkdir",
1006 "\2" "move",
1007 "\3" "path",
1008 "\3" "pause",
1009 "\2" "popd",
1010 "\3" "prompt",
1011 "\2" "pushd",
1012 "\3" "rd",
1013 "\3" "rem",
1014 "\3" "ren",
1015 "\3" "rename",
1016 "\3" "rmdir",
1017 "\3" "set",
1018 "\2" "setlocal",
1019 "\3" "shift",
1020 "\2" "start",
1021 "\3" "time",
1022 "\2" "title",
1023 "\1" "truename",
1024 "\3" "type",
1025 "\1" "unlock",
1026 "\3" "ver",
1027 "\3" "verify",
1028 "\3" "vol",
1029};
1030
1031/* License: Ruby's */
1032static int
1033internal_match(const void *key, const void *elem)
1034{
1035 return strncmp(key, ((const char *)elem) + 1, InternalCmdsMax);
1036}
1037
1038/* License: Ruby's */
1039static int
1040is_command_com(const char *interp)
1041{
1042 int i = strlen(interp) - 11;
1043
1044 if ((i == 0 || (i > 0 && isdirsep(interp[i-1]))) &&
1045 strcasecmp(interp+i, "command.com") == 0) {
1046 return 1;
1047 }
1048 return 0;
1049}
1050
1051static int internal_cmd_match(const char *cmdname, int nt);
1052
1053/* License: Ruby's */
1054static int
1055is_internal_cmd(const char *cmd, int nt)
1056{
1057 char cmdname[9], *b = cmdname, c;
1058
1059 do {
1060 if (!(c = *cmd++)) return 0;
1061 } while (isspace(c));
1062 if (c == '@')
1063 return 1;
1064 while (isalpha(c)) {
1065 *b++ = tolower(c);
1066 if (b == cmdname + sizeof(cmdname)) return 0;
1067 c = *cmd++;
1068 }
1069 if (c == '.') c = *cmd;
1070 switch (c) {
1071 case '<': case '>': case '|':
1072 return 1;
1073 case '\0': case ' ': case '\t': case '\n':
1074 break;
1075 default:
1076 return 0;
1077 }
1078 *b = 0;
1079 return internal_cmd_match(cmdname, nt);
1080}
1081
1082/* License: Ruby's */
1083static int
1084internal_cmd_match(const char *cmdname, int nt)
1085{
1086 char *nm;
1087
1088 nm = bsearch(cmdname, szInternalCmds,
1089 sizeof(szInternalCmds) / sizeof(*szInternalCmds),
1090 sizeof(*szInternalCmds),
1091 internal_match);
1092 if (!nm || !(nm[0] & (nt ? 2 : 1)))
1093 return 0;
1094 return 1;
1095}
1096
1097/* License: Ruby's */
1098SOCKET
1099rb_w32_get_osfhandle(int fh)
1100{
1101 return _get_osfhandle(fh);
1102}
1103
1104/* License: Ruby's */
1105static int
1106join_argv(char *cmd, char *const *argv, BOOL escape, UINT cp, int backslash)
1107{
1108 const char *p, *s;
1109 char *q, *const *t;
1110 int len, n, bs, quote;
1111
1112 for (t = argv, q = cmd, len = 0; (p = *t) != 0; t++) {
1113 quote = 0;
1114 s = p;
1115 if (!*p || strpbrk(p, " \t\"'")) {
1116 quote = 1;
1117 len++;
1118 if (q) *q++ = '"';
1119 }
1120 for (bs = 0; *p; ++p) {
1121 switch (*p) {
1122 case '\\':
1123 ++bs;
1124 break;
1125 case '"':
1126 len += n = p - s;
1127 if (q) {
1128 memcpy(q, s, n);
1129 q += n;
1130 }
1131 s = p;
1132 len += ++bs;
1133 if (q) {
1134 memset(q, '\\', bs);
1135 q += bs;
1136 }
1137 bs = 0;
1138 break;
1139 case '<': case '>': case '|': case '^':
1140 if (escape && !quote) {
1141 len += (n = p - s) + 1;
1142 if (q) {
1143 memcpy(q, s, n);
1144 q += n;
1145 *q++ = '^';
1146 }
1147 s = p;
1148 break;
1149 }
1150 default:
1151 bs = 0;
1152 p = CharNextExA(cp, p, 0) - 1;
1153 break;
1154 }
1155 }
1156 len += (n = p - s) + 1;
1157 if (quote) len++;
1158 if (q) {
1159 memcpy(q, s, n);
1160 if (backslash > 0) {
1161 --backslash;
1162 q[n] = 0;
1163 translate_char(q, '/', '\\', cp);
1164 }
1165 q += n;
1166 if (quote) *q++ = '"';
1167 *q++ = ' ';
1168 }
1169 }
1170 if (q > cmd) --len;
1171 if (q) {
1172 if (q > cmd) --q;
1173 *q = '\0';
1174 }
1175 return len;
1176}
1177
1178/* License: Ruby's */
1179#define STRNDUPV(ptr, v, src, len) \
1180 (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
1181
1182/* License: Ruby's */
1183static int
1184check_spawn_mode(int mode)
1185{
1186 switch (mode) {
1187 case P_NOWAIT:
1188 case P_OVERLAY:
1189 return 0;
1190 default:
1191 errno = EINVAL;
1192 return -1;
1193 }
1194}
1195
1196/* License: Ruby's */
1197static rb_pid_t
1198child_result(struct ChildRecord *child, int mode)
1199{
1200 DWORD exitcode;
1201
1202 if (!child) {
1203 return -1;
1204 }
1205
1206 if (mode == P_OVERLAY) {
1207 WaitForSingleObject(child->hProcess, INFINITE);
1208 GetExitCodeProcess(child->hProcess, &exitcode);
1209 CloseChildHandle(child);
1210 _exit(exitcode);
1211 }
1212 return child->pid;
1213}
1214
1215/* License: Ruby's */
1216static int
1217CreateChild(struct ChildRecord *child, const WCHAR *cmd, const WCHAR *prog, HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
1218{
1219 BOOL fRet;
1220 STARTUPINFOW aStartupInfo;
1221 PROCESS_INFORMATION aProcessInformation;
1222 SECURITY_ATTRIBUTES sa;
1223
1224 if (!cmd && !prog) {
1225 errno = EFAULT;
1226 return FALSE;
1227 }
1228
1229 if (!child) {
1230 errno = EAGAIN;
1231 return FALSE;
1232 }
1233
1234 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
1235 sa.lpSecurityDescriptor = NULL;
1236 sa.bInheritHandle = TRUE;
1237
1238 memset(&aStartupInfo, 0, sizeof(aStartupInfo));
1239 memset(&aProcessInformation, 0, sizeof(aProcessInformation));
1240 aStartupInfo.cb = sizeof(aStartupInfo);
1241 aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
1242 if (hInput) {
1243 aStartupInfo.hStdInput = hInput;
1244 }
1245 else {
1246 aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1247 }
1248 if (hOutput) {
1249 aStartupInfo.hStdOutput = hOutput;
1250 }
1251 else {
1252 aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1253 }
1254 if (hError) {
1255 aStartupInfo.hStdError = hError;
1256 }
1257 else {
1258 aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1259 }
1260
1261 dwCreationFlags |= NORMAL_PRIORITY_CLASS;
1262
1263 if (lstrlenW(cmd) > 32767) {
1264 child->pid = 0; /* release the slot */
1265 errno = E2BIG;
1266 return FALSE;
1267 }
1268
1269 RUBY_CRITICAL {
1270 fRet = CreateProcessW(prog, (WCHAR *)cmd, &sa, &sa,
1271 sa.bInheritHandle, dwCreationFlags, NULL, NULL,
1272 &aStartupInfo, &aProcessInformation);
1273 errno = map_errno(GetLastError());
1274 }
1275
1276 if (!fRet) {
1277 child->pid = 0; /* release the slot */
1278 return FALSE;
1279 }
1280
1281 CloseHandle(aProcessInformation.hThread);
1282
1283 child->hProcess = aProcessInformation.hProcess;
1284 child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
1285
1286 return TRUE;
1287}
1288
1289/* License: Ruby's */
1290static int
1291is_batch(const char *cmd)
1292{
1293 int len = strlen(cmd);
1294 if (len <= 4) return 0;
1295 cmd += len - 4;
1296 if (*cmd++ != '.') return 0;
1297 if (strcasecmp(cmd, "bat") == 0) return 1;
1298 if (strcasecmp(cmd, "cmd") == 0) return 1;
1299 return 0;
1300}
1301
1302#define filecp rb_w32_filecp
1303#define mbstr_to_wstr rb_w32_mbstr_to_wstr
1304#define wstr_to_mbstr rb_w32_wstr_to_mbstr
1305#define acp_to_wstr(str, plen) mbstr_to_wstr(CP_ACP, str, -1, plen)
1306#define wstr_to_acp(str, plen) wstr_to_mbstr(CP_ACP, str, -1, plen)
1307#define filecp_to_wstr(str, plen) mbstr_to_wstr(filecp(), str, -1, plen)
1308#define wstr_to_filecp(str, plen) wstr_to_mbstr(filecp(), str, -1, plen)
1309#define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
1310#define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
1311
1312/* License: Artistic or GPL */
1313static rb_pid_t
1314w32_spawn(int mode, const char *cmd, const char *prog, UINT cp)
1315{
1316 char fbuf[PATH_MAX];
1317 char *p = NULL;
1318 const char *shell = NULL;
1319 WCHAR *wcmd = NULL, *wshell = NULL;
1320 int e = 0;
1321 rb_pid_t ret = -1;
1322 VALUE v = 0;
1323 VALUE v2 = 0;
1324 int sep = 0;
1325 char *cmd_sep = NULL;
1326
1327 if (check_spawn_mode(mode)) return -1;
1328
1329 if (prog) {
1330 if (!(p = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1331 shell = prog;
1332 }
1333 else {
1334 shell = p;
1335 translate_char(p, '/', '\\', cp);
1336 }
1337 }
1338 else {
1339 int redir = -1;
1340 int nt;
1341 while (ISSPACE(*cmd)) cmd++;
1342 if ((shell = w32_getenv("RUBYSHELL", cp)) && (redir = has_redirection(cmd, cp))) {
1343 size_t shell_len = strlen(shell);
1344 size_t cmd_len = strlen(cmd) + sizeof(" -c ") + 2;
1345 char *tmp = ALLOCV(v, shell_len + cmd_len);
1346 memcpy(tmp, shell, shell_len + 1);
1347 translate_char(tmp, '/', '\\', cp);
1348 snprintf(tmp + shell_len, cmd_len, " -c \"%s\"", cmd);
1349 cmd = tmp;
1350 }
1351 else if ((shell = w32_getenv("COMSPEC", cp)) &&
1352 (nt = !is_command_com(shell),
1353 (redir < 0 ? has_redirection(cmd, cp) : redir) ||
1354 is_internal_cmd(cmd, nt))) {
1355 size_t cmd_len = strlen(shell) + strlen(cmd) + sizeof(" /c ") + (nt ? 2 : 0);
1356 char *tmp = ALLOCV(v, cmd_len);
1357 snprintf(tmp, cmd_len, nt ? "%s /c \"%s\"" : "%s /c %s", shell, cmd);
1358 cmd = tmp;
1359 }
1360 else {
1361 int len = 0, quote = (*cmd == '"') ? '"' : (*cmd == '\'') ? '\'' : 0;
1362 int slash = 0;
1363 for (prog = cmd + !!quote;; prog = CharNextExA(cp, prog, 0)) {
1364 if (*prog == '/') slash = 1;
1365 if (!*prog) {
1366 len = prog - cmd;
1367 if (slash) {
1368 STRNDUPV(p, v2, cmd, len);
1369 cmd = p;
1370 }
1371 shell = cmd;
1372 break;
1373 }
1374 if ((unsigned char)*prog == quote) {
1375 len = prog++ - cmd - 1;
1376 STRNDUPV(p, v2, cmd + 1, len);
1377 shell = p;
1378 break;
1379 }
1380 if (quote) continue;
1381 if (ISSPACE(*prog) || strchr("<>|*?\"", *prog)) {
1382 len = prog - cmd;
1383 STRNDUPV(p, v2, cmd, len + (slash ? strlen(prog) : 0));
1384 if (slash) {
1385 cmd = p;
1386 sep = *(cmd_sep = &p[len]);
1387 *cmd_sep = '\0';
1388 }
1389 shell = p;
1390 break;
1391 }
1392 }
1393 shell = dln_find_exe_r(shell, NULL, fbuf, sizeof(fbuf));
1394 if (p && slash) translate_char(p, '/', '\\', cp);
1395 if (!shell) {
1396 shell = p ? p : cmd;
1397 }
1398 else {
1399 len = strlen(shell);
1400 if (strchr(shell, ' ')) quote = -1;
1401 if (shell == fbuf) {
1402 p = fbuf;
1403 }
1404 else if (shell != p && strchr(shell, '/')) {
1405 STRNDUPV(p, v2, shell, len);
1406 shell = p;
1407 }
1408 if (p) translate_char(p, '/', '\\', cp);
1409 if (is_batch(shell)) {
1410 int alen = strlen(prog);
1411 cmd = p = ALLOCV(v, len + alen + (quote ? 2 : 0) + 1);
1412 if (quote) *p++ = '"';
1413 memcpy(p, shell, len);
1414 p += len;
1415 if (quote) *p++ = '"';
1416 memcpy(p, prog, alen + 1);
1417 shell = 0;
1418 }
1419 }
1420 }
1421 }
1422
1423 if (!e && shell && !(wshell = mbstr_to_wstr(cp, shell, -1, NULL))) e = E2BIG;
1424 if (cmd_sep) *cmd_sep = sep;
1425 if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1426 if (v2) ALLOCV_END(v2);
1427 if (v) ALLOCV_END(v);
1428
1429 if (!e) {
1430 struct ChildRecord *child = FindFreeChildSlot();
1431 if (CreateChild(child, wcmd, wshell, NULL, NULL, NULL, 0)) {
1432 ret = child_result(child, mode);
1433 }
1434 }
1435 free(wshell);
1436 free(wcmd);
1437 if (e) errno = e;
1438 return ret;
1439}
1440
1441/* License: Ruby's */
1442rb_pid_t
1443rb_w32_spawn(int mode, const char *cmd, const char *prog)
1444{
1445 /* assume ACP */
1446 return w32_spawn(mode, cmd, prog, filecp());
1447}
1448
1449/* License: Ruby's */
1450rb_pid_t
1451rb_w32_uspawn(int mode, const char *cmd, const char *prog)
1452{
1453 return w32_spawn(mode, cmd, prog, CP_UTF8);
1454}
1455
1456/* License: Artistic or GPL */
1457static rb_pid_t
1458w32_spawn_process(int mode, const char *prog, char *const *argv,
1459 int in_fd, int out_fd, int err_fd, DWORD flags, UINT cp)
1460{
1461 int c_switch = 0;
1462 size_t len;
1463 BOOL ntcmd = FALSE, tmpnt;
1464 const char *shell;
1465 char *cmd, fbuf[PATH_MAX];
1466 WCHAR *wcmd = NULL, *wprog = NULL;
1467 int e = 0;
1468 rb_pid_t ret = -1;
1469 VALUE v = 0;
1470 HANDLE in_handle = NULL, out_handle = NULL, err_handle = NULL;
1471
1472 if (check_spawn_mode(mode)) return -1;
1473
1474 if (in_fd >= 0) {
1475 in_handle = (HANDLE)rb_w32_get_osfhandle(in_fd);
1476 }
1477 if (out_fd >= 0) {
1478 out_handle = (HANDLE)rb_w32_get_osfhandle(out_fd);
1479 }
1480 if (err_fd >= 0) {
1481 err_handle = (HANDLE)rb_w32_get_osfhandle(err_fd);
1482 }
1483
1484 if (!prog) prog = argv[0];
1485 if ((shell = w32_getenv("COMSPEC", cp)) &&
1486 internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
1487 ntcmd = tmpnt;
1488 prog = shell;
1489 c_switch = 1;
1490 }
1491 else if ((cmd = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1492 if (cmd == prog) strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1493 translate_char(cmd, '/', '\\', cp);
1494 prog = cmd;
1495 }
1496 else if (strchr(prog, '/')) {
1497 len = strlen(prog);
1498 if (len < sizeof(fbuf))
1499 strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1500 else
1501 STRNDUPV(cmd, v, prog, len);
1502 translate_char(cmd, '/', '\\', cp);
1503 prog = cmd;
1504 }
1505 if (c_switch || is_batch(prog)) {
1506 char *progs[2];
1507 progs[0] = (char *)prog;
1508 progs[1] = NULL;
1509 len = join_argv(NULL, progs, ntcmd, cp, 1);
1510 if (c_switch) len += 3;
1511 else ++argv;
1512 if (argv[0]) len += join_argv(NULL, argv, ntcmd, cp, 0);
1513 cmd = ALLOCV(v, len);
1514 join_argv(cmd, progs, ntcmd, cp, 1);
1515 if (c_switch) strlcat(cmd, " /c", len);
1516 if (argv[0]) join_argv(cmd + strlcat(cmd, " ", len), argv, ntcmd, cp, 0);
1517 prog = c_switch ? shell : 0;
1518 }
1519 else {
1520 len = join_argv(NULL, argv, FALSE, cp, 1);
1521 cmd = ALLOCV(v, len);
1522 join_argv(cmd, argv, FALSE, cp, 1);
1523 }
1524
1525 if (!e && cmd && !(wcmd = mbstr_to_wstr(cp, cmd, -1, NULL))) e = E2BIG;
1526 if (v) ALLOCV_END(v);
1527 if (!e && prog && !(wprog = mbstr_to_wstr(cp, prog, -1, NULL))) e = E2BIG;
1528
1529 if (!e) {
1530 struct ChildRecord *child = FindFreeChildSlot();
1531 if (CreateChild(child, wcmd, wprog, in_handle, out_handle, err_handle, flags)) {
1532 ret = child_result(child, mode);
1533 }
1534 }
1535 free(wprog);
1536 free(wcmd);
1537 if (e) errno = e;
1538 return ret;
1539}
1540
1541/* License: Ruby's */
1542rb_pid_t
1543rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1544{
1545 /* assume ACP */
1546 return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, filecp());
1547}
1548
1549/* License: Ruby's */
1550rb_pid_t
1551rb_w32_uaspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1552{
1553 return w32_spawn_process(mode, prog, argv, -1, -1, -1, flags, CP_UTF8);
1554}
1555
1556/* License: Ruby's */
1557rb_pid_t
1558rb_w32_aspawn(int mode, const char *prog, char *const *argv)
1559{
1560 return w32_spawn_process(mode, prog, argv, -1, -1, -1, 0, filecp());
1561}
1562
1563/* License: Ruby's */
1564rb_pid_t
1565rb_w32_uaspawn(int mode, const char *prog, char *const *argv)
1566{
1567 return rb_w32_uaspawn_flags(mode, prog, argv, 0);
1568}
1569
1570/* License: Ruby's */
1571rb_pid_t
1572rb_w32_uspawn_process(int mode, const char *prog, char *const *argv,
1573 int in_fd, int out_fd, int err_fd, DWORD flags)
1574{
1575 return w32_spawn_process(mode, prog, argv, in_fd, out_fd, err_fd,
1576 flags, CP_UTF8);
1577}
1578
1579/* License: Artistic or GPL */
1580typedef struct _NtCmdLineElement {
1581 struct _NtCmdLineElement *next;
1582 char *str;
1583 long len;
1584 int flags;
1586
1587//
1588// Possible values for flags
1589//
1590
1591#define NTGLOB 0x1 // element contains a wildcard
1592#define NTMALLOC 0x2 // string in element was malloc'ed
1593#define NTSTRING 0x4 // element contains a quoted string
1594
1595/* License: Ruby's */
1596static int
1597insert(const char *path, VALUE vinfo, void *enc)
1598{
1599 NtCmdLineElement *tmpcurr;
1600 NtCmdLineElement ***tail = (NtCmdLineElement ***)vinfo;
1601
1602 tmpcurr = (NtCmdLineElement *)malloc(sizeof(NtCmdLineElement));
1603 if (!tmpcurr) return -1;
1604 MEMZERO(tmpcurr, NtCmdLineElement, 1);
1605 tmpcurr->len = strlen(path);
1606 tmpcurr->str = strdup(path);
1607 if (!tmpcurr->str) return -1;
1608 tmpcurr->flags |= NTMALLOC;
1609 **tail = tmpcurr;
1610 *tail = &tmpcurr->next;
1611
1612 return 0;
1613}
1614
1615/* License: Artistic or GPL */
1616static NtCmdLineElement **
1617cmdglob(NtCmdLineElement *patt, NtCmdLineElement **tail, UINT cp, rb_encoding *enc)
1618{
1619 char buffer[PATH_MAX], *buf = buffer;
1620 NtCmdLineElement **last = tail;
1621 int status;
1622
1623 if (patt->len >= PATH_MAX)
1624 if (!(buf = malloc(patt->len + 1))) return 0;
1625
1626 memcpy(buf, patt->str, patt->len);
1627 buf[patt->len] = '\0';
1628 translate_char(buf, '\\', '/', cp);
1629 status = ruby_brace_glob_with_enc(buf, 0, insert, (VALUE)&tail, enc);
1630 if (buf != buffer)
1631 free(buf);
1632
1633 if (status || last == tail) return 0;
1634 if (patt->flags & NTMALLOC)
1635 free(patt->str);
1636 free(patt);
1637 return tail;
1638}
1639
1640//
1641// Check a command string to determine if it has I/O redirection
1642// characters that require it to be executed by a command interpreter
1643//
1644
1645/* License: Artistic or GPL */
1646static int
1647has_redirection(const char *cmd, UINT cp)
1648{
1649 char quote = '\0';
1650 const char *ptr;
1651
1652 //
1653 // Scan the string, looking for redirection characters (< or >), pipe
1654 // character (|) or newline (\n) that are not in a quoted string
1655 //
1656
1657 for (ptr = cmd; *ptr;) {
1658 switch (*ptr) {
1659 case '\'':
1660 case '\"':
1661 if (!quote)
1662 quote = *ptr;
1663 else if (quote == *ptr)
1664 quote = '\0';
1665 ptr++;
1666 break;
1667
1668 case '>':
1669 case '<':
1670 case '|':
1671 case '&':
1672 case '\n':
1673 if (!quote)
1674 return TRUE;
1675 ptr++;
1676 break;
1677
1678 case '%':
1679 if (*++ptr != '_' && !ISALPHA(*ptr)) break;
1680 while (*++ptr == '_' || ISALNUM(*ptr));
1681 if (*ptr++ == '%') return TRUE;
1682 break;
1683
1684 case '\\':
1685 ptr++;
1686 default:
1687 ptr = CharNextExA(cp, ptr, 0);
1688 break;
1689 }
1690 }
1691 return FALSE;
1692}
1693
1694/* License: Ruby's */
1695static inline WCHAR *
1696skipspace(WCHAR *ptr)
1697{
1698 while (ISSPACE(*ptr))
1699 ptr++;
1700 return ptr;
1701}
1702
1703/* License: Artistic or GPL */
1704static int
1705w32_cmdvector(const WCHAR *cmd, char ***vec, UINT cp, rb_encoding *enc)
1706{
1707 int globbing, len;
1708 int elements, strsz, done;
1709 int slashes, escape;
1710 WCHAR *ptr, *base, *cmdline;
1711 char *cptr, *buffer;
1712 char **vptr;
1713 WCHAR quote;
1714 NtCmdLineElement *curr, **tail;
1715 NtCmdLineElement *cmdhead = NULL, **cmdtail = &cmdhead;
1716
1717 //
1718 // just return if we don't have a command line
1719 //
1720 while (ISSPACE(*cmd))
1721 cmd++;
1722 if (!*cmd) {
1723 *vec = NULL;
1724 return 0;
1725 }
1726
1727 ptr = cmdline = wcsdup(cmd);
1728
1729 //
1730 // Ok, parse the command line, building a list of CmdLineElements.
1731 // When we've finished, and it's an input command (meaning that it's
1732 // the processes argv), we'll do globing and then build the argument
1733 // vector.
1734 // The outer loop does one iteration for each element seen.
1735 // The inner loop does one iteration for each character in the element.
1736 //
1737
1738 while (*(ptr = skipspace(ptr))) {
1739 base = ptr;
1740 quote = slashes = globbing = escape = 0;
1741 for (done = 0; !done && *ptr; ) {
1742 //
1743 // Switch on the current character. We only care about the
1744 // white-space characters, the wild-card characters, and the
1745 // quote characters.
1746 //
1747
1748 switch (*ptr) {
1749 case L'\\':
1750 if (quote != L'\'') slashes++;
1751 break;
1752
1753 case L' ':
1754 case L'\t':
1755 case L'\n':
1756 //
1757 // if we're not in a string, then we're finished with this
1758 // element
1759 //
1760
1761 if (!quote) {
1762 *ptr = 0;
1763 done = 1;
1764 }
1765 break;
1766
1767 case L'*':
1768 case L'?':
1769 case L'[':
1770 case L'{':
1771 //
1772 // record the fact that this element has a wildcard character
1773 // N.B. Don't glob if inside a single quoted string
1774 //
1775
1776 if (quote != L'\'')
1777 globbing++;
1778 slashes = 0;
1779 break;
1780
1781 case L'\'':
1782 case L'\"':
1783 //
1784 // if we're already in a string, see if this is the
1785 // terminating close-quote. If it is, we're finished with
1786 // the string, but not necessarily with the element.
1787 // If we're not already in a string, start one.
1788 //
1789
1790 if (!(slashes & 1)) {
1791 if (!quote)
1792 quote = *ptr;
1793 else if (quote == *ptr) {
1794 if (quote == L'"' && quote == ptr[1])
1795 ptr++;
1796 quote = L'\0';
1797 }
1798 }
1799 escape++;
1800 slashes = 0;
1801 break;
1802
1803 default:
1804 ptr = CharNextW(ptr);
1805 slashes = 0;
1806 continue;
1807 }
1808 ptr++;
1809 }
1810
1811 //
1812 // when we get here, we've got a pair of pointers to the element,
1813 // base and ptr. Base points to the start of the element while ptr
1814 // points to the character following the element.
1815 //
1816
1817 len = ptr - base;
1818 if (done) --len;
1819
1820 //
1821 // if it's an input vector element and it's enclosed by quotes,
1822 // we can remove them.
1823 //
1824
1825 if (escape) {
1826 WCHAR *p = base, c;
1827 slashes = quote = 0;
1828 while (p < base + len) {
1829 switch (c = *p) {
1830 case L'\\':
1831 p++;
1832 if (quote != L'\'') slashes++;
1833 break;
1834
1835 case L'\'':
1836 case L'"':
1837 if (!(slashes & 1) && quote && quote != c) {
1838 p++;
1839 slashes = 0;
1840 break;
1841 }
1842 memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
1843 sizeof(WCHAR) * (base + len - p));
1844 len -= ((slashes + 1) >> 1) + (~slashes & 1);
1845 p -= (slashes + 1) >> 1;
1846 if (!(slashes & 1)) {
1847 if (quote) {
1848 if (quote == L'"' && quote == *p)
1849 p++;
1850 quote = L'\0';
1851 }
1852 else
1853 quote = c;
1854 }
1855 else
1856 p++;
1857 slashes = 0;
1858 break;
1859
1860 default:
1861 p = CharNextW(p);
1862 slashes = 0;
1863 break;
1864 }
1865 }
1866 }
1867
1868 curr = (NtCmdLineElement *)calloc(1, sizeof(NtCmdLineElement));
1869 if (!curr) goto do_nothing;
1870 curr->str = rb_w32_wstr_to_mbstr(cp, base, len, &curr->len);
1871 curr->flags |= NTMALLOC;
1872
1873 if (globbing && (tail = cmdglob(curr, cmdtail, cp, enc))) {
1874 cmdtail = tail;
1875 }
1876 else {
1877 *cmdtail = curr;
1878 cmdtail = &curr->next;
1879 }
1880 }
1881
1882 //
1883 // Almost done!
1884 // Count up the elements, then allocate space for a vector of pointers
1885 // (argv) and a string table for the elements.
1886 //
1887
1888 for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
1889 elements++;
1890 strsz += (curr->len + 1);
1891 }
1892
1893 len = (elements+1)*sizeof(char *) + strsz;
1894 buffer = (char *)malloc(len);
1895 if (!buffer) {
1896 do_nothing:
1897 while ((curr = cmdhead) != 0) {
1898 cmdhead = curr->next;
1899 if (curr->flags & NTMALLOC) free(curr->str);
1900 free(curr);
1901 }
1902 free(cmdline);
1903 for (vptr = *vec; *vptr; ++vptr);
1904 return vptr - *vec;
1905 }
1906
1907 //
1908 // make vptr point to the start of the buffer
1909 // and cptr point to the area we'll consider the string table.
1910 //
1911 // buffer (*vec)
1912 // |
1913 // V ^---------------------V
1914 // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1915 // | | | .... | NULL | | ..... |\0 | | ..... |\0 |...
1916 // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1917 // |- elements+1 -| ^ 1st element ^ 2nd element
1918
1919 vptr = (char **) buffer;
1920
1921 cptr = buffer + (elements+1) * sizeof(char *);
1922
1923 while ((curr = cmdhead) != 0) {
1924 memcpy(cptr, curr->str, curr->len);
1925 cptr[curr->len] = '\0';
1926 *vptr++ = cptr;
1927 cptr += curr->len + 1;
1928 cmdhead = curr->next;
1929 if (curr->flags & NTMALLOC) free(curr->str);
1930 free(curr);
1931 }
1932 *vptr = 0;
1933
1934 *vec = (char **) buffer;
1935 free(cmdline);
1936 return elements;
1937}
1938
1939//
1940// UNIX compatible directory access functions for NT
1941//
1942
1943/* License: Ruby's */
1944/* TODO: better name */
1945static HANDLE
1946open_special(const WCHAR *path, DWORD access, DWORD flags)
1947{
1948 const DWORD share_mode =
1949 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
1950 return CreateFileW(path, access, share_mode, NULL, OPEN_EXISTING,
1951 FILE_FLAG_BACKUP_SEMANTICS|flags, NULL);
1952}
1953
1954//
1955// The idea here is to read all the directory names into a string table
1956// (separated by nulls) and when one of the other dir functions is called
1957// return the pointer to the current file name.
1958//
1959
1960/* License: Ruby's */
1961#define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] & (1 << (i) % CHAR_BIT))
1962#define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
1963
1964#define BitOfIsDir(n) ((n) * 2)
1965#define BitOfIsRep(n) ((n) * 2 + 1)
1966#define DIRENT_PER_CHAR (CHAR_BIT / 2)
1967
1968static const WCHAR namespace_prefix[] = {L'\\', L'\\', L'?', L'\\'};
1969
1970enum {FINAL_PATH_MAX = PATH_MAX + numberof(namespace_prefix)};
1971
1972/* License: Artistic or GPL */
1973static HANDLE
1974open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
1975{
1976 HANDLE fh;
1977 WCHAR fullname[FINAL_PATH_MAX + rb_strlen_lit("\\*")];
1978 WCHAR *p;
1979 int len = 0;
1980
1981 //
1982 // Create the search pattern
1983 //
1984
1985 fh = open_special(filename, 0, 0);
1986 if (fh != INVALID_HANDLE_VALUE) {
1987 len = GetFinalPathNameByHandleW(fh, fullname, FINAL_PATH_MAX, 0);
1988 CloseHandle(fh);
1989 if (len >= FINAL_PATH_MAX) {
1990 errno = ENAMETOOLONG;
1991 return INVALID_HANDLE_VALUE;
1992 }
1993 }
1994 if (!len) {
1995 len = lstrlenW(filename);
1996 if (len >= PATH_MAX) {
1997 errno = ENAMETOOLONG;
1998 return INVALID_HANDLE_VALUE;
1999 }
2000 MEMCPY(fullname, filename, WCHAR, len);
2001 }
2002 p = &fullname[len-1];
2003 if (!(isdirsep(*p) || *p == L':')) *++p = L'\\';
2004 *++p = L'*';
2005 *++p = L'\0';
2006
2007 //
2008 // do the FindFirstFile call
2009 //
2010 fh = FindFirstFileW(fullname, fd);
2011 if (fh == INVALID_HANDLE_VALUE) {
2012 errno = map_errno(GetLastError());
2013 }
2014 return fh;
2015}
2016
2017/* License: Artistic or GPL */
2018static DIR *
2019w32_wopendir(const WCHAR *wpath)
2020{
2021 struct stati128 sbuf;
2022 WIN32_FIND_DATAW fd;
2023 HANDLE fh;
2024 DIR *p;
2025 long pathlen;
2026 long len;
2027 long altlen;
2028 long idx;
2029 WCHAR *tmpW;
2030 char *tmp;
2031
2032 //
2033 // check to see if we've got a directory
2034 //
2035 if (wstati128(wpath, &sbuf, FALSE) < 0) {
2036 return NULL;
2037 }
2038 if (!(sbuf.st_mode & S_IFDIR) &&
2039 (!ISALPHA(wpath[0]) || wpath[1] != L':' || wpath[2] != L'\0' ||
2040 ((1 << ((wpath[0] & 0x5f) - 'A')) & GetLogicalDrives()) == 0)) {
2041 errno = ENOTDIR;
2042 return NULL;
2043 }
2044 fh = open_dir_handle(wpath, &fd);
2045 if (fh == INVALID_HANDLE_VALUE) {
2046 return NULL;
2047 }
2048
2049 //
2050 // Get us a DIR structure
2051 //
2052 p = calloc(1, sizeof(DIR));
2053 if (p == NULL)
2054 return NULL;
2055
2056 pathlen = lstrlenW(wpath);
2057 idx = 0;
2058
2059 //
2060 // loop finding all the files that match the wildcard
2061 // (which should be all of them in this directory!).
2062 // the variable idx should point one past the null terminator
2063 // of the previous string found.
2064 //
2065 do {
2066 len = lstrlenW(fd.cFileName) + 1;
2067 altlen = lstrlenW(fd.cAlternateFileName) + 1;
2068
2069 //
2070 // bump the string table size by enough for the
2071 // new name and it's null terminator
2072 //
2073 tmpW = realloc(p->start, (idx + len + altlen) * sizeof(WCHAR));
2074 if (!tmpW) {
2075 error:
2076 rb_w32_closedir(p);
2077 FindClose(fh);
2078 errno = ENOMEM;
2079 return NULL;
2080 }
2081
2082 p->start = tmpW;
2083 memcpy(&p->start[idx], fd.cFileName, len * sizeof(WCHAR));
2084 memcpy(&p->start[idx + len], fd.cAlternateFileName, altlen * sizeof(WCHAR));
2085
2086 if (p->nfiles % DIRENT_PER_CHAR == 0) {
2087 tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
2088 if (!tmp)
2089 goto error;
2090 p->bits = tmp;
2091 p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
2092 }
2093 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2094 SetBit(p->bits, BitOfIsDir(p->nfiles));
2095 if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2096 WCHAR *tmppath = malloc((pathlen + len + 1) * sizeof(WCHAR));
2097 memcpy(tmppath, wpath, pathlen * sizeof(WCHAR));
2098 tmppath[pathlen] = L'\\';
2099 memcpy(tmppath + pathlen + 1, fd.cFileName, len * sizeof(WCHAR));
2100 if (rb_w32_reparse_symlink_p(tmppath))
2101 SetBit(p->bits, BitOfIsRep(p->nfiles));
2102 free(tmppath);
2103 }
2104
2105 p->nfiles++;
2106 idx += len + altlen;
2107 } while (FindNextFileW(fh, &fd));
2108 FindClose(fh);
2109 p->size = idx;
2110 p->curr = p->start;
2111 return p;
2112}
2113
2114/* License: Ruby's */
2115UINT
2116filecp(void)
2117{
2118 UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
2119 return cp;
2120}
2121
2122/* License: Ruby's */
2123char *
2124rb_w32_wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
2125{
2126 char *ptr;
2127 int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL);
2128 if (!(ptr = malloc(len))) return 0;
2129 WideCharToMultiByte(cp, 0, wstr, clen, ptr, len, NULL, NULL);
2130 if (plen) {
2131 /* exclude NUL only if NUL-terminated string */
2132 if (clen == -1) --len;
2133 *plen = len;
2134 }
2135 return ptr;
2136}
2137
2138/* License: Ruby's */
2139WCHAR *
2140rb_w32_mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
2141{
2142 WCHAR *ptr;
2143 int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0);
2144 if (!(ptr = malloc(sizeof(WCHAR) * len))) return 0;
2145 MultiByteToWideChar(cp, 0, str, clen, ptr, len);
2146 if (plen) {
2147 /* exclude NUL only if NUL-terminated string */
2148 if (clen == -1) --len;
2149 *plen = len;
2150 }
2151 return ptr;
2152}
2153
2154/* License: Ruby's */
2155DIR *
2156rb_w32_opendir(const char *filename)
2157{
2158 DIR *ret;
2159 WCHAR *wpath = filecp_to_wstr(filename, NULL);
2160 if (!wpath)
2161 return NULL;
2162 ret = w32_wopendir(wpath);
2163 free(wpath);
2164 return ret;
2165}
2166
2167/* License: Ruby's */
2168DIR *
2169rb_w32_uopendir(const char *filename)
2170{
2171 DIR *ret;
2172 WCHAR *wpath = utf8_to_wstr(filename, NULL);
2173 if (!wpath)
2174 return NULL;
2175 ret = w32_wopendir(wpath);
2176 free(wpath);
2177 return ret;
2178}
2179
2180//
2181// Move to next entry
2182//
2183
2184/* License: Artistic or GPL */
2185static void
2186move_to_next_entry(DIR *dirp)
2187{
2188 if (dirp->curr) {
2189 dirp->loc++;
2190 dirp->curr += lstrlenW(dirp->curr) + 1;
2191 dirp->curr += lstrlenW(dirp->curr) + 1;
2192 if (dirp->curr >= (dirp->start + dirp->size)) {
2193 dirp->curr = NULL;
2194 }
2195 }
2196}
2197
2198//
2199// Readdir just returns the current string pointer and bumps the
2200// string pointer to the next entry.
2201//
2202/* License: Ruby's */
2203static BOOL
2204win32_direct_conv(const WCHAR *file, const WCHAR *alt, struct direct *entry, const void *enc)
2205{
2206 UINT cp = *((UINT *)enc);
2207 if (!(entry->d_name = wstr_to_mbstr(cp, file, -1, &entry->d_namlen)))
2208 return FALSE;
2209 if (alt && *alt) {
2210 long altlen = 0;
2211 entry->d_altname = wstr_to_mbstr(cp, alt, -1, &altlen);
2212 entry->d_altlen = altlen;
2213 }
2214 return TRUE;
2215}
2216
2217/* License: Ruby's */
2218VALUE
2219rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
2220{
2221 VALUE src;
2222 long len = lstrlenW(wstr);
2223 int encindex = rb_enc_to_index(enc);
2224
2225 if (encindex == ENCINDEX_UTF_16LE) {
2226 return rb_enc_str_new((char *)wstr, len * sizeof(WCHAR), enc);
2227 }
2228 else {
2229#if SIZEOF_INT < SIZEOF_LONG
2230# error long should equal to int on Windows
2231#endif
2232 int clen = rb_long2int(len);
2233 len = WideCharToMultiByte(CP_UTF8, 0, wstr, clen, NULL, 0, NULL, NULL);
2234 src = rb_enc_str_new(0, len, rb_enc_from_index(ENCINDEX_UTF_8));
2235 WideCharToMultiByte(CP_UTF8, 0, wstr, clen, RSTRING_PTR(src), len, NULL, NULL);
2236 }
2237 switch (encindex) {
2238 case ENCINDEX_ASCII_8BIT:
2239 case ENCINDEX_US_ASCII:
2240 /* assume UTF-8 */
2241 case ENCINDEX_UTF_8:
2242 /* do nothing */
2243 return src;
2244 }
2245 return rb_str_conv_enc_opts(src, NULL, enc, ECONV_UNDEF_REPLACE, Qnil);
2246}
2247
2248/* License: Ruby's */
2249char *
2250rb_w32_conv_from_wstr(const WCHAR *wstr, long *lenp, rb_encoding *enc)
2251{
2252 VALUE str = rb_w32_conv_from_wchar(wstr, enc);
2253 long len;
2254 char *ptr;
2255
2256 if (NIL_P(str)) return wstr_to_utf8(wstr, lenp);
2257 *lenp = len = RSTRING_LEN(str);
2258 memcpy(ptr = malloc(len + 1), RSTRING_PTR(str), len);
2259 ptr[len] = '\0';
2260 return ptr;
2261}
2262
2263/* License: Ruby's */
2264static BOOL
2265ruby_direct_conv(const WCHAR *file, const WCHAR *alt, struct direct *entry, const void *enc)
2266{
2267 if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
2268 return FALSE;
2269 if (alt && *alt) {
2270 long altlen = 0;
2271 entry->d_altname = rb_w32_conv_from_wstr(alt, &altlen, enc);
2272 entry->d_altlen = altlen;
2273 }
2274 return TRUE;
2275}
2276
2277/* License: Artistic or GPL */
2278static struct direct *
2279readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, const WCHAR *, struct direct *, const void *), const void *enc)
2280{
2281 static long dummy_ino = 0;
2282
2283 if (dirp->curr) {
2284
2285 //
2286 // first set up the structure to return
2287 //
2288 free(dirp->dirstr.d_name);
2289 free(dirp->dirstr.d_altname);
2290 dirp->dirstr.d_altname = 0;
2291 dirp->dirstr.d_altlen = 0;
2292 conv(dirp->curr, dirp->curr + lstrlenW(dirp->curr) + 1, &dirp->dirstr, enc);
2293
2294 //
2295 // Fake inode
2296 //
2297 dirp->dirstr.d_ino = (ino_t)(InterlockedIncrement(&dummy_ino) - 1);
2298
2299 //
2300 // Attributes
2301 //
2302 /* ignore FILE_ATTRIBUTE_DIRECTORY as unreliable for reparse points */
2303 if (GetBit(dirp->bits, BitOfIsRep(dirp->loc)))
2304 dirp->dirstr.d_type = DT_LNK;
2305 else if (GetBit(dirp->bits, BitOfIsDir(dirp->loc)))
2306 dirp->dirstr.d_type = DT_DIR;
2307 else
2308 dirp->dirstr.d_type = DT_REG;
2309
2310 //
2311 // Now set up for the next call to readdir
2312 //
2313
2314 move_to_next_entry(dirp);
2315
2316 return &(dirp->dirstr);
2317
2318 }
2319 else
2320 return NULL;
2321}
2322
2323/* License: Ruby's */
2324struct direct *
2325rb_w32_readdir(DIR *dirp, rb_encoding *enc)
2326{
2327 int idx = rb_enc_to_index(enc);
2328 if (idx == ENCINDEX_ASCII_8BIT) {
2329 const UINT cp = filecp();
2330 return readdir_internal(dirp, win32_direct_conv, &cp);
2331 }
2332 else if (idx == ENCINDEX_UTF_8) {
2333 const UINT cp = CP_UTF8;
2334 return readdir_internal(dirp, win32_direct_conv, &cp);
2335 }
2336 else
2337 return readdir_internal(dirp, ruby_direct_conv, enc);
2338}
2339
2340/* License: Ruby's */
2341struct direct *
2342rb_w32_ureaddir(DIR *dirp)
2343{
2344 const UINT cp = CP_UTF8;
2345 return readdir_internal(dirp, win32_direct_conv, &cp);
2346}
2347
2348//
2349// Telldir returns the current string pointer position
2350//
2351
2352/* License: Artistic or GPL */
2353long
2354rb_w32_telldir(DIR *dirp)
2355{
2356 return dirp->loc;
2357}
2358
2359//
2360// Seekdir moves the string pointer to a previously saved position
2361// (Saved by telldir).
2362
2363/* License: Ruby's */
2364void
2365rb_w32_seekdir(DIR *dirp, long loc)
2366{
2367 if (dirp->loc > loc) rb_w32_rewinddir(dirp);
2368
2369 while (dirp->curr && dirp->loc < loc) {
2370 move_to_next_entry(dirp);
2371 }
2372}
2373
2374//
2375// Rewinddir resets the string pointer to the start
2376//
2377
2378/* License: Artistic or GPL */
2379void
2380rb_w32_rewinddir(DIR *dirp)
2381{
2382 dirp->curr = dirp->start;
2383 dirp->loc = 0;
2384}
2385
2386//
2387// This just free's the memory allocated by opendir
2388//
2389
2390/* License: Artistic or GPL */
2391int
2392rb_w32_closedir(DIR *dirp)
2393{
2394 if (dirp) {
2395 free(dirp->dirstr.d_name);
2396 free(dirp->dirstr.d_altname);
2397 free(dirp->start);
2398 free(dirp->bits);
2399 free(dirp);
2400 }
2401 return 0;
2402}
2403
2404#if RUBY_MSVCRT_VERSION >= 140
2405typedef struct {
2406 union
2407 {
2408 FILE _public_file;
2409 char* _ptr;
2410 };
2411
2412 char* _base;
2413 int _cnt;
2414 long _flags;
2415 long _file;
2416 int _charbuf;
2417 int _bufsiz;
2418 char* _tmpfname;
2419 CRITICAL_SECTION _lock;
2420} vcruntime_file;
2421#define FILE_COUNT(stream) ((vcruntime_file*)stream)->_cnt
2422#define FILE_READPTR(stream) ((vcruntime_file*)stream)->_ptr
2423#define FILE_FILENO(stream) ((vcruntime_file*)stream)->_file
2424#else
2425#define FILE_COUNT(stream) stream->_cnt
2426#define FILE_READPTR(stream) stream->_ptr
2427#define FILE_FILENO(stream) stream->_file
2428#endif
2429
2430/* License: Ruby's */
2431#if RUBY_MSVCRT_VERSION >= 140
2432typedef char lowio_text_mode;
2433typedef char lowio_pipe_lookahead[3];
2434
2435typedef struct {
2436 CRITICAL_SECTION lock;
2437 intptr_t osfhnd; // underlying OS file HANDLE
2438 __int64 startpos; // File position that matches buffer start
2439 unsigned char osfile; // Attributes of file (e.g., open in text mode?)
2440 lowio_text_mode textmode;
2441 lowio_pipe_lookahead _pipe_lookahead;
2442
2443 uint8_t unicode : 1; // Was the file opened as unicode?
2444 uint8_t utf8translations : 1; // Buffer contains translations other than CRLF
2445 uint8_t dbcsBufferUsed : 1; // Is the dbcsBuffer in use?
2446 char dbcsBuffer; // Buffer for the lead byte of DBCS when converting from DBCS to Unicode
2447} ioinfo;
2448#else
2449typedef struct {
2450 intptr_t osfhnd; /* underlying OS file HANDLE */
2451 char osfile; /* attributes of file (e.g., open in text mode?) */
2452 char pipech; /* one char buffer for handles opened on pipes */
2453 int lockinitflag;
2454 CRITICAL_SECTION lock;
2455#if RUBY_MSVCRT_VERSION >= 80
2456 char textmode;
2457 char pipech2[2];
2458#endif
2459} ioinfo;
2460#endif
2461
2462#if !defined _CRTIMP || defined __MINGW32__
2463#undef _CRTIMP
2464#define _CRTIMP __declspec(dllimport)
2465#endif
2466
2467#if RUBY_MSVCRT_VERSION >= 140
2468static ioinfo ** __pioinfo = NULL;
2469#define IOINFO_L2E 6
2470#else
2471EXTERN_C _CRTIMP ioinfo * __pioinfo[];
2472#define IOINFO_L2E 5
2473#endif
2474static inline ioinfo* _pioinfo(int);
2475
2476
2477#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
2478#define _osfhnd(i) (_pioinfo(i)->osfhnd)
2479#define _osfile(i) (_pioinfo(i)->osfile)
2480#define rb_acrt_lowio_lock_fh(i) EnterCriticalSection(&_pioinfo(i)->lock)
2481#define rb_acrt_lowio_unlock_fh(i) LeaveCriticalSection(&_pioinfo(i)->lock)
2482
2483#if RUBY_MSVCRT_VERSION >= 80
2484static size_t pioinfo_extra = 0; /* workaround for VC++8 SP1 */
2485
2486/* License: Ruby's */
2487static void
2488set_pioinfo_extra(void)
2489{
2490#if RUBY_MSVCRT_VERSION >= 140
2491# define FUNCTION_RET 0xc3 /* ret */
2492# ifdef _DEBUG
2493# define UCRTBASE "ucrtbased.dll"
2494# else
2495# define UCRTBASE "ucrtbase.dll"
2496# endif
2497 /* get __pioinfo addr with _isatty */
2498 /*
2499 * Why Ruby depends to _pioinfo is
2500 * * to associate socket and fd: CRuby creates fd with dummy file handle
2501 * and set socket to emulate Unix-like behavior. Without __pioinfo
2502 * we need something which manages the fd number allocation
2503 * * to implement overlapped I/O for Windows 2000/XP
2504 * * to emulate fcntl(2)
2505 *
2506 * see also
2507 * * https://bugs.ruby-lang.org/issues/11118
2508 * * https://bugs.ruby-lang.org/issues/18605
2509 */
2510 char *p = (char*)get_proc_address(UCRTBASE, "_isatty", NULL);
2511 /* _osfile(fh) & FDEV */
2512
2513#if defined(_M_ARM64) || defined(__aarch64__)
2514#define IS_INSN(pc, name) ((*(pc) & name##_mask) == name##_id)
2515 const int max_num_inst = 500;
2516 uint32_t *start = (uint32_t*)p;
2517 uint32_t *end_limit = (start + max_num_inst);
2518 uint32_t *pc = start;
2519
2520 if (!p) {
2521 fprintf(stderr, "_isatty proc not found in " UCRTBASE "\n");
2522 _exit(1);
2523 }
2524
2525 /* end of function */
2526 const uint32_t ret_id = 0xd65f0000;
2527 const uint32_t ret_mask = 0xfffffc1f;
2528 for(; pc < end_limit; pc++) {
2529 if (IS_INSN(pc, ret)) {
2530 break;
2531 }
2532 }
2533 if (pc == end_limit) {
2534 fprintf(stderr, "end of _isatty not found in " UCRTBASE "\n");
2535 _exit(1);
2536 }
2537
2538 /* pioinfo instruction mark */
2539 const uint32_t adrp_id = 0x90000000;
2540 const uint32_t adrp_mask = 0x9f000000;
2541 const uint32_t add_id = 0x11000000;
2542 const uint32_t add_mask = 0x7fc00000;
2543 for(; pc > start; pc--) {
2544 if (IS_INSN(pc, adrp) && IS_INSN(pc + 1, add)) {
2545 break;
2546 }
2547 }
2548 if(pc == start) {
2549 fprintf(stderr, "pioinfo mark not found in " UCRTBASE "\n");
2550 _exit(1);
2551 }
2552
2553 /* We now point to instructions that load address of __pioinfo:
2554 * adrp x8, 0x1801d8000
2555 * add x8, x8, #0xdb0
2556 * https://devblogs.microsoft.com/oldnewthing/20220809-00/?p=106955
2557 * The last adrp/add sequence before ret is what we are looking for.
2558 */
2559 const uint32_t adrp_insn = *pc;
2560 const uint32_t adrp_immhi = (adrp_insn & 0x00ffffe0) >> 5;
2561 const uint32_t adrp_immlo = (adrp_insn & 0x60000000) >> (5 + 19 + 5);
2562 const int64_t adrp_sign = (adrp_insn & 0x00800000) ? ~0x001fffff : 0;
2563 /* imm = SignExtend(immhi:immlo:Zeros(12), 64) */
2564 const int64_t adrp_imm = (adrp_sign | (adrp_immhi << 2) | adrp_immlo) << 12;
2565 /* base = PC64<63:12>:Zeros(12) */
2566 const uint64_t adrp_base = (uint64_t)pc & 0xfffffffffffff000;
2567
2568 const uint32_t add_insn = *(pc + 1);
2569 const uint64_t add_imm = (add_insn & 0x3ffc00) >> (5 + 5);
2570
2571 __pioinfo = (ioinfo**)(adrp_base + adrp_imm + add_imm);
2572#else /* _M_ARM64 */
2573 char *pend = p;
2574
2575# ifdef _WIN64
2576 int32_t rel;
2577 char *rip;
2578 /* add rsp, _ */
2579# define FUNCTION_BEFORE_RET_MARK "\x48\x83\xc4"
2580# define FUNCTION_SKIP_BYTES 1
2581# ifdef _DEBUG
2582 /* lea rcx,[__pioinfo's addr in RIP-relative 32bit addr] */
2583# define PIOINFO_MARK "\x48\x8d\x0d"
2584# else
2585 /* lea rdx,[__pioinfo's addr in RIP-relative 32bit addr] */
2586# define PIOINFO_MARK "\x48\x8d\x15"
2587# endif
2588
2589# else /* x86 */
2590 /* pop ebp */
2591# define FUNCTION_BEFORE_RET_MARK "\x5d"
2592 /* leave */
2593# define FUNCTION_BEFORE_RET_MARK_2 "\xc9"
2594# define FUNCTION_SKIP_BYTES 0
2595 /* mov eax,dword ptr [eax*4+100EB430h] */
2596# define PIOINFO_MARK "\x8B\x04\x85"
2597# endif
2598 if (p) {
2599 for (pend += 10; pend < p + 500; pend++) {
2600 // find end of function
2601 if ((memcmp(pend, FUNCTION_BEFORE_RET_MARK, sizeof(FUNCTION_BEFORE_RET_MARK) - 1) == 0
2602# ifdef FUNCTION_BEFORE_RET_MARK_2
2603 || memcmp(pend, FUNCTION_BEFORE_RET_MARK_2, sizeof(FUNCTION_BEFORE_RET_MARK_2) - 1) == 0
2604# endif
2605 ) &&
2606 *(pend + (sizeof(FUNCTION_BEFORE_RET_MARK) - 1) + FUNCTION_SKIP_BYTES) == (char)FUNCTION_RET) {
2607 // search backwards from end of function
2608 for (pend -= (sizeof(PIOINFO_MARK) - 1); pend > p; pend--) {
2609 if (memcmp(pend, PIOINFO_MARK, sizeof(PIOINFO_MARK) - 1) == 0) {
2610 p = pend;
2611 goto found;
2612 }
2613 }
2614 break;
2615 }
2616 }
2617 }
2618 fprintf(stderr, "unexpected " UCRTBASE "\n");
2619 _exit(1);
2620
2621 found:
2622 p += sizeof(PIOINFO_MARK) - 1;
2623#ifdef _WIN64
2624 rel = *(int32_t*)(p);
2625 rip = p + sizeof(int32_t);
2626 __pioinfo = (ioinfo**)(rip + rel);
2627#else
2628 __pioinfo = *(ioinfo***)(p);
2629#endif
2630#endif /* _M_ARM64 */
2631#endif /* RUBY_MSVCRT_VERSION */
2632 int fd;
2633
2634 fd = _open("NUL", O_RDONLY);
2635 for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra += sizeof(void *)) {
2636 if (_osfhnd(fd) == _get_osfhandle(fd)) {
2637 break;
2638 }
2639 }
2640 _close(fd);
2641
2642 if (pioinfo_extra > 64) {
2643 /* not found, maybe something wrong... */
2644 pioinfo_extra = 0;
2645 }
2646}
2647#else
2648#define pioinfo_extra 0
2649#endif
2650
2651static inline ioinfo*
2652_pioinfo(int fd)
2653{
2654 const size_t sizeof_ioinfo = sizeof(ioinfo) + pioinfo_extra;
2655 return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
2656 (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
2657}
2658
2659#define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2660#define _set_osflags(fh, flags) (_osfile(fh) = (flags))
2661
2662#define FOPEN 0x01 /* file handle open */
2663#define FEOFLAG 0x02 /* end of file has been encountered */
2664#define FPIPE 0x08 /* file handle refers to a pipe */
2665#define FNOINHERIT 0x10 /* file handle opened O_NOINHERIT */
2666#define FAPPEND 0x20 /* file handle opened O_APPEND */
2667#define FDEV 0x40 /* file handle refers to device */
2668#define FTEXT 0x80 /* file handle is in text mode */
2669
2670static int is_socket(SOCKET);
2671static int is_console(SOCKET);
2672
2673/* License: Ruby's */
2674int
2675rb_w32_io_cancelable_p(int fd)
2676{
2677 return is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd));
2678}
2679
2680/* License: Ruby's */
2681static int
2682rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2683{
2684 int fh;
2685 char fileflags; /* _osfile flags */
2686 HANDLE hF;
2687
2688 /* copy relevant flags from second parameter */
2689 fileflags = FDEV;
2690
2691 if (flags & O_APPEND)
2692 fileflags |= FAPPEND;
2693
2694 if (flags & O_TEXT)
2695 fileflags |= FTEXT;
2696
2697 if (flags & O_NOINHERIT)
2698 fileflags |= FNOINHERIT;
2699
2700 /* attempt to allocate a C Runtime file handle */
2701 hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2702 fh = _open_osfhandle((intptr_t)hF, 0);
2703 CloseHandle(hF);
2704 if (fh == -1) {
2705 errno = EMFILE; /* too many open files */
2706 _doserrno = 0L; /* not an OS error */
2707 }
2708 else {
2709
2710 rb_acrt_lowio_lock_fh(fh);
2711 /* the file is open. now, set the info in _osfhnd array */
2712 _set_osfhnd(fh, osfhandle);
2713
2714 fileflags |= FOPEN; /* mark as open */
2715
2716 _set_osflags(fh, fileflags); /* set osfile entry */
2717 rb_acrt_lowio_unlock_fh(fh);
2718 }
2719 return fh; /* return handle */
2720}
2721
2722/* License: Ruby's */
2723static void
2724init_stdhandle(void)
2725{
2726 int nullfd = -1;
2727 int keep = 0;
2728#define open_null(fd) \
2729 (((nullfd < 0) ? \
2730 (nullfd = open("NUL", O_RDWR)) : 0), \
2731 ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)), \
2732 (fd))
2733
2734 if (fileno(stdin) < 0) {
2735 FILE_FILENO(stdin) = open_null(0);
2736 }
2737 else {
2738 setmode(fileno(stdin), O_BINARY);
2739 }
2740 if (fileno(stdout) < 0) {
2741 FILE_FILENO(stdout) = open_null(1);
2742 }
2743 if (fileno(stderr) < 0) {
2744 FILE_FILENO(stderr) = open_null(2);
2745 }
2746 if (nullfd >= 0 && !keep) close(nullfd);
2747 setvbuf(stderr, NULL, _IONBF, 0);
2748
2749 {
2750 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
2751 DWORD m;
2752 if (GetConsoleMode(h, &m)) {
2753#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
2754#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
2755#endif
2756 SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
2757 }
2758 }
2759}
2760
2761#undef getsockopt
2762
2763/* License: Ruby's */
2764static int
2765is_socket(SOCKET sock)
2766{
2767 if (socklist_lookup(sock, NULL))
2768 return TRUE;
2769 else
2770 return FALSE;
2771}
2772
2773/* License: Ruby's */
2774int
2775rb_w32_is_socket(int fd)
2776{
2777 return is_socket(TO_SOCKET(fd));
2778}
2779
2780//
2781// Since the errors returned by the socket error function
2782// WSAGetLastError() are not known by the library routine strerror
2783// we have to roll our own.
2784//
2785
2786#undef strerror
2787
2788/* License: Artistic or GPL */
2789char *
2790rb_w32_strerror(int e)
2791{
2792 static char buffer[512];
2793 DWORD source = 0;
2794 char *p;
2795
2796 if (e < 0 || e > sys_nerr) {
2797 if (e < 0)
2798 e = GetLastError();
2799#if WSAEWOULDBLOCK != EWOULDBLOCK
2800 else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
2801 static int s = -1;
2802 int i;
2803 if (s < 0)
2804 for (s = 0; s < (int)(sizeof(errmap)/sizeof(*errmap)); s++)
2805 if (errmap[s].winerr == WSAEWOULDBLOCK)
2806 break;
2807 for (i = s; i < (int)(sizeof(errmap)/sizeof(*errmap)); i++)
2808 if (errmap[i].err == e) {
2809 e = errmap[i].winerr;
2810 break;
2811 }
2812 }
2813#endif
2814 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2815 FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
2816 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
2817 buffer, sizeof(buffer), NULL) == 0 &&
2818 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2819 FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
2820 buffer, sizeof(buffer), NULL) == 0)
2821 strlcpy(buffer, "Unknown Error", sizeof(buffer));
2822 }
2823 else
2824 strlcpy(buffer, strerror(e), sizeof(buffer));
2825
2826 p = buffer;
2827 while ((p = strpbrk(p, "\r\n")) != NULL) {
2828 memmove(p, p + 1, strlen(p));
2829 }
2830 return buffer;
2831}
2832
2833//
2834// various stubs
2835//
2836
2837
2838// Ownership
2839//
2840// Just pretend that everyone is a superuser. NT will let us know if
2841// we don't really have permission to do something.
2842//
2843
2844#define ROOT_UID 0
2845#define ROOT_GID 0
2846
2847/* License: Artistic or GPL */
2848rb_uid_t
2849getuid(void)
2850{
2851 return ROOT_UID;
2852}
2853
2854/* License: Artistic or GPL */
2855rb_uid_t
2856geteuid(void)
2857{
2858 return ROOT_UID;
2859}
2860
2861/* License: Artistic or GPL */
2862rb_gid_t
2863getgid(void)
2864{
2865 return ROOT_GID;
2866}
2867
2868/* License: Artistic or GPL */
2869rb_gid_t
2870getegid(void)
2871{
2872 return ROOT_GID;
2873}
2874
2875/* License: Artistic or GPL */
2876int
2877setuid(rb_uid_t uid)
2878{
2879 return (uid == ROOT_UID ? 0 : -1);
2880}
2881
2882/* License: Artistic or GPL */
2883int
2884setgid(rb_gid_t gid)
2885{
2886 return (gid == ROOT_GID ? 0 : -1);
2887}
2888
2889//
2890// File system stuff
2891//
2892
2893/* License: Artistic or GPL */
2894int
2895ioctl(int i, int u, ...)
2896{
2897 errno = EINVAL;
2898 return -1;
2899}
2900
2901void
2902rb_w32_fdset(int fd, fd_set *set)
2903{
2904 FD_SET(fd, set);
2905}
2906
2907#undef FD_CLR
2908
2909/* License: Ruby's */
2910void
2911rb_w32_fdclr(int fd, fd_set *set)
2912{
2913 unsigned int i;
2914 SOCKET s = TO_SOCKET(fd);
2915
2916 for (i = 0; i < set->fd_count; i++) {
2917 if (set->fd_array[i] == s) {
2918 memmove(&set->fd_array[i], &set->fd_array[i+1],
2919 sizeof(set->fd_array[0]) * (--set->fd_count - i));
2920 break;
2921 }
2922 }
2923}
2924
2925#undef FD_ISSET
2926
2927/* License: Ruby's */
2928int
2929rb_w32_fdisset(int fd, fd_set *set)
2930{
2931 int ret;
2932 SOCKET s = TO_SOCKET(fd);
2933 if (s == (SOCKET)INVALID_HANDLE_VALUE)
2934 return 0;
2935 RUBY_CRITICAL {ret = __WSAFDIsSet(s, set);}
2936 return ret;
2937}
2938
2939/* License: Ruby's */
2940void
2941rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
2942{
2943 max = min(src->fd_count, (UINT)max);
2944 if ((UINT)dst->capa < (UINT)max) {
2945 dst->capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2946 dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2947 }
2948
2949 memcpy(dst->fdset->fd_array, src->fd_array,
2950 max * sizeof(src->fd_array[0]));
2951 dst->fdset->fd_count = src->fd_count;
2952}
2953
2954/* License: Ruby's */
2955void
2957{
2958 if ((UINT)dst->capa < src->fdset->fd_count) {
2959 dst->capa = (src->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2960 dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2961 }
2962
2963 memcpy(dst->fdset->fd_array, src->fdset->fd_array,
2964 src->fdset->fd_count * sizeof(src->fdset->fd_array[0]));
2965 dst->fdset->fd_count = src->fdset->fd_count;
2966}
2967
2968//
2969// Networking trampolines
2970// These are used to avoid socket startup/shutdown overhead in case
2971// the socket routines aren't used.
2972//
2973
2974#undef select
2975
2976/* License: Ruby's */
2977static int
2978extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
2979{
2980 unsigned int s = 0;
2981 unsigned int m = 0;
2982 if (!src) return 0;
2983
2984 while (s < src->fd_count) {
2985 SOCKET fd = src->fd_array[s];
2986
2987 if (!func || (*func)(fd)) {
2988 if (dst) { /* move it to dst */
2989 unsigned int d;
2990
2991 for (d = 0; d < dst->fdset->fd_count; d++) {
2992 if (dst->fdset->fd_array[d] == fd)
2993 break;
2994 }
2995 if (d == dst->fdset->fd_count) {
2996 if ((int)dst->fdset->fd_count >= dst->capa) {
2997 dst->capa = (dst->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2998 dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2999 }
3000 dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
3001 }
3002 memmove(
3003 &src->fd_array[s],
3004 &src->fd_array[s+1],
3005 sizeof(src->fd_array[0]) * (--src->fd_count - s));
3006 }
3007 else {
3008 m++;
3009 s++;
3010 }
3011 }
3012 else s++;
3013 }
3014
3015 return dst ? dst->fdset->fd_count : m;
3016}
3017
3018/* License: Ruby's */
3019static int
3020copy_fd(fd_set *dst, fd_set *src)
3021{
3022 unsigned int s;
3023 if (!src || !dst) return 0;
3024
3025 for (s = 0; s < src->fd_count; ++s) {
3026 SOCKET fd = src->fd_array[s];
3027 unsigned int d;
3028 for (d = 0; d < dst->fd_count; ++d) {
3029 if (dst->fd_array[d] == fd)
3030 break;
3031 }
3032 if (d == dst->fd_count && d < FD_SETSIZE) {
3033 dst->fd_array[dst->fd_count++] = fd;
3034 }
3035 }
3036
3037 return dst->fd_count;
3038}
3039
3040/* License: Ruby's */
3041static int
3042is_not_socket(SOCKET sock)
3043{
3044 return !is_socket(sock);
3045}
3046
3047/* License: Ruby's */
3048static int
3049is_pipe(SOCKET sock) /* DONT call this for SOCKET! it claims it is PIPE. */
3050{
3051 int ret;
3052
3053 RUBY_CRITICAL {
3054 ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
3055 }
3056
3057 return ret;
3058}
3059
3060/* License: Ruby's */
3061static int
3062is_readable_pipe(SOCKET sock) /* call this for pipe only */
3063{
3064 int ret;
3065 DWORD n = 0;
3066
3067 RUBY_CRITICAL {
3068 if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
3069 ret = (n > 0);
3070 }
3071 else {
3072 ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
3073 }
3074 }
3075
3076 return ret;
3077}
3078
3079/* License: Ruby's */
3080static int
3081is_console(SOCKET sock) /* DONT call this for SOCKET! */
3082{
3083 int ret;
3084 DWORD n = 0;
3085 INPUT_RECORD ir;
3086
3087 RUBY_CRITICAL {
3088 ret = (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n));
3089 }
3090
3091 return ret;
3092}
3093
3094/* License: Ruby's */
3095static int
3096is_readable_console(SOCKET sock) /* call this for console only */
3097{
3098 int ret = 0;
3099 DWORD n = 0;
3100 INPUT_RECORD ir;
3101
3102 RUBY_CRITICAL {
3103 if (PeekConsoleInputW((HANDLE)sock, &ir, 1, &n) && n > 0) {
3104 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
3105 ir.Event.KeyEvent.uChar.UnicodeChar) {
3106 ret = 1;
3107 }
3108 else if (ir.EventType == KEY_EVENT && !ir.Event.KeyEvent.bKeyDown &&
3109 ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU /* ALT key */ &&
3110 ir.Event.KeyEvent.uChar.UnicodeChar) {
3111 ret = 1;
3112 }
3113 else {
3114 ReadConsoleInputW((HANDLE)sock, &ir, 1, &n);
3115 }
3116 }
3117 }
3118
3119 return ret;
3120}
3121
3122/* License: Ruby's */
3123static int
3124is_invalid_handle(SOCKET sock)
3125{
3126 return (HANDLE)sock == INVALID_HANDLE_VALUE;
3127}
3128
3129/* License: Artistic or GPL */
3130static int
3131do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3132 struct timeval *timeout)
3133{
3134 int r = 0;
3135
3136 if (nfds == 0) {
3137 if (timeout)
3138 rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
3139 else
3140 rb_w32_sleep(INFINITE);
3141 }
3142 else {
3143 RUBY_CRITICAL {
3144 thread_exclusive(select) {
3145 r = select(nfds, rd, wr, ex, timeout);
3146 }
3147 if (r == SOCKET_ERROR) {
3148 errno = map_errno(WSAGetLastError());
3149 r = -1;
3150 }
3151 }
3152 }
3153
3154 return r;
3155}
3156
3157/*
3158 * rest -= wait
3159 * return 0 if rest is smaller than wait.
3160 */
3161/* License: Ruby's */
3162int
3163rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
3164{
3165 if (rest->tv_sec < wait->tv_sec) {
3166 return 0;
3167 }
3168 while (rest->tv_usec < wait->tv_usec) {
3169 if (rest->tv_sec <= wait->tv_sec) {
3170 return 0;
3171 }
3172 rest->tv_sec -= 1;
3173 rest->tv_usec += 1000 * 1000;
3174 }
3175 rest->tv_sec -= wait->tv_sec;
3176 rest->tv_usec -= wait->tv_usec;
3177 return rest->tv_sec != 0 || rest->tv_usec != 0;
3178}
3179
3180/* License: Ruby's */
3181static inline int
3182compare(const struct timeval *t1, const struct timeval *t2)
3183{
3184 if (t1->tv_sec < t2->tv_sec)
3185 return -1;
3186 if (t1->tv_sec > t2->tv_sec)
3187 return 1;
3188 if (t1->tv_usec < t2->tv_usec)
3189 return -1;
3190 if (t1->tv_usec > t2->tv_usec)
3191 return 1;
3192 return 0;
3193}
3194
3195#undef Sleep
3196
3197int rb_w32_check_interrupt(void *); /* @internal */
3198
3199/* @internal */
3200/* License: Ruby's */
3201int
3202rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3203 struct timeval *timeout, void *th)
3204{
3205 int r;
3206 rb_fdset_t pipe_rd;
3207 rb_fdset_t cons_rd;
3208 rb_fdset_t else_rd;
3209 rb_fdset_t else_wr;
3210 rb_fdset_t except;
3211 int nonsock = 0;
3212 struct timeval limit = {0, 0};
3213
3214 if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
3215 errno = EINVAL;
3216 return -1;
3217 }
3218
3219 if (timeout) {
3220 if (timeout->tv_sec < 0 ||
3221 timeout->tv_usec < 0 ||
3222 timeout->tv_usec >= 1000000) {
3223 errno = EINVAL;
3224 return -1;
3225 }
3226 gettimeofday(&limit, NULL);
3227 limit.tv_sec += timeout->tv_sec;
3228 limit.tv_usec += timeout->tv_usec;
3229 if (limit.tv_usec >= 1000000) {
3230 limit.tv_usec -= 1000000;
3231 limit.tv_sec++;
3232 }
3233 }
3234
3235 // assume else_{rd,wr} (other than socket, pipe reader, console reader)
3236 // are always readable/writable. but this implementation still has
3237 // problem. if pipe's buffer is full, writing to pipe will block
3238 // until some data is read from pipe. but ruby is single threaded system,
3239 // so whole system will be blocked forever.
3240
3241 rb_fd_init(&else_rd);
3242 nonsock += extract_fd(&else_rd, rd, is_not_socket);
3243
3244 rb_fd_init(&else_wr);
3245 nonsock += extract_fd(&else_wr, wr, is_not_socket);
3246
3247 // check invalid handles
3248 if (extract_fd(NULL, else_rd.fdset, is_invalid_handle) > 0 ||
3249 extract_fd(NULL, else_wr.fdset, is_invalid_handle) > 0) {
3250 rb_fd_term(&else_wr);
3251 rb_fd_term(&else_rd);
3252 errno = EBADF;
3253 return -1;
3254 }
3255
3256 rb_fd_init(&pipe_rd);
3257 extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
3258
3259 rb_fd_init(&cons_rd);
3260 extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
3261
3262 rb_fd_init(&except);
3263 extract_fd(&except, ex, is_not_socket); // drop only
3264
3265 r = 0;
3266 if (rd && (int)rd->fd_count > r) r = (int)rd->fd_count;
3267 if (wr && (int)wr->fd_count > r) r = (int)wr->fd_count;
3268 if (ex && (int)ex->fd_count > r) r = (int)ex->fd_count;
3269 if (nfds > r) nfds = r;
3270
3271 {
3272 struct timeval rest;
3273 const struct timeval wait = {0, 10 * 1000}; // 10ms
3274 struct timeval zero = {0, 0}; // 0ms
3275 for (;;) {
3276 if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
3277 r = -1;
3278 break;
3279 }
3280 if (nonsock) {
3281 // modifying {else,pipe,cons}_rd is safe because
3282 // if they are modified, function returns immediately.
3283 extract_fd(&else_rd, pipe_rd.fdset, is_readable_pipe);
3284 extract_fd(&else_rd, cons_rd.fdset, is_readable_console);
3285 }
3286
3287 if (else_rd.fdset->fd_count || else_wr.fdset->fd_count) {
3288 r = do_select(nfds, rd, wr, ex, &zero); // polling
3289 if (r < 0) break; // XXX: should I ignore error and return signaled handles?
3290 r += copy_fd(rd, else_rd.fdset);
3291 r += copy_fd(wr, else_wr.fdset);
3292 if (ex)
3293 r += ex->fd_count;
3294 break;
3295 }
3296 else {
3297 const struct timeval *dowait = &wait;
3298
3299 fd_set orig_rd;
3300 fd_set orig_wr;
3301 fd_set orig_ex;
3302
3303 FD_ZERO(&orig_rd);
3304 FD_ZERO(&orig_wr);
3305 FD_ZERO(&orig_ex);
3306
3307 if (rd) copy_fd(&orig_rd, rd);
3308 if (wr) copy_fd(&orig_wr, wr);
3309 if (ex) copy_fd(&orig_ex, ex);
3310 r = do_select(nfds, rd, wr, ex, &zero); // polling
3311 if (r != 0) break; // signaled or error
3312 if (rd) copy_fd(rd, &orig_rd);
3313 if (wr) copy_fd(wr, &orig_wr);
3314 if (ex) copy_fd(ex, &orig_ex);
3315
3316 if (timeout) {
3317 struct timeval now;
3318 gettimeofday(&now, NULL);
3319 rest = limit;
3320 if (!rb_w32_time_subtract(&rest, &now)) break;
3321 if (compare(&rest, &wait) < 0) dowait = &rest;
3322 }
3323 Sleep(dowait->tv_sec * 1000 + (dowait->tv_usec + 999) / 1000);
3324 }
3325 }
3326 }
3327
3328 rb_fd_term(&except);
3329 rb_fd_term(&cons_rd);
3330 rb_fd_term(&pipe_rd);
3331 rb_fd_term(&else_wr);
3332 rb_fd_term(&else_rd);
3333
3334 return r;
3335}
3336
3337/* License: Ruby's */
3338int WSAAPI
3339rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
3340 struct timeval *timeout)
3341{
3342 return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
3343}
3344
3345/* License: Ruby's */
3346static FARPROC
3347get_wsa_extension_function(SOCKET s, GUID guid)
3348{
3349 DWORD dmy;
3350 FARPROC ptr = NULL;
3351
3352 WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
3353 &ptr, sizeof(ptr), &dmy, NULL, NULL);
3354 if (!ptr)
3355 errno = ENOSYS;
3356 return ptr;
3357}
3358
3359#undef accept
3360
3361/* License: Artistic or GPL */
3362int WSAAPI
3363rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
3364{
3365 SOCKET r;
3366 int fd;
3367
3368 RUBY_CRITICAL {
3369 r = accept(TO_SOCKET(s), addr, addrlen);
3370 if (r != INVALID_SOCKET) {
3371 SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
3372 fd = rb_w32_open_osfhandle((intptr_t)r, O_RDWR|O_BINARY|O_NOINHERIT);
3373 if (fd != -1)
3374 socklist_insert(r, 0);
3375 else
3376 closesocket(r);
3377 }
3378 else {
3379 errno = map_errno(WSAGetLastError());
3380 fd = -1;
3381 }
3382 }
3383 return fd;
3384}
3385
3386#undef bind
3387
3388/* License: Artistic or GPL */
3389int WSAAPI
3390rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
3391{
3392 int r;
3393
3394 RUBY_CRITICAL {
3395 r = bind(TO_SOCKET(s), addr, addrlen);
3396 if (r == SOCKET_ERROR)
3397 errno = map_errno(WSAGetLastError());
3398 }
3399 return r;
3400}
3401
3402#undef connect
3403
3404/* License: Artistic or GPL */
3405int WSAAPI
3406rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
3407{
3408 int r;
3409 RUBY_CRITICAL {
3410 r = connect(TO_SOCKET(s), addr, addrlen);
3411 if (r == SOCKET_ERROR) {
3412 int err = WSAGetLastError();
3413 if (err != WSAEWOULDBLOCK)
3414 errno = map_errno(err);
3415 else
3416 errno = EINPROGRESS;
3417 }
3418 }
3419 return r;
3420}
3421
3422
3423#undef getpeername
3424
3425/* License: Artistic or GPL */
3426int WSAAPI
3427rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
3428{
3429 int r;
3430 RUBY_CRITICAL {
3431 r = getpeername(TO_SOCKET(s), addr, addrlen);
3432 if (r == SOCKET_ERROR)
3433 errno = map_errno(WSAGetLastError());
3434 }
3435 return r;
3436}
3437
3438#undef getsockname
3439
3440/* License: Artistic or GPL */
3441int WSAAPI
3442rb_w32_getsockname(int fd, struct sockaddr *addr, int *addrlen)
3443{
3444 int sock;
3445 int r;
3446 RUBY_CRITICAL {
3447 sock = TO_SOCKET(fd);
3448 r = getsockname(sock, addr, addrlen);
3449 if (r == SOCKET_ERROR) {
3450 DWORD wsaerror = WSAGetLastError();
3451 if (wsaerror == WSAEINVAL) {
3452 int flags;
3453 if (socklist_lookup(sock, &flags)) {
3454 int af = GET_FAMILY(flags);
3455 if (af) {
3456 memset(addr, 0, *addrlen);
3457 addr->sa_family = af;
3458 return 0;
3459 }
3460 }
3461 }
3462 errno = map_errno(wsaerror);
3463 }
3464 }
3465 return r;
3466}
3467
3468#undef getsockopt
3469
3470/* License: Artistic or GPL */
3471int WSAAPI
3472rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
3473{
3474 int r;
3475 RUBY_CRITICAL {
3476 r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3477 if (r == SOCKET_ERROR)
3478 errno = map_errno(WSAGetLastError());
3479 }
3480 return r;
3481}
3482
3483#undef ioctlsocket
3484
3485/* License: Artistic or GPL */
3486int WSAAPI
3487rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
3488{
3489 int r;
3490 RUBY_CRITICAL {
3491 r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3492 if (r == SOCKET_ERROR)
3493 errno = map_errno(WSAGetLastError());
3494 }
3495 return r;
3496}
3497
3498#undef listen
3499
3500/* License: Artistic or GPL */
3501int WSAAPI
3502rb_w32_listen(int s, int backlog)
3503{
3504 int r;
3505 RUBY_CRITICAL {
3506 r = listen(TO_SOCKET(s), backlog);
3507 if (r == SOCKET_ERROR)
3508 errno = map_errno(WSAGetLastError());
3509 }
3510 return r;
3511}
3512
3513#undef recv
3514#undef recvfrom
3515#undef send
3516#undef sendto
3517
3518/* License: Ruby's */
3519static int
3520finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
3521{
3522 DWORD flg;
3523 int err;
3524
3525 if (result != SOCKET_ERROR)
3526 *len = size;
3527 else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
3528 switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
3529 case WAIT_OBJECT_0:
3530 RUBY_CRITICAL {
3531 result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg);
3532 }
3533 if (result) {
3534 result = 0;
3535 *len = size;
3536 break;
3537 }
3538 result = SOCKET_ERROR;
3539 /* thru */
3540 default:
3541 if ((err = WSAGetLastError()) == WSAECONNABORTED && !input)
3542 errno = EPIPE;
3543 else if (err == WSAEMSGSIZE && input) {
3544 result = 0;
3545 *len = size;
3546 break;
3547 }
3548 else
3549 errno = map_errno(err);
3550 /* thru */
3551 case WAIT_OBJECT_0 + 1:
3552 /* interrupted */
3553 *len = -1;
3554 CancelIo((HANDLE)s);
3555 break;
3556 }
3557 }
3558 else {
3559 if (err == WSAECONNABORTED && !input)
3560 errno = EPIPE;
3561 else
3562 errno = map_errno(err);
3563 *len = -1;
3564 }
3565 CloseHandle(wol->hEvent);
3566
3567 return result;
3568}
3569
3570/* License: Artistic or GPL */
3571static int
3572overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
3573 struct sockaddr *addr, int *addrlen)
3574{
3575 int r;
3576 int ret;
3577 int mode = 0;
3578 DWORD flg;
3579 WSAOVERLAPPED wol;
3580 WSABUF wbuf;
3581 SOCKET s;
3582
3583 s = TO_SOCKET(fd);
3584 socklist_lookup(s, &mode);
3585 if (GET_FLAGS(mode) & O_NONBLOCK) {
3586 RUBY_CRITICAL {
3587 if (input) {
3588 if (addr && addrlen)
3589 r = recvfrom(s, buf, len, flags, addr, addrlen);
3590 else
3591 r = recv(s, buf, len, flags);
3592 if (r == SOCKET_ERROR)
3593 errno = map_errno(WSAGetLastError());
3594 }
3595 else {
3596 if (addr && addrlen)
3597 r = sendto(s, buf, len, flags, addr, *addrlen);
3598 else
3599 r = send(s, buf, len, flags);
3600 if (r == SOCKET_ERROR) {
3601 DWORD err = WSAGetLastError();
3602 if (err == WSAECONNABORTED)
3603 errno = EPIPE;
3604 else
3605 errno = map_errno(err);
3606 }
3607 }
3608 }
3609 }
3610 else {
3611 DWORD size;
3612 DWORD rlen;
3613 wbuf.len = len;
3614 wbuf.buf = buf;
3615 memset(&wol, 0, sizeof(wol));
3616 RUBY_CRITICAL {
3617 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3618 if (input) {
3619 flg = flags;
3620 if (addr && addrlen)
3621 ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3622 &wol, NULL);
3623 else
3624 ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3625 }
3626 else {
3627 if (addr && addrlen)
3628 ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3629 &wol, NULL);
3630 else
3631 ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3632 }
3633 }
3634
3635 finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3636 r = (int)rlen;
3637 }
3638
3639 return r;
3640}
3641
3642/* License: Ruby's */
3643int WSAAPI
3644rb_w32_recv(int fd, char *buf, int len, int flags)
3645{
3646 return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
3647}
3648
3649/* License: Ruby's */
3650int WSAAPI
3651rb_w32_recvfrom(int fd, char *buf, int len, int flags,
3652 struct sockaddr *from, int *fromlen)
3653{
3654 return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
3655}
3656
3657/* License: Ruby's */
3658int WSAAPI
3659rb_w32_send(int fd, const char *buf, int len, int flags)
3660{
3661 return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
3662}
3663
3664/* License: Ruby's */
3665int WSAAPI
3666rb_w32_sendto(int fd, const char *buf, int len, int flags,
3667 const struct sockaddr *to, int tolen)
3668{
3669 return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
3670 (struct sockaddr *)to, &tolen);
3671}
3672
3673#if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3674/* License: Ruby's */
3675typedef struct {
3676 SOCKADDR *name;
3677 int namelen;
3678 WSABUF *lpBuffers;
3679 DWORD dwBufferCount;
3680 WSABUF Control;
3681 DWORD dwFlags;
3682} WSAMSG;
3683#endif
3684#ifndef WSAID_WSARECVMSG
3685#define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3686#endif
3687#ifndef WSAID_WSASENDMSG
3688#define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3689#endif
3690
3691/* License: Ruby's */
3692#define msghdr_to_wsamsg(msg, wsamsg) \
3693 do { \
3694 int i; \
3695 (wsamsg)->name = (msg)->msg_name; \
3696 (wsamsg)->namelen = (msg)->msg_namelen; \
3697 (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3698 (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3699 for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3700 (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3701 (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3702 } \
3703 (wsamsg)->Control.buf = (msg)->msg_control; \
3704 (wsamsg)->Control.len = (msg)->msg_controllen; \
3705 (wsamsg)->dwFlags = (msg)->msg_flags; \
3706 } while (0)
3707
3708/* License: Ruby's */
3709int
3710recvmsg(int fd, struct msghdr *msg, int flags)
3711{
3712 typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3713 static WSARecvMsg_t pWSARecvMsg = NULL;
3714 WSAMSG wsamsg;
3715 SOCKET s;
3716 int mode = 0;
3717 DWORD len;
3718 int ret;
3719
3720 s = TO_SOCKET(fd);
3721
3722 if (!pWSARecvMsg) {
3723 static const GUID guid = WSAID_WSARECVMSG;
3724 pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, guid);
3725 if (!pWSARecvMsg)
3726 return -1;
3727 }
3728
3729 msghdr_to_wsamsg(msg, &wsamsg);
3730 wsamsg.dwFlags |= flags;
3731
3732 socklist_lookup(s, &mode);
3733 if (GET_FLAGS(mode) & O_NONBLOCK) {
3734 RUBY_CRITICAL {
3735 if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
3736 errno = map_errno(WSAGetLastError());
3737 len = -1;
3738 }
3739 }
3740 }
3741 else {
3742 DWORD size;
3743 WSAOVERLAPPED wol;
3744 memset(&wol, 0, sizeof(wol));
3745 RUBY_CRITICAL {
3746 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3747 ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3748 }
3749
3750 ret = finish_overlapped_socket(TRUE, s, &wol, ret, &len, size);
3751 }
3752 if (ret == SOCKET_ERROR)
3753 return -1;
3754
3755 /* WSAMSG to msghdr */
3756 msg->msg_name = wsamsg.name;
3757 msg->msg_namelen = wsamsg.namelen;
3758 msg->msg_flags = wsamsg.dwFlags;
3759
3760 return len;
3761}
3762
3763/* License: Ruby's */
3764int
3765sendmsg(int fd, const struct msghdr *msg, int flags)
3766{
3767 typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3768 static WSASendMsg_t pWSASendMsg = NULL;
3769 WSAMSG wsamsg;
3770 SOCKET s;
3771 int mode = 0;
3772 DWORD len;
3773 int ret;
3774
3775 s = TO_SOCKET(fd);
3776
3777 if (!pWSASendMsg) {
3778 static const GUID guid = WSAID_WSASENDMSG;
3779 pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, guid);
3780 if (!pWSASendMsg)
3781 return -1;
3782 }
3783
3784 msghdr_to_wsamsg(msg, &wsamsg);
3785
3786 socklist_lookup(s, &mode);
3787 if (GET_FLAGS(mode) & O_NONBLOCK) {
3788 RUBY_CRITICAL {
3789 if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
3790 errno = map_errno(WSAGetLastError());
3791 len = -1;
3792 }
3793 }
3794 }
3795 else {
3796 DWORD size;
3797 WSAOVERLAPPED wol;
3798 memset(&wol, 0, sizeof(wol));
3799 RUBY_CRITICAL {
3800 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3801 ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3802 }
3803
3804 finish_overlapped_socket(FALSE, s, &wol, ret, &len, size);
3805 }
3806
3807 return len;
3808}
3809
3810#undef setsockopt
3811
3812/* License: Artistic or GPL */
3813int WSAAPI
3814rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
3815{
3816 int r;
3817 RUBY_CRITICAL {
3818 r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3819 if (r == SOCKET_ERROR)
3820 errno = map_errno(WSAGetLastError());
3821 }
3822 return r;
3823}
3824
3825#undef shutdown
3826
3827/* License: Artistic or GPL */
3828int WSAAPI
3829rb_w32_shutdown(int s, int how)
3830{
3831 int r;
3832 RUBY_CRITICAL {
3833 r = shutdown(TO_SOCKET(s), how);
3834 if (r == SOCKET_ERROR)
3835 errno = map_errno(WSAGetLastError());
3836 }
3837 return r;
3838}
3839
3840/* License: Ruby's */
3841static SOCKET
3842open_ifs_socket(int af, int type, int protocol)
3843{
3844 unsigned long proto_buffers_len = 0;
3845 int error_code;
3846 SOCKET out = INVALID_SOCKET;
3847
3848 if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3849 error_code = WSAGetLastError();
3850 if (error_code == WSAENOBUFS) {
3851 WSAPROTOCOL_INFO *proto_buffers;
3852 int protocols_available = 0;
3853
3854 proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3855 if (!proto_buffers) {
3856 WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3857 return INVALID_SOCKET;
3858 }
3859
3860 protocols_available =
3861 WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3862 if (protocols_available != SOCKET_ERROR) {
3863 int i;
3864 for (i = 0; i < protocols_available; i++) {
3865 if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3866 (type != proto_buffers[i].iSocketType) ||
3867 (protocol != 0 && protocol != proto_buffers[i].iProtocol))
3868 continue;
3869
3870 if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3871 continue;
3872
3873 out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
3874 WSA_FLAG_OVERLAPPED);
3875 break;
3876 }
3877 if (out == INVALID_SOCKET)
3878 out = WSASocket(af, type, protocol, NULL, 0, 0);
3879 if (out != INVALID_SOCKET)
3880 SetHandleInformation((HANDLE)out, HANDLE_FLAG_INHERIT, 0);
3881 }
3882
3883 free(proto_buffers);
3884 }
3885 }
3886
3887 return out;
3888}
3889
3890#undef socket
3891
3892/* License: Artistic or GPL */
3893int WSAAPI
3894rb_w32_socket(int af, int type, int protocol)
3895{
3896 SOCKET s;
3897 int fd;
3898
3899 RUBY_CRITICAL {
3900 s = open_ifs_socket(af, type, protocol);
3901 if (s == INVALID_SOCKET) {
3902 errno = map_errno(WSAGetLastError());
3903 fd = -1;
3904 }
3905 else {
3906 fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3907 if (fd != -1)
3908 socklist_insert(s, MAKE_SOCKDATA(af, 0));
3909 else
3910 closesocket(s);
3911 }
3912 }
3913 return fd;
3914}
3915
3916#undef gethostbyaddr
3917
3918/* License: Artistic or GPL */
3919struct hostent * WSAAPI
3920rb_w32_gethostbyaddr(const char *addr, int len, int type)
3921{
3922 struct hostent *r;
3923 RUBY_CRITICAL {
3924 r = gethostbyaddr(addr, len, type);
3925 if (r == NULL)
3926 errno = map_errno(WSAGetLastError());
3927 }
3928 return r;
3929}
3930
3931#undef gethostbyname
3932
3933/* License: Artistic or GPL */
3934struct hostent * WSAAPI
3935rb_w32_gethostbyname(const char *name)
3936{
3937 struct hostent *r;
3938 RUBY_CRITICAL {
3939 r = gethostbyname(name);
3940 if (r == NULL)
3941 errno = map_errno(WSAGetLastError());
3942 }
3943 return r;
3944}
3945
3946#undef gethostname
3947
3948/* License: Artistic or GPL */
3949int WSAAPI
3950rb_w32_gethostname(char *name, int len)
3951{
3952 int r;
3953 RUBY_CRITICAL {
3954 r = gethostname(name, len);
3955 if (r == SOCKET_ERROR)
3956 errno = map_errno(WSAGetLastError());
3957 }
3958 return r;
3959}
3960
3961#undef getprotobyname
3962
3963/* License: Artistic or GPL */
3964struct protoent * WSAAPI
3965rb_w32_getprotobyname(const char *name)
3966{
3967 struct protoent *r;
3968 RUBY_CRITICAL {
3969 r = getprotobyname(name);
3970 if (r == NULL)
3971 errno = map_errno(WSAGetLastError());
3972 }
3973 return r;
3974}
3975
3976#undef getprotobynumber
3977
3978/* License: Artistic or GPL */
3979struct protoent * WSAAPI
3980rb_w32_getprotobynumber(int num)
3981{
3982 struct protoent *r;
3983 RUBY_CRITICAL {
3984 r = getprotobynumber(num);
3985 if (r == NULL)
3986 errno = map_errno(WSAGetLastError());
3987 }
3988 return r;
3989}
3990
3991#undef getservbyname
3992
3993/* License: Artistic or GPL */
3994struct servent * WSAAPI
3995rb_w32_getservbyname(const char *name, const char *proto)
3996{
3997 struct servent *r;
3998 RUBY_CRITICAL {
3999 r = getservbyname(name, proto);
4000 if (r == NULL)
4001 errno = map_errno(WSAGetLastError());
4002 }
4003 return r;
4004}
4005
4006#undef getservbyport
4007
4008/* License: Artistic or GPL */
4009struct servent * WSAAPI
4010rb_w32_getservbyport(int port, const char *proto)
4011{
4012 struct servent *r;
4013 RUBY_CRITICAL {
4014 r = getservbyport(port, proto);
4015 if (r == NULL)
4016 errno = map_errno(WSAGetLastError());
4017 }
4018 return r;
4019}
4020
4021#ifdef HAVE_AFUNIX_H
4022
4023/* License: Ruby's */
4024static size_t
4025socketpair_unix_path(struct sockaddr_un *sock_un)
4026{
4027 SOCKET listener;
4028 WCHAR wpath[sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path)] = L"";
4029
4030 /* AF_UNIX/SOCK_STREAM became available in Windows 10
4031 * See https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows
4032 */
4033 listener = socket(AF_UNIX, SOCK_STREAM, 0);
4034 if (listener == INVALID_SOCKET)
4035 return 0;
4036
4037 memset(sock_un, 0, sizeof(*sock_un));
4038 sock_un->sun_family = AF_UNIX;
4039
4040 /* Abstract sockets (filesystem-independent) don't work, contrary to
4041 * the claims of the aforementioned blog post:
4042 * https://github.com/microsoft/WSL/issues/4240#issuecomment-549663217
4043 *
4044 * So we must use a named path, and that comes with all the attendant
4045 * problems of permissions and collisions. Trying various temporary
4046 * directories and putting high-res time and PID in the filename.
4047 */
4048 for (int try = 0; ; try++) {
4049 LARGE_INTEGER ticks;
4050 size_t path_len = 0;
4051 const size_t maxpath = sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path);
4052
4053 switch (try) {
4054 case 0:
4055 /* user temp dir from TMP or TEMP env var, it ends with a backslash */
4056 path_len = GetTempPathW(maxpath, wpath);
4057 break;
4058 case 1:
4059 wcsncpy(wpath, L"C:/Temp/", maxpath);
4060 path_len = lstrlenW(wpath);
4061 break;
4062 case 2:
4063 /* Current directory */
4064 path_len = 0;
4065 break;
4066 case 3:
4067 closesocket(listener);
4068 return 0;
4069 }
4070
4071 /* Windows UNIXSocket implementation expects UTF-8 instead of UTF16 */
4072 path_len = WideCharToMultiByte(CP_UTF8, 0, wpath, path_len, sock_un->sun_path, maxpath, NULL, NULL);
4073 QueryPerformanceCounter(&ticks);
4074 path_len += snprintf(sock_un->sun_path + path_len,
4075 maxpath - path_len,
4076 "%lld-%ld.($)",
4077 ticks.QuadPart,
4078 GetCurrentProcessId());
4079
4080 /* Convert to UTF16 for DeleteFileW */
4081 MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
4082
4083 if (bind(listener, (struct sockaddr *)sock_un, sizeof(*sock_un)) != SOCKET_ERROR)
4084 break;
4085 }
4086 closesocket(listener);
4087 DeleteFileW(wpath);
4088 return sizeof(*sock_un);
4089}
4090#endif
4091
4092/* License: Ruby's */
4093static int
4094socketpair_internal(int af, int type, int protocol, SOCKET *sv)
4095{
4096 SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
4097 struct sockaddr_in sock_in4;
4098
4099#ifdef INET6
4100 struct sockaddr_in6 sock_in6;
4101#endif
4102
4103#ifdef HAVE_AFUNIX_H
4104 struct sockaddr_un sock_un = {0, {0}};
4105 WCHAR wpath[sizeof(sock_un.sun_path)/sizeof(*sock_un.sun_path)] = L"";
4106#endif
4107
4108 struct sockaddr *addr;
4109 int ret = -1;
4110 int len;
4111
4112 switch (af) {
4113 case AF_INET:
4114#if defined PF_INET && PF_INET != AF_INET
4115 case PF_INET:
4116#endif
4117 sock_in4.sin_family = AF_INET;
4118 sock_in4.sin_port = 0;
4119 sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4120 addr = (struct sockaddr *)&sock_in4;
4121 len = sizeof(sock_in4);
4122 break;
4123#ifdef INET6
4124 case AF_INET6:
4125 memset(&sock_in6, 0, sizeof(sock_in6));
4126 sock_in6.sin6_family = AF_INET6;
4127 sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
4128 addr = (struct sockaddr *)&sock_in6;
4129 len = sizeof(sock_in6);
4130 break;
4131#endif
4132#ifdef HAVE_AFUNIX_H
4133 case AF_UNIX:
4134 addr = (struct sockaddr *)&sock_un;
4135 len = socketpair_unix_path(&sock_un);
4136 MultiByteToWideChar(CP_UTF8, 0, sock_un.sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath));
4137 if (len)
4138 break;
4139 /* fall through */
4140#endif
4141 default:
4142 errno = EAFNOSUPPORT;
4143 return -1;
4144 }
4145 if (type != SOCK_STREAM) {
4146 errno = EPROTOTYPE;
4147 return -1;
4148 }
4149
4150 sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
4151 sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
4152 RUBY_CRITICAL {
4153 do {
4154 svr = open_ifs_socket(af, type, protocol);
4155 if (svr == INVALID_SOCKET)
4156 break;
4157 if (bind(svr, addr, len) < 0)
4158 break;
4159 if (getsockname(svr, addr, &len) < 0)
4160 break;
4161 if (type == SOCK_STREAM)
4162 listen(svr, 5);
4163
4164 w = open_ifs_socket(af, type, protocol);
4165 if (w == INVALID_SOCKET)
4166 break;
4167 if (connect(w, addr, len) < 0)
4168 break;
4169
4170 r = accept(svr, addr, &len);
4171 if (r == INVALID_SOCKET)
4172 break;
4173 SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
4174
4175 ret = 0;
4176 } while (0);
4177
4178 if (ret < 0) {
4179 errno = map_errno(WSAGetLastError());
4180 if (r != INVALID_SOCKET)
4181 closesocket(r);
4182 if (w != INVALID_SOCKET)
4183 closesocket(w);
4184 }
4185 else {
4186 sv[0] = r;
4187 sv[1] = w;
4188 }
4189 if (svr != INVALID_SOCKET)
4190 closesocket(svr);
4191#ifdef HAVE_AFUNIX_H
4192 if (sock_un.sun_family == AF_UNIX)
4193 DeleteFileW(wpath);
4194#endif
4195 }
4196
4197 return ret;
4198}
4199
4200/* License: Ruby's */
4201int
4202socketpair(int af, int type, int protocol, int *sv)
4203{
4204 SOCKET pair[2];
4205
4206 if (socketpair_internal(af, type, protocol, pair) < 0)
4207 return -1;
4208 sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
4209 if (sv[0] == -1) {
4210 closesocket(pair[0]);
4211 closesocket(pair[1]);
4212 return -1;
4213 }
4214 sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
4215 if (sv[1] == -1) {
4216 rb_w32_close(sv[0]);
4217 closesocket(pair[1]);
4218 return -1;
4219 }
4220 socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
4221 socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
4222
4223 return 0;
4224}
4225
4226#if !defined(_MSC_VER) || _MSC_VER >= 1400
4227/* License: Ruby's */
4228static void
4229str2guid(const char *str, GUID *guid)
4230{
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))
4233 char *end;
4234 int i;
4235 if (*str == '{') str++;
4236 guid->Data1 = (long)strtoul(str, &end, 16);
4237 str += 9;
4238 guid->Data2 = (unsigned short)strtoul(str, &end, 16);
4239 str += 5;
4240 guid->Data3 = (unsigned short)strtoul(str, &end, 16);
4241 str += 5;
4242 guid->Data4[0] = hex2byte(str);
4243 str += 2;
4244 guid->Data4[1] = hex2byte(str);
4245 str += 3;
4246 for (i = 0; i < 6; i++) {
4247 guid->Data4[i + 2] = hex2byte(str);
4248 str += 2;
4249 }
4250}
4251
4252/* License: Ruby's */
4253#ifndef HAVE_TYPE_NET_LUID
4254 typedef struct {
4255 uint64_t Value;
4256 struct {
4257 uint64_t Reserved :24;
4258 uint64_t NetLuidIndex :24;
4259 uint64_t IfType :16;
4260 } Info;
4261 } NET_LUID;
4262#endif
4263
4264int
4265getifaddrs(struct ifaddrs **ifap)
4266{
4267 ULONG size = 0;
4268 ULONG ret;
4269 IP_ADAPTER_ADDRESSES *root, *addr;
4270 struct ifaddrs *prev;
4271
4272 ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
4273 if (ret != ERROR_BUFFER_OVERFLOW) {
4274 errno = map_errno(ret);
4275 return -1;
4276 }
4277 root = ruby_xmalloc(size);
4278 ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, root, &size);
4279 if (ret != ERROR_SUCCESS) {
4280 errno = map_errno(ret);
4281 ruby_xfree(root);
4282 return -1;
4283 }
4284
4285 for (prev = NULL, addr = root; addr; addr = addr->Next) {
4286 struct ifaddrs *ifa = ruby_xcalloc(1, sizeof(*ifa));
4287 char name[IFNAMSIZ];
4288 GUID guid;
4289 NET_LUID luid;
4290
4291 if (prev)
4292 prev->ifa_next = ifa;
4293 else
4294 *ifap = ifa;
4295
4296 str2guid(addr->AdapterName, &guid);
4297 if (ConvertInterfaceGuidToLuid(&guid, &luid) == NO_ERROR &&
4298 ConvertInterfaceLuidToNameA(&luid, name, sizeof(name)) == NO_ERROR) {
4299 ifa->ifa_name = ruby_strdup(name);
4300 }
4301 else {
4302 ifa->ifa_name = ruby_strdup(addr->AdapterName);
4303 }
4304
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;
4309
4310 if (addr->FirstUnicastAddress) {
4311 IP_ADAPTER_UNICAST_ADDRESS *cur;
4312 int added = 0;
4313 for (cur = addr->FirstUnicastAddress; cur; cur = cur->Next) {
4314 if (cur->Flags & IP_ADAPTER_ADDRESS_TRANSIENT ||
4315 cur->DadState == IpDadStateDeprecated) {
4316 continue;
4317 }
4318 if (added) {
4319 prev = ifa;
4320 ifa = ruby_xcalloc(1, sizeof(*ifa));
4321 prev->ifa_next = ifa;
4322 ifa->ifa_name = ruby_strdup(prev->ifa_name);
4323 ifa->ifa_flags = prev->ifa_flags;
4324 }
4325 ifa->ifa_addr = ruby_xmalloc(cur->Address.iSockaddrLength);
4326 memcpy(ifa->ifa_addr, cur->Address.lpSockaddr,
4327 cur->Address.iSockaddrLength);
4328 added = 1;
4329 }
4330 }
4331 }
4332
4333 prev = ifa;
4334 }
4335
4336 ruby_xfree(root);
4337 return 0;
4338}
4339
4340/* License: Ruby's */
4341void
4342freeifaddrs(struct ifaddrs *ifp)
4343{
4344 while (ifp) {
4345 struct ifaddrs *next = ifp->ifa_next;
4346 ruby_xfree(ifp->ifa_addr);
4347 ruby_xfree(ifp->ifa_name);
4348 ruby_xfree(ifp);
4349 ifp = next;
4350 }
4351}
4352#endif
4353
4354#if 0 // Have never been used
4355//
4356// Networking stubs
4357//
4358
4359void endhostent(void) {}
4360void endnetent(void) {}
4361void endprotoent(void) {}
4362void endservent(void) {}
4363
4364struct netent *getnetent (void) {return (struct netent *) NULL;}
4365
4366struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
4367
4368struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
4369
4370struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
4371
4372struct servent *getservent (void) {return (struct servent *) NULL;}
4373
4374void sethostent (int stayopen) {}
4375
4376void setnetent (int stayopen) {}
4377
4378void setprotoent (int stayopen) {}
4379
4380void setservent (int stayopen) {}
4381#endif
4382
4383int rb_w32_set_nonblock2(int fd, int nonblock);
4384
4385/* License: Ruby's */
4386static int
4387setfl(SOCKET sock, int arg)
4388{
4389 int ret;
4390 int af = 0;
4391 int flag = 0;
4392 u_long ioctlArg;
4393
4394 socklist_lookup(sock, &flag);
4395 af = GET_FAMILY(flag);
4396 flag = GET_FLAGS(flag);
4397 if (arg & O_NONBLOCK) {
4398 flag |= O_NONBLOCK;
4399 ioctlArg = 1;
4400 }
4401 else {
4402 flag &= ~O_NONBLOCK;
4403 ioctlArg = 0;
4404 }
4405 RUBY_CRITICAL {
4406 ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
4407 if (ret == 0)
4408 socklist_insert(sock, MAKE_SOCKDATA(af, flag));
4409 else
4410 errno = map_errno(WSAGetLastError());
4411 }
4412
4413 return ret;
4414}
4415
4416/* License: Ruby's */
4417static int
4418dupfd(HANDLE hDup, int flags, int minfd)
4419{
4420 int save_errno;
4421 int ret;
4422 int fds[32];
4423 int filled = 0;
4424
4425 do {
4426 ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
4427 if (ret == -1) {
4428 goto close_fds_and_return;
4429 }
4430 if (ret >= minfd) {
4431 goto close_fds_and_return;
4432 }
4433 fds[filled++] = ret;
4434 } while (filled < (int)numberof(fds));
4435
4436 ret = dupfd(hDup, flags, minfd);
4437
4438 close_fds_and_return:
4439 save_errno = errno;
4440 while (filled > 0) {
4441 int fd = fds[--filled];
4442 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
4443 close(fd);
4444 }
4445 errno = save_errno;
4446
4447 return ret;
4448}
4449
4450/* License: Ruby's */
4451int
4452fcntl(int fd, int cmd, ...)
4453{
4454 va_list va;
4455 int arg;
4456 DWORD flag;
4457
4458 switch (cmd) {
4459 case F_SETFL: {
4460 va_start(va, cmd);
4461 arg = va_arg(va, int);
4462 va_end(va);
4463 return rb_w32_set_nonblock2(fd, arg);
4464 }
4465 case F_DUPFD: case F_DUPFD_CLOEXEC: {
4466 int ret;
4467 HANDLE hDup;
4468 flag = _osfile(fd);
4469 if (!(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
4470 GetCurrentProcess(), &hDup, 0L,
4471 cmd == F_DUPFD && !(flag & FNOINHERIT),
4472 DUPLICATE_SAME_ACCESS))) {
4473 errno = map_errno(GetLastError());
4474 return -1;
4475 }
4476
4477 va_start(va, cmd);
4478 arg = va_arg(va, int);
4479 va_end(va);
4480
4481 if (cmd != F_DUPFD)
4482 flag |= FNOINHERIT;
4483 else
4484 flag &= ~FNOINHERIT;
4485 if ((ret = dupfd(hDup, flag, arg)) == -1)
4486 CloseHandle(hDup);
4487 return ret;
4488 }
4489 case F_GETFD: {
4490 SIGNED_VALUE h = _get_osfhandle(fd);
4491 if (h == -1) return -1;
4492 if (!GetHandleInformation((HANDLE)h, &flag)) {
4493 errno = map_errno(GetLastError());
4494 return -1;
4495 }
4496 return (flag & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
4497 }
4498 case F_SETFD: {
4499 SIGNED_VALUE h = _get_osfhandle(fd);
4500 if (h == -1) return -1;
4501 va_start(va, cmd);
4502 arg = va_arg(va, int);
4503 va_end(va);
4504 if (!SetHandleInformation((HANDLE)h, HANDLE_FLAG_INHERIT,
4505 (arg & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT)) {
4506 errno = map_errno(GetLastError());
4507 return -1;
4508 }
4509 if (arg & FD_CLOEXEC)
4510 _osfile(fd) |= FNOINHERIT;
4511 else
4512 _osfile(fd) &= ~FNOINHERIT;
4513 return 0;
4514 }
4515 default:
4516 errno = EINVAL;
4517 return -1;
4518 }
4519}
4520
4521/* License: Ruby's */
4522int
4523rb_w32_set_nonblock2(int fd, int nonblock)
4524{
4525 SOCKET sock = TO_SOCKET(fd);
4526 if (is_socket(sock)) {
4527 return setfl(sock, nonblock ? O_NONBLOCK : 0);
4528 }
4529 else if (is_pipe(sock)) {
4530 DWORD state;
4531 if (!GetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL, NULL, NULL, 0)) {
4532 errno = map_errno(GetLastError());
4533 return -1;
4534 }
4535 if (nonblock) {
4536 state |= PIPE_NOWAIT;
4537 }
4538 else {
4539 state &= ~PIPE_NOWAIT;
4540 }
4541 if (!SetNamedPipeHandleState((HANDLE)sock, &state, NULL, NULL)) {
4542 errno = map_errno(GetLastError());
4543 return -1;
4544 }
4545 return 0;
4546 }
4547 else {
4548 errno = EBADF;
4549 return -1;
4550 }
4551}
4552
4553int
4554rb_w32_set_nonblock(int fd)
4555{
4556 return rb_w32_set_nonblock2(fd, TRUE);
4557}
4558
4559#ifndef WNOHANG
4560#define WNOHANG -1
4561#endif
4562
4563/* License: Ruby's */
4564static rb_pid_t
4565poll_child_status(struct ChildRecord *child, int *stat_loc)
4566{
4567 DWORD exitcode;
4568 DWORD err;
4569
4570 if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
4571 /* If an error occurred, return immediately. */
4572 err = GetLastError();
4573 switch (err) {
4574 case ERROR_INVALID_PARAMETER:
4575 errno = ECHILD;
4576 break;
4577 case ERROR_INVALID_HANDLE:
4578 errno = EINVAL;
4579 break;
4580 default:
4581 errno = map_errno(err);
4582 break;
4583 }
4584 error_exit:
4585 CloseChildHandle(child);
4586 return -1;
4587 }
4588 if (exitcode != STILL_ACTIVE) {
4589 rb_pid_t pid;
4590 /* If already died, wait process's real termination. */
4591 if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
4592 goto error_exit;
4593 }
4594 pid = child->pid;
4595 CloseChildHandle(child);
4596 if (stat_loc) {
4597 *stat_loc = exitcode << 8;
4598 if (exitcode & 0xC0000000) {
4599 static const struct {
4600 DWORD status;
4601 int sig;
4602 } table[] = {
4603 {STATUS_ACCESS_VIOLATION, SIGSEGV},
4604 {STATUS_ILLEGAL_INSTRUCTION, SIGILL},
4605 {STATUS_PRIVILEGED_INSTRUCTION, SIGILL},
4606 {STATUS_FLOAT_DENORMAL_OPERAND, SIGFPE},
4607 {STATUS_FLOAT_DIVIDE_BY_ZERO, SIGFPE},
4608 {STATUS_FLOAT_INEXACT_RESULT, SIGFPE},
4609 {STATUS_FLOAT_INVALID_OPERATION, SIGFPE},
4610 {STATUS_FLOAT_OVERFLOW, SIGFPE},
4611 {STATUS_FLOAT_STACK_CHECK, SIGFPE},
4612 {STATUS_FLOAT_UNDERFLOW, SIGFPE},
4613#ifdef STATUS_FLOAT_MULTIPLE_FAULTS
4614 {STATUS_FLOAT_MULTIPLE_FAULTS, SIGFPE},
4615#endif
4616#ifdef STATUS_FLOAT_MULTIPLE_TRAPS
4617 {STATUS_FLOAT_MULTIPLE_TRAPS, SIGFPE},
4618#endif
4619 {STATUS_CONTROL_C_EXIT, SIGINT},
4620 };
4621 int i;
4622 for (i = 0; i < (int)numberof(table); i++) {
4623 if (table[i].status == exitcode) {
4624 *stat_loc |= table[i].sig;
4625 break;
4626 }
4627 }
4628 // if unknown status, assume SEGV
4629 if (i >= (int)numberof(table))
4630 *stat_loc |= SIGSEGV;
4631 }
4632 }
4633 return pid;
4634 }
4635 return 0;
4636}
4637
4638/* License: Artistic or GPL */
4639rb_pid_t
4640waitpid(rb_pid_t pid, int *stat_loc, int options)
4641{
4642 DWORD timeout;
4643
4644 /* Artistic or GPL part start */
4645 if (options == WNOHANG) {
4646 timeout = 0;
4647 }
4648 else {
4649 timeout = INFINITE;
4650 }
4651 /* Artistic or GPL part end */
4652
4653 if (pid == -1) {
4654 int count = 0;
4655 int ret;
4656 HANDLE events[MAXCHILDNUM];
4657 struct ChildRecord* cause;
4658
4659 FOREACH_CHILD(child) {
4660 if (!child->pid || child->pid < 0) continue;
4661 if ((pid = poll_child_status(child, stat_loc))) return pid;
4662 events[count++] = child->hProcess;
4663 } END_FOREACH_CHILD;
4664 if (!count) {
4665 errno = ECHILD;
4666 return -1;
4667 }
4668
4669 ret = rb_w32_wait_events_blocking(events, count, timeout);
4670 if (ret == WAIT_TIMEOUT) return 0;
4671 if ((ret -= WAIT_OBJECT_0) == count) {
4672 return -1;
4673 }
4674 if (ret > count) {
4675 errno = map_errno(GetLastError());
4676 return -1;
4677 }
4678
4679 cause = FindChildSlotByHandle(events[ret]);
4680 if (!cause) {
4681 errno = ECHILD;
4682 return -1;
4683 }
4684 return poll_child_status(cause, stat_loc);
4685 }
4686 else {
4687 struct ChildRecord* child = FindChildSlot(pid);
4688 int retried = 0;
4689 if (!child) {
4690 errno = ECHILD;
4691 return -1;
4692 }
4693
4694 while (!(pid = poll_child_status(child, stat_loc))) {
4695 /* wait... */
4696 int ret = rb_w32_wait_events_blocking(&child->hProcess, 1, timeout);
4697 if (ret == WAIT_OBJECT_0 + 1) return -1; /* maybe EINTR */
4698 if (ret != WAIT_OBJECT_0) {
4699 /* still active */
4700 if (options & WNOHANG) {
4701 pid = 0;
4702 break;
4703 }
4704 ++retried;
4705 }
4706 }
4707 if (pid == -1 && retried) pid = 0;
4708 }
4709
4710 return pid;
4711}
4712
4713#include <sys/timeb.h>
4714
4715/* License: Ruby's */
4716/* split FILETIME value into UNIX time and sub-seconds in NT ticks */
4717static time_t
4718filetime_split(const FILETIME* ft, long *subsec)
4719{
4720 ULARGE_INTEGER tmp;
4721 unsigned LONG_LONG lt;
4722 const unsigned LONG_LONG subsec_unit = (unsigned LONG_LONG)10 * 1000 * 1000;
4723
4724 tmp.LowPart = ft->dwLowDateTime;
4725 tmp.HighPart = ft->dwHighDateTime;
4726 lt = tmp.QuadPart;
4727
4728 /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
4729 convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
4730 the first leap second is at 1972/06/30, so we doesn't need to think
4731 about it. */
4732 lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * subsec_unit;
4733
4734 *subsec = (long)(lt % subsec_unit);
4735 return (time_t)(lt / subsec_unit);
4736}
4737
4738/* License: Ruby's */
4739int __cdecl
4740gettimeofday(struct timeval *tv, struct timezone *tz)
4741{
4742 FILETIME ft;
4743 long subsec;
4744
4745 GetSystemTimePreciseAsFileTime(&ft);
4746 tv->tv_sec = filetime_split(&ft, &subsec);
4747 tv->tv_usec = subsec / 10;
4748
4749 return 0;
4750}
4751
4752/* License: Ruby's */
4753int
4754clock_gettime(clockid_t clock_id, struct timespec *sp)
4755{
4756 switch (clock_id) {
4757 case CLOCK_REALTIME:
4758 {
4759 FILETIME ft;
4760 long subsec;
4761
4762 GetSystemTimePreciseAsFileTime(&ft);
4763 sp->tv_sec = filetime_split(&ft, &subsec);
4764 sp->tv_nsec = subsec * 100;
4765 return 0;
4766 }
4767 case CLOCK_MONOTONIC:
4768 {
4769 LARGE_INTEGER freq;
4770 LARGE_INTEGER count;
4771 if (!QueryPerformanceFrequency(&freq)) {
4772 errno = map_errno(GetLastError());
4773 return -1;
4774 }
4775 if (!QueryPerformanceCounter(&count)) {
4776 errno = map_errno(GetLastError());
4777 return -1;
4778 }
4779 sp->tv_sec = count.QuadPart / freq.QuadPart;
4780 if (freq.QuadPart < 1000000000)
4781 sp->tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
4782 else
4783 sp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
4784 return 0;
4785 }
4786 default:
4787 errno = EINVAL;
4788 return -1;
4789 }
4790}
4791
4792/* License: Ruby's */
4793int
4794clock_getres(clockid_t clock_id, struct timespec *sp)
4795{
4796 switch (clock_id) {
4797 case CLOCK_REALTIME:
4798 {
4799 sp->tv_sec = 0;
4800 sp->tv_nsec = 1000;
4801 return 0;
4802 }
4803 case CLOCK_MONOTONIC:
4804 {
4805 LARGE_INTEGER freq;
4806 if (!QueryPerformanceFrequency(&freq)) {
4807 errno = map_errno(GetLastError());
4808 return -1;
4809 }
4810 sp->tv_sec = 0;
4811 sp->tv_nsec = (long)(1000000000.0 / freq.QuadPart);
4812 return 0;
4813 }
4814 default:
4815 errno = EINVAL;
4816 return -1;
4817 }
4818}
4819
4820/* License: Ruby's */
4821static char *
4822w32_getcwd(char *buffer, int size, UINT cp, void *alloc(int, void *), void *arg)
4823{
4824 WCHAR *p;
4825 int wlen, len;
4826
4827 len = GetCurrentDirectoryW(0, NULL);
4828 if (!len) {
4829 errno = map_errno(GetLastError());
4830 return NULL;
4831 }
4832
4833 if (buffer && size < len) {
4834 errno = ERANGE;
4835 return NULL;
4836 }
4837
4838 p = ALLOCA_N(WCHAR, len);
4839 if (!GetCurrentDirectoryW(len, p)) {
4840 errno = map_errno(GetLastError());
4841 return NULL;
4842 }
4843
4844 wlen = translate_wchar(p, L'\\', L'/') - p + 1;
4845 len = WideCharToMultiByte(cp, 0, p, wlen, NULL, 0, NULL, NULL);
4846 if (buffer) {
4847 if (size < len) {
4848 errno = ERANGE;
4849 return NULL;
4850 }
4851 }
4852 else {
4853 buffer = (*alloc)(len, arg);
4854 if (!buffer) {
4855 errno = ENOMEM;
4856 return NULL;
4857 }
4858 }
4859 WideCharToMultiByte(cp, 0, p, wlen, buffer, len, NULL, NULL);
4860
4861 return buffer;
4862}
4863
4864/* License: Ruby's */
4865static void *
4866getcwd_alloc(int size, void *dummy)
4867{
4868 return malloc(size);
4869}
4870
4871/* License: Ruby's */
4872char *
4873rb_w32_getcwd(char *buffer, int size)
4874{
4875 return w32_getcwd(buffer, size, filecp(), getcwd_alloc, NULL);
4876}
4877
4878/* License: Ruby's */
4879char *
4880rb_w32_ugetcwd(char *buffer, int size)
4881{
4882 return w32_getcwd(buffer, size, CP_UTF8, getcwd_alloc, NULL);
4883}
4884
4885/* License: Ruby's */
4886static void *
4887getcwd_value(int size, void *arg)
4888{
4889 VALUE str = *(VALUE *)arg = rb_utf8_str_new(0, size - 1);
4890 return RSTRING_PTR(str);
4891}
4892
4893/* License: Ruby's */
4894VALUE
4895rb_dir_getwd_ospath(void)
4896{
4897 VALUE cwd = Qnil;
4898 w32_getcwd(NULL, 0, CP_UTF8, getcwd_value, &cwd);
4899 return cwd;
4900}
4901
4902/* License: Artistic or GPL */
4903int
4904chown(const char *path, int owner, int group)
4905{
4906 return 0;
4907}
4908
4909/* License: Artistic or GPL */
4910int
4911rb_w32_uchown(const char *path, int owner, int group)
4912{
4913 return 0;
4914}
4915
4916int
4917lchown(const char *path, int owner, int group)
4918{
4919 return 0;
4920}
4921
4922int
4923rb_w32_ulchown(const char *path, int owner, int group)
4924{
4925 return 0;
4926}
4927
4928/* License: Ruby's */
4929int
4930kill(rb_pid_t pid, int sig)
4931{
4932 int ret = 0;
4933 DWORD err;
4934
4935 if (pid < 0 || (pid == 0 && sig != SIGINT)) {
4936 errno = EINVAL;
4937 return -1;
4938 }
4939
4940 if ((unsigned int)pid == GetCurrentProcessId() &&
4941 (sig != 0 && sig != SIGKILL)) {
4942 if ((ret = raise(sig)) != 0) {
4943 /* MSVCRT doesn't set errno... */
4944 errno = EINVAL;
4945 }
4946 return ret;
4947 }
4948
4949 switch (sig) {
4950 case 0:
4951 RUBY_CRITICAL {
4952 HANDLE hProc =
4953 OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4954 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4955 if (GetLastError() == ERROR_INVALID_PARAMETER) {
4956 errno = ESRCH;
4957 }
4958 else {
4959 errno = EPERM;
4960 }
4961 ret = -1;
4962 }
4963 else {
4964 CloseHandle(hProc);
4965 }
4966 }
4967 break;
4968
4969 case SIGINT:
4970 RUBY_CRITICAL {
4971 DWORD ctrlEvent = CTRL_C_EVENT;
4972 if (pid != 0) {
4973 /* CTRL+C signal cannot be generated for process groups.
4974 * Instead, we use CTRL+BREAK signal. */
4975 ctrlEvent = CTRL_BREAK_EVENT;
4976 }
4977 if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
4978 if ((err = GetLastError()) == 0)
4979 errno = EPERM;
4980 else
4981 errno = map_errno(GetLastError());
4982 ret = -1;
4983 }
4984 }
4985 break;
4986
4987 case SIGKILL:
4988 RUBY_CRITICAL {
4989 HANDLE hProc;
4990 struct ChildRecord* child = FindChildSlot(pid);
4991 if (child) {
4992 hProc = child->hProcess;
4993 }
4994 else {
4995 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4996 }
4997 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4998 if (GetLastError() == ERROR_INVALID_PARAMETER) {
4999 errno = ESRCH;
5000 }
5001 else {
5002 errno = EPERM;
5003 }
5004 ret = -1;
5005 }
5006 else {
5007 DWORD status;
5008 if (!GetExitCodeProcess(hProc, &status)) {
5009 errno = map_errno(GetLastError());
5010 ret = -1;
5011 }
5012 else if (status == STILL_ACTIVE) {
5013 if (!TerminateProcess(hProc, 0)) {
5014 errno = EPERM;
5015 ret = -1;
5016 }
5017 }
5018 else {
5019 errno = ESRCH;
5020 ret = -1;
5021 }
5022 if (!child) {
5023 CloseHandle(hProc);
5024 }
5025 }
5026 }
5027 break;
5028
5029 default:
5030 errno = EINVAL;
5031 ret = -1;
5032 break;
5033 }
5034
5035 return ret;
5036}
5037
5038/* License: Ruby's */
5039static int
5040wlink(const WCHAR *from, const WCHAR *to)
5041{
5042 if (!CreateHardLinkW(to, from, NULL)) {
5043 errno = map_errno(GetLastError());
5044 return -1;
5045 }
5046
5047 return 0;
5048}
5049
5050/* License: Ruby's */
5051int
5052rb_w32_ulink(const char *from, const char *to)
5053{
5054 WCHAR *wfrom;
5055 WCHAR *wto;
5056 int ret;
5057
5058 if (!(wfrom = utf8_to_wstr(from, NULL)))
5059 return -1;
5060 if (!(wto = utf8_to_wstr(to, NULL))) {
5061 free(wfrom);
5062 return -1;
5063 }
5064 ret = wlink(wfrom, wto);
5065 free(wto);
5066 free(wfrom);
5067 return ret;
5068}
5069
5070/* License: Ruby's */
5071int
5072link(const char *from, const char *to)
5073{
5074 WCHAR *wfrom;
5075 WCHAR *wto;
5076 int ret;
5077
5078 if (!(wfrom = filecp_to_wstr(from, NULL)))
5079 return -1;
5080 if (!(wto = filecp_to_wstr(to, NULL))) {
5081 free(wfrom);
5082 return -1;
5083 }
5084 ret = wlink(wfrom, wto);
5085 free(wto);
5086 free(wfrom);
5087 return ret;
5088}
5089
5090/* License: Public Domain, copied from mingw headers */
5091#ifndef FILE_DEVICE_FILE_SYSTEM
5092# define FILE_DEVICE_FILE_SYSTEM 0x00000009
5093#endif
5094#ifndef FSCTL_GET_REPARSE_POINT
5095# define FSCTL_GET_REPARSE_POINT ((0x9<<16)|(42<<2))
5096#endif
5097#ifndef IO_REPARSE_TAG_SYMLINK
5098# define IO_REPARSE_TAG_SYMLINK 0xA000000CL
5099#endif
5100
5101/* License: Ruby's */
5102static int
5103reparse_symlink(const WCHAR *path, rb_w32_reparse_buffer_t *rp, size_t size)
5104{
5105 HANDLE f;
5106 DWORD ret;
5107 int e = 0;
5108
5109 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5110 if (f == INVALID_HANDLE_VALUE) {
5111 return GetLastError();
5112 }
5113
5114 if (!DeviceIoControl(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
5115 rp, size, &ret, NULL)) {
5116 e = GetLastError();
5117 }
5118 else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
5119 rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
5120 e = ERROR_INVALID_PARAMETER;
5121 }
5122 CloseHandle(f);
5123 return e;
5124}
5125
5126/* License: Ruby's */
5127int
5128rb_w32_reparse_symlink_p(const WCHAR *path)
5129{
5130 VALUE wtmp = 0;
5131 rb_w32_reparse_buffer_t rbuf, *rp = &rbuf;
5132 WCHAR *wbuf;
5133 DWORD len;
5134 int e;
5135
5136 e = rb_w32_read_reparse_point(path, rp, sizeof(rbuf), &wbuf, &len);
5137 if (e == ERROR_MORE_DATA) {
5138 size_t size = rb_w32_reparse_buffer_size(len + 1);
5139 rp = ALLOCV(wtmp, size);
5140 e = rb_w32_read_reparse_point(path, rp, size, &wbuf, &len);
5141 ALLOCV_END(wtmp);
5142 }
5143 switch (e) {
5144 case 0:
5145 case ERROR_MORE_DATA:
5146 return TRUE;
5147 }
5148 return FALSE;
5149}
5150
5151/* License: Ruby's */
5152int
5153rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
5154 size_t bufsize, WCHAR **result, DWORD *len)
5155{
5156 int e = reparse_symlink(path, rp, bufsize);
5157 DWORD ret = 0;
5158
5159 if (!e || e == ERROR_MORE_DATA) {
5160 void *name;
5161 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
5162 name = ((char *)rp->SymbolicLinkReparseBuffer.PathBuffer +
5163 rp->SymbolicLinkReparseBuffer.PrintNameOffset);
5164 ret = rp->SymbolicLinkReparseBuffer.PrintNameLength;
5165 *len = ret / sizeof(WCHAR);
5166 }
5167 else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
5168 static const WCHAR volume[] = L"Volume{";
5169 enum {volume_prefix_len = rb_strlen_lit("\\??\\")};
5170 name = ((char *)rp->MountPointReparseBuffer.PathBuffer +
5171 rp->MountPointReparseBuffer.SubstituteNameOffset +
5172 volume_prefix_len * sizeof(WCHAR));
5173 ret = rp->MountPointReparseBuffer.SubstituteNameLength;
5174 *len = ret / sizeof(WCHAR);
5175 ret -= volume_prefix_len * sizeof(WCHAR);
5176 if (ret > sizeof(volume) - 1 * sizeof(WCHAR) &&
5177 memcmp(name, volume, sizeof(volume) - 1 * sizeof(WCHAR)) == 0)
5178 return -1;
5179 }
5180 else {
5181 return -1;
5182 }
5183 *result = name;
5184 if (e) {
5185 if ((char *)name + ret + sizeof(WCHAR) > (char *)rp + bufsize)
5186 return e;
5187 /* SubstituteName is not used */
5188 }
5189 ((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0';
5190 translate_wchar(name, L'\\', L'/');
5191 return 0;
5192 }
5193 else {
5194 return e;
5195 }
5196}
5197
5198/* License: Ruby's */
5199static ssize_t
5200w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize)
5201{
5202 VALUE rp_buf, rp_buf_bigger = 0;
5203 DWORD len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0);
5204 size_t size = rb_w32_reparse_buffer_size(bufsize);
5205 WCHAR *wname;
5206 WCHAR *wpath = ALLOCV(rp_buf, sizeof(WCHAR) * len + size);
5207 rb_w32_reparse_buffer_t *rp = (void *)(wpath + len);
5208 ssize_t ret;
5209 int e;
5210
5211 MultiByteToWideChar(cp, 0, path, -1, wpath, len);
5212 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5213 if (e == ERROR_MORE_DATA) {
5214 size = rb_w32_reparse_buffer_size(len + 1);
5215 rp = ALLOCV(rp_buf_bigger, size);
5216 e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
5217 }
5218 if (e) {
5219 ALLOCV_END(rp_buf);
5220 ALLOCV_END(rp_buf_bigger);
5221 errno = e == -1 ? EINVAL : map_errno(e);
5222 return -1;
5223 }
5224 len = lstrlenW(wname);
5225 ret = WideCharToMultiByte(cp, 0, wname, len, buf, bufsize, NULL, NULL);
5226 ALLOCV_END(rp_buf);
5227 ALLOCV_END(rp_buf_bigger);
5228 if (!ret) {
5229 ret = bufsize;
5230 }
5231 return ret;
5232}
5233
5234/* License: Ruby's */
5235ssize_t
5236rb_w32_ureadlink(const char *path, char *buf, size_t bufsize)
5237{
5238 return w32_readlink(CP_UTF8, path, buf, bufsize);
5239}
5240
5241/* License: Ruby's */
5242ssize_t
5243readlink(const char *path, char *buf, size_t bufsize)
5244{
5245 return w32_readlink(filecp(), path, buf, bufsize);
5246}
5247
5248#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
5249#define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
5250#endif
5251#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
5252#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
5253#endif
5254
5255/* License: Ruby's */
5256static int
5257w32_symlink(UINT cp, const char *src, const char *link)
5258{
5259 int atts, len1, len2;
5260 VALUE buf;
5261 WCHAR *wsrc, *wlink;
5262 DWORD flag = 0;
5263 BOOLEAN ret;
5264 int e;
5265
5266 static DWORD create_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5267
5268 if (!*link) {
5269 errno = ENOENT;
5270 return -1;
5271 }
5272 if (!*src) {
5273 errno = EINVAL;
5274 return -1;
5275 }
5276 len1 = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
5277 len2 = MultiByteToWideChar(cp, 0, link, -1, NULL, 0);
5278 wsrc = ALLOCV_N(WCHAR, buf, len1+len2);
5279 wlink = wsrc + len1;
5280 MultiByteToWideChar(cp, 0, src, -1, wsrc, len1);
5281 MultiByteToWideChar(cp, 0, link, -1, wlink, len2);
5282 translate_wchar(wsrc, L'/', L'\\');
5283
5284 atts = GetFileAttributesW(wsrc);
5285 if (atts != -1 && atts & FILE_ATTRIBUTE_DIRECTORY)
5286 flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
5287 ret = CreateSymbolicLinkW(wlink, wsrc, flag |= create_flag);
5288 if (!ret &&
5289 (e = GetLastError()) == ERROR_INVALID_PARAMETER &&
5290 (flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
5291 create_flag = 0;
5292 flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
5293 ret = CreateSymbolicLinkW(wlink, wsrc, flag);
5294 if (!ret) e = GetLastError();
5295 }
5296 ALLOCV_END(buf);
5297
5298 if (!ret) {
5299 errno = map_errno(e);
5300 return -1;
5301 }
5302 return 0;
5303}
5304
5305/* License: Ruby's */
5306int
5307rb_w32_usymlink(const char *src, const char *link)
5308{
5309 return w32_symlink(CP_UTF8, src, link);
5310}
5311
5312/* License: Ruby's */
5313int
5314symlink(const char *src, const char *link)
5315{
5316 return w32_symlink(filecp(), src, link);
5317}
5318
5319/* License: Ruby's */
5320rb_pid_t
5321wait(int *status)
5322{
5323 return waitpid(-1, status, 0);
5324}
5325
5326/* License: Ruby's */
5327static char *
5328w32_getenv(const char *name, UINT cp)
5329{
5330 WCHAR *wenvarea, *wenv;
5331 int len = strlen(name);
5332 char *env, *found = NULL;
5333 int wlen;
5334
5335 if (len == 0) return NULL;
5336
5337 if (!NTLoginName) {
5338 /* initialized in init_env, uenvarea_mutex should have been
5339 * initialized before it */
5340 return getenv(name);
5341 }
5342
5343 thread_exclusive(uenvarea) {
5344 if (uenvarea) {
5345 free(uenvarea);
5346 uenvarea = NULL;
5347 }
5348 wenvarea = GetEnvironmentStringsW();
5349 if (!wenvarea) {
5350 map_errno(GetLastError());
5351 continue;
5352 }
5353 for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
5354 wlen += lstrlenW(wenv) + 1;
5355 uenvarea = wstr_to_mbstr(cp, wenvarea, wlen, NULL);
5356 FreeEnvironmentStringsW(wenvarea);
5357 if (!uenvarea)
5358 continue;
5359
5360 for (env = uenvarea; *env; env += strlen(env) + 1) {
5361 if (strncasecmp(env, name, len) == 0 && *(env + len) == '=') {
5362 found = env + len + 1;
5363 break;
5364 }
5365 }
5366 }
5367
5368 return found;
5369}
5370
5371/* License: Ruby's */
5372char *
5373rb_w32_ugetenv(const char *name)
5374{
5375 return w32_getenv(name, CP_UTF8);
5376}
5377
5378/* License: Ruby's */
5379char *
5380rb_w32_getenv(const char *name)
5381{
5382 return w32_getenv(name, CP_ACP);
5383}
5384
5385/* License: Ruby's */
5386static DWORD
5387get_attr_vsn(const WCHAR *path, DWORD *atts, DWORD *vsn)
5388{
5389 BY_HANDLE_FILE_INFORMATION st = {0};
5390 DWORD e = 0;
5391 HANDLE h = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5392
5393 if (h == INVALID_HANDLE_VALUE) {
5394 e = GetLastError();
5395 ASSUME(e);
5396 return e;
5397 }
5398 if (!GetFileInformationByHandle(h, &st)) {
5399 e = GetLastError();
5400 ASSUME(e);
5401 }
5402 else {
5403 *atts = st.dwFileAttributes;
5404 *vsn = st.dwVolumeSerialNumber;
5405 }
5406 CloseHandle(h);
5407 return e;
5408}
5409
5410/* License: Artistic or GPL */
5411static int
5412wrename(const WCHAR *oldpath, const WCHAR *newpath)
5413{
5414 int res = 0;
5415 DWORD oldatts = 0, newatts = (DWORD)-1;
5416 DWORD oldvsn = 0, newvsn = 0, e;
5417
5418 e = get_attr_vsn(oldpath, &oldatts, &oldvsn);
5419 if (e) {
5420 errno = map_errno(e);
5421 return -1;
5422 }
5423 if (oldatts & FILE_ATTRIBUTE_REPARSE_POINT) {
5424 HANDLE fh = open_special(oldpath, 0, 0);
5425 if (fh == INVALID_HANDLE_VALUE) {
5426 e = GetLastError();
5427 if (e == ERROR_CANT_RESOLVE_FILENAME) {
5428 errno = ELOOP;
5429 return -1;
5430 }
5431 }
5432 CloseHandle(fh);
5433 }
5434 get_attr_vsn(newpath, &newatts, &newvsn);
5435
5436 RUBY_CRITICAL {
5437 if (newatts != (DWORD)-1 && newatts & FILE_ATTRIBUTE_READONLY)
5438 SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
5439
5440 if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
5441 res = -1;
5442
5443 if (res) {
5444 DWORD e = GetLastError();
5445 if ((e == ERROR_ACCESS_DENIED) && (oldatts & FILE_ATTRIBUTE_DIRECTORY) &&
5446 oldvsn != newvsn)
5447 errno = EXDEV;
5448 else
5449 errno = map_errno(e);
5450 }
5451 else
5452 SetFileAttributesW(newpath, oldatts);
5453 }
5454
5455 return res;
5456}
5457
5458/* License: Ruby's */
5459int
5460rb_w32_urename(const char *from, const char *to)
5461{
5462 WCHAR *wfrom;
5463 WCHAR *wto;
5464 int ret = -1;
5465
5466 if (!(wfrom = utf8_to_wstr(from, NULL)))
5467 return -1;
5468 if (!(wto = utf8_to_wstr(to, NULL))) {
5469 free(wfrom);
5470 return -1;
5471 }
5472 ret = wrename(wfrom, wto);
5473 free(wto);
5474 free(wfrom);
5475 return ret;
5476}
5477
5478/* License: Ruby's */
5479int
5480rb_w32_rename(const char *from, const char *to)
5481{
5482 WCHAR *wfrom;
5483 WCHAR *wto;
5484 int ret = -1;
5485
5486 if (!(wfrom = filecp_to_wstr(from, NULL)))
5487 return -1;
5488 if (!(wto = filecp_to_wstr(to, NULL))) {
5489 free(wfrom);
5490 return -1;
5491 }
5492 ret = wrename(wfrom, wto);
5493 free(wto);
5494 free(wfrom);
5495 return ret;
5496}
5497
5498/* License: Ruby's */
5499static int
5500isUNCRoot(const WCHAR *path)
5501{
5502 if (path[0] == L'\\' && path[1] == L'\\') {
5503 const WCHAR *p = path + 2;
5504 if (p[0] == L'?' && p[1] == L'\\') {
5505 p += 2;
5506 }
5507 for (; *p; p++) {
5508 if (*p == L'\\')
5509 break;
5510 }
5511 if (p[0] && p[1]) {
5512 for (p++; *p; p++) {
5513 if (*p == L'\\')
5514 break;
5515 }
5516 if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
5517 return 1;
5518 }
5519 }
5520 return 0;
5521}
5522
5523#define COPY_STAT(src, dest, size_cast) do { \
5524 (dest).st_dev = (src).st_dev; \
5525 (dest).st_ino = (src).st_ino; \
5526 (dest).st_mode = (src).st_mode; \
5527 (dest).st_nlink = (src).st_nlink; \
5528 (dest).st_uid = (src).st_uid; \
5529 (dest).st_gid = (src).st_gid; \
5530 (dest).st_rdev = (src).st_rdev; \
5531 (dest).st_size = size_cast(src).st_size; \
5532 (dest).st_atime = (src).st_atime; \
5533 (dest).st_mtime = (src).st_mtime; \
5534 (dest).st_ctime = (src).st_ctime; \
5535 } while (0)
5536
5537static time_t filetime_to_unixtime(const FILETIME *ft);
5538static long filetime_to_nsec(const FILETIME *ft);
5539static WCHAR *name_for_stat(WCHAR *buf, const WCHAR *path);
5540static DWORD stati128_handle(HANDLE h, struct stati128 *st);
5541
5542#undef fstat
5543/* License: Ruby's */
5544int
5545rb_w32_fstat(int fd, struct stat *st)
5546{
5547 BY_HANDLE_FILE_INFORMATION info;
5548 int ret = fstat(fd, st);
5549
5550 if (ret) return ret;
5551 if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) return ret;
5552 if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
5553 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5554 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5555 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5556 }
5557 return ret;
5558}
5559
5560/* License: Ruby's */
5561int
5562rb_w32_fstati128(int fd, struct stati128 *st)
5563{
5564 struct stat tmp;
5565 int ret = fstat(fd, &tmp);
5566
5567 if (ret) return ret;
5568 COPY_STAT(tmp, *st, +);
5569 stati128_handle((HANDLE)_get_osfhandle(fd), st);
5570 return ret;
5571}
5572
5573#if !defined FILE_INVALID_FILE_ID && !defined __MINGW32__
5574typedef struct {
5575 BYTE Identifier[16];
5576} FILE_ID_128;
5577#endif
5578
5579#if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < 0x602
5580#define FileIdInfo 0x12
5581
5582typedef struct {
5583 unsigned LONG_LONG VolumeSerialNumber;
5584 FILE_ID_128 FileId;
5585} FILE_ID_INFO;
5586#endif
5587
5588static BOOL
5589get_ino(HANDLE h, FILE_ID_INFO *id)
5590{
5591 return GetFileInformationByHandleEx(h, FileIdInfo, id, sizeof(*id));
5592}
5593
5594/* License: Ruby's */
5595static DWORD
5596stati128_handle(HANDLE h, struct stati128 *st)
5597{
5598 BY_HANDLE_FILE_INFORMATION info;
5599 DWORD attr = (DWORD)-1;
5600
5601 if (GetFileInformationByHandle(h, &info)) {
5602 FILE_ID_INFO fii;
5603 st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
5604 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
5605 st->st_atimensec = filetime_to_nsec(&info.ftLastAccessTime);
5606 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
5607 st->st_mtimensec = filetime_to_nsec(&info.ftLastWriteTime);
5608 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
5609 st->st_ctimensec = filetime_to_nsec(&info.ftCreationTime);
5610 st->st_nlink = info.nNumberOfLinks;
5611 attr = info.dwFileAttributes;
5612 if (get_ino(h, &fii)) {
5613 st->st_ino = *((unsigned __int64 *)&fii.FileId);
5614 st->st_inohigh = *((__int64 *)&fii.FileId + 1);
5615 }
5616 else {
5617 st->st_ino = ((__int64)info.nFileIndexHigh << 32) | info.nFileIndexLow;
5618 st->st_inohigh = 0;
5619 }
5620 }
5621 return attr;
5622}
5623
5624/* License: Ruby's */
5625static time_t
5626filetime_to_unixtime(const FILETIME *ft)
5627{
5628 long subsec;
5629 time_t t = filetime_split(ft, &subsec);
5630
5631 if (t < 0) return 0;
5632 return t;
5633}
5634
5635/* License: Ruby's */
5636static long
5637filetime_to_nsec(const FILETIME *ft)
5638{
5639 ULARGE_INTEGER tmp;
5640 tmp.LowPart = ft->dwLowDateTime;
5641 tmp.HighPart = ft->dwHighDateTime;
5642 return (long)(tmp.QuadPart % 10000000) * 100;
5643}
5644
5645/* License: Ruby's */
5646static unsigned
5647fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode)
5648{
5649 if (attr & FILE_ATTRIBUTE_READONLY) {
5650 mode |= S_IREAD;
5651 }
5652 else {
5653 mode |= S_IREAD | S_IWRITE | S_IWUSR;
5654 }
5655
5656 if (mode & S_IFMT) {
5657 /* format is already set */
5658 }
5659 else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5660 /* Only used by stat_by_find in the case the file can not be opened.
5661 * In this case we can't get more details. */
5662 }
5663 else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5664 mode |= S_IFDIR | S_IEXEC;
5665 }
5666 else {
5667 mode |= S_IFREG;
5668 }
5669
5670 if (path && (mode & S_IFREG)) {
5671 const WCHAR *end = path + lstrlenW(path);
5672 while (path < end) {
5673 end = CharPrevW(path, end);
5674 if (*end == L'.') {
5675 if ((_wcsicmp(end, L".bat") == 0) ||
5676 (_wcsicmp(end, L".cmd") == 0) ||
5677 (_wcsicmp(end, L".com") == 0) ||
5678 (_wcsicmp(end, L".exe") == 0)) {
5679 mode |= S_IEXEC;
5680 }
5681 break;
5682 }
5683 if (!iswalnum(*end)) break;
5684 }
5685 }
5686
5687 mode |= (mode & 0500) >> 3;
5688 mode |= (mode & 0500) >> 6;
5689
5690 return mode;
5691}
5692
5693/* License: Ruby's */
5694static int
5695check_valid_dir(const WCHAR *path)
5696{
5697 WIN32_FIND_DATAW fd;
5698 HANDLE fh;
5699 WCHAR full[PATH_MAX];
5700 WCHAR *dmy;
5701 WCHAR *p, *q;
5702
5703 /* GetFileAttributes() determines "..." as directory. */
5704 /* We recheck it by FindFirstFile(). */
5705 if (!(p = wcsstr(path, L"...")))
5706 return 0;
5707 q = p + wcsspn(p, L".");
5708 if ((p == path || wcschr(L":/\\", *(p - 1))) &&
5709 (!*q || wcschr(L":/\\", *q))) {
5710 errno = ENOENT;
5711 return -1;
5712 }
5713
5714 /* if the specified path is the root of a drive and the drive is empty, */
5715 /* FindFirstFile() returns INVALID_HANDLE_VALUE. */
5716 if (!GetFullPathNameW(path, sizeof(full) / sizeof(WCHAR), full, &dmy)) {
5717 errno = map_errno(GetLastError());
5718 return -1;
5719 }
5720 if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
5721 return 0;
5722
5723 fh = open_dir_handle(path, &fd);
5724 if (fh == INVALID_HANDLE_VALUE)
5725 return -1;
5726 FindClose(fh);
5727 return 0;
5728}
5729
5730/* License: Ruby's */
5731static int
5732stat_by_find(const WCHAR *path, struct stati128 *st)
5733{
5734 HANDLE h;
5735 WIN32_FIND_DATAW wfd;
5736
5737 /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
5738 h = FindFirstFileW(path, &wfd);
5739 if (h == INVALID_HANDLE_VALUE) {
5740 errno = map_errno(GetLastError());
5741 return -1;
5742 }
5743 FindClose(h);
5744 st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path, 0);
5745 st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
5746 st->st_atimensec = filetime_to_nsec(&wfd.ftLastAccessTime);
5747 st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
5748 st->st_mtimensec = filetime_to_nsec(&wfd.ftLastWriteTime);
5749 st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
5750 st->st_ctimensec = filetime_to_nsec(&wfd.ftCreationTime);
5751 st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
5752 st->st_nlink = 1;
5753 return 0;
5754}
5755
5756/* License: Ruby's */
5757static int
5758path_drive(const WCHAR *path)
5759{
5760 return (iswalpha(path[0]) && path[1] == L':') ?
5761 towupper(path[0]) - L'A' : _getdrive() - 1;
5762}
5763
5764/* License: Ruby's */
5765static int
5766winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
5767{
5768 DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0;
5769 HANDLE f;
5770 WCHAR finalname[PATH_MAX];
5771 int open_error;
5772
5773 memset(st, 0, sizeof(*st));
5774 f = open_special(path, 0, flags);
5775 open_error = GetLastError();
5776 if (f == INVALID_HANDLE_VALUE && !lstat) {
5777 /* Support stat (not only lstat) of UNIXSocket */
5778 FILE_ATTRIBUTE_TAG_INFO attr_info;
5779 DWORD e;
5780
5781 f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT);
5782 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5783 &attr_info, sizeof(attr_info));
5784 if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
5785 CloseHandle(f);
5786 f = INVALID_HANDLE_VALUE;
5787 }
5788 }
5789 if (f != INVALID_HANDLE_VALUE) {
5790 DWORD attr = stati128_handle(f, st);
5791 const DWORD len = GetFinalPathNameByHandleW(f, finalname, numberof(finalname), 0);
5792 unsigned mode = 0;
5793 switch (GetFileType(f)) {
5794 case FILE_TYPE_CHAR:
5795 mode = S_IFCHR;
5796 break;
5797 case FILE_TYPE_PIPE:
5798 mode = S_IFIFO;
5799 break;
5800 default:
5801 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
5802 FILE_ATTRIBUTE_TAG_INFO attr_info;
5803 DWORD e;
5804
5805 e = GetFileInformationByHandleEx(f, FileAttributeTagInfo,
5806 &attr_info, sizeof(attr_info));
5807 if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) {
5808 st->st_size = 0;
5809 mode |= S_IFSOCK;
5810 }
5811 else if (rb_w32_reparse_symlink_p(path)) {
5812 /* TODO: size in which encoding? */
5813 st->st_size = 0;
5814 mode |= S_IFLNK | S_IEXEC;
5815 }
5816 else {
5817 mode |= S_IFDIR | S_IEXEC;
5818 }
5819 }
5820 }
5821 CloseHandle(f);
5822 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
5823 if (check_valid_dir(path)) return -1;
5824 }
5825 st->st_mode = fileattr_to_unixmode(attr, path, mode);
5826 if (len) {
5827 finalname[min(len, numberof(finalname)-1)] = L'\0';
5828 path = finalname;
5829 if (wcsncmp(path, namespace_prefix, numberof(namespace_prefix)) == 0)
5830 path += numberof(namespace_prefix);
5831 }
5832 }
5833 else {
5834 if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME)
5835 || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) {
5836 errno = map_errno(open_error);
5837 return -1;
5838 }
5839
5840 if (stat_by_find(path, st)) return -1;
5841 }
5842
5843 st->st_dev = st->st_rdev = path_drive(path);
5844
5845 return 0;
5846}
5847
5848/* License: Ruby's */
5849int
5850rb_w32_stat(const char *path, struct stat *st)
5851{
5852 struct stati128 tmp;
5853
5854 if (w32_stati128(path, &tmp, filecp(), FALSE)) return -1;
5855 COPY_STAT(tmp, *st, (_off_t));
5856 return 0;
5857}
5858
5859/* License: Ruby's */
5860static int
5861wstati128(const WCHAR *path, struct stati128 *st, BOOL lstat)
5862{
5863 WCHAR *buf1;
5864 int ret, size;
5865 VALUE v;
5866
5867 if (!path || !st) {
5868 errno = EFAULT;
5869 return -1;
5870 }
5871 size = lstrlenW(path) + 2;
5872 buf1 = ALLOCV_N(WCHAR, v, size);
5873 if (!(path = name_for_stat(buf1, path)))
5874 return -1;
5875 ret = winnt_stat(path, st, lstat);
5876 if (v)
5877 ALLOCV_END(v);
5878
5879 return ret;
5880}
5881
5882/* License: Ruby's */
5883static WCHAR *
5884name_for_stat(WCHAR *buf1, const WCHAR *path)
5885{
5886 const WCHAR *p;
5887 WCHAR *s, *end;
5888 int len;
5889
5890 for (p = path, s = buf1; *p; p++, s++) {
5891 if (*p == L'/')
5892 *s = L'\\';
5893 else
5894 *s = *p;
5895 }
5896 *s = '\0';
5897 len = s - buf1;
5898 if (!len || L'\"' == *(--s)) {
5899 errno = ENOENT;
5900 return NULL;
5901 }
5902 end = buf1 + len - 1;
5903
5904 if (isUNCRoot(buf1)) {
5905 if (*end == L'.')
5906 *end = L'\0';
5907 else if (*end != L'\\')
5908 lstrcatW(buf1, L"\\");
5909 }
5910 else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
5911 lstrcatW(buf1, L".");
5912
5913 return buf1;
5914}
5915
5916/* License: Ruby's */
5917int
5918rb_w32_ustati128(const char *path, struct stati128 *st)
5919{
5920 return w32_stati128(path, st, CP_UTF8, FALSE);
5921}
5922
5923/* License: Ruby's */
5924int
5925rb_w32_stati128(const char *path, struct stati128 *st)
5926{
5927 return w32_stati128(path, st, filecp(), FALSE);
5928}
5929
5930/* License: Ruby's */
5931static int
5932w32_stati128(const char *path, struct stati128 *st, UINT cp, BOOL lstat)
5933{
5934 WCHAR *wpath;
5935 int ret;
5936
5937 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
5938 return -1;
5939 ret = wstati128(wpath, st, lstat);
5940 free(wpath);
5941 return ret;
5942}
5943
5944/* License: Ruby's */
5945int
5946rb_w32_ulstati128(const char *path, struct stati128 *st)
5947{
5948 return w32_stati128(path, st, CP_UTF8, TRUE);
5949}
5950
5951/* License: Ruby's */
5952int
5953rb_w32_lstati128(const char *path, struct stati128 *st)
5954{
5955 return w32_stati128(path, st, filecp(), TRUE);
5956}
5957
5958/* License: Ruby's */
5959rb_off_t
5960rb_w32_lseek(int fd, rb_off_t ofs, int whence)
5961{
5962 SOCKET sock = TO_SOCKET(fd);
5963 if (is_socket(sock) || is_pipe(sock)) {
5964 errno = ESPIPE;
5965 return -1;
5966 }
5967 return _lseeki64(fd, ofs, whence);
5968}
5969
5970/* License: Ruby's */
5971static int
5972w32_access(const char *path, int mode, UINT cp)
5973{
5974 struct stati128 stat;
5975 if (w32_stati128(path, &stat, cp, FALSE) != 0)
5976 return -1;
5977 mode <<= 6;
5978 if ((stat.st_mode & mode) != mode) {
5979 errno = EACCES;
5980 return -1;
5981 }
5982 return 0;
5983}
5984
5985/* License: Ruby's */
5986int
5987rb_w32_access(const char *path, int mode)
5988{
5989 return w32_access(path, mode, filecp());
5990}
5991
5992/* License: Ruby's */
5993int
5994rb_w32_uaccess(const char *path, int mode)
5995{
5996 return w32_access(path, mode, CP_UTF8);
5997}
5998
5999/* License: Ruby's */
6000static int
6001rb_chsize(HANDLE h, rb_off_t size)
6002{
6003 long upos, lpos, usize, lsize;
6004 int ret = -1;
6005 DWORD e;
6006
6007 if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
6008 (e = GetLastError())) {
6009 errno = map_errno(e);
6010 return -1;
6011 }
6012 usize = (long)(size >> 32);
6013 lsize = (long)size;
6014 if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
6015 (e = GetLastError())) {
6016 errno = map_errno(e);
6017 }
6018 else if (!SetEndOfFile(h)) {
6019 errno = map_errno(GetLastError());
6020 }
6021 else {
6022 ret = 0;
6023 }
6024 SetFilePointer(h, lpos, &upos, SEEK_SET);
6025 return ret;
6026}
6027
6028/* License: Ruby's */
6029static int
6030w32_truncate(const char *path, rb_off_t length, UINT cp)
6031{
6032 HANDLE h;
6033 int ret;
6034 WCHAR *wpath;
6035
6036 if (!(wpath = mbstr_to_wstr(cp, path, -1, NULL)))
6037 return -1;
6038 h = CreateFileW(wpath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
6039 if (h == INVALID_HANDLE_VALUE) {
6040 errno = map_errno(GetLastError());
6041 free(wpath);
6042 return -1;
6043 }
6044 free(wpath);
6045 ret = rb_chsize(h, length);
6046 CloseHandle(h);
6047 return ret;
6048}
6049
6050/* License: Ruby's */
6051int
6052rb_w32_utruncate(const char *path, rb_off_t length)
6053{
6054 return w32_truncate(path, length, CP_UTF8);
6055}
6056
6057/* License: Ruby's */
6058int
6059rb_w32_truncate(const char *path, rb_off_t length)
6060{
6061 return w32_truncate(path, length, filecp());
6062}
6063
6064/* License: Ruby's */
6065int
6066rb_w32_ftruncate(int fd, rb_off_t length)
6067{
6068 HANDLE h;
6069
6070 h = (HANDLE)_get_osfhandle(fd);
6071 if (h == (HANDLE)-1) return -1;
6072 return rb_chsize(h, length);
6073}
6074
6075/* License: Ruby's */
6076static long
6077filetime_to_clock(FILETIME *ft)
6078{
6079 __int64 qw = ft->dwHighDateTime;
6080 qw <<= 32;
6081 qw |= ft->dwLowDateTime;
6082 qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
6083 return (long) qw;
6084}
6085
6086/* License: Ruby's */
6087int
6088rb_w32_times(struct tms *tmbuf)
6089{
6090 FILETIME create, exit, kernel, user;
6091
6092 if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
6093 tmbuf->tms_utime = filetime_to_clock(&user);
6094 tmbuf->tms_stime = filetime_to_clock(&kernel);
6095 tmbuf->tms_cutime = 0;
6096 tmbuf->tms_cstime = 0;
6097 }
6098 else {
6099 tmbuf->tms_utime = clock();
6100 tmbuf->tms_stime = 0;
6101 tmbuf->tms_cutime = 0;
6102 tmbuf->tms_cstime = 0;
6103 }
6104 return 0;
6105}
6106
6107
6108/* License: Ruby's */
6109#define yield_once() Sleep(0)
6110#define yield_until(condition) do yield_once(); while (!(condition))
6111
6112/* License: Ruby's */
6114 /* output field */
6115 void* stackaddr;
6116 int errnum;
6117
6118 /* input field */
6119 uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
6120 uintptr_t self;
6121 int argc;
6122 uintptr_t* argv;
6123};
6124
6125/* License: Ruby's */
6126static DWORD WINAPI
6127call_asynchronous(PVOID argp)
6128{
6129 DWORD ret;
6130 struct asynchronous_arg_t *arg = argp;
6131 arg->stackaddr = &argp;
6132 ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
6133 arg->errnum = errno;
6134 return ret;
6135}
6136
6137/* License: Ruby's */
6138uintptr_t
6139rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
6140 int argc, uintptr_t* argv, uintptr_t intrval)
6141{
6142 DWORD val;
6143 BOOL interrupted = FALSE;
6144 HANDLE thr;
6145
6146 RUBY_CRITICAL {
6147 struct asynchronous_arg_t arg;
6148
6149 arg.stackaddr = NULL;
6150 arg.errnum = 0;
6151 arg.func = func;
6152 arg.self = self;
6153 arg.argc = argc;
6154 arg.argv = argv;
6155
6156 thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
6157
6158 if (thr) {
6159 yield_until(arg.stackaddr);
6160
6161 if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
6162 interrupted = TRUE;
6163
6164 if (TerminateThread(thr, intrval)) {
6165 yield_once();
6166 }
6167 }
6168
6169 GetExitCodeThread(thr, &val);
6170 CloseHandle(thr);
6171
6172 if (interrupted) {
6173 /* must release stack of killed thread, why doesn't Windows? */
6174 MEMORY_BASIC_INFORMATION m;
6175
6176 memset(&m, 0, sizeof(m));
6177 if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
6178 Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
6179 arg.stackaddr, GetLastError()));
6180 }
6181 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
6182 Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
6183 m.AllocationBase, GetLastError()));
6184 }
6185 errno = EINTR;
6186 }
6187 else {
6188 errno = arg.errnum;
6189 }
6190 }
6191 }
6192
6193 if (!thr) {
6194 rb_fatal("failed to launch waiter thread:%ld", GetLastError());
6195 }
6196
6197 return val;
6198}
6199
6200/* License: Ruby's */
6201char **
6202rb_w32_get_environ(void)
6203{
6204 WCHAR *envtop, *env;
6205 char **myenvtop, **myenv;
6206 int num;
6207
6208 /*
6209 * We avoid values started with `='. If you want to deal those values,
6210 * change this function, and some functions in hash.c which recognize
6211 * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
6212 * CygWin deals these values by changing first `=' to '!'. But we don't
6213 * use such trick and follow cmd.exe's way that just doesn't show these
6214 * values.
6215 *
6216 * This function returns UTF-8 strings.
6217 */
6218 envtop = GetEnvironmentStringsW();
6219 for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
6220 if (*env != '=') num++;
6221
6222 myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
6223 for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
6224 if (*env != '=') {
6225 if (!(*myenv = wstr_to_utf8(env, NULL))) {
6226 break;
6227 }
6228 myenv++;
6229 }
6230 }
6231 *myenv = NULL;
6232 FreeEnvironmentStringsW(envtop);
6233
6234 return myenvtop;
6235}
6236
6237/* License: Ruby's */
6238void
6239rb_w32_free_environ(char **env)
6240{
6241 char **t = env;
6242
6243 while (*t) free(*t++);
6244 free(env);
6245}
6246
6247/* License: Ruby's */
6248rb_pid_t
6249rb_w32_getpid(void)
6250{
6251 return GetCurrentProcessId();
6252}
6253
6254
6255/* License: Ruby's */
6256rb_pid_t
6257rb_w32_getppid(void)
6258{
6259 typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
6260 static query_func *pNtQueryInformationProcess = (query_func *)-1;
6261 rb_pid_t ppid = 0;
6262
6263 if (pNtQueryInformationProcess == (query_func *)-1)
6264 pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
6265 if (pNtQueryInformationProcess) {
6266 struct {
6267 long ExitStatus;
6268 void* PebBaseAddress;
6269 uintptr_t AffinityMask;
6270 uintptr_t BasePriority;
6271 uintptr_t UniqueProcessId;
6272 uintptr_t ParentProcessId;
6273 } pbi;
6274 ULONG len;
6275 long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
6276 if (!ret) {
6277 ppid = pbi.ParentProcessId;
6278 }
6279 }
6280
6281 return ppid;
6282}
6283
6284STATIC_ASSERT(std_handle, (STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)==(STD_ERROR_HANDLE-STD_OUTPUT_HANDLE));
6285
6286/* License: Ruby's */
6287#define set_new_std_handle(newfd, handle) do { \
6288 if ((unsigned)(newfd) > 2) break; \
6289 SetStdHandle(STD_INPUT_HANDLE+(STD_OUTPUT_HANDLE-STD_INPUT_HANDLE)*(newfd), \
6290 (handle)); \
6291 } while (0)
6292#define set_new_std_fd(newfd) set_new_std_handle(newfd, (HANDLE)rb_w32_get_osfhandle(newfd))
6293
6294/* License: Ruby's */
6295int
6296rb_w32_dup2(int oldfd, int newfd)
6297{
6298 int ret;
6299
6300 if (oldfd == newfd) return newfd;
6301 ret = dup2(oldfd, newfd);
6302 if (ret < 0) return ret;
6303 set_new_std_fd(newfd);
6304 return newfd;
6305}
6306
6307/* License: Ruby's */
6308int
6309rb_w32_uopen(const char *file, int oflag, ...)
6310{
6311 WCHAR *wfile;
6312 int ret;
6313 int pmode;
6314
6315 va_list arg;
6316 va_start(arg, oflag);
6317 pmode = va_arg(arg, int);
6318 va_end(arg);
6319
6320 if (!(wfile = utf8_to_wstr(file, NULL)))
6321 return -1;
6322 ret = w32_wopen(wfile, oflag, pmode);
6323 free(wfile);
6324 return ret;
6325}
6326
6327/* License: Ruby's */
6328static int
6329check_if_wdir(const WCHAR *wfile)
6330{
6331 DWORD attr = GetFileAttributesW(wfile);
6332 if (attr == (DWORD)-1L ||
6333 !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
6334 check_valid_dir(wfile)) {
6335 return FALSE;
6336 }
6337 errno = EISDIR;
6338 return TRUE;
6339}
6340
6341/* License: Ruby's */
6342int
6343rb_w32_open(const char *file, int oflag, ...)
6344{
6345 WCHAR *wfile;
6346 int ret;
6347 int pmode;
6348
6349 va_list arg;
6350 va_start(arg, oflag);
6351 pmode = va_arg(arg, int);
6352 va_end(arg);
6353
6354 if (!(wfile = filecp_to_wstr(file, NULL)))
6355 return -1;
6356 ret = w32_wopen(wfile, oflag, pmode);
6357 free(wfile);
6358 return ret;
6359}
6360
6361/* License: Ruby's */
6362int
6363rb_w32_wopen(const WCHAR *file, int oflag, ...)
6364{
6365 int pmode = 0;
6366
6367 if (oflag & O_CREAT) {
6368 va_list arg;
6369 va_start(arg, oflag);
6370 pmode = va_arg(arg, int);
6371 va_end(arg);
6372 }
6373
6374 return w32_wopen(file, oflag, pmode);
6375}
6376
6377static int
6378w32_wopen(const WCHAR *file, int oflag, int pmode)
6379{
6380 char flags = 0;
6381 int fd;
6382 DWORD access;
6383 DWORD create;
6384 DWORD attr = FILE_ATTRIBUTE_NORMAL;
6385 SECURITY_ATTRIBUTES sec;
6386 HANDLE h;
6387 int share_delete;
6388
6389 share_delete = oflag & O_SHARE_DELETE ? FILE_SHARE_DELETE : 0;
6390 oflag &= ~O_SHARE_DELETE;
6391 if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
6392 fd = _wopen(file, oflag, pmode);
6393 if (fd == -1) {
6394 switch (errno) {
6395 case EACCES:
6396 check_if_wdir(file);
6397 break;
6398 case EINVAL:
6399 errno = map_errno(GetLastError());
6400 break;
6401 }
6402 }
6403 return fd;
6404 }
6405
6406 sec.nLength = sizeof(sec);
6407 sec.lpSecurityDescriptor = NULL;
6408 if (oflag & O_NOINHERIT) {
6409 sec.bInheritHandle = FALSE;
6410 flags |= FNOINHERIT;
6411 }
6412 else {
6413 sec.bInheritHandle = TRUE;
6414 }
6415 oflag &= ~O_NOINHERIT;
6416
6417 /* always open with binary mode */
6418 oflag &= ~(O_BINARY | O_TEXT);
6419
6420 switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
6421 case O_RDWR:
6422 access = GENERIC_READ | GENERIC_WRITE;
6423 break;
6424 case O_RDONLY:
6425 access = GENERIC_READ;
6426 break;
6427 case O_WRONLY:
6428 access = GENERIC_WRITE;
6429 break;
6430 default:
6431 errno = EINVAL;
6432 return -1;
6433 }
6434 oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
6435
6436 switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
6437 case O_CREAT:
6438 create = OPEN_ALWAYS;
6439 break;
6440 case 0:
6441 case O_EXCL:
6442 create = OPEN_EXISTING;
6443 break;
6444 case O_CREAT | O_EXCL:
6445 case O_CREAT | O_EXCL | O_TRUNC:
6446 create = CREATE_NEW;
6447 break;
6448 case O_TRUNC:
6449 case O_TRUNC | O_EXCL:
6450 create = TRUNCATE_EXISTING;
6451 break;
6452 case O_CREAT | O_TRUNC:
6453 create = CREATE_ALWAYS;
6454 break;
6455 default:
6456 errno = EINVAL;
6457 return -1;
6458 }
6459 if (oflag & O_CREAT) {
6460 /* TODO: we need to check umask here, but it's not exported... */
6461 if (!(pmode & S_IWRITE))
6462 attr = FILE_ATTRIBUTE_READONLY;
6463 }
6464 oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
6465
6466 if (oflag & O_TEMPORARY) {
6467 attr |= FILE_FLAG_DELETE_ON_CLOSE;
6468 access |= DELETE;
6469 }
6470 oflag &= ~O_TEMPORARY;
6471
6472 if (oflag & _O_SHORT_LIVED)
6473 attr |= FILE_ATTRIBUTE_TEMPORARY;
6474 oflag &= ~_O_SHORT_LIVED;
6475
6476 switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
6477 case 0:
6478 break;
6479 case O_SEQUENTIAL:
6480 attr |= FILE_FLAG_SEQUENTIAL_SCAN;
6481 break;
6482 case O_RANDOM:
6483 attr |= FILE_FLAG_RANDOM_ACCESS;
6484 break;
6485 default:
6486 errno = EINVAL;
6487 return -1;
6488 }
6489 oflag &= ~(O_SEQUENTIAL | O_RANDOM);
6490
6491 if (oflag & ~O_APPEND) {
6492 errno = EINVAL;
6493 return -1;
6494 }
6495
6496 /* allocate a C Runtime file handle */
6497 RUBY_CRITICAL {
6498 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6499 fd = _open_osfhandle((intptr_t)h, 0);
6500 CloseHandle(h);
6501 }
6502 if (fd == -1) {
6503 errno = EMFILE;
6504 return -1;
6505 }
6506 RUBY_CRITICAL {
6507 rb_acrt_lowio_lock_fh(fd);
6508 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
6509 _set_osflags(fd, 0);
6510
6511 h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE | share_delete, &sec, create, attr, NULL);
6512 if (h == INVALID_HANDLE_VALUE) {
6513 DWORD e = GetLastError();
6514 if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
6515 errno = map_errno(e);
6516 rb_acrt_lowio_unlock_fh(fd);
6517 fd = -1;
6518 goto quit;
6519 }
6520
6521 switch (GetFileType(h)) {
6522 case FILE_TYPE_CHAR:
6523 flags |= FDEV;
6524 break;
6525 case FILE_TYPE_PIPE:
6526 flags |= FPIPE;
6527 break;
6528 case FILE_TYPE_UNKNOWN:
6529 errno = map_errno(GetLastError());
6530 CloseHandle(h);
6531 rb_acrt_lowio_unlock_fh(fd);
6532 fd = -1;
6533 goto quit;
6534 }
6535 if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
6536 flags |= FAPPEND;
6537
6538 _set_osfhnd(fd, (intptr_t)h);
6539 _set_osflags(fd, flags | FOPEN);
6540
6541 rb_acrt_lowio_unlock_fh(fd);
6542 quit:
6543 ;
6544 }
6545
6546 return fd;
6547}
6548
6549/* License: Ruby's */
6550int
6551rb_w32_fclose(FILE *fp)
6552{
6553 int fd = fileno(fp);
6554 SOCKET sock = TO_SOCKET(fd);
6555 int save_errno = errno;
6556
6557 if (fflush(fp)) return -1;
6558 if (!is_socket(sock)) {
6559 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6560 return fclose(fp);
6561 }
6562 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6563 fclose(fp);
6564 errno = save_errno;
6565 if (closesocket(sock) == SOCKET_ERROR) {
6566 errno = map_errno(WSAGetLastError());
6567 return -1;
6568 }
6569 return 0;
6570}
6571
6572/* License: Ruby's */
6573int
6574rb_w32_pipe(int fds[2])
6575{
6576 static long serial = 0;
6577 static const char prefix[] = "\\\\.\\pipe\\ruby";
6578 enum {
6579 width_of_prefix = (int)sizeof(prefix) - 1,
6580 width_of_pid = (int)sizeof(rb_pid_t) * 2,
6581 width_of_serial = (int)sizeof(serial) * 2,
6582 width_of_ids = width_of_pid + 1 + width_of_serial + 1
6583 };
6584 char name[sizeof(prefix) + width_of_ids];
6585 SECURITY_ATTRIBUTES sec;
6586 HANDLE hRead, hWrite, h;
6587 int fdRead, fdWrite;
6588 int ret;
6589
6590 memcpy(name, prefix, width_of_prefix);
6591 snprintf(name + width_of_prefix, width_of_ids, "%.*"PRI_PIDT_PREFIX"x-%.*lx",
6592 width_of_pid, rb_w32_getpid(), width_of_serial, InterlockedIncrement(&serial)-1);
6593
6594 sec.nLength = sizeof(sec);
6595 sec.lpSecurityDescriptor = NULL;
6596 sec.bInheritHandle = FALSE;
6597
6598 RUBY_CRITICAL {
6599 hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
6600 0, 2, 65536, 65536, 0, &sec);
6601 }
6602 if (hRead == INVALID_HANDLE_VALUE) {
6603 DWORD err = GetLastError();
6604 if (err == ERROR_PIPE_BUSY)
6605 errno = EMFILE;
6606 else
6607 errno = map_errno(GetLastError());
6608 return -1;
6609 }
6610
6611 RUBY_CRITICAL {
6612 hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
6613 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
6614 }
6615 if (hWrite == INVALID_HANDLE_VALUE) {
6616 errno = map_errno(GetLastError());
6617 CloseHandle(hRead);
6618 return -1;
6619 }
6620
6621 RUBY_CRITICAL do {
6622 ret = 0;
6623 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6624 fdRead = _open_osfhandle((intptr_t)h, 0);
6625 CloseHandle(h);
6626 if (fdRead == -1) {
6627 errno = EMFILE;
6628 CloseHandle(hWrite);
6629 CloseHandle(hRead);
6630 ret = -1;
6631 break;
6632 }
6633
6634 rb_acrt_lowio_lock_fh(fdRead);
6635 _set_osfhnd(fdRead, (intptr_t)hRead);
6636 _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
6637 rb_acrt_lowio_unlock_fh(fdRead);
6638 } while (0);
6639 if (ret)
6640 return ret;
6641
6642 RUBY_CRITICAL do {
6643 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
6644 fdWrite = _open_osfhandle((intptr_t)h, 0);
6645 CloseHandle(h);
6646 if (fdWrite == -1) {
6647 errno = EMFILE;
6648 CloseHandle(hWrite);
6649 ret = -1;
6650 break;
6651 }
6652 rb_acrt_lowio_lock_fh(fdWrite);
6653 _set_osfhnd(fdWrite, (intptr_t)hWrite);
6654 _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
6655 rb_acrt_lowio_unlock_fh(fdWrite);
6656 } while (0);
6657 if (ret) {
6658 rb_w32_close(fdRead);
6659 return ret;
6660 }
6661
6662 fds[0] = fdRead;
6663 fds[1] = fdWrite;
6664
6665 return 0;
6666}
6667
6668/* License: Ruby's */
6669static int
6670console_emulator_p(void)
6671{
6672#ifdef _WIN32_WCE
6673 return FALSE;
6674#else
6675 const void *const func = WriteConsoleW;
6676 HMODULE k;
6677 MEMORY_BASIC_INFORMATION m;
6678
6679 memset(&m, 0, sizeof(m));
6680 if (!VirtualQuery(func, &m, sizeof(m))) {
6681 return FALSE;
6682 }
6683 k = GetModuleHandle("kernel32.dll");
6684 if (!k) return FALSE;
6685 return (HMODULE)m.AllocationBase != k;
6686#endif
6687}
6688
6689/* License: Ruby's */
6690static struct constat *
6691constat_handle(HANDLE h)
6692{
6693 st_data_t data;
6694 struct constat *p = NULL;
6695 thread_exclusive(conlist) {
6696 if (!conlist) {
6697 if (console_emulator_p()) {
6698 conlist = conlist_disabled;
6699 continue;
6700 }
6701 conlist = st_init_numtable();
6702 install_vm_exit_handler();
6703 }
6704 else if (conlist == conlist_disabled) {
6705 continue;
6706 }
6707 if (st_lookup(conlist, (st_data_t)h, &data)) {
6708 p = (struct constat *)data;
6709 }
6710 else {
6711 CONSOLE_SCREEN_BUFFER_INFO csbi;
6712 p = ALLOC(struct constat);
6713 p->vt100.state = constat_init;
6714 p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6715 p->vt100.reverse = 0;
6716 p->vt100.saved.X = p->vt100.saved.Y = 0;
6717 if (GetConsoleScreenBufferInfo(h, &csbi)) {
6718 p->vt100.attr = csbi.wAttributes;
6719 }
6720 st_insert(conlist, (st_data_t)h, (st_data_t)p);
6721 }
6722 }
6723 return p;
6724}
6725
6726/* License: Ruby's */
6727static void
6728constat_reset(HANDLE h)
6729{
6730 st_data_t data;
6731 struct constat *p;
6732 thread_exclusive(conlist) {
6733 if (!conlist || conlist == conlist_disabled) continue;
6734 if (!st_lookup(conlist, (st_data_t)h, &data)) continue;
6735 p = (struct constat *)data;
6736 p->vt100.state = constat_init;
6737 }
6738}
6739
6740#define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
6741#define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
6742
6743#define constat_attr_color_reverse(attr) \
6744 ((attr) & ~(FOREGROUND_MASK | BACKGROUND_MASK)) | \
6745 (((attr) & FOREGROUND_MASK) << 4) | \
6746 (((attr) & BACKGROUND_MASK) >> 4)
6747
6748/* License: Ruby's */
6749static WORD
6750constat_attr(int count, const int *seq, WORD attr, WORD default_attr, int *reverse)
6751{
6752 int rev = *reverse;
6753 WORD bold;
6754
6755 if (!count) return attr;
6756 if (rev) attr = constat_attr_color_reverse(attr);
6757 bold = attr & FOREGROUND_INTENSITY;
6758 attr &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
6759
6760 while (count-- > 0) {
6761 switch (*seq++) {
6762 case 0:
6763 attr = default_attr;
6764 rev = 0;
6765 bold = 0;
6766 break;
6767 case 1:
6768 bold = FOREGROUND_INTENSITY;
6769 break;
6770 case 4:
6771#ifndef COMMON_LVB_UNDERSCORE
6772#define COMMON_LVB_UNDERSCORE 0x8000
6773#endif
6774 attr |= COMMON_LVB_UNDERSCORE;
6775 break;
6776 case 7:
6777 rev = 1;
6778 break;
6779
6780 case 30:
6781 attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
6782 break;
6783 case 17:
6784 case 31:
6785 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)) | FOREGROUND_RED;
6786 break;
6787 case 18:
6788 case 32:
6789 attr = (attr & ~(FOREGROUND_BLUE | FOREGROUND_RED)) | FOREGROUND_GREEN;
6790 break;
6791 case 19:
6792 case 33:
6793 attr = (attr & ~FOREGROUND_BLUE) | FOREGROUND_GREEN | FOREGROUND_RED;
6794 break;
6795 case 20:
6796 case 34:
6797 attr = (attr & ~(FOREGROUND_GREEN | FOREGROUND_RED)) | FOREGROUND_BLUE;
6798 break;
6799 case 21:
6800 case 35:
6801 attr = (attr & ~FOREGROUND_GREEN) | FOREGROUND_BLUE | FOREGROUND_RED;
6802 break;
6803 case 22:
6804 case 36:
6805 attr = (attr & ~FOREGROUND_RED) | FOREGROUND_BLUE | FOREGROUND_GREEN;
6806 break;
6807 case 23:
6808 case 37:
6809 attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
6810 break;
6811
6812 case 40:
6813 attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
6814 break;
6815 case 41:
6816 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN)) | BACKGROUND_RED;
6817 break;
6818 case 42:
6819 attr = (attr & ~(BACKGROUND_BLUE | BACKGROUND_RED)) | BACKGROUND_GREEN;
6820 break;
6821 case 43:
6822 attr = (attr & ~BACKGROUND_BLUE) | BACKGROUND_GREEN | BACKGROUND_RED;
6823 break;
6824 case 44:
6825 attr = (attr & ~(BACKGROUND_GREEN | BACKGROUND_RED)) | BACKGROUND_BLUE;
6826 break;
6827 case 45:
6828 attr = (attr & ~BACKGROUND_GREEN) | BACKGROUND_BLUE | BACKGROUND_RED;
6829 break;
6830 case 46:
6831 attr = (attr & ~BACKGROUND_RED) | BACKGROUND_BLUE | BACKGROUND_GREEN;
6832 break;
6833 case 47:
6834 attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
6835 break;
6836 }
6837 }
6838 attr |= bold;
6839 if (rev) attr = constat_attr_color_reverse(attr);
6840 *reverse = rev;
6841 return attr;
6842}
6843
6844/* License: Ruby's */
6845static void
6846constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
6847{
6848 DWORD written;
6849
6850 FillConsoleOutputAttribute(handle, attr, len, pos, &written);
6851 FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
6852}
6853
6854/* License: Ruby's */
6855static void
6856constat_apply(HANDLE handle, struct constat *s, WCHAR w)
6857{
6858 CONSOLE_SCREEN_BUFFER_INFO csbi;
6859 const int *seq = s->vt100.seq;
6860 int count = s->vt100.state;
6861 int arg0, arg1 = 1;
6862 COORD pos;
6863
6864 if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
6865 arg0 = (count > 0 && seq[0] > 0);
6866 if (arg0) arg1 = seq[0];
6867 switch (w) {
6868 case L'm':
6869 SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr, &s->vt100.reverse));
6870 break;
6871 case L'F':
6872 csbi.dwCursorPosition.X = 0;
6873 case L'A':
6874 csbi.dwCursorPosition.Y -= arg1;
6875 if (csbi.dwCursorPosition.Y < csbi.srWindow.Top)
6876 csbi.dwCursorPosition.Y = csbi.srWindow.Top;
6877 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6878 break;
6879 case L'E':
6880 csbi.dwCursorPosition.X = 0;
6881 case L'B':
6882 case L'e':
6883 csbi.dwCursorPosition.Y += arg1;
6884 if (csbi.dwCursorPosition.Y > csbi.srWindow.Bottom)
6885 csbi.dwCursorPosition.Y = csbi.srWindow.Bottom;
6886 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6887 break;
6888 case L'C':
6889 csbi.dwCursorPosition.X += arg1;
6890 if (csbi.dwCursorPosition.X >= csbi.srWindow.Right)
6891 csbi.dwCursorPosition.X = csbi.srWindow.Right;
6892 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6893 break;
6894 case L'D':
6895 csbi.dwCursorPosition.X -= arg1;
6896 if (csbi.dwCursorPosition.X < csbi.srWindow.Left)
6897 csbi.dwCursorPosition.X = csbi.srWindow.Left;
6898 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6899 break;
6900 case L'G':
6901 case L'`':
6902 arg1 += csbi.srWindow.Left;
6903 if (arg1 > csbi.srWindow.Right)
6904 arg1 = csbi.srWindow.Right;
6905 csbi.dwCursorPosition.X = arg1;
6906 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6907 break;
6908 case L'd':
6909 arg1 += csbi.srWindow.Top;
6910 if (arg1 > csbi.srWindow.Bottom)
6911 arg1 = csbi.srWindow.Bottom;
6912 csbi.dwCursorPosition.Y = arg1;
6913 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
6914 break;
6915 case L'H':
6916 case L'f':
6917 pos.Y = arg1 + csbi.srWindow.Top - 1;
6918 if (pos.Y > csbi.srWindow.Bottom) pos.Y = csbi.srWindow.Bottom;
6919 if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
6920 pos.X = arg1 + csbi.srWindow.Left - 1;
6921 if (pos.X > csbi.srWindow.Right) pos.X = csbi.srWindow.Right;
6922 SetConsoleCursorPosition(handle, pos);
6923 break;
6924 case L'J':
6925 switch (arg0 ? arg1 : 0) {
6926 case 0: /* erase after cursor */
6927 constat_clear(handle, csbi.wAttributes,
6928 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.dwCursorPosition.Y + 1)
6929 - csbi.dwCursorPosition.X),
6930 csbi.dwCursorPosition);
6931 break;
6932 case 1: /* erase before *and* cursor */
6933 pos.X = 0;
6934 pos.Y = csbi.srWindow.Top;
6935 constat_clear(handle, csbi.wAttributes,
6936 (csbi.dwSize.X * (csbi.dwCursorPosition.Y - csbi.srWindow.Top)
6937 + csbi.dwCursorPosition.X + 1),
6938 pos);
6939 break;
6940 case 2: /* erase entire screen */
6941 pos.X = 0;
6942 pos.Y = csbi.srWindow.Top;
6943 constat_clear(handle, csbi.wAttributes,
6944 (csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1)),
6945 pos);
6946 break;
6947 case 3: /* erase entire screen */
6948 pos.X = 0;
6949 pos.Y = 0;
6950 constat_clear(handle, csbi.wAttributes,
6951 (csbi.dwSize.X * csbi.dwSize.Y),
6952 pos);
6953 break;
6954 }
6955 break;
6956 case L'K':
6957 switch (arg0 ? arg1 : 0) {
6958 case 0: /* erase after cursor */
6959 constat_clear(handle, csbi.wAttributes,
6960 (csbi.dwSize.X - csbi.dwCursorPosition.X),
6961 csbi.dwCursorPosition);
6962 break;
6963 case 1: /* erase before *and* cursor */
6964 pos.X = 0;
6965 pos.Y = csbi.dwCursorPosition.Y;
6966 constat_clear(handle, csbi.wAttributes,
6967 csbi.dwCursorPosition.X + 1, pos);
6968 break;
6969 case 2: /* erase entire line */
6970 pos.X = 0;
6971 pos.Y = csbi.dwCursorPosition.Y;
6972 constat_clear(handle, csbi.wAttributes,
6973 csbi.dwSize.X, pos);
6974 break;
6975 }
6976 break;
6977 case L's':
6978 s->vt100.saved = csbi.dwCursorPosition;
6979 break;
6980 case L'u':
6981 SetConsoleCursorPosition(handle, s->vt100.saved);
6982 break;
6983 case L'h':
6984 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
6985 CONSOLE_CURSOR_INFO cci;
6986 GetConsoleCursorInfo(handle, &cci);
6987 cci.bVisible = TRUE;
6988 SetConsoleCursorInfo(handle, &cci);
6989 }
6990 break;
6991 case L'l':
6992 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
6993 CONSOLE_CURSOR_INFO cci;
6994 GetConsoleCursorInfo(handle, &cci);
6995 cci.bVisible = FALSE;
6996 SetConsoleCursorInfo(handle, &cci);
6997 }
6998 break;
6999 }
7000}
7001
7002/* get rid of console writing bug; assume WriteConsole and WriteFile
7003 * on a console share the same limit. */
7004static const long MAXSIZE_CONSOLE_WRITING = 31366;
7005
7006/* License: Ruby's */
7007static long
7008constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
7009{
7010 const WCHAR *ptr = *ptrp;
7011 long rest, len = *lenp;
7012 while (len-- > 0) {
7013 WCHAR wc = *ptr++;
7014 if (wc == 0x1b) {
7015 rest = *lenp - len - 1;
7016 if (s->vt100.state == constat_esc) {
7017 rest++; /* reuse this ESC */
7018 }
7019 s->vt100.state = constat_init;
7020 if (len > 0 && *ptr != L'[') continue;
7021 s->vt100.state = constat_esc;
7022 }
7023 else if (s->vt100.state == constat_esc) {
7024 if (wc != L'[') {
7025 /* TODO: supply dropped ESC at beginning */
7026 s->vt100.state = constat_init;
7027 continue;
7028 }
7029 rest = *lenp - len - 1;
7030 if (rest > 0) --rest;
7031 s->vt100.state = constat_seq;
7032 s->vt100.seq[0] = 0;
7033 }
7034 else if (s->vt100.state >= constat_seq) {
7035 if (wc >= L'0' && wc <= L'9') {
7036 if (s->vt100.state < (int)numberof(s->vt100.seq)) {
7037 int *seq = &s->vt100.seq[s->vt100.state];
7038 *seq = (*seq * 10) + (wc - L'0');
7039 }
7040 }
7041 else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
7042 s->vt100.seq[s->vt100.state++] = -1;
7043 }
7044 else {
7045 do {
7046 if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
7047 s->vt100.seq[s->vt100.state] = 0;
7048 }
7049 else {
7050 s->vt100.state = (int)numberof(s->vt100.seq);
7051 }
7052 } while (0);
7053 if (wc != L';') {
7054 constat_apply(h, s, wc);
7055 s->vt100.state = constat_init;
7056 }
7057 }
7058 rest = 0;
7059 }
7060 else if ((rest = *lenp - len) < MAXSIZE_CONSOLE_WRITING) {
7061 continue;
7062 }
7063 *ptrp = ptr;
7064 *lenp = len;
7065 return rest;
7066 }
7067 len = *lenp;
7068 *ptrp = ptr;
7069 *lenp = 0;
7070 return len;
7071}
7072
7073
7074/* License: Ruby's */
7075int
7076rb_w32_close(int fd)
7077{
7078 SOCKET sock = TO_SOCKET(fd);
7079 int save_errno = errno;
7080
7081 if (!is_socket(sock)) {
7082 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
7083 constat_delete((HANDLE)sock);
7084 return _close(fd);
7085 }
7086 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
7087 socklist_delete(&sock, NULL);
7088 _close(fd);
7089 errno = save_errno;
7090 if (closesocket(sock) == SOCKET_ERROR) {
7091 errno = map_errno(WSAGetLastError());
7092 return -1;
7093 }
7094 return 0;
7095}
7096
7097#ifndef INVALID_SET_FILE_POINTER
7098#define INVALID_SET_FILE_POINTER ((DWORD)-1)
7099#endif
7100
7101static int
7102setup_overlapped(OVERLAPPED *ol, int fd, int iswrite, rb_off_t *_offset)
7103{
7104 memset(ol, 0, sizeof(*ol));
7105
7106 // On mode:a, it can write only FILE_END.
7107 // On mode:a+, though it can write only FILE_END,
7108 // it can read from everywhere.
7109 DWORD seek_method = ((_osfile(fd) & FAPPEND) && iswrite) ? FILE_END : FILE_CURRENT;
7110
7111 if (_offset) {
7112 // Explicit offset was provided (pread/pwrite) - use it:
7113 uint64_t offset = *_offset;
7114 ol->Offset = (uint32_t)(offset & 0xFFFFFFFFLL);
7115 ol->OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
7116
7117 // Update _offset with the current offset:
7118 LARGE_INTEGER seek_offset = {0}, current_offset = {0};
7119 if (!SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, &current_offset, seek_method)) {
7120 DWORD last_error = GetLastError();
7121 if (last_error != NO_ERROR) {
7122 errno = map_errno(last_error);
7123 return -1;
7124 }
7125 }
7126
7127 // As we need to restore the current offset later, we save it here:
7128 *_offset = current_offset.QuadPart;
7129 }
7130 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7131 LONG high = 0;
7132 DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, seek_method);
7133
7134 if (low == INVALID_SET_FILE_POINTER) {
7135 DWORD err = GetLastError();
7136 if (err != NO_ERROR) {
7137 errno = map_errno(err);
7138 return -1;
7139 }
7140 }
7141
7142 ol->Offset = low;
7143 ol->OffsetHigh = high;
7144 }
7145
7146 ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
7147 if (!ol->hEvent) {
7148 errno = map_errno(GetLastError());
7149 return -1;
7150 }
7151 return 0;
7152}
7153
7154static void
7155finish_overlapped(OVERLAPPED *ol, int fd, DWORD size, rb_off_t *_offset)
7156{
7157 CloseHandle(ol->hEvent);
7158
7159 if (_offset) {
7160 // If we were doing a `pread`/`pwrite`, we need to restore the current that was saved in setup_overlapped:
7161 DWORD seek_method = (_osfile(fd) & FAPPEND) ? FILE_END : FILE_BEGIN;
7162
7163 LARGE_INTEGER seek_offset = {0};
7164 if (seek_method == FILE_BEGIN) {
7165 seek_offset.QuadPart = *_offset;
7166 }
7167
7168 SetFilePointerEx((HANDLE)_osfhnd(fd), seek_offset, NULL, seek_method);
7169 }
7170 else if (!(_osfile(fd) & (FDEV | FPIPE))) {
7171 LONG high = ol->OffsetHigh;
7172 DWORD low = ol->Offset + size;
7173 if (low < ol->Offset)
7174 ++high;
7175 SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
7176 }
7177}
7178
7179#undef read
7180/* License: Ruby's */
7181static ssize_t
7182rb_w32_read_internal(int fd, void *buf, size_t size, rb_off_t *offset)
7183{
7184 SOCKET sock = TO_SOCKET(fd);
7185 DWORD read;
7186 DWORD wait;
7187 DWORD err;
7188 size_t len;
7189 size_t ret;
7190 OVERLAPPED ol;
7191 BOOL isconsole;
7192 BOOL islineinput = FALSE;
7193 int start = 0;
7194
7195 if (is_socket(sock))
7196 return rb_w32_recv(fd, buf, size, 0);
7197
7198 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7199 if (_get_osfhandle(fd) == -1) {
7200 return -1;
7201 }
7202
7203 if (!offset && _osfile(fd) & FTEXT) {
7204 return _read(fd, buf, size);
7205 }
7206
7207 rb_acrt_lowio_lock_fh(fd);
7208
7209 if (!size || _osfile(fd) & FEOFLAG) {
7210 _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
7211 rb_acrt_lowio_unlock_fh(fd);
7212 return 0;
7213 }
7214
7215 ret = 0;
7216 isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
7217 if (isconsole) {
7218 DWORD mode;
7219 GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
7220 islineinput = (mode & ENABLE_LINE_INPUT) != 0;
7221 }
7222 retry:
7223 /* get rid of console reading bug */
7224 if (isconsole) {
7225 constat_reset((HANDLE)_osfhnd(fd));
7226 if (start)
7227 len = 1;
7228 else {
7229 len = 0;
7230 start = 1;
7231 }
7232 }
7233 else
7234 len = size;
7235 size -= len;
7236
7237 if (setup_overlapped(&ol, fd, FALSE, offset)) {
7238 rb_acrt_lowio_unlock_fh(fd);
7239 return -1;
7240 }
7241
7242 if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, &ol)) {
7243 err = GetLastError();
7244 if (err == ERROR_NO_DATA && (_osfile(fd) & FPIPE)) {
7245 DWORD state;
7246 if (GetNamedPipeHandleState((HANDLE)_osfhnd(fd), &state, NULL, NULL, NULL, NULL, 0) && (state & PIPE_NOWAIT)) {
7247 errno = EWOULDBLOCK;
7248 }
7249 else {
7250 errno = map_errno(err);
7251 }
7252 rb_acrt_lowio_unlock_fh(fd);
7253 return -1;
7254 }
7255 else if (err != ERROR_IO_PENDING) {
7256 CloseHandle(ol.hEvent);
7257 if (err == ERROR_ACCESS_DENIED)
7258 errno = EBADF;
7259 else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
7260 rb_acrt_lowio_unlock_fh(fd);
7261 return 0;
7262 }
7263 else
7264 errno = map_errno(err);
7265
7266 rb_acrt_lowio_unlock_fh(fd);
7267 return -1;
7268 }
7269
7270 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7271 if (wait != WAIT_OBJECT_0) {
7272 if (wait == WAIT_OBJECT_0 + 1)
7273 errno = EINTR;
7274 else
7275 errno = map_errno(GetLastError());
7276 CloseHandle(ol.hEvent);
7277 CancelIo((HANDLE)_osfhnd(fd));
7278 rb_acrt_lowio_unlock_fh(fd);
7279 return -1;
7280 }
7281
7282 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
7283 (err = GetLastError()) != ERROR_HANDLE_EOF) {
7284 int ret = 0;
7285 if (err != ERROR_BROKEN_PIPE) {
7286 errno = map_errno(err);
7287 ret = -1;
7288 }
7289 CloseHandle(ol.hEvent);
7290 CancelIo((HANDLE)_osfhnd(fd));
7291 rb_acrt_lowio_unlock_fh(fd);
7292 return ret;
7293 }
7294 }
7295 else {
7296 err = GetLastError();
7297 errno = map_errno(err);
7298 }
7299
7300 finish_overlapped(&ol, fd, read, offset);
7301
7302 ret += read;
7303 if (read >= len) {
7304 buf = (char *)buf + read;
7305 if (err != ERROR_OPERATION_ABORTED &&
7306 !(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
7307 goto retry;
7308 }
7309 if (read == 0)
7310 _set_osflags(fd, _osfile(fd) | FEOFLAG);
7311
7312
7313 rb_acrt_lowio_unlock_fh(fd);
7314
7315 return ret;
7316}
7317
7318#undef write
7319/* License: Ruby's */
7320static ssize_t
7321rb_w32_write_internal(int fd, const void *buf, size_t size, rb_off_t *offset)
7322{
7323 SOCKET sock = TO_SOCKET(fd);
7324 DWORD written;
7325 DWORD wait;
7326 DWORD err;
7327 size_t len;
7328 size_t ret;
7329 OVERLAPPED ol;
7330
7331 if (is_socket(sock))
7332 return rb_w32_send(fd, buf, size, 0);
7333
7334 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7335 if (_get_osfhandle(fd) == -1) {
7336 return -1;
7337 }
7338
7339 // If an offset is given, we can't use `_write`.
7340 if (!offset && (_osfile(fd) & FTEXT) &&
7341 (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
7342 ssize_t w = _write(fd, buf, size);
7343 if (w == (ssize_t)-1 && errno == EINVAL) {
7344 errno = map_errno(GetLastError());
7345 }
7346 return w;
7347 }
7348
7349 rb_acrt_lowio_lock_fh(fd);
7350
7351 if (!size || _osfile(fd) & FEOFLAG) {
7352 rb_acrt_lowio_unlock_fh(fd);
7353 return 0;
7354 }
7355
7356 ret = 0;
7357 retry:
7358 len = (_osfile(fd) & FDEV) ? min(MAXSIZE_CONSOLE_WRITING, size) : size;
7359 size -= len;
7360 retry2:
7361
7362 // Provide the requested offset.
7363 if (setup_overlapped(&ol, fd, TRUE, offset)) {
7364 rb_acrt_lowio_unlock_fh(fd);
7365 return -1;
7366 }
7367
7368 if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, &ol)) {
7369 err = GetLastError();
7370 if (err != ERROR_IO_PENDING) {
7371 CloseHandle(ol.hEvent);
7372 if (err == ERROR_ACCESS_DENIED)
7373 errno = EBADF;
7374 else
7375 errno = map_errno(err);
7376
7377 rb_acrt_lowio_unlock_fh(fd);
7378 return -1;
7379 }
7380
7381 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
7382 if (wait != WAIT_OBJECT_0) {
7383 if (wait == WAIT_OBJECT_0 + 1)
7384 errno = EINTR;
7385 else
7386 errno = map_errno(GetLastError());
7387 CloseHandle(ol.hEvent);
7388 CancelIo((HANDLE)_osfhnd(fd));
7389 rb_acrt_lowio_unlock_fh(fd);
7390 return -1;
7391 }
7392
7393 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) {
7394 errno = map_errno(GetLastError());
7395 CloseHandle(ol.hEvent);
7396 CancelIo((HANDLE)_osfhnd(fd));
7397 rb_acrt_lowio_unlock_fh(fd);
7398 return -1;
7399 }
7400 }
7401
7402 finish_overlapped(&ol, fd, written, offset);
7403
7404 ret += written;
7405 if (written == len) {
7406 buf = (const char *)buf + len;
7407 if (size > 0)
7408 goto retry;
7409 }
7410 if (ret == 0) {
7411 size_t newlen = len / 2;
7412 if (newlen > 0) {
7413 size += len - newlen;
7414 len = newlen;
7415 goto retry2;
7416 }
7417 ret = -1;
7418 errno = EWOULDBLOCK;
7419 }
7420
7421 rb_acrt_lowio_unlock_fh(fd);
7422
7423 return ret;
7424}
7425
7426ssize_t
7427rb_w32_read(int fd, void *buf, size_t size)
7428{
7429 return rb_w32_read_internal(fd, buf, size, NULL);
7430}
7431
7432ssize_t
7433rb_w32_write(int fd, const void *buf, size_t size)
7434{
7435 return rb_w32_write_internal(fd, buf, size, NULL);
7436}
7437
7438ssize_t
7439rb_w32_pread(int descriptor, void *base, size_t size, rb_off_t offset)
7440{
7441 return rb_w32_read_internal(descriptor, base, size, &offset);
7442}
7443
7444ssize_t
7445rb_w32_pwrite(int descriptor, const void *base, size_t size, rb_off_t offset)
7446{
7447 return rb_w32_write_internal(descriptor, base, size, &offset);
7448}
7449
7450/* License: Ruby's */
7451long
7452rb_w32_write_console(uintptr_t strarg, int fd)
7453{
7454 HANDLE handle;
7455 DWORD dwMode, reslen;
7456 VALUE str = strarg;
7457 int encindex;
7458 WCHAR *wbuffer = 0;
7459 const WCHAR *ptr, *next;
7460 struct constat *s;
7461 long len;
7462
7463 handle = (HANDLE)_osfhnd(fd);
7464 if (!GetConsoleMode(handle, &dwMode))
7465 return -1L;
7466
7467 s = constat_handle(handle);
7468 if (!s) return -1L;
7469 encindex = ENCODING_GET(str);
7470 switch (encindex) {
7471 default:
7472 if (!rb_econv_has_convpath_p(rb_enc_name(rb_enc_from_index(encindex)), "UTF-8"))
7473 return -1L;
7474 str = rb_str_conv_enc_opts(str, NULL, rb_enc_from_index(ENCINDEX_UTF_8),
7476 /* fall through */
7477 case ENCINDEX_US_ASCII:
7478 case ENCINDEX_ASCII_8BIT:
7479 /* assume UTF-8 */
7480 case ENCINDEX_UTF_8:
7481 ptr = wbuffer = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(str), RSTRING_LEN(str), &len);
7482 if (!ptr) return -1L;
7483 break;
7484 case ENCINDEX_UTF_16LE:
7485 ptr = (const WCHAR *)RSTRING_PTR(str);
7486 len = RSTRING_LEN(str) / sizeof(WCHAR);
7487 break;
7488 }
7489 reslen = 0;
7490 if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
7491 if (!WriteConsoleW(handle, ptr, len, &reslen, NULL))
7492 reslen = (DWORD)-1L;
7493 }
7494 else {
7495 while (len > 0) {
7496 long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
7497 reslen += next - ptr;
7498 if (curlen > 0) {
7499 DWORD written;
7500 if (!WriteConsoleW(handle, ptr, curlen, &written, NULL)) {
7501 reslen = (DWORD)-1L;
7502 break;
7503 }
7504 }
7505 ptr = next;
7506 }
7507 }
7508 RB_GC_GUARD(str);
7509 free(wbuffer);
7510 return (long)reslen;
7511}
7512
7513#if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7514/* License: Ruby's */
7515static int
7516unixtime_to_filetime(time_t time, FILETIME *ft)
7517{
7518 ULARGE_INTEGER tmp;
7519
7520 tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7521 ft->dwLowDateTime = tmp.LowPart;
7522 ft->dwHighDateTime = tmp.HighPart;
7523 return 0;
7524}
7525#endif
7526
7527/* License: Ruby's */
7528static int
7529timespec_to_filetime(const struct timespec *ts, FILETIME *ft)
7530{
7531 ULARGE_INTEGER tmp;
7532
7533 tmp.QuadPart = ((LONG_LONG)ts->tv_sec + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
7534 tmp.QuadPart += ts->tv_nsec / 100;
7535 ft->dwLowDateTime = tmp.LowPart;
7536 ft->dwHighDateTime = tmp.HighPart;
7537 return 0;
7538}
7539
7540/* License: Ruby's */
7541static int
7542wutimensat(int dirfd, const WCHAR *path, const struct timespec *times, int flags)
7543{
7544 HANDLE hFile;
7545 FILETIME atime, mtime;
7546 struct stati128 stat;
7547 int ret = 0;
7548
7549 /* TODO: When path is absolute, dirfd should be ignored. */
7550 if (dirfd != AT_FDCWD) {
7551 errno = ENOSYS;
7552 return -1;
7553 }
7554
7555 if (flags != 0) {
7556 errno = EINVAL; /* AT_SYMLINK_NOFOLLOW isn't supported. */
7557 return -1;
7558 }
7559
7560 if (wstati128(path, &stat, FALSE)) {
7561 return -1;
7562 }
7563
7564 if (times) {
7565 if (timespec_to_filetime(&times[0], &atime)) {
7566 return -1;
7567 }
7568 if (timespec_to_filetime(&times[1], &mtime)) {
7569 return -1;
7570 }
7571 }
7572 else {
7573 GetSystemTimePreciseAsFileTime(&atime);
7574 mtime = atime;
7575 }
7576
7577 RUBY_CRITICAL {
7578 const DWORD attr = GetFileAttributesW(path);
7579 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7580 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7581 hFile = open_special(path, GENERIC_WRITE, 0);
7582 if (hFile == INVALID_HANDLE_VALUE) {
7583 errno = map_errno(GetLastError());
7584 ret = -1;
7585 }
7586 else {
7587 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
7588 errno = map_errno(GetLastError());
7589 ret = -1;
7590 }
7591 CloseHandle(hFile);
7592 }
7593 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
7594 SetFileAttributesW(path, attr);
7595 }
7596
7597 return ret;
7598}
7599
7600/* License: Ruby's */
7601static int
7602w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags, UINT cp)
7603{
7604 WCHAR *wpath = mbstr_to_wstr(cp, path, -1, NULL);
7605 int ret = -1;
7606
7607 if (wpath) {
7608 ret = wutimensat(dirfd, wpath, times, flags);
7609 free(wpath);
7610 }
7611 return ret;
7612}
7613
7614/* License: Ruby's */
7615int
7616rb_w32_uutime(const char *path, const struct utimbuf *times)
7617{
7618 struct timespec ts[2];
7619
7620 ts[0].tv_sec = times->actime;
7621 ts[0].tv_nsec = 0;
7622 ts[1].tv_sec = times->modtime;
7623 ts[1].tv_nsec = 0;
7624 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7625}
7626
7627/* License: Ruby's */
7628int
7629rb_w32_utime(const char *path, const struct utimbuf *times)
7630{
7631 struct timespec ts[2];
7632
7633 ts[0].tv_sec = times->actime;
7634 ts[0].tv_nsec = 0;
7635 ts[1].tv_sec = times->modtime;
7636 ts[1].tv_nsec = 0;
7637 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7638}
7639
7640/* License: Ruby's */
7641int
7642rb_w32_uutimes(const char *path, const struct timeval *times)
7643{
7644 struct timespec ts[2];
7645
7646 ts[0].tv_sec = times[0].tv_sec;
7647 ts[0].tv_nsec = times[0].tv_usec * 1000;
7648 ts[1].tv_sec = times[1].tv_sec;
7649 ts[1].tv_nsec = times[1].tv_usec * 1000;
7650 return w32_utimensat(AT_FDCWD, path, ts, 0, CP_UTF8);
7651}
7652
7653/* License: Ruby's */
7654int
7655rb_w32_utimes(const char *path, const struct timeval *times)
7656{
7657 struct timespec ts[2];
7658
7659 ts[0].tv_sec = times[0].tv_sec;
7660 ts[0].tv_nsec = times[0].tv_usec * 1000;
7661 ts[1].tv_sec = times[1].tv_sec;
7662 ts[1].tv_nsec = times[1].tv_usec * 1000;
7663 return w32_utimensat(AT_FDCWD, path, ts, 0, filecp());
7664}
7665
7666/* License: Ruby's */
7667int
7668rb_w32_uutimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7669{
7670 return w32_utimensat(dirfd, path, times, flags, CP_UTF8);
7671}
7672
7673/* License: Ruby's */
7674int
7675rb_w32_utimensat(int dirfd, const char *path, const struct timespec *times, int flags)
7676{
7677 return w32_utimensat(dirfd, path, times, flags, filecp());
7678}
7679
7680/* License: Ruby's */
7681int
7682rb_w32_uchdir(const char *path)
7683{
7684 WCHAR *wpath;
7685 int ret;
7686
7687 if (!(wpath = utf8_to_wstr(path, NULL)))
7688 return -1;
7689 ret = _wchdir(wpath);
7690 free(wpath);
7691 return ret;
7692}
7693
7694/* License: Ruby's */
7695static int
7696wmkdir(const WCHAR *wpath, int mode)
7697{
7698 int ret = -1;
7699
7700 RUBY_CRITICAL do {
7701 if (CreateDirectoryW(wpath, NULL) == FALSE) {
7702 errno = map_errno(GetLastError());
7703 break;
7704 }
7705 if (_wchmod(wpath, mode) == -1) {
7706 RemoveDirectoryW(wpath);
7707 break;
7708 }
7709 ret = 0;
7710 } while (0);
7711 return ret;
7712}
7713
7714/* License: Ruby's */
7715int
7716rb_w32_umkdir(const char *path, int mode)
7717{
7718 WCHAR *wpath;
7719 int ret;
7720
7721 if (!(wpath = utf8_to_wstr(path, NULL)))
7722 return -1;
7723 ret = wmkdir(wpath, mode);
7724 free(wpath);
7725 return ret;
7726}
7727
7728/* License: Ruby's */
7729int
7730rb_w32_mkdir(const char *path, int mode)
7731{
7732 WCHAR *wpath;
7733 int ret;
7734
7735 if (!(wpath = filecp_to_wstr(path, NULL)))
7736 return -1;
7737 ret = wmkdir(wpath, mode);
7738 free(wpath);
7739 return ret;
7740}
7741
7742/* License: Ruby's */
7743static int
7744wrmdir(const WCHAR *wpath)
7745{
7746 int ret = 0;
7747 RUBY_CRITICAL {
7748 const DWORD attr = GetFileAttributesW(wpath);
7749 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7750 SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
7751 }
7752 if (RemoveDirectoryW(wpath) == FALSE) {
7753 errno = map_errno(GetLastError());
7754 ret = -1;
7755 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7756 SetFileAttributesW(wpath, attr);
7757 }
7758 }
7759 }
7760 return ret;
7761}
7762
7763/* License: Ruby's */
7764int
7765rb_w32_rmdir(const char *path)
7766{
7767 WCHAR *wpath;
7768 int ret;
7769
7770 if (!(wpath = filecp_to_wstr(path, NULL)))
7771 return -1;
7772 ret = wrmdir(wpath);
7773 free(wpath);
7774 return ret;
7775}
7776
7777/* License: Ruby's */
7778int
7779rb_w32_urmdir(const char *path)
7780{
7781 WCHAR *wpath;
7782 int ret;
7783
7784 if (!(wpath = utf8_to_wstr(path, NULL)))
7785 return -1;
7786 ret = wrmdir(wpath);
7787 free(wpath);
7788 return ret;
7789}
7790
7791/* License: Ruby's */
7792static int
7793wunlink(const WCHAR *path)
7794{
7795 int ret = 0;
7796 const DWORD SYMLINKD = FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY;
7797 RUBY_CRITICAL {
7798 const DWORD attr = GetFileAttributesW(path);
7799 if (attr == (DWORD)-1) {
7800 }
7801 else if ((attr & SYMLINKD) == SYMLINKD) {
7802 ret = RemoveDirectoryW(path);
7803 }
7804 else {
7805 if (attr & FILE_ATTRIBUTE_READONLY) {
7806 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
7807 }
7808 ret = DeleteFileW(path);
7809 }
7810 if (!ret) {
7811 errno = map_errno(GetLastError());
7812 ret = -1;
7813 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
7814 SetFileAttributesW(path, attr);
7815 }
7816 }
7817 }
7818 return ret;
7819}
7820
7821/* License: Ruby's */
7822int
7823rb_w32_uunlink(const char *path)
7824{
7825 WCHAR *wpath;
7826 int ret;
7827
7828 if (!(wpath = utf8_to_wstr(path, NULL)))
7829 return -1;
7830 ret = wunlink(wpath);
7831 free(wpath);
7832 return ret;
7833}
7834
7835/* License: Ruby's */
7836int
7837rb_w32_unlink(const char *path)
7838{
7839 WCHAR *wpath;
7840 int ret;
7841
7842 if (!(wpath = filecp_to_wstr(path, NULL)))
7843 return -1;
7844 ret = wunlink(wpath);
7845 free(wpath);
7846 return ret;
7847}
7848
7849/* License: Ruby's */
7850int
7851rb_w32_uchmod(const char *path, int mode)
7852{
7853 WCHAR *wpath;
7854 int ret;
7855
7856 if (!(wpath = utf8_to_wstr(path, NULL)))
7857 return -1;
7858 ret = _wchmod(wpath, mode);
7859 free(wpath);
7860 return ret;
7861}
7862
7863/* License: Ruby's */
7864int
7865fchmod(int fd, int mode)
7866{
7867 /* from winbase.h of the mingw-w64 runtime package. */
7868 struct {
7869 LARGE_INTEGER CreationTime;
7870 LARGE_INTEGER LastAccessTime;
7871 LARGE_INTEGER LastWriteTime;
7872 LARGE_INTEGER ChangeTime;
7873 DWORD FileAttributes;
7874 } info = {{{0}}, {{0}}, {{0}},}; /* fields with 0 are unchanged */
7875 HANDLE h = (HANDLE)_get_osfhandle(fd);
7876
7877 info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
7878 if (!(mode & 0200)) info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
7879 if (!SetFileInformationByHandle(h, 0, &info, sizeof(info))) {
7880 errno = map_errno(GetLastError());
7881 return -1;
7882 }
7883 return 0;
7884}
7885
7886/* License: Ruby's */
7887int
7888rb_w32_isatty(int fd)
7889{
7890 DWORD mode;
7891
7892 // validate fd by using _get_osfhandle() because we cannot access _nhandle
7893 if (_get_osfhandle(fd) == -1) {
7894 return 0;
7895 }
7896 if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
7897 errno = ENOTTY;
7898 return 0;
7899 }
7900 return 1;
7901}
7902
7903#if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
7904extern long _ftol(double);
7905/* License: Ruby's */
7906long
7907_ftol2(double d)
7908{
7909 return _ftol(d);
7910}
7911
7912/* License: Ruby's */
7913long
7914_ftol2_sse(double d)
7915{
7916 return _ftol(d);
7917}
7918#endif
7919
7920#ifndef signbit
7921/* License: Ruby's */
7922int
7923signbit(double x)
7924{
7925 int *ip = (int *)(&x + 1) - 1;
7926 return *ip < 0;
7927}
7928#endif
7929
7930/* License: Ruby's */
7931const char * WSAAPI
7932rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
7933{
7934 return (inet_ntop)(af, (void *)addr, numaddr, numaddr_len);
7935}
7936
7937/* License: Ruby's */
7938int WSAAPI
7939rb_w32_inet_pton(int af, const char *src, void *dst)
7940{
7941 return (inet_pton)(af, src, dst);
7942}
7943
7944/* License: Ruby's */
7945char
7946rb_w32_fd_is_text(int fd)
7947{
7948 return _osfile(fd) & FTEXT;
7949}
7950
7951#if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__GMTIME64_S)
7952/* License: Ruby's */
7953static int
7954unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
7955{
7956 FILETIME ft;
7957 if (unixtime_to_filetime(t, &ft)) return -1;
7958 if (!FileTimeToSystemTime(&ft, st)) return -1;
7959 return 0;
7960}
7961
7962/* License: Ruby's */
7963static void
7964systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
7965{
7966 int y = st->wYear, m = st->wMonth, d = st->wDay;
7967 t->tm_sec = st->wSecond;
7968 t->tm_min = st->wMinute;
7969 t->tm_hour = st->wHour;
7970 t->tm_mday = st->wDay;
7971 t->tm_mon = st->wMonth - 1;
7972 t->tm_year = y - 1900;
7973 t->tm_wday = st->wDayOfWeek;
7974 switch (m) {
7975 case 1:
7976 break;
7977 case 2:
7978 d += 31;
7979 break;
7980 default:
7981 d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
7982 d += ((m - 3) * 153 + 2) / 5;
7983 break;
7984 }
7985 t->tm_yday = d - 1;
7986}
7987
7988/* License: Ruby's */
7989static int
7990systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
7991{
7992 TIME_ZONE_INFORMATION stdtz;
7993 SYSTEMTIME sst;
7994
7995 if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
7996 if (!tz) {
7997 GetTimeZoneInformation(&stdtz);
7998 tz = &stdtz;
7999 }
8000 if (tz->StandardBias == tz->DaylightBias) return 0;
8001 if (!tz->StandardDate.wMonth) return 0;
8002 if (!tz->DaylightDate.wMonth) return 0;
8003 if (tz != &stdtz) stdtz = *tz;
8004
8005 stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
8006 if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst)) return 0;
8007 if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
8008 return 0;
8009 return 1;
8010}
8011#endif
8012
8013#ifdef HAVE__GMTIME64_S
8014# ifndef HAVE__LOCALTIME64_S
8015/* assume same as _gmtime64_s() */
8016# define HAVE__LOCALTIME64_S 1
8017# endif
8018# ifndef MINGW_HAS_SECURE_API
8019 _CRTIMP errno_t __cdecl _gmtime64_s(struct tm* tm, const __time64_t *time);
8020 _CRTIMP errno_t __cdecl _localtime64_s(struct tm* tm, const __time64_t *time);
8021# endif
8022# define gmtime_s _gmtime64_s
8023# define localtime_s _localtime64_s
8024#endif
8025
8026/* License: Ruby's */
8027struct tm *
8028gmtime_r(const time_t *tp, struct tm *rp)
8029{
8030 int e = EINVAL;
8031 if (!tp || !rp) {
8032 error:
8033 errno = e;
8034 return NULL;
8035 }
8036#if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__GMTIME64_S)
8037 e = gmtime_s(rp, tp);
8038 if (e != 0) goto error;
8039#else
8040 {
8041 SYSTEMTIME st;
8042 if (unixtime_to_systemtime(*tp, &st)) goto error;
8043 rp->tm_isdst = 0;
8044 systemtime_to_tm(&st, rp);
8045 }
8046#endif
8047 return rp;
8048}
8049
8050/* License: Ruby's */
8051struct tm *
8052localtime_r(const time_t *tp, struct tm *rp)
8053{
8054 int e = EINVAL;
8055 if (!tp || !rp) {
8056 error:
8057 errno = e;
8058 return NULL;
8059 }
8060#if RUBY_MSVCRT_VERSION >= 80 || defined(HAVE__LOCALTIME64_S)
8061 e = localtime_s(rp, tp);
8062 if (e) goto error;
8063#else
8064 {
8065 SYSTEMTIME gst, lst;
8066 if (unixtime_to_systemtime(*tp, &gst)) goto error;
8067 rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
8068 systemtime_to_tm(&lst, rp);
8069 }
8070#endif
8071 return rp;
8072}
8073
8074/* License: Ruby's */
8075int
8076rb_w32_wrap_io_handle(HANDLE h, int flags)
8077{
8078 BOOL tmp;
8079 int len = sizeof(tmp);
8080 int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (char *)&tmp, &len);
8081 if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
8082 int f = 0;
8083 if (flags & O_NONBLOCK) {
8084 flags &= ~O_NONBLOCK;
8085 f = O_NONBLOCK;
8086 }
8087 socklist_insert((SOCKET)h, f);
8088 }
8089 else if (flags & O_NONBLOCK) {
8090 errno = EINVAL;
8091 return -1;
8092 }
8093 return rb_w32_open_osfhandle((intptr_t)h, flags);
8094}
8095
8096/* License: Ruby's */
8097int
8098rb_w32_unwrap_io_handle(int fd)
8099{
8100 SOCKET sock = TO_SOCKET(fd);
8101 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
8102 if (!is_socket(sock)) {
8103 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
8104 constat_delete((HANDLE)sock);
8105 }
8106 else {
8107 socklist_delete(&sock, NULL);
8108 }
8109 return _close(fd);
8110}
8111
8112#if !defined(__MINGW64__) && defined(__MINGW64_VERSION_MAJOR)
8113/*
8114 * Set floating point precision for pow() of mingw-w64 x86.
8115 * With default precision the result is not proper on WinXP.
8116 */
8117double
8118rb_w32_pow(double x, double y)
8119{
8120#undef pow
8121 double r;
8122 unsigned int default_control = _controlfp(0, 0);
8123 _controlfp(_PC_64, _MCW_PC);
8124 r = pow(x, y);
8125 /* Restore setting */
8126 _controlfp(default_control, _MCW_PC);
8127 return r;
8128}
8129#endif
8130
8131typedef struct {
8132 BOOL file_id_p;
8133 union {
8134 BY_HANDLE_FILE_INFORMATION bhfi;
8135 FILE_ID_INFO fii;
8136 } info;
8138
8139static HANDLE
8140w32_io_info(VALUE *file, w32_io_info_t *st)
8141{
8142 VALUE tmp;
8143 HANDLE f, ret = 0;
8144
8145 tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
8146 if (!NIL_P(tmp)) {
8147 f = (HANDLE)rb_w32_get_osfhandle(rb_io_descriptor(tmp));
8148 if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
8149 }
8150 else {
8151 VALUE tmp;
8152 WCHAR *ptr;
8153 int len;
8154 VALUE v;
8155
8156 FilePathValue(*file);
8157 tmp = rb_str_encode_ospath(*file);
8158 len = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, NULL, 0);
8159 ptr = ALLOCV_N(WCHAR, v, len);
8160 MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(tmp), -1, ptr, len);
8161 f = CreateFileW(ptr, 0,
8162 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
8163 FILE_FLAG_BACKUP_SEMANTICS, NULL);
8164 ALLOCV_END(v);
8165 if (f == INVALID_HANDLE_VALUE) return f;
8166 ret = f;
8167 }
8168 if (GetFileType(f) == FILE_TYPE_DISK) {
8169 ZeroMemory(st, sizeof(*st));
8170 if (get_ino(f, &st->info.fii)) {
8171 st->file_id_p = TRUE;
8172 return ret;
8173 }
8174 else if (GetLastError() != ERROR_INVALID_PARAMETER) {
8175 CloseHandle(f);
8176 return INVALID_HANDLE_VALUE;
8177 }
8178 /* this API may not work at files on non Microsoft SMB
8179 * server, fallback to old API then. */
8180 if (GetFileInformationByHandle(f, &st->info.bhfi)) {
8181 st->file_id_p = FALSE;
8182 return ret;
8183 }
8184 }
8185 if (ret) CloseHandle(ret);
8186 return INVALID_HANDLE_VALUE;
8187}
8188
8189static VALUE
8190close_handle(VALUE h)
8191{
8192 CloseHandle((HANDLE)h);
8193 return Qfalse;
8194}
8195
8197 VALUE *fname;
8198 w32_io_info_t *st;
8199};
8200
8201static VALUE
8202call_w32_io_info(VALUE arg)
8203{
8204 struct w32_io_info_args *p = (void *)arg;
8205 return (VALUE)w32_io_info(p->fname, p->st);
8206}
8207
8208VALUE
8209rb_w32_file_identical_p(VALUE fname1, VALUE fname2)
8210{
8211 w32_io_info_t st1, st2;
8212 HANDLE f1 = 0, f2 = 0;
8213
8214 f1 = w32_io_info(&fname1, &st1);
8215 if (f1 == INVALID_HANDLE_VALUE) return Qfalse;
8216 if (f1) {
8217 struct w32_io_info_args arg;
8218 arg.fname = &fname2;
8219 arg.st = &st2;
8220 f2 = (HANDLE)rb_ensure(call_w32_io_info, (VALUE)&arg, close_handle, (VALUE)f1);
8221 }
8222 else {
8223 f2 = w32_io_info(&fname2, &st2);
8224 }
8225 if (f2 == INVALID_HANDLE_VALUE) return Qfalse;
8226 if (f2) CloseHandle(f2);
8227
8228 if (st1.file_id_p != st2.file_id_p) return Qfalse;
8229 if (!st1.file_id_p) {
8230 if (st1.info.bhfi.dwVolumeSerialNumber == st2.info.bhfi.dwVolumeSerialNumber &&
8231 st1.info.bhfi.nFileIndexHigh == st2.info.bhfi.nFileIndexHigh &&
8232 st1.info.bhfi.nFileIndexLow == st2.info.bhfi.nFileIndexLow)
8233 return Qtrue;
8234 }
8235 else {
8236 if (st1.info.fii.VolumeSerialNumber == st2.info.fii.VolumeSerialNumber &&
8237 memcmp(&st1.info.fii.FileId, &st2.info.fii.FileId, sizeof(FILE_ID_128)) == 0)
8238 return Qtrue;
8239 }
8240 return Qfalse;
8241}
8242
8243int
8244rb_w32_set_thread_description(HANDLE th, const WCHAR *name)
8245{
8246 int result = FALSE;
8247 typedef HRESULT (WINAPI *set_thread_description_func)(HANDLE, PCWSTR);
8248 static set_thread_description_func set_thread_description =
8249 (set_thread_description_func)-1;
8250 if (set_thread_description == (set_thread_description_func)-1) {
8251 /* Since Windows 10, version 1607 and Windows Server 2016 */
8252 set_thread_description = (set_thread_description_func)
8253 get_proc_address("kernel32", "SetThreadDescription", NULL);
8254 }
8255 if (set_thread_description) {
8256 result = set_thread_description(th, name);
8257 }
8258 return result;
8259}
8260
8261int
8262rb_w32_set_thread_description_str(HANDLE th, VALUE name)
8263{
8264 int idx, result = FALSE;
8265 WCHAR *s;
8266
8267 if (NIL_P(name)) {
8268 return rb_w32_set_thread_description(th, L"");
8269 }
8270 s = (WCHAR *)StringValueCStr(name);
8271 idx = rb_enc_get_index(name);
8272 if (idx == ENCINDEX_UTF_16LE) {
8273 result = rb_w32_set_thread_description(th, s);
8274 }
8275 else {
8276 name = rb_str_conv_enc(name, rb_enc_from_index(idx), rb_utf8_encoding());
8277 s = mbstr_to_wstr(CP_UTF8, RSTRING_PTR(name), RSTRING_LEN(name)+1, NULL);
8278 result = rb_w32_set_thread_description(th, s);
8279 free(s);
8280 }
8281 RB_GC_GUARD(name);
8282 return result;
8283}
8284
8285VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notimplement;
8286
8287#if RUBY_MSVCRT_VERSION < 120
8288#include "missing/nextafter.c"
8289#endif
8290
8291void *
8292rb_w32_mmap(void *addr, size_t len, int prot, int flags, int fd, rb_off_t offset)
8293{
8294 void *ptr;
8295 //DWORD protect = 0;
8296 DWORD protect = PAGE_EXECUTE_READWRITE;
8297
8298 if (fd > 0 || offset) {
8299 /* not supported */
8300 errno = EINVAL;
8301 return MAP_FAILED;
8302 }
8303
8304/*
8305 if (prot & PROT_EXEC) {
8306 if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8307 else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8308 else protect = PAGE_EXECUTE;
8309 }
8310 else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8311 else if (prot & PROT_READ) protect = PAGE_READONLY;
8312*/
8313 ptr = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, protect);
8314 if (!ptr) {
8315 errno = rb_w32_map_errno(GetLastError());
8316 return MAP_FAILED;
8317 }
8318
8319 return ptr;
8320}
8321
8322int
8323rb_w32_munmap(void *addr, size_t len)
8324{
8325 if (!VirtualFree(addr, 0, MEM_RELEASE)) {
8326 errno = rb_w32_map_errno(GetLastError());
8327 return -1;
8328 }
8329
8330 return 0;
8331}
8332
8333inline int
8334rb_w32_mprotect(void *addr, size_t len, int prot)
8335{
8336/*
8337 DWORD protect = 0;
8338 if (prot & PROT_EXEC) {
8339 if (prot & PROT_WRITE) protect = PAGE_EXECUTE_READWRITE;
8340 else if (prot & PROT_READ) protect = PAGE_EXECUTE_READ;
8341 else protect = PAGE_EXECUTE;
8342 }
8343 else if (prot & PROT_WRITE) protect = PAGE_READWRITE;
8344 else if (prot & PROT_READ) protect = PAGE_READONLY;
8345 if (!VirtualProtect(addr, len, protect, NULL)) {
8346 errno = rb_w32_map_errno(GetLastError());
8347 return -1;
8348 }
8349*/
8350 if (prot & PROT_EXEC) {
8351 if (!FlushInstructionCache(GetCurrentProcess(), addr, len)) {
8352 errno = rb_w32_map_errno(GetLastError());
8353 return -1;
8354 }
8355 }
8356 return 0;
8357}
#define LONG_LONG
Definition long_long.h:38
#define RBIMPL_ATTR_FORMAT(x, y, z)
Wraps (or simulates) __attribute__((format))
Definition format.h:29
int ruby_glob_func(const char *path, VALUE arg, void *enc)
Type of a glob callback function.
Definition glob.h:49
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
#define ISSPACE
Old name of rb_isspace.
Definition ctype.h:88
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define xrealloc
Old name of ruby_xrealloc.
Definition xmalloc.h:56
#define ECONV_UNDEF_REPLACE
Old name of RUBY_ECONV_UNDEF_REPLACE.
Definition transcode.h:526
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
Definition encoding.h:109
#define ECONV_INVALID_REPLACE
Old name of RUBY_ECONV_INVALID_REPLACE.
Definition transcode.h:524
#define ASSUME
Old name of RBIMPL_ASSUME.
Definition assume.h:27
#define ISALPHA
Old name of rb_isalpha.
Definition ctype.h:92
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition memory.h:405
#define ISALNUM
Old name of rb_isalnum.
Definition ctype.h:91
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:406
Encoding relates APIs.
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
Definition string.c:1286
VALUE rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts)
Identical to rb_str_conv_enc(), except it additionally takes IO encoder options.
Definition string.c:1170
int rb_econv_has_convpath_p(const char *from_encoding, const char *to_encoding)
Queries if there is more than one way to convert between the passed two encodings.
Definition transcode.c:3211
void rb_write_error2(const char *str, long len)
Identical to rb_write_error(), except it additionally takes the message's length.
Definition io.c:9136
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3445
#define rb_strlen_lit(str)
Length of a string literal.
Definition string.h:1692
#define rb_utf8_str_new(str, len)
Identical to rb_str_new, except it generates a string of "UTF-8" encoding.
Definition string.h:1549
VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker)
Raises rb_eNotImpError.
Definition vm_method.c:474
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition io.c:2892
int len
Length of the buffer.
Definition io.h:8
char * ruby_strdup(const char *str)
This is our own version of strdup(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
Definition util.c:536
#define strdup(s)
Just another name of ruby_strdup.
Definition util.h:187
void ruby_vm_at_exit(void(*func)(ruby_vm_t *))
ruby_vm_at_exit registers a function func to be invoked when a VM passed away.
Definition vm.c:864
void rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
Identical to rb_w32_fd_copy(), except it copies unlimited number of file descriptors.
Definition win32.c:2956
void rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
Destructively overwrites an fdset with another.
Definition win32.c:2941
void rb_fd_term(rb_fdset_t *f)
Destroys the rb_fdset_t, releasing any memory and resources it used.
#define rb_long2int
Just another name of rb_long2int_inline.
Definition long.h:62
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define ALLOCA_N(type, n)
Definition memory.h:292
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE type(ANYARGS)
ANYARGS-ed function type.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define RBIMPL_ATTR_NONNULL(list)
Wraps (or simulates) __attribute__((nonnull))
Definition nonnull.h:30
#define PRI_PIDT_PREFIX
A rb_sprintf() format prefix to be used for a pid_t parameter.
Definition pid_t.h:38
#define rb_fd_init
Initialises the :given :rb_fdset_t.
Definition posix.h:63
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
C99 shim for <stdbool.h>
Definition dir.h:21
Definition dir.h:13
The data structure which wraps the fd_set bitmap used by select(2).
Definition largesize.h:71
fd_set * fdset
File descriptors buffer.
Definition largesize.h:73
int capa
Maximum allowed number of FDs.
Definition win32.h:50
Definition st.h:79
Definition win32.h:701
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
Definition value.h:63
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40