Ruby 2.0.0p247(2013-06-27revision41674)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

win32.c
Go to the documentation of this file.
1 /*
2 * Copyright (c) 1993, Intergraph Corporation
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Artistic License, as specified in the perl README file.
6 *
7 * Various Unix compatibility functions and NT specific functions.
8 *
9 * Some of this code was derived from the MSDOS port(s) and the OS/2 port.
10 *
11 */
12 /*
13 The parts licensed under above copyright notice are marked as "Artistic or
14 GPL".
15 Another parts are licensed under Ruby's License.
16
17 Copyright (C) 1993-2011 Yukihiro Matsumoto
18 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
19 Copyright (C) 2000 Information-technology Promotion Agency, Japan
20 */
21
22 #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
75 static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE, DWORD);
76 static int has_redirection(const char *);
77 int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
78 static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
79 static int wstati64(const WCHAR *path, struct stati64 *st);
80 VALUE 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 */
85 static 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 */
222 int
223 rb_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
245 static const char *NTLoginName;
246
247 static OSVERSIONINFO osver;
248
249 /* License: Artistic or GPL */
250 static void
251 get_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 */
260 DWORD
261 rb_w32_osid(void)
262 {
263 return osver.dwPlatformId;
264 }
265 #endif
266
267 /* License: Artistic or GPL */
268 DWORD
269 rb_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 */
294 static uintptr_t
295 flock_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 */
333 int
334 flock(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 */
344 static inline WCHAR *
345 translate_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 */
355 static inline char *
356 translate_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 */
383 static BOOL
384 get_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 */
399 static void
400 regulate_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 */
410 static FARPROC
411 get_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 */
434 static UINT
435 get_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 */
448 VALUE
449 rb_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 */
459 UINT
460 rb_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 */
476 static void
477 init_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
539 typedef BOOL (WINAPI *cancel_io_t)(HANDLE);
540 static cancel_io_t cancel_io = NULL;
541
542 /* License: Ruby's */
543 static void
544 init_func(void)
545 {
546 if (!cancel_io)
547 cancel_io = (cancel_io_t)get_proc_address("kernel32", "CancelIo", NULL);
548 }
549
550 static void init_stdhandle(void);
551
552 #if RUBY_MSVCRT_VERSION >= 80
553 /* License: Ruby's */
554 static void
555 invalid_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
560 int ruby_w32_rtc_error;
561
562 /* License: Ruby's */
563 static int __cdecl
564 rtc_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
580 static CRITICAL_SECTION select_mutex;
581 static int NtSocketsInitialized = 0;
582 static st_table *socklist = NULL;
583 static st_table *conlist = NULL;
584 static char *envarea;
585 static char *uenvarea;
586
587 /* License: Ruby's */
588 struct constat {
589 struct {
590 int state, seq[16];
591 WORD attr;
592 COORD saved;
593 } vt100;
594 };
595 enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
596
597 /* License: Ruby's */
598 static int
599 free_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 */
606 static void
607 constat_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 */
617 static void
618 exit_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 */
645 static void
646 StartSockets(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 */
671 static inline int
672 socklist_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 */
680 static inline int
681 socklist_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 */
696 static inline int
697 socklist_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 */
722 void
723 rb_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
761 char *
762 getlogin(void)
763 {
764 return (char *)NTLoginName;
765 }
766
767 #define MAXCHILDNUM 256 /* max num of child processes */
768
769 /* License: Ruby's */
770 static 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 */
782 static struct ChildRecord *
783 FindChildSlot(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 */
795 static struct ChildRecord *
796 FindChildSlotByHandle(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 */
808 static void
809 CloseChildHandle(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 */
818 static struct ChildRecord *
819 FindFreeChildSlot(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 */
838 static 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 */
891 static int
892 internal_match(const void *key, const void *elem)
893 {
894 return strcmp(key, (*(const char *const *)elem) + 1);
895 }
896
897 /* License: Ruby's */
898 static int
899 is_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
910 static int internal_cmd_match(const char *cmdname, int nt);
911
912 /* License: Ruby's */
913 static int
914 is_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 */
942 static int
943 internal_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 */
957 SOCKET
958 rb_w32_get_osfhandle(int fh)
959 {
960 return _get_osfhandle(fh);
961 }
962
963 /* License: Ruby's */
964 static int
965 join_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 */
1043 static int
1044 check_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 */
1057 static rb_pid_t
1058 child_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 */
1076 static struct ChildRecord *
1077 CreateChild(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 */
1156 static int
1157 is_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
1168 static UINT filecp(void);
1169 static WCHAR *mbstr_to_wstr(UINT, const char *, int, long *);
1170 static 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 */
1179 rb_pid_t
1180 rb_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 */
1286 rb_pid_t
1287 rb_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
1356 rb_pid_t
1357 rb_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 */
1363 typedef 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 */
1379 static int
1380 insert(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 */
1399 static NtCmdLineElement **
1400 cmdglob(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 */
1432 static int
1433 has_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 */
1481 static inline char *
1482 skipspace(char *ptr)
1483 {
1484 while (ISSPACE(*ptr))
1485 ptr++;
1486 return ptr;
1487 }
1488
1489 /* License: Artistic or GPL */
1490 int
1491 rb_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 */
1745 static HANDLE
1746 open_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 */
1779 static DIR *
1780 opendir_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 */
1864 static inline UINT
1865 filecp(void)
1866 {
1867 UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
1868 return cp;
1869 }
1870
1871 /* License: Ruby's */
1872 static char *
1873 wstr_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 */
1884 static WCHAR *
1885 mbstr_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 */
1896 DIR *
1897 rb_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 */
1909 DIR *
1910 rb_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 */
1926 static void
1927 move_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 */
1943 static BOOL
1944 win32_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 */
1952 VALUE
1953 rb_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 */
1972 char *
1973 rb_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 */
1987 static BOOL
1988 ruby_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 */
1996 static struct direct *
1997 readdir_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 */
2035 struct direct *
2036 rb_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 */
2049 long
2050 rb_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 */
2060 void
2061 rb_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 */
2075 void
2076 rb_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 */
2087 void
2088 rb_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 */
2116 typedef 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__)
2136 EXTERN_C _CRTIMP ioinfo * __pioinfo[];
2137 static 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
2146 static size_t pioinfo_extra = 0; /* workaround for VC++8 SP1 */
2147
2148 /* License: Ruby's */
2149 static void
2150 set_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
2171 static 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
2190 static int is_socket(SOCKET);
2191 static int is_console(SOCKET);
2192
2193 /* License: Ruby's */
2194 int
2195 rb_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 */
2201 static int
2202 rb_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 */
2243 static void
2244 init_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 */
2275 static void
2276 init_stdhandle(void)
2277 {
2278 }
2279 #endif
2280
2281 /* License: Ruby's */
2282 #ifdef __BORLANDC__
2283 static int
2284 rb_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 */
2298 static int
2299 is_socket(SOCKET sock)
2300 {
2301 if (socklist_lookup(sock, NULL))
2302 return TRUE;
2303 else
2304 return FALSE;
2305 }
2306
2307 /* License: Ruby's */
2308 int
2309 rb_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 */
2323 char *
2324 rb_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 */
2391 rb_uid_t
2392 getuid(void)
2393 {
2394 return ROOT_UID;
2395 }
2396
2397 /* License: Artistic or GPL */
2398 rb_uid_t
2399 geteuid(void)
2400 {
2401 return ROOT_UID;
2402 }
2403
2404 /* License: Artistic or GPL */
2405 rb_gid_t
2406 getgid(void)
2407 {
2408 return ROOT_GID;
2409 }
2410
2411 /* License: Artistic or GPL */
2412 rb_gid_t
2413 getegid(void)
2414 {
2415 return ROOT_GID;
2416 }
2417
2418 /* License: Artistic or GPL */
2419 int
2420 setuid(rb_uid_t uid)
2421 {
2422 return (uid == ROOT_UID ? 0 : -1);
2423 }
2424
2425 /* License: Artistic or GPL */
2426 int
2427 setgid(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 */
2437 int
2438 ioctl(int i, int u, ...)
2439 {
2440 errno = EINVAL;
2441 return -1;
2442 }
2443
2444 void
2445 rb_w32_fdset(int fd, fd_set *set)
2446 {
2447 FD_SET(fd, set);
2448 }
2449
2450 #undef FD_CLR
2451
2452 /* License: Ruby's */
2453 void
2454 rb_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 */
2471 int
2472 rb_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 */
2483 void
2484 rb_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 */
2498 void
2499 rb_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 */
2520 static int
2521 extract_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 */
2562 static int
2563 copy_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 */
2584 static int
2585 is_not_socket(SOCKET sock)
2586 {
2587 return !is_socket(sock);
2588 }
2589
2590 /* License: Ruby's */
2591 static int
2592 is_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 */
2604 static int
2605 is_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 */
2623 static int
2624 is_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 */
2638 static int
2639 is_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 */
2661 static int
2662 is_invalid_handle(SOCKET sock)
2663 {
2664 return (HANDLE)sock == INVALID_HANDLE_VALUE;
2665 }
2666
2667 /* License: Artistic or GPL */
2668 static int
2669 do_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 */
2703 int
2704 rb_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 */
2722 static inline int
2723 compare(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
2738 int rb_w32_check_interrupt(void *); /* @internal */
2739
2740 /* @internal */
2741 /* License: Ruby's */
2742 int
2743 rb_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 */
2881 int WSAAPI
2882 rb_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 */
2889 static FARPROC
2890 get_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 */
2905 int WSAAPI
2906 rb_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 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
2921 _set_osfhnd(fd, r);
2922 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
2923 CloseHandle(h);
2924 socklist_insert(r, 0);
2925 }
2926 else {
2927 errno = map_errno(WSAGetLastError());
2928 close(fd);
2929 fd = -1;
2930 }
2931 }
2932 else
2933 CloseHandle(h);
2934 });
2935 return fd;
2936 }
2937
2938 #undef bind
2939
2940 /* License: Artistic or GPL */
2941 int WSAAPI
2942 rb_w32_bind(int s, const struct sockaddr *addr, int addrlen)
2943 {
2944 int r;
2945
2946 if (!NtSocketsInitialized) {
2947 StartSockets();
2948 }
2949 RUBY_CRITICAL({
2950 r = bind(TO_SOCKET(s), addr, addrlen);
2951 if (r == SOCKET_ERROR)
2952 errno = map_errno(WSAGetLastError());
2953 });
2954 return r;
2955 }
2956
2957 #undef connect
2958
2959 /* License: Artistic or GPL */
2960 int WSAAPI
2961 rb_w32_connect(int s, const struct sockaddr *addr, int addrlen)
2962 {
2963 int r;
2964 if (!NtSocketsInitialized) {
2965 StartSockets();
2966 }
2967 RUBY_CRITICAL({
2968 r = connect(TO_SOCKET(s), addr, addrlen);
2969 if (r == SOCKET_ERROR) {
2970 int err = WSAGetLastError();
2971 if (err != WSAEWOULDBLOCK)
2972 errno = map_errno(err);
2973 else
2974 errno = EINPROGRESS;
2975 }
2976 });
2977 return r;
2978 }
2979
2980
2981 #undef getpeername
2982
2983 /* License: Artistic or GPL */
2984 int WSAAPI
2985 rb_w32_getpeername(int s, struct sockaddr *addr, int *addrlen)
2986 {
2987 int r;
2988 if (!NtSocketsInitialized) {
2989 StartSockets();
2990 }
2991 RUBY_CRITICAL({
2992 r = getpeername(TO_SOCKET(s), addr, addrlen);
2993 if (r == SOCKET_ERROR)
2994 errno = map_errno(WSAGetLastError());
2995 });
2996 return r;
2997 }
2998
2999 #undef getsockname
3000
3001 /* License: Artistic or GPL */
3002 int WSAAPI
3003 rb_w32_getsockname(int fd, struct sockaddr *addr, int *addrlen)
3004 {
3005 int sock;
3006 int r;
3007 if (!NtSocketsInitialized) {
3008 StartSockets();
3009 }
3010 RUBY_CRITICAL({
3011 sock = TO_SOCKET(fd);
3012 r = getsockname(sock, addr, addrlen);
3013 if (r == SOCKET_ERROR) {
3014 DWORD wsaerror = WSAGetLastError();
3015 if (wsaerror == WSAEINVAL) {
3016 int flags;
3017 if (socklist_lookup(sock, &flags)) {
3018 int af = GET_FAMILY(flags);
3019 if (af) {
3020 memset(addr, 0, *addrlen);
3021 addr->sa_family = af;
3022 return 0;
3023 }
3024 }
3025 }
3026 errno = map_errno(wsaerror);
3027 }
3028 });
3029 return r;
3030 }
3031
3032 #undef getsockopt
3033
3034 /* License: Artistic or GPL */
3035 int WSAAPI
3036 rb_w32_getsockopt(int s, int level, int optname, char *optval, int *optlen)
3037 {
3038 int r;
3039 if (!NtSocketsInitialized) {
3040 StartSockets();
3041 }
3042 RUBY_CRITICAL({
3043 r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3044 if (r == SOCKET_ERROR)
3045 errno = map_errno(WSAGetLastError());
3046 });
3047 return r;
3048 }
3049
3050 #undef ioctlsocket
3051
3052 /* License: Artistic or GPL */
3053 int WSAAPI
3054 rb_w32_ioctlsocket(int s, long cmd, u_long *argp)
3055 {
3056 int r;
3057 if (!NtSocketsInitialized) {
3058 StartSockets();
3059 }
3060 RUBY_CRITICAL({
3061 r = ioctlsocket(TO_SOCKET(s), cmd, argp);
3062 if (r == SOCKET_ERROR)
3063 errno = map_errno(WSAGetLastError());
3064 });
3065 return r;
3066 }
3067
3068 #undef listen
3069
3070 /* License: Artistic or GPL */
3071 int WSAAPI
3072 rb_w32_listen(int s, int backlog)
3073 {
3074 int r;
3075 if (!NtSocketsInitialized) {
3076 StartSockets();
3077 }
3078 RUBY_CRITICAL({
3079 r = listen(TO_SOCKET(s), backlog);
3080 if (r == SOCKET_ERROR)
3081 errno = map_errno(WSAGetLastError());
3082 });
3083 return r;
3084 }
3085
3086 #undef recv
3087 #undef recvfrom
3088 #undef send
3089 #undef sendto
3090
3091 /* License: Ruby's */
3092 static int
3093 finish_overlapped_socket(BOOL input, SOCKET s, WSAOVERLAPPED *wol, int result, DWORD *len, DWORD size)
3094 {
3095 DWORD flg;
3096 int err;
3097
3098 if (result != SOCKET_ERROR)
3099 *len = size;
3100 else if ((err = WSAGetLastError()) == WSA_IO_PENDING) {
3101 switch (rb_w32_wait_events_blocking(&wol->hEvent, 1, INFINITE)) {
3102 case WAIT_OBJECT_0:
3103 RUBY_CRITICAL(
3104 result = WSAGetOverlappedResult(s, wol, &size, TRUE, &flg)
3105 );
3106 if (result) {
3107 *len = size;
3108 break;
3109 }
3110 /* thru */
3111 default:
3112 if ((err = WSAGetLastError()) == WSAECONNABORTED && !input)
3113 errno = EPIPE;
3114 else
3115 errno = map_errno(WSAGetLastError());
3116 /* thru */
3117 case WAIT_OBJECT_0 + 1:
3118 /* interrupted */
3119 *len = -1;
3120 cancel_io((HANDLE)s);
3121 break;
3122 }
3123 }
3124 else {
3125 if (err == WSAECONNABORTED && !input)
3126 errno = EPIPE;
3127 else
3128 errno = map_errno(err);
3129 *len = -1;
3130 }
3131 CloseHandle(wol->hEvent);
3132
3133 return result;
3134 }
3135
3136 /* License: Artistic or GPL */
3137 static int
3138 overlapped_socket_io(BOOL input, int fd, char *buf, int len, int flags,
3139 struct sockaddr *addr, int *addrlen)
3140 {
3141 int r;
3142 int ret;
3143 int mode = 0;
3144 DWORD flg;
3145 WSAOVERLAPPED wol;
3146 WSABUF wbuf;
3147 SOCKET s;
3148
3149 if (!NtSocketsInitialized)
3150 StartSockets();
3151
3152 s = TO_SOCKET(fd);
3153 socklist_lookup(s, &mode);
3154 if (!cancel_io || (GET_FLAGS(mode) & O_NONBLOCK)) {
3155 RUBY_CRITICAL({
3156 if (input) {
3157 if (addr && addrlen)
3158 r = recvfrom(s, buf, len, flags, addr, addrlen);
3159 else
3160 r = recv(s, buf, len, flags);
3161 if (r == SOCKET_ERROR)
3162 errno = map_errno(WSAGetLastError());
3163 }
3164 else {
3165 if (addr && addrlen)
3166 r = sendto(s, buf, len, flags, addr, *addrlen);
3167 else
3168 r = send(s, buf, len, flags);
3169 if (r == SOCKET_ERROR) {
3170 DWORD err = WSAGetLastError();
3171 if (err == WSAECONNABORTED)
3172 errno = EPIPE;
3173 else
3174 errno = map_errno(err);
3175 }
3176 }
3177 });
3178 }
3179 else {
3180 DWORD size;
3181 DWORD rlen;
3182 wbuf.len = len;
3183 wbuf.buf = buf;
3184 memset(&wol, 0, sizeof(wol));
3185 RUBY_CRITICAL({
3186 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3187 if (input) {
3188 flg = flags;
3189 if (addr && addrlen)
3190 ret = WSARecvFrom(s, &wbuf, 1, &size, &flg, addr, addrlen,
3191 &wol, NULL);
3192 else
3193 ret = WSARecv(s, &wbuf, 1, &size, &flg, &wol, NULL);
3194 }
3195 else {
3196 if (addr && addrlen)
3197 ret = WSASendTo(s, &wbuf, 1, &size, flags, addr, *addrlen,
3198 &wol, NULL);
3199 else
3200 ret = WSASend(s, &wbuf, 1, &size, flags, &wol, NULL);
3201 }
3202 });
3203
3204 finish_overlapped_socket(input, s, &wol, ret, &rlen, size);
3205 r = (int)rlen;
3206 }
3207
3208 return r;
3209 }
3210
3211 /* License: Ruby's */
3212 int WSAAPI
3213 rb_w32_recv(int fd, char *buf, int len, int flags)
3214 {
3215 return overlapped_socket_io(TRUE, fd, buf, len, flags, NULL, NULL);
3216 }
3217
3218 /* License: Ruby's */
3219 int WSAAPI
3220 rb_w32_recvfrom(int fd, char *buf, int len, int flags,
3221 struct sockaddr *from, int *fromlen)
3222 {
3223 return overlapped_socket_io(TRUE, fd, buf, len, flags, from, fromlen);
3224 }
3225
3226 /* License: Ruby's */
3227 int WSAAPI
3228 rb_w32_send(int fd, const char *buf, int len, int flags)
3229 {
3230 return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags, NULL, NULL);
3231 }
3232
3233 /* License: Ruby's */
3234 int WSAAPI
3235 rb_w32_sendto(int fd, const char *buf, int len, int flags,
3236 const struct sockaddr *to, int tolen)
3237 {
3238 return overlapped_socket_io(FALSE, fd, (char *)buf, len, flags,
3239 (struct sockaddr *)to, &tolen);
3240 }
3241
3242 #if !defined(MSG_TRUNC) && !defined(__MINGW32__)
3243 /* License: Ruby's */
3244 typedef struct {
3245 SOCKADDR *name;
3246 int namelen;
3247 WSABUF *lpBuffers;
3248 DWORD dwBufferCount;
3249 WSABUF Control;
3250 DWORD dwFlags;
3251 } WSAMSG;
3252 #endif
3253 #ifndef WSAID_WSARECVMSG
3254 #define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
3255 #endif
3256 #ifndef WSAID_WSASENDMSG
3257 #define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
3258 #endif
3259
3260 /* License: Ruby's */
3261 #define msghdr_to_wsamsg(msg, wsamsg) \
3262 do { \
3263 int i; \
3264 (wsamsg)->name = (msg)->msg_name; \
3265 (wsamsg)->namelen = (msg)->msg_namelen; \
3266 (wsamsg)->lpBuffers = ALLOCA_N(WSABUF, (msg)->msg_iovlen); \
3267 (wsamsg)->dwBufferCount = (msg)->msg_iovlen; \
3268 for (i = 0; i < (msg)->msg_iovlen; ++i) { \
3269 (wsamsg)->lpBuffers[i].buf = (msg)->msg_iov[i].iov_base; \
3270 (wsamsg)->lpBuffers[i].len = (msg)->msg_iov[i].iov_len; \
3271 } \
3272 (wsamsg)->Control.buf = (msg)->msg_control; \
3273 (wsamsg)->Control.len = (msg)->msg_controllen; \
3274 (wsamsg)->dwFlags = (msg)->msg_flags; \
3275 } while (0)
3276
3277 /* License: Ruby's */
3278 int
3279 recvmsg(int fd, struct msghdr *msg, int flags)
3280 {
3281 typedef int (WSAAPI *WSARecvMsg_t)(SOCKET, WSAMSG *, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3282 static WSARecvMsg_t pWSARecvMsg = NULL;
3283 WSAMSG wsamsg;
3284 SOCKET s;
3285 int mode = 0;
3286 DWORD len;
3287 int ret;
3288
3289 if (!NtSocketsInitialized)
3290 StartSockets();
3291
3292 s = TO_SOCKET(fd);
3293
3294 if (!pWSARecvMsg) {
3295 static GUID guid = WSAID_WSARECVMSG;
3296 pWSARecvMsg = (WSARecvMsg_t)get_wsa_extension_function(s, &guid);
3297 if (!pWSARecvMsg)
3298 return -1;
3299 }
3300
3301 msghdr_to_wsamsg(msg, &wsamsg);
3302 wsamsg.dwFlags |= flags;
3303
3304 socklist_lookup(s, &mode);
3305 if (!cancel_io || (GET_FLAGS(mode) & O_NONBLOCK)) {
3306 RUBY_CRITICAL({
3307 if ((ret = pWSARecvMsg(s, &wsamsg, &len, NULL, NULL)) == SOCKET_ERROR) {
3308 errno = map_errno(WSAGetLastError());
3309 len = -1;
3310 }
3311 });
3312 }
3313 else {
3314 DWORD size;
3315 WSAOVERLAPPED wol;
3316 memset(&wol, 0, sizeof(wol));
3317 RUBY_CRITICAL({
3318 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3319 ret = pWSARecvMsg(s, &wsamsg, &size, &wol, NULL);
3320 });
3321
3322 ret = finish_overlapped_socket(TRUE, s, &wol, ret, &len, size);
3323 }
3324 if (ret == SOCKET_ERROR)
3325 return -1;
3326
3327 /* WSAMSG to msghdr */
3328 msg->msg_name = wsamsg.name;
3329 msg->msg_namelen = wsamsg.namelen;
3330 msg->msg_flags = wsamsg.dwFlags;
3331
3332 return len;
3333 }
3334
3335 /* License: Ruby's */
3336 int
3337 sendmsg(int fd, const struct msghdr *msg, int flags)
3338 {
3339 typedef int (WSAAPI *WSASendMsg_t)(SOCKET, const WSAMSG *, DWORD, DWORD *, WSAOVERLAPPED *, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
3340 static WSASendMsg_t pWSASendMsg = NULL;
3341 WSAMSG wsamsg;
3342 SOCKET s;
3343 int mode = 0;
3344 DWORD len;
3345 int ret;
3346
3347 if (!NtSocketsInitialized)
3348 StartSockets();
3349
3350 s = TO_SOCKET(fd);
3351
3352 if (!pWSASendMsg) {
3353 static GUID guid = WSAID_WSASENDMSG;
3354 pWSASendMsg = (WSASendMsg_t)get_wsa_extension_function(s, &guid);
3355 if (!pWSASendMsg)
3356 return -1;
3357 }
3358
3359 msghdr_to_wsamsg(msg, &wsamsg);
3360
3361 socklist_lookup(s, &mode);
3362 if (!cancel_io || (GET_FLAGS(mode) & O_NONBLOCK)) {
3363 RUBY_CRITICAL({
3364 if ((ret = pWSASendMsg(s, &wsamsg, flags, &len, NULL, NULL)) == SOCKET_ERROR) {
3365 errno = map_errno(WSAGetLastError());
3366 len = -1;
3367 }
3368 });
3369 }
3370 else {
3371 DWORD size;
3372 WSAOVERLAPPED wol;
3373 memset(&wol, 0, sizeof(wol));
3374 RUBY_CRITICAL({
3375 wol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
3376 ret = pWSASendMsg(s, &wsamsg, flags, &size, &wol, NULL);
3377 });
3378
3379 finish_overlapped_socket(FALSE, s, &wol, ret, &len, size);
3380 }
3381
3382 return len;
3383 }
3384
3385 #undef setsockopt
3386
3387 /* License: Artistic or GPL */
3388 int WSAAPI
3389 rb_w32_setsockopt(int s, int level, int optname, const char *optval, int optlen)
3390 {
3391 int r;
3392 if (!NtSocketsInitialized) {
3393 StartSockets();
3394 }
3395 RUBY_CRITICAL({
3396 r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen);
3397 if (r == SOCKET_ERROR)
3398 errno = map_errno(WSAGetLastError());
3399 });
3400 return r;
3401 }
3402
3403 #undef shutdown
3404
3405 /* License: Artistic or GPL */
3406 int WSAAPI
3407 rb_w32_shutdown(int s, int how)
3408 {
3409 int r;
3410 if (!NtSocketsInitialized) {
3411 StartSockets();
3412 }
3413 RUBY_CRITICAL({
3414 r = shutdown(TO_SOCKET(s), how);
3415 if (r == SOCKET_ERROR)
3416 errno = map_errno(WSAGetLastError());
3417 });
3418 return r;
3419 }
3420
3421 /* License: Ruby's */
3422 static SOCKET
3423 open_ifs_socket(int af, int type, int protocol)
3424 {
3425 unsigned long proto_buffers_len = 0;
3426 int error_code;
3427 SOCKET out = INVALID_SOCKET;
3428
3429 if (WSAEnumProtocols(NULL, NULL, &proto_buffers_len) == SOCKET_ERROR) {
3430 error_code = WSAGetLastError();
3431 if (error_code == WSAENOBUFS) {
3432 WSAPROTOCOL_INFO *proto_buffers;
3433 int protocols_available = 0;
3434
3435 proto_buffers = (WSAPROTOCOL_INFO *)malloc(proto_buffers_len);
3436 if (!proto_buffers) {
3437 WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
3438 return INVALID_SOCKET;
3439 }
3440
3441 protocols_available =
3442 WSAEnumProtocols(NULL, proto_buffers, &proto_buffers_len);
3443 if (protocols_available != SOCKET_ERROR) {
3444 int i;
3445 for (i = 0; i < protocols_available; i++) {
3446 if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily) ||
3447 (type != proto_buffers[i].iSocketType) ||
3448 (protocol != 0 && protocol != proto_buffers[i].iProtocol))
3449 continue;
3450
3451 if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
3452 continue;
3453
3454 out = WSASocket(af, type, protocol, &(proto_buffers[i]), 0,
3455 WSA_FLAG_OVERLAPPED);
3456 break;
3457 }
3458 if (out == INVALID_SOCKET)
3459 out = WSASocket(af, type, protocol, NULL, 0, 0);
3460 }
3461
3462 free(proto_buffers);
3463 }
3464 }
3465
3466 return out;
3467 }
3468
3469 #undef socket
3470
3471 /* License: Artistic or GPL */
3472 int WSAAPI
3473 rb_w32_socket(int af, int type, int protocol)
3474 {
3475 SOCKET s;
3476 int fd;
3477
3478 if (!NtSocketsInitialized) {
3479 StartSockets();
3480 }
3481 RUBY_CRITICAL({
3482 s = open_ifs_socket(af, type, protocol);
3483 if (s == INVALID_SOCKET) {
3484 errno = map_errno(WSAGetLastError());
3485 fd = -1;
3486 }
3487 else {
3488 fd = rb_w32_open_osfhandle(s, O_RDWR|O_BINARY|O_NOINHERIT);
3489 if (fd != -1)
3490 socklist_insert(s, MAKE_SOCKDATA(af, 0));
3491 else
3492 closesocket(s);
3493 }
3494 });
3495 return fd;
3496 }
3497
3498 #undef gethostbyaddr
3499
3500 /* License: Artistic or GPL */
3501 struct hostent * WSAAPI
3502 rb_w32_gethostbyaddr(const char *addr, int len, int type)
3503 {
3504 struct hostent *r;
3505 if (!NtSocketsInitialized) {
3506 StartSockets();
3507 }
3508 RUBY_CRITICAL({
3509 r = gethostbyaddr(addr, len, type);
3510 if (r == NULL)
3511 errno = map_errno(WSAGetLastError());
3512 });
3513 return r;
3514 }
3515
3516 #undef gethostbyname
3517
3518 /* License: Artistic or GPL */
3519 struct hostent * WSAAPI
3520 rb_w32_gethostbyname(const char *name)
3521 {
3522 struct hostent *r;
3523 if (!NtSocketsInitialized) {
3524 StartSockets();
3525 }
3526 RUBY_CRITICAL({
3527 r = gethostbyname(name);
3528 if (r == NULL)
3529 errno = map_errno(WSAGetLastError());
3530 });
3531 return r;
3532 }
3533
3534 #undef gethostname
3535
3536 /* License: Artistic or GPL */
3537 int WSAAPI
3538 rb_w32_gethostname(char *name, int len)
3539 {
3540 int r;
3541 if (!NtSocketsInitialized) {
3542 StartSockets();
3543 }
3544 RUBY_CRITICAL({
3545 r = gethostname(name, len);
3546 if (r == SOCKET_ERROR)
3547 errno = map_errno(WSAGetLastError());
3548 });
3549 return r;
3550 }
3551
3552 #undef getprotobyname
3553
3554 /* License: Artistic or GPL */
3555 struct protoent * WSAAPI
3556 rb_w32_getprotobyname(const char *name)
3557 {
3558 struct protoent *r;
3559 if (!NtSocketsInitialized) {
3560 StartSockets();
3561 }
3562 RUBY_CRITICAL({
3563 r = getprotobyname(name);
3564 if (r == NULL)
3565 errno = map_errno(WSAGetLastError());
3566 });
3567 return r;
3568 }
3569
3570 #undef getprotobynumber
3571
3572 /* License: Artistic or GPL */
3573 struct protoent * WSAAPI
3574 rb_w32_getprotobynumber(int num)
3575 {
3576 struct protoent *r;
3577 if (!NtSocketsInitialized) {
3578 StartSockets();
3579 }
3580 RUBY_CRITICAL({
3581 r = getprotobynumber(num);
3582 if (r == NULL)
3583 errno = map_errno(WSAGetLastError());
3584 });
3585 return r;
3586 }
3587
3588 #undef getservbyname
3589
3590 /* License: Artistic or GPL */
3591 struct servent * WSAAPI
3592 rb_w32_getservbyname(const char *name, const char *proto)
3593 {
3594 struct servent *r;
3595 if (!NtSocketsInitialized) {
3596 StartSockets();
3597 }
3598 RUBY_CRITICAL({
3599 r = getservbyname(name, proto);
3600 if (r == NULL)
3601 errno = map_errno(WSAGetLastError());
3602 });
3603 return r;
3604 }
3605
3606 #undef getservbyport
3607
3608 /* License: Artistic or GPL */
3609 struct servent * WSAAPI
3610 rb_w32_getservbyport(int port, const char *proto)
3611 {
3612 struct servent *r;
3613 if (!NtSocketsInitialized) {
3614 StartSockets();
3615 }
3616 RUBY_CRITICAL({
3617 r = getservbyport(port, proto);
3618 if (r == NULL)
3619 errno = map_errno(WSAGetLastError());
3620 });
3621 return r;
3622 }
3623
3624 /* License: Ruby's */
3625 static int
3626 socketpair_internal(int af, int type, int protocol, SOCKET *sv)
3627 {
3628 SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET;
3629 struct sockaddr_in sock_in4;
3630 #ifdef INET6
3631 struct sockaddr_in6 sock_in6;
3632 #endif
3633 struct sockaddr *addr;
3634 int ret = -1;
3635 int len;
3636
3637 if (!NtSocketsInitialized) {
3638 StartSockets();
3639 }
3640
3641 switch (af) {
3642 case AF_INET:
3643 #if defined PF_INET && PF_INET != AF_INET
3644 case PF_INET:
3645 #endif
3646 sock_in4.sin_family = AF_INET;
3647 sock_in4.sin_port = 0;
3648 sock_in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
3649 addr = (struct sockaddr *)&sock_in4;
3650 len = sizeof(sock_in4);
3651 break;
3652 #ifdef INET6
3653 case AF_INET6:
3654 memset(&sock_in6, 0, sizeof(sock_in6));
3655 sock_in6.sin6_family = AF_INET6;
3656 sock_in6.sin6_addr = IN6ADDR_LOOPBACK_INIT;
3657 addr = (struct sockaddr *)&sock_in6;
3658 len = sizeof(sock_in6);
3659 break;
3660 #endif
3661 default:
3662 errno = EAFNOSUPPORT;
3663 return -1;
3664 }
3665 if (type != SOCK_STREAM) {
3666 errno = EPROTOTYPE;
3667 return -1;
3668 }
3669
3670 sv[0] = (SOCKET)INVALID_HANDLE_VALUE;
3671 sv[1] = (SOCKET)INVALID_HANDLE_VALUE;
3672 RUBY_CRITICAL({
3673 do {
3674 svr = open_ifs_socket(af, type, protocol);
3675 if (svr == INVALID_SOCKET)
3676 break;
3677 if (bind(svr, addr, len) < 0)
3678 break;
3679 if (getsockname(svr, addr, &len) < 0)
3680 break;
3681 if (type == SOCK_STREAM)
3682 listen(svr, 5);
3683
3684 w = open_ifs_socket(af, type, protocol);
3685 if (w == INVALID_SOCKET)
3686 break;
3687 if (connect(w, addr, len) < 0)
3688 break;
3689
3690 r = accept(svr, addr, &len);
3691 if (r == INVALID_SOCKET)
3692 break;
3693
3694 ret = 0;
3695 } while (0);
3696
3697 if (ret < 0) {
3698 errno = map_errno(WSAGetLastError());
3699 if (r != INVALID_SOCKET)
3700 closesocket(r);
3701 if (w != INVALID_SOCKET)
3702 closesocket(w);
3703 }
3704 else {
3705 sv[0] = r;
3706 sv[1] = w;
3707 }
3708 if (svr != INVALID_SOCKET)
3709 closesocket(svr);
3710 });
3711
3712 return ret;
3713 }
3714
3715 /* License: Ruby's */
3716 int
3717 rb_w32_socketpair(int af, int type, int protocol, int *sv)
3718 {
3719 SOCKET pair[2];
3720
3721 if (socketpair_internal(af, type, protocol, pair) < 0)
3722 return -1;
3723 sv[0] = rb_w32_open_osfhandle(pair[0], O_RDWR|O_BINARY|O_NOINHERIT);
3724 if (sv[0] == -1) {
3725 closesocket(pair[0]);
3726 closesocket(pair[1]);
3727 return -1;
3728 }
3729 sv[1] = rb_w32_open_osfhandle(pair[1], O_RDWR|O_BINARY|O_NOINHERIT);
3730 if (sv[1] == -1) {
3731 rb_w32_close(sv[0]);
3732 closesocket(pair[1]);
3733 return -1;
3734 }
3735 socklist_insert(pair[0], MAKE_SOCKDATA(af, 0));
3736 socklist_insert(pair[1], MAKE_SOCKDATA(af, 0));
3737
3738 return 0;
3739 }
3740
3741 //
3742 // Networking stubs
3743 //
3744
3745 void endhostent(void) {}
3746 void endnetent(void) {}
3747 void endprotoent(void) {}
3748 void endservent(void) {}
3749
3750 struct netent *getnetent (void) {return (struct netent *) NULL;}
3751
3752 struct netent *getnetbyaddr(long net, int type) {return (struct netent *)NULL;}
3753
3754 struct netent *getnetbyname(const char *name) {return (struct netent *)NULL;}
3755
3756 struct protoent *getprotoent (void) {return (struct protoent *) NULL;}
3757
3758 struct servent *getservent (void) {return (struct servent *) NULL;}
3759
3760 void sethostent (int stayopen) {}
3761
3762 void setnetent (int stayopen) {}
3763
3764 void setprotoent (int stayopen) {}
3765
3766 void setservent (int stayopen) {}
3767
3768 /* License: Ruby's */
3769 static int
3770 setfl(SOCKET sock, int arg)
3771 {
3772 int ret;
3773 int af = 0;
3774 int flag = 0;
3775 u_long ioctlArg;
3776
3777 socklist_lookup(sock, &flag);
3778 af = GET_FAMILY(flag);
3779 flag = GET_FLAGS(flag);
3780 if (arg & O_NONBLOCK) {
3781 flag |= O_NONBLOCK;
3782 ioctlArg = 1;
3783 }
3784 else {
3785 flag &= ~O_NONBLOCK;
3786 ioctlArg = 0;
3787 }
3788 RUBY_CRITICAL({
3789 ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
3790 if (ret == 0)
3791 socklist_insert(sock, MAKE_SOCKDATA(af, flag));
3792 else
3793 errno = map_errno(WSAGetLastError());
3794 });
3795
3796 return ret;
3797 }
3798
3799 /* License: Ruby's */
3800 static int
3801 dupfd(HANDLE hDup, char flags, int minfd)
3802 {
3803 int save_errno;
3804 int ret;
3805 int fds[32];
3806 int filled = 0;
3807
3808 do {
3809 ret = _open_osfhandle((intptr_t)hDup, flags | FOPEN);
3810 if (ret == -1) {
3811 goto close_fds_and_return;
3812 }
3813 if (ret >= minfd) {
3814 goto close_fds_and_return;
3815 }
3816 fds[filled++] = ret;
3817 } while (filled < (int)numberof(fds));
3818
3819 ret = dupfd(hDup, flags, minfd);
3820
3821 close_fds_and_return:
3822 save_errno = errno;
3823 while (filled > 0) {
3824 int fd = fds[--filled];
3825 _osfhnd(fd) = (intptr_t)INVALID_HANDLE_VALUE;
3826 close(fd);
3827 }
3828 errno = save_errno;
3829
3830 return ret;
3831 }
3832
3833 /* License: Ruby's */
3834 int
3835 fcntl(int fd, int cmd, ...)
3836 {
3837 va_list va;
3838 int arg;
3839
3840 if (cmd == F_SETFL) {
3841 SOCKET sock = TO_SOCKET(fd);
3842 if (!is_socket(sock)) {
3843 errno = EBADF;
3844 return -1;
3845 }
3846
3847 va_start(va, cmd);
3848 arg = va_arg(va, int);
3849 va_end(va);
3850 return setfl(sock, arg);
3851 }
3852 else if (cmd == F_DUPFD) {
3853 int ret;
3854 HANDLE hDup;
3855 if (!(DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
3856 GetCurrentProcess(), &hDup, 0L,
3857 !(_osfile(fd) & FNOINHERIT),
3858 DUPLICATE_SAME_ACCESS))) {
3859 errno = map_errno(GetLastError());
3860 return -1;
3861 }
3862
3863 va_start(va, cmd);
3864 arg = va_arg(va, int);
3865 va_end(va);
3866
3867 if ((ret = dupfd(hDup, _osfile(fd), arg)) == -1)
3868 CloseHandle(hDup);
3869 return ret;
3870 }
3871 else {
3872 errno = EINVAL;
3873 return -1;
3874 }
3875 }
3876
3877 #ifndef WNOHANG
3878 #define WNOHANG -1
3879 #endif
3880
3881 /* License: Ruby's */
3882 static rb_pid_t
3883 poll_child_status(struct ChildRecord *child, int *stat_loc)
3884 {
3885 DWORD exitcode;
3886 DWORD err;
3887
3888 if (!GetExitCodeProcess(child->hProcess, &exitcode)) {
3889 /* If an error occured, return immediatly. */
3890 error_exit:
3891 err = GetLastError();
3892 if (err == ERROR_INVALID_PARAMETER)
3893 errno = ECHILD;
3894 else {
3895 if (GetLastError() == ERROR_INVALID_HANDLE)
3896 errno = EINVAL;
3897 else
3898 errno = map_errno(GetLastError());
3899 }
3900 CloseChildHandle(child);
3901 return -1;
3902 }
3903 if (exitcode != STILL_ACTIVE) {
3904 rb_pid_t pid;
3905 /* If already died, wait process's real termination. */
3906 if (rb_w32_wait_events_blocking(&child->hProcess, 1, INFINITE) != WAIT_OBJECT_0) {
3907 goto error_exit;
3908 }
3909 pid = child->pid;
3910 CloseChildHandle(child);
3911 if (stat_loc) *stat_loc = exitcode << 8;
3912 return pid;
3913 }
3914 return 0;
3915 }
3916
3917 /* License: Artistic or GPL */
3918 rb_pid_t
3919 waitpid(rb_pid_t pid, int *stat_loc, int options)
3920 {
3921 DWORD timeout;
3922
3923 /* Artistic or GPL part start */
3924 if (options == WNOHANG) {
3925 timeout = 0;
3926 }
3927 else {
3928 timeout = INFINITE;
3929 }
3930 /* Artistic or GPL part end */
3931
3932 if (pid == -1) {
3933 int count = 0;
3934 int ret;
3935 HANDLE events[MAXCHILDNUM];
3936 struct ChildRecord* cause;
3937
3938 FOREACH_CHILD(child) {
3939 if (!child->pid || child->pid < 0) continue;
3940 if ((pid = poll_child_status(child, stat_loc))) return pid;
3941 events[count++] = child->hProcess;
3942 } END_FOREACH_CHILD;
3943 if (!count) {
3944 errno = ECHILD;
3945 return -1;
3946 }
3947
3948 ret = rb_w32_wait_events_blocking(events, count, timeout);
3949 if (ret == WAIT_TIMEOUT) return 0;
3950 if ((ret -= WAIT_OBJECT_0) == count) {
3951 return -1;
3952 }
3953 if (ret > count) {
3954 errno = map_errno(GetLastError());
3955 return -1;
3956 }
3957
3958 cause = FindChildSlotByHandle(events[ret]);
3959 if (!cause) {
3960 errno = ECHILD;
3961 return -1;
3962 }
3963 return poll_child_status(cause, stat_loc);
3964 }
3965 else {
3966 struct ChildRecord* child = FindChildSlot(pid);
3967 if (!child) {
3968 errno = ECHILD;
3969 return -1;
3970 }
3971
3972 while (!(pid = poll_child_status(child, stat_loc))) {
3973 /* wait... */
3974 if (rb_w32_wait_events_blocking(&child->hProcess, 1, timeout) != WAIT_OBJECT_0) {
3975 /* still active */
3976 pid = 0;
3977 break;
3978 }
3979 }
3980 }
3981
3982 return pid;
3983 }
3984
3985 #include <sys/timeb.h>
3986
3987 /* License: Ruby's */
3988 static int
3989 filetime_to_timeval(const FILETIME* ft, struct timeval *tv)
3990 {
3991 ULARGE_INTEGER tmp;
3992 unsigned LONG_LONG lt;
3993
3994 tmp.LowPart = ft->dwLowDateTime;
3995 tmp.HighPart = ft->dwHighDateTime;
3996 lt = tmp.QuadPart;
3997
3998 /* lt is now 100-nanosec intervals since 1601/01/01 00:00:00 UTC,
3999 convert it into UNIX time (since 1970/01/01 00:00:00 UTC).
4000 the first leap second is at 1972/06/30, so we doesn't need to think
4001 about it. */
4002 lt /= 10; /* to usec */
4003 lt -= (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60 * 1000 * 1000;
4004
4005 tv->tv_sec = (long)(lt / (1000 * 1000));
4006 tv->tv_usec = (long)(lt % (1000 * 1000));
4007
4008 return tv->tv_sec > 0 ? 0 : -1;
4009 }
4010
4011 /* License: Ruby's */
4012 int _cdecl
4013 gettimeofday(struct timeval *tv, struct timezone *tz)
4014 {
4015 FILETIME ft;
4016
4017 GetSystemTimeAsFileTime(&ft);
4018 filetime_to_timeval(&ft, tv);
4019
4020 return 0;
4021 }
4022
4023 /* License: Ruby's */
4024 char *
4025 rb_w32_getcwd(char *buffer, int size)
4026 {
4027 char *p = buffer;
4028 int len;
4029
4030 len = GetCurrentDirectory(0, NULL);
4031 if (!len) {
4032 errno = map_errno(GetLastError());
4033 return NULL;
4034 }
4035
4036 if (p) {
4037 if (size < len) {
4038 errno = ERANGE;
4039 return NULL;
4040 }
4041 }
4042 else {
4043 p = malloc(len);
4044 size = len;
4045 if (!p) {
4046 errno = ENOMEM;
4047 return NULL;
4048 }
4049 }
4050
4051 if (!GetCurrentDirectory(size, p)) {
4052 errno = map_errno(GetLastError());
4053 if (!buffer)
4054 free(p);
4055 return NULL;
4056 }
4057
4058 translate_char(p, '\\', '/');
4059
4060 return p;
4061 }
4062
4063 /* License: Artistic or GPL */
4064 int
4065 chown(const char *path, int owner, int group)
4066 {
4067 return 0;
4068 }
4069
4070 /* License: Artistic or GPL */
4071 int
4072 rb_w32_uchown(const char *path, int owner, int group)
4073 {
4074 return 0;
4075 }
4076
4077 /* License: Ruby's */
4078 int
4079 kill(int pid, int sig)
4080 {
4081 int ret = 0;
4082 DWORD err;
4083
4084 if (pid < 0 || pid == 0 && sig != SIGINT) {
4085 errno = EINVAL;
4086 return -1;
4087 }
4088
4089 if ((unsigned int)pid == GetCurrentProcessId() &&
4090 (sig != 0 && sig != SIGKILL)) {
4091 if ((ret = raise(sig)) != 0) {
4092 /* MSVCRT doesn't set errno... */
4093 errno = EINVAL;
4094 }
4095 return ret;
4096 }
4097
4098 switch (sig) {
4099 case 0:
4100 RUBY_CRITICAL({
4101 HANDLE hProc =
4102 OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4103 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4104 if (GetLastError() == ERROR_INVALID_PARAMETER) {
4105 errno = ESRCH;
4106 }
4107 else {
4108 errno = EPERM;
4109 }
4110 ret = -1;
4111 }
4112 else {
4113 CloseHandle(hProc);
4114 }
4115 });
4116 break;
4117
4118 case SIGINT:
4119 RUBY_CRITICAL({
4120 DWORD ctrlEvent = CTRL_C_EVENT;
4121 if (pid != 0) {
4122 /* CTRL+C signal cannot be generated for process groups.
4123 * Instead, we use CTRL+BREAK signal. */
4124 ctrlEvent = CTRL_BREAK_EVENT;
4125 }
4126 if (!GenerateConsoleCtrlEvent(ctrlEvent, (DWORD)pid)) {
4127 if ((err = GetLastError()) == 0)
4128 errno = EPERM;
4129 else
4130 errno = map_errno(GetLastError());
4131 ret = -1;
4132 }
4133 });
4134 break;
4135
4136 case SIGKILL:
4137 RUBY_CRITICAL({
4138 HANDLE hProc;
4139 struct ChildRecord* child = FindChildSlot(pid);
4140 if (child) {
4141 hProc = child->hProcess;
4142 }
4143 else {
4144 hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
4145 }
4146 if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) {
4147 if (GetLastError() == ERROR_INVALID_PARAMETER) {
4148 errno = ESRCH;
4149 }
4150 else {
4151 errno = EPERM;
4152 }
4153 ret = -1;
4154 }
4155 else {
4156 DWORD status;
4157 if (!GetExitCodeProcess(hProc, &status)) {
4158 errno = map_errno(GetLastError());
4159 ret = -1;
4160 }
4161 else if (status == STILL_ACTIVE) {
4162 if (!TerminateProcess(hProc, 0)) {
4163 errno = EPERM;
4164 ret = -1;
4165 }
4166 }
4167 else {
4168 errno = ESRCH;
4169 ret = -1;
4170 }
4171 if (!child) {
4172 CloseHandle(hProc);
4173 }
4174 }
4175 });
4176 break;
4177
4178 default:
4179 errno = EINVAL;
4180 ret = -1;
4181 break;
4182 }
4183
4184 return ret;
4185 }
4186
4187 /* License: Ruby's */
4188 static int
4189 wlink(const WCHAR *from, const WCHAR *to)
4190 {
4191 typedef BOOL (WINAPI link_func)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
4192 static link_func *pCreateHardLinkW = NULL;
4193 static int myerrno = 0;
4194
4195 if (!pCreateHardLinkW && !myerrno) {
4196 pCreateHardLinkW = (link_func *)get_proc_address("kernel32", "CreateHardLinkW", NULL);
4197 if (!pCreateHardLinkW)
4198 myerrno = ENOSYS;
4199 }
4200 if (!pCreateHardLinkW) {
4201 errno = myerrno;
4202 return -1;
4203 }
4204
4205 if (!pCreateHardLinkW(to, from, NULL)) {
4206 errno = map_errno(GetLastError());
4207 return -1;
4208 }
4209
4210 return 0;
4211 }
4212
4213 /* License: Ruby's */
4214 int
4215 rb_w32_ulink(const char *from, const char *to)
4216 {
4217 WCHAR *wfrom;
4218 WCHAR *wto;
4219 int ret;
4220
4221 if (!(wfrom = utf8_to_wstr(from, NULL)))
4222 return -1;
4223 if (!(wto = utf8_to_wstr(to, NULL))) {
4224 free(wfrom);
4225 return -1;
4226 }
4227 ret = wlink(wfrom, wto);
4228 free(wto);
4229 free(wfrom);
4230 return ret;
4231 }
4232
4233 /* License: Ruby's */
4234 int
4235 link(const char *from, const char *to)
4236 {
4237 WCHAR *wfrom;
4238 WCHAR *wto;
4239 int ret;
4240
4241 if (!(wfrom = filecp_to_wstr(from, NULL)))
4242 return -1;
4243 if (!(wto = filecp_to_wstr(to, NULL))) {
4244 free(wfrom);
4245 return -1;
4246 }
4247 ret = wlink(wfrom, wto);
4248 free(wto);
4249 free(wfrom);
4250 return ret;
4251 }
4252
4253 /* License: Ruby's */
4254 int
4255 wait(int *status)
4256 {
4257 return waitpid(-1, status, 0);
4258 }
4259
4260 /* License: Ruby's */
4261 char *
4262 rb_w32_ugetenv(const char *name)
4263 {
4264 WCHAR *wenvarea, *wenv;
4265 int len = strlen(name);
4266 char *env;
4267 int wlen;
4268
4269 if (len == 0) return NULL;
4270
4271 if (uenvarea) {
4272 free(uenvarea);
4273 uenvarea = NULL;
4274 }
4275 if (envarea) {
4276 FreeEnvironmentStrings(envarea);
4277 envarea = NULL;
4278 }
4279 wenvarea = GetEnvironmentStringsW();
4280 if (!wenvarea) {
4281 map_errno(GetLastError());
4282 return NULL;
4283 }
4284 for (wenv = wenvarea, wlen = 1; *wenv; wenv += lstrlenW(wenv) + 1)
4285 wlen += lstrlenW(wenv) + 1;
4286 uenvarea = wstr_to_mbstr(CP_UTF8, wenvarea, wlen, NULL);
4287 FreeEnvironmentStringsW(wenvarea);
4288 if (!uenvarea)
4289 return NULL;
4290
4291 for (env = uenvarea; *env; env += strlen(env) + 1)
4292 if (strncasecmp(env, name, len) == 0 && *(env + len) == '=')
4293 return env + len + 1;
4294
4295 return NULL;
4296 }
4297
4298 /* License: Ruby's */
4299 char *
4300 rb_w32_getenv(const char *name)
4301 {
4302 int len = strlen(name);
4303 char *env;
4304
4305 if (len == 0) return NULL;
4306 if (uenvarea) {
4307 free(uenvarea);
4308 uenvarea = NULL;
4309 }
4310 if (envarea) {
4311 FreeEnvironmentStrings(envarea);
4312 envarea = NULL;
4313 }
4314 envarea = GetEnvironmentStrings();
4315 if (!envarea) {
4316 map_errno(GetLastError());
4317 return NULL;
4318 }
4319
4320 for (env = envarea; *env; env += strlen(env) + 1)
4321 if (strncasecmp(env, name, len) == 0 && *(env + len) == '=')
4322 return env + len + 1;
4323
4324 return NULL;
4325 }
4326
4327 /* License: Artistic or GPL */
4328 static int
4329 wrename(const WCHAR *oldpath, const WCHAR *newpath)
4330 {
4331 int res = 0;
4332 int oldatts;
4333 int newatts;
4334
4335 oldatts = GetFileAttributesW(oldpath);
4336 newatts = GetFileAttributesW(newpath);
4337
4338 if (oldatts == -1) {
4339 errno = map_errno(GetLastError());
4340 return -1;
4341 }
4342
4343 RUBY_CRITICAL({
4344 if (newatts != -1 && newatts & FILE_ATTRIBUTE_READONLY)
4345 SetFileAttributesW(newpath, newatts & ~ FILE_ATTRIBUTE_READONLY);
4346
4347 if (!MoveFileW(oldpath, newpath))
4348 res = -1;
4349
4350 if (res) {
4351 switch (GetLastError()) {
4352 case ERROR_ALREADY_EXISTS:
4353 case ERROR_FILE_EXISTS:
4354 if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
4355 res = 0;
4356 }
4357 }
4358
4359 if (res)
4360 errno = map_errno(GetLastError());
4361 else
4362 SetFileAttributesW(newpath, oldatts);
4363 });
4364
4365 return res;
4366 }
4367
4368 /* License: Ruby's */
4369 int rb_w32_urename(const char *from, const char *to)
4370 {
4371 WCHAR *wfrom;
4372 WCHAR *wto;
4373 int ret = -1;
4374
4375 if (!(wfrom = utf8_to_wstr(from, NULL)))
4376 return -1;
4377 if (!(wto = utf8_to_wstr(to, NULL))) {
4378 free(wfrom);
4379 return -1;
4380 }
4381 ret = wrename(wfrom, wto);
4382 free(wto);
4383 free(wfrom);
4384 return ret;
4385 }
4386
4387 /* License: Ruby's */
4388 int rb_w32_rename(const char *from, const char *to)
4389 {
4390 WCHAR *wfrom;
4391 WCHAR *wto;
4392 int ret = -1;
4393
4394 if (!(wfrom = filecp_to_wstr(from, NULL)))
4395 return -1;
4396 if (!(wto = filecp_to_wstr(to, NULL))) {
4397 free(wfrom);
4398 return -1;
4399 }
4400 ret = wrename(wfrom, wto);
4401 free(wto);
4402 free(wfrom);
4403 return ret;
4404 }
4405
4406 /* License: Ruby's */
4407 static int
4408 isUNCRoot(const WCHAR *path)
4409 {
4410 if (path[0] == L'\\' && path[1] == L'\\') {
4411 const WCHAR *p = path + 2;
4412 if (p[0] == L'?' && p[1] == L'\\') {
4413 p += 2;
4414 }
4415 for (; *p; p++) {
4416 if (*p == L'\\')
4417 break;
4418 }
4419 if (p[0] && p[1]) {
4420 for (p++; *p; p++) {
4421 if (*p == L'\\')
4422 break;
4423 }
4424 if (!p[0] || !p[1] || (p[1] == L'.' && !p[2]))
4425 return 1;
4426 }
4427 }
4428 return 0;
4429 }
4430
4431 #define COPY_STAT(src, dest, size_cast) do { \
4432 (dest).st_dev = (src).st_dev; \
4433 (dest).st_ino = (src).st_ino; \
4434 (dest).st_mode = (src).st_mode; \
4435 (dest).st_nlink = (src).st_nlink; \
4436 (dest).st_uid = (src).st_uid; \
4437 (dest).st_gid = (src).st_gid; \
4438 (dest).st_rdev = (src).st_rdev; \
4439 (dest).st_size = size_cast(src).st_size; \
4440 (dest).st_atime = (src).st_atime; \
4441 (dest).st_mtime = (src).st_mtime; \
4442 (dest).st_ctime = (src).st_ctime; \
4443 } while (0)
4444
4445 static time_t filetime_to_unixtime(const FILETIME *ft);
4446
4447 #undef fstat
4448 /* License: Ruby's */
4449 int
4450 rb_w32_fstat(int fd, struct stat *st)
4451 {
4452 BY_HANDLE_FILE_INFORMATION info;
4453 int ret = fstat(fd, st);
4454
4455 if (ret) return ret;
4456 #ifdef __BORLANDC__
4457 st->st_mode &= ~(S_IWGRP | S_IWOTH);
4458 #endif
4459 if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
4460 #ifdef __BORLANDC__
4461 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
4462 st->st_mode |= S_IWUSR;
4463 }
4464 #endif
4465 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
4466 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
4467 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
4468 }
4469 return ret;
4470 }
4471
4472 /* License: Ruby's */
4473 int
4474 rb_w32_fstati64(int fd, struct stati64 *st)
4475 {
4476 BY_HANDLE_FILE_INFORMATION info;
4477 struct stat tmp;
4478 int ret = fstat(fd, &tmp);
4479
4480 if (ret) return ret;
4481 #ifdef __BORLANDC__
4482 tmp.st_mode &= ~(S_IWGRP | S_IWOTH);
4483 #endif
4484 COPY_STAT(tmp, *st, +);
4485 if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
4486 #ifdef __BORLANDC__
4487 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
4488 st->st_mode |= S_IWUSR;
4489 }
4490 #endif
4491 st->st_size = ((__int64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
4492 st->st_atime = filetime_to_unixtime(&info.ftLastAccessTime);
4493 st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
4494 st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
4495 }
4496 return ret;
4497 }
4498
4499 /* License: Ruby's */
4500 static time_t
4501 filetime_to_unixtime(const FILETIME *ft)
4502 {
4503 struct timeval tv;
4504
4505 if (filetime_to_timeval(ft, &tv) == (time_t)-1)
4506 return 0;
4507 else
4508 return tv.tv_sec;
4509 }
4510
4511 /* License: Ruby's */
4512 static unsigned
4513 fileattr_to_unixmode(DWORD attr, const WCHAR *path)
4514 {
4515 unsigned mode = 0;
4516
4517 if (attr & FILE_ATTRIBUTE_READONLY) {
4518 mode |= S_IREAD;
4519 }
4520 else {
4521 mode |= S_IREAD | S_IWRITE | S_IWUSR;
4522 }
4523
4524 if (attr & FILE_ATTRIBUTE_DIRECTORY) {
4525 mode |= S_IFDIR | S_IEXEC;
4526 }
4527 else {
4528 mode |= S_IFREG;
4529 }
4530
4531 if (path && (mode & S_IFREG)) {
4532 const WCHAR *end = path + lstrlenW(path);
4533 while (path < end) {
4534 end = CharPrevW(path, end);
4535 if (*end == L'.') {
4536 if ((_wcsicmp(end, L".bat") == 0) ||
4537 (_wcsicmp(end, L".cmd") == 0) ||
4538 (_wcsicmp(end, L".com") == 0) ||
4539 (_wcsicmp(end, L".exe") == 0)) {
4540 mode |= S_IEXEC;
4541 }
4542 break;
4543 }
4544 }
4545 }
4546
4547 mode |= (mode & 0700) >> 3;
4548 mode |= (mode & 0700) >> 6;
4549
4550 return mode;
4551 }
4552
4553 /* License: Ruby's */
4554 static int
4555 check_valid_dir(const WCHAR *path)
4556 {
4557 WIN32_FIND_DATAW fd;
4558 HANDLE fh;
4559 WCHAR full[MAX_PATH];
4560 WCHAR *dmy;
4561 WCHAR *p, *q;
4562
4563 /* GetFileAttributes() determines "..." as directory. */
4564 /* We recheck it by FindFirstFile(). */
4565 if (!(p = wcsstr(path, L"...")))
4566 return 0;
4567 q = p + wcsspn(p, L".");
4568 if ((p == path || wcschr(L":/\\", *(p - 1))) &&
4569 (!*q || wcschr(L":/\\", *q))) {
4570 errno = ENOENT;
4571 return -1;
4572 }
4573
4574 /* if the specified path is the root of a drive and the drive is empty, */
4575 /* FindFirstFile() returns INVALID_HANDLE_VALUE. */
4576 if (!GetFullPathNameW(path, sizeof(full) / sizeof(WCHAR), full, &dmy)) {
4577 errno = map_errno(GetLastError());
4578 return -1;
4579 }
4580 if (full[1] == L':' && !full[3] && GetDriveTypeW(full) != DRIVE_NO_ROOT_DIR)
4581 return 0;
4582
4583 fh = open_dir_handle(path, &fd);
4584 if (fh == INVALID_HANDLE_VALUE)
4585 return -1;
4586 FindClose(fh);
4587 return 0;
4588 }
4589
4590 /* License: Ruby's */
4591 static int
4592 winnt_stat(const WCHAR *path, struct stati64 *st)
4593 {
4594 HANDLE h;
4595 WIN32_FIND_DATAW wfd;
4596 WIN32_FILE_ATTRIBUTE_DATA wfa;
4597 const WCHAR *p = path;
4598
4599 memset(st, 0, sizeof(*st));
4600 st->st_nlink = 1;
4601
4602 if (wcsncmp(p, L"\\\\?\\", 4) == 0) p += 4;
4603 if (wcspbrk(p, L"?*")) {
4604 errno = ENOENT;
4605 return -1;
4606 }
4607 if (GetFileAttributesExW(path, GetFileExInfoStandard, (void*)&wfa)) {
4608 if (wfa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
4609 if (check_valid_dir(path)) return -1;
4610 st->st_size = 0;
4611 }
4612 else {
4613 st->st_size = ((__int64)wfa.nFileSizeHigh << 32) | wfa.nFileSizeLow;
4614 }
4615 st->st_mode = fileattr_to_unixmode(wfa.dwFileAttributes, path);
4616 st->st_atime = filetime_to_unixtime(&wfa.ftLastAccessTime);
4617 st->st_mtime = filetime_to_unixtime(&wfa.ftLastWriteTime);
4618 st->st_ctime = filetime_to_unixtime(&wfa.ftCreationTime);
4619 }
4620 else {
4621 /* GetFileAttributesEx failed; check why. */
4622 int e = GetLastError();
4623
4624 if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME)
4625 || (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) {
4626 errno = map_errno(e);
4627 return -1;
4628 }
4629
4630 /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */
4631 h = FindFirstFileW(path, &wfd);
4632 if (h != INVALID_HANDLE_VALUE) {
4633 FindClose(h);
4634 st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path);
4635 st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
4636 st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
4637 st->st_ctime = filetime_to_unixtime(&wfd.ftCreationTime);
4638 st->st_size = ((__int64)wfd.nFileSizeHigh << 32) | wfd.nFileSizeLow;
4639 }
4640 else {
4641 errno = map_errno(GetLastError());
4642 return -1;
4643 }
4644 }
4645
4646 st->st_dev = st->st_rdev = (iswalpha(path[0]) && path[1] == L':') ?
4647 towupper(path[0]) - L'A' : _getdrive() - 1;
4648
4649 return 0;
4650 }
4651
4652 /* License: Ruby's */
4653 int
4654 rb_w32_stat(const char *path, struct stat *st)
4655 {
4656 struct stati64 tmp;
4657
4658 if (rb_w32_stati64(path, &tmp)) return -1;
4659 COPY_STAT(tmp, *st, (_off_t));
4660 return 0;
4661 }
4662
4663 /* License: Ruby's */
4664 static int
4665 wstati64(const WCHAR *path, struct stati64 *st)
4666 {
4667 const WCHAR *p;
4668 WCHAR *buf1, *s, *end;
4669 int len, size;
4670 int ret;
4671 VALUE v;
4672
4673 if (!path || !st) {
4674 errno = EFAULT;
4675 return -1;
4676 }
4677 size = lstrlenW(path) + 2;
4678 buf1 = ALLOCV_N(WCHAR, v, size);
4679 for (p = path, s = buf1; *p; p++, s++) {
4680 if (*p == L'/')
4681 *s = L'\\';
4682 else
4683 *s = *p;
4684 }
4685 *s = '\0';
4686 len = s - buf1;
4687 if (!len || L'\"' == *(--s)) {
4688 errno = ENOENT;
4689 return -1;
4690 }
4691 end = buf1 + len - 1;
4692
4693 if (isUNCRoot(buf1)) {
4694 if (*end == L'.')
4695 *end = L'\0';
4696 else if (*end != L'\\')
4697 lstrcatW(buf1, L"\\");
4698 }
4699 else if (*end == L'\\' || (buf1 + 1 == end && *end == L':'))
4700 lstrcatW(buf1, L".");
4701
4702 ret = winnt_stat(buf1, st);
4703 if (ret == 0) {
4704 st->st_mode &= ~(S_IWGRP | S_IWOTH);
4705 }
4706 if (v)
4707 ALLOCV_END(v);
4708
4709 return ret;
4710 }
4711
4712 /* License: Ruby's */
4713 int
4714 rb_w32_ustati64(const char *path, struct stati64 *st)
4715 {
4716 WCHAR *wpath;
4717 int ret;
4718
4719 if (!(wpath = utf8_to_wstr(path, NULL)))
4720 return -1;
4721 ret = wstati64(wpath, st);
4722 free(wpath);
4723 return ret;
4724 }
4725
4726 /* License: Ruby's */
4727 int
4728 rb_w32_stati64(const char *path, struct stati64 *st)
4729 {
4730 WCHAR *wpath;
4731 int ret;
4732
4733 if (!(wpath = filecp_to_wstr(path, NULL)))
4734 return -1;
4735 ret = wstati64(wpath, st);
4736 free(wpath);
4737 return ret;
4738 }
4739
4740 /* License: Ruby's */
4741 int
4742 rb_w32_access(const char *path, int mode)
4743 {
4744 struct stati64 stat;
4745 if (rb_w32_stati64(path, &stat) != 0)
4746 return -1;
4747 mode <<= 6;
4748 if ((stat.st_mode & mode) != mode) {
4749 errno = EACCES;
4750 return -1;
4751 }
4752 return 0;
4753 }
4754
4755 /* License: Ruby's */
4756 int
4757 rb_w32_uaccess(const char *path, int mode)
4758 {
4759 struct stati64 stat;
4760 if (rb_w32_ustati64(path, &stat) != 0)
4761 return -1;
4762 mode <<= 6;
4763 if ((stat.st_mode & mode) != mode) {
4764 errno = EACCES;
4765 return -1;
4766 }
4767 return 0;
4768 }
4769
4770 /* License: Ruby's */
4771 static int
4772 rb_chsize(HANDLE h, off_t size)
4773 {
4774 long upos, lpos, usize, lsize;
4775 int ret = -1;
4776 DWORD e;
4777
4778 if ((lpos = SetFilePointer(h, 0, (upos = 0, &upos), SEEK_CUR)) == -1L &&
4779 (e = GetLastError())) {
4780 errno = map_errno(e);
4781 return -1;
4782 }
4783 usize = (long)(size >> 32);
4784 lsize = (long)size;
4785 if (SetFilePointer(h, lsize, &usize, SEEK_SET) == (DWORD)-1L &&
4786 (e = GetLastError())) {
4787 errno = map_errno(e);
4788 }
4789 else if (!SetEndOfFile(h)) {
4790 errno = map_errno(GetLastError());
4791 }
4792 else {
4793 ret = 0;
4794 }
4795 SetFilePointer(h, lpos, &upos, SEEK_SET);
4796 return ret;
4797 }
4798
4799 /* License: Ruby's */
4800 int
4801 rb_w32_truncate(const char *path, off_t length)
4802 {
4803 HANDLE h;
4804 int ret;
4805 h = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
4806 if (h == INVALID_HANDLE_VALUE) {
4807 errno = map_errno(GetLastError());
4808 return -1;
4809 }
4810 ret = rb_chsize(h, length);
4811 CloseHandle(h);
4812 return ret;
4813 }
4814
4815 /* License: Ruby's */
4816 int
4817 rb_w32_ftruncate(int fd, off_t length)
4818 {
4819 HANDLE h;
4820
4821 h = (HANDLE)_get_osfhandle(fd);
4822 if (h == (HANDLE)-1) return -1;
4823 return rb_chsize(h, length);
4824 }
4825
4826 #ifdef __BORLANDC__
4827 /* License: Ruby's */
4828 off_t
4829 _filelengthi64(int fd)
4830 {
4831 DWORD u, l;
4832 int e;
4833
4834 l = GetFileSize((HANDLE)_get_osfhandle(fd), &u);
4835 if (l == (DWORD)-1L && (e = GetLastError())) {
4836 errno = map_errno(e);
4837 return (off_t)-1;
4838 }
4839 return ((off_t)u << 32) | l;
4840 }
4841
4842 /* License: Ruby's */
4843 off_t
4844 _lseeki64(int fd, off_t offset, int whence)
4845 {
4846 long u, l;
4847 int e;
4848 HANDLE h = (HANDLE)_get_osfhandle(fd);
4849
4850 if (!h) {
4851 errno = EBADF;
4852 return -1;
4853 }
4854 u = (long)(offset >> 32);
4855 if ((l = SetFilePointer(h, (long)offset, &u, whence)) == -1L &&
4856 (e = GetLastError())) {
4857 errno = map_errno(e);
4858 return -1;
4859 }
4860 return ((off_t)u << 32) | l;
4861 }
4862 #endif
4863
4864 /* License: Ruby's */
4865 int
4866 fseeko(FILE *stream, off_t offset, int whence)
4867 {
4868 off_t pos;
4869 switch (whence) {
4870 case SEEK_CUR:
4871 if (fgetpos(stream, (fpos_t *)&pos))
4872 return -1;
4873 pos += offset;
4874 break;
4875 case SEEK_END:
4876 if ((pos = _filelengthi64(fileno(stream))) == (off_t)-1)
4877 return -1;
4878 pos += offset;
4879 break;
4880 default:
4881 pos = offset;
4882 break;
4883 }
4884 return fsetpos(stream, (fpos_t *)&pos);
4885 }
4886
4887 /* License: Ruby's */
4888 off_t
4889 rb_w32_ftello(FILE *stream)
4890 {
4891 off_t pos;
4892 if (fgetpos(stream, (fpos_t *)&pos)) return (off_t)-1;
4893 return pos;
4894 }
4895
4896 /* License: Ruby's */
4897 static long
4898 filetime_to_clock(FILETIME *ft)
4899 {
4900 __int64 qw = ft->dwHighDateTime;
4901 qw <<= 32;
4902 qw |= ft->dwLowDateTime;
4903 qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
4904 return (long) qw;
4905 }
4906
4907 /* License: Ruby's */
4908 int
4909 rb_w32_times(struct tms *tmbuf)
4910 {
4911 FILETIME create, exit, kernel, user;
4912
4913 if (GetProcessTimes(GetCurrentProcess(),&create, &exit, &kernel, &user)) {
4914 tmbuf->tms_utime = filetime_to_clock(&user);
4915 tmbuf->tms_stime = filetime_to_clock(&kernel);
4916 tmbuf->tms_cutime = 0;
4917 tmbuf->tms_cstime = 0;
4918 }
4919 else {
4920 tmbuf->tms_utime = clock();
4921 tmbuf->tms_stime = 0;
4922 tmbuf->tms_cutime = 0;
4923 tmbuf->tms_cstime = 0;
4924 }
4925 return 0;
4926 }
4927
4928 #define yield_once() Sleep(0)
4929 #define yield_until(condition) do yield_once(); while (!(condition))
4930
4931 /* License: Ruby's */
4932 static void
4933 catch_interrupt(void)
4934 {
4935 yield_once();
4936 RUBY_CRITICAL(rb_w32_wait_events(NULL, 0, 0));
4937 }
4938
4939 #if defined __BORLANDC__
4940 #undef read
4941 /* License: Ruby's */
4942 int
4943 read(int fd, void *buf, size_t size)
4944 {
4945 int ret = _read(fd, buf, size);
4946 if ((ret < 0) && (errno == EPIPE)) {
4947 errno = 0;
4948 ret = 0;
4949 }
4950 catch_interrupt();
4951 return ret;
4952 }
4953 #endif
4954
4955
4956 #define FILE_COUNT _cnt
4957 #define FILE_READPTR _ptr
4958
4959 #undef fgetc
4960 /* License: Ruby's */
4961 int
4962 rb_w32_getc(FILE* stream)
4963 {
4964 int c;
4965 if (enough_to_get(stream->FILE_COUNT)) {
4966 c = (unsigned char)*stream->FILE_READPTR++;
4967 }
4968 else {
4969 c = _filbuf(stream);
4970 #if defined __BORLANDC__
4971 if ((c == EOF) && (errno == EPIPE)) {
4972 clearerr(stream);
4973 }
4974 #endif
4975 catch_interrupt();
4976 }
4977 return c;
4978 }
4979
4980 #undef fputc
4981 /* License: Ruby's */
4982 int
4983 rb_w32_putc(int c, FILE* stream)
4984 {
4985 if (enough_to_put(stream->FILE_COUNT)) {
4986 c = (unsigned char)(*stream->FILE_READPTR++ = (char)c);
4987 }
4988 else {
4989 c = _flsbuf(c, stream);
4990 catch_interrupt();
4991 }
4992 return c;
4993 }
4994
4995 /* License: Ruby's */
4996 struct asynchronous_arg_t {
4997 /* output field */
4998 void* stackaddr;
4999 int errnum;
5000
5001 /* input field */
5002 uintptr_t (*func)(uintptr_t self, int argc, uintptr_t* argv);
5003 uintptr_t self;
5004 int argc;
5005 uintptr_t* argv;
5006 };
5007
5008 /* License: Ruby's */
5009 static DWORD WINAPI
5010 call_asynchronous(PVOID argp)
5011 {
5012 DWORD ret;
5013 struct asynchronous_arg_t *arg = argp;
5014 arg->stackaddr = &argp;
5015 ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
5016 arg->errnum = errno;
5017 return ret;
5018 }
5019
5020 /* License: Ruby's */
5021 uintptr_t
5022 rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self,
5023 int argc, uintptr_t* argv, uintptr_t intrval)
5024 {
5025 DWORD val;
5026 BOOL interrupted = FALSE;
5027 HANDLE thr;
5028
5029 RUBY_CRITICAL({
5030 struct asynchronous_arg_t arg;
5031
5032 arg.stackaddr = NULL;
5033 arg.errnum = 0;
5034 arg.func = func;
5035 arg.self = self;
5036 arg.argc = argc;
5037 arg.argv = argv;
5038
5039 thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);
5040
5041 if (thr) {
5042 yield_until(arg.stackaddr);
5043
5044 if (rb_w32_wait_events_blocking(&thr, 1, INFINITE) != WAIT_OBJECT_0) {
5045 interrupted = TRUE;
5046
5047 if (TerminateThread(thr, intrval)) {
5048 yield_once();
5049 }
5050 }
5051
5052 GetExitCodeThread(thr, &val);
5053 CloseHandle(thr);
5054
5055 if (interrupted) {
5056 /* must release stack of killed thread, why doesn't Windows? */
5057 MEMORY_BASIC_INFORMATION m;
5058
5059 memset(&m, 0, sizeof(m));
5060 if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
5061 Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
5062 arg.stackaddr, GetLastError()));
5063 }
5064 else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
5065 Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
5066 m.AllocationBase, GetLastError()));
5067 }
5068 errno = EINTR;
5069 }
5070 else {
5071 errno = arg.errnum;
5072 }
5073 }
5074 });
5075
5076 if (!thr) {
5077 rb_fatal("failed to launch waiter thread:%ld", GetLastError());
5078 }
5079
5080 return val;
5081 }
5082
5083 /* License: Ruby's */
5084 char **
5085 rb_w32_get_environ(void)
5086 {
5087 WCHAR *envtop, *env;
5088 char **myenvtop, **myenv;
5089 int num;
5090
5091 /*
5092 * We avoid values started with `='. If you want to deal those values,
5093 * change this function, and some functions in hash.c which recognize
5094 * `=' as delimiter or rb_w32_getenv() and ruby_setenv().
5095 * CygWin deals these values by changing first `=' to '!'. But we don't
5096 * use such trick and follow cmd.exe's way that just doesn't show these
5097 * values.
5098 *
5099 * This function returns UTF-8 strings.
5100 */
5101 envtop = GetEnvironmentStringsW();
5102 for (env = envtop, num = 0; *env; env += lstrlenW(env) + 1)
5103 if (*env != '=') num++;
5104
5105 myenvtop = (char **)malloc(sizeof(char *) * (num + 1));
5106 for (env = envtop, myenv = myenvtop; *env; env += lstrlenW(env) + 1) {
5107 if (*env != '=') {
5108 if (!(*myenv = wstr_to_utf8(env, NULL))) {
5109 break;
5110 }
5111 myenv++;
5112 }
5113 }
5114 *myenv = NULL;
5115 FreeEnvironmentStringsW(envtop);
5116
5117 return myenvtop;
5118 }
5119
5120 /* License: Ruby's */
5121 void
5122 rb_w32_free_environ(char **env)
5123 {
5124 char **t = env;
5125
5126 while (*t) free(*t++);
5127 free(env);
5128 }
5129
5130 /* License: Ruby's */
5131 rb_pid_t
5132 rb_w32_getpid(void)
5133 {
5134 return GetCurrentProcessId();
5135 }
5136
5137
5138 /* License: Ruby's */
5139 rb_pid_t
5140 rb_w32_getppid(void)
5141 {
5142 typedef long (WINAPI query_func)(HANDLE, int, void *, ULONG, ULONG *);
5143 static query_func *pNtQueryInformationProcess = NULL;
5144 rb_pid_t ppid = 0;
5145
5146 if (rb_w32_osver() >= 5) {
5147 if (!pNtQueryInformationProcess)
5148 pNtQueryInformationProcess = (query_func *)get_proc_address("ntdll.dll", "NtQueryInformationProcess", NULL);
5149 if (pNtQueryInformationProcess) {
5150 struct {
5151 long ExitStatus;
5152 void* PebBaseAddress;
5153 uintptr_t AffinityMask;
5154 uintptr_t BasePriority;
5155 uintptr_t UniqueProcessId;
5156 uintptr_t ParentProcessId;
5157 } pbi;
5158 ULONG len;
5159 long ret = pNtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &len);
5160 if (!ret) {
5161 ppid = pbi.ParentProcessId;
5162 }
5163 }
5164 }
5165
5166 return ppid;
5167 }
5168
5169 /* License: Ruby's */
5170 int
5171 rb_w32_uopen(const char *file, int oflag, ...)
5172 {
5173 WCHAR *wfile;
5174 int ret;
5175 int pmode;
5176
5177 va_list arg;
5178 va_start(arg, oflag);
5179 pmode = va_arg(arg, int);
5180 va_end(arg);
5181
5182 if (!(wfile = utf8_to_wstr(file, NULL)))
5183 return -1;
5184 ret = rb_w32_wopen(wfile, oflag, pmode);
5185 free(wfile);
5186 return ret;
5187 }
5188
5189 /* License: Ruby's */
5190 static int
5191 check_if_wdir(const WCHAR *wfile)
5192 {
5193 DWORD attr = GetFileAttributesW(wfile);
5194 if (attr == (DWORD)-1L ||
5195 !(attr & FILE_ATTRIBUTE_DIRECTORY) ||
5196 check_valid_dir(wfile)) {
5197 return FALSE;
5198 }
5199 errno = EISDIR;
5200 return TRUE;
5201 }
5202
5203 /* License: Ruby's */
5204 static int
5205 check_if_dir(const char *file)
5206 {
5207 WCHAR *wfile;
5208 int ret;
5209
5210 if (!(wfile = filecp_to_wstr(file, NULL)))
5211 return FALSE;
5212 ret = check_if_wdir(wfile);
5213 free(wfile);
5214 return ret;
5215 }
5216
5217 /* License: Ruby's */
5218 int
5219 rb_w32_open(const char *file, int oflag, ...)
5220 {
5221 WCHAR *wfile;
5222 int ret;
5223 int pmode;
5224
5225 va_list arg;
5226 va_start(arg, oflag);
5227 pmode = va_arg(arg, int);
5228 va_end(arg);
5229
5230 if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
5231 ret = _open(file, oflag, pmode);
5232 if (ret == -1 && errno == EACCES) check_if_dir(file);
5233 return ret;
5234 }
5235
5236 if (!(wfile = filecp_to_wstr(file, NULL)))
5237 return -1;
5238 ret = rb_w32_wopen(wfile, oflag, pmode);
5239 free(wfile);
5240 return ret;
5241 }
5242
5243 int
5244 rb_w32_wopen(const WCHAR *file, int oflag, ...)
5245 {
5246 char flags = 0;
5247 int fd;
5248 DWORD access;
5249 DWORD create;
5250 DWORD attr = FILE_ATTRIBUTE_NORMAL;
5251 SECURITY_ATTRIBUTES sec;
5252 HANDLE h;
5253
5254 if ((oflag & O_TEXT) || !(oflag & O_BINARY)) {
5255 va_list arg;
5256 int pmode;
5257 va_start(arg, oflag);
5258 pmode = va_arg(arg, int);
5259 va_end(arg);
5260 fd = _wopen(file, oflag, pmode);
5261 if (fd == -1 && errno == EACCES) check_if_wdir(file);
5262 return fd;
5263 }
5264
5265 sec.nLength = sizeof(sec);
5266 sec.lpSecurityDescriptor = NULL;
5267 if (oflag & O_NOINHERIT) {
5268 sec.bInheritHandle = FALSE;
5269 flags |= FNOINHERIT;
5270 }
5271 else {
5272 sec.bInheritHandle = TRUE;
5273 }
5274 oflag &= ~O_NOINHERIT;
5275
5276 /* always open with binary mode */
5277 oflag &= ~(O_BINARY | O_TEXT);
5278
5279 switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) {
5280 case O_RDWR:
5281 access = GENERIC_READ | GENERIC_WRITE;
5282 break;
5283 case O_RDONLY:
5284 access = GENERIC_READ;
5285 break;
5286 case O_WRONLY:
5287 access = GENERIC_WRITE;
5288 break;
5289 default:
5290 errno = EINVAL;
5291 return -1;
5292 }
5293 oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY);
5294
5295 switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) {
5296 case O_CREAT:
5297 create = OPEN_ALWAYS;
5298 break;
5299 case 0:
5300 case O_EXCL:
5301 create = OPEN_EXISTING;
5302 break;
5303 case O_CREAT | O_EXCL:
5304 case O_CREAT | O_EXCL | O_TRUNC:
5305 create = CREATE_NEW;
5306 break;
5307 case O_TRUNC:
5308 case O_TRUNC | O_EXCL:
5309 create = TRUNCATE_EXISTING;
5310 break;
5311 case O_CREAT | O_TRUNC:
5312 create = CREATE_ALWAYS;
5313 break;
5314 default:
5315 errno = EINVAL;
5316 return -1;
5317 }
5318 if (oflag & O_CREAT) {
5319 va_list arg;
5320 int pmode;
5321 va_start(arg, oflag);
5322 pmode = va_arg(arg, int);
5323 va_end(arg);
5324 /* TODO: we need to check umask here, but it's not exported... */
5325 if (!(pmode & S_IWRITE))
5326 attr = FILE_ATTRIBUTE_READONLY;
5327 }
5328 oflag &= ~(O_CREAT | O_EXCL | O_TRUNC);
5329
5330 if (oflag & O_TEMPORARY) {
5331 attr |= FILE_FLAG_DELETE_ON_CLOSE;
5332 access |= DELETE;
5333 }
5334 oflag &= ~O_TEMPORARY;
5335
5336 if (oflag & _O_SHORT_LIVED)
5337 attr |= FILE_ATTRIBUTE_TEMPORARY;
5338 oflag &= ~_O_SHORT_LIVED;
5339
5340 switch (oflag & (O_SEQUENTIAL | O_RANDOM)) {
5341 case 0:
5342 break;
5343 case O_SEQUENTIAL:
5344 attr |= FILE_FLAG_SEQUENTIAL_SCAN;
5345 break;
5346 case O_RANDOM:
5347 attr |= FILE_FLAG_RANDOM_ACCESS;
5348 break;
5349 default:
5350 errno = EINVAL;
5351 return -1;
5352 }
5353 oflag &= ~(O_SEQUENTIAL | O_RANDOM);
5354
5355 if (oflag & ~O_APPEND) {
5356 errno = EINVAL;
5357 return -1;
5358 }
5359
5360 /* allocate a C Runtime file handle */
5361 RUBY_CRITICAL({
5362 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5363 fd = _open_osfhandle((intptr_t)h, 0);
5364 CloseHandle(h);
5365 });
5366 if (fd == -1) {
5367 errno = EMFILE;
5368 return -1;
5369 }
5370 RUBY_CRITICAL({
5371 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
5372 _set_osfhnd(fd, (intptr_t)INVALID_HANDLE_VALUE);
5373 _set_osflags(fd, 0);
5374
5375 h = CreateFileW(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec,
5376 create, attr, NULL);
5377 if (h == INVALID_HANDLE_VALUE) {
5378 DWORD e = GetLastError();
5379 if (e != ERROR_ACCESS_DENIED || !check_if_wdir(file))
5380 errno = map_errno(e);
5381 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5382 fd = -1;
5383 goto quit;
5384 }
5385
5386 switch (GetFileType(h)) {
5387 case FILE_TYPE_CHAR:
5388 flags |= FDEV;
5389 break;
5390 case FILE_TYPE_PIPE:
5391 flags |= FPIPE;
5392 break;
5393 case FILE_TYPE_UNKNOWN:
5394 errno = map_errno(GetLastError());
5395 CloseHandle(h);
5396 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5397 fd = -1;
5398 goto quit;
5399 }
5400 if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND))
5401 flags |= FAPPEND;
5402
5403 _set_osfhnd(fd, (intptr_t)h);
5404 _osfile(fd) = flags | FOPEN;
5405
5406 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5407 quit:
5408 ;
5409 });
5410
5411 return fd;
5412 }
5413
5414 /* License: Ruby's */
5415 int
5416 rb_w32_fclose(FILE *fp)
5417 {
5418 int fd = fileno(fp);
5419 SOCKET sock = TO_SOCKET(fd);
5420 int save_errno = errno;
5421
5422 if (fflush(fp)) return -1;
5423 if (!is_socket(sock)) {
5424 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
5425 return fclose(fp);
5426 }
5427 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
5428 fclose(fp);
5429 errno = save_errno;
5430 if (closesocket(sock) == SOCKET_ERROR) {
5431 errno = map_errno(WSAGetLastError());
5432 return -1;
5433 }
5434 return 0;
5435 }
5436
5437 /* License: Ruby's */
5438 int
5439 rb_w32_pipe(int fds[2])
5440 {
5441 static DWORD serial = 0;
5442 char name[] = "\\\\.\\pipe\\ruby0000000000000000-0000000000000000";
5443 char *p;
5444 SECURITY_ATTRIBUTES sec;
5445 HANDLE hRead, hWrite, h;
5446 int fdRead, fdWrite;
5447 int ret;
5448
5449 /* if doesn't have CancelIo, use default pipe function */
5450 if (!cancel_io)
5451 return _pipe(fds, 65536L, _O_NOINHERIT);
5452
5453 p = strchr(name, '0');
5454 snprintf(p, strlen(p) + 1, "%"PRI_PIDT_PREFIX"x-%lx", rb_w32_getpid(), serial++);
5455
5456 sec.nLength = sizeof(sec);
5457 sec.lpSecurityDescriptor = NULL;
5458 sec.bInheritHandle = FALSE;
5459
5460 RUBY_CRITICAL({
5461 hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
5462 0, 2, 65536, 65536, 0, &sec);
5463 });
5464 if (hRead == INVALID_HANDLE_VALUE) {
5465 DWORD err = GetLastError();
5466 if (err == ERROR_PIPE_BUSY)
5467 errno = EMFILE;
5468 else
5469 errno = map_errno(GetLastError());
5470 return -1;
5471 }
5472
5473 RUBY_CRITICAL({
5474 hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec,
5475 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
5476 });
5477 if (hWrite == INVALID_HANDLE_VALUE) {
5478 errno = map_errno(GetLastError());
5479 CloseHandle(hRead);
5480 return -1;
5481 }
5482
5483 RUBY_CRITICAL(do {
5484 ret = 0;
5485 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5486 fdRead = _open_osfhandle((intptr_t)h, 0);
5487 CloseHandle(h);
5488 if (fdRead == -1) {
5489 errno = EMFILE;
5490 CloseHandle(hWrite);
5491 CloseHandle(hRead);
5492 ret = -1;
5493 break;
5494 }
5495
5496 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdRead)->lock)));
5497 _set_osfhnd(fdRead, (intptr_t)hRead);
5498 _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT);
5499 MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdRead)->lock)));
5500 } while (0));
5501 if (ret)
5502 return ret;
5503
5504 RUBY_CRITICAL(do {
5505 h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
5506 fdWrite = _open_osfhandle((intptr_t)h, 0);
5507 CloseHandle(h);
5508 if (fdWrite == -1) {
5509 errno = EMFILE;
5510 CloseHandle(hWrite);
5511 ret = -1;
5512 break;
5513 }
5514 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdWrite)->lock)));
5515 _set_osfhnd(fdWrite, (intptr_t)hWrite);
5516 _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT);
5517 MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdWrite)->lock)));
5518 } while (0));
5519 if (ret) {
5520 rb_w32_close(fdRead);
5521 return ret;
5522 }
5523
5524 fds[0] = fdRead;
5525 fds[1] = fdWrite;
5526
5527 return 0;
5528 }
5529
5530 /* License: Ruby's */
5531 static struct constat *
5532 constat_handle(HANDLE h)
5533 {
5534 st_data_t data;
5535 struct constat *p;
5536 if (!conlist) {
5537 conlist = st_init_numtable();
5538 }
5539 if (st_lookup(conlist, (st_data_t)h, &data)) {
5540 p = (struct constat *)data;
5541 }
5542 else {
5543 CONSOLE_SCREEN_BUFFER_INFO csbi;
5544 p = ALLOC(struct constat);
5545 p->vt100.state = constat_init;
5546 p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
5547 p->vt100.saved.X = p->vt100.saved.Y = 0;
5548 if (GetConsoleScreenBufferInfo(h, &csbi)) {
5549 p->vt100.attr = csbi.wAttributes;
5550 }
5551 st_insert(conlist, (st_data_t)h, (st_data_t)p);
5552 }
5553 return p;
5554 }
5555
5556 /* License: Ruby's */
5557 static void
5558 constat_reset(HANDLE h)
5559 {
5560 st_data_t data;
5561 struct constat *p;
5562 if (!conlist) return;
5563 if (!st_lookup(conlist, (st_data_t)h, &data)) return;
5564 p = (struct constat *)data;
5565 p->vt100.state = constat_init;
5566 }
5567
5568 /* License: Ruby's */
5569 static DWORD
5570 constat_attr(int count, const int *seq, DWORD attr, DWORD default_attr)
5571 {
5572 #define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
5573 #define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED)
5574 DWORD bold = attr & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
5575 int rev = 0;
5576
5577 if (!count) return attr;
5578 while (count-- > 0) {
5579 switch (*seq++) {
5580 case 0:
5581 attr = default_attr;
5582 rev = 0;
5583 bold = 0;
5584 break;
5585 case 1:
5586 bold |= rev ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
5587 break;
5588 case 4:
5589 #ifndef COMMON_LVB_UNDERSCORE
5590 #define COMMON_LVB_UNDERSCORE 0x8000
5591 #endif
5592 attr |= COMMON_LVB_UNDERSCORE;
5593 break;
5594 case 7:
5595 rev = 1;
5596 break;
5597
5598 case 30:
5599 attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
5600 break;
5601 case 17:
5602 case 31:
5603 attr = attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN) | FOREGROUND_RED;
5604 break;
5605 case 18:
5606 case 32:
5607 attr = attr & ~(FOREGROUND_BLUE | FOREGROUND_RED) | FOREGROUND_GREEN;
5608 break;
5609 case 19:
5610 case 33:
5611 attr = attr & ~FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
5612 break;
5613 case 20:
5614 case 34:
5615 attr = attr & ~(FOREGROUND_GREEN | FOREGROUND_RED) | FOREGROUND_BLUE;
5616 break;
5617 case 21:
5618 case 35:
5619 attr = attr & ~FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
5620 break;
5621 case 22:
5622 case 36:
5623 attr = attr & ~FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
5624 break;
5625 case 23:
5626 case 37:
5627 attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
5628 break;
5629
5630 case 40:
5631 attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
5632 break;
5633 case 41:
5634 attr = attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN) | BACKGROUND_RED;
5635 break;
5636 case 42:
5637 attr = attr & ~(BACKGROUND_BLUE | BACKGROUND_RED) | BACKGROUND_GREEN;
5638 break;
5639 case 43:
5640 attr = attr & ~BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
5641 break;
5642 case 44:
5643 attr = attr & ~(BACKGROUND_GREEN | BACKGROUND_RED) | BACKGROUND_BLUE;
5644 break;
5645 case 45:
5646 attr = attr & ~BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
5647 break;
5648 case 46:
5649 attr = attr & ~BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN;
5650 break;
5651 case 47:
5652 attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
5653 break;
5654 }
5655 }
5656 if (rev) {
5657 attr = attr & ~(FOREGROUND_MASK | BACKGROUND_MASK) |
5658 ((attr & FOREGROUND_MASK) << 4) |
5659 ((attr & BACKGROUND_MASK) >> 4);
5660 }
5661 return attr | bold;
5662 }
5663
5664 /* License: Ruby's */
5665 static void
5666 constat_apply(HANDLE handle, struct constat *s, WCHAR w)
5667 {
5668 CONSOLE_SCREEN_BUFFER_INFO csbi;
5669 const int *seq = s->vt100.seq;
5670 int count = s->vt100.state;
5671 int arg1 = 1;
5672 COORD pos;
5673 DWORD written;
5674
5675 if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
5676 if (count > 0 && seq[0] > 0) arg1 = seq[0];
5677 switch (w) {
5678 case L'm':
5679 SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr));
5680 break;
5681 case L'F':
5682 csbi.dwCursorPosition.X = 0;
5683 case L'A':
5684 csbi.dwCursorPosition.Y -= arg1;
5685 if (csbi.dwCursorPosition.Y < 0)
5686 csbi.dwCursorPosition.Y = 0;
5687 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5688 break;
5689 case L'E':
5690 csbi.dwCursorPosition.X = 0;
5691 case L'B':
5692 case L'e':
5693 csbi.dwCursorPosition.Y += arg1;
5694 if (csbi.dwCursorPosition.Y >= csbi.dwSize.Y)
5695 csbi.dwCursorPosition.Y = csbi.dwSize.Y;
5696 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5697 break;
5698 case L'C':
5699 csbi.dwCursorPosition.X += arg1;
5700 if (csbi.dwCursorPosition.X >= csbi.dwSize.X)
5701 csbi.dwCursorPosition.X = csbi.dwSize.X;
5702 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5703 break;
5704 case L'D':
5705 csbi.dwCursorPosition.X -= arg1;
5706 if (csbi.dwCursorPosition.X < 0)
5707 csbi.dwCursorPosition.X = 0;
5708 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5709 break;
5710 case L'G':
5711 case L'`':
5712 csbi.dwCursorPosition.X = (arg1 > csbi.dwSize.X ? csbi.dwSize.X : arg1) - 1;
5713 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5714 break;
5715 case L'd':
5716 csbi.dwCursorPosition.Y = (arg1 > csbi.dwSize.Y ? csbi.dwSize.Y : arg1) - 1;
5717 SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
5718 break;
5719 case L'H':
5720 case L'f':
5721 pos.Y = (arg1 > csbi.dwSize.Y ? csbi.dwSize.Y : arg1) - 1;
5722 if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
5723 pos.X = (arg1 > csbi.dwSize.X ? csbi.dwSize.X : arg1) - 1;
5724 SetConsoleCursorPosition(handle, pos);
5725 break;
5726 case L'J':
5727 switch (arg1) {
5728 case 0: /* erase after cursor */
5729 FillConsoleOutputCharacterW(handle, L' ',
5730 csbi.dwSize.X * (csbi.dwSize.Y - csbi.dwCursorPosition.Y) - csbi.dwCursorPosition.X,
5731 csbi.dwCursorPosition, &written);
5732 break;
5733 case 1: /* erase before cursor */
5734 pos.X = 0;
5735 pos.Y = csbi.dwCursorPosition.Y;
5736 FillConsoleOutputCharacterW(handle, L' ',
5737 csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X,
5738 pos, &written);
5739 break;
5740 case 2: /* erase entire line */
5741 pos.X = 0;
5742 pos.Y = 0;
5743 FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X * csbi.dwSize.Y, pos, &written);
5744 break;
5745 }
5746 break;
5747 case L'K':
5748 switch (arg1) {
5749 case 0: /* erase after cursor */
5750 FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X - csbi.dwCursorPosition.X, csbi.dwCursorPosition, &written);
5751 break;
5752 case 1: /* erase before cursor */
5753 pos.X = 0;
5754 pos.Y = csbi.dwCursorPosition.Y;
5755 FillConsoleOutputCharacterW(handle, L' ', csbi.dwCursorPosition.X, pos, &written);
5756 break;
5757 case 2: /* erase entire line */
5758 pos.X = 0;
5759 pos.Y = csbi.dwCursorPosition.Y;
5760 FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X, pos, &written);
5761 break;
5762 }
5763 break;
5764 case L's':
5765 s->vt100.saved = csbi.dwCursorPosition;
5766 break;
5767 case L'u':
5768 SetConsoleCursorPosition(handle, s->vt100.saved);
5769 break;
5770 case L'h':
5771 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
5772 CONSOLE_CURSOR_INFO cci;
5773 GetConsoleCursorInfo(handle, &cci);
5774 cci.bVisible = TRUE;
5775 SetConsoleCursorInfo(handle, &cci);
5776 }
5777 break;
5778 case L'l':
5779 if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
5780 CONSOLE_CURSOR_INFO cci;
5781 GetConsoleCursorInfo(handle, &cci);
5782 cci.bVisible = FALSE;
5783 SetConsoleCursorInfo(handle, &cci);
5784 }
5785 break;
5786 }
5787 }
5788
5789 /* License: Ruby's */
5790 static long
5791 constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
5792 {
5793 const WCHAR *ptr = *ptrp;
5794 long rest, len = *lenp;
5795 while (len-- > 0) {
5796 WCHAR wc = *ptr++;
5797 if (wc == 0x1b) {
5798 rest = *lenp - len - 1;
5799 s->vt100.state = constat_esc;
5800 }
5801 else if (s->vt100.state == constat_esc && wc == L'[') {
5802 rest = *lenp - len - 1;
5803 if (rest > 0) --rest;
5804 s->vt100.state = constat_seq;
5805 s->vt100.seq[0] = 0;
5806 }
5807 else if (s->vt100.state >= constat_seq) {
5808 if (wc >= L'0' && wc <= L'9') {
5809 if (s->vt100.state < (int)numberof(s->vt100.seq)) {
5810 int *seq = &s->vt100.seq[s->vt100.state];
5811 *seq = (*seq * 10) + (wc - L'0');
5812 }
5813 }
5814 else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
5815 s->vt100.seq[s->vt100.state++] = -1;
5816 }
5817 else {
5818 do {
5819 if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
5820 s->vt100.seq[s->vt100.state] = 0;
5821 }
5822 else {
5823 s->vt100.state = (int)numberof(s->vt100.seq);
5824 }
5825 } while (0);
5826 if (wc != L';') {
5827 constat_apply(h, s, wc);
5828 s->vt100.state = constat_init;
5829 }
5830 }
5831 rest = 0;
5832 }
5833 else {
5834 continue;
5835 }
5836 *ptrp = ptr;
5837 *lenp = len;
5838 return rest;
5839 }
5840 len = *lenp;
5841 *ptrp = ptr;
5842 *lenp = 0;
5843 return len;
5844 }
5845
5846
5847 /* License: Ruby's */
5848 int
5849 rb_w32_close(int fd)
5850 {
5851 SOCKET sock = TO_SOCKET(fd);
5852 int save_errno = errno;
5853
5854 if (!is_socket(sock)) {
5855 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
5856 constat_delete((HANDLE)sock);
5857 return _close(fd);
5858 }
5859 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
5860 socklist_delete(&sock, NULL);
5861 _close(fd);
5862 errno = save_errno;
5863 if (closesocket(sock) == SOCKET_ERROR) {
5864 errno = map_errno(WSAGetLastError());
5865 return -1;
5866 }
5867 return 0;
5868 }
5869
5870 static int
5871 setup_overlapped(OVERLAPPED *ol, int fd)
5872 {
5873 memset(ol, 0, sizeof(*ol));
5874 if (!(_osfile(fd) & (FDEV | FPIPE))) {
5875 LONG high = 0;
5876 DWORD method = _osfile(fd) & FAPPEND ? FILE_END : FILE_CURRENT;
5877 DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, method);
5878 #ifndef INVALID_SET_FILE_POINTER
5879 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
5880 #endif
5881 if (low == INVALID_SET_FILE_POINTER) {
5882 DWORD err = GetLastError();
5883 if (err != NO_ERROR) {
5884 errno = map_errno(err);
5885 return -1;
5886 }
5887 }
5888 ol->Offset = low;
5889 ol->OffsetHigh = high;
5890 }
5891 ol->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
5892 if (!ol->hEvent) {
5893 errno = map_errno(GetLastError());
5894 return -1;
5895 }
5896 return 0;
5897 }
5898
5899 static void
5900 finish_overlapped(OVERLAPPED *ol, int fd, DWORD size)
5901 {
5902 CloseHandle(ol->hEvent);
5903
5904 if (!(_osfile(fd) & (FDEV | FPIPE))) {
5905 LONG high = ol->OffsetHigh;
5906 DWORD low = ol->Offset + size;
5907 if (low < ol->Offset)
5908 ++high;
5909 SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN);
5910 }
5911 }
5912
5913 #undef read
5914 /* License: Ruby's */
5915 ssize_t
5916 rb_w32_read(int fd, void *buf, size_t size)
5917 {
5918 SOCKET sock = TO_SOCKET(fd);
5919 DWORD read;
5920 DWORD wait;
5921 DWORD err;
5922 size_t len;
5923 size_t ret;
5924 OVERLAPPED ol, *pol = NULL;
5925 BOOL isconsole;
5926 BOOL islineinput = FALSE;
5927 int start = 0;
5928
5929 if (is_socket(sock))
5930 return rb_w32_recv(fd, buf, size, 0);
5931
5932 // validate fd by using _get_osfhandle() because we cannot access _nhandle
5933 if (_get_osfhandle(fd) == -1) {
5934 return -1;
5935 }
5936
5937 if (_osfile(fd) & FTEXT) {
5938 return _read(fd, buf, size);
5939 }
5940
5941 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
5942
5943 if (!size || _osfile(fd) & FEOFLAG) {
5944 _set_osflags(fd, _osfile(fd) & ~FEOFLAG);
5945 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5946 return 0;
5947 }
5948
5949 ret = 0;
5950 isconsole = is_console(_osfhnd(fd)) && (osver.dwMajorVersion < 6 || (osver.dwMajorVersion == 6 && osver.dwMinorVersion < 2));
5951 if (isconsole) {
5952 DWORD mode;
5953 GetConsoleMode((HANDLE)_osfhnd(fd),&mode);
5954 islineinput = (mode & ENABLE_LINE_INPUT) != 0;
5955 }
5956 retry:
5957 /* get rid of console reading bug */
5958 if (isconsole) {
5959 constat_reset((HANDLE)_osfhnd(fd));
5960 if (start)
5961 len = 1;
5962 else {
5963 len = 0;
5964 start = 1;
5965 }
5966 }
5967 else
5968 len = size;
5969 size -= len;
5970
5971 /* if have cancel_io, use Overlapped I/O */
5972 if (cancel_io) {
5973 if (setup_overlapped(&ol, fd)) {
5974 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5975 return -1;
5976 }
5977
5978 pol = &ol;
5979 }
5980
5981 if (!ReadFile((HANDLE)_osfhnd(fd), buf, len, &read, pol)) {
5982 err = GetLastError();
5983 if (err != ERROR_IO_PENDING) {
5984 if (pol) CloseHandle(ol.hEvent);
5985 if (err == ERROR_ACCESS_DENIED)
5986 errno = EBADF;
5987 else if (err == ERROR_BROKEN_PIPE || err == ERROR_HANDLE_EOF) {
5988 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5989 return 0;
5990 }
5991 else
5992 errno = map_errno(err);
5993
5994 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
5995 return -1;
5996 }
5997
5998 if (pol) {
5999 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
6000 if (wait != WAIT_OBJECT_0) {
6001 if (wait == WAIT_OBJECT_0 + 1)
6002 errno = EINTR;
6003 else
6004 errno = map_errno(GetLastError());
6005 CloseHandle(ol.hEvent);
6006 cancel_io((HANDLE)_osfhnd(fd));
6007 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6008 return -1;
6009 }
6010
6011 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) &&
6012 (err = GetLastError()) != ERROR_HANDLE_EOF) {
6013 int ret = 0;
6014 if (err != ERROR_BROKEN_PIPE) {
6015 errno = map_errno(err);
6016 ret = -1;
6017 }
6018 CloseHandle(ol.hEvent);
6019 cancel_io((HANDLE)_osfhnd(fd));
6020 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6021 return ret;
6022 }
6023 }
6024 }
6025 else {
6026 err = GetLastError();
6027 errno = map_errno(err);
6028 }
6029
6030 if (pol) {
6031 finish_overlapped(&ol, fd, read);
6032 }
6033
6034 ret += read;
6035 if (read >= len) {
6036 buf = (char *)buf + read;
6037 if (err != ERROR_OPERATION_ABORTED &&
6038 !(isconsole && len == 1 && (!islineinput || *((char *)buf - 1) == '\n')) && size > 0)
6039 goto retry;
6040 }
6041 if (read == 0)
6042 _set_osflags(fd, _osfile(fd) | FEOFLAG);
6043
6044
6045 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6046
6047 return ret;
6048 }
6049
6050 #undef write
6051 /* License: Ruby's */
6052 ssize_t
6053 rb_w32_write(int fd, const void *buf, size_t size)
6054 {
6055 SOCKET sock = TO_SOCKET(fd);
6056 DWORD written;
6057 DWORD wait;
6058 DWORD err;
6059 size_t len;
6060 size_t ret;
6061 OVERLAPPED ol, *pol = NULL;
6062
6063 if (is_socket(sock))
6064 return rb_w32_send(fd, buf, size, 0);
6065
6066 // validate fd by using _get_osfhandle() because we cannot access _nhandle
6067 if (_get_osfhandle(fd) == -1) {
6068 return -1;
6069 }
6070
6071 if ((_osfile(fd) & FTEXT) &&
6072 (!(_osfile(fd) & FPIPE) || fd == fileno(stdout) || fd == fileno(stderr))) {
6073 return _write(fd, buf, size);
6074 }
6075
6076 MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock)));
6077
6078 if (!size || _osfile(fd) & FEOFLAG) {
6079 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6080 return 0;
6081 }
6082
6083 ret = 0;
6084 retry:
6085 /* get rid of console writing bug */
6086 len = (_osfile(fd) & FDEV) ? min(32 * 1024, size) : size;
6087 size -= len;
6088
6089 /* if have cancel_io, use Overlapped I/O */
6090 if (cancel_io) {
6091 if (setup_overlapped(&ol, fd)) {
6092 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6093 return -1;
6094 }
6095
6096 pol = &ol;
6097 }
6098
6099 if (!WriteFile((HANDLE)_osfhnd(fd), buf, len, &written, pol)) {
6100 err = GetLastError();
6101 if (err != ERROR_IO_PENDING) {
6102 if (pol) CloseHandle(ol.hEvent);
6103 if (err == ERROR_ACCESS_DENIED)
6104 errno = EBADF;
6105 else
6106 errno = map_errno(err);
6107
6108 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6109 return -1;
6110 }
6111
6112 if (pol) {
6113 wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE);
6114 if (wait != WAIT_OBJECT_0) {
6115 if (wait == WAIT_OBJECT_0 + 1)
6116 errno = EINTR;
6117 else
6118 errno = map_errno(GetLastError());
6119 CloseHandle(ol.hEvent);
6120 cancel_io((HANDLE)_osfhnd(fd));
6121 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6122 return -1;
6123 }
6124
6125 if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written,
6126 TRUE)) {
6127 errno = map_errno(err);
6128 CloseHandle(ol.hEvent);
6129 cancel_io((HANDLE)_osfhnd(fd));
6130 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6131 return -1;
6132 }
6133 }
6134 }
6135
6136 if (pol) {
6137 finish_overlapped(&ol, fd, written);
6138 }
6139
6140 ret += written;
6141 if (written == len) {
6142 buf = (const char *)buf + len;
6143 if (size > 0)
6144 goto retry;
6145 }
6146
6147 MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock));
6148
6149 return ret;
6150 }
6151
6152 /* License: Ruby's */
6153 long
6154 rb_w32_write_console(uintptr_t strarg, int fd)
6155 {
6156 static int disable;
6157 HANDLE handle;
6158 DWORD dwMode, reslen;
6159 VALUE str = strarg;
6160 rb_encoding *utf16 = rb_enc_find("UTF-16LE");
6161 const WCHAR *ptr, *next;
6162 struct constat *s;
6163 long len;
6164
6165 if (disable) return -1L;
6166 handle = (HANDLE)_osfhnd(fd);
6167 if (!GetConsoleMode(handle, &dwMode) ||
6168 !rb_econv_has_convpath_p(rb_enc_name(rb_enc_get(str)), "UTF-16LE"))
6169 return -1L;
6170
6171 str = rb_str_encode(str, rb_enc_from_encoding(utf16),
6172 ECONV_INVALID_REPLACE|ECONV_UNDEF_REPLACE, Qnil);
6173 ptr = (const WCHAR *)RSTRING_PTR(str);
6174 len = RSTRING_LEN(str) / sizeof(WCHAR);
6175 s = constat_handle(handle);
6176 while (len > 0) {
6177 long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
6178 if (curlen > 0) {
6179 if (!WriteConsoleW(handle, ptr, curlen, &reslen, NULL)) {
6180 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
6181 disable = TRUE;
6182 return -1L;
6183 }
6184 }
6185 ptr = next;
6186 }
6187 RB_GC_GUARD(str);
6188 return (long)reslen;
6189 }
6190
6191 /* License: Ruby's */
6192 static int
6193 unixtime_to_filetime(time_t time, FILETIME *ft)
6194 {
6195 ULARGE_INTEGER tmp;
6196
6197 tmp.QuadPart = ((LONG_LONG)time + (LONG_LONG)((1970-1601)*365.2425) * 24 * 60 * 60) * 10 * 1000 * 1000;
6198 ft->dwLowDateTime = tmp.LowPart;
6199 ft->dwHighDateTime = tmp.HighPart;
6200 return 0;
6201 }
6202
6203 /* License: Ruby's */
6204 static int
6205 wutime(const WCHAR *path, const struct utimbuf *times)
6206 {
6207 HANDLE hFile;
6208 FILETIME atime, mtime;
6209 struct stati64 stat;
6210 int ret = 0;
6211
6212 if (wstati64(path, &stat)) {
6213 return -1;
6214 }
6215
6216 if (times) {
6217 if (unixtime_to_filetime(times->actime, &atime)) {
6218 return -1;
6219 }
6220 if (unixtime_to_filetime(times->modtime, &mtime)) {
6221 return -1;
6222 }
6223 }
6224 else {
6225 GetSystemTimeAsFileTime(&atime);
6226 mtime = atime;
6227 }
6228
6229 RUBY_CRITICAL({
6230 const DWORD attr = GetFileAttributesW(path);
6231 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
6232 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
6233 hFile = CreateFileW(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
6234 FILE_FLAG_BACKUP_SEMANTICS, 0);
6235 if (hFile == INVALID_HANDLE_VALUE) {
6236 errno = map_errno(GetLastError());
6237 ret = -1;
6238 }
6239 else {
6240 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
6241 errno = map_errno(GetLastError());
6242 ret = -1;
6243 }
6244 CloseHandle(hFile);
6245 }
6246 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
6247 SetFileAttributesW(path, attr);
6248 });
6249
6250 return ret;
6251 }
6252
6253 /* License: Ruby's */
6254 int
6255 rb_w32_uutime(const char *path, const struct utimbuf *times)
6256 {
6257 WCHAR *wpath;
6258 int ret;
6259
6260 if (!(wpath = utf8_to_wstr(path, NULL)))
6261 return -1;
6262 ret = wutime(wpath, times);
6263 free(wpath);
6264 return ret;
6265 }
6266
6267 /* License: Ruby's */
6268 int
6269 rb_w32_utime(const char *path, const struct utimbuf *times)
6270 {
6271 WCHAR *wpath;
6272 int ret;
6273
6274 if (!(wpath = filecp_to_wstr(path, NULL)))
6275 return -1;
6276 ret = wutime(wpath, times);
6277 free(wpath);
6278 return ret;
6279 }
6280
6281 /* License: Ruby's */
6282 int
6283 rb_w32_uchdir(const char *path)
6284 {
6285 WCHAR *wpath;
6286 int ret;
6287
6288 if (!(wpath = utf8_to_wstr(path, NULL)))
6289 return -1;
6290 ret = _wchdir(wpath);
6291 free(wpath);
6292 return ret;
6293 }
6294
6295 /* License: Ruby's */
6296 static int
6297 wmkdir(const WCHAR *wpath, int mode)
6298 {
6299 int ret = -1;
6300
6301 RUBY_CRITICAL(do {
6302 if (CreateDirectoryW(wpath, NULL) == FALSE) {
6303 errno = map_errno(GetLastError());
6304 break;
6305 }
6306 if (_wchmod(wpath, mode) == -1) {
6307 RemoveDirectoryW(wpath);
6308 break;
6309 }
6310 ret = 0;
6311 } while (0));
6312 return ret;
6313 }
6314
6315 /* License: Ruby's */
6316 int
6317 rb_w32_umkdir(const char *path, int mode)
6318 {
6319 WCHAR *wpath;
6320 int ret;
6321
6322 if (!(wpath = utf8_to_wstr(path, NULL)))
6323 return -1;
6324 ret = wmkdir(wpath, mode);
6325 free(wpath);
6326 return ret;
6327 }
6328
6329 /* License: Ruby's */
6330 int
6331 rb_w32_mkdir(const char *path, int mode)
6332 {
6333 WCHAR *wpath;
6334 int ret;
6335
6336 if (!(wpath = filecp_to_wstr(path, NULL)))
6337 return -1;
6338 ret = wmkdir(wpath, mode);
6339 free(wpath);
6340 return ret;
6341 }
6342
6343 /* License: Ruby's */
6344 static int
6345 wrmdir(const WCHAR *wpath)
6346 {
6347 int ret = 0;
6348 RUBY_CRITICAL({
6349 const DWORD attr = GetFileAttributesW(wpath);
6350 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6351 SetFileAttributesW(wpath, attr & ~FILE_ATTRIBUTE_READONLY);
6352 }
6353 if (RemoveDirectoryW(wpath) == FALSE) {
6354 errno = map_errno(GetLastError());
6355 ret = -1;
6356 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6357 SetFileAttributesW(wpath, attr);
6358 }
6359 }
6360 });
6361 return ret;
6362 }
6363
6364 /* License: Ruby's */
6365 int
6366 rb_w32_rmdir(const char *path)
6367 {
6368 WCHAR *wpath;
6369 int ret;
6370
6371 if (!(wpath = filecp_to_wstr(path, NULL)))
6372 return -1;
6373 ret = wrmdir(wpath);
6374 free(wpath);
6375 return ret;
6376 }
6377
6378 /* License: Ruby's */
6379 int
6380 rb_w32_urmdir(const char *path)
6381 {
6382 WCHAR *wpath;
6383 int ret;
6384
6385 if (!(wpath = utf8_to_wstr(path, NULL)))
6386 return -1;
6387 ret = wrmdir(wpath);
6388 free(wpath);
6389 return ret;
6390 }
6391
6392 /* License: Ruby's */
6393 static int
6394 wunlink(const WCHAR *path)
6395 {
6396 int ret = 0;
6397 RUBY_CRITICAL({
6398 const DWORD attr = GetFileAttributesW(path);
6399 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6400 SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
6401 }
6402 if (!DeleteFileW(path)) {
6403 errno = map_errno(GetLastError());
6404 ret = -1;
6405 if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) {
6406 SetFileAttributesW(path, attr);
6407 }
6408 }
6409 });
6410 return ret;
6411 }
6412
6413 /* License: Ruby's */
6414 int
6415 rb_w32_uunlink(const char *path)
6416 {
6417 WCHAR *wpath;
6418 int ret;
6419
6420 if (!(wpath = utf8_to_wstr(path, NULL)))
6421 return -1;
6422 ret = wunlink(wpath);
6423 free(wpath);
6424 return ret;
6425 }
6426
6427 /* License: Ruby's */
6428 int
6429 rb_w32_unlink(const char *path)
6430 {
6431 WCHAR *wpath;
6432 int ret;
6433
6434 if (!(wpath = filecp_to_wstr(path, NULL)))
6435 return -1;
6436 ret = wunlink(wpath);
6437 free(wpath);
6438 return ret;
6439 }
6440
6441 /* License: Ruby's */
6442 int
6443 rb_w32_uchmod(const char *path, int mode)
6444 {
6445 WCHAR *wpath;
6446 int ret;
6447
6448 if (!(wpath = utf8_to_wstr(path, NULL)))
6449 return -1;
6450 ret = _wchmod(wpath, mode);
6451 free(wpath);
6452 return ret;
6453 }
6454
6455 #if !defined(__BORLANDC__)
6456 /* License: Ruby's */
6457 int
6458 rb_w32_isatty(int fd)
6459 {
6460 DWORD mode;
6461
6462 // validate fd by using _get_osfhandle() because we cannot access _nhandle
6463 if (_get_osfhandle(fd) == -1) {
6464 return 0;
6465 }
6466 if (!GetConsoleMode((HANDLE)_osfhnd(fd), &mode)) {
6467 errno = ENOTTY;
6468 return 0;
6469 }
6470 return 1;
6471 }
6472 #endif
6473
6474 //
6475 // Fix bcc32's stdio bug
6476 //
6477
6478 #ifdef __BORLANDC__
6479 /* License: Ruby's */
6480 static int
6481 too_many_files(void)
6482 {
6483 FILE *f;
6484 for (f = _streams; f < _streams + _nfile; f++) {
6485 if (f->fd < 0) return 0;
6486 }
6487 return 1;
6488 }
6489
6490 #undef fopen
6491 /* License: Ruby's */
6492 FILE *
6493 rb_w32_fopen(const char *path, const char *mode)
6494 {
6495 FILE *f = (errno = 0, fopen(path, mode));
6496 if (f == NULL && errno == 0) {
6497 if (too_many_files())
6498 errno = EMFILE;
6499 }
6500 return f;
6501 }
6502
6503 /* License: Ruby's */
6504 FILE *
6505 rb_w32_fdopen(int handle, const char *type)
6506 {
6507 FILE *f = (errno = 0, _fdopen(handle, (char *)type));
6508 if (f == NULL && errno == 0) {
6509 if (handle < 0)
6510 errno = EBADF;
6511 else if (too_many_files())
6512 errno = EMFILE;
6513 }
6514 return f;
6515 }
6516
6517 /* License: Ruby's */
6518 FILE *
6519 rb_w32_fsopen(const char *path, const char *mode, int shflags)
6520 {
6521 FILE *f = (errno = 0, _fsopen(path, mode, shflags));
6522 if (f == NULL && errno == 0) {
6523 if (too_many_files())
6524 errno = EMFILE;
6525 }
6526 return f;
6527 }
6528 #endif
6529
6530 #if defined(_MSC_VER) && RUBY_MSVCRT_VERSION <= 60
6531 extern long _ftol(double);
6532 /* License: Ruby's */
6533 long
6534 _ftol2(double d)
6535 {
6536 return _ftol(d);
6537 }
6538
6539 /* License: Ruby's */
6540 long
6541 _ftol2_sse(double d)
6542 {
6543 return _ftol(d);
6544 }
6545 #endif
6546
6547 #ifndef signbit
6548 /* License: Ruby's */
6549 int
6550 signbit(double x)
6551 {
6552 int *ip = (int *)(&x + 1) - 1;
6553 return *ip < 0;
6554 }
6555 #endif
6556
6557 /* License: Ruby's */
6558 const char * WSAAPI
6559 rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
6560 {
6561 typedef char *(WSAAPI inet_ntop_t)(int, void *, char *, size_t);
6562 inet_ntop_t *pInetNtop;
6563 pInetNtop = (inet_ntop_t *)get_proc_address("ws2_32", "inet_ntop", NULL);
6564 if (pInetNtop) {
6565 return pInetNtop(af, (void *)addr, numaddr, numaddr_len);
6566 }
6567 else {
6568 struct in_addr in;
6569 memcpy(&in.s_addr, addr, sizeof(in.s_addr));
6570 snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
6571 }
6572 return numaddr;
6573 }
6574
6575 /* License: Ruby's */
6576 char
6577 rb_w32_fd_is_text(int fd)
6578 {
6579 return _osfile(fd) & FTEXT;
6580 }
6581
6582 #if RUBY_MSVCRT_VERSION < 80 && !defined(__MINGW64__)
6583 /* License: Ruby's */
6584 static int
6585 unixtime_to_systemtime(const time_t t, SYSTEMTIME *st)
6586 {
6587 FILETIME ft;
6588 if (unixtime_to_filetime(t, &ft)) return -1;
6589 if (!FileTimeToSystemTime(&ft, st)) return -1;
6590 return 0;
6591 }
6592
6593 /* License: Ruby's */
6594 static void
6595 systemtime_to_tm(const SYSTEMTIME *st, struct tm *t)
6596 {
6597 int y = st->wYear, m = st->wMonth, d = st->wDay;
6598 t->tm_sec = st->wSecond;
6599 t->tm_min = st->wMinute;
6600 t->tm_hour = st->wHour;
6601 t->tm_mday = st->wDay;
6602 t->tm_mon = st->wMonth - 1;
6603 t->tm_year = y - 1900;
6604 t->tm_wday = st->wDayOfWeek;
6605 switch (m) {
6606 case 1:
6607 break;
6608 case 2:
6609 d += 31;
6610 break;
6611 default:
6612 d += 31 + 28 + (!(y % 4) && ((y % 100) || !(y % 400)));
6613 d += ((m - 3) * 153 + 2) / 5;
6614 break;
6615 }
6616 t->tm_yday = d - 1;
6617 }
6618
6619 /* License: Ruby's */
6620 static int
6621 systemtime_to_localtime(TIME_ZONE_INFORMATION *tz, SYSTEMTIME *gst, SYSTEMTIME *lst)
6622 {
6623 TIME_ZONE_INFORMATION stdtz;
6624 SYSTEMTIME sst;
6625
6626 if (!SystemTimeToTzSpecificLocalTime(tz, gst, lst)) return -1;
6627 if (!tz) {
6628 GetTimeZoneInformation(&stdtz);
6629 tz = &stdtz;
6630 }
6631 if (tz->StandardBias == tz->DaylightBias) return 0;
6632 if (!tz->StandardDate.wMonth) return 0;
6633 if (!tz->DaylightDate.wMonth) return 0;
6634 if (tz != &stdtz) stdtz = *tz;
6635
6636 stdtz.StandardDate.wMonth = stdtz.DaylightDate.wMonth = 0;
6637 if (!SystemTimeToTzSpecificLocalTime(&stdtz, gst, &sst)) return 0;
6638 if (lst->wMinute == sst.wMinute && lst->wHour == sst.wHour)
6639 return 0;
6640 return 1;
6641 }
6642 #endif
6643
6644 #ifdef __MINGW64__
6645 # ifndef MINGW_HAS_SECURE_API
6646 _CRTIMP errno_t __cdecl _gmtime64_s(struct tm* tm, const __time64_t *time);
6647 _CRTIMP errno_t __cdecl _localtime64_s(struct tm* tm, const __time64_t *time);
6648 # endif
6649 # define gmtime_s _gmtime64_s
6650 # define localtime_s _localtime64_s
6651 #endif
6652
6653 /* License: Ruby's */
6654 struct tm *
6655 gmtime_r(const time_t *tp, struct tm *rp)
6656 {
6657 int e = EINVAL;
6658 if (!tp || !rp) {
6659 error:
6660 errno = e;
6661 return NULL;
6662 }
6663 #if RUBY_MSVCRT_VERSION >= 80 || defined(__MINGW64__)
6664 e = gmtime_s(rp, tp);
6665 if (e != 0) goto error;
6666 #else
6667 {
6668 SYSTEMTIME st;
6669 if (unixtime_to_systemtime(*tp, &st)) goto error;
6670 rp->tm_isdst = 0;
6671 systemtime_to_tm(&st, rp);
6672 }
6673 #endif
6674 return rp;
6675 }
6676
6677 /* License: Ruby's */
6678 struct tm *
6679 localtime_r(const time_t *tp, struct tm *rp)
6680 {
6681 int e = EINVAL;
6682 if (!tp || !rp) {
6683 error:
6684 errno = e;
6685 return NULL;
6686 }
6687 #if RUBY_MSVCRT_VERSION >= 80 || defined(__MINGW64__)
6688 e = localtime_s(rp, tp);
6689 if (e) goto error;
6690 #else
6691 {
6692 SYSTEMTIME gst, lst;
6693 if (unixtime_to_systemtime(*tp, &gst)) goto error;
6694 rp->tm_isdst = systemtime_to_localtime(NULL, &gst, &lst);
6695 systemtime_to_tm(&lst, rp);
6696 }
6697 #endif
6698 return rp;
6699 }
6700
6701 /* License: Ruby's */
6702 int
6703 rb_w32_wrap_io_handle(HANDLE h, int flags)
6704 {
6705 BOOL tmp;
6706 int len = sizeof(tmp);
6707 int r = getsockopt((SOCKET)h, SOL_SOCKET, SO_DEBUG, (char *)&tmp, &len);
6708 if (r != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
6709 int f = 0;
6710 if (flags & O_NONBLOCK) {
6711 flags &= ~O_NONBLOCK;
6712 f = O_NONBLOCK;
6713 }
6714 socklist_insert((SOCKET)h, f);
6715 }
6716 else if (flags & O_NONBLOCK) {
6717 errno = EINVAL;
6718 return -1;
6719 }
6720 return rb_w32_open_osfhandle((intptr_t)h, flags);
6721 }
6722
6723 /* License: Ruby's */
6724 int
6725 rb_w32_unwrap_io_handle(int fd)
6726 {
6727 SOCKET sock = TO_SOCKET(fd);
6728 _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
6729 if (!is_socket(sock)) {
6730 UnlockFile((HANDLE)sock, 0, 0, LK_LEN, LK_LEN);
6731 constat_delete((HANDLE)sock);
6732 }
6733 else {
6734 socklist_delete(&sock, NULL);
6735 }
6736 return _close(fd);
6737 }
6738