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>
|
||||
|
||||
* 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::NAN
|
||||
|
||||
* File
|
||||
* new methods:
|
||||
* File.realpath
|
||||
* File.realdirpath
|
||||
|
||||
* IO
|
||||
* new method:
|
||||
* IO#fdatasync
|
||||
|
|
26
dir.c
26
dir.c
|
@ -851,6 +851,21 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
|
|||
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:
|
||||
* Dir.getwd => string
|
||||
|
@ -865,16 +880,7 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj)
|
|||
static VALUE
|
||||
dir_s_getwd(VALUE dir)
|
||||
{
|
||||
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;
|
||||
return rb_dir_getwd();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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, "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, "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);
|
||||
|
|
|
@ -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_paired(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE,VALUE);
|
||||
VALUE rb_exec_recursive_outer(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE);
|
||||
/* dir.c */
|
||||
VALUE rb_dir_getwd(void);
|
||||
/* file.c */
|
||||
VALUE rb_file_s_expand_path(int, VALUE *);
|
||||
VALUE rb_file_expand_path(VALUE, VALUE);
|
||||
|
|
|
@ -435,61 +435,6 @@ class Pathname
|
|||
end
|
||||
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
|
||||
# filesystem not containing symlinks or useless dots.
|
||||
|
@ -498,7 +443,7 @@ class Pathname
|
|||
# called.
|
||||
#
|
||||
def realpath
|
||||
real_path_internal(true)
|
||||
self.class.new(File.realpath(@path))
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -508,7 +453,7 @@ class Pathname
|
|||
# The last component of the real pathname can be nonexistent.
|
||||
#
|
||||
def realdirpath
|
||||
real_path_internal(false)
|
||||
self.class.new(File.realdirpath(@path))
|
||||
end
|
||||
|
||||
# #parent returns the parent directory.
|
||||
|
|
|
@ -32,7 +32,7 @@ module Kernel
|
|||
if /\A\((.*)\)/ =~ file # eval, etc.
|
||||
raise LoadError, "require_relative is called in #{$1}"
|
||||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'test/unit'
|
|||
|
||||
require 'tempfile'
|
||||
require_relative 'envutil'
|
||||
require 'tmpdir'
|
||||
|
||||
class TestRequire < Test::Unit::TestCase
|
||||
def test_require_invalid_shared_object
|
||||
|
@ -246,7 +247,6 @@ class TestRequire < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_relative
|
||||
require 'tmpdir'
|
||||
load_path = $:.dup
|
||||
$:.delete(".")
|
||||
Dir.mktmpdir do |tmp|
|
||||
|
@ -268,4 +268,19 @@ class TestRequire < Test::Unit::TestCase
|
|||
ensure
|
||||
$:.replace(load_path) if load_path
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue