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…
	
	Add table
		Add a link
		
	
		Reference in a new issue