diff --git a/ChangeLog b/ChangeLog index 663e8b4..b565825 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Thu Mar 14 12:53:15 2013 Luis Lavena + + * win32/file.c (get_user_from_path): add internal function that retrieves + username from supplied path (refactored). + * win32/file.c (rb_file_expand_path_internal): refactor expansion of user + home to use get_user_from_path and cover dir_string corner cases. + [ruby-core:53168] [Bug #8034] + +Sun Mar 10 23:38:15 2013 Luis Lavena + + * win32/file.c (rb_file_expand_path_internal): Expand home directory when + used as second parameter (dir_string). [ruby-core:53168] [Bug #8034] + * test/ruby/test_file_exhaustive.rb: add test to verify. + Tue Mar 12 00:56:19 2013 KOSAKI Motohiro * test/thread/test_queue.rb (TestQueue#test_thr_kill): reduce diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index 360a1c7..8a33258 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -475,10 +475,12 @@ class TestFileExhaustive < Test::Unit::TestCase ENV["HOME"] = home end + UnknownUserHome = "~foo_bar_baz_unknown_user_wahaha".freeze + def test_expand_path_home assert_kind_of(String, File.expand_path("~")) if ENV["HOME"] - assert_raise(ArgumentError) { File.expand_path("~foo_bar_baz_unknown_user_wahaha") } - assert_raise(ArgumentError) { File.expand_path("~foo_bar_baz_unknown_user_wahaha", "/") } + assert_raise(ArgumentError) { File.expand_path(UnknownUserHome) } + assert_raise(ArgumentError) { File.expand_path(UnknownUserHome, "/") } begin bug3630 = '[ruby-core:31537]' home = ENV["HOME"] @@ -502,6 +504,21 @@ class TestFileExhaustive < Test::Unit::TestCase end end + def test_expand_path_home_dir_string + home = ENV["HOME"] + new_home = "#{DRIVE}/UserHome" + ENV["HOME"] = new_home + bug8034 = "[ruby-core:53168]" + + assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034 + assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034 + + assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) } + assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) } + ensure + ENV["HOME"] = home + end + def test_expand_path_remove_trailing_alternative_data assert_equal File.join(@rootdir, "aaa"), File.expand_path("#{@rootdir}/aaa::$DATA") assert_equal File.join(@rootdir, "aa:a"), File.expand_path("#{@rootdir}/aa:a:$DATA") diff --git a/win32/file.c b/win32/file.c index dfe1224..350f8da 100644 --- a/win32/file.c +++ b/win32/file.c @@ -317,13 +317,45 @@ replace_to_long_name(wchar_t **wfullpath, size_t size, int heap) return size; } +static inline VALUE +get_user_from_path(wchar_t **wpath, int offset, UINT cp, UINT path_cp, rb_encoding *path_encoding) +{ + VALUE result, tmp; + wchar_t *wuser = *wpath + offset; + wchar_t *pos = wuser; + char *user; + size_t size; + + while (!IS_DIR_SEPARATOR_P(*pos) && *pos != '\0') + pos++; + + *pos = '\0'; + convert_wchar_to_mb(wuser, &user, &size, cp); + + /* convert to VALUE and set the path encoding */ + if (path_cp == INVALID_CODE_PAGE) { + tmp = rb_enc_str_new(user, size, rb_utf8_encoding()); + result = rb_str_encode(tmp, rb_enc_from_encoding(path_encoding), 0, Qnil); + rb_str_resize(tmp, 0); + } + else { + result = rb_enc_str_new(user, size, path_encoding); + } + + if (user) + xfree(user); + + return result; +} + VALUE rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_name, VALUE result) { size_t size = 0, wpath_len = 0, wdir_len = 0, whome_len = 0; size_t buffer_len = 0; char *fullpath = NULL; - wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL, *wdir = NULL; + wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL; + wchar_t *wdir = NULL, *wdir_pos = NULL; wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL; UINT path_cp, cp; VALUE path = fname, dir = dname; @@ -404,32 +436,10 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na } } else if (abs_mode == 0 && wpath_len >= 2 && wpath_pos[0] == L'~') { - wchar_t *wuser = wpath_pos + 1; - wchar_t *pos = wuser; - char *user; - - /* tainted if expanding '~' */ - tainted = 1; - - while (!IS_DIR_SEPARATOR_P(*pos) && *pos != '\0') - pos++; + result = get_user_from_path(&wpath_pos, 1, cp, path_cp, path_encoding); - *pos = '\0'; - convert_wchar_to_mb(wuser, &user, &size, cp); - - /* convert to VALUE and set the path encoding */ - if (path_cp == INVALID_CODE_PAGE) { - VALUE tmp = rb_enc_str_new(user, size, rb_utf8_encoding()); - result = rb_str_encode(tmp, rb_enc_from_encoding(path_encoding), 0, Qnil); - rb_str_resize(tmp, 0); - } - else { - result = rb_enc_str_new(user, size, path_encoding); - } - - xfree(wpath); - if (user) - xfree(user); + if (wpath) + xfree(wpath); rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result)); } @@ -442,9 +452,38 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na } /* convert char * to wchar_t */ - convert_mb_to_wchar(dir, &wdir, NULL, &wdir_len, cp); + convert_mb_to_wchar(dir, &wdir, &wdir_pos, &wdir_len, cp); + + if (abs_mode == 0 && wdir_len > 0 && wdir_pos[0] == L'~' && + (wdir_len == 1 || IS_DIR_SEPARATOR_P(wdir_pos[1]))) { + /* tainted if expanding '~' */ + tainted = 1; - if (wdir_len >= 2 && wdir[1] == L':') { + whome = home_dir(); + if (whome == NULL) { + xfree(wpath); + xfree(wdir); + rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'"); + } + whome_len = wcslen(whome); + + if (PathIsRelativeW(whome) && !(whome_len >= 2 && IS_DIR_UNC_P(whome))) { + xfree(wpath); + xfree(wdir); + rb_raise(rb_eArgError, "non-absolute home"); + } + + /* exclude ~ from the result */ + wdir_pos++; + wdir_len--; + + /* exclude separator if present */ + if (wdir_len && IS_DIR_SEPARATOR_P(wdir_pos[0])) { + wdir_pos++; + wdir_len--; + } + } + else if (wdir_len >= 2 && wdir[1] == L':') { dir_drive = wdir[0]; if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) { wdir_len = 2; @@ -466,6 +505,16 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na wdir_len = pos - 1; } } + else if (abs_mode == 0 && wdir_len >= 2 && wdir_pos[0] == L'~') { + result = get_user_from_path(&wdir_pos, 1, cp, path_cp, path_encoding); + if (wpath) + xfree(wpath); + + if (wdir) + xfree(wdir); + + rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result)); + } } /* determine if we ignore dir or not */ @@ -515,7 +564,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na if (!tainted && OBJ_TAINTED(dir)) tainted = 1; - wcsncpy(buffer_pos, wdir, wdir_len); + wcsncpy(buffer_pos, wdir_pos, wdir_len); buffer_pos += wdir_len; }