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