[macruby-changes] [4195] MacRuby/trunk/file.c

source_changes at macosforge.org source_changes at macosforge.org
Wed Jun 2 15:35:21 PDT 2010


Revision: 4195
          http://trac.macosforge.org/projects/ruby/changeset/4195
Author:   martinlagardette at apple.com
Date:     2010-06-02 15:35:19 -0700 (Wed, 02 Jun 2010)
Log Message:
-----------
Backport `File#realpath` and `File#readdirpath`

 - Fixes <rdar://problem/8053799>

Modified Paths:
--------------
    MacRuby/trunk/file.c

Modified: MacRuby/trunk/file.c
===================================================================
--- MacRuby/trunk/file.c	2010-06-02 17:43:29 UTC (rev 4194)
+++ MacRuby/trunk/file.c	2010-06-02 22:35:19 UTC (rev 4195)
@@ -2305,6 +2305,16 @@
     return (char *)path;
 }
 
+#define nextdirsep rb_path_next
+char *
+rb_path_next(const char *s)
+{
+    while (*s && !isdirsep(*s)) {
+	s = CharNext(s);
+    }
+    return (char *)s;
+}
+
 #define skipprefix(path) (path)
 
 #define strrdirsep rb_path_last_separator
@@ -2403,10 +2413,205 @@
 	return rb_file_absolute_path(argv[0], Qnil);
     }
     rb_scan_args(argc, argv, "11", &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)
+{
+    ID resolving = rb_intern("resolving");
+    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 = (char *)RSTRING_PTR(*resolvedp) + *prefixlenp;
+                char *lastsep = rb_path_last_separator(resolved_names);
+                long len = lastsep ? lastsep - resolved_names : 0;
+                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(resolving)) {
+                    errno = ELOOP;
+                    rb_sys_fail((char *)RSTRING_PTR(testpath));
+                }
+                else {
+                    *resolvedp = rb_str_dup(checkval);
+                }
+            }
+            else {
+                struct stat sbuf;
+                int ret;
+                ret = lstat((char *)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((char *)RSTRING_PTR(testpath));
+                    }
+                }
+#ifdef HAVE_READLINK
+                if (S_ISLNK(sbuf.st_mode)) {
+                    volatile VALUE link;
+                    char *link_prefix, *link_names;
+                    long link_prefixlen;
+                    rb_hash_aset(loopcheck, testpath, ID2SYM(resolving));
+                    link = rb_file_s_readlink(rb_cFile, 0, testpath);
+                    link_prefix = (char *)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(*resolvedp));
+                }
+                else
+#endif
+                {
+                    VALUE s = rb_str_dup(testpath);
+                    rb_hash_aset(loopcheck, s, s);
+                    *resolvedp = testpath;
+                }
+            }
+        }
+    }
+}
+
+VALUE
+rb_realpath_internal(VALUE basedir, VALUE path, int strict)
+{
+    long prefixlen;
+    VALUE resolved;
+    volatile VALUE unresolved_path;
+    VALUE loopcheck;
+    volatile VALUE curdir = Qnil;
+
+    char *path_names = NULL, *basedir_names = NULL, *curdir_names = NULL;
+    char *ptr, *prefixptr = NULL;
+
+    rb_secure(2);
+
+    FilePathValue(path);
+    unresolved_path = rb_str_dup(path);
+
+    if (!NIL_P(basedir)) {
+        FilePathValue(basedir);
+        basedir = rb_str_dup(basedir);
+    }
+
+    ptr = (char *)RSTRING_PTR(unresolved_path);
+    path_names = skiproot(ptr);
+    if (ptr != path_names) {
+        resolved = rb_str_new(ptr, path_names - ptr);
+        goto root_found;
+    }
+
+    if (!NIL_P(basedir)) {
+        ptr = (char *)RSTRING_PTR(basedir);
+        basedir_names = skiproot(ptr);
+        if (ptr != basedir_names) {
+            resolved = rb_str_new(ptr, basedir_names - ptr);
+            goto root_found;
+        }
+    }
+
+    curdir = my_getcwd();
+    ptr = (char *)RSTRING_PTR(curdir);
+    curdir_names = skiproot(ptr);
+    resolved = rb_str_new(ptr, curdir_names - ptr);
+
+  root_found:
+    prefixptr = (char *)RSTRING_PTR(resolved);
+    prefixlen = RSTRING_LEN(resolved);
+    ptr = chompdirsep(prefixptr);
+    if (*ptr) {
+        prefixlen = ++ptr - prefixptr;
+        rb_str_set_len(resolved, prefixlen);
+    }
+#ifdef FILE_ALT_SEPARATOR
+    while (prefixptr < ptr) {
+	if (*prefixptr == FILE_ALT_SEPARATOR) {
+	    *prefixptr = '/';
+	}
+	prefixptr = CharNext(prefixptr);
+    }
+#endif
+
+    loopcheck = rb_hash_new();
+    if (curdir_names)
+        realpath_rec(&prefixlen, &resolved, curdir_names, loopcheck, 1, 0);
+    if (basedir_names)
+        realpath_rec(&prefixlen, &resolved, basedir_names, loopcheck, 1, 0);
+    realpath_rec(&prefixlen, &resolved, path_names, loopcheck, strict, 1);
+
+    OBJ_TAINT(resolved);
+    return resolved;
+}
+
+/*
+ * call-seq:
+ *     File.realpath(pathname [, dir_string])  ->  real_pathname
+ *
+ *  Returns the real (absolute) pathname of _pathname_ in the actual
+ *  filesystem not containing symlinks or useless dots.
+ *
+ *  If _dir_string_ is given, it is used as a base directory
+ *  for interpreting relative pathname instead of the current directory.
+ *
+ *  All components of the pathname must exist when this method is
+ *  called.
+ */
+static VALUE
+rb_file_s_realpath(VALUE klass, SEL sel, int argc, VALUE *argv)
+{
+    VALUE path, basedir;
+    rb_scan_args(argc, argv, "11", &path, &basedir);
+    return rb_realpath_internal(basedir, path, 1);
+}
+
+/*
+ * call-seq:
+ *     File.realdirpath(pathname [, dir_string])  ->  real_pathname
+ *
+ *  Returns the real (absolute) pathname of _pathname_ in the actual filesystem.
+ *  The real pathname doesn't contain symlinks or useless dots.
+ *
+ *  If _dir_string_ is given, it is used as a base directory
+ *  for interpreting relative pathname instead of the current directory.
+ *
+ *  The last component of the real pathname can be nonexistent.
+ */
+static VALUE
+rb_file_s_realdirpath(VALUE klass, SEL sel, int argc, VALUE *argv)
+{
+    VALUE path, basedir;
+    rb_scan_args(argc, argv, "11", &path, &basedir);
+    return rb_realpath_internal(basedir, path, 0);
+}
+
 static int
 rmext(const char *p, int l1, const char *e)
 {
@@ -4009,6 +4214,8 @@
     rb_objc_define_method(rb_ccFile, "truncate", rb_file_s_truncate, 2);
     rb_objc_define_method(rb_ccFile, "expand_path", rb_file_s_expand_path, -1);
     rb_objc_define_method(rb_ccFile, "absolute_path", rb_file_s_absolute_path, -1);
+    rb_objc_define_method(rb_ccFile, "realpath", rb_file_s_realpath, -1);
+    rb_objc_define_method(rb_ccFile, "realdirpath", rb_file_s_realdirpath, -1);
     rb_objc_define_method(rb_ccFile, "basename", rb_file_s_basename, -1);
     rb_objc_define_method(rb_ccFile, "dirname", rb_file_s_dirname, 1);
     rb_objc_define_method(rb_ccFile, "extname", rb_file_s_extname, 1);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100602/e7d817a8/attachment-0001.html>


More information about the macruby-changes mailing list