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