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#include "ruby/ruby.h"
23#include "ruby/encoding.h"
24#include "dln.h"
25#include <fcntl.h>
26#include <process.h>
27#include <sys/stat.h>
28/* #include <sys/wait.h> */
29#include <stdio.h>
30#include <stdlib.h>
31#include <errno.h>
32#include <assert.h>
33#include <ctype.h>
34
35#include <windows.h>
36#include <winbase.h>
37#include <wincon.h>
38#include <share.h>
39#include <shlobj.h>
40#include <mbstring.h>
41#if _MSC_VER >= 1400
42#include <crtdbg.h>
43#include <rtcapi.h>
44#endif
45#ifdef __MINGW32__
46#include <mswsock.h>
47#endif
48#include "ruby/win32.h"
49#include "win32/dir.h"
50#define isdirsep(x) ((x) == '/' || (x) == '\\')
51
52#undef stat
53#undef fclose
54#undef close
55#undef setsockopt
56
57#if defined __BORLANDC__
58#  define _filbuf _fgetc
59#  define _flsbuf _fputc
60#  define enough_to_get(n) (--(n) >= 0)
61#  define enough_to_put(n) (++(n) < 0)
62#else
63#  define enough_to_get(n) (--(n) >= 0)
64#  define enough_to_put(n) (--(n) >= 0)
65#endif
66
67#ifdef WIN32_DEBUG
68#define Debug(something) something
69#else
70#define Debug(something) /* nothing */
71#endif
72
73#define TO_SOCKET(x)	_get_osfhandle(x)
74
75static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE, DWORD);
76static int has_redirection(const char *);
77int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
78static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
79static int wstati64(const WCHAR *path, struct stati64 *st);
80VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
81
82#define RUBY_CRITICAL(expr) do { expr; } while (0)
83
84/* errno mapping */
85static struct {
86    DWORD winerr;
87    int err;
88} errmap[] = {
89    {	ERROR_INVALID_FUNCTION,		EINVAL		},
90    {	ERROR_FILE_NOT_FOUND,		ENOENT		},
91    {	ERROR_PATH_NOT_FOUND,		ENOENT		},
92    {	ERROR_TOO_MANY_OPEN_FILES,	EMFILE		},
93    {	ERROR_ACCESS_DENIED,		EACCES		},
94    {	ERROR_INVALID_HANDLE,		EBADF		},
95    {	ERROR_ARENA_TRASHED,		ENOMEM		},
96    {	ERROR_NOT_ENOUGH_MEMORY,	ENOMEM		},
97    {	ERROR_INVALID_BLOCK,		ENOMEM		},
98    {	ERROR_BAD_ENVIRONMENT,		E2BIG		},
99    {	ERROR_BAD_FORMAT,		ENOEXEC		},
100    {	ERROR_INVALID_ACCESS,		EINVAL		},
101    {	ERROR_INVALID_DATA,		EINVAL		},
102    {	ERROR_INVALID_DRIVE,		ENOENT		},
103    {	ERROR_CURRENT_DIRECTORY,	EACCES		},
104    {	ERROR_NOT_SAME_DEVICE,		EXDEV		},
105    {	ERROR_NO_MORE_FILES,		ENOENT		},
106    {	ERROR_WRITE_PROTECT,		EROFS		},
107    {	ERROR_BAD_UNIT,			ENODEV		},
108    {	ERROR_NOT_READY,		ENXIO		},
109    {	ERROR_BAD_COMMAND,		EACCES		},
110    {	ERROR_CRC,			EACCES		},
111    {	ERROR_BAD_LENGTH,		EACCES		},
112    {	ERROR_SEEK,			EIO		},
113    {	ERROR_NOT_DOS_DISK,		EACCES		},
114    {	ERROR_SECTOR_NOT_FOUND,		EACCES		},
115    {	ERROR_OUT_OF_PAPER,		EACCES		},
116    {	ERROR_WRITE_FAULT,		EIO		},
117    {	ERROR_READ_FAULT,		EIO		},
118    {	ERROR_GEN_FAILURE,		EACCES		},
119    {	ERROR_LOCK_VIOLATION,		EACCES		},
120    {	ERROR_SHARING_VIOLATION,	EACCES		},
121    {	ERROR_WRONG_DISK,		EACCES		},
122    {	ERROR_SHARING_BUFFER_EXCEEDED,	EACCES		},
123    {	ERROR_BAD_NETPATH,		ENOENT		},
124    {	ERROR_NETWORK_ACCESS_DENIED,	EACCES		},
125    {	ERROR_BAD_NET_NAME,		ENOENT		},
126    {	ERROR_FILE_EXISTS,		EEXIST		},
127    {	ERROR_CANNOT_MAKE,		EACCES		},
128    {	ERROR_FAIL_I24,			EACCES		},
129    {	ERROR_INVALID_PARAMETER,	EINVAL		},
130    {	ERROR_NO_PROC_SLOTS,		EAGAIN		},
131    {	ERROR_DRIVE_LOCKED,		EACCES		},
132    {	ERROR_BROKEN_PIPE,		EPIPE		},
133    {	ERROR_DISK_FULL,		ENOSPC		},
134    {	ERROR_INVALID_TARGET_HANDLE,	EBADF		},
135    {	ERROR_INVALID_HANDLE,		EINVAL		},
136    {	ERROR_WAIT_NO_CHILDREN,		ECHILD		},
137    {	ERROR_CHILD_NOT_COMPLETE,	ECHILD		},
138    {	ERROR_DIRECT_ACCESS_HANDLE,	EBADF		},
139    {	ERROR_NEGATIVE_SEEK,		EINVAL		},
140    {	ERROR_SEEK_ON_DEVICE,		EACCES		},
141    {	ERROR_DIR_NOT_EMPTY,		ENOTEMPTY	},
142    {	ERROR_DIRECTORY,		ENOTDIR		},
143    {	ERROR_NOT_LOCKED,		EACCES		},
144    {	ERROR_BAD_PATHNAME,		ENOENT		},
145    {	ERROR_MAX_THRDS_REACHED,	EAGAIN		},
146    {	ERROR_LOCK_FAILED,		EACCES		},
147    {	ERROR_ALREADY_EXISTS,		EEXIST		},
148    {	ERROR_INVALID_STARTING_CODESEG,	ENOEXEC		},
149    {	ERROR_INVALID_STACKSEG,		ENOEXEC		},
150    {	ERROR_INVALID_MODULETYPE,	ENOEXEC		},
151    {	ERROR_INVALID_EXE_SIGNATURE,	ENOEXEC		},
152    {	ERROR_EXE_MARKED_INVALID,	ENOEXEC		},
153    {	ERROR_BAD_EXE_FORMAT,		ENOEXEC		},
154    {	ERROR_ITERATED_DATA_EXCEEDS_64k,ENOEXEC		},
155    {	ERROR_INVALID_MINALLOCSIZE,	ENOEXEC		},
156    {	ERROR_DYNLINK_FROM_INVALID_RING,ENOEXEC		},
157    {	ERROR_IOPL_NOT_ENABLED,		ENOEXEC		},
158    {	ERROR_INVALID_SEGDPL,		ENOEXEC		},
159    {	ERROR_AUTODATASEG_EXCEEDS_64k,	ENOEXEC		},
160    {	ERROR_RING2SEG_MUST_BE_MOVABLE,	ENOEXEC		},
161    {	ERROR_RELOC_CHAIN_XEEDS_SEGLIM,	ENOEXEC		},
162    {	ERROR_INFLOOP_IN_RELOC_CHAIN,	ENOEXEC		},
163    {	ERROR_FILENAME_EXCED_RANGE,	ENOENT		},
164    {	ERROR_NESTING_NOT_ALLOWED,	EAGAIN		},
165#ifndef ERROR_PIPE_LOCAL
166#define ERROR_PIPE_LOCAL	229L
167#endif
168    {	ERROR_PIPE_LOCAL,		EPIPE		},
169    {	ERROR_BAD_PIPE,			EPIPE		},
170    {	ERROR_PIPE_BUSY,		EAGAIN		},
171    {	ERROR_NO_DATA,			EPIPE		},
172    {	ERROR_PIPE_NOT_CONNECTED,	EPIPE		},
173    {	ERROR_OPERATION_ABORTED,	EINTR		},
174    {	ERROR_NOT_ENOUGH_QUOTA,		ENOMEM		},
175    {	ERROR_MOD_NOT_FOUND,		ENOENT		},
176    {	WSAEINTR,			EINTR		},
177    {	WSAEBADF,			EBADF		},
178    {	WSAEACCES,			EACCES		},
179    {	WSAEFAULT,			EFAULT		},
180    {	WSAEINVAL,			EINVAL		},
181    {	WSAEMFILE,			EMFILE		},
182    {	WSAEWOULDBLOCK,			EWOULDBLOCK	},
183    {	WSAEINPROGRESS,			EINPROGRESS	},
184    {	WSAEALREADY,			EALREADY	},
185    {	WSAENOTSOCK,			ENOTSOCK	},
186    {	WSAEDESTADDRREQ,		EDESTADDRREQ	},
187    {	WSAEMSGSIZE,			EMSGSIZE	},
188    {	WSAEPROTOTYPE,			EPROTOTYPE	},
189    {	WSAENOPROTOOPT,			ENOPROTOOPT	},
190    {	WSAEPROTONOSUPPORT,		EPROTONOSUPPORT	},
191    {	WSAESOCKTNOSUPPORT,		ESOCKTNOSUPPORT	},
192    {	WSAEOPNOTSUPP,			EOPNOTSUPP	},
193    {	WSAEPFNOSUPPORT,		EPFNOSUPPORT	},
194    {	WSAEAFNOSUPPORT,		EAFNOSUPPORT	},
195    {	WSAEADDRINUSE,			EADDRINUSE	},
196    {	WSAEADDRNOTAVAIL,		EADDRNOTAVAIL	},
197    {	WSAENETDOWN,			ENETDOWN	},
198    {	WSAENETUNREACH,			ENETUNREACH	},
199    {	WSAENETRESET,			ENETRESET	},
200    {	WSAECONNABORTED,		ECONNABORTED	},
201    {	WSAECONNRESET,			ECONNRESET	},
202    {	WSAENOBUFS,			ENOBUFS		},
203    {	WSAEISCONN,			EISCONN		},
204    {	WSAENOTCONN,			ENOTCONN	},
205    {	WSAESHUTDOWN,			ESHUTDOWN	},
206    {	WSAETOOMANYREFS,		ETOOMANYREFS	},
207    {	WSAETIMEDOUT,			ETIMEDOUT	},
208    {	WSAECONNREFUSED,		ECONNREFUSED	},
209    {	WSAELOOP,			ELOOP		},
210    {	WSAENAMETOOLONG,		ENAMETOOLONG	},
211    {	WSAEHOSTDOWN,			EHOSTDOWN	},
212    {	WSAEHOSTUNREACH,		EHOSTUNREACH	},
213    {	WSAEPROCLIM,			EPROCLIM	},
214    {	WSAENOTEMPTY,			ENOTEMPTY	},
215    {	WSAEUSERS,			EUSERS		},
216    {	WSAEDQUOT,			EDQUOT		},
217    {	WSAESTALE,			ESTALE		},
218    {	WSAEREMOTE,			EREMOTE		},
219};
220
221/* License: Ruby's */
222int
223rb_w32_map_errno(DWORD winerr)
224{
225    int i;
226
227    if (winerr == 0) {
228	return 0;
229    }
230
231    for (i = 0; i < (int)(sizeof(errmap) / sizeof(*errmap)); i++) {
232	if (errmap[i].winerr == winerr) {
233	    return errmap[i].err;
234	}
235    }
236
237    if (winerr >= WSABASEERR) {
238	return winerr;
239    }
240    return EINVAL;
241}
242
243#define map_errno rb_w32_map_errno
244
245static const char *NTLoginName;
246
247static OSVERSIONINFO osver;
248
249/* License: Artistic or GPL */
250static void
251get_version(void)
252{
253    memset(&osver, 0, sizeof(OSVERSIONINFO));
254    osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
255    GetVersionEx(&osver);
256}
257
258#ifdef _M_IX86
259/* License: Artistic or GPL */
260DWORD
261rb_w32_osid(void)
262{
263    return osver.dwPlatformId;
264}
265#endif
266
267/* License: Artistic or GPL */
268DWORD
269rb_w32_osver(void)
270{
271    return osver.dwMajorVersion;
272}
273
274/* simulate flock by locking a range on the file */
275
276/* License: Artistic or GPL */
277#define LK_ERR(f,i) \
278    do {								\
279	if (f)								\
280	    i = 0;							\
281	else {								\
282	    DWORD err = GetLastError();					\
283	    if (err == ERROR_LOCK_VIOLATION || err == ERROR_IO_PENDING)	\
284		errno = EWOULDBLOCK;					\
285	    else if (err == ERROR_NOT_LOCKED)				\
286		i = 0;							\
287	    else							\
288		errno = map_errno(err);					\
289	}								\
290    } while (0)
291#define LK_LEN      ULONG_MAX
292
293/* License: Artistic or GPL */
294static uintptr_t
295flock_winnt(uintptr_t self, int argc, uintptr_t* argv)
296{
297    OVERLAPPED o;
298    int i = -1;
299    const HANDLE fh = (HANDLE)self;
300    const int oper = argc;
301
302    memset(&o, 0, sizeof(o));
303
304    switch(oper) {
305      case LOCK_SH:		/* shared lock */
306	LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, LK_LEN, &o), i);
307	break;
308      case LOCK_EX:		/* exclusive lock */
309	LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, LK_LEN, &o), i);
310	break;
311      case LOCK_SH|LOCK_NB:	/* non-blocking shared lock */
312	LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, LK_LEN, &o), i);
313	break;
314      case LOCK_EX|LOCK_NB:	/* non-blocking exclusive lock */
315	LK_ERR(LockFileEx(fh,
316			  LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
317			  0, LK_LEN, LK_LEN, &o), i);
318	break;
319      case LOCK_UN:		/* unlock lock */
320      case LOCK_UN|LOCK_NB:	/* unlock is always non-blocking, I hope */
321	LK_ERR(UnlockFileEx(fh, 0, LK_LEN, LK_LEN, &o), i);
322	break;
323      default:            /* unknown */
324	errno = EINVAL;
325	break;
326    }
327    return i;
328}
329
330#undef LK_ERR
331
332/* License: Artistic or GPL */
333int
334flock(int fd, int oper)
335{
336    const asynchronous_func_t locker = flock_winnt;
337
338    return rb_w32_asynchronize(locker,
339			      (VALUE)_get_osfhandle(fd), oper, NULL,
340			      (DWORD)-1);
341}
342
343/* License: Ruby's */
344static inline WCHAR *
345translate_wchar(WCHAR *p, int from, int to)
346{
347    for (; *p; p++) {
348	if (*p == from)
349	    *p = to;
350    }
351    return p;
352}
353
354/* License: Ruby's */
355static inline char *
356translate_char(char *p, int from, int to)
357{
358    while (*p) {
359	if ((unsigned char)*p == from)
360	    *p = to;
361	p = CharNext(p);
362    }
363    return p;
364}
365
366#ifndef CSIDL_LOCAL_APPDATA
367#define CSIDL_LOCAL_APPDATA 28
368#endif
369#ifndef CSIDL_COMMON_APPDATA
370#define CSIDL_COMMON_APPDATA 35
371#endif
372#ifndef CSIDL_WINDOWS
373#define CSIDL_WINDOWS	36
374#endif
375#ifndef CSIDL_SYSTEM
376#define CSIDL_SYSTEM	37
377#endif
378#ifndef CSIDL_PROFILE
379#define CSIDL_PROFILE 40
380#endif
381
382/* License: Ruby's */
383static BOOL
384get_special_folder(int n, WCHAR *env)
385{
386    LPITEMIDLIST pidl;
387    LPMALLOC alloc;
388    BOOL f = FALSE;
389    if (SHGetSpecialFolderLocation(NULL, n, &pidl) == 0) {
390	f = SHGetPathFromIDListW(pidl, env);
391	SHGetMalloc(&alloc);
392	alloc->lpVtbl->Free(alloc, pidl);
393	alloc->lpVtbl->Release(alloc);
394    }
395    return f;
396}
397
398/* License: Ruby's */
399static void
400regulate_path(WCHAR *path)
401{
402    WCHAR *p = translate_wchar(path, L'\\', L'/');
403    if (p - path == 2 && path[1] == L':') {
404	*p++ = L'/';
405	*p = L'\0';
406    }
407}
408
409/* License: Ruby's */
410static FARPROC
411get_proc_address(const char *module, const char *func, HANDLE *mh)
412{
413    HANDLE h;
414    FARPROC ptr;
415
416    if (mh)
417	h = LoadLibrary(module);
418    else
419	h = GetModuleHandle(module);
420    if (!h)
421	return NULL;
422
423    ptr = GetProcAddress(h, func);
424    if (mh) {
425	if (ptr)
426	    *mh = h;
427	else
428	    FreeLibrary(h);
429    }
430    return ptr;
431}
432
433/* License: Ruby's */
434static UINT
435get_system_directory(WCHAR *path, UINT len)
436{
437    typedef UINT WINAPI wgetdir_func(WCHAR*, UINT);
438    FARPROC ptr =
439	get_proc_address("kernel32", "GetSystemWindowsDirectoryW", NULL);
440    if (ptr)
441	return (*(wgetdir_func *)ptr)(path, len);
442    return GetWindowsDirectoryW(path, len);
443}
444
445#define numberof(array) (sizeof(array) / sizeof(*array))
446
447/* License: Ruby's */
448VALUE
449rb_w32_special_folder(int type)
450{
451    WCHAR path[_MAX_PATH];
452
453    if (!get_special_folder(type, path)) return Qnil;
454    regulate_path(path);
455    return rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
456}
457
458/* License: Ruby's */
459UINT
460rb_w32_system_tmpdir(WCHAR *path, UINT len)
461{
462    static const WCHAR temp[] = L"temp";
463    WCHAR *p;
464
465    if (!get_special_folder(CSIDL_LOCAL_APPDATA, path)) {
466	if (get_system_directory(path, len)) return 0;
467    }
468    p = translate_wchar(path, L'\\', L'/');
469    if (*(p - 1) != L'/') *p++ = L'/';
470    if (p - path + numberof(temp) >= len) return 0;
471    memcpy(p, temp, sizeof(temp));
472    return p - path + numberof(temp) - 1;
473}
474
475/* License: Ruby's */
476static void
477init_env(void)
478{
479    static const WCHAR TMPDIR[] = L"TMPDIR";
480    struct {WCHAR name[6], eq, val[_MAX_PATH];} wk;
481    DWORD len;
482    BOOL f;
483#define env wk.val
484#define set_env_val(vname) do { \
485	typedef char namesizecheck[numberof(wk.name) < numberof(vname) - 1 ? -1 : 1]; \
486	WCHAR *const buf = wk.name + numberof(wk.name) - numberof(vname) + 1; \
487	MEMCPY(buf, vname, WCHAR, numberof(vname) - 1); \
488	_wputenv(buf); \
489    } while (0)
490
491    wk.eq = L'=';
492
493    if (!GetEnvironmentVariableW(L"HOME", env, numberof(env))) {
494	f = FALSE;
495	if (GetEnvironmentVariableW(L"HOMEDRIVE", env, numberof(env)))
496	    len = lstrlenW(env);
497	else
498	    len = 0;
499	if (GetEnvironmentVariableW(L"HOMEPATH", env + len, numberof(env) - len) || len) {
500	    f = TRUE;
501	}
502	else if (GetEnvironmentVariableW(L"USERPROFILE", env, numberof(env))) {
503	    f = TRUE;
504	}
505	else if (get_special_folder(CSIDL_PROFILE, env)) {
506	    f = TRUE;
507	}
508	else if (get_special_folder(CSIDL_PERSONAL, env)) {
509	    f = TRUE;
510	}
511	if (f) {
512	    regulate_path(env);
513	    set_env_val(L"HOME");
514	}
515    }
516
517    if (!GetEnvironmentVariableW(L"USER", env, numberof(env))) {
518	if (!GetEnvironmentVariableW(L"USERNAME", env, numberof(env)) &&
519	    !GetUserNameW(env, (len = numberof(env), &len))) {
520	    NTLoginName = "<Unknown>";
521	    return;
522	}
523	set_env_val(L"USER");
524    }
525    NTLoginName = strdup(rb_w32_getenv("USER"));
526
527    if (!GetEnvironmentVariableW(TMPDIR, env, numberof(env)) &&
528	!GetEnvironmentVariableW(L"TMP", env, numberof(env)) &&
529	!GetEnvironmentVariableW(L"TEMP", env, numberof(env)) &&
530	rb_w32_system_tmpdir(env, numberof(env))) {
531	set_env_val(TMPDIR);
532    }
533
534#undef env
535#undef set_env_val
536}
537
538
539typedef BOOL (WINAPI *cancel_io_t)(HANDLE);
540static cancel_io_t cancel_io = NULL;
541
542/* License: Ruby's */
543static void
544init_func(void)
545{
546    if (!cancel_io)
547	cancel_io = (cancel_io_t)get_proc_address("kernel32", "CancelIo", NULL);
548}
549
550static void init_stdhandle(void);
551
552#if RUBY_MSVCRT_VERSION >= 80
553/* License: Ruby's */
554static void
555invalid_parameter(const wchar_t *expr, const wchar_t *func, const wchar_t *file, unsigned int line, uintptr_t dummy)
556{
557    // nothing to do
558}
559
560int ruby_w32_rtc_error;
561
562/* License: Ruby's */
563static int __cdecl
564rtc_error_handler(int e, const char *src, int line, const char *exe, const char *fmt, ...)
565{
566    va_list ap;
567    VALUE str;
568
569    if (!ruby_w32_rtc_error) return 0;
570    str = rb_sprintf("%s:%d: ", src, line);
571    va_start(ap, fmt);
572    rb_str_vcatf(str, fmt, ap);
573    va_end(ap);
574    rb_str_cat(str, "\n", 1);
575    rb_write_error2(RSTRING_PTR(str), RSTRING_LEN(str));
576    return 0;
577}
578#endif
579
580static CRITICAL_SECTION select_mutex;
581static int NtSocketsInitialized = 0;
582static st_table *socklist = NULL;
583static st_table *conlist = NULL;
584static char *envarea;
585static char *uenvarea;
586
587/* License: Ruby's */
588struct constat {
589    struct {
590	int state, seq[16];
591	WORD attr;
592	COORD saved;
593    } vt100;
594};
595enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
596
597/* License: Ruby's */
598static int
599free_conlist(st_data_t key, st_data_t val, st_data_t arg)
600{
601    xfree((struct constat *)val);
602    return ST_DELETE;
603}
604
605/* License: Ruby's */
606static void
607constat_delete(HANDLE h)
608{
609    if (conlist) {
610	st_data_t key = (st_data_t)h, val;
611	st_delete(conlist, &key, &val);
612	xfree((struct constat *)val);
613    }
614}
615
616/* License: Ruby's */
617static void
618exit_handler(void)
619{
620    if (NtSocketsInitialized) {
621	WSACleanup();
622	if (socklist) {
623	    st_free_table(socklist);
624	    socklist = NULL;
625	}
626	DeleteCriticalSection(&select_mutex);
627	NtSocketsInitialized = 0;
628    }
629    if (conlist) {
630	st_foreach(conlist, free_conlist, 0);
631	st_free_table(conlist);
632	conlist = NULL;
633    }
634    if (envarea) {
635	FreeEnvironmentStrings(envarea);
636	envarea = NULL;
637    }
638    if (uenvarea) {
639	free(uenvarea);
640	uenvarea = NULL;
641    }
642}
643
644/* License: Artistic or GPL */
645static void
646StartSockets(void)
647{
648    WORD version;
649    WSADATA retdata;
650
651    //
652    // initalize the winsock interface and insure that it's
653    // cleaned up at exit.
654    //
655    version = MAKEWORD(2, 0);
656    if (WSAStartup(version, &retdata))
657	rb_fatal ("Unable to locate winsock library!\n");
658    if (LOBYTE(retdata.wVersion) != 2)
659	rb_fatal("could not find version 2 of winsock dll\n");
660
661    InitializeCriticalSection(&select_mutex);
662
663    NtSocketsInitialized = 1;
664}
665
666#define MAKE_SOCKDATA(af, fl)	((int)((((int)af)<<4)|((fl)&0xFFFF)))
667#define GET_FAMILY(v)		((int)(((v)>>4)&0xFFFF))
668#define GET_FLAGS(v)		((int)((v)&0xFFFF))
669
670/* License: Ruby's */
671static inline int
672socklist_insert(SOCKET sock, int flag)
673{
674    if (!socklist)
675	socklist = st_init_numtable();
676    return st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
677}
678
679/* License: Ruby's */
680static inline int
681socklist_lookup(SOCKET sock, int *flagp)
682{
683    st_data_t data;
684    int ret;
685
686    if (!socklist)
687	return 0;
688    ret = st_lookup(socklist, (st_data_t)sock, (st_data_t *)&data);
689    if (ret && flagp)
690	*flagp = (int)data;
691
692    return ret;
693}
694
695/* License: Ruby's */
696static inline int
697socklist_delete(SOCKET *sockp, int *flagp)
698{
699    st_data_t key;
700    st_data_t data;
701    int ret;
702
703    if (!socklist)
704	return 0;
705    key = (st_data_t)*sockp;
706    if (flagp)
707	data = (st_data_t)*flagp;
708    ret = st_delete(socklist, &key, &data);
709    if (ret) {
710	*sockp = (SOCKET)key;
711	if (flagp)
712	    *flagp = (int)data;
713    }
714
715    return ret;
716}
717
718//
719// Initialization stuff
720//
721/* License: Ruby's */
722void
723rb_w32_sysinit(int *argc, char ***argv)
724{
725#if RUBY_MSVCRT_VERSION >= 80
726    static void set_pioinfo_extra(void);
727
728    _CrtSetReportMode(_CRT_ASSERT, 0);
729    _set_invalid_parameter_handler(invalid_parameter);
730    _RTC_SetErrorFunc(rtc_error_handler);
731    set_pioinfo_extra();
732#else
733    SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
734#endif
735
736    get_version();
737
738    //
739    // subvert cmd.exe's feeble attempt at command line parsing
740    //
741    *argc = rb_w32_cmdvector(GetCommandLine(), argv);
742
743    //
744    // Now set up the correct time stuff
745    //
746
747    tzset();
748
749    init_env();
750
751    init_func();
752
753    init_stdhandle();
754
755    atexit(exit_handler);
756
757    // Initialize Winsock
758    StartSockets();
759}
760
761char *
762getlogin(void)
763{
764    return (char *)NTLoginName;
765}
766
767#define MAXCHILDNUM 256	/* max num of child processes */
768
769/* License: Ruby's */
770static struct ChildRecord {
771    HANDLE hProcess;	/* process handle */
772    rb_pid_t pid;	/* process id */
773} ChildRecord[MAXCHILDNUM];
774
775/* License: Ruby's */
776#define FOREACH_CHILD(v) do { \
777    struct ChildRecord* v; \
778    for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v)
779#define END_FOREACH_CHILD } while (0)
780
781/* License: Ruby's */
782static struct ChildRecord *
783FindChildSlot(rb_pid_t pid)
784{
785
786    FOREACH_CHILD(child) {
787	if (child->pid == pid) {
788	    return child;
789	}
790    } END_FOREACH_CHILD;
791    return NULL;
792}
793
794/* License: Ruby's */
795static struct ChildRecord *
796FindChildSlotByHandle(HANDLE h)
797{
798
799    FOREACH_CHILD(child) {
800	if (child->hProcess == h) {
801	    return child;
802	}
803    } END_FOREACH_CHILD;
804    return NULL;
805}
806
807/* License: Ruby's */
808static void
809CloseChildHandle(struct ChildRecord *child)
810{
811    HANDLE h = child->hProcess;
812    child->hProcess = NULL;
813    child->pid = 0;
814    CloseHandle(h);
815}
816
817/* License: Ruby's */
818static struct ChildRecord *
819FindFreeChildSlot(void)
820{
821    FOREACH_CHILD(child) {
822	if (!child->pid) {
823	    child->pid = -1;	/* lock the slot */
824	    child->hProcess = NULL;
825	    return child;
826	}
827    } END_FOREACH_CHILD;
828    return NULL;
829}
830
831
832/*
833  ruby -lne 'BEGIN{$cmds = Hash.new(0); $mask = 1}'
834   -e '$cmds[$_.downcase] |= $mask' -e '$mask <<= 1 if ARGF.eof'
835   -e 'END{$cmds.sort.each{|n,f|puts "    \"\\#{f.to_s(8)}\" #{n.dump} + 1,"}}'
836   98cmd ntcmd
837 */
838static const char *const szInternalCmds[] = {
839    "\2" "assoc",
840    "\3" "break",
841    "\3" "call",
842    "\3" "cd",
843    "\1" "chcp",
844    "\3" "chdir",
845    "\3" "cls",
846    "\2" "color",
847    "\3" "copy",
848    "\1" "ctty",
849    "\3" "date",
850    "\3" "del",
851    "\3" "dir",
852    "\3" "echo",
853    "\2" "endlocal",
854    "\3" "erase",
855    "\3" "exit",
856    "\3" "for",
857    "\2" "ftype",
858    "\3" "goto",
859    "\3" "if",
860    "\1" "lfnfor",
861    "\1" "lh",
862    "\1" "lock",
863    "\3" "md",
864    "\3" "mkdir",
865    "\2" "move",
866    "\3" "path",
867    "\3" "pause",
868    "\2" "popd",
869    "\3" "prompt",
870    "\2" "pushd",
871    "\3" "rd",
872    "\3" "rem",
873    "\3" "ren",
874    "\3" "rename",
875    "\3" "rmdir",
876    "\3" "set",
877    "\2" "setlocal",
878    "\3" "shift",
879    "\2" "start",
880    "\3" "time",
881    "\2" "title",
882    "\1" "truename",
883    "\3" "type",
884    "\1" "unlock",
885    "\3" "ver",
886    "\3" "verify",
887    "\3" "vol",
888};
889
890/* License: Ruby's */
891static int
892internal_match(const void *key, const void *elem)
893{
894    return strcmp(key, (*(const char *const *)elem) + 1);
895}
896
897/* License: Ruby's */
898static int
899is_command_com(const char *interp)
900{
901    int i = strlen(interp) - 11;
902
903    if ((i == 0 || i > 0 && isdirsep(interp[i-1])) &&
904	strcasecmp(interp+i, "command.com") == 0) {
905	return 1;
906    }
907    return 0;
908}
909
910static int internal_cmd_match(const char *cmdname, int nt);
911
912/* License: Ruby's */
913static int
914is_internal_cmd(const char *cmd, int nt)
915{
916    char cmdname[9], *b = cmdname, c;
917
918    do {
919	if (!(c = *cmd++)) return 0;
920    } while (isspace(c));
921    if (c == '@')
922	return 1;
923    while (isalpha(c)) {
924	*b++ = tolower(c);
925	if (b == cmdname + sizeof(cmdname)) return 0;
926	c = *cmd++;
927    }
928    if (c == '.') c = *cmd;
929    switch (c) {
930      case '<': case '>': case '|':
931	return 1;
932      case '\0': case ' ': case '\t': case '\n':
933	break;
934      default:
935	return 0;
936    }
937    *b = 0;
938    return internal_cmd_match(cmdname, nt);
939}
940
941/* License: Ruby's */
942static int
943internal_cmd_match(const char *cmdname, int nt)
944{
945    char **nm;
946
947    nm = bsearch(cmdname, szInternalCmds,
948		 sizeof(szInternalCmds) / sizeof(*szInternalCmds),
949		 sizeof(*szInternalCmds),
950		 internal_match);
951    if (!nm || !(nm[0][0] & (nt ? 2 : 1)))
952	return 0;
953    return 1;
954}
955
956/* License: Ruby's */
957SOCKET
958rb_w32_get_osfhandle(int fh)
959{
960    return _get_osfhandle(fh);
961}
962
963/* License: Ruby's */
964static int
965join_argv(char *cmd, char *const *argv, BOOL escape)
966{
967    const char *p, *s;
968    char *q, *const *t;
969    int len, n, bs, quote;
970
971    for (t = argv, q = cmd, len = 0; p = *t; t++) {
972	quote = 0;
973	s = p;
974	if (!*p || strpbrk(p, " \t\"'")) {
975	    quote = 1;
976	    len++;
977	    if (q) *q++ = '"';
978	}
979	for (bs = 0; *p; ++p) {
980	    switch (*p) {
981	      case '\\':
982		++bs;
983		break;
984	      case '"':
985		len += n = p - s;
986		if (q) {
987		    memcpy(q, s, n);
988		    q += n;
989		}
990		s = p;
991		len += ++bs;
992		if (q) {
993		    memset(q, '\\', bs);
994		    q += bs;
995		}
996		bs = 0;
997		break;
998	      case '<': case '>': case '|': case '^':
999		if (escape && !quote) {
1000		    len += (n = p - s) + 1;
1001		    if (q) {
1002			memcpy(q, s, n);
1003			q += n;
1004			*q++ = '^';
1005		    }
1006		    s = p;
1007		    break;
1008		}
1009	      default:
1010		bs = 0;
1011		p = CharNext(p) - 1;
1012		break;
1013	    }
1014	}
1015	len += (n = p - s) + 1;
1016	if (quote) len++;
1017	if (q) {
1018	    memcpy(q, s, n);
1019	    q += n;
1020	    if (quote) *q++ = '"';
1021	    *q++ = ' ';
1022	}
1023    }
1024    if (q > cmd) --len;
1025    if (q) {
1026	if (q > cmd) --q;
1027	*q = '\0';
1028    }
1029    return len;
1030}
1031
1032#ifdef HAVE_SYS_PARAM_H
1033# include <sys/param.h>
1034#else
1035# define MAXPATHLEN 512
1036#endif
1037
1038/* License: Ruby's */
1039#define STRNDUPV(ptr, v, src, len)					\
1040    (((char *)memcpy(((ptr) = ALLOCV((v), (len) + 1)), (src), (len)))[len] = 0)
1041
1042/* License: Ruby's */
1043static int
1044check_spawn_mode(int mode)
1045{
1046    switch (mode) {
1047      case P_NOWAIT:
1048      case P_OVERLAY:
1049	return 0;
1050      default:
1051	errno = EINVAL;
1052	return -1;
1053    }
1054}
1055
1056/* License: Ruby's */
1057static rb_pid_t
1058child_result(struct ChildRecord *child, int mode)
1059{
1060    DWORD exitcode;
1061
1062    if (!child) {
1063	return -1;
1064    }
1065
1066    if (mode == P_OVERLAY) {
1067	WaitForSingleObject(child->hProcess, INFINITE);
1068	GetExitCodeProcess(child->hProcess, &exitcode);
1069	CloseChildHandle(child);
1070	_exit(exitcode);
1071    }
1072    return child->pid;
1073}
1074
1075/* License: Ruby's */
1076static struct ChildRecord *
1077CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
1078	    HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
1079{
1080    BOOL fRet;
1081    STARTUPINFOW aStartupInfo;
1082    PROCESS_INFORMATION aProcessInformation;
1083    SECURITY_ATTRIBUTES sa;
1084    struct ChildRecord *child;
1085
1086    if (!cmd && !prog) {
1087	errno = EFAULT;
1088	return NULL;
1089    }
1090
1091    child = FindFreeChildSlot();
1092    if (!child) {
1093	errno = EAGAIN;
1094	return NULL;
1095    }
1096
1097    if (!psa) {
1098	sa.nLength              = sizeof (SECURITY_ATTRIBUTES);
1099	sa.lpSecurityDescriptor = NULL;
1100	sa.bInheritHandle       = TRUE;
1101	psa = &sa;
1102    }
1103
1104    memset(&aStartupInfo, 0, sizeof(aStartupInfo));
1105    memset(&aProcessInformation, 0, sizeof(aProcessInformation));
1106    aStartupInfo.cb = sizeof(aStartupInfo);
1107    aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
1108    if (hInput) {
1109	aStartupInfo.hStdInput  = hInput;
1110    }
1111    else {
1112	aStartupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
1113    }
1114    if (hOutput) {
1115	aStartupInfo.hStdOutput = hOutput;
1116    }
1117    else {
1118	aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1119    }
1120    if (hError) {
1121	aStartupInfo.hStdError = hError;
1122    }
1123    else {
1124	aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1125    }
1126
1127    dwCreationFlags |= NORMAL_PRIORITY_CLASS;
1128
1129    if (lstrlenW(cmd) > 32767) {
1130	child->pid = 0;		/* release the slot */
1131	errno = E2BIG;
1132	return NULL;
1133    }
1134
1135    RUBY_CRITICAL({
1136	fRet = CreateProcessW(prog, (WCHAR *)cmd, psa, psa,
1137			      psa->bInheritHandle, dwCreationFlags, NULL, NULL,
1138			      &aStartupInfo, &aProcessInformation);
1139	errno = map_errno(GetLastError());
1140    });
1141
1142    if (!fRet) {
1143	child->pid = 0;		/* release the slot */
1144	return NULL;
1145    }
1146
1147    CloseHandle(aProcessInformation.hThread);
1148
1149    child->hProcess = aProcessInformation.hProcess;
1150    child->pid = (rb_pid_t)aProcessInformation.dwProcessId;
1151
1152    return child;
1153}
1154
1155/* License: Ruby's */
1156static int
1157is_batch(const char *cmd)
1158{
1159    int len = strlen(cmd);
1160    if (len <= 4) return 0;
1161    cmd += len - 4;
1162    if (*cmd++ != '.') return 0;
1163    if (strcasecmp(cmd, "bat") == 0) return 1;
1164    if (strcasecmp(cmd, "cmd") == 0) return 1;
1165    return 0;
1166}
1167
1168static UINT filecp(void);
1169static WCHAR *mbstr_to_wstr(UINT, const char *, int, long *);
1170static char *wstr_to_mbstr(UINT, const WCHAR *, int, long *);
1171#define acp_to_wstr(str, plen) mbstr_to_wstr(CP_ACP, str, -1, plen)
1172#define wstr_to_acp(str, plen) wstr_to_mbstr(CP_ACP, str, -1, plen)
1173#define filecp_to_wstr(str, plen) mbstr_to_wstr(filecp(), str, -1, plen)
1174#define wstr_to_filecp(str, plen) wstr_to_mbstr(filecp(), str, -1, plen)
1175#define utf8_to_wstr(str, plen) mbstr_to_wstr(CP_UTF8, str, -1, plen)
1176#define wstr_to_utf8(str, plen) wstr_to_mbstr(CP_UTF8, str, -1, plen)
1177
1178/* License: Artistic or GPL */
1179rb_pid_t
1180rb_w32_spawn(int mode, const char *cmd, const char *prog)
1181{
1182    char fbuf[MAXPATHLEN];
1183    char *p = NULL;
1184    const char *shell = NULL;
1185    WCHAR *wcmd = NULL, *wshell = NULL;
1186    int e = 0;
1187    rb_pid_t ret = -1;
1188    VALUE v = 0;
1189    VALUE v2 = 0;
1190
1191    if (check_spawn_mode(mode)) return -1;
1192
1193    if (prog) {
1194	if (!(p = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1195	    shell = prog;
1196	}
1197	else {
1198	    shell = p;
1199	    translate_char(p, '/', '\\');
1200	}
1201    }
1202    else {
1203	int redir = -1;
1204	int nt;
1205	while (ISSPACE(*cmd)) cmd++;
1206	if ((shell = getenv("RUBYSHELL")) && (redir = has_redirection(cmd))) {
1207	    char *tmp = ALLOCV(v, strlen(shell) + strlen(cmd) + sizeof(" -c ") + 2);
1208	    sprintf(tmp, "%s -c \"%s\"", shell, cmd);
1209	    cmd = tmp;
1210	}
1211	else if ((shell = getenv("COMSPEC")) &&
1212		 (nt = !is_command_com(shell),
1213		  (redir < 0 ? has_redirection(cmd) : redir) ||
1214		  is_internal_cmd(cmd, nt))) {
1215	    char *tmp = ALLOCV(v, strlen(shell) + strlen(cmd) + sizeof(" /c ") + (nt ? 2 : 0));
1216	    sprintf(tmp, nt ? "%s /c \"%s\"" : "%s /c %s", shell, cmd);
1217	    cmd = tmp;
1218	}
1219	else {
1220	    int len = 0, quote = (*cmd == '"') ? '"' : (*cmd == '\'') ? '\'' : 0;
1221	    for (prog = cmd + !!quote;; prog = CharNext(prog)) {
1222		if (!*prog) {
1223		    len = prog - cmd;
1224		    shell = cmd;
1225		    break;
1226		}
1227		if ((unsigned char)*prog == quote) {
1228		    len = prog++ - cmd - 1;
1229		    STRNDUPV(p, v2, cmd + 1, len);
1230		    shell = p;
1231		    break;
1232		}
1233		if (quote) continue;
1234		if (ISSPACE(*prog) || strchr("<>|*?\"", *prog)) {
1235		    len = prog - cmd;
1236		    STRNDUPV(p, v2, cmd, len);
1237		    shell = p;
1238		    break;
1239		}
1240	    }
1241	    shell = dln_find_exe_r(shell, NULL, fbuf, sizeof(fbuf));
1242	    if (!shell) {
1243		shell = p ? p : cmd;
1244	    }
1245	    else {
1246		len = strlen(shell);
1247		if (strchr(shell, ' ')) quote = -1;
1248		if (shell == fbuf) {
1249		    p = fbuf;
1250		}
1251		else if (shell != p && strchr(shell, '/')) {
1252		    STRNDUPV(p, v2, shell, len);
1253		    shell = p;
1254		}
1255		if (p) translate_char(p, '/', '\\');
1256		if (is_batch(shell)) {
1257		    int alen = strlen(prog);
1258		    cmd = p = ALLOCV(v, len + alen + (quote ? 2 : 0) + 1);
1259		    if (quote) *p++ = '"';
1260		    memcpy(p, shell, len);
1261		    p += len;
1262		    if (quote) *p++ = '"';
1263		    memcpy(p, prog, alen + 1);
1264		    shell = 0;
1265		}
1266	    }
1267	}
1268    }
1269
1270    /* assume ACP */
1271    if (!e && cmd && !(wcmd = acp_to_wstr(cmd, NULL))) e = E2BIG;
1272    if (v) ALLOCV_END(v);
1273    if (!e && shell && !(wshell = acp_to_wstr(shell, NULL))) e = E2BIG;
1274    if (v2) ALLOCV_END(v2);
1275
1276    if (!e) {
1277	ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL, 0), mode);
1278    }
1279    free(wshell);
1280    free(wcmd);
1281    if (e) errno = e;
1282    return ret;
1283}
1284
1285/* License: Artistic or GPL */
1286rb_pid_t
1287rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
1288{
1289    int c_switch = 0;
1290    size_t len;
1291    BOOL ntcmd = FALSE, tmpnt;
1292    const char *shell;
1293    char *cmd, fbuf[MAXPATHLEN];
1294    WCHAR *wcmd = NULL, *wprog = NULL;
1295    int e = 0;
1296    rb_pid_t ret = -1;
1297    VALUE v = 0;
1298
1299    if (check_spawn_mode(mode)) return -1;
1300
1301    if (!prog) prog = argv[0];
1302    if ((shell = getenv("COMSPEC")) &&
1303	internal_cmd_match(prog, tmpnt = !is_command_com(shell))) {
1304	ntcmd = tmpnt;
1305	prog = shell;
1306	c_switch = 1;
1307    }
1308    else if ((cmd = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)))) {
1309	if (cmd == prog) strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1310	translate_char(cmd, '/', '\\');
1311	prog = cmd;
1312    }
1313    else if (strchr(prog, '/')) {
1314	len = strlen(prog);
1315	if (len < sizeof(fbuf))
1316	    strlcpy(cmd = fbuf, prog, sizeof(fbuf));
1317	else
1318	    STRNDUPV(cmd, v, prog, len);
1319	translate_char(cmd, '/', '\\');
1320	prog = cmd;
1321    }
1322    if (c_switch || is_batch(prog)) {
1323	char *progs[2];
1324	progs[0] = (char *)prog;
1325	progs[1] = NULL;
1326	len = join_argv(NULL, progs, ntcmd);
1327	if (c_switch) len += 3;
1328	else ++argv;
1329	if (argv[0]) len += join_argv(NULL, argv, ntcmd);
1330	cmd = ALLOCV(v, len);
1331	join_argv(cmd, progs, ntcmd);
1332	if (c_switch) strlcat(cmd, " /c", len);
1333	if (argv[0]) join_argv(cmd + strlcat(cmd, " ", len), argv, ntcmd);
1334	prog = c_switch ? shell : 0;
1335    }
1336    else {
1337	len = join_argv(NULL, argv, FALSE);
1338	cmd = ALLOCV(v, len);
1339	join_argv(cmd, argv, FALSE);
1340    }
1341
1342    /* assume ACP */
1343    if (!e && cmd && !(wcmd = acp_to_wstr(cmd, NULL))) e = E2BIG;
1344    if (v) ALLOCV_END(v);
1345    if (!e && prog && !(wprog = acp_to_wstr(prog, NULL))) e = E2BIG;
1346
1347    if (!e) {
1348	ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL, flags), mode);
1349    }
1350    free(wprog);
1351    free(wcmd);
1352    if (e) errno = e;
1353    return ret;
1354}
1355
1356rb_pid_t
1357rb_w32_aspawn(int mode, const char *prog, char *const *argv)
1358{
1359    return rb_w32_aspawn_flags(mode, prog, argv, 0);
1360}
1361
1362/* License: Artistic or GPL */
1363typedef struct _NtCmdLineElement {
1364    struct _NtCmdLineElement *next;
1365    char *str;
1366    int len;
1367    int flags;
1368} NtCmdLineElement;
1369
1370//
1371// Possible values for flags
1372//
1373
1374#define NTGLOB   0x1	// element contains a wildcard
1375#define NTMALLOC 0x2	// string in element was malloc'ed
1376#define NTSTRING 0x4	// element contains a quoted string
1377
1378/* License: Ruby's */
1379static int
1380insert(const char *path, VALUE vinfo, void *enc)
1381{
1382    NtCmdLineElement *tmpcurr;
1383    NtCmdLineElement ***tail = (NtCmdLineElement ***)vinfo;
1384
1385    tmpcurr = (NtCmdLineElement *)malloc(sizeof(NtCmdLineElement));
1386    if (!tmpcurr) return -1;
1387    MEMZERO(tmpcurr, NtCmdLineElement, 1);
1388    tmpcurr->len = strlen(path);
1389    tmpcurr->str = strdup(path);
1390    if (!tmpcurr->str) return -1;
1391    tmpcurr->flags |= NTMALLOC;
1392    **tail = tmpcurr;
1393    *tail = &tmpcurr->next;
1394
1395    return 0;
1396}
1397
1398/* License: Artistic or GPL */
1399static NtCmdLineElement **
1400cmdglob(NtCmdLineElement *patt, NtCmdLineElement **tail)
1401{
1402    char buffer[MAXPATHLEN], *buf = buffer;
1403    char *p;
1404    NtCmdLineElement **last = tail;
1405    int status;
1406
1407    if (patt->len >= MAXPATHLEN)
1408	if (!(buf = malloc(patt->len + 1))) return 0;
1409
1410    strlcpy(buf, patt->str, patt->len + 1);
1411    buf[patt->len] = '\0';
1412    for (p = buf; *p; p = CharNext(p))
1413	if (*p == '\\')
1414	    *p = '/';
1415    status = ruby_brace_glob(buf, 0, insert, (VALUE)&tail);
1416    if (buf != buffer)
1417	free(buf);
1418
1419    if (status || last == tail) return 0;
1420    if (patt->flags & NTMALLOC)
1421	free(patt->str);
1422    free(patt);
1423    return tail;
1424}
1425
1426//
1427// Check a command string to determine if it has I/O redirection
1428// characters that require it to be executed by a command interpreter
1429//
1430
1431/* License: Artistic or GPL */
1432static int
1433has_redirection(const char *cmd)
1434{
1435    char quote = '\0';
1436    const char *ptr;
1437
1438    //
1439    // Scan the string, looking for redirection characters (< or >), pipe
1440    // character (|) or newline (\n) that are not in a quoted string
1441    //
1442
1443    for (ptr = cmd; *ptr;) {
1444	switch (*ptr) {
1445	  case '\'':
1446	  case '\"':
1447	    if (!quote)
1448		quote = *ptr;
1449	    else if (quote == *ptr)
1450		quote = '\0';
1451	    ptr++;
1452	    break;
1453
1454	  case '>':
1455	  case '<':
1456	  case '|':
1457	  case '&':
1458	  case '\n':
1459	    if (!quote)
1460		return TRUE;
1461	    ptr++;
1462	    break;
1463
1464	  case '%':
1465	    if (*++ptr != '_' && !ISALPHA(*ptr)) break;
1466	    while (*++ptr == '_' || ISALNUM(*ptr));
1467	    if (*ptr++ == '%') return TRUE;
1468	    break;
1469
1470	  case '\\':
1471	    ptr++;
1472	  default:
1473	    ptr = CharNext(ptr);
1474	    break;
1475	}
1476    }
1477    return FALSE;
1478}
1479
1480/* License: Ruby's */
1481static inline char *
1482skipspace(char *ptr)
1483{
1484    while (ISSPACE(*ptr))
1485	ptr++;
1486    return ptr;
1487}
1488
1489/* License: Artistic or GPL */
1490int
1491rb_w32_cmdvector(const char *cmd, char ***vec)
1492{
1493    int globbing, len;
1494    int elements, strsz, done;
1495    int slashes, escape;
1496    char *ptr, *base, *buffer, *cmdline;
1497    char **vptr;
1498    char quote;
1499    NtCmdLineElement *curr, **tail;
1500    NtCmdLineElement *cmdhead = NULL, **cmdtail = &cmdhead;
1501
1502    //
1503    // just return if we don't have a command line
1504    //
1505
1506    while (ISSPACE(*cmd))
1507	cmd++;
1508    if (!*cmd) {
1509	*vec = NULL;
1510	return 0;
1511    }
1512
1513    ptr = cmdline = strdup(cmd);
1514
1515    //
1516    // Ok, parse the command line, building a list of CmdLineElements.
1517    // When we've finished, and it's an input command (meaning that it's
1518    // the processes argv), we'll do globing and then build the argument
1519    // vector.
1520    // The outer loop does one interation for each element seen.
1521    // The inner loop does one interation for each character in the element.
1522    //
1523
1524    while (*(ptr = skipspace(ptr))) {
1525	base = ptr;
1526	quote = slashes = globbing = escape = 0;
1527	for (done = 0; !done && *ptr; ) {
1528	    //
1529	    // Switch on the current character. We only care about the
1530	    // white-space characters, the  wild-card characters, and the
1531	    // quote characters.
1532	    //
1533
1534	    switch (*ptr) {
1535	      case '\\':
1536		if (quote != '\'') slashes++;
1537	        break;
1538
1539	      case ' ':
1540	      case '\t':
1541	      case '\n':
1542		//
1543		// if we're not in a string, then we're finished with this
1544		// element
1545		//
1546
1547		if (!quote) {
1548		    *ptr = 0;
1549		    done = 1;
1550		}
1551		break;
1552
1553	      case '*':
1554	      case '?':
1555	      case '[':
1556	      case '{':
1557		//
1558		// record the fact that this element has a wildcard character
1559		// N.B. Don't glob if inside a single quoted string
1560		//
1561
1562		if (quote != '\'')
1563		    globbing++;
1564		slashes = 0;
1565		break;
1566
1567	      case '\'':
1568	      case '\"':
1569		//
1570		// if we're already in a string, see if this is the
1571		// terminating close-quote. If it is, we're finished with
1572		// the string, but not neccessarily with the element.
1573		// If we're not already in a string, start one.
1574		//
1575
1576		if (!(slashes & 1)) {
1577		    if (!quote)
1578			quote = *ptr;
1579		    else if (quote == *ptr) {
1580			if (quote == '"' && quote == ptr[1])
1581			    ptr++;
1582			quote = '\0';
1583		    }
1584		}
1585		escape++;
1586		slashes = 0;
1587		break;
1588
1589	      default:
1590		ptr = CharNext(ptr);
1591		slashes = 0;
1592		continue;
1593	    }
1594	    ptr++;
1595	}
1596
1597	//
1598	// when we get here, we've got a pair of pointers to the element,
1599	// base and ptr. Base points to the start of the element while ptr
1600	// points to the character following the element.
1601	//
1602
1603	len = ptr - base;
1604	if (done) --len;
1605
1606	//
1607	// if it's an input vector element and it's enclosed by quotes,
1608	// we can remove them.
1609	//
1610
1611	if (escape) {
1612	    char *p = base, c;
1613	    slashes = quote = 0;
1614	    while (p < base + len) {
1615		switch (c = *p) {
1616		  case '\\':
1617		    p++;
1618		    if (quote != '\'') slashes++;
1619		    break;
1620
1621		  case '\'':
1622		  case '"':
1623		    if (!(slashes & 1) && quote && quote != c) {
1624			p++;
1625			slashes = 0;
1626			break;
1627		    }
1628		    memcpy(p - ((slashes + 1) >> 1), p + (~slashes & 1),
1629			   base + len - p);
1630		    len -= ((slashes + 1) >> 1) + (~slashes & 1);
1631		    p -= (slashes + 1) >> 1;
1632		    if (!(slashes & 1)) {
1633			if (quote) {
1634			    if (quote == '"' && quote == *p)
1635				p++;
1636			    quote = '\0';
1637			}
1638			else
1639			    quote = c;
1640		    }
1641		    else
1642			p++;
1643		    slashes = 0;
1644		    break;
1645
1646		  default:
1647		    p = CharNext(p);
1648		    slashes = 0;
1649		    break;
1650		}
1651	    }
1652	}
1653
1654	curr = (NtCmdLineElement *)calloc(sizeof(NtCmdLineElement), 1);
1655	if (!curr) goto do_nothing;
1656	curr->str = base;
1657	curr->len = len;
1658
1659	if (globbing && (tail = cmdglob(curr, cmdtail))) {
1660	    cmdtail = tail;
1661	}
1662	else {
1663	    *cmdtail = curr;
1664	    cmdtail = &curr->next;
1665	}
1666    }
1667
1668    //
1669    // Almost done!
1670    // Count up the elements, then allocate space for a vector of pointers
1671    // (argv) and a string table for the elements.
1672    //
1673
1674    for (elements = 0, strsz = 0, curr = cmdhead; curr; curr = curr->next) {
1675	elements++;
1676	strsz += (curr->len + 1);
1677    }
1678
1679    len = (elements+1)*sizeof(char *) + strsz;
1680    buffer = (char *)malloc(len);
1681    if (!buffer) {
1682      do_nothing:
1683	while (curr = cmdhead) {
1684	    cmdhead = curr->next;
1685	    if (curr->flags & NTMALLOC) free(curr->str);
1686	    free(curr);
1687	}
1688	free(cmdline);
1689	for (vptr = *vec; *vptr; ++vptr);
1690	return vptr - *vec;
1691    }
1692
1693    //
1694    // make vptr point to the start of the buffer
1695    // and ptr point to the area we'll consider the string table.
1696    //
1697    //   buffer (*vec)
1698    //   |
1699    //   V       ^---------------------V
1700    //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1701    //   |   |       | ....  | NULL  |   | ..... |\0 |   | ..... |\0 |...
1702    //   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1703    //   |-  elements+1             -| ^ 1st element   ^ 2nd element
1704
1705    vptr = (char **) buffer;
1706
1707    ptr = buffer + (elements+1) * sizeof(char *);
1708
1709    while (curr = cmdhead) {
1710	strlcpy(ptr, curr->str, curr->len + 1);
1711	*vptr++ = ptr;
1712	ptr += curr->len + 1;
1713	cmdhead = curr->next;
1714	if (curr->flags & NTMALLOC) free(curr->str);
1715	free(curr);
1716    }
1717    *vptr = 0;
1718
1719    *vec = (char **) buffer;
1720    free(cmdline);
1721    return elements;
1722}
1723
1724//
1725// UNIX compatible directory access functions for NT
1726//
1727
1728#define PATHLEN 1024
1729
1730//
1731// The idea here is to read all the directory names into a string table
1732// (separated by nulls) and when one of the other dir functions is called
1733// return the pointer to the current file name.
1734//
1735
1736/* License: Ruby's */
1737#define GetBit(bits, i) ((bits)[(i) / CHAR_BIT] &  (1 << (i) % CHAR_BIT))
1738#define SetBit(bits, i) ((bits)[(i) / CHAR_BIT] |= (1 << (i) % CHAR_BIT))
1739
1740#define BitOfIsDir(n) ((n) * 2)
1741#define BitOfIsRep(n) ((n) * 2 + 1)
1742#define DIRENT_PER_CHAR (CHAR_BIT / 2)
1743
1744/* License: Artistic or GPL */
1745static HANDLE
1746open_dir_handle(const WCHAR *filename, WIN32_FIND_DATAW *fd)
1747{
1748    HANDLE fh;
1749    static const WCHAR wildcard[] = L"\\*";
1750    WCHAR *scanname;
1751    WCHAR *p;
1752    int len;
1753    VALUE v;
1754
1755    //
1756    // Create the search pattern
1757    //
1758    len = lstrlenW(filename);
1759    scanname = ALLOCV_N(WCHAR, v, len + sizeof(wildcard) / sizeof(WCHAR));
1760    lstrcpyW(scanname, filename);
1761    p = CharPrevW(scanname, scanname + len);
1762    if (*p == L'/' || *p == L'\\' || *p == L':')
1763	lstrcatW(scanname, wildcard + 1);
1764    else
1765	lstrcatW(scanname, wildcard);
1766
1767    //
1768    // do the FindFirstFile call
1769    //
1770    fh = FindFirstFileW(scanname, fd);
1771    ALLOCV_END(v);
1772    if (fh == INVALID_HANDLE_VALUE) {
1773	errno = map_errno(GetLastError());
1774    }
1775    return fh;
1776}
1777
1778/* License: Artistic or GPL */
1779static DIR *
1780opendir_internal(WCHAR *wpath, const char *filename)
1781{
1782    struct stati64 sbuf;
1783    WIN32_FIND_DATAW fd;
1784    HANDLE fh;
1785    DIR *p;
1786    long len;
1787    long idx;
1788    WCHAR *tmpW;
1789    char *tmp;
1790
1791    //
1792    // check to see if we've got a directory
1793    //
1794    if (wstati64(wpath, &sbuf) < 0) {
1795	return NULL;
1796    }
1797    if (!(sbuf.st_mode & S_IFDIR) &&
1798	(!ISALPHA(filename[0]) || filename[1] != ':' || filename[2] != '\0' ||
1799	 ((1 << ((filename[0] & 0x5f) - 'A')) & GetLogicalDrives()) == 0)) {
1800	errno = ENOTDIR;
1801	return NULL;
1802    }
1803    fh = open_dir_handle(wpath, &fd);
1804    if (fh == INVALID_HANDLE_VALUE) {
1805	return NULL;
1806    }
1807
1808    //
1809    // Get us a DIR structure
1810    //
1811    p = calloc(sizeof(DIR), 1);
1812    if (p == NULL)
1813	return NULL;
1814
1815    idx = 0;
1816
1817    //
1818    // loop finding all the files that match the wildcard
1819    // (which should be all of them in this directory!).
1820    // the variable idx should point one past the null terminator
1821    // of the previous string found.
1822    //
1823    do {
1824	len = lstrlenW(fd.cFileName) + 1;
1825
1826	//
1827	// bump the string table size by enough for the
1828	// new name and it's null terminator
1829	//
1830	tmpW = realloc(p->start, (idx + len) * sizeof(WCHAR));
1831	if (!tmpW) {
1832	  error:
1833	    rb_w32_closedir(p);
1834	    FindClose(fh);
1835	    errno = ENOMEM;
1836	    return NULL;
1837	}
1838
1839	p->start = tmpW;
1840	memcpy(&p->start[idx], fd.cFileName, len * sizeof(WCHAR));
1841
1842	if (p->nfiles % DIRENT_PER_CHAR == 0) {
1843	    tmp = realloc(p->bits, p->nfiles / DIRENT_PER_CHAR + 1);
1844	    if (!tmp)
1845		goto error;
1846	    p->bits = tmp;
1847	    p->bits[p->nfiles / DIRENT_PER_CHAR] = 0;
1848	}
1849	if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1850	    SetBit(p->bits, BitOfIsDir(p->nfiles));
1851	if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1852	    SetBit(p->bits, BitOfIsRep(p->nfiles));
1853
1854	p->nfiles++;
1855	idx += len;
1856    } while (FindNextFileW(fh, &fd));
1857    FindClose(fh);
1858    p->size = idx;
1859    p->curr = p->start;
1860    return p;
1861}
1862
1863/* License: Ruby's */
1864static inline UINT
1865filecp(void)
1866{
1867    UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
1868    return cp;
1869}
1870
1871/* License: Ruby's */
1872static char *
1873wstr_to_mbstr(UINT cp, const WCHAR *wstr, int clen, long *plen)
1874{
1875    char *ptr;
1876    int len = WideCharToMultiByte(cp, 0, wstr, clen, NULL, 0, NULL, NULL) - 1;
1877    if (!(ptr = malloc(len + 1))) return 0;
1878    WideCharToMultiByte(cp, 0, wstr, clen, ptr, len + 1, NULL, NULL);
1879    if (plen) *plen = len;
1880    return ptr;
1881}
1882
1883/* License: Ruby's */
1884static WCHAR *
1885mbstr_to_wstr(UINT cp, const char *str, int clen, long *plen)
1886{
1887    WCHAR *ptr;
1888    int len = MultiByteToWideChar(cp, 0, str, clen, NULL, 0) - 1;
1889    if (!(ptr = malloc(sizeof(WCHAR) * (len + 1)))) return 0;
1890    MultiByteToWideChar(cp, 0, str, clen, ptr, len + 1);
1891    if (plen) *plen = len;
1892    return ptr;
1893}
1894
1895/* License: Ruby's */
1896DIR *
1897rb_w32_opendir(const char *filename)
1898{
1899    DIR *ret;
1900    WCHAR *wpath = filecp_to_wstr(filename, NULL);
1901    if (!wpath)
1902	return NULL;
1903    ret = opendir_internal(wpath, filename);
1904    free(wpath);
1905    return ret;
1906}
1907
1908/* License: Ruby's */
1909DIR *
1910rb_w32_uopendir(const char *filename)
1911{
1912    DIR *ret;
1913    WCHAR *wpath = utf8_to_wstr(filename, NULL);
1914    if (!wpath)
1915	return NULL;
1916    ret = opendir_internal(wpath, filename);
1917    free(wpath);
1918    return ret;
1919}
1920
1921//
1922// Move to next entry
1923//
1924
1925/* License: Artistic or GPL */
1926static void
1927move_to_next_entry(DIR *dirp)
1928{
1929    if (dirp->curr) {
1930	dirp->loc++;
1931	dirp->curr += lstrlenW(dirp->curr) + 1;
1932	if (dirp->curr >= (dirp->start + dirp->size)) {
1933	    dirp->curr = NULL;
1934	}
1935    }
1936}
1937
1938//
1939// Readdir just returns the current string pointer and bumps the
1940// string pointer to the next entry.
1941//
1942/* License: Ruby's */
1943static BOOL
1944win32_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *dummy)
1945{
1946    if (!(entry->d_name = wstr_to_filecp(file, &entry->d_namlen)))
1947	return FALSE;
1948    return TRUE;
1949}
1950
1951/* License: Ruby's */
1952VALUE
1953rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
1954{
1955    static rb_encoding *utf16 = (rb_encoding *)-1;
1956    VALUE src;
1957
1958    if (utf16 == (rb_encoding *)-1) {
1959	utf16 = rb_enc_find("UTF-16LE");
1960	if (utf16 == rb_ascii8bit_encoding())
1961	    utf16 = NULL;
1962    }
1963    if (!utf16)
1964	/* maybe miniruby */
1965	return Qnil;
1966
1967    src = rb_enc_str_new((char *)wstr, lstrlenW(wstr) * sizeof(WCHAR), utf16);
1968    return rb_str_encode(src, rb_enc_from_encoding(enc), ECONV_UNDEF_REPLACE, Qnil);
1969}
1970
1971/* License: Ruby's */
1972char *
1973rb_w32_conv_from_wstr(const WCHAR *wstr, long *lenp, rb_encoding *enc)
1974{
1975    VALUE str = rb_w32_conv_from_wchar(wstr, enc);
1976    long len;
1977    char *ptr;
1978
1979    if (NIL_P(str)) return wstr_to_filecp(wstr, lenp);
1980    *lenp = len = RSTRING_LEN(str);
1981    memcpy(ptr = malloc(len + 1), RSTRING_PTR(str), len);
1982    ptr[len] = '\0';
1983    return ptr;
1984}
1985
1986/* License: Ruby's */
1987static BOOL
1988ruby_direct_conv(const WCHAR *file, struct direct *entry, rb_encoding *enc)
1989{
1990    if (!(entry->d_name = rb_w32_conv_from_wstr(file, &entry->d_namlen, enc)))
1991	return FALSE;
1992    return TRUE;
1993}
1994
1995/* License: Artistic or GPL */
1996static struct direct *
1997readdir_internal(DIR *dirp, BOOL (*conv)(const WCHAR *, struct direct *, rb_encoding *), rb_encoding *enc)
1998{
1999    static int dummy = 0;
2000
2001    if (dirp->curr) {
2002
2003	//
2004	// first set up the structure to return
2005	//
2006	if (dirp->dirstr.d_name)
2007	    free(dirp->dirstr.d_name);
2008	conv(dirp->curr, &dirp->dirstr, enc);
2009
2010	//
2011	// Fake inode
2012	//
2013	dirp->dirstr.d_ino = dummy++;
2014
2015	//
2016	// Attributes
2017	//
2018	dirp->dirstr.d_isdir = GetBit(dirp->bits, BitOfIsDir(dirp->loc));
2019	dirp->dirstr.d_isrep = GetBit(dirp->bits, BitOfIsRep(dirp->loc));
2020
2021	//
2022	// Now set up for the next call to readdir
2023	//
2024
2025	move_to_next_entry(dirp);
2026
2027	return &(dirp->dirstr);
2028
2029    }
2030    else
2031	return NULL;
2032}
2033
2034/* License: Ruby's */
2035struct direct  *
2036rb_w32_readdir(DIR *dirp, rb_encoding *enc)
2037{
2038    if (!enc || enc == rb_ascii8bit_encoding())
2039	return readdir_internal(dirp, win32_direct_conv, NULL);
2040    else
2041	return readdir_internal(dirp, ruby_direct_conv, enc);
2042}
2043
2044//
2045// Telldir returns the current string pointer position
2046//
2047
2048/* License: Artistic or GPL */
2049long
2050rb_w32_telldir(DIR *dirp)
2051{
2052    return dirp->loc;
2053}
2054
2055//
2056// Seekdir moves the string pointer to a previously saved position
2057// (Saved by telldir).
2058
2059/* License: Ruby's */
2060void
2061rb_w32_seekdir(DIR *dirp, long loc)
2062{
2063    if (dirp->loc > loc) rb_w32_rewinddir(dirp);
2064
2065    while (dirp->curr && dirp->loc < loc) {
2066	move_to_next_entry(dirp);
2067    }
2068}
2069
2070//
2071// Rewinddir resets the string pointer to the start
2072//
2073
2074/* License: Artistic or GPL */
2075void
2076rb_w32_rewinddir(DIR *dirp)
2077{
2078    dirp->curr = dirp->start;
2079    dirp->loc = 0;
2080}
2081
2082//
2083// This just free's the memory allocated by opendir
2084//
2085
2086/* License: Artistic or GPL */
2087void
2088rb_w32_closedir(DIR *dirp)
2089{
2090    if (dirp) {
2091	if (dirp->dirstr.d_name)
2092	    free(dirp->dirstr.d_name);
2093	if (dirp->start)
2094	    free(dirp->start);
2095	if (dirp->bits)
2096	    free(dirp->bits);
2097	free(dirp);
2098    }
2099}
2100
2101#if (defined _MT || defined __MSVCRT__) && !defined __BORLANDC__
2102#define MSVCRT_THREADS
2103#endif
2104#ifdef MSVCRT_THREADS
2105# define MTHREAD_ONLY(x) x
2106# define STHREAD_ONLY(x)
2107#elif defined(__BORLANDC__)
2108# define MTHREAD_ONLY(x)
2109# define STHREAD_ONLY(x)
2110#else
2111# define MTHREAD_ONLY(x)
2112# define STHREAD_ONLY(x) x
2113#endif
2114
2115/* License: Ruby's */
2116typedef struct	{
2117    intptr_t osfhnd;	/* underlying OS file HANDLE */
2118    char osfile;	/* attributes of file (e.g., open in text mode?) */
2119    char pipech;	/* one char buffer for handles opened on pipes */
2120#ifdef MSVCRT_THREADS
2121    int lockinitflag;
2122    CRITICAL_SECTION lock;
2123#endif
2124#if RUBY_MSVCRT_VERSION >= 80
2125    char textmode;
2126    char pipech2[2];
2127#endif
2128}	ioinfo;
2129
2130#if !defined _CRTIMP || defined __MINGW32__
2131#undef _CRTIMP
2132#define _CRTIMP __declspec(dllimport)
2133#endif
2134
2135#if !defined(__BORLANDC__)
2136EXTERN_C _CRTIMP ioinfo * __pioinfo[];
2137static inline ioinfo* _pioinfo(int);
2138
2139#define IOINFO_L2E			5
2140#define IOINFO_ARRAY_ELTS	(1 << IOINFO_L2E)
2141#define _osfhnd(i)  (_pioinfo(i)->osfhnd)
2142#define _osfile(i)  (_pioinfo(i)->osfile)
2143#define _pipech(i)  (_pioinfo(i)->pipech)
2144
2145#if RUBY_MSVCRT_VERSION >= 80
2146static size_t pioinfo_extra = 0;	/* workaround for VC++8 SP1 */
2147
2148/* License: Ruby's */
2149static void
2150set_pioinfo_extra(void)
2151{
2152    int fd;
2153
2154    fd = _open("NUL", O_RDONLY);
2155    for (pioinfo_extra = 0; pioinfo_extra <= 64; pioinfo_extra += sizeof(void *)) {
2156	if (_osfhnd(fd) == _get_osfhandle(fd)) {
2157	    break;
2158	}
2159    }
2160    _close(fd);
2161
2162    if (pioinfo_extra > 64) {
2163	/* not found, maybe something wrong... */
2164	pioinfo_extra = 0;
2165    }
2166}
2167#else
2168#define pioinfo_extra 0
2169#endif
2170
2171static inline ioinfo*
2172_pioinfo(int fd)
2173{
2174    const size_t sizeof_ioinfo = sizeof(ioinfo) + pioinfo_extra;
2175    return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
2176		     (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
2177}
2178
2179#define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
2180#define _set_osflags(fh, flags) (_osfile(fh) = (flags))
2181
2182#define FOPEN			0x01	/* file handle open */
2183#define FEOFLAG			0x02	/* end of file has been encountered */
2184#define FPIPE			0x08	/* file handle refers to a pipe */
2185#define FNOINHERIT		0x10	/* file handle opened O_NOINHERIT */
2186#define FAPPEND			0x20	/* file handle opened O_APPEND */
2187#define FDEV			0x40	/* file handle refers to device */
2188#define FTEXT			0x80	/* file handle is in text mode */
2189
2190static int is_socket(SOCKET);
2191static int is_console(SOCKET);
2192
2193/* License: Ruby's */
2194int
2195rb_w32_io_cancelable_p(int fd)
2196{
2197    return cancel_io != NULL && (is_socket(TO_SOCKET(fd)) || !is_console(TO_SOCKET(fd)));
2198}
2199
2200/* License: Ruby's */
2201static int
2202rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2203{
2204    int fh;
2205    char fileflags;		/* _osfile flags */
2206    HANDLE hF;
2207
2208    /* copy relevant flags from second parameter */
2209    fileflags = FDEV;
2210
2211    if (flags & O_APPEND)
2212	fileflags |= FAPPEND;
2213
2214    if (flags & O_TEXT)
2215	fileflags |= FTEXT;
2216
2217    if (flags & O_NOINHERIT)
2218	fileflags |= FNOINHERIT;
2219
2220    /* attempt to allocate a C Runtime file handle */
2221    hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2222    fh = _open_osfhandle((intptr_t)hF, 0);
2223    CloseHandle(hF);
2224    if (fh == -1) {
2225	errno = EMFILE;		/* too many open files */
2226	_doserrno = 0L;		/* not an OS error */
2227    }
2228    else {
2229
2230	MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fh)->lock)));
2231	/* the file is open. now, set the info in _osfhnd array */
2232	_set_osfhnd(fh, osfhandle);
2233
2234	fileflags |= FOPEN;		/* mark as open */
2235
2236	_set_osflags(fh, fileflags); /* set osfile entry */
2237	MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fh)->lock));
2238    }
2239    return fh;			/* return handle */
2240}
2241
2242/* License: Ruby's */
2243static void
2244init_stdhandle(void)
2245{
2246    int nullfd = -1;
2247    int keep = 0;
2248#define open_null(fd)						\
2249    (((nullfd < 0) ?						\
2250      (nullfd = open("NUL", O_RDWR)) : 0),		\
2251     ((nullfd == (fd)) ? (keep = 1) : dup2(nullfd, fd)),	\
2252     (fd))
2253
2254    if (fileno(stdin) < 0) {
2255	stdin->_file = open_null(0);
2256    }
2257    else {
2258	setmode(fileno(stdin), O_BINARY);
2259    }
2260    if (fileno(stdout) < 0) {
2261	stdout->_file = open_null(1);
2262    }
2263    if (fileno(stderr) < 0) {
2264	stderr->_file = open_null(2);
2265    }
2266    if (nullfd >= 0 && !keep) close(nullfd);
2267    setvbuf(stderr, NULL, _IONBF, 0);
2268}
2269#else
2270
2271#define _set_osfhnd(fh, osfh) (void)((fh), (osfh))
2272#define _set_osflags(fh, flags) (void)((fh), (flags))
2273
2274/* License: Ruby's */
2275static void
2276init_stdhandle(void)
2277{
2278}
2279#endif
2280
2281/* License: Ruby's */
2282#ifdef __BORLANDC__
2283static int
2284rb_w32_open_osfhandle(intptr_t osfhandle, int flags)
2285{
2286    int fd = _open_osfhandle(osfhandle, flags);
2287    if (fd == -1) {
2288	errno = EMFILE;		/* too many open files */
2289	_doserrno = 0L;		/* not an OS error */
2290    }
2291    return fd;
2292}
2293#endif
2294
2295#undef getsockopt
2296
2297/* License: Ruby's */
2298static int
2299is_socket(SOCKET sock)
2300{
2301    if (socklist_lookup(sock, NULL))
2302	return TRUE;
2303    else
2304	return FALSE;
2305}
2306
2307/* License: Ruby's */
2308int
2309rb_w32_is_socket(int fd)
2310{
2311    return is_socket(TO_SOCKET(fd));
2312}
2313
2314//
2315// Since the errors returned by the socket error function
2316// WSAGetLastError() are not known by the library routine strerror
2317// we have to roll our own.
2318//
2319
2320#undef strerror
2321
2322/* License: Artistic or GPL */
2323char *
2324rb_w32_strerror(int e)
2325{
2326    static char buffer[512];
2327    DWORD source = 0;
2328    char *p;
2329
2330#if defined __BORLANDC__ && defined ENOTEMPTY // _sys_errlist is broken
2331    switch (e) {
2332      case ENAMETOOLONG:
2333	return "Filename too long";
2334      case ENOTEMPTY:
2335	return "Directory not empty";
2336    }
2337#endif
2338
2339    if (e < 0 || e > sys_nerr) {
2340	if (e < 0)
2341	    e = GetLastError();
2342#if WSAEWOULDBLOCK != EWOULDBLOCK
2343	else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
2344	    static int s = -1;
2345	    int i;
2346	    if (s < 0)
2347		for (s = 0; s < (int)(sizeof(errmap)/sizeof(*errmap)); s++)
2348		    if (errmap[s].winerr == WSAEWOULDBLOCK)
2349			break;
2350	    for (i = s; i < (int)(sizeof(errmap)/sizeof(*errmap)); i++)
2351		if (errmap[i].err == e) {
2352		    e = errmap[i].winerr;
2353		    break;
2354		}
2355	}
2356#endif
2357	if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2358			  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e,
2359			  MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
2360			  buffer, sizeof(buffer), NULL) == 0 &&
2361	    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2362			  FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, 0,
2363			  buffer, sizeof(buffer), NULL) == 0)
2364	    strlcpy(buffer, "Unknown Error", sizeof(buffer));
2365    }
2366    else
2367	strlcpy(buffer, strerror(e), sizeof(buffer));
2368
2369    p = buffer;
2370    while ((p = strpbrk(p, "\r\n")) != NULL) {
2371	memmove(p, p + 1, strlen(p));
2372    }
2373    return buffer;
2374}
2375
2376//
2377// various stubs
2378//
2379
2380
2381// Ownership
2382//
2383// Just pretend that everyone is a superuser. NT will let us know if
2384// we don't really have permission to do something.
2385//
2386
2387#define ROOT_UID	0
2388#define ROOT_GID	0
2389
2390/* License: Artistic or GPL */
2391rb_uid_t
2392getuid(void)
2393{
2394	return ROOT_UID;
2395}
2396
2397/* License: Artistic or GPL */
2398rb_uid_t
2399geteuid(void)
2400{
2401	return ROOT_UID;
2402}
2403
2404/* License: Artistic or GPL */
2405rb_gid_t
2406getgid(void)
2407{
2408	return ROOT_GID;
2409}
2410
2411/* License: Artistic or GPL */
2412rb_gid_t
2413getegid(void)
2414{
2415    return ROOT_GID;
2416}
2417
2418/* License: Artistic or GPL */
2419int
2420setuid(rb_uid_t uid)
2421{
2422    return (uid == ROOT_UID ? 0 : -1);
2423}
2424
2425/* License: Artistic or GPL */
2426int
2427setgid(rb_gid_t gid)
2428{
2429    return (gid == ROOT_GID ? 0 : -1);
2430}
2431
2432//
2433// File system stuff
2434//
2435
2436/* License: Artistic or GPL */
2437int
2438ioctl(int i, int u, ...)
2439{
2440    errno = EINVAL;
2441    return -1;
2442}
2443
2444void
2445rb_w32_fdset(int fd, fd_set *set)
2446{
2447    FD_SET(fd, set);
2448}
2449
2450#undef FD_CLR
2451
2452/* License: Ruby's */
2453void
2454rb_w32_fdclr(int fd, fd_set *set)
2455{
2456    unsigned int i;
2457    SOCKET s = TO_SOCKET(fd);
2458
2459    for (i = 0; i < set->fd_count; i++) {
2460        if (set->fd_array[i] == s) {
2461	    memmove(&set->fd_array[i], &set->fd_array[i+1],
2462		    sizeof(set->fd_array[0]) * (--set->fd_count - i));
2463            break;
2464        }
2465    }
2466}
2467
2468#undef FD_ISSET
2469
2470/* License: Ruby's */
2471int
2472rb_w32_fdisset(int fd, fd_set *set)
2473{
2474    int ret;
2475    SOCKET s = TO_SOCKET(fd);
2476    if (s == (SOCKET)INVALID_HANDLE_VALUE)
2477        return 0;
2478    RUBY_CRITICAL(ret = __WSAFDIsSet(s, set));
2479    return ret;
2480}
2481
2482/* License: Ruby's */
2483void
2484rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
2485{
2486    max = min(src->fd_count, (UINT)max);
2487    if ((UINT)dst->capa < (UINT)max) {
2488	dst->capa = (src->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2489	dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2490    }
2491
2492    memcpy(dst->fdset->fd_array, src->fd_array,
2493	   max * sizeof(src->fd_array[0]));
2494    dst->fdset->fd_count = src->fd_count;
2495}
2496
2497/* License: Ruby's */
2498void
2499rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
2500{
2501    if ((UINT)dst->capa < src->fdset->fd_count) {
2502	dst->capa = (src->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2503	dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2504    }
2505
2506    memcpy(dst->fdset->fd_array, src->fdset->fd_array,
2507	   src->fdset->fd_count * sizeof(src->fdset->fd_array[0]));
2508    dst->fdset->fd_count = src->fdset->fd_count;
2509}
2510
2511//
2512// Networking trampolines
2513// These are used to avoid socket startup/shutdown overhead in case
2514// the socket routines aren't used.
2515//
2516
2517#undef select
2518
2519/* License: Ruby's */
2520static int
2521extract_fd(rb_fdset_t *dst, fd_set *src, int (*func)(SOCKET))
2522{
2523    unsigned int s = 0;
2524    unsigned int m = 0;
2525    if (!src) return 0;
2526
2527    while (s < src->fd_count) {
2528        SOCKET fd = src->fd_array[s];
2529
2530	if (!func || (*func)(fd)) {
2531	    if (dst) { /* move it to dst */
2532		unsigned int d;
2533
2534		for (d = 0; d < dst->fdset->fd_count; d++) {
2535		    if (dst->fdset->fd_array[d] == fd)
2536			break;
2537		}
2538		if (d == dst->fdset->fd_count) {
2539		    if ((int)dst->fdset->fd_count >= dst->capa) {
2540			dst->capa = (dst->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
2541			dst->fdset = xrealloc(dst->fdset, sizeof(unsigned int) + sizeof(SOCKET) * dst->capa);
2542		    }
2543		    dst->fdset->fd_array[dst->fdset->fd_count++] = fd;
2544		}
2545		memmove(
2546		    &src->fd_array[s],
2547		    &src->fd_array[s+1],
2548		    sizeof(src->fd_array[0]) * (--src->fd_count - s));
2549	    }
2550	    else {
2551		m++;
2552		s++;
2553	    }
2554	}
2555	else s++;
2556    }
2557
2558    return dst ? dst->fdset->fd_count : m;
2559}
2560
2561/* License: Ruby's */
2562static int
2563copy_fd(fd_set *dst, fd_set *src)
2564{
2565    unsigned int s;
2566    if (!src || !dst) return 0;
2567
2568    for (s = 0; s < src->fd_count; ++s) {
2569	SOCKET fd = src->fd_array[s];
2570	unsigned int d;
2571	for (d = 0; d < dst->fd_count; ++d) {
2572	    if (dst->fd_array[d] == fd)
2573		break;
2574	}
2575	if (d == dst->fd_count && d < FD_SETSIZE) {
2576	    dst->fd_array[dst->fd_count++] = fd;
2577	}
2578    }
2579
2580    return dst->fd_count;
2581}
2582
2583/* License: Ruby's */
2584static int
2585is_not_socket(SOCKET sock)
2586{
2587    return !is_socket(sock);
2588}
2589
2590/* License: Ruby's */
2591static int
2592is_pipe(SOCKET sock) /* DONT call this for SOCKET! it clains it is PIPE. */
2593{
2594    int ret;
2595
2596    RUBY_CRITICAL({
2597	ret = (GetFileType((HANDLE)sock) == FILE_TYPE_PIPE);
2598    });
2599
2600    return ret;
2601}
2602
2603/* License: Ruby's */
2604static int
2605is_readable_pipe(SOCKET sock) /* call this for pipe only */
2606{
2607    int ret;
2608    DWORD n = 0;
2609
2610    RUBY_CRITICAL(
2611	if (PeekNamedPipe((HANDLE)sock, NULL, 0, NULL, &n, NULL)) {
2612	    ret = (n > 0);
2613	}
2614	else {
2615	    ret = (GetLastError() == ERROR_BROKEN_PIPE); /* pipe was closed */
2616	}
2617    );
2618
2619    return ret;
2620}
2621
2622/* License: Ruby's */
2623static int
2624is_console(SOCKET sock) /* DONT call this for SOCKET! */
2625{
2626    int ret;
2627    DWORD n = 0;
2628    INPUT_RECORD ir;
2629
2630    RUBY_CRITICAL(
2631	ret = (PeekConsoleInput((HANDLE)sock, &ir, 1, &n))
2632    );
2633
2634    return ret;
2635}
2636
2637/* License: Ruby's */
2638static int
2639is_readable_console(SOCKET sock) /* call this for console only */
2640{
2641    int ret = 0;
2642    DWORD n = 0;
2643    INPUT_RECORD ir;
2644
2645    RUBY_CRITICAL(
2646	if (PeekConsoleInput((HANDLE)sock, &ir, 1, &n) && n > 0) {
2647	    if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown &&
2648		ir.Event.KeyEvent.uChar.AsciiChar) {
2649		ret = 1;
2650	    }
2651	    else {
2652		ReadConsoleInput((HANDLE)sock, &ir, 1, &n);
2653	    }
2654	}
2655    );
2656
2657    return ret;
2658}
2659
2660/* License: Ruby's */
2661static int
2662is_invalid_handle(SOCKET sock)
2663{
2664    return (HANDLE)sock == INVALID_HANDLE_VALUE;
2665}
2666
2667/* License: Artistic or GPL */
2668static int
2669do_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
2670            struct timeval *timeout)
2671{
2672    int r = 0;
2673
2674    if (nfds == 0) {
2675	if (timeout)
2676	    rb_w32_sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
2677	else
2678	    rb_w32_sleep(INFINITE);
2679    }
2680    else {
2681	if (!NtSocketsInitialized)
2682	    StartSockets();
2683
2684	RUBY_CRITICAL(
2685	    EnterCriticalSection(&select_mutex);
2686	    r = select(nfds, rd, wr, ex, timeout);
2687	    LeaveCriticalSection(&select_mutex);
2688	    if (r == SOCKET_ERROR) {
2689		errno = map_errno(WSAGetLastError());
2690		r = -1;
2691	    }
2692	);
2693    }
2694
2695    return r;
2696}
2697
2698/*
2699 * rest -= wait
2700 * return 0 if rest is smaller than wait.
2701 */
2702/* License: Ruby's */
2703int
2704rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait)
2705{
2706    if (rest->tv_sec < wait->tv_sec) {
2707	return 0;
2708    }
2709    while (rest->tv_usec < wait->tv_usec) {
2710	if (rest->tv_sec <= wait->tv_sec) {
2711	    return 0;
2712	}
2713	rest->tv_sec -= 1;
2714	rest->tv_usec += 1000 * 1000;
2715    }
2716    rest->tv_sec -= wait->tv_sec;
2717    rest->tv_usec -= wait->tv_usec;
2718    return rest->tv_sec != 0 || rest->tv_usec != 0;
2719}
2720
2721/* License: Ruby's */
2722static inline int
2723compare(const struct timeval *t1, const struct timeval *t2)
2724{
2725    if (t1->tv_sec < t2->tv_sec)
2726	return -1;
2727    if (t1->tv_sec > t2->tv_sec)
2728	return 1;
2729    if (t1->tv_usec < t2->tv_usec)
2730	return -1;
2731    if (t1->tv_usec > t2->tv_usec)
2732	return 1;
2733    return 0;
2734}
2735
2736#undef Sleep
2737
2738int rb_w32_check_interrupt(void *);	/* @internal */
2739
2740/* @internal */
2741/* License: Ruby's */
2742int
2743rb_w32_select_with_thread(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
2744			  struct timeval *timeout, void *th)
2745{
2746    int r;
2747    rb_fdset_t pipe_rd;
2748    rb_fdset_t cons_rd;
2749    rb_fdset_t else_rd;
2750    rb_fdset_t else_wr;
2751    rb_fdset_t except;
2752    int nonsock = 0;
2753    struct timeval limit = {0, 0};
2754
2755    if (nfds < 0 || (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0))) {
2756	errno = EINVAL;
2757	return -1;
2758    }
2759
2760    if (timeout) {
2761	if (timeout->tv_sec < 0 ||
2762	    timeout->tv_usec < 0 ||
2763	    timeout->tv_usec >= 1000000) {
2764	    errno = EINVAL;
2765	    return -1;
2766	}
2767	gettimeofday(&limit, NULL);
2768	limit.tv_sec += timeout->tv_sec;
2769	limit.tv_usec += timeout->tv_usec;
2770	if (limit.tv_usec >= 1000000) {
2771	    limit.tv_usec -= 1000000;
2772	    limit.tv_sec++;
2773	}
2774    }
2775
2776    // assume else_{rd,wr} (other than socket, pipe reader, console reader)
2777    // are always readable/writable. but this implementation still has
2778    // problem. if pipe's buffer is full, writing to pipe will block
2779    // until some data is read from pipe. but ruby is single threaded system,
2780    // so whole system will be blocked forever.
2781
2782    rb_fd_init(&else_rd);
2783    nonsock += extract_fd(&else_rd, rd, is_not_socket);
2784
2785    rb_fd_init(&else_wr);
2786    nonsock += extract_fd(&else_wr, wr, is_not_socket);
2787
2788    // check invalid handles
2789    if (extract_fd(NULL, else_rd.fdset, is_invalid_handle) > 0 ||
2790	extract_fd(NULL, else_wr.fdset, is_invalid_handle) > 0) {
2791	rb_fd_term(&else_wr);
2792	rb_fd_term(&else_rd);
2793	errno = EBADF;
2794	return -1;
2795    }
2796
2797    rb_fd_init(&pipe_rd);
2798    extract_fd(&pipe_rd, else_rd.fdset, is_pipe); // should not call is_pipe for socket
2799
2800    rb_fd_init(&cons_rd);
2801    extract_fd(&cons_rd, else_rd.fdset, is_console); // ditto
2802
2803    rb_fd_init(&except);
2804    extract_fd(&except, ex, is_not_socket); // drop only
2805
2806    r = 0;
2807    if (rd && (int)rd->fd_count > r) r = (int)rd->fd_count;
2808    if (wr && (int)wr->fd_count > r) r = (int)wr->fd_count;
2809    if (ex && (int)ex->fd_count > r) r = (int)ex->fd_count;
2810    if (nfds > r) nfds = r;
2811
2812    {
2813	struct timeval rest;
2814	struct timeval wait;
2815	struct timeval zero;
2816	wait.tv_sec = 0; wait.tv_usec = 10 * 1000; // 10ms
2817	zero.tv_sec = 0; zero.tv_usec = 0;         //  0ms
2818	for (;;) {
2819	    if (th && rb_w32_check_interrupt(th) != WAIT_TIMEOUT) {
2820		r = -1;
2821		break;
2822	    }
2823	    if (nonsock) {
2824		// modifying {else,pipe,cons}_rd is safe because
2825		// if they are modified, function returns immediately.
2826		extract_fd(&else_rd, pipe_rd.fdset, is_readable_pipe);
2827		extract_fd(&else_rd, cons_rd.fdset, is_readable_console);
2828	    }
2829
2830	    if (else_rd.fdset->fd_count || else_wr.fdset->fd_count) {
2831		r = do_select(nfds, rd, wr, ex, &zero); // polling
2832		if (r < 0) break; // XXX: should I ignore error and return signaled handles?
2833		r += copy_fd(rd, else_rd.fdset);
2834		r += copy_fd(wr, else_wr.fdset);
2835		if (ex)
2836		    r += ex->fd_count;
2837		break;
2838	    }
2839	    else {
2840		struct timeval *dowait = &wait;
2841
2842		fd_set orig_rd;
2843		fd_set orig_wr;
2844		fd_set orig_ex;
2845
2846		FD_ZERO(&orig_rd);
2847		FD_ZERO(&orig_wr);
2848		FD_ZERO(&orig_ex);
2849
2850		if (rd) copy_fd(&orig_rd, rd);
2851		if (wr) copy_fd(&orig_wr, wr);
2852		if (ex) copy_fd(&orig_ex, ex);
2853		r = do_select(nfds, rd, wr, ex, &zero);	// polling
2854		if (r != 0) break; // signaled or error
2855		if (rd) copy_fd(rd, &orig_rd);
2856		if (wr) copy_fd(wr, &orig_wr);
2857		if (ex) copy_fd(ex, &orig_ex);
2858
2859		if (timeout) {
2860		    struct timeval now;
2861		    gettimeofday(&now, NULL);
2862		    rest = limit;
2863		    if (!rb_w32_time_subtract(&rest, &now)) break;
2864		    if (compare(&rest, &wait) < 0) dowait = &rest;
2865		}
2866		Sleep(dowait->tv_sec * 1000 + dowait->tv_usec / 1000);
2867	    }
2868	}
2869    }
2870
2871    rb_fd_term(&except);
2872    rb_fd_term(&cons_rd);
2873    rb_fd_term(&pipe_rd);
2874    rb_fd_term(&else_wr);
2875    rb_fd_term(&else_rd);
2876
2877    return r;
2878}
2879
2880/* License: Ruby's */
2881int WSAAPI
2882rb_w32_select(int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
2883	      struct timeval *timeout)
2884{
2885    return rb_w32_select_with_thread(nfds, rd, wr, ex, timeout, 0);
2886}
2887
2888/* License: Ruby's */
2889static FARPROC
2890get_wsa_extension_function(SOCKET s, GUID *guid)
2891{
2892    DWORD dmy;
2893    FARPROC ptr = NULL;
2894
2895    WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, guid, sizeof(*guid),
2896	     &ptr, sizeof(ptr), &dmy, NULL, NULL);
2897    if (!ptr)
2898	errno = ENOSYS;
2899    return ptr;
2900}
2901
2902#undef accept
2903
2904/* License: Artistic or GPL */
2905int WSAAPI
2906rb_w32_accept(int s, struct sockaddr *addr, int *addrlen)
2907{
2908    SOCKET r;
2909    int fd;
2910
2911    if (!NtSocketsInitialized) {
2912	StartSockets();
2913    }
2914    RUBY_CRITICAL({
2915	HANDLE h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2916	fd = rb_w32_open_osfhandle((intptr_t)h, O_RDWR|O_BINARY|O_NOINHERIT);
2917	if (fd != -1) {
2918	    r = accept(TO_SOCKET(s), addr, addrlen);
2919	    if (r != INVALID_SOCKET) {
2920		SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
2921		MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
2922		_set_osfhnd(fd, r);
2923		MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
2924		CloseHandle(h);
2925		socklist_insert(r, 0);
2926	    }
2927	    else {
2928		errno = map_errno(WSAGetLastError());
2929		close(fd);
2930		fd = -1;
2931	    }
2932	}
2933	else
2934	    CloseHandle(h);
2935    });
2936    return fd;
2937}
2938
2939#undef bind
2940
2941/* License: Artistic or GPL */
2942int WSAAPI
2943rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
2944{
2945    int r;
2946
2947    if (!NtSocketsInitialized) {
2948	StartSockets();
2949    }
2950    RUBY_CRITICAL({
2951	r = bind(TO_SOCKET(s), addr, addrlen);
2952	if (r == SOCKET_ERROR)
2953	    errno = map_errno(WSAGetLastError());
2954    });
2955    return r;
2956}
2957
2958#undef connect
2959
2960/* License: Artistic or GPL */
2961int WSAAPI
2962rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
2963{
2964    int r;
2965    if (!NtSocketsInitialized) {
2966	StartSockets();
2967    }
2968    RUBY_CRITICAL({
2969	r = connect(TO_SOCKET(s), addr, addrlen);
2970	if (r == SOCKET_ERROR) {
2971	    int err = WSAGetLastError();
2972	    if (err != WSAEWOULDBLOCK)
2973		errno = map_errno(err);
2974	    else
2975		errno = EINPROGRESS;
2976	}
2977    });
2978    return r;
2979}
2980
2981
2982#undef getpeername
2983
2984/* License: Artistic or GPL */
2985int WSAAPI
2986rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
2987{
2988    int r;
2989    if (!NtSocketsInitialized) {
2990	StartSockets();
2991    }
2992    RUBY_CRITICAL({
2993	r = getpeername(TO_SOCKET(s), addr, addrlen);
2994	if (r == SOCKET_ERROR)
2995	    errno = map_errno(WSAGetLastError());
2996    });
2997    return r;
2998}
2999
3000#undef getsockname
3001
3002/* License: Artistic or GPL */
3003int WSAAPI
3004rb_w32_getsockname(int fd, struct sockaddr *addr, int *addrlen)
3005{
3006    int sock;
3007    int r;
3008    if (!NtSocketsInitialized) {
3009	StartSockets();
3010    }
3011    RUBY_CRITICAL({
3012	sock = TO_SOCKET(fd);
3013	r = getsockname(sock, addr, addrlen);
3014	if (r == SOCKET_ERROR) {
3015	    DWORD wsaerror = WSAGetLastError();
3016	    if (wsaerror == WSAEINVAL) {
3017		int flags;
3018		if (socklist_lookup(sock, &flags)) {
3019		    int af = GET_FAMILY(flags);
3020		    if (af) {
3021			memset(addr, 0, *addrlen);
3022			addr->sa_family = af;
3023			return 0;
3024		    }
3025		}
3026	    }
3027	    errno = map_errno(wsaerror);
3028	}
3029    });
3030    return r;
3031}
3032
3033#undef getsockopt
3034
3035/* License: Artistic or GPL */
3036int WSAAPI
3037rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
3038{
3039    int r;
3040    if (!NtSocketsInitialized) {
3041	StartSockets();
3042    }
3043    RUBY_CRITICAL({
3044	r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3045	if (r == SOCKET_ERROR)
3046	    errno = map_errno(WSAGetLastError());
3047    });
3048    return r;
3049}
3050
3051#undef ioctlsocket
3052
3053/* License: Artistic or GPL */
3054int WSAAPI
3055rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
3056{
3057    int r;
3058    if (!NtSocketsInitialized) {
3059	StartSockets();
3060    }
3061    RUBY_CRITICAL({
3062	r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3063	if (r == SOCKET_ERROR)
3064	    errno = map_errno(WSAGetLastError());
3065    });
3066    return r;
3067}
3068
3069#undef listen
3070
3071/* License: Artistic or GPL */
3072int WSAAPI
3073rb_w32_listen(int s, int backlog)
3074{
3075    int r;
3076    if (!NtSocketsInitialized) {
3077	StartSockets();
3078    }
3079    RUBY_CRITICAL({
3080	r = listen(TO_SOCKET(s), backlog);
3081	if (r == SOCKET_ERROR)
3082	    errno = map_errno(WSAGetLastError());
3083    });
3084    return r;
3085}
3086
3087#undef recv
3088#undef recvfrom
3089#undef send
3090#undef sendto
3091
3092/* License: Ruby's */
3093static int
3094finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
3095{
3096    DWORD flg;
3097    int err;
3098
3099    if (result != SOCKET_ERROR)
3100	*len = size;
3101    else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
3102	switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
3103	  case WAIT_OBJECT_0:
3104	    RUBY_CRITICAL(
3105		result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg)
3106		);
3107	    if (result) {
3108		*len = size;
3109		break;
3110	    }
3111	    /* thru */
3112	  default:
3113	    if ((err = WSAGetLastError()) == WSAECONNABORTED && !input)
3114		errno = EPIPE;
3115	    else
3116		errno = map_errno(WSAGetLastError());
3117	    /* thru */
3118	  case WAIT_OBJECT_0 + 1:
3119	    /* interrupted */
3120	    *len = -1;
3121	    cancel_io((HANDLE)s);
3122	    break;
3123	}
3124    }
3125    else {
3126	if (err == WSAECONNABORTED && !input)
3127	    errno = EPIPE;
3128	else
3129	    errno = map_errno(err);
3130	*len = -1;
3131    }
3132    CloseHandle(wol->hEvent);
3133
3134    return result;
3135}
3136
3137/* License: Artistic or GPL */
3138static int
3139overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
3140		     struct sockaddr *addr, int *addrlen)
3141{
3142    int r;
3143    int ret;
3144    int mode = 0;
3145    DWORD flg;
3146    WSAOVERLAPPED wol;
3147    WSABUF wbuf;
3148    SOCKET s;
3149
3150    if (!NtSocketsInitialized)
3151	StartSockets();
3152
3153    s = TO_SOCKET(fd);
3154    socklist_lookup(s, &mode);
3155    if (!cancel_io || (GET_FLAGS(mode) & O_NONBLOCK)) {
3156	RUBY_CRITICAL({
3157	    if (input) {
3158		if (addr && addrlen)
3159		    r = recvfrom(s, buf, len, flags, addr, addrlen);
3160		else
3161		    r = recv(s, buf, len, flags);
3162		if (r == SOCKET_ERROR)
3163		    errno = map_errno(WSAGetLastError());
3164	    }
3165	    else {
3166		if (addr && addrlen)
3167		    r = sendto(s, buf, len, flags, addr, *addrlen);
3168		else
3169		    r = send(s, buf, len, flags);
3170		if (r == SOCKET_ERROR) {
3171		    DWORD err = WSAGetLastError();
3172		    if (err == WSAECONNABORTED)
3173			errno = EPIPE;
3174		    else
3175			errno = map_errno(err);
3176		}
3177	    }
3178	});
3179    }
3180    else {
3181	DWORD size;
3182	DWORD rlen;
3183	wbuf.len = len;
3184	wbuf.buf = buf;
3185	memset(&wol, 0, sizeof(wol));
3186	RUBY_CRITICAL({
3187	    wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3188	    if (input) {
3189		flg = flags;
3190		if (addr && addrlen)
3191		    ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3192				      &wol, NULL);
3193		else
3194		    ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3195	    }
3196	    else {
3197		if (addr && addrlen)
3198		    ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3199				    &wol, NULL);
3200		else
3201		    ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3202	    }
3203	});
3204
3205	finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3206	r = (int)rlen;
3207    }
3208
3209    return r;
3210}
3211
3212/* License: Ruby's */
3213int WSAAPI
3214rb_w32_recv(int fd, char *buf, int len, int flags)
3215{
3216    return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
3217}
3218
3219/* License: Ruby's */
3220int WSAAPI
3221rb_w32_recvfrom(int fd, char *buf, int len, int flags,
3222		struct sockaddr *from, int *fromlen)
3223{
3224    return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
3225}
3226
3227/* License: Ruby's */
3228int WSAAPI
3229rb_w32_send(int fd, const char *buf, int len, int flags)
3230{
3231    return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
3232}
3233
3234/* License: Ruby's */
3235int WSAAPI
3236rb_w32_sendto(int fd, const char *buf, int len, int flags,
3237	      const struct sockaddr *to, int tolen)
3238{
3239    return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
3240				(struct sockaddr *)to, &tolen);
3241}
3242
3243#if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3244/* License: Ruby's */
3245typedef struct {
3246    SOCKADDR *name;
3247    int namelen;
3248    WSABUF *lpBuffers;
3249    DWORD dwBufferCount;
3250    WSABUF Control;
3251    DWORD dwFlags;
3252} WSAMSG;
3253#endif
3254#ifndef WSAID_WSARECVMSG
3255#define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3256#endif
3257#ifndef WSAID_WSASENDMSG
3258#define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3259#endif
3260
3261/* License: Ruby's */
3262#define msghdr_to_wsamsg(msg, wsamsg) \
3263    do { \
3264	int i; \
3265	(wsamsg)->name = (msg)->msg_name; \
3266	(wsamsg)->namelen = (msg)->msg_namelen; \
3267	(wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3268	(wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3269	for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3270	    (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3271	    (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3272	} \
3273	(wsamsg)->Control.buf = (msg)->msg_control; \
3274	(wsamsg)->Control.len = (msg)->msg_controllen; \
3275	(wsamsg)->dwFlags = (msg)->msg_flags; \
3276    } while (0)
3277
3278/* License: Ruby's */
3279int
3280recvmsg(int fd, struct msghdr *msg, int flags)
3281{
3282    typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3283    static WSARecvMsg_t pWSARecvMsg = NULL;
3284    WSAMSG wsamsg;
3285    SOCKET s;
3286    int mode = 0;
3287    DWORD len;
3288    int ret;
3289
3290    if (!NtSocketsInitialized)
3291	StartSockets();
3292
3293    s = TO_SOCKET(fd);
3294
3295    if (!pWSARecvMsg) {
3296	static GUID guid = WSAID_WSARECVMSG;
3297	pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, &guid);
3298	if (!pWSARecvMsg)
3299	    return -1;
3300    }
3301
3302    msghdr_to_wsamsg(msg, &wsamsg);
3303    wsamsg.dwFlags |= flags;
3304
3305    socklist_lookup(s, &mode);
3306    if (!cancel_io || (GET_FLAGS(mode) & O_NONBLOCK)) {
3307	RUBY_CRITICAL({
3308	    if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
3309		errno = map_errno(WSAGetLastError());
3310		len = -1;
3311	    }
3312	});
3313    }
3314    else {
3315	DWORD size;
3316	WSAOVERLAPPED wol;
3317	memset(&wol, 0, sizeof(wol));
3318	RUBY_CRITICAL({
3319	    wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3320	    ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3321	});
3322
3323	ret = finish_overlapped_socket(TRUE, s, &wol, ret, &len, size);
3324    }
3325    if (ret == SOCKET_ERROR)
3326	return -1;
3327
3328    /* WSAMSG to msghdr */
3329    msg->msg_name = wsamsg.name;
3330    msg->msg_namelen = wsamsg.namelen;
3331    msg->msg_flags = wsamsg.dwFlags;
3332
3333    return len;
3334}
3335
3336/* License: Ruby's */
3337int
3338sendmsg(int fd, const struct msghdr *msg, int flags)
3339{
3340    typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3341    static WSASendMsg_t pWSASendMsg = NULL;
3342    WSAMSG wsamsg;
3343    SOCKET s;
3344    int mode = 0;
3345    DWORD len;
3346    int ret;
3347
3348    if (!NtSocketsInitialized)
3349	StartSockets();
3350
3351    s = TO_SOCKET(fd);
3352
3353    if (!pWSASendMsg) {
3354	static GUID guid = WSAID_WSASENDMSG;
3355	pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, &guid);
3356	if (!pWSASendMsg)
3357	    return -1;
3358    }
3359
3360    msghdr_to_wsamsg(msg, &wsamsg);
3361
3362    socklist_lookup(s, &mode);
3363    if (!cancel_io || (GET_FLAGS(mode) & O_NONBLOCK)) {
3364	RUBY_CRITICAL({
3365	    if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
3366		errno = map_errno(WSAGetLastError());
3367		len = -1;
3368	    }
3369	});
3370    }
3371    else {
3372	DWORD size;
3373	WSAOVERLAPPED wol;
3374	memset(&wol, 0, sizeof(wol));
3375	RUBY_CRITICAL({
3376	    wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3377	    ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3378	});
3379
3380	finish_overlapped_socket(FALSE, s, &wol, ret, &len, size);
3381    }
3382
3383    return len;
3384}
3385
3386#undef setsockopt
3387
3388/* License: Artistic or GPL */
3389int WSAAPI
3390rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
3391{
3392    int r;
3393    if (!NtSocketsInitialized) {
3394	StartSockets();
3395    }
3396    RUBY_CRITICAL({
3397	r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3398	if (r == SOCKET_ERROR)
3399	    errno = map_errno(WSAGetLastError());
3400    });
3401    return r;
3402}
3403
3404#undef shutdown
3405
3406/* License: Artistic or GPL */
3407int WSAAPI
3408rb_w32_shutdown(int s, int how)
3409{
3410    int r;
3411    if (!NtSocketsInitialized) {
3412	StartSockets();
3413    }
3414    RUBY_CRITICAL({
3415	r = shutdown(TO_SOCKET(s), how);
3416	if (r == SOCKET_ERROR)
3417	    errno = map_errno(WSAGetLastError());
3418    });
3419    return r;
3420}
3421
3422/* License: Ruby's */
3423static SOCKET
3424open_ifs_socket(int af, int type, int protocol)
3425{
3426    unsigned long proto_buffers_len = 0;
3427    int error_code;
3428    SOCKET out = INVALID_SOCKET;
3429
3430    if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3431	error_code = WSAGetLastError();
3432	if (error_code == WSAENOBUFS) {
3433	    WSAPROTOCOL_INFO *proto_buffers;
3434	    int protocols_available = 0;
3435
3436	    proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3437	    if (!proto_buffers) {
3438		WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3439		return INVALID_SOCKET;
3440	    }
3441
3442	    protocols_available =
3443		WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3444	    if (protocols_available != SOCKET_ERROR) {
3445		int i;
3446		for (i = 0; i < protocols_available; i++) {
3447		    if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3448			(type != proto_buffers[i].iSocketType) ||
3449			(protocol != 0 && protocol != proto_buffers[i].iProtocol))
3450			continue;
3451
3452		    if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3453			continue;
3454
3455		    out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
3456				    WSA_FLAG_OVERLAPPED);
3457		    break;
3458		}
3459		if (out == INVALID_SOCKET)
3460		    out = WSASocket(af, type, protocol, NULL, 0, 0);
3461		if (out != INVALID_SOCKET)
3462		    SetHandleInformation((HANDLE)out, HANDLE_FLAG_INHERIT, 0);
3463	    }
3464
3465	    free(proto_buffers);
3466	}
3467    }
3468
3469    return out;
3470}
3471
3472#undef socket
3473
3474/* License: Artistic or GPL */
3475int WSAAPI
3476rb_w32_socket(int af, int type, int protocol)
3477{
3478    SOCKET s;
3479    int fd;
3480
3481    if (!NtSocketsInitialized) {
3482	StartSockets();
3483    }
3484    RUBY_CRITICAL({
3485	s = open_ifs_socket(af, type, protocol);
3486	if (s == INVALID_SOCKET) {
3487	    errno = map_errno(WSAGetLastError());
3488	    fd = -1;
3489	}
3490	else {
3491	    fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3492	    if (fd != -1)
3493		socklist_insert(s, MAKE_SOCKDATA(af, 0));
3494	    else
3495		closesocket(s);
3496	}
3497    });
3498    return fd;
3499}
3500
3501#undef gethostbyaddr
3502
3503/* License: Artistic or GPL */
3504struct hostent * WSAAPI
3505rb_w32_gethostbyaddr(const char *addr, int len, int type)
3506{
3507    struct hostent *r;
3508    if (!NtSocketsInitialized) {
3509	StartSockets();
3510    }
3511    RUBY_CRITICAL({
3512	r = gethostbyaddr(addr, len, type);
3513	if (r == NULL)
3514	    errno = map_errno(WSAGetLastError());
3515    });
3516    return r;
3517}
3518
3519#undef gethostbyname
3520
3521/* License: Artistic or GPL */
3522struct hostent * WSAAPI
3523rb_w32_gethostbyname(const char *name)
3524{
3525    struct hostent *r;
3526    if (!NtSocketsInitialized) {
3527	StartSockets();
3528    }
3529    RUBY_CRITICAL({
3530	r = gethostbyname(name);
3531	if (r == NULL)
3532	    errno = map_errno(WSAGetLastError());
3533    });
3534    return r;
3535}
3536
3537#undef gethostname
3538
3539/* License: Artistic or GPL */
3540int WSAAPI
3541rb_w32_gethostname(char *name, int len)
3542{
3543    int r;
3544    if (!NtSocketsInitialized) {
3545	StartSockets();
3546    }
3547    RUBY_CRITICAL({
3548	r = gethostname(name, len);
3549	if (r == SOCKET_ERROR)
3550	    errno = map_errno(WSAGetLastError());
3551    });
3552    return r;
3553}
3554
3555#undef getprotobyname
3556
3557/* License: Artistic or GPL */
3558struct protoent * WSAAPI
3559rb_w32_getprotobyname(const char *name)
3560{
3561    struct protoent *r;
3562    if (!NtSocketsInitialized) {
3563	StartSockets();
3564    }
3565    RUBY_CRITICAL({
3566	r = getprotobyname(name);
3567	if (r == NULL)
3568	    errno = map_errno(WSAGetLastError());
3569    });
3570    return r;
3571}
3572
3573#undef getprotobynumber
3574
3575/* License: Artistic or GPL */
3576struct protoent * WSAAPI
3577rb_w32_getprotobynumber(int num)
3578{
3579    struct protoent *r;
3580    if (!NtSocketsInitialized) {
3581	StartSockets();
3582    }
3583    RUBY_CRITICAL({
3584	r = getprotobynumber(num);
3585	if (r == NULL)
3586	    errno = map_errno(WSAGetLastError());
3587    });
3588    return r;
3589}
3590
3591#undef getservbyname
3592
3593/* License: Artistic or GPL */
3594struct servent * WSAAPI
3595rb_w32_getservbyname(const char *name, const char *proto)
3596{
3597    struct servent *r;
3598    if (!NtSocketsInitialized) {
3599	StartSockets();
3600    }
3601    RUBY_CRITICAL({
3602	r = getservbyname(name, proto);
3603	if (r == NULL)
3604	    errno = map_errno(WSAGetLastError());
3605    });
3606    return r;
3607}
3608
3609#undef getservbyport
3610
3611/* License: Artistic or GPL */
3612struct servent * WSAAPI
3613rb_w32_getservbyport(int port, const char *proto)
3614{
3615    struct servent *r;
3616    if (!NtSocketsInitialized) {
3617	StartSockets();
3618    }
3619    RUBY_CRITICAL({
3620	r = getservbyport(port, proto);
3621	if (r == NULL)
3622	    errno = map_errno(WSAGetLastError());
3623    });
3624    return r;
3625}
3626
3627/* License: Ruby's */
3628static int
3629socketpair_internal(int af, int type, int protocol, SOCKET *sv)
3630{
3631    SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
3632    struct sockaddr_in sock_in4;
3633#ifdef INET6
3634    struct sockaddr_in6 sock_in6;
3635#endif
3636    struct sockaddr *addr;
3637    int ret = -1;
3638    int len;
3639
3640    if (!NtSocketsInitialized) {
3641	StartSockets();
3642    }
3643
3644    switch (af) {
3645      case AF_INET:
3646#if defined PF_INET && PF_INET != AF_INET
3647      case PF_INET:
3648#endif
3649	sock_in4.sin_family = AF_INET;
3650	sock_in4.sin_port = 0;
3651	sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
3652	addr = (struct sockaddr *)&sock_in4;
3653	len = sizeof(sock_in4);
3654	break;
3655#ifdef INET6
3656      case AF_INET6:
3657	memset(&sock_in6, 0, sizeof(sock_in6));
3658	sock_in6.sin6_family = AF_INET6;
3659	sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
3660	addr = (struct sockaddr *)&sock_in6;
3661	len = sizeof(sock_in6);
3662	break;
3663#endif
3664      default:
3665	errno = EAFNOSUPPORT;
3666	return -1;
3667    }
3668    if (type != SOCK_STREAM) {
3669	errno = EPROTOTYPE;
3670	return -1;
3671    }
3672
3673    sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
3674    sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
3675    RUBY_CRITICAL({
3676	do {
3677	    svr = open_ifs_socket(af, type, protocol);
3678	    if (svr == INVALID_SOCKET)
3679		break;
3680	    if (bind(svr, addr, len) < 0)
3681		break;
3682	    if (getsockname(svr, addr, &len) < 0)
3683		break;
3684	    if (type == SOCK_STREAM)
3685		listen(svr, 5);
3686
3687	    w = open_ifs_socket(af, type, protocol);
3688	    if (w == INVALID_SOCKET)
3689		break;
3690	    if (connect(w, addr, len) < 0)
3691		break;
3692
3693	    r = accept(svr, addr, &len);
3694	    if (r == INVALID_SOCKET)
3695		break;
3696	    SetHandleInformation((HANDLE)r, HANDLE_FLAG_INHERIT, 0);
3697
3698	    ret = 0;
3699	} while (0);
3700
3701	if (ret < 0) {
3702	    errno = map_errno(WSAGetLastError());
3703	    if (r != INVALID_SOCKET)
3704		closesocket(r);
3705	    if (w != INVALID_SOCKET)
3706		closesocket(w);
3707	}
3708	else {
3709	    sv[0] = r;
3710	    sv[1] = w;
3711	}
3712	if (svr != INVALID_SOCKET)
3713	    closesocket(svr);
3714    });
3715
3716    return ret;
3717}
3718
3719/* License: Ruby's */
3720int
3721rb_w32_socketpair(int af, int type, int protocol, int *sv)
3722{
3723    SOCKET pair[2];
3724
3725    if (socketpair_internal(af, type, protocol, pair) < 0)
3726	return -1;
3727    sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
3728    if (sv[0] == -1) {
3729	closesocket(pair[0]);
3730	closesocket(pair[1]);
3731	return -1;
3732    }
3733    sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
3734    if (sv[1] == -1) {
3735	rb_w32_close(sv[0]);
3736	closesocket(pair[1]);
3737	return -1;
3738    }
3739    socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
3740    socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
3741
3742    return 0;
3743}
3744
3745//
3746// Networking stubs
3747//
3748
3749void endhostent(void) {}
3750void endnetent(void) {}
3751void endprotoent(void) {}
3752void endservent(void) {}
3753
3754struct netent *getnetent (void) {return (struct netent *) NULL;}
3755
3756struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
3757
3758struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
3759
3760struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
3761
3762struct servent *getservent (void) {return (struct servent *) NULL;}
3763
3764void sethostent (int stayopen) {}
3765
3766void setnetent (int stayopen) {}
3767
3768void setprotoent (int stayopen) {}
3769
3770void setservent (int stayopen) {}
3771
3772/* License: Ruby's */
3773static int
3774setfl(SOCKET sock, int arg)
3775{
3776    int ret;
3777    int af = 0;
3778    int flag = 0;
3779    u_long ioctlArg;
3780
3781    socklist_lookup(sock, &flag);
3782    af = GET_FAMILY(flag);
3783    flag = GET_FLAGS(flag);
3784    if (arg & O_NONBLOCK) {
3785	flag |= O_NONBLOCK;
3786	ioctlArg = 1;
3787    }
3788    else {
3789	flag &= ~O_NONBLOCK;
3790	ioctlArg = 0;
3791    }
3792    RUBY_CRITICAL({
3793	ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
3794	if (ret == 0)
3795	    socklist_insert(sock, MAKE_SOCKDATA(af, flag));
3796	else
3797	    errno = map_errno(WSAGetLastError());
3798    });
3799
3800    return ret;
3801}
3802
3803/* License: Ruby's */
3804static int
3805dupfd(HANDLE hDup, char flags, int minfd)
3806{
3807    int save_errno;
3808    int ret;
3809    int fds[32];
3810    int filled = 0;
3811
3812    do {
3813	ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
3814	if (ret == -1) {
3815	    goto close_fds_and_return;
3816	}
3817	if (ret >= minfd) {
3818	    goto close_fds_and_return;
3819	}
3820	fds[filled++] = ret;
3821    } while (filled < (int)numberof(fds));
3822
3823    ret = dupfd(hDup, flags, minfd);
3824
3825  close_fds_and_return:
3826    save_errno = errno;
3827    while (filled > 0) {
3828	int fd = fds[--filled];
3829	_osfhnd(fd) = (intptr_t)INVALID_HANDLE_VALUE;
3830	close(fd);
3831    }
3832    errno = save_errno;
3833
3834    return ret;
3835}
3836
3837/* License: Ruby's */
3838int
3839fcntl(int fd, int cmd, ...)
3840{
3841    va_list va;
3842    int arg;
3843
3844    if (cmd == F_SETFL) {
3845	SOCKET sock = TO_SOCKET(fd);
3846	if (!is_socket(sock)) {
3847	    errno = EBADF;
3848	    return -1;
3849	}
3850
3851	va_start(va, cmd);
3852	arg = va_arg(va, int);
3853	va_end(va);
3854	return setfl(sock, arg);
3855    }
3856    else if (cmd == F_DUPFD) {
3857	int ret;
3858	HANDLE hDup;
3859	if (!(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
3860			      GetCurrentProcess(), &hDup, 0L,
3861			      !(_osfile(fd) & FNOINHERIT),
3862			      DUPLICATE_SAME_ACCESS))) {
3863	    errno = map_errno(GetLastError());
3864	    return -1;
3865	}
3866
3867	va_start(va, cmd);
3868	arg = va_arg(va, int);
3869	va_end(va);
3870
3871	if ((ret = dupfd(hDup, _osfile(fd), arg)) == -1)
3872	    CloseHandle(hDup);
3873	return ret;
3874    }
3875    else {
3876	errno = EINVAL;
3877	return -1;
3878    }
3879}
3880
3881#ifndef WNOHANG
3882#define WNOHANG -1
3883#endif
3884
3885/* License: Ruby's */
3886static rb_pid_t
3887poll_child_status(struct ChildRecord *child, int *stat_loc)
3888{
3889    DWORD exitcode;
3890    DWORD err;
3891
3892    if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
3893	/* If an error occured, return immediatly. */
3894    error_exit:
3895	err = GetLastError();
3896	if (err == ERROR_INVALID_PARAMETER)
3897	    errno = ECHILD;
3898	else {
3899	    if (GetLastError() == ERROR_INVALID_HANDLE)
3900		errno = EINVAL;
3901	    else
3902		errno = map_errno(GetLastError());
3903	}
3904	CloseChildHandle(child);
3905	return -1;
3906    }
3907    if (exitcode != STILL_ACTIVE) {
3908        rb_pid_t pid;
3909	/* If already died, wait process's real termination. */
3910        if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
3911	    goto error_exit;
3912        }
3913	pid = child->pid;
3914	CloseChildHandle(child);
3915	if (stat_loc) *stat_loc = exitcode << 8;
3916	return pid;
3917    }
3918    return 0;
3919}
3920
3921/* License: Artistic or GPL */
3922rb_pid_t
3923waitpid(rb_pid_t pid, int *stat_loc, int options)
3924{
3925    DWORD timeout;
3926
3927    /* Artistic or GPL part start */
3928    if (options == WNOHANG) {
3929	timeout = 0;
3930    }
3931    else {
3932	timeout = INFINITE;
3933    }
3934    /* Artistic or GPL part end */
3935
3936    if (pid == -1) {
3937	int count = 0;
3938	int ret;
3939	HANDLE events[MAXCHILDNUM];
3940	struct ChildRecord* cause;
3941
3942	FOREACH_CHILD(child) {
3943	    if (!child->pid || child->pid < 0) continue;
3944	    if ((pid = poll_child_status(child, stat_loc))) return pid;
3945	    events[count++] = child->hProcess;
3946	} END_FOREACH_CHILD;
3947	if (!count) {
3948	    errno = ECHILD;
3949	    return -1;
3950	}
3951
3952	ret = rb_w32_wait_events_blocking(events, count, timeout);
3953	if (ret == WAIT_TIMEOUT) return 0;
3954	if ((ret -= WAIT_OBJECT_0) == count) {
3955	    return -1;
3956	}
3957	if (ret > count) {
3958	    errno = map_errno(GetLastError());
3959	    return -1;
3960	}
3961
3962	cause = FindChildSlotByHandle(events[ret]);
3963	if (!cause) {
3964	    errno = ECHILD;
3965	    return -1;
3966	}
3967	return poll_child_status(cause, stat_loc);
3968    }
3969    else {
3970	struct ChildRecord* child = FindChildSlot(pid);
3971	if (!child) {
3972	    errno = ECHILD;
3973	    return -1;
3974	}
3975
3976	while (!(pid = poll_child_status(child, stat_loc))) {
3977	    /* wait... */
3978	    if (rb_w32_wait_events_blocking(&child->hProcess, 1, timeout) != WAIT_OBJECT_0) {
3979		/* still active */
3980		pid = 0;
3981		break;
3982	    }
3983	}
3984    }
3985
3986    return pid;
3987}
3988
3989#include <sys/timeb.h>
3990
3991/* License: Ruby's */
3992static int
3993filetime_to_timeval(const FILETIME* ft, struct timeval *tv)
3994{
3995    ULARGE_INTEGER tmp;
3996    unsigned LONG_LONG lt;
3997
3998    tmp.LowPart = ft->dwLowDateTime;
3999    tmp.HighPart = ft->dwHighDateTime;
4000    lt = tmp.QuadPart;
4001
4002    /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
4003       convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
4004       the first leap second is at 1972/06/30, so we doesn't need to think
4005       about it. */
4006    lt /= 10;	/* to usec */
4007    lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * 1000 * 1000;
4008
4009    tv->tv_sec = (long)(lt / (1000 * 1000));
4010    tv->tv_usec = (long)(lt % (1000 * 1000));
4011
4012    return tv->tv_sec > 0 ? 0 : -1;
4013}
4014
4015/* License: Ruby's */
4016int _cdecl
4017gettimeofday(struct timeval *tv, struct timezone *tz)
4018{
4019    FILETIME ft;
4020
4021    GetSystemTimeAsFileTime(&ft);
4022    filetime_to_timeval(&ft, tv);
4023
4024    return 0;
4025}
4026
4027/* License: Ruby's */
4028char *
4029rb_w32_getcwd(char *buffer, int size)
4030{
4031    char *p = buffer;
4032    int len;
4033
4034    len = GetCurrentDirectory(0, NULL);
4035    if (!len) {
4036	errno = map_errno(GetLastError());
4037	return NULL;
4038    }
4039
4040    if (p) {
4041	if (size < len) {
4042	    errno = ERANGE;
4043	    return NULL;
4044	}
4045    }
4046    else {
4047	p = malloc(len);
4048	size = len;
4049	if (!p) {
4050	    errno = ENOMEM;
4051	    return NULL;
4052	}
4053    }
4054
4055    if (!GetCurrentDirectory(size, p)) {
4056	errno = map_errno(GetLastError());
4057	if (!buffer)
4058	    free(p);
4059        return NULL;
4060    }
4061
4062    translate_char(p, '\\', '/');
4063
4064    return p;
4065}
4066
4067/* License: Artistic or GPL */
4068int
4069chown(const char *path, int owner, int group)
4070{
4071    return 0;
4072}
4073
4074/* License: Artistic or GPL */
4075int
4076rb_w32_uchown(const char *path, int owner, int group)
4077{
4078    return 0;
4079}
4080
4081/* License: Ruby's */
4082int
4083kill(int pid, int sig)
4084{
4085    int ret = 0;
4086    DWORD err;
4087
4088    if (pid < 0 || pid == 0 && sig != SIGINT) {
4089	errno = EINVAL;
4090	return -1;
4091    }
4092
4093    if ((unsigned int)pid == GetCurrentProcessId() &&
4094	(sig != 0 && sig != SIGKILL)) {
4095	if ((ret = raise(sig)) != 0) {
4096	    /* MSVCRT doesn't set errno... */
4097	    errno = EINVAL;
4098	}
4099	return ret;
4100    }
4101
4102    switch (sig) {
4103      case 0:
4104	RUBY_CRITICAL({
4105	    HANDLE hProc =
4106		OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4107	    if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4108		if (GetLastError() == ERROR_INVALID_PARAMETER) {
4109		    errno = ESRCH;
4110		}
4111		else {
4112		    errno = EPERM;
4113		}
4114		ret = -1;
4115	    }
4116	    else {
4117		CloseHandle(hProc);
4118	    }
4119	});
4120	break;
4121
4122      case SIGINT:
4123	RUBY_CRITICAL({
4124	    DWORD ctrlEvent = CTRL_C_EVENT;
4125	    if (pid != 0) {
4126	        /* CTRL+C signal cannot be generated for process groups.
4127		 * Instead, we use CTRL+BREAK signal. */
4128	        ctrlEvent = CTRL_BREAK_EVENT;
4129	    }
4130	    if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
4131		if ((err = GetLastError()) == 0)
4132		    errno = EPERM;
4133		else
4134		    errno = map_errno(GetLastError());
4135		ret = -1;
4136	    }
4137	});
4138	break;
4139
4140      case SIGKILL:
4141	RUBY_CRITICAL({
4142	    HANDLE hProc;
4143	    struct ChildRecord* child = FindChildSlot(pid);
4144	    if (child) {
4145		hProc = child->hProcess;
4146	    }
4147	    else {
4148		hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4149	    }
4150	    if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4151		if (GetLastError() == ERROR_INVALID_PARAMETER) {
4152		    errno = ESRCH;
4153		}
4154		else {
4155		    errno = EPERM;
4156		}
4157		ret = -1;
4158	    }
4159	    else {
4160		DWORD status;
4161		if (!GetExitCodeProcess(hProc, &status)) {
4162		    errno = map_errno(GetLastError());
4163		    ret = -1;
4164		}
4165		else if (status == STILL_ACTIVE) {
4166		    if (!TerminateProcess(hProc, 0)) {
4167			errno = EPERM;
4168			ret = -1;
4169		    }
4170		}
4171		else {
4172		    errno = ESRCH;
4173		    ret = -1;
4174		}
4175		if (!child) {
4176		    CloseHandle(hProc);
4177		}
4178	    }
4179	});
4180	break;
4181
4182      default:
4183	errno = EINVAL;
4184	ret = -1;
4185	break;
4186    }
4187
4188    return ret;
4189}
4190
4191/* License: Ruby's */
4192static int
4193wlink(const WCHAR *from, const WCHAR *to)
4194{
4195    typedef BOOL (WINAPI link_func)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
4196    static link_func *pCreateHardLinkW = NULL;
4197    static int myerrno = 0;
4198
4199    if (!pCreateHardLinkW && !myerrno) {
4200	pCreateHardLinkW = (link_func *)get_proc_address("kernel32", "CreateHardLinkW", NULL);
4201	if (!pCreateHardLinkW)
4202	    myerrno = ENOSYS;
4203    }
4204    if (!pCreateHardLinkW) {
4205	errno = myerrno;
4206	return -1;
4207    }
4208
4209    if (!pCreateHardLinkW(to, from, NULL)) {
4210	errno = map_errno(GetLastError());
4211	return -1;
4212    }
4213
4214    return 0;
4215}
4216
4217/* License: Ruby's */
4218int
4219rb_w32_ulink(const char *from, const char *to)
4220{
4221    WCHAR *wfrom;
4222    WCHAR *wto;
4223    int ret;
4224
4225    if (!(wfrom = utf8_to_wstr(from, NULL)))
4226	return -1;
4227    if (!(wto = utf8_to_wstr(to, NULL))) {
4228	free(wfrom);
4229	return -1;
4230    }
4231    ret = wlink(wfrom, wto);
4232    free(wto);
4233    free(wfrom);
4234    return ret;
4235}
4236
4237/* License: Ruby's */
4238int
4239link(const char *from, const char *to)
4240{
4241    WCHAR *wfrom;
4242    WCHAR *wto;
4243    int ret;
4244
4245    if (!(wfrom = filecp_to_wstr(from, NULL)))
4246	return -1;
4247    if (!(wto = filecp_to_wstr(to, NULL))) {
4248	free(wfrom);
4249	return -1;
4250    }
4251    ret = wlink(wfrom, wto);
4252    free(wto);
4253    free(wfrom);
4254    return ret;
4255}
4256
4257/* License: Ruby's */
4258int
4259wait(int *status)
4260{
4261    return waitpid(-1, status, 0);
4262}
4263
4264/* License: Ruby's */
4265char *
4266rb_w32_ugetenv(const char *name)
4267{
4268    WCHAR *wenvarea, *wenv;
4269    int len = strlen(name);
4270    char *env;
4271    int wlen;
4272
4273    if (len == 0) return NULL;
4274
4275    if (uenvarea) {
4276	free(uenvarea);
4277	uenvarea = NULL;
4278    }
4279    if (envarea) {
4280	FreeEnvironmentStrings(envarea);
4281	envarea = NULL;
4282    }
4283    wenvarea = GetEnvironmentStringsW();
4284    if (!wenvarea) {
4285	map_errno(GetLastError());
4286	return NULL;
4287    }
4288    for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
4289	wlen += lstrlenW(wenv) + 1;
4290    uenvarea = wstr_to_mbstr(CP_UTF8, wenvarea, wlen, NULL);
4291    FreeEnvironmentStringsW(wenvarea);
4292    if (!uenvarea)
4293	return NULL;
4294
4295    for (env = uenvarea; *env; env += strlen(env) + 1)
4296	if (strncasecmp(env, name, len) == 0 && *(env + len) == '=')
4297	    return env + len + 1;
4298
4299    return NULL;
4300}
4301
4302/* License: Ruby's */
4303char *
4304rb_w32_getenv(const char *name)
4305{
4306    int len = strlen(name);
4307    char *env;
4308
4309    if (len == 0) return NULL;
4310    if (uenvarea) {
4311	free(uenvarea);
4312	uenvarea = NULL;
4313    }
4314    if (envarea) {
4315	FreeEnvironmentStrings(envarea);
4316	envarea = NULL;
4317    }
4318    envarea = GetEnvironmentStrings();
4319    if (!envarea) {
4320	map_errno(GetLastError());
4321	return NULL;
4322    }
4323
4324    for (env = envarea; *env; env += strlen(env) + 1)
4325	if (strncasecmp(env, name, len) == 0 && *(env + len) == '=')
4326	    return env + len + 1;
4327
4328    return NULL;
4329}
4330
4331/* License: Artistic or GPL */
4332static int
4333wrename(const WCHAR *oldpath, const WCHAR *newpath)
4334{
4335    int res = 0;
4336    int oldatts;
4337    int newatts;
4338
4339    oldatts = GetFileAttributesW(oldpath);
4340    newatts = GetFileAttributesW(newpath);
4341
4342    if (oldatts == -1) {
4343	errno = map_errno(GetLastError());
4344	return -1;
4345    }
4346
4347    RUBY_CRITICAL({
4348	if (newatts != -1 && newatts & FILE_ATTRIBUTE_READONLY)
4349	    SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
4350
4351	if (!MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
4352	    res = -1;
4353
4354	if (res)
4355	    errno = map_errno(GetLastError());
4356	else
4357	    SetFileAttributesW(newpath, oldatts);
4358    });
4359
4360    return res;
4361}
4362
4363/* License: Ruby's */
4364int rb_w32_urename(const char *from, const char *to)
4365{
4366    WCHAR *wfrom;
4367    WCHAR *wto;
4368    int ret = -1;
4369
4370    if (!(wfrom = utf8_to_wstr(from, NULL)))
4371	return -1;
4372    if (!(wto = utf8_to_wstr(to, NULL))) {
4373	free(wfrom);
4374	return -1;
4375    }
4376    ret = wrename(wfrom, wto);
4377    free(wto);
4378    free(wfrom);
4379    return ret;
4380}
4381
4382/* License: Ruby's */
4383int rb_w32_rename(const char *from, const char *to)
4384{
4385    WCHAR *wfrom;
4386    WCHAR *wto;
4387    int ret = -1;
4388
4389    if (!(wfrom = filecp_to_wstr(from, NULL)))
4390	return -1;
4391    if (!(wto = filecp_to_wstr(to, NULL))) {
4392	free(wfrom);
4393	return -1;
4394    }
4395    ret = wrename(wfrom, wto);
4396    free(wto);
4397    free(wfrom);
4398    return ret;
4399}
4400
4401/* License: Ruby's */
4402static int
4403isUNCRoot(const WCHAR *path)
4404{
4405    if (path[0] == L'\\' && path[1] == L'\\') {
4406	const WCHAR *p = path + 2;
4407	if (p[0] == L'?' && p[1] == L'\\') {
4408	    p += 2;
4409	}
4410	for (; *p; p++) {
4411	    if (*p == L'\\')
4412		break;
4413	}
4414	if (p[0] && p[1]) {
4415	    for (p++; *p; p++) {
4416		if (*p == L'\\')
4417		    break;
4418	    }
4419	    if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
4420		return 1;
4421	}
4422    }
4423    return 0;
4424}
4425
4426#define COPY_STAT(src, dest, size_cast) do {	\
4427	(dest).st_dev 	= (src).st_dev;		\
4428	(dest).st_ino 	= (src).st_ino;		\
4429	(dest).st_mode  = (src).st_mode;	\
4430	(dest).st_nlink = (src).st_nlink;	\
4431	(dest).st_uid   = (src).st_uid;		\
4432	(dest).st_gid   = (src).st_gid;		\
4433	(dest).st_rdev 	= (src).st_rdev;	\
4434	(dest).st_size 	= size_cast(src).st_size; \
4435	(dest).st_atime = (src).st_atime;	\
4436	(dest).st_mtime = (src).st_mtime;	\
4437	(dest).st_ctime = (src).st_ctime;	\
4438    } while (0)
4439
4440static time_t filetime_to_unixtime(const FILETIME *ft);
4441
4442#undef fstat
4443/* License: Ruby's */
4444int
4445rb_w32_fstat(int fd, struct stat *st)
4446{
4447    BY_HANDLE_FILE_INFORMATION info;
4448    int ret = fstat(fd, st);
4449
4450    if (ret) return ret;
4451#ifdef __BORLANDC__
4452    st->st_mode &= ~(S_IWGRP | S_IWOTH);
4453#endif
4454    if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
4455#ifdef __BORLANDC__
4456	if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
4457	    st->st_mode |= S_IWUSR;
4458	}
4459#endif
4460	st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
4461	st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
4462	st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
4463    }
4464    return ret;
4465}
4466
4467/* License: Ruby's */
4468int
4469rb_w32_fstati64(int fd, struct stati64 *st)
4470{
4471    BY_HANDLE_FILE_INFORMATION info;
4472    struct stat tmp;
4473    int ret = fstat(fd, &tmp);
4474
4475    if (ret) return ret;
4476#ifdef __BORLANDC__
4477    tmp.st_mode &= ~(S_IWGRP | S_IWOTH);
4478#endif
4479    COPY_STAT(tmp, *st, +);
4480    if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
4481#ifdef __BORLANDC__
4482	if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
4483	    st->st_mode |= S_IWUSR;
4484	}
4485#endif
4486	st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
4487	st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
4488	st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
4489	st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
4490    }
4491    return ret;
4492}
4493
4494/* License: Ruby's */
4495static time_t
4496filetime_to_unixtime(const FILETIME *ft)
4497{
4498    struct timeval tv;
4499
4500    if (filetime_to_timeval(ft, &tv) == (time_t)-1)
4501	return 0;
4502    else
4503	return tv.tv_sec;
4504}
4505
4506/* License: Ruby's */
4507static unsigned
4508fileattr_to_unixmode(DWORD attr, const WCHAR *path)
4509{
4510    unsigned mode = 0;
4511
4512    if (attr & FILE_ATTRIBUTE_READONLY) {
4513	mode |= S_IREAD;
4514    }
4515    else {
4516	mode |= S_IREAD | S_IWRITE | S_IWUSR;
4517    }
4518
4519    if (attr & FILE_ATTRIBUTE_DIRECTORY) {
4520	mode |= S_IFDIR | S_IEXEC;
4521    }
4522    else {
4523	mode |= S_IFREG;
4524    }
4525
4526    if (path && (mode & S_IFREG)) {
4527	const WCHAR *end = path + lstrlenW(path);
4528	while (path < end) {
4529	    end = CharPrevW(path, end);
4530	    if (*end == L'.') {
4531		if ((_wcsicmp(end, L".bat") == 0) ||
4532		    (_wcsicmp(end, L".cmd") == 0) ||
4533		    (_wcsicmp(end, L".com") == 0) ||
4534		    (_wcsicmp(end, L".exe") == 0)) {
4535		    mode |= S_IEXEC;
4536		}
4537		break;
4538	    }
4539	}
4540    }
4541
4542    mode |= (mode & 0700) >> 3;
4543    mode |= (mode & 0700) >> 6;
4544
4545    return mode;
4546}
4547
4548/* License: Ruby's */
4549static int
4550check_valid_dir(const WCHAR *path)
4551{
4552    WIN32_FIND_DATAW fd;
4553    HANDLE fh;
4554    WCHAR full[MAX_PATH];
4555    WCHAR *dmy;
4556    WCHAR *p, *q;
4557
4558    /* GetFileAttributes() determines "..." as directory. */
4559    /* We recheck it by FindFirstFile(). */
4560    if (!(p = wcsstr(path, L"...")))
4561	return 0;
4562    q = p + wcsspn(p, L".");
4563    if ((p == path || wcschr(L":/\\", *(p - 1))) &&
4564	(!*q || wcschr(L":/\\", *q))) {
4565	errno = ENOENT;
4566	return -1;
4567    }
4568
4569    /* if the specified path is the root of a drive and the drive is empty, */
4570    /* FindFirstFile() returns INVALID_HANDLE_VALUE. */
4571    if (!GetFullPathNameW(path, sizeof(full) / sizeof(WCHAR), full, &dmy)) {
4572	errno = map_errno(GetLastError());
4573	return -1;
4574    }
4575    if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
4576	return 0;
4577
4578    fh = open_dir_handle(path, &fd);
4579    if (fh == INVALID_HANDLE_VALUE)
4580	return -1;
4581    FindClose(fh);
4582    return 0;
4583}
4584
4585/* License: Ruby's */
4586static int
4587winnt_stat(const WCHAR *path, struct stati64 *st)
4588{
4589    HANDLE h;
4590    WIN32_FIND_DATAW wfd;
4591    WIN32_FILE_ATTRIBUTE_DATA wfa;
4592    const WCHAR *p = path;
4593
4594    memset(st, 0, sizeof(*st));
4595    st->st_nlink = 1;
4596
4597    if (wcsncmp(p, L"\\\\?\\", 4) == 0) p += 4;
4598    if (wcspbrk(p, L"?*")) {
4599	errno = ENOENT;
4600	return -1;
4601    }
4602    if (GetFileAttributesExW(path, GetFileExInfoStandard, (void*)&wfa)) {
4603	if (wfa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
4604	    if (check_valid_dir(path)) return -1;
4605	    st->st_size = 0;
4606	}
4607	else {
4608	    st->st_size = ((__int64)wfa.nFileSizeHigh << 32) | wfa.nFileSizeLow;
4609	}
4610	st->st_mode  = fileattr_to_unixmode(wfa.dwFileAttributes, path);
4611	st->st_atime = filetime_to_unixtime(&wfa.ftLastAccessTime);
4612	st->st_mtime = filetime_to_unixtime(&wfa.ftLastWriteTime);
4613	st->st_ctime = filetime_to_unixtime(&wfa.ftCreationTime);
4614    }
4615    else {
4616	/* GetFileAttributesEx failed; check why. */
4617	int e = GetLastError();
4618
4619	if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME)
4620	    || (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) {
4621	    errno = map_errno(e);
4622	    return -1;
4623	}
4624
4625	/* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
4626	h = FindFirstFileW(path, &wfd);
4627	if (h != INVALID_HANDLE_VALUE) {
4628	    FindClose(h);
4629	    st->st_mode  = fileattr_to_unixmode(wfd.dwFileAttributes, path);
4630	    st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
4631	    st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
4632	    st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
4633	    st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
4634	}
4635	else {
4636	    errno = map_errno(GetLastError());
4637	    return -1;
4638	}
4639    }
4640
4641    st->st_dev = st->st_rdev = (iswalpha(path[0]) && path[1] == L':') ?
4642	towupper(path[0]) - L'A' : _getdrive() - 1;
4643
4644    return 0;
4645}
4646
4647/* License: Ruby's */
4648int
4649rb_w32_stat(const char *path, struct stat *st)
4650{
4651    struct stati64 tmp;
4652
4653    if (rb_w32_stati64(path, &tmp)) return -1;
4654    COPY_STAT(tmp, *st, (_off_t));
4655    return 0;
4656}
4657
4658/* License: Ruby's */
4659static int
4660wstati64(const WCHAR *path, struct stati64 *st)
4661{
4662    const WCHAR *p;
4663    WCHAR *buf1, *s, *end;
4664    int len, size;
4665    int ret;
4666    VALUE v;
4667
4668    if (!path || !st) {
4669	errno = EFAULT;
4670	return -1;
4671    }
4672    size = lstrlenW(path) + 2;
4673    buf1 = ALLOCV_N(WCHAR, v, size);
4674    for (p = path, s = buf1; *p; p++, s++) {
4675	if (*p == L'/')
4676	    *s = L'\\';
4677	else
4678	    *s = *p;
4679    }
4680    *s = '\0';
4681    len = s - buf1;
4682    if (!len || L'\"' == *(--s)) {
4683	errno = ENOENT;
4684	return -1;
4685    }
4686    end = buf1 + len - 1;
4687
4688    if (isUNCRoot(buf1)) {
4689	if (*end == L'.')
4690	    *end = L'\0';
4691	else if (*end != L'\\')
4692	    lstrcatW(buf1, L"\\");
4693    }
4694    else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
4695	lstrcatW(buf1, L".");
4696
4697    ret = winnt_stat(buf1, st);
4698    if (ret == 0) {
4699	st->st_mode &= ~(S_IWGRP | S_IWOTH);
4700    }
4701    if (v)
4702	ALLOCV_END(v);
4703
4704    return ret;
4705}
4706
4707/* License: Ruby's */
4708int
4709rb_w32_ustati64(const char *path, struct stati64 *st)
4710{
4711    WCHAR *wpath;
4712    int ret;
4713
4714    if (!(wpath = utf8_to_wstr(path, NULL)))
4715	return -1;
4716    ret = wstati64(wpath, st);
4717    free(wpath);
4718    return ret;
4719}
4720
4721/* License: Ruby's */
4722int
4723rb_w32_stati64(const char *path, struct stati64 *st)
4724{
4725    WCHAR *wpath;
4726    int ret;
4727
4728    if (!(wpath = filecp_to_wstr(path, NULL)))
4729	return -1;
4730    ret = wstati64(wpath, st);
4731    free(wpath);
4732    return ret;
4733}
4734
4735/* License: Ruby's */
4736int
4737rb_w32_access(const char *path, int mode)
4738{
4739    struct stati64 stat;
4740    if (rb_w32_stati64(path, &stat) != 0)
4741	return -1;
4742    mode <<= 6;
4743    if ((stat.st_mode & mode) != mode) {
4744	errno = EACCES;
4745	return -1;
4746    }
4747    return 0;
4748}
4749
4750/* License: Ruby's */
4751int
4752rb_w32_uaccess(const char *path, int mode)
4753{
4754    struct stati64 stat;
4755    if (rb_w32_ustati64(path, &stat) != 0)
4756	return -1;
4757    mode <<= 6;
4758    if ((stat.st_mode & mode) != mode) {
4759	errno = EACCES;
4760	return -1;
4761    }
4762    return 0;
4763}
4764
4765/* License: Ruby's */
4766static int
4767rb_chsize(HANDLE h, off_t size)
4768{
4769    long upos, lpos, usize, lsize;
4770    int ret = -1;
4771    DWORD e;
4772
4773    if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
4774	(e = GetLastError())) {
4775	errno = map_errno(e);
4776	return -1;
4777    }
4778    usize = (long)(size >> 32);
4779    lsize = (long)size;
4780    if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
4781	(e = GetLastError())) {
4782	errno = map_errno(e);
4783    }
4784    else if (!SetEndOfFile(h)) {
4785	errno = map_errno(GetLastError());
4786    }
4787    else {
4788	ret = 0;
4789    }
4790    SetFilePointer(h, lpos, &upos, SEEK_SET);
4791    return ret;
4792}
4793
4794/* License: Ruby's */
4795int
4796rb_w32_truncate(const char *path, off_t length)
4797{
4798    HANDLE h;
4799    int ret;
4800    h = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
4801    if (h == INVALID_HANDLE_VALUE) {
4802	errno = map_errno(GetLastError());
4803	return -1;
4804    }
4805    ret = rb_chsize(h, length);
4806    CloseHandle(h);
4807    return ret;
4808}
4809
4810/* License: Ruby's */
4811int
4812rb_w32_ftruncate(int fd, off_t length)
4813{
4814    HANDLE h;
4815
4816    h = (HANDLE)_get_osfhandle(fd);
4817    if (h == (HANDLE)-1) return -1;
4818    return rb_chsize(h, length);
4819}
4820
4821#ifdef __BORLANDC__
4822/* License: Ruby's */
4823off_t
4824_filelengthi64(int fd)
4825{
4826    DWORD u, l;
4827    int e;
4828
4829    l = GetFileSize((HANDLE)_get_osfhandle(fd), &u);
4830    if (l == (DWORD)-1L && (e = GetLastError())) {
4831	errno = map_errno(e);
4832	return (off_t)-1;
4833    }
4834    return ((off_t)u << 32) | l;
4835}
4836
4837/* License: Ruby's */
4838off_t
4839_lseeki64(int fd, off_t offset, int whence)
4840{
4841    long u, l;
4842    int e;
4843    HANDLE h = (HANDLE)_get_osfhandle(fd);
4844
4845    if (!h) {
4846	errno = EBADF;
4847	return -1;
4848    }
4849    u = (long)(offset >> 32);
4850    if ((l = SetFilePointer(h, (long)offset, &u, whence)) == -1L &&
4851	(e = GetLastError())) {
4852	errno = map_errno(e);
4853	return -1;
4854    }
4855    return ((off_t)u << 32) | l;
4856}
4857#endif
4858
4859/* License: Ruby's */
4860int
4861fseeko(FILE *stream, off_t offset, int whence)
4862{
4863    off_t pos;
4864    switch (whence) {
4865      case SEEK_CUR:
4866	if (fgetpos(stream, (fpos_t *)&pos))
4867	    return -1;
4868	pos += offset;
4869	break;
4870      case SEEK_END:
4871	if ((pos = _filelengthi64(fileno(stream))) == (off_t)-1)
4872	    return -1;
4873	pos += offset;
4874	break;
4875      default:
4876	pos = offset;
4877	break;
4878    }
4879    return fsetpos(stream, (fpos_t *)&pos);
4880}
4881
4882/* License: Ruby's */
4883off_t
4884rb_w32_ftello(FILE *stream)
4885{
4886    off_t pos;
4887    if (fgetpos(stream, (fpos_t *)&pos)) return (off_t)-1;
4888    return pos;
4889}
4890
4891/* License: Ruby's */
4892static long
4893filetime_to_clock(FILETIME *ft)
4894{
4895    __int64 qw = ft->dwHighDateTime;
4896    qw <<= 32;
4897    qw |= ft->dwLowDateTime;
4898    qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
4899    return (long) qw;
4900}
4901
4902/* License: Ruby's */
4903int
4904rb_w32_times(struct tms *tmbuf)
4905{
4906    FILETIME create, exit, kernel, user;
4907
4908    if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
4909	tmbuf->tms_utime = filetime_to_clock(&user);
4910	tmbuf->tms_stime = filetime_to_clock(&kernel);
4911	tmbuf->tms_cutime = 0;
4912	tmbuf->tms_cstime = 0;
4913    }
4914    else {
4915	tmbuf->tms_utime = clock();
4916	tmbuf->tms_stime = 0;
4917	tmbuf->tms_cutime = 0;
4918	tmbuf->tms_cstime = 0;
4919    }
4920    return 0;
4921}
4922
4923#define yield_once() Sleep(0)
4924#define yield_until(condition) do yield_once(); while (!(condition))
4925
4926/* License: Ruby's */
4927static void
4928catch_interrupt(void)
4929{
4930    yield_once();
4931    RUBY_CRITICAL(rb_w32_wait_events(NULL, 0, 0));
4932}
4933
4934#if defined __BORLANDC__
4935#undef read
4936/* License: Ruby's */
4937int
4938read(int fd, void *buf, size_t size)
4939{
4940    int ret = _read(fd, buf, size);
4941    if ((ret < 0) && (errno == EPIPE)) {
4942	errno = 0;
4943	ret = 0;
4944    }
4945    catch_interrupt();
4946    return ret;
4947}
4948#endif
4949
4950
4951#define FILE_COUNT _cnt
4952#define FILE_READPTR _ptr
4953
4954#undef fgetc
4955/* License: Ruby's */
4956int
4957rb_w32_getc(FILE* stream)
4958{
4959    int c;
4960    if (enough_to_get(stream->FILE_COUNT)) {
4961	c = (unsigned char)*stream->FILE_READPTR++;
4962    }
4963    else {
4964	c = _filbuf(stream);
4965#if defined __BORLANDC__
4966        if ((c == EOF) && (errno == EPIPE)) {
4967	    clearerr(stream);
4968        }
4969#endif
4970	catch_interrupt();
4971    }
4972    return c;
4973}
4974
4975#undef fputc
4976/* License: Ruby's */
4977int
4978rb_w32_putc(int c, FILE* stream)
4979{
4980    if (enough_to_put(stream->FILE_COUNT)) {
4981	c = (unsigned char)(*stream->FILE_READPTR++ = (char)c);
4982    }
4983    else {
4984	c = _flsbuf(c, stream);
4985	catch_interrupt();
4986    }
4987    return c;
4988}
4989
4990/* License: Ruby's */
4991struct asynchronous_arg_t {
4992    /* output field */
4993    void* stackaddr;
4994    int errnum;
4995
4996    /* input field */
4997    uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
4998    uintptr_t self;
4999    int argc;
5000    uintptr_t* argv;
5001};
5002
5003/* License: Ruby's */
5004static DWORD WINAPI
5005call_asynchronous(PVOID argp)
5006{
5007    DWORD ret;
5008    struct asynchronous_arg_t *arg = argp;
5009    arg->stackaddr = &argp;
5010    ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
5011    arg->errnum = errno;
5012    return ret;
5013}
5014
5015/* License: Ruby's */
5016uintptr_t
5017rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
5018		    int argc, uintptr_t* argv, uintptr_t intrval)
5019{
5020    DWORD val;
5021    BOOL interrupted = FALSE;
5022    HANDLE thr;
5023
5024    RUBY_CRITICAL({
5025	struct asynchronous_arg_t arg;
5026
5027	arg.stackaddr = NULL;
5028	arg.errnum = 0;
5029	arg.func = func;
5030	arg.self = self;
5031	arg.argc = argc;
5032	arg.argv = argv;
5033
5034	thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
5035
5036	if (thr) {
5037	    yield_until(arg.stackaddr);
5038
5039	    if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
5040		interrupted = TRUE;
5041
5042		if (TerminateThread(thr, intrval)) {
5043		    yield_once();
5044		}
5045	    }
5046
5047	    GetExitCodeThread(thr, &val);
5048	    CloseHandle(thr);
5049
5050	    if (interrupted) {
5051		/* must release stack of killed thread, why doesn't Windows? */
5052		MEMORY_BASIC_INFORMATION m;
5053
5054		memset(&m, 0, sizeof(m));
5055		if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
5056		    Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
5057				  arg.stackaddr, GetLastError()));
5058		}
5059		else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
5060		    Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
5061				  m.AllocationBase, GetLastError()));
5062		}
5063		errno = EINTR;
5064	    }
5065	    else {
5066		errno = arg.errnum;
5067	    }
5068	}
5069    });
5070
5071    if (!thr) {
5072	rb_fatal("failed to launch waiter thread:%ld", GetLastError());
5073    }
5074
5075    return val;
5076}
5077
5078/* License: Ruby's */
5079char **
5080rb_w32_get_environ(void)
5081{
5082    WCHAR *envtop, *env;
5083    char **myenvtop, **myenv;
5084    int num;
5085
5086    /*
5087     * We avoid values started with `='. If you want to deal those values,
5088     * change this function, and some functions in hash.c which recognize
5089     * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
5090     * CygWin deals these values by changing first `=' to '!'. But we don't
5091     * use such trick and follow cmd.exe's way that just doesn't show these
5092     * values.
5093     *
5094     * This function returns UTF-8 strings.
5095     */
5096    envtop = GetEnvironmentStringsW();
5097    for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
5098	if (*env != '=') num++;
5099
5100    myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
5101    for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
5102	if (*env != '=') {
5103	    if (!(*myenv = wstr_to_utf8(env, NULL))) {
5104		break;
5105	    }
5106	    myenv++;
5107	}
5108    }
5109    *myenv = NULL;
5110    FreeEnvironmentStringsW(envtop);
5111
5112    return myenvtop;
5113}
5114
5115/* License: Ruby's */
5116void
5117rb_w32_free_environ(char **env)
5118{
5119    char **t = env;
5120
5121    while (*t) free(*t++);
5122    free(env);
5123}
5124
5125/* License: Ruby's */
5126rb_pid_t
5127rb_w32_getpid(void)
5128{
5129    return GetCurrentProcessId();
5130}
5131
5132
5133/* License: Ruby's */
5134rb_pid_t
5135rb_w32_getppid(void)
5136{
5137    typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
5138    static query_func *pNtQueryInformationProcess = NULL;
5139    rb_pid_t ppid = 0;
5140
5141    if (rb_w32_osver() >= 5) {
5142	if (!pNtQueryInformationProcess)
5143	    pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
5144	if (pNtQueryInformationProcess) {
5145	    struct {
5146		long ExitStatus;
5147		void* PebBaseAddress;
5148		uintptr_t AffinityMask;
5149		uintptr_t BasePriority;
5150		uintptr_t UniqueProcessId;
5151		uintptr_t ParentProcessId;
5152	    } pbi;
5153	    ULONG len;
5154	    long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
5155	    if (!ret) {
5156		ppid = pbi.ParentProcessId;
5157	    }
5158	}
5159    }
5160
5161    return ppid;
5162}
5163
5164/* License: Ruby's */
5165int
5166rb_w32_uopen(const char *file, int oflag, ...)
5167{
5168    WCHAR *wfile;
5169    int ret;
5170    int pmode;
5171
5172    va_list arg;
5173    va_start(arg, oflag);
5174    pmode = va_arg(arg, int);
5175    va_end(arg);
5176
5177    if (!(wfile = utf8_to_wstr(file, NULL)))
5178	return -1;
5179    ret = rb_w32_wopen(wfile, oflag, pmode);
5180    free(wfile);
5181    return ret;
5182}
5183
5184/* License: Ruby's */
5185static int
5186check_if_wdir(const WCHAR *wfile)
5187{
5188    DWORD attr = GetFileAttributesW(wfile);
5189    if (attr == (DWORD)-1L ||
5190	!(attr & FILE_ATTRIBUTE_DIRECTORY) ||
5191	check_valid_dir(wfile)) {
5192	return FALSE;
5193    }
5194    errno = EISDIR;
5195    return TRUE;
5196}
5197
5198/* License: Ruby's */
5199static int
5200check_if_dir(const char *file)
5201{
5202    WCHAR *wfile;
5203    int ret;
5204
5205    if (!(wfile = filecp_to_wstr(file, NULL)))
5206	return FALSE;
5207    ret = check_if_wdir(wfile);
5208    free(wfile);
5209    return ret;
5210}
5211
5212/* License: Ruby's */
5213int
5214rb_w32_open(const char *file, int oflag, ...)
5215{
5216    WCHAR *wfile;
5217    int ret;
5218    int pmode;
5219
5220    va_list arg;
5221    va_start(arg, oflag);
5222    pmode = va_arg(arg, int);
5223    va_end(arg);
5224
5225    if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
5226	ret = _open(file, oflag, pmode);
5227	if (ret == -1 && errno == EACCES) check_if_dir(file);
5228	return ret;
5229    }
5230
5231    if (!(wfile = filecp_to_wstr(file, NULL)))
5232	return -1;
5233    ret = rb_w32_wopen(wfile, oflag, pmode);
5234    free(wfile);
5235    return ret;
5236}
5237
5238int
5239rb_w32_wopen(const WCHAR *file, int oflag, ...)
5240{
5241    char flags = 0;
5242    int fd;
5243    DWORD access;
5244    DWORD create;
5245    DWORD attr = FILE_ATTRIBUTE_NORMAL;
5246    SECURITY_ATTRIBUTES sec;
5247    HANDLE h;
5248
5249    if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
5250	va_list arg;
5251	int pmode;
5252	va_start(arg, oflag);
5253	pmode = va_arg(arg, int);
5254	va_end(arg);
5255	fd = _wopen(file, oflag, pmode);
5256	if (fd == -1 && errno == EACCES) check_if_wdir(file);
5257	return fd;
5258    }
5259
5260    sec.nLength = sizeof(sec);
5261    sec.lpSecurityDescriptor = NULL;
5262    if (oflag & O_NOINHERIT) {
5263	sec.bInheritHandle = FALSE;
5264	flags |= FNOINHERIT;
5265    }
5266    else {
5267	sec.bInheritHandle = TRUE;
5268    }
5269    oflag &= ~O_NOINHERIT;
5270
5271    /* always open with binary mode */
5272    oflag &= ~(O_BINARY | O_TEXT);
5273
5274    switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
5275      case O_RDWR:
5276	access = GENERIC_READ | GENERIC_WRITE;
5277	break;
5278      case O_RDONLY:
5279	access = GENERIC_READ;
5280	break;
5281      case O_WRONLY:
5282	access = GENERIC_WRITE;
5283	break;
5284      default:
5285	errno = EINVAL;
5286	return -1;
5287    }
5288    oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
5289
5290    switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
5291      case O_CREAT:
5292	create = OPEN_ALWAYS;
5293	break;
5294      case 0:
5295      case O_EXCL:
5296	create = OPEN_EXISTING;
5297	break;
5298      case O_CREAT | O_EXCL:
5299      case O_CREAT | O_EXCL | O_TRUNC:
5300	create = CREATE_NEW;
5301	break;
5302      case O_TRUNC:
5303      case O_TRUNC | O_EXCL:
5304	create = TRUNCATE_EXISTING;
5305	break;
5306      case O_CREAT | O_TRUNC:
5307	create = CREATE_ALWAYS;
5308	break;
5309      default:
5310	errno = EINVAL;
5311	return -1;
5312    }
5313    if (oflag & O_CREAT) {
5314	va_list arg;
5315	int pmode;
5316	va_start(arg, oflag);
5317	pmode = va_arg(arg, int);
5318	va_end(arg);
5319	/* TODO: we need to check umask here, but it's not exported... */
5320	if (!(pmode & S_IWRITE))
5321	    attr = FILE_ATTRIBUTE_READONLY;
5322    }
5323    oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
5324
5325    if (oflag & O_TEMPORARY) {
5326	attr |= FILE_FLAG_DELETE_ON_CLOSE;
5327	access |= DELETE;
5328    }
5329    oflag &= ~O_TEMPORARY;
5330
5331    if (oflag & _O_SHORT_LIVED)
5332	attr |= FILE_ATTRIBUTE_TEMPORARY;
5333    oflag &= ~_O_SHORT_LIVED;
5334
5335    switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
5336      case 0:
5337	break;
5338      case O_SEQUENTIAL:
5339	attr |= FILE_FLAG_SEQUENTIAL_SCAN;
5340	break;
5341      case O_RANDOM:
5342	attr |= FILE_FLAG_RANDOM_ACCESS;
5343	break;
5344      default:
5345	errno = EINVAL;
5346	return -1;
5347    }
5348    oflag &= ~(O_SEQUENTIAL | O_RANDOM);
5349
5350    if (oflag & ~O_APPEND) {
5351	errno = EINVAL;
5352	return -1;
5353    }
5354
5355    /* allocate a C Runtime file handle */
5356    RUBY_CRITICAL({
5357	h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5358	fd = _open_osfhandle((intptr_t)h, 0);
5359	CloseHandle(h);
5360    });
5361    if (fd == -1) {
5362	errno = EMFILE;
5363	return -1;
5364    }
5365    RUBY_CRITICAL({
5366	MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
5367	_set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
5368	_set_osflags(fd, 0);
5369
5370	h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec,
5371			create, attr, NULL);
5372	if (h == INVALID_HANDLE_VALUE) {
5373	    DWORD e = GetLastError();
5374	    if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
5375		errno = map_errno(e);
5376	    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5377	    fd = -1;
5378	    goto quit;
5379	}
5380
5381	switch (GetFileType(h)) {
5382	  case FILE_TYPE_CHAR:
5383	    flags |= FDEV;
5384	    break;
5385	  case FILE_TYPE_PIPE:
5386	    flags |= FPIPE;
5387	    break;
5388	  case FILE_TYPE_UNKNOWN:
5389	    errno = map_errno(GetLastError());
5390	    CloseHandle(h);
5391	    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5392	    fd = -1;
5393	    goto quit;
5394	}
5395	if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
5396	    flags |= FAPPEND;
5397
5398	_set_osfhnd(fd, (intptr_t)h);
5399	_osfile(fd) = flags | FOPEN;
5400
5401	MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5402      quit:
5403	;
5404    });
5405
5406    return fd;
5407}
5408
5409/* License: Ruby's */
5410int
5411rb_w32_fclose(FILE *fp)
5412{
5413    int fd = fileno(fp);
5414    SOCKET sock = TO_SOCKET(fd);
5415    int save_errno = errno;
5416
5417    if (fflush(fp)) return -1;
5418    if (!is_socket(sock)) {
5419	UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
5420	return fclose(fp);
5421    }
5422    _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
5423    fclose(fp);
5424    errno = save_errno;
5425    if (closesocket(sock) == SOCKET_ERROR) {
5426	errno = map_errno(WSAGetLastError());
5427	return -1;
5428    }
5429    return 0;
5430}
5431
5432/* License: Ruby's */
5433int
5434rb_w32_pipe(int fds[2])
5435{
5436    static DWORD serial = 0;
5437    char name[] = "\\\\.\\pipe\\ruby0000000000000000-0000000000000000";
5438    char *p;
5439    SECURITY_ATTRIBUTES sec;
5440    HANDLE hRead, hWrite, h;
5441    int fdRead, fdWrite;
5442    int ret;
5443
5444    /* if doesn't have CancelIo, use default pipe function */
5445    if (!cancel_io)
5446	return _pipe(fds, 65536L, _O_NOINHERIT);
5447
5448    p = strchr(name, '0');
5449    snprintf(p, strlen(p) + 1, "%"PRI_PIDT_PREFIX"x-%lx", rb_w32_getpid(), serial++);
5450
5451    sec.nLength = sizeof(sec);
5452    sec.lpSecurityDescriptor = NULL;
5453    sec.bInheritHandle = FALSE;
5454
5455    RUBY_CRITICAL({
5456	hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
5457				0, 2, 65536, 65536, 0, &sec);
5458    });
5459    if (hRead == INVALID_HANDLE_VALUE) {
5460	DWORD err = GetLastError();
5461	if (err == ERROR_PIPE_BUSY)
5462	    errno = EMFILE;
5463	else
5464	    errno = map_errno(GetLastError());
5465	return -1;
5466    }
5467
5468    RUBY_CRITICAL({
5469	hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
5470			    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
5471    });
5472    if (hWrite == INVALID_HANDLE_VALUE) {
5473	errno = map_errno(GetLastError());
5474	CloseHandle(hRead);
5475	return -1;
5476    }
5477
5478    RUBY_CRITICAL(do {
5479	ret = 0;
5480	h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5481	fdRead = _open_osfhandle((intptr_t)h, 0);
5482	CloseHandle(h);
5483	if (fdRead == -1) {
5484	    errno = EMFILE;
5485	    CloseHandle(hWrite);
5486	    CloseHandle(hRead);
5487	    ret = -1;
5488	    break;
5489	}
5490
5491	MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdRead)->lock)));
5492	_set_osfhnd(fdRead, (intptr_t)hRead);
5493	_set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
5494	MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdRead)->lock)));
5495    } while (0));
5496    if (ret)
5497	return ret;
5498
5499    RUBY_CRITICAL(do {
5500	h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5501	fdWrite = _open_osfhandle((intptr_t)h, 0);
5502	CloseHandle(h);
5503	if (fdWrite == -1) {
5504	    errno = EMFILE;
5505	    CloseHandle(hWrite);
5506	    ret = -1;
5507	    break;
5508	}
5509	MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdWrite)->lock)));
5510	_set_osfhnd(fdWrite, (intptr_t)hWrite);
5511	_set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
5512	MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdWrite)->lock)));
5513    } while (0));
5514    if (ret) {
5515	rb_w32_close(fdRead);
5516	return ret;
5517    }
5518
5519    fds[0] = fdRead;
5520    fds[1] = fdWrite;
5521
5522    return 0;
5523}
5524
5525/* License: Ruby's */
5526static struct constat *
5527constat_handle(HANDLE h)
5528{
5529    st_data_t data;
5530    struct constat *p;
5531    if (!conlist) {
5532	conlist = st_init_numtable();
5533    }
5534    if (st_lookup(conlist, (st_data_t)h, &data)) {
5535	p = (struct constat *)data;
5536    }
5537    else {
5538	CONSOLE_SCREEN_BUFFER_INFO csbi;
5539	p = ALLOC(struct constat);
5540	p->vt100.state = constat_init;
5541	p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
5542	p->vt100.saved.X = p->vt100.saved.Y = 0;
5543	if (GetConsoleScreenBufferInfo(h, &csbi)) {
5544	    p->vt100.attr = csbi.wAttributes;
5545	}
5546	st_insert(conlist, (st_data_t)h, (st_data_t)p);
5547    }
5548    return p;
5549}
5550
5551/* License: Ruby's */
5552static void
5553constat_reset(HANDLE h)
5554{
5555    st_data_t data;
5556    struct constat *p;
5557    if (!conlist) return;
5558    if (!st_lookup(conlist, (st_data_t)h, &data)) return;
5559    p = (struct constat *)data;
5560    p->vt100.state = constat_init;
5561}
5562
5563/* License: Ruby's */
5564static DWORD
5565constat_attr(int count, const int *seq, DWORD attr, DWORD default_attr)
5566{
5567#define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
5568#define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED)
5569    DWORD bold = attr & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
5570    int rev = 0;
5571
5572    if (!count) return attr;
5573    while (count-- > 0) {
5574	switch (*seq++) {
5575	  case 0:
5576	    attr = default_attr;
5577	    rev = 0;
5578	    bold = 0;
5579	    break;
5580	  case 1:
5581	    bold |= rev ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
5582	    break;
5583	  case 4:
5584#ifndef COMMON_LVB_UNDERSCORE
5585#define COMMON_LVB_UNDERSCORE 0x8000
5586#endif
5587	    attr |= COMMON_LVB_UNDERSCORE;
5588	    break;
5589	  case 7:
5590	    rev = 1;
5591	    break;
5592
5593	  case 30:
5594	    attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
5595	    break;
5596	  case 17:
5597	  case 31:
5598	    attr = attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN) | FOREGROUND_RED;
5599	    break;
5600	  case 18:
5601	  case 32:
5602	    attr = attr & ~(FOREGROUND_BLUE | FOREGROUND_RED) | FOREGROUND_GREEN;
5603	    break;
5604	  case 19:
5605	  case 33:
5606	    attr = attr & ~FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
5607	    break;
5608	  case 20:
5609	  case 34:
5610	    attr = attr & ~(FOREGROUND_GREEN | FOREGROUND_RED) | FOREGROUND_BLUE;
5611	    break;
5612	  case 21:
5613	  case 35:
5614	    attr = attr & ~FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
5615	    break;
5616	  case 22:
5617	  case 36:
5618	    attr = attr & ~FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
5619	    break;
5620	  case 23:
5621	  case 37:
5622	    attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
5623	    break;
5624
5625	  case 40:
5626	    attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
5627	    break;
5628	  case 41:
5629	    attr = attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN) | BACKGROUND_RED;
5630	    break;
5631	  case 42:
5632	    attr = attr & ~(BACKGROUND_BLUE | BACKGROUND_RED) | BACKGROUND_GREEN;
5633	    break;
5634	  case 43:
5635	    attr = attr & ~BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
5636	    break;
5637	  case 44:
5638	    attr = attr & ~(BACKGROUND_GREEN | BACKGROUND_RED) | BACKGROUND_BLUE;
5639	    break;
5640	  case 45:
5641	    attr = attr & ~BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
5642	    break;
5643	  case 46:
5644	    attr = attr & ~BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN;
5645	    break;
5646	  case 47:
5647	    attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
5648	    break;
5649	}
5650    }
5651    if (rev) {
5652	attr = attr & ~(FOREGROUND_MASK | BACKGROUND_MASK) |
5653	    ((attr & FOREGROUND_MASK) << 4) |
5654	    ((attr & BACKGROUND_MASK) >> 4);
5655    }
5656    return attr | bold;
5657}
5658
5659/* License: Ruby's */
5660static void
5661constat_apply(HANDLE handle, struct constat *s, WCHAR w)
5662{
5663    CONSOLE_SCREEN_BUFFER_INFO csbi;
5664    const int *seq = s->vt100.seq;
5665    int count = s->vt100.state;
5666    int arg1 = 1;
5667    COORD pos;
5668    DWORD written;
5669
5670    if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
5671    if (count > 0 && seq[0] > 0) arg1 = seq[0];
5672    switch (w) {
5673      case L'm':
5674	SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr));
5675	break;
5676      case L'F':
5677	csbi.dwCursorPosition.X = 0;
5678      case L'A':
5679	csbi.dwCursorPosition.Y -= arg1;
5680	if (csbi.dwCursorPosition.Y < 0)
5681	    csbi.dwCursorPosition.Y = 0;
5682	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5683	break;
5684      case L'E':
5685	csbi.dwCursorPosition.X = 0;
5686      case L'B':
5687      case L'e':
5688	csbi.dwCursorPosition.Y += arg1;
5689	if (csbi.dwCursorPosition.Y >= csbi.dwSize.Y)
5690	    csbi.dwCursorPosition.Y = csbi.dwSize.Y;
5691	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5692	break;
5693      case L'C':
5694	csbi.dwCursorPosition.X += arg1;
5695	if (csbi.dwCursorPosition.X >= csbi.dwSize.X)
5696	    csbi.dwCursorPosition.X = csbi.dwSize.X;
5697	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5698	break;
5699      case L'D':
5700	csbi.dwCursorPosition.X -= arg1;
5701	if (csbi.dwCursorPosition.X < 0)
5702	    csbi.dwCursorPosition.X = 0;
5703	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5704	break;
5705      case L'G':
5706      case L'`':
5707	csbi.dwCursorPosition.X = (arg1 > csbi.dwSize.X ? csbi.dwSize.X : arg1) - 1;
5708	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5709	break;
5710      case L'd':
5711	csbi.dwCursorPosition.Y = (arg1 > csbi.dwSize.Y ? csbi.dwSize.Y : arg1) - 1;
5712	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5713	break;
5714      case L'H':
5715      case L'f':
5716	pos.Y = (arg1 > csbi.dwSize.Y ? csbi.dwSize.Y : arg1) - 1;
5717	if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
5718	pos.X = (arg1 > csbi.dwSize.X ? csbi.dwSize.X : arg1) - 1;
5719	SetConsoleCursorPosition(handle, pos);
5720	break;
5721      case L'J':
5722	switch (arg1) {
5723	  case 0:	/* erase after cursor */
5724	    FillConsoleOutputCharacterW(handle, L' ',
5725					csbi.dwSize.X * (csbi.dwSize.Y - csbi.dwCursorPosition.Y) - csbi.dwCursorPosition.X,
5726					csbi.dwCursorPosition, &written);
5727	    break;
5728	  case 1:	/* erase before cursor */
5729	    pos.X = 0;
5730	    pos.Y = csbi.dwCursorPosition.Y;
5731	    FillConsoleOutputCharacterW(handle, L' ',
5732					csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X,
5733					pos, &written);
5734	    break;
5735	  case 2:	/* erase entire line */
5736	    pos.X = 0;
5737	    pos.Y = 0;
5738	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X * csbi.dwSize.Y, pos, &written);
5739	    break;
5740	}
5741	break;
5742      case L'K':
5743	switch (arg1) {
5744	  case 0:	/* erase after cursor */
5745	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X - csbi.dwCursorPosition.X, csbi.dwCursorPosition, &written);
5746	    break;
5747	  case 1:	/* erase before cursor */
5748	    pos.X = 0;
5749	    pos.Y = csbi.dwCursorPosition.Y;
5750	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwCursorPosition.X, pos, &written);
5751	    break;
5752	  case 2:	/* erase entire line */
5753	    pos.X = 0;
5754	    pos.Y = csbi.dwCursorPosition.Y;
5755	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X, pos, &written);
5756	    break;
5757	}
5758	break;
5759      case L's':
5760	s->vt100.saved = csbi.dwCursorPosition;
5761	break;
5762      case L'u':
5763	SetConsoleCursorPosition(handle, s->vt100.saved);
5764	break;
5765      case L'h':
5766	if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
5767	    CONSOLE_CURSOR_INFO cci;
5768	    GetConsoleCursorInfo(handle, &cci);
5769	    cci.bVisible = TRUE;
5770	    SetConsoleCursorInfo(handle, &cci);
5771	}
5772	break;
5773      case L'l':
5774	if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
5775	    CONSOLE_CURSOR_INFO cci;
5776	    GetConsoleCursorInfo(handle, &cci);
5777	    cci.bVisible = FALSE;
5778	    SetConsoleCursorInfo(handle, &cci);
5779	}
5780	break;
5781    }
5782}
5783
5784/* License: Ruby's */
5785static long
5786constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
5787{
5788    const WCHAR *ptr = *ptrp;
5789    long rest, len = *lenp;
5790    while (len-- > 0) {
5791	WCHAR wc = *ptr++;
5792	if (wc == 0x1b) {
5793	    rest = *lenp - len - 1;
5794	    s->vt100.state = constat_esc;
5795	}
5796	else if (s->vt100.state == constat_esc && wc == L'[') {
5797	    rest = *lenp - len - 1;
5798	    if (rest > 0) --rest;
5799	    s->vt100.state = constat_seq;
5800	    s->vt100.seq[0] = 0;
5801	}
5802	else if (s->vt100.state >= constat_seq) {
5803	    if (wc >= L'0' && wc <= L'9') {
5804		if (s->vt100.state < (int)numberof(s->vt100.seq)) {
5805		    int *seq = &s->vt100.seq[s->vt100.state];
5806		    *seq = (*seq * 10) + (wc - L'0');
5807		}
5808	    }
5809	    else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
5810		s->vt100.seq[s->vt100.state++] = -1;
5811	    }
5812	    else {
5813		do {
5814		    if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
5815			s->vt100.seq[s->vt100.state] = 0;
5816		    }
5817		    else {
5818			s->vt100.state = (int)numberof(s->vt100.seq);
5819		    }
5820		} while (0);
5821		if (wc != L';') {
5822		    constat_apply(h, s, wc);
5823		    s->vt100.state = constat_init;
5824		}
5825	    }
5826	    rest = 0;
5827	}
5828	else {
5829	    continue;
5830	}
5831	*ptrp = ptr;
5832	*lenp = len;
5833	return rest;
5834    }
5835    len = *lenp;
5836    *ptrp = ptr;
5837    *lenp = 0;
5838    return len;
5839}
5840
5841
5842/* License: Ruby's */
5843int
5844rb_w32_close(int fd)
5845{
5846    SOCKET sock = TO_SOCKET(fd);
5847    int save_errno = errno;
5848
5849    if (!is_socket(sock)) {
5850	UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
5851	constat_delete((HANDLE)sock);
5852	return _close(fd);
5853    }
5854    _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
5855    socklist_delete(&sock, NULL);
5856    _close(fd);
5857    errno = save_errno;
5858    if (closesocket(sock) == SOCKET_ERROR) {
5859	errno = map_errno(WSAGetLastError());
5860	return -1;
5861    }
5862    return 0;
5863}
5864
5865static int
5866setup_overlapped(OVERLAPPED *ol, int fd)
5867{
5868    memset(ol, 0, sizeof(*ol));
5869    if (!(_osfile(fd) & (FDEV | FPIPE))) {
5870	LONG high = 0;
5871	DWORD method = _osfile(fd) & FAPPEND ? FILE_END : FILE_CURRENT;
5872	DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method);
5873#ifndef INVALID_SET_FILE_POINTER
5874#define INVALID_SET_FILE_POINTER ((DWORD)-1)
5875#endif
5876	if (low == INVALID_SET_FILE_POINTER) {
5877	    DWORD err = GetLastError();
5878	    if (err != NO_ERROR) {
5879		errno = map_errno(err);
5880		return -1;
5881	    }
5882	}
5883	ol->Offset = low;
5884	ol->OffsetHigh = high;
5885    }
5886    ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
5887    if (!ol->hEvent) {
5888	errno = map_errno(GetLastError());
5889	return -1;
5890    }
5891    return 0;
5892}
5893
5894static void
5895finish_overlapped(OVERLAPPED *ol, int fd, DWORD size)
5896{
5897    CloseHandle(ol->hEvent);
5898
5899    if (!(_osfile(fd) & (FDEV | FPIPE))) {
5900	LONG high = ol->OffsetHigh;
5901	DWORD low = ol->Offset + size;
5902	if (low < ol->Offset)
5903	    ++high;
5904	SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
5905    }
5906}
5907
5908#undef read
5909/* License: Ruby's */
5910ssize_t
5911rb_w32_read(int fd, void *buf, size_t size)
5912{
5913    SOCKET sock = TO_SOCKET(fd);
5914    DWORD read;
5915    DWORD wait;
5916    DWORD err;
5917    size_t len;
5918    size_t ret;
5919    OVERLAPPED ol, *pol = NULL;
5920    BOOL isconsole;
5921    BOOL islineinput = FALSE;
5922    int start = 0;
5923
5924    if (is_socket(sock))
5925	return rb_w32_recv(fd, buf, size, 0);
5926
5927    // validate fd by using _get_osfhandle() because we cannot access _nhandle
5928    if (_get_osfhandle(fd) == -1) {
5929	return -1;
5930    }
5931
5932    if (_osfile(fd) & FTEXT) {
5933	return _read(fd, buf, size);
5934    }
5935
5936    MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
5937
5938    if (!size || _osfile(fd) & FEOFLAG) {
5939	_set_osflags(fd, _osfile(fd) & ~FEOFLAG);
5940	MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5941	return 0;
5942    }
5943
5944    ret = 0;
5945    isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
5946    if (isconsole) {
5947	DWORD mode;
5948	GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
5949	islineinput = (mode & ENABLE_LINE_INPUT) != 0;
5950    }
5951  retry:
5952    /* get rid of console reading bug */
5953    if (isconsole) {
5954	constat_reset((HANDLE)_osfhnd(fd));
5955	if (start)
5956	    len = 1;
5957	else {
5958	    len = 0;
5959	    start = 1;
5960	}
5961    }
5962    else
5963	len = size;
5964    size -= len;
5965
5966    /* if have cancel_io, use Overlapped I/O */
5967    if (cancel_io) {
5968	if (setup_overlapped(&ol, fd)) {
5969	    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5970	    return -1;
5971	}
5972
5973	pol = &ol;
5974    }
5975
5976    if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, pol)) {
5977	err = GetLastError();
5978	if (err != ERROR_IO_PENDING) {
5979	    if (pol) CloseHandle(ol.hEvent);
5980	    if (err == ERROR_ACCESS_DENIED)
5981		errno = EBADF;
5982	    else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
5983		MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5984		return 0;
5985	    }
5986	    else
5987		errno = map_errno(err);
5988
5989	    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5990	    return -1;
5991	}
5992
5993	if (pol) {
5994	    wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
5995	    if (wait != WAIT_OBJECT_0) {
5996		if (wait == WAIT_OBJECT_0 + 1)
5997		    errno = EINTR;
5998		else
5999		    errno = map_errno(GetLastError());
6000		CloseHandle(ol.hEvent);
6001		cancel_io((HANDLE)_osfhnd(fd));
6002		MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6003		return -1;
6004	    }
6005
6006	    if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
6007		(err = GetLastError()) != ERROR_HANDLE_EOF) {
6008		int ret = 0;
6009		if (err != ERROR_BROKEN_PIPE) {
6010		    errno = map_errno(err);
6011		    ret = -1;
6012		}
6013		CloseHandle(ol.hEvent);
6014		cancel_io((HANDLE)_osfhnd(fd));
6015		MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6016		return ret;
6017	    }
6018	}
6019    }
6020    else {
6021	err = GetLastError();
6022	errno = map_errno(err);
6023    }
6024
6025    if (pol) {
6026	finish_overlapped(&ol, fd, read);
6027    }
6028
6029    ret += read;
6030    if (read >= len) {
6031	buf = (char *)buf + read;
6032	if (err != ERROR_OPERATION_ABORTED &&
6033	    !(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
6034	    goto retry;
6035    }
6036    if (read == 0)
6037	_set_osflags(fd, _osfile(fd) | FEOFLAG);
6038
6039
6040    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6041
6042    return ret;
6043}
6044
6045#undef write
6046/* License: Ruby's */
6047ssize_t
6048rb_w32_write(int fd, const void *buf, size_t size)
6049{
6050    SOCKET sock = TO_SOCKET(fd);
6051    DWORD written;
6052    DWORD wait;
6053    DWORD err;
6054    size_t len;
6055    size_t ret;
6056    OVERLAPPED ol, *pol = NULL;
6057
6058    if (is_socket(sock))
6059	return rb_w32_send(fd, buf, size, 0);
6060
6061    // validate fd by using _get_osfhandle() because we cannot access _nhandle
6062    if (_get_osfhandle(fd) == -1) {
6063	return -1;
6064    }
6065
6066    if ((_osfile(fd) & FTEXT) &&
6067        (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
6068	return _write(fd, buf, size);
6069    }
6070
6071    MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
6072
6073    if (!size || _osfile(fd) & FEOFLAG) {
6074	MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6075	return 0;
6076    }
6077
6078    ret = 0;
6079  retry:
6080    /* get rid of console writing bug */
6081    len = (_osfile(fd) & FDEV) ? min(32 * 1024, size) : size;
6082    size -= len;
6083
6084    /* if have cancel_io, use Overlapped I/O */
6085    if (cancel_io) {
6086	if (setup_overlapped(&ol, fd)) {
6087	    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6088	    return -1;
6089	}
6090
6091	pol = &ol;
6092    }
6093
6094    if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, pol)) {
6095	err = GetLastError();
6096	if (err != ERROR_IO_PENDING) {
6097	    if (pol) CloseHandle(ol.hEvent);
6098	    if (err == ERROR_ACCESS_DENIED)
6099		errno = EBADF;
6100	    else
6101		errno = map_errno(err);
6102
6103	    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6104	    return -1;
6105	}
6106
6107	if (pol) {
6108	    wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
6109	    if (wait != WAIT_OBJECT_0) {
6110		if (wait == WAIT_OBJECT_0 + 1)
6111		    errno = EINTR;
6112		else
6113		    errno = map_errno(GetLastError());
6114		CloseHandle(ol.hEvent);
6115		cancel_io((HANDLE)_osfhnd(fd));
6116		MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6117		return -1;
6118	    }
6119
6120	    if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written,
6121				     TRUE)) {
6122		errno = map_errno(err);
6123		CloseHandle(ol.hEvent);
6124		cancel_io((HANDLE)_osfhnd(fd));
6125		MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6126		return -1;
6127	    }
6128	}
6129    }
6130
6131    if (pol) {
6132	finish_overlapped(&ol, fd, written);
6133    }
6134
6135    ret += written;
6136    if (written == len) {
6137	buf = (const char *)buf + len;
6138	if (size > 0)
6139	    goto retry;
6140    }
6141
6142    MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6143
6144    return ret;
6145}
6146
6147/* License: Ruby's */
6148long
6149rb_w32_write_console(uintptr_t strarg, int fd)
6150{
6151    static int disable;
6152    HANDLE handle;
6153    DWORD dwMode, reslen;
6154    VALUE str = strarg;
6155    rb_encoding *utf16 = rb_enc_find("UTF-16LE");
6156    const WCHAR *ptr, *next;
6157    struct constat *s;
6158    long len;
6159
6160    if (disable) return -1L;
6161    handle = (HANDLE)_osfhnd(fd);
6162    if (!GetConsoleMode(handle, &dwMode) ||
6163	!rb_econv_has_convpath_p(rb_enc_name(rb_enc_get(str)), "UTF-16LE"))
6164	return -1L;
6165
6166    str = rb_str_encode(str, rb_enc_from_encoding(utf16),
6167			ECONV_INVALID_REPLACE|ECONV_UNDEF_REPLACE, Qnil);
6168    ptr = (const WCHAR *)RSTRING_PTR(str);
6169    len = RSTRING_LEN(str) / sizeof(WCHAR);
6170    s = constat_handle(handle);
6171    while (len > 0) {
6172	long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
6173	if (curlen > 0) {
6174	    if (!WriteConsoleW(handle, ptr, curlen, &reslen, NULL)) {
6175		if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
6176		    disable = TRUE;
6177		return -1L;
6178	    }
6179	}
6180	ptr = next;
6181    }
6182    RB_GC_GUARD(str);
6183    return (long)reslen;
6184}
6185
6186/* License: Ruby's */
6187static int
6188unixtime_to_filetime(time_t time, FILETIME *ft)
6189{
6190    ULARGE_INTEGER tmp;
6191
6192    tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
6193    ft->dwLowDateTime = tmp.LowPart;
6194    ft->dwHighDateTime = tmp.HighPart;
6195    return 0;
6196}
6197
6198/* License: Ruby's */
6199static int
6200wutime(const WCHAR *path, const struct utimbuf *times)
6201{
6202    HANDLE hFile;
6203    FILETIME atime, mtime;
6204    struct stati64 stat;
6205    int ret = 0;
6206
6207    if (wstati64(path, &stat)) {
6208	return -1;
6209    }
6210
6211    if (times) {
6212	if (unixtime_to_filetime(times->actime, &atime)) {
6213	    return -1;
6214	}
6215	if (unixtime_to_filetime(times->modtime, &mtime)) {
6216	    return -1;
6217	}
6218    }
6219    else {
6220	GetSystemTimeAsFileTime(&atime);
6221	mtime = atime;
6222    }
6223
6224    RUBY_CRITICAL({
6225	const DWORD attr = GetFileAttributesW(path);
6226	if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
6227	    SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
6228	hFile = CreateFileW(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
6229			    FILE_FLAG_BACKUP_SEMANTICS, 0);
6230	if (hFile == INVALID_HANDLE_VALUE) {
6231	    errno = map_errno(GetLastError());
6232	    ret = -1;
6233	}
6234	else {
6235	    if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
6236		errno = map_errno(GetLastError());
6237		ret = -1;
6238	    }
6239	    CloseHandle(hFile);
6240	}
6241	if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
6242	    SetFileAttributesW(path, attr);
6243    });
6244
6245    return ret;
6246}
6247
6248/* License: Ruby's */
6249int
6250rb_w32_uutime(const char *path, const struct utimbuf *times)
6251{
6252    WCHAR *wpath;
6253    int ret;
6254
6255    if (!(wpath = utf8_to_wstr(path, NULL)))
6256	return -1;
6257    ret = wutime(wpath, times);
6258    free(wpath);
6259    return ret;
6260}
6261
6262/* License: Ruby's */
6263int
6264rb_w32_utime(const char *path, const struct utimbuf *times)
6265{
6266    WCHAR *wpath;
6267    int ret;
6268
6269    if (!(wpath = filecp_to_wstr(path, NULL)))
6270	return -1;
6271    ret = wutime(wpath, times);
6272    free(wpath);
6273    return ret;
6274}
6275
6276/* License: Ruby's */
6277int
6278rb_w32_uchdir(const char *path)
6279{
6280    WCHAR *wpath;
6281    int ret;
6282
6283    if (!(wpath = utf8_to_wstr(path, NULL)))
6284	return -1;
6285    ret = _wchdir(wpath);
6286    free(wpath);
6287    return ret;
6288}
6289
6290/* License: Ruby's */
6291static int
6292wmkdir(const WCHAR *wpath, int mode)
6293{
6294    int ret = -1;
6295
6296    RUBY_CRITICAL(do {
6297	if (CreateDirectoryW(wpath, NULL) == FALSE) {
6298	    errno = map_errno(GetLastError());
6299	    break;
6300	}
6301	if (_wchmod(wpath, mode) == -1) {
6302	    RemoveDirectoryW(wpath);
6303	    break;
6304	}
6305	ret = 0;
6306    } while (0));
6307    return ret;
6308}
6309
6310/* License: Ruby's */
6311int
6312rb_w32_umkdir(const char *path, int mode)
6313{
6314    WCHAR *wpath;
6315    int ret;
6316
6317    if (!(wpath = utf8_to_wstr(path, NULL)))
6318	return -1;
6319    ret = wmkdir(wpath, mode);
6320    free(wpath);
6321    return ret;
6322}
6323
6324/* License: Ruby's */
6325int
6326rb_w32_mkdir(const char *path, int mode)
6327{
6328    WCHAR *wpath;
6329    int ret;
6330
6331    if (!(wpath = filecp_to_wstr(path, NULL)))
6332	return -1;
6333    ret = wmkdir(wpath, mode);
6334    free(wpath);
6335    return ret;
6336}
6337
6338/* License: Ruby's */
6339static int
6340wrmdir(const WCHAR *wpath)
6341{
6342    int ret = 0;
6343    RUBY_CRITICAL({
6344	const DWORD attr = GetFileAttributesW(wpath);
6345	if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6346	    SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
6347	}
6348	if (RemoveDirectoryW(wpath) == FALSE) {
6349	    errno = map_errno(GetLastError());
6350	    ret = -1;
6351	    if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6352		SetFileAttributesW(wpath, attr);
6353	    }
6354	}
6355    });
6356    return ret;
6357}
6358
6359/* License: Ruby's */
6360int
6361rb_w32_rmdir(const char *path)
6362{
6363    WCHAR *wpath;
6364    int ret;
6365
6366    if (!(wpath = filecp_to_wstr(path, NULL)))
6367	return -1;
6368    ret = wrmdir(wpath);
6369    free(wpath);
6370    return ret;
6371}
6372
6373/* License: Ruby's */
6374int
6375rb_w32_urmdir(const char *path)
6376{
6377    WCHAR *wpath;
6378    int ret;
6379
6380    if (!(wpath = utf8_to_wstr(path, NULL)))
6381	return -1;
6382    ret = wrmdir(wpath);
6383    free(wpath);
6384    return ret;
6385}
6386
6387/* License: Ruby's */
6388static int
6389wunlink(const WCHAR *path)
6390{
6391    int ret = 0;
6392    RUBY_CRITICAL({
6393	const DWORD attr = GetFileAttributesW(path);
6394	if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6395	    SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
6396	}
6397	if (!DeleteFileW(path)) {
6398	    errno = map_errno(GetLastError());
6399	    ret = -1;
6400	    if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6401		SetFileAttributesW(path, attr);
6402	    }
6403	}
6404    });
6405    return ret;
6406}
6407
6408/* License: Ruby's */
6409int
6410rb_w32_uunlink(const char *path)
6411{
6412    WCHAR *wpath;
6413    int ret;
6414
6415    if (!(wpath = utf8_to_wstr(path, NULL)))
6416	return -1;
6417    ret = wunlink(wpath);
6418    free(wpath);
6419    return ret;
6420}
6421
6422/* License: Ruby's */
6423int
6424rb_w32_unlink(const char *path)
6425{
6426    WCHAR *wpath;
6427    int ret;
6428
6429    if (!(wpath = filecp_to_wstr(path, NULL)))
6430	return -1;
6431    ret = wunlink(wpath);
6432    free(wpath);
6433    return ret;
6434}
6435
6436/* License: Ruby's */
6437int
6438rb_w32_uchmod(const char *path, int mode)
6439{
6440    WCHAR *wpath;
6441    int ret;
6442
6443    if (!(wpath = utf8_to_wstr(path, NULL)))
6444	return -1;
6445    ret = _wchmod(wpath, mode);
6446    free(wpath);
6447    return ret;
6448}
6449
6450#if !defined(__BORLANDC__)
6451/* License: Ruby's */
6452int
6453rb_w32_isatty(int fd)
6454{
6455    DWORD mode;
6456
6457    // validate fd by using _get_osfhandle() because we cannot access _nhandle
6458    if (_get_osfhandle(fd) == -1) {
6459	return 0;
6460    }
6461    if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
6462	errno = ENOTTY;
6463	return 0;
6464    }
6465    return 1;
6466}
6467#endif
6468
6469//
6470// Fix bcc32's stdio bug
6471//
6472
6473#ifdef __BORLANDC__
6474/* License: Ruby's */
6475static int
6476too_many_files(void)
6477{
6478    FILE *f;
6479    for (f = _streams; f < _streams + _nfile; f++) {
6480	if (f->fd < 0) return 0;
6481    }
6482    return 1;
6483}
6484
6485#undef fopen
6486/* License: Ruby's */
6487FILE *
6488rb_w32_fopen(const char *path, const char *mode)
6489{
6490    FILE *f = (errno = 0, fopen(path, mode));
6491    if (f == NULL && errno == 0) {
6492	if (too_many_files())
6493	    errno = EMFILE;
6494    }
6495    return f;
6496}
6497
6498/* License: Ruby's */
6499FILE *
6500rb_w32_fdopen(int handle, const char *type)
6501{
6502    FILE *f = (errno = 0, _fdopen(handle, (char *)type));
6503    if (f == NULL && errno == 0) {
6504	if (handle < 0)
6505	    errno = EBADF;
6506	else if (too_many_files())
6507	    errno = EMFILE;
6508    }
6509    return f;
6510}
6511
6512/* License: Ruby's */
6513FILE *
6514rb_w32_fsopen(const char *path, const char *mode, int shflags)
6515{
6516    FILE *f = (errno = 0, _fsopen(path, mode, shflags));
6517    if (f == NULL && errno == 0) {
6518	if (too_many_files())
6519	    errno = EMFILE;
6520    }
6521    return f;
6522}
6523#endif
6524
6525#if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
6526extern long _ftol(double);
6527/* License: Ruby's */
6528long
6529_ftol2(double d)
6530{
6531    return _ftol(d);
6532}
6533
6534/* License: Ruby's */
6535long
6536_ftol2_sse(double d)
6537{
6538    return _ftol(d);
6539}
6540#endif
6541
6542#ifndef signbit
6543/* License: Ruby's */
6544int
6545signbit(double x)
6546{
6547    int *ip = (int *)(&x + 1) - 1;
6548    return *ip < 0;
6549}
6550#endif
6551
6552/* License: Ruby's */
6553const char * WSAAPI
6554rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
6555{
6556    typedef char *(WSAAPI inet_ntop_t)(int, void *, char *, size_t);
6557    inet_ntop_t *pInetNtop;
6558    pInetNtop = (inet_ntop_t *)get_proc_address("ws2_32", "inet_ntop", NULL);
6559    if (pInetNtop) {
6560	return pInetNtop(af, (void *)addr, numaddr, numaddr_len);
6561    }
6562    else {
6563	struct in_addr in;
6564	memcpy(&in.s_addr, addr, sizeof(in.s_addr));
6565	snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
6566    }
6567    return numaddr;
6568}
6569
6570/* License: Ruby's */
6571char
6572rb_w32_fd_is_text(int fd)
6573{
6574    return _osfile(fd) & FTEXT;
6575}
6576
6577#if RUBY_MSVCRT_VERSION < 80 && !defined(__MINGW64__)
6578/* License: Ruby's */
6579static int
6580unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
6581{
6582    FILETIME ft;
6583    if (unixtime_to_filetime(t, &ft)) return -1;
6584    if (!FileTimeToSystemTime(&ft, st)) return -1;
6585    return 0;
6586}
6587
6588/* License: Ruby's */
6589static void
6590systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
6591{
6592    int y = st->wYear, m = st->wMonth, d = st->wDay;
6593    t->tm_sec  = st->wSecond;
6594    t->tm_min  = st->wMinute;
6595    t->tm_hour = st->wHour;
6596    t->tm_mday = st->wDay;
6597    t->tm_mon  = st->wMonth - 1;
6598    t->tm_year = y - 1900;
6599    t->tm_wday = st->wDayOfWeek;
6600    switch (m) {
6601      case 1:
6602	break;
6603      case 2:
6604	d += 31;
6605	break;
6606      default:
6607	d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
6608	d += ((m - 3) * 153 + 2) / 5;
6609	break;
6610    }
6611    t->tm_yday = d - 1;
6612}
6613
6614/* License: Ruby's */
6615static int
6616systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
6617{
6618    TIME_ZONE_INFORMATION stdtz;
6619    SYSTEMTIME sst;
6620
6621    if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
6622    if (!tz) {
6623	GetTimeZoneInformation(&stdtz);
6624	tz = &stdtz;
6625    }
6626    if (tz->StandardBias == tz->DaylightBias) return 0;
6627    if (!tz->StandardDate.wMonth) return 0;
6628    if (!tz->DaylightDate.wMonth) return 0;
6629    if (tz != &stdtz) stdtz = *tz;
6630
6631    stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
6632    if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst)) return 0;
6633    if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
6634	return 0;
6635    return 1;
6636}
6637#endif
6638
6639#ifdef __MINGW64__
6640# ifndef MINGW_HAS_SECURE_API
6641   _CRTIMP errno_t __cdecl _gmtime64_s(struct tm* tm, const __time64_t *time);
6642   _CRTIMP errno_t __cdecl _localtime64_s(struct tm* tm, const __time64_t *time);
6643# endif
6644# define gmtime_s _gmtime64_s
6645# define localtime_s _localtime64_s
6646#endif
6647
6648/* License: Ruby's */
6649struct tm *
6650gmtime_r(const time_t *tp, struct tm *rp)
6651{
6652    int e = EINVAL;
6653    if (!tp || !rp) {
6654      error:
6655	errno = e;
6656	return NULL;
6657    }
6658#if RUBY_MSVCRT_VERSION >= 80 || defined(__MINGW64__)
6659    e = gmtime_s(rp, tp);
6660    if (e != 0) goto error;
6661#else
6662    {
6663	SYSTEMTIME st;
6664	if (unixtime_to_systemtime(*tp, &st)) goto error;
6665	rp->tm_isdst = 0;
6666	systemtime_to_tm(&st, rp);
6667    }
6668#endif
6669    return rp;
6670}
6671
6672/* License: Ruby's */
6673struct tm *
6674localtime_r(const time_t *tp, struct tm *rp)
6675{
6676    int e = EINVAL;
6677    if (!tp || !rp) {
6678      error:
6679	errno = e;
6680	return NULL;
6681    }
6682#if RUBY_MSVCRT_VERSION >= 80 || defined(__MINGW64__)
6683    e = localtime_s(rp, tp);
6684    if (e) goto error;
6685#else
6686    {
6687	SYSTEMTIME gst, lst;
6688	if (unixtime_to_systemtime(*tp, &gst)) goto error;
6689	rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
6690	systemtime_to_tm(&lst, rp);
6691    }
6692#endif
6693    return rp;
6694}
6695
6696/* License: Ruby's */
6697int
6698rb_w32_wrap_io_handle(HANDLE h, int flags)
6699{
6700    BOOL tmp;
6701    int len = sizeof(tmp);
6702    int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (char *)&tmp, &len);
6703    if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
6704        int f = 0;
6705        if (flags & O_NONBLOCK) {
6706            flags &= ~O_NONBLOCK;
6707            f = O_NONBLOCK;
6708        }
6709        socklist_insert((SOCKET)h, f);
6710    }
6711    else if (flags & O_NONBLOCK) {
6712        errno = EINVAL;
6713        return -1;
6714    }
6715    return rb_w32_open_osfhandle((intptr_t)h, flags);
6716}
6717
6718/* License: Ruby's */
6719int
6720rb_w32_unwrap_io_handle(int fd)
6721{
6722    SOCKET sock = TO_SOCKET(fd);
6723    _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6724    if (!is_socket(sock)) {
6725	UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6726	constat_delete((HANDLE)sock);
6727    }
6728    else {
6729	socklist_delete(&sock, NULL);
6730    }
6731    return _close(fd);
6732}
6733