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