summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-01-30 12:50:55 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-01-30 12:50:55 +0000
commite94e674d0e8a8ab7641717c2d2ba4b5aac5c7295 (patch)
tree2d743938a5b84a0a802fc32d5f55176d66fdde83
parent7a4a31064a59eba104d7204a320220a6cdbddf60 (diff)
* lib/pathname.rb (Pathname#realdirpath): new method.
[ruby-dev:36290] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21904 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog5
-rw-r--r--NEWS3
-rw-r--r--lib/pathname.rb55
-rw-r--r--test/pathname/test_pathname.rb47
4 files changed, 89 insertions, 21 deletions
diff --git a/ChangeLog b/ChangeLog
index 837aa99d96..7e8893fde8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Fri Jan 30 21:49:32 2009 Tanaka Akira <akr@fsij.org>
+
+ * lib/pathname.rb (Pathname#realdirpath): new method.
+ [ruby-dev:36290]
+
Fri Jan 30 18:04:23 2009 NAKAMURA Usaku <usa@ruby-lang.org>
* win32/win32.c (rb_w32_write): limit write size to 32KB if the file
diff --git a/NEWS b/NEWS
index e52801b798..a54df1b2a6 100644
--- a/NEWS
+++ b/NEWS
@@ -85,6 +85,9 @@ with all sufficient information, see the ChangeLog file.
socket option name and shutdown's how argument can be specified as a
string/symbol.
+* pathname
+ * realdirpath
+
=== Compatibility issues (excluding feature bug fixes)
* Enumerator#rewind
diff --git a/lib/pathname.rb b/lib/pathname.rb
index 2b25ca957d..788e7d4e3f 100644
--- a/lib/pathname.rb
+++ b/lib/pathname.rb
@@ -76,9 +76,9 @@
#
# === Core methods
#
-# These methods are effectively manipulating a String, because that's all a path
-# is. Except for #mountpoint?, #children, and #realpath, they don't access the
-# filesystem.
+# These methods are effectively manipulating a String, because that's
+# all a path is. Except for #mountpoint?, #children, #realdirpath
+# and #realpath, they don't access the filesystem.
#
# - +
# - #join
@@ -90,6 +90,7 @@
# - #each_filename
# - #cleanpath
# - #realpath
+# - #realdirpath
# - #children
# - #mountpoint?
#
@@ -411,7 +412,7 @@ class Pathname
end
private :cleanpath_conservative
- def realpath_rec(prefix, unresolved, h)
+ def realpath_rec(prefix, unresolved, h, strict, last = true)
resolved = []
until unresolved.empty?
n = unresolved.shift
@@ -428,14 +429,20 @@ class Pathname
prefix, *resolved = h[path]
end
else
- s = File.lstat(path)
+ begin
+ s = File.lstat(path)
+ rescue Errno::ENOENT => e
+ raise e if strict || !last || !unresolved.empty?
+ resolved << n
+ break
+ end
if s.symlink?
h[path] = :resolving
link_prefix, link_names = split_names(File.readlink(path))
if link_prefix == ''
- prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h)
+ prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h, strict, unresolved.empty?)
else
- prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h)
+ prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h, strict, unresolved.empty?)
end
else
resolved << n
@@ -448,22 +455,40 @@ class Pathname
end
private :realpath_rec
- #
- # Returns a real (absolute) pathname of +self+ in the actual filesystem.
- # The real pathname doesn't contain symlinks or useless dots.
- #
- # No arguments should be given; the old behaviour is *obsoleted*.
- #
- def realpath
+ def real_path_internal(strict = false)
path = @path
prefix, names = split_names(path)
if prefix == ''
prefix, names2 = split_names(Dir.pwd)
names = names2 + names
end
- prefix, *names = realpath_rec(prefix, names, {})
+ prefix, *names = realpath_rec(prefix, names, {}, strict)
self.class.new(prepend_prefix(prefix, File.join(*names)))
end
+ private :real_path_internal
+
+ #
+ # Returns the real (absolute) pathname of +self+ in the actual
+ # filesystem not containing symlinks or useless dots.
+ #
+ # All components of the pathname must exist when this method is
+ # called.
+ #
+ # No arguments should be given; the old behaviour is *obsoleted*.
+ #
+ def realpath
+ real_path_internal(true)
+ end
+
+ #
+ # Returns the real (absolute) pathname of +self+ in the actual filesystem.
+ # The real pathname doesn't contain symlinks or useless dots.
+ #
+ # The last component of the real pathname can be nonexistent.
+ #
+ def realdirpath
+ real_path_internal(false)
+ end
# #parent returns the parent directory.
#
diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb
index 2b0d8a6a99..d979ff7023 100644
--- a/test/pathname/test_pathname.rb
+++ b/test/pathname/test_pathname.rb
@@ -278,24 +278,37 @@ class TestPathname < Test::Unit::TestCase
}
end
- def realpath(path)
- Pathname.new(path).realpath.to_s
- end
-
- def test_realpath
+ def has_symlink?
begin
File.symlink(nil, nil)
rescue NotImplementedError
- return
+ return false
rescue TypeError
end
+ return true
+ end
+
+ def realpath(path)
+ Pathname.new(path).realpath.to_s
+ end
+
+ def test_realpath
+ return if !has_symlink?
with_tmpchdir('rubytest-pathname') {|dir|
+ assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist") }
File.symlink("not-exist-target", "#{dir}/not-exist")
assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist") }
File.symlink("loop", "#{dir}/loop")
assert_raise(Errno::ELOOP) { realpath("#{dir}/loop") }
+ File.symlink("../#{File.basename(dir)}/./not-exist-target", "#{dir}/not-exist2")
+ assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist2") }
+
+ File.open("#{dir}/exist-target", "w") {}
+ File.symlink("../#{File.basename(dir)}/./exist-target", "#{dir}/exist2")
+ assert_nothing_raised { realpath("#{dir}/exist2") }
+
File.symlink("loop-relative", "loop-relative")
assert_raise(Errno::ELOOP) { realpath("#{dir}/loop-relative") }
@@ -332,6 +345,28 @@ class TestPathname < Test::Unit::TestCase
}
end
+ def realdirpath(path)
+ Pathname.new(path).realdirpath.to_s
+ end
+
+ def test_realdirpath
+ return if !has_symlink?
+ Dir.mktmpdir('rubytest-pathname') {|dir|
+ rdir = realpath(dir)
+ assert_equal("#{rdir}/not-exist", realdirpath("#{dir}/not-exist"))
+ assert_raise(Errno::ENOENT) { realdirpath("#{dir}/not-exist/not-exist-child") }
+ File.symlink("not-exist-target", "#{dir}/not-exist")
+ assert_equal("#{rdir}/not-exist-target", realdirpath("#{dir}/not-exist"))
+ File.symlink("../#{File.basename(dir)}/./not-exist-target", "#{dir}/not-exist2")
+ assert_equal("#{rdir}/not-exist-target", realdirpath("#{dir}/not-exist2"))
+ File.open("#{dir}/exist-target", "w") {}
+ File.symlink("../#{File.basename(dir)}/./exist-target", "#{dir}/exist")
+ assert_equal("#{rdir}/exist-target", realdirpath("#{dir}/exist"))
+ File.symlink("loop", "#{dir}/loop")
+ assert_raise(Errno::ELOOP) { realdirpath("#{dir}/loop") }
+ }
+ end
+
def descend(path)
Pathname.new(path).enum_for(:descend).map {|v| v.to_s }
end