mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* prelude.rb (require_relative): use File.realpath. [ruby-dev:40040]
* include/ruby/intern.h: declare rb_dir_getwd. * dir.c (rb_dir_getwd): copied from dir_s_getwd to export. (dir_s_getwd): use rb_dir_getwd. * file.c (rb_file_s_realpath): new method File.realpath. (rb_file_s_realdirpath): new method File.realdirpath. * lib/pathname.rb (Pathname#realpath): use File.realpath. (Pathname#realdirpath): use File.realdirpath. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26290 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
f86cca6f55
commit
853dd6cabe
8 changed files with 198 additions and 69 deletions
15
ChangeLog
15
ChangeLog
|
@ -1,3 +1,18 @@
|
||||||
|
Tue Jan 12 09:22:43 2010 Tanaka Akira <akr@fsij.org>
|
||||||
|
|
||||||
|
* prelude.rb (require_relative): use File.realpath. [ruby-dev:40040]
|
||||||
|
|
||||||
|
* include/ruby/intern.h: declare rb_dir_getwd.
|
||||||
|
|
||||||
|
* dir.c (rb_dir_getwd): copied from dir_s_getwd to export.
|
||||||
|
(dir_s_getwd): use rb_dir_getwd.
|
||||||
|
|
||||||
|
* file.c (rb_file_s_realpath): new method File.realpath.
|
||||||
|
(rb_file_s_realdirpath): new method File.realdirpath.
|
||||||
|
|
||||||
|
* lib/pathname.rb (Pathname#realpath): use File.realpath.
|
||||||
|
(Pathname#realdirpath): use File.realdirpath.
|
||||||
|
|
||||||
Mon Jan 11 22:45:08 2010 Akinori MUSHA <knu@iDaemons.org>
|
Mon Jan 11 22:45:08 2010 Akinori MUSHA <knu@iDaemons.org>
|
||||||
|
|
||||||
* hash.c (ruby_setenv): Improve the emulatation of setenv(3) on
|
* hash.c (ruby_setenv): Improve the emulatation of setenv(3) on
|
||||||
|
|
5
NEWS
5
NEWS
|
@ -52,6 +52,11 @@ with all sufficient information, see the ChangeLog file.
|
||||||
* Float::INFINITY
|
* Float::INFINITY
|
||||||
* Float::NAN
|
* Float::NAN
|
||||||
|
|
||||||
|
* File
|
||||||
|
* new methods:
|
||||||
|
* File.realpath
|
||||||
|
* File.realdirpath
|
||||||
|
|
||||||
* IO
|
* IO
|
||||||
* new method:
|
* new method:
|
||||||
* IO#fdatasync
|
* IO#fdatasync
|
||||||
|
|
26
dir.c
26
dir.c
|
@ -851,6 +851,21 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
|
||||||
return INT2FIX(0);
|
return INT2FIX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE
|
||||||
|
rb_dir_getwd(void)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
VALUE cwd;
|
||||||
|
|
||||||
|
rb_secure(4);
|
||||||
|
path = my_getcwd();
|
||||||
|
cwd = rb_tainted_str_new2(path);
|
||||||
|
rb_enc_associate(cwd, rb_filesystem_encoding());
|
||||||
|
|
||||||
|
xfree(path);
|
||||||
|
return cwd;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* Dir.getwd => string
|
* Dir.getwd => string
|
||||||
|
@ -865,16 +880,7 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
|
||||||
static VALUE
|
static VALUE
|
||||||
dir_s_getwd(VALUE dir)
|
dir_s_getwd(VALUE dir)
|
||||||
{
|
{
|
||||||
char *path;
|
return rb_dir_getwd();
|
||||||
VALUE cwd;
|
|
||||||
|
|
||||||
rb_secure(4);
|
|
||||||
path = my_getcwd();
|
|
||||||
cwd = rb_tainted_str_new2(path);
|
|
||||||
rb_enc_associate(cwd, rb_filesystem_encoding());
|
|
||||||
|
|
||||||
xfree(path);
|
|
||||||
return cwd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
141
file.c
141
file.c
|
@ -3082,6 +3082,145 @@ rb_file_s_absolute_path(int argc, VALUE *argv)
|
||||||
return rb_file_absolute_path(fname, dname);
|
return rb_file_absolute_path(fname, dname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
realpath_rec(long *prefixlenp, VALUE *resolvedp, char *unresolved, VALUE loopcheck, int strict, int last)
|
||||||
|
{
|
||||||
|
while (*unresolved) {
|
||||||
|
char *testname = unresolved;
|
||||||
|
char *unresolved_firstsep = rb_path_next(unresolved);
|
||||||
|
long testnamelen = unresolved_firstsep - unresolved;
|
||||||
|
char *unresolved_nextname = unresolved_firstsep;
|
||||||
|
while (isdirsep(*unresolved_nextname)) unresolved_nextname++;
|
||||||
|
unresolved = unresolved_nextname;
|
||||||
|
if (testnamelen == 1 && testname[0] == '.') {
|
||||||
|
}
|
||||||
|
else if (testnamelen == 2 && testname[0] == '.' && testname[1] == '.') {
|
||||||
|
if (*prefixlenp < RSTRING_LEN(*resolvedp)) {
|
||||||
|
char *resolved_names = RSTRING_PTR(*resolvedp) + *prefixlenp;
|
||||||
|
long len = rb_path_last_separator(resolved_names) - resolved_names;
|
||||||
|
rb_str_modify(*resolvedp);
|
||||||
|
rb_str_set_len(*resolvedp, *prefixlenp + len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VALUE checkval;
|
||||||
|
VALUE testpath = rb_str_dup(*resolvedp);
|
||||||
|
if (*prefixlenp < RSTRING_LEN(testpath))
|
||||||
|
rb_str_cat2(testpath, "/");
|
||||||
|
rb_str_cat(testpath, testname, testnamelen);
|
||||||
|
checkval = rb_hash_aref(loopcheck, testpath);
|
||||||
|
if (!NIL_P(checkval)) {
|
||||||
|
if (checkval == ID2SYM(rb_intern("resolving"))) {
|
||||||
|
errno = ELOOP;
|
||||||
|
rb_sys_fail(RSTRING_PTR(testpath));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*resolvedp = rb_str_dup(checkval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
struct stat sbuf;
|
||||||
|
int ret;
|
||||||
|
ret = lstat(RSTRING_PTR(testpath), &sbuf);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
if (strict || !last || *unresolved_firstsep)
|
||||||
|
rb_sys_fail(RSTRING_PTR(testpath));
|
||||||
|
*resolvedp = testpath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_sys_fail(RSTRING_PTR(testpath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (S_ISLNK(sbuf.st_mode)) {
|
||||||
|
volatile VALUE link;
|
||||||
|
char *link_prefix, *link_names;
|
||||||
|
long link_prefixlen;
|
||||||
|
rb_hash_aset(loopcheck, testpath, ID2SYM(rb_intern("resolving")));
|
||||||
|
link = rb_file_s_readlink(rb_cFile, testpath);
|
||||||
|
link_prefix = RSTRING_PTR(link);
|
||||||
|
link_names = skiproot(link_prefix);
|
||||||
|
link_prefixlen = link_names - link_prefix;
|
||||||
|
if (link_prefixlen == 0) {
|
||||||
|
realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*resolvedp = rb_str_new(link_prefix, link_prefixlen);
|
||||||
|
*prefixlenp = link_prefixlen;
|
||||||
|
realpath_rec(prefixlenp, resolvedp, link_names, loopcheck, strict, *unresolved_firstsep == '\0');
|
||||||
|
}
|
||||||
|
rb_hash_aset(loopcheck, testpath, rb_str_dup_frozen(*resolvedp));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VALUE s = rb_str_dup_frozen(testpath);
|
||||||
|
rb_hash_aset(loopcheck, s, s);
|
||||||
|
*resolvedp = testpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
realpath_internal(VALUE path, int strict)
|
||||||
|
{
|
||||||
|
long prefixlen;
|
||||||
|
VALUE resolved;
|
||||||
|
volatile VALUE unresolved_path;
|
||||||
|
char *unresolved_names;
|
||||||
|
VALUE loopcheck;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolved = rb_str_new(RSTRING_PTR(unresolved_path), prefixlen);
|
||||||
|
}
|
||||||
|
realpath_rec(&prefixlen, &resolved, unresolved_names, loopcheck, strict, 1);
|
||||||
|
OBJ_TAINT(resolved);
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* File.realpath(pathname) -> real_pathname
|
||||||
|
*
|
||||||
|
* Returns the real (absolute) pathname of +pathname+ in the actual
|
||||||
|
* filesystem not containing symlinks or useless dots.
|
||||||
|
*
|
||||||
|
* All components of the pathname must exist when this method is
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
rb_file_s_realpath(VALUE klass, VALUE path)
|
||||||
|
{
|
||||||
|
return realpath_internal(path, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* File.realdirpath(pathname) -> real_pathname
|
||||||
|
*
|
||||||
|
* Returns the real (absolute) pathname of +pathname+ in the actual filesystem.
|
||||||
|
* The real pathname doesn't contain symlinks or useless dots.
|
||||||
|
*
|
||||||
|
* The last component of the real pathname can be nonexistent.
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
rb_file_s_realdirpath(VALUE klass, VALUE path)
|
||||||
|
{
|
||||||
|
return realpath_internal(path, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
rmext(const char *p, long l1, const char *e)
|
rmext(const char *p, long l1, const char *e)
|
||||||
{
|
{
|
||||||
|
@ -4896,6 +5035,8 @@ Init_File(void)
|
||||||
rb_define_singleton_method(rb_cFile, "truncate", rb_file_s_truncate, 2);
|
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, "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, "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, "basename", rb_file_s_basename, -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, "dirname", rb_file_s_dirname, 1);
|
||||||
rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
|
rb_define_singleton_method(rb_cFile, "extname", rb_file_s_extname, 1);
|
||||||
|
|
|
@ -346,6 +346,8 @@ void rb_thread_atfork_before_exec(void);
|
||||||
VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE);
|
VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE);
|
||||||
VALUE rb_exec_recursive_paired(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE,VALUE);
|
VALUE rb_exec_recursive_paired(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE,VALUE);
|
||||||
VALUE rb_exec_recursive_outer(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE);
|
VALUE rb_exec_recursive_outer(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE);
|
||||||
|
/* dir.c */
|
||||||
|
VALUE rb_dir_getwd(void);
|
||||||
/* file.c */
|
/* file.c */
|
||||||
VALUE rb_file_s_expand_path(int, VALUE *);
|
VALUE rb_file_s_expand_path(int, VALUE *);
|
||||||
VALUE rb_file_expand_path(VALUE, VALUE);
|
VALUE rb_file_expand_path(VALUE, VALUE);
|
||||||
|
|
|
@ -435,61 +435,6 @@ class Pathname
|
||||||
end
|
end
|
||||||
private :cleanpath_conservative
|
private :cleanpath_conservative
|
||||||
|
|
||||||
def realpath_rec(prefix, unresolved, h, strict, last = true)
|
|
||||||
resolved = []
|
|
||||||
until unresolved.empty?
|
|
||||||
n = unresolved.shift
|
|
||||||
if n == '.'
|
|
||||||
next
|
|
||||||
elsif n == '..'
|
|
||||||
resolved.pop
|
|
||||||
else
|
|
||||||
path = prepend_prefix(prefix, File.join(*(resolved + [n])))
|
|
||||||
if h.include? path
|
|
||||||
if h[path] == :resolving
|
|
||||||
raise Errno::ELOOP.new(path)
|
|
||||||
else
|
|
||||||
prefix, *resolved = h[path]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
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, strict, unresolved.empty?)
|
|
||||||
else
|
|
||||||
prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h, strict, unresolved.empty?)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
resolved << n
|
|
||||||
h[path] = [prefix, *resolved]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return prefix, *resolved
|
|
||||||
end
|
|
||||||
private :realpath_rec
|
|
||||||
|
|
||||||
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, {}, 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
|
# Returns the real (absolute) pathname of +self+ in the actual
|
||||||
# filesystem not containing symlinks or useless dots.
|
# filesystem not containing symlinks or useless dots.
|
||||||
|
@ -498,7 +443,7 @@ class Pathname
|
||||||
# called.
|
# called.
|
||||||
#
|
#
|
||||||
def realpath
|
def realpath
|
||||||
real_path_internal(true)
|
self.class.new(File.realpath(@path))
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -508,7 +453,7 @@ class Pathname
|
||||||
# The last component of the real pathname can be nonexistent.
|
# The last component of the real pathname can be nonexistent.
|
||||||
#
|
#
|
||||||
def realdirpath
|
def realdirpath
|
||||||
real_path_internal(false)
|
self.class.new(File.realdirpath(@path))
|
||||||
end
|
end
|
||||||
|
|
||||||
# #parent returns the parent directory.
|
# #parent returns the parent directory.
|
||||||
|
|
|
@ -32,7 +32,7 @@ module Kernel
|
||||||
if /\A\((.*)\)/ =~ file # eval, etc.
|
if /\A\((.*)\)/ =~ file # eval, etc.
|
||||||
raise LoadError, "require_relative is called in #{$1}"
|
raise LoadError, "require_relative is called in #{$1}"
|
||||||
end
|
end
|
||||||
absolute_feature = File.expand_path(File.join(File.dirname(file), relative_feature))
|
absolute_feature = File.join(File.dirname(File.realpath(file)), relative_feature)
|
||||||
require absolute_feature
|
require absolute_feature
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@ require 'test/unit'
|
||||||
|
|
||||||
require 'tempfile'
|
require 'tempfile'
|
||||||
require_relative 'envutil'
|
require_relative 'envutil'
|
||||||
|
require 'tmpdir'
|
||||||
|
|
||||||
class TestRequire < Test::Unit::TestCase
|
class TestRequire < Test::Unit::TestCase
|
||||||
def test_require_invalid_shared_object
|
def test_require_invalid_shared_object
|
||||||
|
@ -246,7 +247,6 @@ class TestRequire < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_relative
|
def test_relative
|
||||||
require 'tmpdir'
|
|
||||||
load_path = $:.dup
|
load_path = $:.dup
|
||||||
$:.delete(".")
|
$:.delete(".")
|
||||||
Dir.mktmpdir do |tmp|
|
Dir.mktmpdir do |tmp|
|
||||||
|
@ -268,4 +268,19 @@ class TestRequire < Test::Unit::TestCase
|
||||||
ensure
|
ensure
|
||||||
$:.replace(load_path) if load_path
|
$:.replace(load_path) if load_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_relative_symlink
|
||||||
|
Dir.mktmpdir {|tmp|
|
||||||
|
Dir.chdir(tmp) {
|
||||||
|
Dir.mkdir "a"
|
||||||
|
Dir.mkdir "b"
|
||||||
|
File.open("a/lib.rb", "w") {|f| f.puts 'puts "a/lib.rb"' }
|
||||||
|
File.open("b/lib.rb", "w") {|f| f.puts 'puts "b/lib.rb"' }
|
||||||
|
File.open("a/tst.rb", "w") {|f| f.puts 'require_relative "lib"' }
|
||||||
|
File.symlink("../a/tst.rb", "b/tst.rb")
|
||||||
|
result = IO.popen([EnvUtil.rubybin, "b/tst.rb"]).read
|
||||||
|
assert_equal("a/lib.rb\n", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue