Bug #6836 ยป improve-require-and-file-expand_path-windows.v2.diff
configure.in | ||
---|---|---|
1186 | 1186 |
AC_CHECK_FUNCS(cygwin_conv_path) |
1187 | 1187 |
AC_LIBOBJ([langinfo]) |
1188 | 1188 |
], |
1189 |
[mingw*], [ LIBS="-lshell32 -lws2_32 -limagehlp $LIBS" |
|
1189 |
[mingw*], [ LIBS="-lshell32 -lws2_32 -limagehlp -lshlwapi $LIBS"
|
|
1190 | 1190 |
ac_cv_header_a_out_h=no |
1191 | 1191 |
ac_cv_header_pwd_h=no |
1192 | 1192 |
ac_cv_header_utime_h=no |
file.c | ||
---|---|---|
2882 | 2882 |
return buf + dirlen; |
2883 | 2883 |
} |
2884 | 2884 | |
2885 |
static VALUE |
|
2886 |
file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result) |
|
2885 |
#ifndef _WIN32 |
|
2886 |
VALUE |
|
2887 |
rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result) |
|
2887 | 2888 |
{ |
2888 | 2889 |
const char *s, *b, *fend; |
2889 | 2890 |
char *buf, *p, *pend, *root; |
... | ... | |
2945 | 2946 |
/* specified drive, but not full path */ |
2946 | 2947 |
int same = 0; |
2947 | 2948 |
if (!NIL_P(dname) && !not_same_drive(dname, s[0])) { |
2948 |
file_expand_path(dname, Qnil, abs_mode, result);
|
|
2949 |
rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
|
|
2949 | 2950 |
BUFINIT(); |
2950 | 2951 |
if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) { |
2951 | 2952 |
/* ok, same drive */ |
... | ... | |
2969 | 2970 |
#endif |
2970 | 2971 |
else if (!rb_is_absolute_path(s)) { |
2971 | 2972 |
if (!NIL_P(dname)) { |
2972 |
file_expand_path(dname, Qnil, abs_mode, result);
|
|
2973 |
rb_file_expand_path_internal(dname, Qnil, abs_mode, long_name, result);
|
|
2973 | 2974 |
rb_enc_associate(result, rb_enc_check(result, fname)); |
2974 | 2975 |
BUFINIT(); |
2975 | 2976 |
p = pend; |
... | ... | |
3222 | 3223 |
ENC_CODERANGE_CLEAR(result); |
3223 | 3224 |
return result; |
3224 | 3225 |
} |
3226 |
#endif /* _WIN32 */ |
|
3225 | 3227 | |
3226 | 3228 |
#define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, MAXPATHLEN + 2) |
3227 | 3229 | |
... | ... | |
3232 | 3234 |
static VALUE |
3233 | 3235 |
file_expand_path_1(VALUE fname) |
3234 | 3236 |
{ |
3235 |
return file_expand_path(fname, Qnil, 0, EXPAND_PATH_BUFFER());
|
|
3237 |
return rb_file_expand_path_internal(fname, Qnil, 0, 0, EXPAND_PATH_BUFFER());
|
|
3236 | 3238 |
} |
3237 | 3239 | |
3238 | 3240 |
VALUE |
3239 | 3241 |
rb_file_expand_path(VALUE fname, VALUE dname) |
3240 | 3242 |
{ |
3241 | 3243 |
check_expand_path_args(fname, dname); |
3242 |
return file_expand_path(fname, dname, 0, EXPAND_PATH_BUFFER()); |
|
3244 |
return rb_file_expand_path_internal(fname, dname, 0, 1, EXPAND_PATH_BUFFER()); |
|
3245 |
} |
|
3246 | ||
3247 |
VALUE |
|
3248 |
rb_file_expand_path_fast(VALUE fname, VALUE dname) |
|
3249 |
{ |
|
3250 |
check_expand_path_args(fname, dname); |
|
3251 |
return rb_file_expand_path_internal(fname, dname, 0, 0, EXPAND_PATH_BUFFER()); |
|
3243 | 3252 |
} |
3244 | 3253 | |
3245 | 3254 |
/* |
... | ... | |
3276 | 3285 |
rb_file_absolute_path(VALUE fname, VALUE dname) |
3277 | 3286 |
{ |
3278 | 3287 |
check_expand_path_args(fname, dname); |
3279 |
return file_expand_path(fname, dname, 1, EXPAND_PATH_BUFFER());
|
|
3288 |
return rb_file_expand_path_internal(fname, dname, 1, 1, EXPAND_PATH_BUFFER());
|
|
3280 | 3289 |
} |
3281 | 3290 | |
3282 | 3291 |
/* |
... | ... | |
5250 | 5259 | |
5251 | 5260 |
RB_GC_GUARD(str) = rb_get_path_check(str, safe_level); |
5252 | 5261 |
if (RSTRING_LEN(str) == 0) continue; |
5253 |
file_expand_path(fname, str, 0, tmp);
|
|
5262 |
rb_file_expand_path_internal(fname, str, 0, 0, tmp);
|
|
5254 | 5263 |
if (rb_file_load_ok(RSTRING_PTR(tmp))) { |
5255 | 5264 |
*filep = copy_path_class(tmp, *filep); |
5256 | 5265 |
return (int)(j+1); |
... | ... | |
5309 | 5318 |
VALUE str = RARRAY_PTR(load_path)[i]; |
5310 | 5319 |
RB_GC_GUARD(str) = rb_get_path_check(str, safe_level); |
5311 | 5320 |
if (RSTRING_LEN(str) > 0) { |
5312 |
file_expand_path(path, str, 0, tmp);
|
|
5321 |
rb_file_expand_path_internal(path, str, 0, 0, tmp);
|
|
5313 | 5322 |
f = RSTRING_PTR(tmp); |
5314 | 5323 |
if (rb_file_load_ok(f)) goto found; |
5315 | 5324 |
} |
... | ... | |
5544 | 5553 |
rb_define_method(rb_cStat, "setuid?", rb_stat_suid, 0); |
5545 | 5554 |
rb_define_method(rb_cStat, "setgid?", rb_stat_sgid, 0); |
5546 | 5555 |
rb_define_method(rb_cStat, "sticky?", rb_stat_sticky, 0); |
5556 | ||
5557 |
#ifdef _WIN32 |
|
5558 |
rb_w32_init_file(); |
|
5559 |
#endif |
|
5547 | 5560 |
} |
internal.h | ||
---|---|---|
105 | 105 |
VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict); |
106 | 106 |
void rb_file_const(const char*, VALUE); |
107 | 107 |
int rb_file_load_ok(const char *); |
108 |
VALUE rb_file_expand_path_fast(VALUE, VALUE); |
|
109 |
VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE); |
|
108 | 110 |
void Init_File(void); |
109 | 111 | |
112 |
#ifdef _WIN32 |
|
113 |
/* file.c, win32/file.c */ |
|
114 |
void rb_w32_init_file(void); |
|
115 |
#endif |
|
116 | ||
110 | 117 |
/* gc.c */ |
111 | 118 |
void Init_heap(void); |
112 | 119 |
void *ruby_mimmalloc(size_t size); |
load.c | ||
---|---|---|
43 | 43 | |
44 | 44 |
ary = rb_ary_new2(RARRAY_LEN(load_path)); |
45 | 45 |
for (i = 0; i < RARRAY_LEN(load_path); ++i) { |
46 |
VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil); |
|
46 |
VALUE path = rb_file_expand_path_fast(RARRAY_PTR(load_path)[i], Qnil);
|
|
47 | 47 |
rb_str_freeze(path); |
48 | 48 |
rb_ary_push(ary, path); |
49 | 49 |
} |
... | ... | |
233 | 233 | |
234 | 234 |
if (*feature == '.' && |
235 | 235 |
(feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) { |
236 |
fullpath = rb_file_expand_path(rb_str_new2(feature), Qnil); |
|
236 |
fullpath = rb_file_expand_path_fast(rb_str_new2(feature), Qnil);
|
|
237 | 237 |
feature = RSTRING_PTR(fullpath); |
238 | 238 |
} |
239 | 239 |
if (ext && !strchr(ext, '/')) { |
test/ruby/test_file_exhaustive.rb | ||
---|---|---|
14 | 14 | |
15 | 15 |
def setup |
16 | 16 |
@dir = Dir.mktmpdir("rubytest-file") |
17 |
@rootdir = "#{DRIVE}/" |
|
17 | 18 |
File.chown(-1, Process.gid, @dir) |
18 | 19 |
@file = make_tmp_filename("file") |
19 | 20 |
@zerofile = make_tmp_filename("zerofile") |
... | ... | |
450 | 451 |
assert_equal(expected.force_encoding(cp), File.expand_path(a.dup.force_encoding(cp)), cp) |
451 | 452 |
end |
452 | 453 | |
454 |
path = "\u3042\u3044\u3046\u3048\u304a".encode("EUC-JP") |
|
455 |
assert_equal("#{Dir.pwd}/#{path}".encode("CP932"), File.expand_path(path).encode("CP932")) |
|
456 | ||
457 |
path = "\u3042\u3044\u3046\u3048\u304a".encode("CP51932") |
|
458 |
assert_equal("#{Dir.pwd}/#{path}", File.expand_path(path)) |
|
459 | ||
453 | 460 |
assert_incompatible_encoding {|d| File.expand_path(d)} |
454 | 461 |
end |
455 | 462 | |
... | ... | |
460 | 467 |
begin |
461 | 468 |
bug3630 = '[ruby-core:31537]' |
462 | 469 |
home = ENV["HOME"] |
470 |
home_drive = ENV["HOMEDRIVE"] |
|
471 |
home_path = ENV["HOMEPATH"] |
|
472 |
user_profile = ENV["USERPROFILE"] |
|
463 | 473 |
ENV["HOME"] = nil |
474 |
ENV["HOMEDRIVE"] = nil |
|
475 |
ENV["HOMEPATH"] = nil |
|
476 |
ENV["USERPROFILE"] = nil |
|
464 | 477 |
assert_raise(ArgumentError) { File.expand_path("~") } |
465 | 478 |
ENV["HOME"] = "~" |
466 | 479 |
assert_raise(ArgumentError, bug3630) { File.expand_path("~") } |
... | ... | |
468 | 481 |
assert_raise(ArgumentError, bug3630) { File.expand_path("~") } |
469 | 482 |
ensure |
470 | 483 |
ENV["HOME"] = home |
484 |
ENV["HOMEDRIVE"] = home_drive |
|
485 |
ENV["HOMEPATH"] = home_path |
|
486 |
ENV["USERPROFILE"] = user_profile |
|
487 |
end |
|
488 |
end |
|
489 | ||
490 |
def test_expand_path_resolve_empty_string_current_directory |
|
491 |
assert_equal(Dir.pwd, File.expand_path("")) |
|
492 |
end |
|
493 | ||
494 |
def test_expand_path_resolve_dot_current_directory |
|
495 |
assert_equal(Dir.pwd, File.expand_path(".")) |
|
496 |
end |
|
497 | ||
498 |
def test_expand_path_resolve_file_name_relative_current_directory |
|
499 |
assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo")) |
|
500 |
end |
|
501 | ||
502 |
def test_ignore_nil_dir_string |
|
503 |
assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo", nil)) |
|
504 |
end |
|
505 | ||
506 |
def test_expand_path_resolve_file_name_and_dir_string_relative |
|
507 |
assert_equal(File.join(Dir.pwd, "bar", "foo"), |
|
508 |
File.expand_path("foo", "bar")) |
|
509 |
end |
|
510 | ||
511 |
def test_expand_path_cleanup_dots_file_name |
|
512 |
bug = "[ruby-talk:18512]" |
|
513 | ||
514 |
assert_equal(File.join(Dir.pwd, ".a"), File.expand_path(".a"), bug) |
|
515 |
assert_equal(File.join(Dir.pwd, "..a"), File.expand_path("..a"), bug) |
|
516 | ||
517 |
if DRIVE |
|
518 |
# cleanup dots only on Windows |
|
519 |
assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a."), bug) |
|
520 |
skip "FIXME" |
|
521 |
assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a.."), bug) |
|
522 |
else |
|
523 |
assert_equal(File.join(Dir.pwd, "a."), File.expand_path("a."), bug) |
|
524 |
assert_equal(File.join(Dir.pwd, "a.."), File.expand_path("a.."), bug) |
|
471 | 525 |
end |
472 | 526 |
end |
473 | 527 | |
528 |
def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_a_complete_path |
|
529 |
assert_equal(@dir, File.expand_path("", "#{@dir}")) |
|
530 |
assert_equal(File.join(@dir, "a"), File.expand_path("a", "#{@dir}")) |
|
531 |
assert_equal(File.join(@dir, "a"), File.expand_path("../a", "#{@dir}/xxx")) |
|
532 |
assert_equal(@rootdir, File.expand_path(".", "#{@rootdir}")) |
|
533 |
end |
|
534 | ||
535 |
def test_expand_path_ignores_supplied_dir_if_path_contains_a_drive_letter |
|
536 |
assert_equal(@rootdir, File.expand_path(@rootdir, "D:/")) |
|
537 |
end if DRIVE |
|
538 | ||
539 |
def test_expand_path_removes_trailing_slashes_from_absolute_path |
|
540 |
assert_equal(File.join(@rootdir, "foo"), File.expand_path("#{@rootdir}foo/")) |
|
541 |
assert_equal(File.join(@rootdir, "foo.rb"), File.expand_path("#{@rootdir}foo.rb/")) |
|
542 |
end |
|
543 | ||
544 |
def test_expand_path_removes_trailing_spaces_from_absolute_path |
|
545 |
assert_equal(File.join(@rootdir, "a"), File.expand_path("#{@rootdir}a ")) |
|
546 |
end if DRIVE |
|
547 | ||
548 |
def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_dir_s_drive |
|
549 |
assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar")) |
|
550 |
end if DRIVE |
|
551 | ||
552 |
def test_expand_path_converts_a_pathname_which_starts_with_a_slash_and_unc_pathname |
|
553 |
assert_equal("//foo", File.expand_path('//foo', "//bar")) |
|
554 |
assert_equal("//bar/foo", File.expand_path('/foo', "//bar")) |
|
555 |
assert_equal("//foo", File.expand_path('//foo', "/bar")) |
|
556 |
end if DRIVE |
|
557 | ||
558 |
def test_expand_path_converts_a_dot_with_unc_dir |
|
559 |
assert_equal("//", File.expand_path('.', "//")) |
|
560 |
end |
|
561 | ||
562 |
def test_expand_path_preserves_unc_path_root |
|
563 |
assert_equal("//", File.expand_path("//")) |
|
564 |
assert_equal("//", File.expand_path("//.")) |
|
565 |
assert_equal("//", File.expand_path("//..")) |
|
566 |
end |
|
567 | ||
568 |
def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_host_share |
|
569 |
assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar")) |
|
570 |
end if DRIVE |
|
571 | ||
572 |
def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_a_current_drive |
|
573 |
assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo')) |
|
574 |
end |
|
575 | ||
576 |
def test_expand_path_returns_tainted_strings_or_not |
|
577 |
assert_equal(true, File.expand_path('foo').tainted?) |
|
578 |
assert_equal(true, File.expand_path('foo'.taint).tainted?) |
|
579 |
assert_equal(true, File.expand_path('/foo'.taint).tainted?) |
|
580 |
assert_equal(true, File.expand_path('foo', 'bar').tainted?) |
|
581 |
assert_equal(true, File.expand_path('foo', '/bar'.taint).tainted?) |
|
582 |
assert_equal(true, File.expand_path('foo'.taint, '/bar').tainted?) |
|
583 |
assert_equal(true, File.expand_path('~').tainted?) |
|
584 | ||
585 |
if DRIVE |
|
586 |
assert_equal(true, File.expand_path('/foo').tainted?) |
|
587 |
assert_equal(false, File.expand_path('//foo').tainted?) |
|
588 |
assert_equal(true, File.expand_path('C:/foo'.taint).tainted?) |
|
589 |
assert_equal(false, File.expand_path('C:/foo').tainted?) |
|
590 |
assert_equal(true, File.expand_path('foo', '/bar').tainted?) |
|
591 |
assert_equal(true, File.expand_path('foo', 'C:/bar'.taint).tainted?) |
|
592 |
assert_equal(true, File.expand_path('foo'.taint, 'C:/bar').tainted?) |
|
593 |
assert_equal(false, File.expand_path('foo', 'C:/bar').tainted?) |
|
594 |
assert_equal(false, File.expand_path('C:/foo/../bar').tainted?) |
|
595 |
assert_equal(false, File.expand_path('foo', '//bar').tainted?) |
|
596 |
else |
|
597 |
assert_equal(false, File.expand_path('/foo').tainted?) |
|
598 |
assert_equal(false, File.expand_path('foo', '/bar').tainted?) |
|
599 |
end |
|
600 |
end |
|
601 | ||
602 |
def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base |
|
603 |
old_home = ENV["HOME"] |
|
604 |
home = ENV["HOME"] = "#{DRIVE}/UserHome" |
|
605 |
assert_equal(home, File.expand_path("~")) |
|
606 |
assert_equal(home, File.expand_path("~", "C:/FooBar")) |
|
607 |
assert_equal(File.join(home, "a"), File.expand_path("~/a", "C:/FooBar")) |
|
608 |
ensure |
|
609 |
ENV["HOME"] = old_home |
|
610 |
end |
|
611 | ||
612 |
def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home |
|
613 |
old_home = ENV["HOME"] |
|
614 |
unc_home = ENV["HOME"] = "//UserHome" |
|
615 |
assert_equal(unc_home, File.expand_path("~")) |
|
616 |
ensure |
|
617 |
ENV["HOME"] = old_home |
|
618 |
end if DRIVE |
|
619 | ||
620 |
def test_expand_path_does_not_modify_a_home_string_argument |
|
621 |
old_home = ENV["HOME"] |
|
622 |
home = ENV["HOME"] = "#{DRIVE}/UserHome" |
|
623 |
str = "~/a" |
|
624 |
assert_equal("#{home}/a", File.expand_path(str)) |
|
625 |
assert_equal("~/a", str) |
|
626 |
ensure |
|
627 |
ENV["HOME"] = old_home |
|
628 |
end |
|
629 | ||
630 |
def test_expand_path_raises_argument_error_for_any_supplied_username |
|
631 |
bug = '[ruby-core:39597]' |
|
632 |
assert_raise(ArgumentError, bug) { File.expand_path("~anything") } |
|
633 |
end if DRIVE |
|
634 | ||
635 |
def test_expand_path_raises_a_type_error_if_not_passed_a_string_type |
|
636 |
assert_raise(TypeError) { File.expand_path(1) } |
|
637 |
assert_raise(TypeError) { File.expand_path(nil) } |
|
638 |
assert_raise(TypeError) { File.expand_path(true) } |
|
639 |
end |
|
640 | ||
641 |
def test_expand_path_expands_dot_dir |
|
642 |
assert_equal("#{DRIVE}/dir", File.expand_path("#{DRIVE}/./dir")) |
|
643 |
end |
|
644 | ||
645 |
def test_expand_path_does_not_modify_the_string_argument |
|
646 |
str = "./a/b/../c" |
|
647 |
assert_equal("#{Dir.pwd}/a/c", File.expand_path(str, Dir.pwd)) |
|
648 |
assert_equal("./a/b/../c", str) |
|
649 |
end |
|
650 | ||
651 |
def test_expand_path_returns_a_string_when_passed_a_string_subclass |
|
652 |
sub = Class.new(String) |
|
653 |
str = sub.new "./a/b/../c" |
|
654 |
path = File.expand_path(str, Dir.pwd) |
|
655 |
assert_equal("#{Dir.pwd}/a/c", path) |
|
656 |
assert_instance_of(String, path) |
|
657 |
end |
|
658 | ||
659 |
def test_expand_path_accepts_objects_that_have_a_to_path_method |
|
660 |
klass = Class.new { def to_path; "a/b/c"; end } |
|
661 |
obj = klass.new |
|
662 |
assert_equal("#{Dir.pwd}/a/b/c", File.expand_path(obj)) |
|
663 |
end |
|
664 | ||
474 | 665 |
def test_basename |
475 | 666 |
assert_equal(File.basename(@file).sub(/\.test$/, ""), File.basename(@file, ".test")) |
476 | 667 |
assert_equal("", s = File.basename("")) |
win32/Makefile.sub | ||
---|---|---|
226 | 226 |
EXTSOLIBS = |
227 | 227 |
!endif |
228 | 228 |
!if !defined(LIBS) |
229 |
LIBS = oldnames.lib user32.lib advapi32.lib shell32.lib ws2_32.lib imagehlp.lib $(EXTLIBS) |
|
229 |
LIBS = oldnames.lib user32.lib advapi32.lib shell32.lib ws2_32.lib imagehlp.lib shlwapi.lib $(EXTLIBS)
|
|
230 | 230 |
!endif |
231 | 231 |
!if !defined(MISSING) |
232 | 232 |
MISSING = acosh.obj cbrt.obj crypt.obj erf.obj ffs.obj langinfo.obj lgamma_r.obj strlcat.obj strlcpy.obj tgamma.obj win32/win32.obj win32/file.obj setproctitle.obj |
win32/file.c | ||
---|---|---|
1 | 1 |
#include "ruby/ruby.h" |
2 |
#include "ruby/encoding.h" |
|
2 | 3 |
#include <winbase.h> |
4 |
#include <wchar.h> |
|
5 |
#include <shlwapi.h> |
|
3 | 6 | |
4 | 7 |
#ifndef INVALID_FILE_ATTRIBUTES |
5 | 8 |
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) |
6 | 9 |
#endif |
7 | 10 | |
11 |
/* cache 'encoding name' => 'code page' into a hash */ |
|
12 |
static VALUE rb_code_page; |
|
13 | ||
14 |
#define IS_DIR_SEPARATOR_P(c) (c == L'\\' || c == L'/') |
|
15 |
#define IS_DIR_UNC_P(c) (IS_DIR_SEPARATOR_P(c[0]) && IS_DIR_SEPARATOR_P(c[1])) |
|
16 | ||
17 |
/* MultiByteToWideChar() doesn't work with code page 51932 */ |
|
18 |
#define INVALID_CODE_PAGE 51932 |
|
19 |
#define PATH_BUFFER_SIZE MAX_PATH * 2 |
|
20 | ||
21 |
#define insecure_obj_p(obj, level) ((level) >= 4 || ((level) > 0 && OBJ_TAINTED(obj))) |
|
22 | ||
23 |
static inline void |
|
24 |
replace_wchar(wchar_t *s, int find, int replace) |
|
25 |
{ |
|
26 |
while (*s != 0) { |
|
27 |
if (*s == find) |
|
28 |
*s = replace; |
|
29 |
s++; |
|
30 |
} |
|
31 |
} |
|
32 | ||
33 |
/* Convert str from multibyte char to wchar with specified code page */ |
|
34 |
static inline void |
|
35 |
convert_mb_to_wchar(VALUE str, wchar_t **wstr, wchar_t **wstr_pos, size_t *wstr_len, UINT code_page) |
|
36 |
{ |
|
37 |
size_t len; |
|
38 | ||
39 |
if (NIL_P(str)) |
|
40 |
return; |
|
41 | ||
42 |
len = MultiByteToWideChar(code_page, 0, RSTRING_PTR(str), -1, NULL, 0) + 1; |
|
43 |
*wstr = (wchar_t *)xmalloc(len * sizeof(wchar_t)); |
|
44 |
if (wstr_pos) |
|
45 |
*wstr_pos = *wstr; |
|
46 | ||
47 |
MultiByteToWideChar(code_page, 0, RSTRING_PTR(str), -1, *wstr, len); |
|
48 |
*wstr_len = len - 2; |
|
49 |
} |
|
50 | ||
51 |
static inline void |
|
52 |
convert_wchar_to_mb(const wchar_t *wstr, char **str, size_t *str_len, UINT code_page) |
|
53 |
{ |
|
54 |
size_t len; |
|
55 | ||
56 |
len = WideCharToMultiByte(code_page, 0, wstr, -1, NULL, 0, NULL, NULL); |
|
57 |
*str = (char *)xmalloc(len * sizeof(char)); |
|
58 |
WideCharToMultiByte(code_page, 0, wstr, -1, *str, len, NULL, NULL); |
|
59 | ||
60 |
/* do not count terminator as part of the string length */ |
|
61 |
*str_len = len - 1; |
|
62 |
} |
|
63 | ||
64 |
/* |
|
65 |
Return user's home directory using environment variables combinations. |
|
66 |
Memory allocated by this function should be manually freeded afterwards. |
|
67 | ||
68 |
Try: |
|
69 |
HOME, HOMEDRIVE + HOMEPATH and USERPROFILE environment variables |
|
70 |
TODO: Special Folders - Profile and Personal |
|
71 |
*/ |
|
72 |
static wchar_t * |
|
73 |
home_dir() |
|
74 |
{ |
|
75 |
wchar_t *buffer = NULL; |
|
76 |
size_t buffer_len = 0, len = 0; |
|
77 |
size_t home_env = 0; |
|
78 | ||
79 |
/* |
|
80 |
GetEnvironmentVariableW when used with NULL will return the required |
|
81 |
buffer size and its terminating character. |
|
82 |
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx |
|
83 |
*/ |
|
84 | ||
85 |
if (len = GetEnvironmentVariableW(L"HOME", NULL, 0)) { |
|
86 |
buffer_len = len; |
|
87 |
home_env = 1; |
|
88 |
} else if (len = GetEnvironmentVariableW(L"HOMEDRIVE", NULL, 0)) { |
|
89 |
buffer_len = len; |
|
90 |
if (len = GetEnvironmentVariableW(L"HOMEPATH", NULL, 0)) { |
|
91 |
buffer_len += len; |
|
92 |
home_env = 2; |
|
93 |
} else { |
|
94 |
buffer_len = 0; |
|
95 |
} |
|
96 |
} else if (len = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0)) { |
|
97 |
buffer_len = len; |
|
98 |
home_env = 3; |
|
99 |
} |
|
100 | ||
101 |
/* allocate buffer */ |
|
102 |
if (home_env) |
|
103 |
buffer = (wchar_t *)xmalloc(buffer_len * sizeof(wchar_t)); |
|
104 | ||
105 |
switch (home_env) { |
|
106 |
case 1: |
|
107 |
/* HOME */ |
|
108 |
GetEnvironmentVariableW(L"HOME", buffer, buffer_len); |
|
109 |
break; |
|
110 |
case 2: |
|
111 |
/* HOMEDRIVE + HOMEPATH */ |
|
112 |
len = GetEnvironmentVariableW(L"HOMEDRIVE", buffer, buffer_len); |
|
113 |
GetEnvironmentVariableW(L"HOMEPATH", buffer + len, buffer_len - len); |
|
114 |
break; |
|
115 |
case 3: |
|
116 |
/* USERPROFILE */ |
|
117 |
GetEnvironmentVariableW(L"USERPROFILE", buffer, buffer_len); |
|
118 |
break; |
|
119 |
default: |
|
120 |
break; |
|
121 |
} |
|
122 | ||
123 |
if (home_env) { |
|
124 |
/* sanitize backslashes with forwardslashes */ |
|
125 |
replace_wchar(buffer, L'\\', L'/'); |
|
126 | ||
127 |
return buffer; |
|
128 |
} |
|
129 | ||
130 |
return NULL; |
|
131 |
} |
|
132 | ||
133 |
/* Remove trailing invalid ':$DATA' of the path. */ |
|
134 |
static inline size_t |
|
135 |
remove_invalid_alternative_data(wchar_t *wfullpath, size_t size) { |
|
136 |
static const wchar_t prime[] = L":$DATA"; |
|
137 |
enum { prime_len = (sizeof(prime) / sizeof(wchar_t)) -1 }; |
|
138 | ||
139 |
if (size <= prime_len || _wcsnicmp(wfullpath + size - prime_len, prime, prime_len) != 0) |
|
140 |
return size; |
|
141 | ||
142 |
/* alias of stream */ |
|
143 |
/* get rid of a bug of x64 VC++ */ |
|
144 |
if (wfullpath[size - (prime_len + 1)] == ':') { |
|
145 |
/* remove trailing '::$DATA' */ |
|
146 |
size -= prime_len + 1; /* prime */ |
|
147 |
wfullpath[size] = L'\0'; |
|
148 |
} else { |
|
149 |
/* remove trailing ':$DATA' of paths like '/aa:a:$DATA' */ |
|
150 |
wchar_t *pos = wfullpath + size - (prime_len + 1); |
|
151 |
while (!IS_DIR_SEPARATOR_P(*pos) && pos != wfullpath) { |
|
152 |
if (*pos == L':') { |
|
153 |
size -= prime_len; /* alternative */ |
|
154 |
wfullpath[size] = L'\0'; |
|
155 |
break; |
|
156 |
} |
|
157 |
pos--; |
|
158 |
} |
|
159 |
} |
|
160 |
return size; |
|
161 |
} |
|
162 | ||
163 |
/* Return system code page. */ |
|
164 |
static inline UINT |
|
165 |
system_code_page() { |
|
166 |
return AreFileApisANSI() ? CP_ACP : CP_OEMCP; |
|
167 |
} |
|
168 | ||
169 |
/* |
|
170 |
Return code page number of the encoding. |
|
171 |
Cache code page into a hash for performance since finding the code page in |
|
172 |
Encoding#names is slow. |
|
173 |
*/ |
|
174 |
static UINT |
|
175 |
code_page(rb_encoding *enc) |
|
176 |
{ |
|
177 |
VALUE code_page_value, name_key; |
|
178 |
VALUE encoding, names_ary = Qundef, name; |
|
179 |
char *enc_name; |
|
180 |
struct RString fake_str; |
|
181 |
ID names; |
|
182 |
long i; |
|
183 | ||
184 |
if (!enc) |
|
185 |
return system_code_page(); |
|
186 | ||
187 |
enc_name = (char *)rb_enc_name(enc); |
|
188 | ||
189 |
fake_str.basic.flags = T_STRING|RSTRING_NOEMBED; |
|
190 |
fake_str.basic.klass = rb_cString; |
|
191 |
fake_str.as.heap.len = strlen(enc_name); |
|
192 |
fake_str.as.heap.ptr = enc_name; |
|
193 |
fake_str.as.heap.aux.capa = fake_str.as.heap.len; |
|
194 |
name_key = (VALUE)&fake_str; |
|
195 |
ENCODING_CODERANGE_SET(name_key, rb_usascii_encindex(), ENC_CODERANGE_7BIT); |
|
196 | ||
197 |
code_page_value = rb_hash_lookup(rb_code_page, name_key); |
|
198 |
if (code_page_value != Qnil) |
|
199 |
return (UINT)FIX2INT(code_page_value); |
|
200 | ||
201 |
name_key = rb_usascii_str_new2(enc_name); |
|
202 | ||
203 |
encoding = rb_enc_from_encoding(enc); |
|
204 |
if (!NIL_P(encoding)) { |
|
205 |
CONST_ID(names, "names"); |
|
206 |
names_ary = rb_funcall(encoding, names, 0); |
|
207 |
} |
|
208 | ||
209 |
if (enc == rb_usascii_encoding()) { |
|
210 |
UINT code_page = 20127; |
|
211 |
rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page)); |
|
212 |
return code_page; |
|
213 |
} |
|
214 |
else if (enc == rb_ascii8bit_encoding()) { |
|
215 |
UINT code_page = 437; |
|
216 |
rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page)); |
|
217 |
return code_page; |
|
218 |
} |
|
219 | ||
220 |
if (names_ary != Qundef) { |
|
221 |
for (i = 0; i < RARRAY_LEN(names_ary); i++) { |
|
222 |
name = RARRAY_PTR(names_ary)[i]; |
|
223 |
if (strncmp("CP", RSTRING_PTR(name), 2) == 0) { |
|
224 |
int code_page = atoi(RSTRING_PTR(name) + 2); |
|
225 |
if (code_page != 0) { |
|
226 |
rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page)); |
|
227 |
return (UINT)code_page; |
|
228 |
} |
|
229 |
} |
|
230 |
} |
|
231 |
} |
|
232 | ||
233 |
rb_hash_aset(rb_code_page, name_key, INT2FIX(INVALID_CODE_PAGE)); |
|
234 |
return INVALID_CODE_PAGE; |
|
235 |
} |
|
236 | ||
237 |
static inline VALUE |
|
238 |
fix_string_encoding(VALUE str, rb_encoding *encoding) |
|
239 |
{ |
|
240 |
VALUE result, tmp; |
|
241 | ||
242 |
tmp = rb_enc_str_new(RSTRING_PTR(str), RSTRING_LEN(str), encoding); |
|
243 |
result = rb_str_encode(tmp, rb_enc_from_encoding(rb_utf8_encoding()), 0, Qnil); |
|
244 | ||
245 |
return result; |
|
246 |
} |
|
247 | ||
248 |
/* |
|
249 |
Replace the last part of the path to long name. |
|
250 |
We try to avoid to call FindFirstFileW() since it takes long time. |
|
251 |
*/ |
|
252 |
static inline size_t |
|
253 |
replace_to_long_name(wchar_t **wfullpath, size_t size, int heap) { |
|
254 |
WIN32_FIND_DATAW find_data; |
|
255 |
HANDLE find_handle; |
|
256 | ||
257 |
/* |
|
258 |
Skip long name conversion if the path is already long name. |
|
259 |
Short name is 8.3 format. |
|
260 |
http://en.wikipedia.org/wiki/8.3_filename |
|
261 |
This check can be skipped for directory components that have file |
|
262 |
extensions longer than 3 characters, or total lengths longer than |
|
263 |
12 characters. |
|
264 |
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx |
|
265 |
*/ |
|
266 |
size_t const max_short_name_size = 8 + 1 + 3; |
|
267 |
size_t const max_extension_size = 3; |
|
268 |
size_t path_len = 1, extension_len = 0; |
|
269 |
wchar_t *pos = *wfullpath; |
|
270 | ||
271 |
if (size == 3 && pos[1] == L':' && pos[2] == L'\\' && pos[3] == L'\0') { |
|
272 |
/* root path doesn't need short name expansion */ |
|
273 |
return size; |
|
274 |
} |
|
275 | ||
276 |
pos = *wfullpath + size - 1; |
|
277 |
while (!IS_DIR_SEPARATOR_P(*pos) && pos != *wfullpath) { |
|
278 |
if (!extension_len && *pos == L'.') { |
|
279 |
extension_len = path_len - 1; |
|
280 |
} |
|
281 |
if (path_len > max_short_name_size || extension_len > max_extension_size) { |
|
282 |
return size; |
|
283 |
} |
|
284 |
path_len++; |
|
285 |
pos--; |
|
286 |
} |
|
287 | ||
288 |
find_handle = FindFirstFileW(*wfullpath, &find_data); |
|
289 |
if (find_handle != INVALID_HANDLE_VALUE) { |
|
290 |
size_t trail_pos = wcslen(*wfullpath); |
|
291 |
size_t file_len = wcslen(find_data.cFileName); |
|
292 | ||
293 |
FindClose(find_handle); |
|
294 |
while (trail_pos > 0) { |
|
295 |
if (IS_DIR_SEPARATOR_P((*wfullpath)[trail_pos])) |
|
296 |
break; |
|
297 |
trail_pos--; |
|
298 |
} |
|
299 |
size = trail_pos + 1 + file_len; |
|
300 |
if ((size + 1) > sizeof(*wfullpath) / sizeof((*wfullpath)[0])) { |
|
301 |
wchar_t *buf = (wchar_t *)xmalloc((size + 1) * sizeof(wchar_t)); |
|
302 |
wcsncpy(buf, *wfullpath, trail_pos + 1); |
|
303 |
if (heap) |
|
304 |
xfree(*wfullpath); |
|
305 |
*wfullpath = buf; |
|
306 |
} |
|
307 |
wcsncpy(*wfullpath + trail_pos + 1, find_data.cFileName, file_len + 1); |
|
308 |
} |
|
309 |
return size; |
|
310 |
} |
|
311 | ||
312 |
VALUE |
|
313 |
rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result) |
|
314 |
{ |
|
315 |
size_t size = 0, wpath_len = 0, wdir_len = 0, whome_len = 0; |
|
316 |
size_t buffer_len = 0; |
|
317 |
char *fullpath = NULL; |
|
318 |
wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL, *wdir = NULL; |
|
319 |
wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL; |
|
320 |
UINT path_cp, cp; |
|
321 |
VALUE path = fname, dir = dname; |
|
322 |
wchar_t wfullpath_buffer[PATH_BUFFER_SIZE]; |
|
323 |
wchar_t path_drive = L'\0', dir_drive = L'\0'; |
|
324 |
int ignore_dir = 0; |
|
325 |
rb_encoding *path_encoding; |
|
326 |
int tainted = 0; |
|
327 | ||
328 |
/* tainted if path is tainted */ |
|
329 |
tainted = OBJ_TAINTED(path); |
|
330 | ||
331 |
/* get path encoding */ |
|
332 |
if (NIL_P(dir)) { |
|
333 |
path_encoding = rb_enc_get(path); |
|
334 |
} else { |
|
335 |
path_encoding = rb_enc_check(path, dir); |
|
336 |
} |
|
337 | ||
338 |
cp = path_cp = code_page(path_encoding); |
|
339 | ||
340 |
/* workaround invalid codepage */ |
|
341 |
if (path_cp == INVALID_CODE_PAGE) { |
|
342 |
cp = CP_UTF8; |
|
343 |
if (!NIL_P(path)) { |
|
344 |
path = fix_string_encoding(path, path_encoding); |
|
345 |
} |
|
346 |
} |
|
347 | ||
348 |
/* convert char * to wchar_t */ |
|
349 |
convert_mb_to_wchar(path, &wpath, &wpath_pos, &wpath_len, cp); |
|
350 | ||
351 |
/* determine if we need the user's home directory */ |
|
352 |
/* expand '~' only if NOT rb_file_absolute_path() where `abs_mode` is 1 */ |
|
353 |
if (abs_mode == 0 && ((wpath_len == 1 && wpath_pos[0] == L'~') || |
|
354 |
(wpath_len >= 2 && wpath_pos[0] == L'~' && IS_DIR_SEPARATOR_P(wpath_pos[1])))) { |
|
355 |
/* tainted if expanding '~' */ |
|
356 |
tainted = 1; |
|
357 | ||
358 |
whome = home_dir(); |
|
359 |
if (whome == NULL) { |
|
360 |
xfree(wpath); |
|
361 |
rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'"); |
|
362 |
} |
|
363 |
whome_len = wcslen(whome); |
|
364 | ||
365 |
if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) { |
|
366 |
xfree(wpath); |
|
367 |
rb_raise(rb_eArgError, "non-absolute home"); |
|
368 |
} |
|
369 | ||
370 |
/* use filesystem encoding if expanding home dir */ |
|
371 |
path_encoding = rb_filesystem_encoding(); |
|
372 |
cp = path_cp = system_code_page(); |
|
373 | ||
374 |
/* ignores dir since we are expading home */ |
|
375 |
ignore_dir = 1; |
|
376 | ||
377 |
/* exclude ~ from the result */ |
|
378 |
wpath_pos++; |
|
379 |
wpath_len--; |
|
380 | ||
381 |
/* exclude separator if present */ |
|
382 |
if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { |
|
383 |
wpath_pos++; |
|
384 |
wpath_len--; |
|
385 |
} |
|
386 |
} else if (wpath_len >= 2 && wpath_pos[1] == L':') { |
|
387 |
if (wpath_len >= 3 && IS_DIR_SEPARATOR_P(wpath_pos[2])) { |
|
388 |
/* ignore dir since path contains a drive letter and a root slash */ |
|
389 |
ignore_dir = 1; |
|
390 |
} else { |
|
391 |
/* determine if we ignore dir or not later */ |
|
392 |
path_drive = wpath_pos[0]; |
|
393 |
} |
|
394 |
} else if (abs_mode == 0 && wpath_len >= 2 && wpath_pos[0] == L'~') { |
|
395 |
wchar_t *wuser = wpath_pos + 1; |
|
396 |
wchar_t *pos = wuser; |
|
397 |
char *user; |
|
398 | ||
399 |
/* tainted if expanding '~' */ |
|
400 |
tainted = 1; |
|
401 | ||
402 |
while (!IS_DIR_SEPARATOR_P(*pos) && *pos != '\0') |
|
403 |
pos++; |
|
404 | ||
405 |
*pos = '\0'; |
|
406 |
convert_wchar_to_mb(wuser, &user, &size, cp); |
|
407 | ||
408 |
/* convert to VALUE and set the path encoding */ |
|
409 |
if (path_cp == INVALID_CODE_PAGE) { |
|
410 |
VALUE tmp = rb_enc_str_new(user, size, rb_utf8_encoding()); |
|
411 |
result = rb_str_encode(tmp, rb_enc_from_encoding(path_encoding), 0, Qnil); |
|
412 |
rb_str_resize(tmp, 0); |
|
413 |
} else { |
|
414 |
result = rb_enc_str_new(user, size, path_encoding); |
|
415 |
} |
|
416 | ||
417 |
xfree(wpath); |
|
418 |
if (user) |
|
419 |
xfree(user); |
|
420 | ||
421 |
rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result)); |
|
422 |
} |
|
423 | ||
424 |
/* convert dir */ |
|
425 |
if (!ignore_dir && !NIL_P(dir)) { |
|
426 |
/* fix string encoding */ |
|
427 |
if (path_cp == INVALID_CODE_PAGE) { |
|
428 |
dir = fix_string_encoding(dir, path_encoding); |
|
429 |
} |
|
430 | ||
431 |
/* convert char * to wchar_t */ |
|
432 |
convert_mb_to_wchar(dir, &wdir, NULL, &wdir_len, cp); |
|
433 | ||
434 |
if (wdir_len >= 2 && wdir[1] == L':') { |
|
435 |
dir_drive = wdir[0]; |
|
436 |
if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { |
|
437 |
wdir_len = 2; |
|
438 |
} |
|
439 |
} else if (wdir_len >= 2 && IS_DIR_UNC_P(wdir)) { |
|
440 |
/* UNC path */ |
|
441 |
if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { |
|
442 |
/* cut the UNC path tail to '//host/share' */ |
|
443 |
size_t separators = 0; |
|
444 |
size_t pos = 2; |
|
445 |
while (pos < wdir_len && separators < 2) { |
|
446 |
if (IS_DIR_SEPARATOR_P(wdir[pos])) { |
|
447 |
separators++; |
|
448 |
} |
|
449 |
pos++; |
|
450 |
} |
|
451 |
if (separators == 2) |
|
452 |
wdir_len = pos - 1; |
|
453 |
} |
|
454 |
} |
|
455 |
} |
|
456 | ||
457 |
/* determine if we ignore dir or not */ |
|
458 |
if (!ignore_dir && path_drive && dir_drive) { |
|
459 |
if (towupper(path_drive) == towupper(dir_drive)) { |
|
460 |
/* exclude path drive letter to use dir */ |
|
461 |
wpath_pos += 2; |
|
462 |
wpath_len -= 2; |
|
463 |
} else { |
|
464 |
/* ignore dir since path drive is different from dir drive */ |
|
465 |
ignore_dir = 1; |
|
466 |
wdir_len = 0; |
|
467 |
} |
|
468 |
} |
|
469 | ||
470 |
if (!ignore_dir && wpath_len >= 2 && IS_DIR_UNC_P(wpath)) { |
|
471 |
/* ignore dir since path has UNC root */ |
|
472 |
ignore_dir = 1; |
|
473 |
wdir_len = 0; |
|
474 |
} else if (!ignore_dir && wpath_len >= 1 && IS_DIR_SEPARATOR_P(wpath[0]) && |
|
475 |
!dir_drive && !(wdir_len >= 2 && IS_DIR_UNC_P(wdir))) { |
|
476 |
/* ignore dir since path has root slash and dir doesn't have drive or UNC root */ |
|
477 |
ignore_dir = 1; |
|
478 |
wdir_len = 0; |
|
479 |
} |
|
480 | ||
481 |
buffer_len = wpath_len + 1 + wdir_len + 1 + whome_len + 1; |
|
482 | ||
483 |
buffer = buffer_pos = (wchar_t *)xmalloc((buffer_len + 1) * sizeof(wchar_t)); |
|
484 | ||
485 |
/* add home */ |
|
486 |
if (whome_len) { |
|
487 |
wcsncpy(buffer_pos, whome, whome_len); |
|
488 |
buffer_pos += whome_len; |
|
489 |
} |
|
490 | ||
491 |
/* Add separator if required */ |
|
492 |
if (whome_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) { |
|
493 |
buffer_pos[0] = L'\\'; |
|
494 |
buffer_pos++; |
|
495 |
} |
|
496 | ||
497 |
if (wdir_len) { |
|
498 |
/* tainted if dir is used and dir is tainted */ |
|
499 |
if (!tainted && OBJ_TAINTED(dir)) |
|
500 |
tainted = 1; |
|
501 | ||
502 |
wcsncpy(buffer_pos, wdir, wdir_len); |
|
503 |
buffer_pos += wdir_len; |
|
504 |
} |
|
505 | ||
506 |
/* add separator if required */ |
|
507 |
if (wdir_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) { |
|
508 |
buffer_pos[0] = L'\\'; |
|
509 |
buffer_pos++; |
|
510 |
} |
|
511 | ||
512 |
/* now deal with path */ |
|
513 |
if (wpath_len) { |
|
514 |
wcsncpy(buffer_pos, wpath_pos, wpath_len); |
|
515 |
buffer_pos += wpath_len; |
|
516 |
} |
|
517 | ||
518 |
/* GetFullPathNameW requires at least "." to determine current directory */ |
|
519 |
if (wpath_len == 0) { |
|
520 |
buffer_pos[0] = L'.'; |
|
521 |
buffer_pos++; |
|
522 |
} |
|
523 | ||
524 |
/* Ensure buffer is NULL terminated */ |
|
525 |
buffer_pos[0] = L'\0'; |
|
526 | ||
527 |
/* tainted if path is relative */ |
|
528 |
if (!tainted && PathIsRelativeW(buffer) && !(buffer_len >= 2 && IS_DIR_UNC_P(buffer))) |
|
529 |
tainted = 1; |
|
530 | ||
531 |
/* FIXME: Make this more robust */ |
|
532 |
/* Determine require buffer size */ |
|
533 |
size = GetFullPathNameW(buffer, PATH_BUFFER_SIZE, wfullpath_buffer, NULL); |
|
534 |
if (size > PATH_BUFFER_SIZE) { |
|
535 |
// allocate enough memory to contain the response |
|
536 |
wfullpath = (wchar_t *)xmalloc(size * sizeof(wchar_t)); |
|
537 |
size = GetFullPathNameW(buffer, size, wfullpath, NULL); |
|
538 |
} else { |
|
539 |
wfullpath = wfullpath_buffer; |
|
540 |
} |
|
541 | ||
542 |
/* Remove any trailing slashes */ |
|
543 |
if (IS_DIR_SEPARATOR_P(wfullpath[size - 1]) && |
|
544 |
wfullpath[size - 2] != L':' && |
|
545 |
!(size == 2 && IS_DIR_UNC_P(wfullpath))) { |
|
546 |
size -= 1; |
|
547 |
wfullpath[size] = L'\0'; |
|
548 |
} |
|
549 | ||
550 |
/* Remove any trailing dot */ |
|
551 |
if (wfullpath[size - 1] == L'.') { |
|
552 |
size -= 1; |
|
553 |
wfullpath[size] = L'\0'; |
|
554 |
} |
|
555 | ||
556 |
/* removes trailing invalid ':$DATA' */ |
|
557 |
size = remove_invalid_alternative_data(wfullpath, size); |
|
558 | ||
559 |
/* Replace the trailing path to long name */ |
|
560 |
if (long_name) |
|
561 |
size = replace_to_long_name(&wfullpath, size, (wfullpath != wfullpath_buffer)); |
|
562 | ||
563 |
/* sanitize backslashes with forwardslashes */ |
|
564 |
replace_wchar(wfullpath, L'\\', L'/'); |
|
565 | ||
566 |
/* convert to char * */ |
|
567 |
size = WideCharToMultiByte(cp, 0, wfullpath, size, NULL, 0, NULL, NULL); |
|
568 |
if (size > (size_t)RSTRING_LEN(result)) { |
|
569 |
rb_str_modify(result); |
|
570 |
rb_str_resize(result, size); |
|
571 |
} |
|
572 | ||
573 |
WideCharToMultiByte(cp, 0, wfullpath, size, RSTRING_PTR(result), size, NULL, NULL); |
|
574 |
rb_str_set_len(result, size); |
|
575 | ||
576 |
/* convert to VALUE and set the path encoding */ |
|
577 |
if (path_cp == INVALID_CODE_PAGE) { |
|
578 |
VALUE tmp; |
|
579 |
size_t len; |
|
580 | ||
581 |
rb_enc_associate(result, rb_utf8_encoding()); |
|
582 |
ENC_CODERANGE_CLEAR(result); |
|
583 |
tmp = rb_str_encode(result, rb_enc_from_encoding(path_encoding), 0, Qnil); |
|
584 |
len = RSTRING_LEN(tmp); |
|
585 |
rb_str_modify(result); |
|
586 |
rb_str_resize(result, len); |
|
587 |
memcpy(RSTRING_PTR(result), RSTRING_PTR(tmp), len); |
|
588 |
rb_str_resize(tmp, 0); |
|
589 |
} |
|
590 |
rb_enc_associate(result, path_encoding); |
|
591 |
ENC_CODERANGE_CLEAR(result); |
|
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 |
xfree(wpath); |
|
603 | ||
604 |
if (wdir) |
|
605 |
xfree(wdir); |
|
606 | ||
607 |
if (whome) |
|
608 |
xfree(whome); |
|
609 | ||
610 |
if (wfullpath && wfullpath != wfullpath_buffer) |
|
611 |
xfree(wfullpath); |
|
612 | ||
613 |
if (fullpath) |
|
614 |
xfree(fullpath); |
|
615 | ||
616 |
return result; |
|
617 |
} |
|
618 | ||
8 | 619 |
int |
9 | 620 |
rb_file_load_ok(const char *path) |
10 | 621 |
{ |
... | ... | |
27 | 638 |
} |
28 | 639 |
return ret; |
29 | 640 |
} |
641 | ||
642 |
void |
|
643 |
rb_w32_init_file(void) |
|
644 |
{ |
|
645 |
rb_code_page = rb_hash_new(); |
|
646 | ||
647 |
/* prevent GC removing rb_code_page */ |
|
648 |
rb_gc_register_mark_object(rb_code_page); |
|
649 |
} |