summaryrefslogtreecommitdiff
path: root/file.c
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-01-12 00:32:22 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-01-12 00:32:22 +0000
commit853dd6cabe692251376271421be433d68bea2865 (patch)
tree85adecadc51445c95962b556da3f34cfd0c639da /file.c
parentf86cca6f55ce0e9ec50bc1cb61718d02ebe31e10 (diff)
* 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
Diffstat (limited to 'file.c')
-rw-r--r--file.c141
1 files changed, 141 insertions, 0 deletions
diff --git a/file.c b/file.c
index 331bb2e244..969c75eb16 100644
--- a/file.c
+++ b/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);