Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
file.c
Go to the documentation of this file.
1#if defined(__MINGW32__)
2/* before stdio.h in ruby/define.h */
3# define MINGW_HAS_SECURE_API 1
4#endif
5#include "ruby/ruby.h"
6#include "ruby/encoding.h"
7#include "internal.h"
8#include <winbase.h>
9#include <wchar.h>
10#include <shlwapi.h>
11#include "win32/file.h"
12
13#ifndef INVALID_FILE_ATTRIBUTES
14# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
15#endif
16
17/* cache 'encoding name' => 'code page' into a hash */
18static struct code_page_table {
19 USHORT *table;
20 unsigned int count;
21} rb_code_page;
22
23#define IS_DIR_SEPARATOR_P(c) (c == L'\\' || c == L'/')
24#define IS_DIR_UNC_P(c) (IS_DIR_SEPARATOR_P(c[0]) && IS_DIR_SEPARATOR_P(c[1]))
25static int
26IS_ABSOLUTE_PATH_P(const WCHAR *path, size_t len)
27{
28 if (len < 2) return FALSE;
29 if (ISALPHA(path[0]))
30 return len > 2 && path[1] == L':' && IS_DIR_SEPARATOR_P(path[2]);
31 else
32 return IS_DIR_UNC_P(path);
33}
34
35/* MultiByteToWideChar() doesn't work with code page 51932 */
36#define INVALID_CODE_PAGE 51932
37#define PATH_BUFFER_SIZE MAX_PATH * 2
38
39#define insecure_obj_p(obj, level) ((level) > 0 && OBJ_TAINTED(obj))
40
41/* defined in win32/win32.c */
42#define system_code_page rb_w32_filecp
43#define mbstr_to_wstr rb_w32_mbstr_to_wstr
44#define wstr_to_mbstr rb_w32_wstr_to_mbstr
45
46static inline void
47replace_wchar(wchar_t *s, int find, int replace)
48{
49 while (*s != 0) {
50 if (*s == find)
51 *s = replace;
52 s++;
53 }
54}
55
56/* Remove trailing invalid ':$DATA' of the path. */
57static inline size_t
58remove_invalid_alternative_data(wchar_t *wfullpath, size_t size)
59{
60 static const wchar_t prime[] = L":$DATA";
61 enum { prime_len = (sizeof(prime) / sizeof(wchar_t)) -1 };
62
63 if (size <= prime_len || _wcsnicmp(wfullpath + size - prime_len, prime, prime_len) != 0)
64 return size;
65
66 /* alias of stream */
67 /* get rid of a bug of x64 VC++ */
68 if (wfullpath[size - (prime_len + 1)] == ':') {
69 /* remove trailing '::$DATA' */
70 size -= prime_len + 1; /* prime */
71 wfullpath[size] = L'\0';
72 }
73 else {
74 /* remove trailing ':$DATA' of paths like '/aa:a:$DATA' */
75 wchar_t *pos = wfullpath + size - (prime_len + 1);
76 while (!IS_DIR_SEPARATOR_P(*pos) && pos != wfullpath) {
77 if (*pos == L':') {
78 size -= prime_len; /* alternative */
79 wfullpath[size] = L'\0';
80 break;
81 }
82 pos--;
83 }
84 }
85 return size;
86}
87
89
90static int
91code_page_i(st_data_t name, st_data_t idx, st_data_t arg)
92{
93 const char *n = (const char *)name;
94 if (strncmp("CP", n, 2) == 0) {
95 int code_page = atoi(n + 2);
96 if (code_page != 0) {
97 struct code_page_table *cp = (struct code_page_table *)arg;
98 unsigned int count = cp->count;
99 USHORT *table = cp->table;
100 if (count <= idx) {
101 unsigned int i = count;
102 count = (((idx + 4) & ~31) | 28);
103 table = realloc(table, count * sizeof(*table));
104 if (!table) return ST_CONTINUE;
105 cp->count = count;
106 cp->table = table;
107 while (i < count) table[i++] = INVALID_CODE_PAGE;
108 }
109 table[idx] = (USHORT)code_page;
110 }
111 }
112 return ST_CONTINUE;
113}
114
115/*
116 Return code page number of the encoding.
117 Cache code page into a hash for performance since finding the code page in
118 Encoding#names is slow.
119*/
120static UINT
121code_page(rb_encoding *enc)
122{
123 int enc_idx;
124
125 if (!enc)
126 return system_code_page();
127
128 enc_idx = rb_enc_to_index(enc);
129
130 /* map US-ASCII and ASCII-8bit as code page 1252 (us-ascii) */
131 if (enc_idx == rb_usascii_encindex() || enc_idx == rb_ascii8bit_encindex()) {
132 return 1252;
133 }
134 if (enc_idx == rb_utf8_encindex()) {
135 return CP_UTF8;
136 }
137
138 if (0 <= enc_idx && (unsigned int)enc_idx < rb_code_page.count)
139 return rb_code_page.table[enc_idx];
140
141 return INVALID_CODE_PAGE;
142}
143
144#define fix_string_encoding(str, encoding) rb_str_conv_enc((str), (encoding), rb_utf8_encoding())
145
146/*
147 Replace the last part of the path to long name.
148 We try to avoid to call FindFirstFileW() since it takes long time.
149*/
150static inline size_t
151replace_to_long_name(wchar_t **wfullpath, size_t size, size_t buffer_size)
152{
153 WIN32_FIND_DATAW find_data;
154 HANDLE find_handle;
155
156 /*
157 Skip long name conversion if the path is already long name.
158 Short name is 8.3 format.
159 http://en.wikipedia.org/wiki/8.3_filename
160 This check can be skipped for directory components that have file
161 extensions longer than 3 characters, or total lengths longer than
162 12 characters.
163 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx
164 */
165 size_t const max_short_name_size = 8 + 1 + 3;
166 size_t const max_extension_size = 3;
167 size_t path_len = 1, extension_len = 0;
168 wchar_t *pos = *wfullpath;
169
170 if (size == 3 && pos[1] == L':' && pos[2] == L'\\' && pos[3] == L'\0') {
171 /* root path doesn't need short name expansion */
172 return size;
173 }
174
175 /* skip long name conversion if path contains wildcard characters */
176 if (wcspbrk(pos, L"*?")) {
177 return size;
178 }
179
180 pos = *wfullpath + size - 1;
181 while (!IS_DIR_SEPARATOR_P(*pos) && pos != *wfullpath) {
182 if (!extension_len && *pos == L'.') {
183 extension_len = path_len - 1;
184 }
185 if (path_len > max_short_name_size || extension_len > max_extension_size) {
186 return size;
187 }
188 path_len++;
189 pos--;
190 }
191
192 if ((pos >= *wfullpath + 2) &&
193 (*wfullpath)[0] == L'\\' && (*wfullpath)[1] == L'\\') {
194 /* UNC path: no short file name, and needs Network Share
195 * Management functions instead of FindFirstFile. */
196 if (pos == *wfullpath + 2) {
197 /* //host only */
198 return size;
199 }
200 if (!wmemchr(*wfullpath + 2, L'\\', pos - *wfullpath - 2)) {
201 /* //host/share only */
202 return size;
203 }
204 }
205
206 find_handle = FindFirstFileW(*wfullpath, &find_data);
207 if (find_handle != INVALID_HANDLE_VALUE) {
208 size_t trail_pos = pos - *wfullpath + IS_DIR_SEPARATOR_P(*pos);
209 size_t file_len = wcslen(find_data.cFileName);
210 size_t oldsize = size;
211
212 FindClose(find_handle);
213 size = trail_pos + file_len;
214 if (size > (buffer_size ? buffer_size-1 : oldsize)) {
215 wchar_t *buf = ALLOC_N(wchar_t, (size + 1));
216 wcsncpy(buf, *wfullpath, trail_pos);
217 if (!buffer_size)
218 xfree(*wfullpath);
219 *wfullpath = buf;
220 }
221 wcsncpy(*wfullpath + trail_pos, find_data.cFileName, file_len + 1);
222 }
223 return size;
224}
225
226static inline size_t
227user_length_in_path(const wchar_t *wuser, size_t len)
228{
229 size_t i;
230
231 for (i = 0; i < len && !IS_DIR_SEPARATOR_P(wuser[i]); i++)
232 ;
233
234 return i;
235}
236
237static VALUE
238append_wstr(VALUE dst, const WCHAR *ws, ssize_t len, UINT cp, rb_encoding *enc)
239{
240 long olen, nlen = (long)len;
241
242 if (cp != INVALID_CODE_PAGE) {
243 if (len == -1) len = lstrlenW(ws);
244 nlen = WideCharToMultiByte(cp, 0, ws, len, NULL, 0, NULL, NULL);
245 olen = RSTRING_LEN(dst);
246 rb_str_modify_expand(dst, nlen);
247 WideCharToMultiByte(cp, 0, ws, len, RSTRING_PTR(dst) + olen, nlen, NULL, NULL);
248 rb_enc_associate(dst, enc);
249 rb_str_set_len(dst, olen + nlen);
250 }
251 else {
252 const int replaceflags = ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE;
253 char *utf8str = wstr_to_mbstr(CP_UTF8, ws, (int)len, &nlen);
254 rb_econv_t *ec = rb_econv_open("UTF-8", rb_enc_name(enc), replaceflags);
255 dst = rb_econv_append(ec, utf8str, nlen, dst, replaceflags);
256 rb_econv_close(ec);
257 free(utf8str);
258 }
259 return dst;
260}
261
262VALUE
264{
265 WCHAR *dir = rb_w32_home_dir();
266 if (!dir) {
267 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
268 }
269 append_wstr(result, dir, -1,
271 xfree(dir);
272 return result;
273}
274
275VALUE
276rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
277{
278 size_t size = 0, whome_len = 0;
279 size_t buffer_len = 0;
280 long wpath_len = 0, wdir_len = 0;
281 wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL;
282 wchar_t *wdir = NULL, *wdir_pos = NULL;
283 wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL;
284 UINT path_cp, cp;
285 VALUE path = fname, dir = dname;
286 wchar_t wfullpath_buffer[PATH_BUFFER_SIZE];
287 wchar_t path_drive = L'\0', dir_drive = L'\0';
288 int ignore_dir = 0;
289 rb_encoding *path_encoding;
290 int tainted = 0;
291
292 /* tainted if path is tainted */
293 tainted = OBJ_TAINTED(path);
294
295 /* get path encoding */
296 if (NIL_P(dir)) {
297 path_encoding = rb_enc_get(path);
298 }
299 else {
300 path_encoding = rb_enc_check(path, dir);
301 }
302
303 cp = path_cp = code_page(path_encoding);
304
305 /* workaround invalid codepage */
306 if (path_cp == INVALID_CODE_PAGE) {
307 cp = CP_UTF8;
308 if (!NIL_P(path)) {
309 path = fix_string_encoding(path, path_encoding);
310 }
311 }
312
313 /* convert char * to wchar_t */
314 if (!NIL_P(path)) {
315 const long path_len = RSTRING_LEN(path);
316#if SIZEOF_INT < SIZEOF_LONG
317 if ((long)(int)path_len != path_len) {
318 rb_raise(rb_eRangeError, "path (%ld bytes) is too long",
319 path_len);
320 }
321#endif
322 wpath = mbstr_to_wstr(cp, RSTRING_PTR(path), path_len, &wpath_len);
323 wpath_pos = wpath;
324 }
325
326 /* determine if we need the user's home directory */
327 /* expand '~' only if NOT rb_file_absolute_path() where `abs_mode` is 1 */
328 if (abs_mode == 0 && wpath_len > 0 && wpath_pos[0] == L'~' &&
329 (wpath_len == 1 || IS_DIR_SEPARATOR_P(wpath_pos[1]))) {
330 /* tainted if expanding '~' */
331 tainted = 1;
332
333 whome = rb_w32_home_dir();
334 if (whome == NULL) {
335 free(wpath);
336 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
337 }
338 whome_len = wcslen(whome);
339
340 if (!IS_ABSOLUTE_PATH_P(whome, whome_len)) {
341 free(wpath);
342 xfree(whome);
343 rb_raise(rb_eArgError, "non-absolute home");
344 }
345
346 if (path_cp == INVALID_CODE_PAGE || rb_enc_str_asciionly_p(path)) {
347 /* use filesystem encoding if expanding home dir */
348 path_encoding = rb_filesystem_encoding();
349 cp = path_cp = system_code_page();
350 }
351
352 /* ignores dir since we are expanding home */
353 ignore_dir = 1;
354
355 /* exclude ~ from the result */
356 wpath_pos++;
357 wpath_len--;
358
359 /* exclude separator if present */
360 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
361 wpath_pos++;
362 wpath_len--;
363 }
364 }
365 else if (wpath_len >= 2 && wpath_pos[1] == L':') {
366 if (wpath_len >= 3 && IS_DIR_SEPARATOR_P(wpath_pos[2])) {
367 /* ignore dir since path contains a drive letter and a root slash */
368 ignore_dir = 1;
369 }
370 else {
371 /* determine if we ignore dir or not later */
372 path_drive = wpath_pos[0];
373 wpath_pos += 2;
374 wpath_len -= 2;
375 }
376 }
377 else if (abs_mode == 0 && wpath_len >= 2 && wpath_pos[0] == L'~') {
378 result = rb_str_new_cstr("can't find user ");
379 result = append_wstr(result, wpath_pos + 1, user_length_in_path(wpath_pos + 1, wpath_len - 1),
380 path_cp, path_encoding);
381
382 if (wpath)
383 free(wpath);
384
386 }
387
388 /* convert dir */
389 if (!ignore_dir && !NIL_P(dir)) {
390 /* fix string encoding */
391 if (path_cp == INVALID_CODE_PAGE) {
392 dir = fix_string_encoding(dir, path_encoding);
393 }
394
395 /* convert char * to wchar_t */
396 if (!NIL_P(dir)) {
397 const long dir_len = RSTRING_LEN(dir);
398#if SIZEOF_INT < SIZEOF_LONG
399 if ((long)(int)dir_len != dir_len) {
400 if (wpath) free(wpath);
401 rb_raise(rb_eRangeError, "base directory (%ld bytes) is too long",
402 dir_len);
403 }
404#endif
405 wdir = mbstr_to_wstr(cp, RSTRING_PTR(dir), dir_len, &wdir_len);
406 wdir_pos = wdir;
407 }
408
409 if (abs_mode == 0 && wdir_len > 0 && wdir_pos[0] == L'~' &&
410 (wdir_len == 1 || IS_DIR_SEPARATOR_P(wdir_pos[1]))) {
411 /* tainted if expanding '~' */
412 tainted = 1;
413
414 whome = rb_w32_home_dir();
415 if (whome == NULL) {
416 free(wpath);
417 free(wdir);
418 rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
419 }
420 whome_len = wcslen(whome);
421
422 if (!IS_ABSOLUTE_PATH_P(whome, whome_len)) {
423 free(wpath);
424 free(wdir);
425 xfree(whome);
426 rb_raise(rb_eArgError, "non-absolute home");
427 }
428
429 /* exclude ~ from the result */
430 wdir_pos++;
431 wdir_len--;
432
433 /* exclude separator if present */
434 if (wdir_len && IS_DIR_SEPARATOR_P(wdir_pos[0])) {
435 wdir_pos++;
436 wdir_len--;
437 }
438 }
439 else if (wdir_len >= 2 && wdir[1] == L':') {
440 dir_drive = wdir[0];
441 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
442 wdir_len = 2;
443 }
444 }
445 else if (wdir_len >= 2 && IS_DIR_UNC_P(wdir)) {
446 /* UNC path */
447 if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
448 /* cut the UNC path tail to '//host/share' */
449 long separators = 0;
450 long pos = 2;
451 while (pos < wdir_len && separators < 2) {
452 if (IS_DIR_SEPARATOR_P(wdir[pos])) {
453 separators++;
454 }
455 pos++;
456 }
457 if (separators == 2)
458 wdir_len = pos - 1;
459 }
460 }
461 else if (abs_mode == 0 && wdir_len >= 2 && wdir_pos[0] == L'~') {
462 result = rb_str_new_cstr("can't find user ");
463 result = append_wstr(result, wdir_pos + 1, user_length_in_path(wdir_pos + 1, wdir_len - 1),
464 path_cp, path_encoding);
465 if (wpath)
466 free(wpath);
467
468 if (wdir)
469 free(wdir);
470
472 }
473 }
474
475 /* determine if we ignore dir or not */
476 if (!ignore_dir && path_drive && dir_drive) {
477 if (towupper(path_drive) != towupper(dir_drive)) {
478 /* ignore dir since path drive is different from dir drive */
479 ignore_dir = 1;
480 wdir_len = 0;
481 dir_drive = 0;
482 }
483 }
484
485 if (!ignore_dir && wpath_len >= 2 && IS_DIR_UNC_P(wpath)) {
486 /* ignore dir since path has UNC root */
487 ignore_dir = 1;
488 wdir_len = 0;
489 }
490 else if (!ignore_dir && wpath_len >= 1 && IS_DIR_SEPARATOR_P(wpath[0]) &&
491 !dir_drive && !(wdir_len >= 2 && IS_DIR_UNC_P(wdir))) {
492 /* ignore dir since path has root slash and dir doesn't have drive or UNC root */
493 ignore_dir = 1;
494 wdir_len = 0;
495 }
496
497 buffer_len = wpath_len + 1 + wdir_len + 1 + whome_len + 1;
498
499 buffer = buffer_pos = ALLOC_N(wchar_t, (buffer_len + 1));
500
501 /* add home */
502 if (whome_len) {
503 wcsncpy(buffer_pos, whome, whome_len);
504 buffer_pos += whome_len;
505 }
506
507 /* Add separator if required */
508 if (whome_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) {
509 buffer_pos[0] = L'\\';
510 buffer_pos++;
511 }
512 else if (!dir_drive && path_drive) {
513 *buffer_pos++ = path_drive;
514 *buffer_pos++ = L':';
515 }
516
517 if (wdir_len) {
518 /* tainted if dir is used and dir is tainted */
519 if (!tainted && OBJ_TAINTED(dir))
520 tainted = 1;
521
522 wcsncpy(buffer_pos, wdir_pos, wdir_len);
523 buffer_pos += wdir_len;
524 }
525
526 /* add separator if required */
527 if (wdir_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) {
528 buffer_pos[0] = L'\\';
529 buffer_pos++;
530 }
531
532 /* now deal with path */
533 if (wpath_len) {
534 wcsncpy(buffer_pos, wpath_pos, wpath_len);
535 buffer_pos += wpath_len;
536 }
537
538 /* GetFullPathNameW requires at least "." to determine current directory */
539 if (wpath_len == 0) {
540 buffer_pos[0] = L'.';
541 buffer_pos++;
542 }
543
544 /* Ensure buffer is NULL terminated */
545 buffer_pos[0] = L'\0';
546
547 /* tainted if path is relative */
548 if (!tainted && !IS_ABSOLUTE_PATH_P(buffer, buffer_len))
549 tainted = 1;
550
551 /* FIXME: Make this more robust */
552 /* Determine require buffer size */
553 size = GetFullPathNameW(buffer, PATH_BUFFER_SIZE, wfullpath_buffer, NULL);
554 if (size > PATH_BUFFER_SIZE) {
555 /* allocate more memory than allotted originally by PATH_BUFFER_SIZE */
556 wfullpath = ALLOC_N(wchar_t, size);
557 size = GetFullPathNameW(buffer, size, wfullpath, NULL);
558 }
559 else {
560 wfullpath = wfullpath_buffer;
561 }
562
563 /* Remove any trailing slashes */
564 if (IS_DIR_SEPARATOR_P(wfullpath[size - 1]) &&
565 wfullpath[size - 2] != L':' &&
566 !(size == 2 && IS_DIR_UNC_P(wfullpath))) {
567 size -= 1;
568 wfullpath[size] = L'\0';
569 }
570
571 /* Remove any trailing dot */
572 if (wfullpath[size - 1] == L'.') {
573 size -= 1;
574 wfullpath[size] = L'\0';
575 }
576
577 /* removes trailing invalid ':$DATA' */
578 size = remove_invalid_alternative_data(wfullpath, size);
579
580 /* Replace the trailing path to long name */
581 if (long_name) {
582 size_t bufsize = wfullpath == wfullpath_buffer ? PATH_BUFFER_SIZE : 0;
583 size = replace_to_long_name(&wfullpath, size, bufsize);
584 }
585
586 /* sanitize backslashes with forwardslashes */
587 replace_wchar(wfullpath, L'\\', L'/');
588
589 /* convert to VALUE and set the path encoding */
590 rb_str_set_len(result, 0);
591 result = append_wstr(result, wfullpath, size, path_cp, path_encoding);
592
593 /* makes the result object tainted if expanding tainted strings or returning modified path */
594 if (tainted)
595 OBJ_TAINT(result);
596
597 /* TODO: better cleanup */
598 if (buffer)
599 xfree(buffer);
600
601 if (wpath)
602 free(wpath);
603
604 if (wdir)
605 free(wdir);
606
607 if (whome)
608 xfree(whome);
609
610 if (wfullpath != wfullpath_buffer)
611 xfree(wfullpath);
612
613 rb_enc_associate(result, path_encoding);
614 return result;
615}
616
617VALUE
619{
620 DWORD len;
621 VALUE wtmp = 0, wpathbuf, str;
622 rb_w32_reparse_buffer_t rbuf, *rp = &rbuf;
623 WCHAR *wpath, *wbuf;
624 rb_encoding *enc;
625 UINT cp, path_cp;
626 int e;
627
629 enc = rb_enc_get(path);
630 cp = path_cp = code_page(enc);
631 if (cp == INVALID_CODE_PAGE) {
633 cp = CP_UTF8;
634 }
635 len = MultiByteToWideChar(cp, 0, RSTRING_PTR(path), RSTRING_LEN(path), NULL, 0);
636 wpath = ALLOCV_N(WCHAR, wpathbuf, len+1);
637 MultiByteToWideChar(cp, 0, RSTRING_PTR(path), RSTRING_LEN(path), wpath, len);
638 wpath[len] = L'\0';
639 e = rb_w32_read_reparse_point(wpath, rp, sizeof(rbuf), &wbuf, &len);
640 if (e == ERROR_MORE_DATA) {
641 size_t size = rb_w32_reparse_buffer_size(len + 1);
642 rp = ALLOCV(wtmp, size);
643 e = rb_w32_read_reparse_point(wpath, rp, size, &wbuf, &len);
644 }
645 ALLOCV_END(wpathbuf);
646 if (e) {
647 ALLOCV_END(wtmp);
648 if (e != -1)
650 else /* not symlink; maybe volume mount point */
652 }
653 enc = resultenc;
654 path_cp = code_page(enc);
655 len = lstrlenW(wbuf);
656 str = append_wstr(rb_enc_str_new(0, 0, enc), wbuf, len, path_cp, enc);
657 ALLOCV_END(wtmp);
658 return str;
659}
660
661int
663{
664 DWORD attr;
665 int ret = 1;
666 long len;
667 wchar_t* wpath;
668
669 wpath = mbstr_to_wstr(CP_UTF8, path, -1, &len);
670 if (!wpath) return 0;
671
672 attr = GetFileAttributesW(wpath);
673 if (attr == INVALID_FILE_ATTRIBUTES ||
674 (attr & FILE_ATTRIBUTE_DIRECTORY)) {
675 ret = 0;
676 }
677 else {
678 HANDLE h = CreateFileW(wpath, GENERIC_READ,
679 FILE_SHARE_READ | FILE_SHARE_WRITE,
680 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
681 if (h != INVALID_HANDLE_VALUE) {
682 CloseHandle(h);
683 }
684 else {
685 ret = 0;
686 }
687 }
688 free(wpath);
689 return ret;
690}
691
692int
693rb_freopen(VALUE fname, const char *mode, FILE *file)
694{
695 WCHAR *wname, wmode[4];
696 VALUE wtmp;
697 char *name;
698 long len;
699 int e = 0, n = MultiByteToWideChar(CP_ACP, 0, mode, -1, NULL, 0);
700 if (n > numberof(wmode)) return EINVAL;
701 MultiByteToWideChar(CP_ACP, 0, mode, -1, wmode, numberof(wmode));
702 RSTRING_GETMEM(fname, name, len);
703 n = rb_long2int(len);
704 len = MultiByteToWideChar(CP_UTF8, 0, name, n, NULL, 0);
705 wname = ALLOCV_N(WCHAR, wtmp, len + 1);
706 len = MultiByteToWideChar(CP_UTF8, 0, name, n, wname, len);
707 wname[len] = L'\0';
708 RB_GC_GUARD(fname);
709#if RUBY_MSVCRT_VERSION < 80 && !defined(HAVE__WFREOPEN_S)
710 e = _wfreopen(wname, wmode, file) ? 0 : errno;
711#else
712 {
713 FILE *newfp = 0;
714 e = _wfreopen_s(&newfp, wname, wmode, file);
715 }
716#endif
717 ALLOCV_END(wtmp);
718 return e;
719}
720
721void
723{
724 if (rb_code_page.count) return;
725 rb_enc_foreach_name(code_page_i, (st_data_t)&rb_code_page);
726}
int errno
#define L(x)
Definition: asm.h:125
#define free(x)
Definition: dln.c:52
VALUE rb_enc_associate(VALUE obj, rb_encoding *enc)
Definition: encoding.c:866
rb_encoding * rb_filesystem_encoding(void)
Definition: encoding.c:1387
int rb_utf8_encindex(void)
Definition: encoding.c:1334
rb_encoding * rb_enc_get(VALUE obj)
Definition: encoding.c:872
int rb_ascii8bit_encindex(void)
Definition: encoding.c:1322
int rb_enc_to_index(rb_encoding *enc)
Definition: encoding.c:125
rb_encoding * rb_enc_check(VALUE str1, VALUE str2)
Definition: encoding.c:891
int count
Definition: encoding.c:57
int rb_usascii_encindex(void)
Definition: encoding.c:1346
rb_econv_t * rb_econv_open(const char *source_encoding, const char *destination_encoding, int ecflags)
Definition: transcode.c:1052
#define ECONV_UNDEF_REPLACE
Definition: encoding.h:396
VALUE rb_enc_str_new(const char *, long, rb_encoding *)
Definition: string.c:796
#define rb_enc_name(enc)
Definition: encoding.h:177
#define ECONV_INVALID_REPLACE
Definition: encoding.h:394
int rb_enc_str_asciionly_p(VALUE)
Definition: string.c:678
void rb_econv_close(rb_econv_t *ec)
Definition: transcode.c:1685
VALUE rb_econv_append(rb_econv_t *ec, const char *bytesrc, long bytesize, VALUE dst, int flags)
Definition: transcode.c:1796
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
int rb_file_load_ok(const char *path)
Definition: file.c:6283
VALUE rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result)
Definition: file.c:3707
VALUE rb_default_home_dir(VALUE result)
Definition: file.c:3616
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition: eval.c:668
VALUE rb_eRangeError
Definition: error.c:928
VALUE rb_exc_new_str(VALUE, VALUE)
Definition: error.c:974
VALUE rb_eArgError
Definition: error.c:925
const char * name
Definition: nkf.c:208
#define NULL
#define RSTRING_LEN(str)
#define ALLOCV_END(v)
#define rb_syserr_fail_path(err, path)
unsigned long st_data_t
#define xfree
int atoi(const char *__nptr)
#define EINVAL
#define RSTRING_PTR(str)
#define NIL_P(v)
#define numberof(array)
const char size_t n
void rb_str_set_len(VALUE, long)
Definition: string.c:2692
unsigned long VALUE
void * realloc(void *, size_t) __attribute__((__warn_unused_result__)) __attribute__((__alloc_size__(2)))
#define OBJ_TAINTED(x)
#define FilePathValue(v)
#define rp(obj)
uint32_t i
#define RSTRING_GETMEM(str, ptrvar, lenvar)
int strncmp(const char *, const char *, size_t)
__inline__ const void *__restrict__ size_t len
#define ALLOC_N(type, n)
#define ALLOCV(v, n)
#define long
#define rb_long2int(n)
#define RB_GC_GUARD(v)
#define ALLOCV_N(type, v, n)
#define FALSE
unsigned int size
struct rb_call_cache buf
void rb_str_modify_expand(VALUE, long)
Definition: string.c:2122
_ssize_t ssize_t
size_t st_index_t h
#define rb_str_new_cstr(str)
#define OBJ_TAINT(x)
#define ISALPHA(c)
#define system_code_page
Definition: file.c:42
#define wstr_to_mbstr
Definition: file.c:44
int rb_freopen(VALUE fname, const char *mode, FILE *file)
Definition: file.c:693
#define INVALID_CODE_PAGE
Definition: file.c:36
#define INVALID_FILE_ATTRIBUTES
Definition: file.c:14
#define IS_DIR_SEPARATOR_P(c)
Definition: file.c:23
#define mbstr_to_wstr
Definition: file.c:43
#define IS_DIR_UNC_P(c)
Definition: file.c:24
void Init_w32_codepage(void)
Definition: file.c:722
#define PATH_BUFFER_SIZE
Definition: file.c:37
VALUE rb_readlink(VALUE path, rb_encoding *resultenc)
Definition: file.c:618
#define fix_string_encoding(str, encoding)
Definition: file.c:144
void rb_enc_foreach_name(int(*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg)
Definition: encoding.c:1972
UINT rb_w32_filecp(void)
WCHAR * rb_w32_home_dir(void)
Definition: win32.c:540
#define rb_w32_reparse_buffer_size(n)
Definition: file.h:33
int rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp, size_t bufsize, WCHAR **result, DWORD *len)
Definition: win32.c:5042
int rb_w32_map_errno(DWORD)
Definition: win32.c:273
IUnknown DWORD
Definition: win32ole.c:33