aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--file.c88
-rw-r--r--lib/pathname.rb8
-rw-r--r--test/pathname/test_pathname.rb6
-rw-r--r--test/ruby/test_file.rb20
5 files changed, 102 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index e56eafaa89..fa872f91da 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Sun Mar 7 13:49:49 2010 Tanaka Akira <akr@fsij.org>
+
+ * file.c: add optional basedir argument for realpath/realdirpath.
+ (realpath_internal): handle basedir.
+ (rb_file_s_realpath): extract basedir from argument list.
+ (rb_file_s_realdirpath): extract basedir from argument list.
+
+ * lib/pathname.rb (realpath): pass basedir.
+ (realdirpath): ditto.
+
Sun Mar 7 02:05:38 2010 NARUSE, Yui <naruse@ruby-lang.org>
* encoding.c (enc_set_filesystem_encoding):
diff --git a/file.c b/file.c
index 7aca492c65..50516ad4f8 100644
--- a/file.c
+++ b/file.c
@@ -3166,65 +3166,105 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, char *unresolved, VALUE loopche
}
static VALUE
-realpath_internal(VALUE path, int strict)
+realpath_internal(VALUE basedir, VALUE path, int strict)
{
long prefixlen;
VALUE resolved;
volatile VALUE unresolved_path;
- char *unresolved_names;
VALUE loopcheck;
+ volatile VALUE curdir = Qnil;
+
+ char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
+ char *ptr;
rb_secure(2);
FilePathValue(path);
unresolved_path = rb_str_dup_frozen(path);
- unresolved_names = skiproot(RSTRING_PTR(unresolved_path));
- prefixlen = unresolved_names - RSTRING_PTR(unresolved_path);
- loopcheck = rb_hash_new();
- if (prefixlen == 0) {
- volatile VALUE curdir = rb_dir_getwd();
- char *unresolved_curdir_names = skiproot(RSTRING_PTR(curdir));
- prefixlen = unresolved_curdir_names - RSTRING_PTR(curdir);
- resolved = rb_str_new(RSTRING_PTR(curdir), prefixlen);
- realpath_rec(&prefixlen, &resolved, unresolved_curdir_names, loopcheck, 1, 0);
+
+ if (!NIL_P(basedir)) {
+ FilePathValue(basedir);
+ basedir = rb_str_dup_frozen(basedir);
}
- else {
- resolved = rb_str_new(RSTRING_PTR(unresolved_path), prefixlen);
+
+ ptr = RSTRING_PTR(unresolved_path);
+ path_names = skiproot(ptr);
+ if (ptr != path_names) {
+ resolved = rb_str_new(ptr, path_names - ptr);
+ goto root_found;
}
- realpath_rec(&prefixlen, &resolved, unresolved_names, loopcheck, strict, 1);
+
+ if (!NIL_P(basedir)) {
+ ptr = RSTRING_PTR(basedir);
+ basedir_names = skiproot(ptr);
+ if (ptr != basedir_names) {
+ resolved = rb_str_new(ptr, basedir_names - ptr);
+ goto root_found;
+ }
+ }
+
+ curdir = rb_dir_getwd();
+ ptr = RSTRING_PTR(curdir);
+ curdir_names = skiproot(ptr);
+ resolved = rb_str_new(ptr, curdir_names - ptr);
+
+ root_found:
+ ptr = chompdirsep(RSTRING_PTR(resolved));
+ if (*ptr) {
+ rb_str_set_len(resolved, ptr - RSTRING_PTR(resolved) + 1);
+ }
+ prefixlen = RSTRING_LEN(resolved);
+
+ loopcheck = rb_hash_new();
+ if (curdir_names)
+ realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0);
+ if (basedir_names)
+ realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0);
+ realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1);
+
OBJ_TAINT(resolved);
return resolved;
}
/*
* call-seq:
- * File.realpath(pathname) -> real_pathname
+ * File.realpath(pathname [, dir_string]) -> real_pathname
*
- * Returns the real (absolute) pathname of +pathname+ in the actual
+ * Returns the real (absolute) pathname of _pathname_ in the actual
* filesystem not containing symlinks or useless dots.
+ *
+ * If _dir_string_ is given, it is used as a base directory
+ * for interpreting relative pathname instead of the current directory.
*
* All components of the pathname must exist when this method is
* called.
*/
static VALUE
-rb_file_s_realpath(VALUE klass, VALUE path)
+rb_file_s_realpath(int argc, VALUE *argv, VALUE klass)
{
- return realpath_internal(path, 1);
+ VALUE path, basedir;
+ rb_scan_args(argc, argv, "11", &path, &basedir);
+ return realpath_internal(basedir, path, 1);
}
/*
* call-seq:
- * File.realdirpath(pathname) -> real_pathname
+ * File.realdirpath(pathname [, dir_string]) -> real_pathname
*
- * Returns the real (absolute) pathname of +pathname+ in the actual filesystem.
+ * Returns the real (absolute) pathname of _pathname_ in the actual filesystem.
* The real pathname doesn't contain symlinks or useless dots.
*
+ * If _dir_string_ is given, it is used as a base directory
+ * for interpreting relative pathname instead of the current directory.
+ *
* The last component of the real pathname can be nonexistent.
*/
static VALUE
-rb_file_s_realdirpath(VALUE klass, VALUE path)
+rb_file_s_realdirpath(int argc, VALUE *argv, VALUE klass)
{
- return realpath_internal(path, 0);
+ VALUE path, basedir;
+ rb_scan_args(argc, argv, "11", &path, &basedir);
+ return realpath_internal(basedir, path, 0);
}
static size_t
@@ -5041,8 +5081,8 @@ Init_File(void)
rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
rb_define_singleton_method(rb_cFile, "expand_path", rb_file_s_expand_path, -1);
rb_define_singleton_method(rb_cFile, "absolute_path", rb_file_s_absolute_path, -1);
- rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, 1);
- rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, 1);
+ rb_define_singleton_method(rb_cFile, "realpath", rb_file_s_realpath, -1);
+ rb_define_singleton_method(rb_cFile, "realdirpath", rb_file_s_realdirpath, -1);
rb_define_singleton_method(rb_cFile, "basename", rb_file_s_basename, -1);
rb_define_singleton_method(rb_cFile, "dirname", rb_file_s_dirname, 1);
rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
diff --git a/lib/pathname.rb b/lib/pathname.rb
index bfd3771c28..cede33d273 100644
--- a/lib/pathname.rb
+++ b/lib/pathname.rb
@@ -442,8 +442,8 @@ class Pathname
# All components of the pathname must exist when this method is
# called.
#
- def realpath
- self.class.new(File.realpath(@path))
+ def realpath(basedir=nil)
+ self.class.new(File.realpath(@path, basedir))
end
#
@@ -452,8 +452,8 @@ class Pathname
#
# The last component of the real pathname can be nonexistent.
#
- def realdirpath
- self.class.new(File.realdirpath(@path))
+ def realdirpath(basedir=nil)
+ self.class.new(File.realdirpath(@path, basedir))
end
# #parent returns the parent directory.
diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb
index a9f7149633..5dfd9c6e31 100644
--- a/test/pathname/test_pathname.rb
+++ b/test/pathname/test_pathname.rb
@@ -288,8 +288,8 @@ class TestPathname < Test::Unit::TestCase
return true
end
- def realpath(path)
- Pathname.new(path).realpath.to_s
+ def realpath(path, basedir=nil)
+ Pathname.new(path).realpath(basedir).to_s
end
def test_realpath
@@ -301,6 +301,7 @@ class TestPathname < Test::Unit::TestCase
File.symlink("loop", "#{dir}/loop")
assert_raise(Errno::ELOOP) { realpath("#{dir}/loop") }
+ assert_raise(Errno::ELOOP) { realpath("#{dir}/loop", dir) }
File.symlink("../#{File.basename(dir)}/./not-exist-target", "#{dir}/not-exist2")
assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist2") }
@@ -314,6 +315,7 @@ class TestPathname < Test::Unit::TestCase
Dir.mkdir("exist")
assert_equal("#{dir}/exist", realpath("exist"))
+ assert_raise(Errno::ELOOP) { realpath("../loop", "#{dir}/exist") }
File.symlink("loop1/loop1", "loop1")
assert_raise(Errno::ELOOP) { realpath("#{dir}/loop1") }
diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb
index b8ba9a4b19..90f2ec1f53 100644
--- a/test/ruby/test_file.rb
+++ b/test/ruby/test_file.rb
@@ -157,4 +157,24 @@ class TestFile < Test::Unit::TestCase
assert_raise(TypeError) { File::Stat.allocate.readable? }
assert_nothing_raised { File::Stat.allocate.inspect }
end
+
+ def test_realpath
+ Dir.mktmpdir('rubytest-realpath') {|tmpdir|
+ realdir = File.realpath(tmpdir)
+ tst = realdir.sub(/#{Regexp.escape(File::SEPARATOR)}/, '\0\0\0')
+ assert_equal(realdir, File.realpath(tst))
+ assert_equal(realdir, File.realpath(".", tst))
+ }
+ end
+
+ def test_realdirpath
+ Dir.mktmpdir('rubytest-realdirpath') {|tmpdir|
+ realdir = File.realpath(tmpdir)
+ tst = realdir.sub(/#{Regexp.escape(File::SEPARATOR)}/, '\0\0\0')
+ assert_equal(realdir, File.realdirpath(tst))
+ assert_equal(realdir, File.realdirpath(".", tst))
+ assert_equal(File.join(realdir, "foo"), File.realdirpath("foo", tst))
+ }
+ end
+
end