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