1
0
Fork 0
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:
akr 2010-01-12 00:32:22 +00:00
parent f86cca6f55
commit 853dd6cabe
8 changed files with 198 additions and 69 deletions

View file

@ -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
View file

@ -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
View file

@ -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
View file

@ -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);

View file

@ -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);

View file

@ -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.

View file

@ -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

View file

@ -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