[前][次][番号順一覧][スレッド一覧]

ruby-changes:14458

From: akr <ko1@a...>
Date: Tue, 12 Jan 2010 09:32:40 +0900 (JST)
Subject: [ruby-changes:14458] Ruby:r26290 (trunk): * prelude.rb (require_relative): use File.realpath.

akr	2010-01-12 09:32:22 +0900 (Tue, 12 Jan 2010)

  New Revision: 26290

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=26290

  Log:
    * 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.

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/dir.c
    trunk/file.c
    trunk/include/ruby/intern.h
    trunk/lib/pathname.rb
    trunk/prelude.rb
    trunk/test/ruby/test_require.rb

Index: prelude.rb
===================================================================
--- prelude.rb	(revision 26289)
+++ prelude.rb	(revision 26290)
@@ -32,7 +32,7 @@
     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
Index: include/ruby/intern.h
===================================================================
--- include/ruby/intern.h	(revision 26289)
+++ include/ruby/intern.h	(revision 26290)
@@ -346,6 +346,8 @@
 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);
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 26289)
+++ ChangeLog	(revision 26290)
@@ -1,3 +1,18 @@
+Tue Jan 12 09:22:43 2010  Tanaka Akira  <akr@f...>
+
+	* 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@i...>
 
 	* hash.c (ruby_setenv): Improve the emulatation of setenv(3) on
Index: lib/pathname.rb
===================================================================
--- lib/pathname.rb	(revision 26289)
+++ lib/pathname.rb	(revision 26290)
@@ -435,61 +435,6 @@
   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 @@
   # called.
   #
   def realpath
-    real_path_internal(true)
+    self.class.new(File.realpath(@path))
   end
 
   #
@@ -508,7 +453,7 @@
   # 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.
Index: dir.c
===================================================================
--- dir.c	(revision 26289)
+++ dir.c	(revision 26290)
@@ -851,6 +851,21 @@
     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 @@
 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
Index: NEWS
===================================================================
--- NEWS	(revision 26289)
+++ NEWS	(revision 26290)
@@ -52,6 +52,11 @@
       * Float::INFINITY
       * Float::NAN
 
+  * File
+    * new methods:
+      * File.realpath
+      * File.realdirpath
+
   * IO
     * new method:
       * IO#fdatasync
Index: test/ruby/test_require.rb
===================================================================
--- test/ruby/test_require.rb	(revision 26289)
+++ test/ruby/test_require.rb	(revision 26290)
@@ -2,6 +2,7 @@
 
 require 'tempfile'
 require_relative 'envutil'
+require 'tmpdir'
 
 class TestRequire < Test::Unit::TestCase
   def test_require_invalid_shared_object
@@ -246,7 +247,6 @@
   end
 
   def test_relative
-    require 'tmpdir'
     load_path = $:.dup
     $:.delete(".")
     Dir.mktmpdir do |tmp|
@@ -268,4 +268,19 @@
   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
Index: file.c
===================================================================
--- file.c	(revision 26289)
+++ file.c	(revision 26290)
@@ -3082,6 +3082,145 @@
     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 @@
     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);

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]