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

ruby-changes:46745

From: nobu <ko1@a...>
Date: Tue, 23 May 2017 23:34:34 +0900 (JST)
Subject: [ruby-changes:46745] nobu:r58860 (trunk): Dir as base option

nobu	2017-05-23 23:34:12 +0900 (Tue, 23 May 2017)

  New Revision: 58860

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58860

  Log:
    Dir as base option
    
    * dir.c (glob_helper): support Dir instance as `base` option.
      [Feature#13056]

  Modified files:
    trunk/configure.in
    trunk/dir.c
    trunk/test/ruby/test_dir.rb
Index: configure.in
===================================================================
--- configure.in	(revision 58859)
+++ configure.in	(revision 58860)
@@ -2410,8 +2410,10 @@ AC_CHECK_FUNCS(fchmod) https://github.com/ruby/ruby/blob/trunk/configure.in#L2410
 AC_CHECK_FUNCS(fchown)
 AC_CHECK_FUNCS(fcntl)
 AC_CHECK_FUNCS(fdatasync)
+AC_CHECK_FUNCS(fdopendir)
 AC_CHECK_FUNCS(fgetattrlist)
 AC_CHECK_FUNCS(fmod)
+AC_CHECK_FUNCS(fstatat)
 AC_CHECK_FUNCS(fsync)
 AC_CHECK_FUNCS(ftruncate)
 AC_CHECK_FUNCS(ftruncate64)		# used for Win32 platform
@@ -2455,6 +2457,7 @@ AC_CHECK_FUNCS(memmem) https://github.com/ruby/ruby/blob/trunk/configure.in#L2457
 AC_CHECK_FUNCS(mkfifo)
 AC_CHECK_FUNCS(mknod)
 AC_CHECK_FUNCS(mktime)
+AC_CHECK_FUNCS(openat)
 AC_CHECK_FUNCS(pipe2)
 AC_CHECK_FUNCS(poll)
 AC_CHECK_FUNCS(posix_fadvise)
Index: dir.c
===================================================================
--- dir.c	(revision 58859)
+++ dir.c	(revision 58860)
@@ -21,6 +21,21 @@ https://github.com/ruby/ruby/blob/trunk/dir.c#L21
 #include <unistd.h>
 #endif
 
+#ifndef USE_OPENDIR_AT
+# if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD) && \
+    defined(HAVE_OPENAT) && defined(HAVE_FSTATAT)
+#   define USE_OPENDIR_AT 1
+# else
+#   define USE_OPENDIR_AT 0
+# endif
+#endif
+#if USE_OPENDIR_AT
+# include <fcntl.h>
+#endif
+#ifndef AT_FDCWD
+# define AT_FDCWD -1
+#endif
+
 #undef HAVE_DIRENT_NAMLEN
 #if defined HAVE_DIRENT_H && !defined _WIN32
 # include <dirent.h>
@@ -1267,20 +1282,28 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/dir.c#L1282
 
 /* System call with warning */
 static int
-do_stat(const char *path, struct stat *pst, int flags, rb_encoding *enc)
+do_stat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc)
 {
+#if USE_OPENDIR_AT
+    int ret = fstatat(fd, path, pst, 0);
+#else
     int ret = STAT(path, pst);
+#endif
     if (ret < 0 && !to_be_ignored(errno))
 	sys_warning(path, enc);
 
     return ret;
 }
 
-#if defined HAVE_LSTAT || defined lstat
+#if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
 static int
-do_lstat(const char *path, struct stat *pst, int flags, rb_encoding *enc)
+do_lstat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc)
 {
+#if USE_OPENDIR_AT
+    int ret = fstatat(fd, path, pst, AT_SYMLINK_NOFOLLOW);
+#else
     int ret = lstat(path, pst);
+#endif
     if (ret < 0 && !to_be_ignored(errno))
 	sys_warning(path, enc);
 
@@ -1291,9 +1314,17 @@ do_lstat(const char *path, struct stat * https://github.com/ruby/ruby/blob/trunk/dir.c#L1314
 #endif
 
 static DIR *
-do_opendir(const char *path, int flags, rb_encoding *enc,
+do_opendir(const int basefd, const char *path, int flags, rb_encoding *enc,
 	   ruby_glob_errfunc *errfunc, VALUE arg, int *status)
 {
+#if USE_OPENDIR_AT
+    const int opendir_flags = (O_RDONLY|O_CLOEXEC|
+#ifdef O_DIRECTORY
+			       O_DIRECTORY|
+#endif
+			       0);
+    int fd;
+#endif
     DIR *dirp;
 #ifdef _WIN32
     VALUE tmp = 0;
@@ -1303,14 +1334,28 @@ do_opendir(const char *path, int flags, https://github.com/ruby/ruby/blob/trunk/dir.c#L1334
 	path = RSTRING_PTR(tmp);
     }
 #endif
+#if USE_OPENDIR_AT
+    fd = openat(basefd, path, 0, opendir_flags);
+    dirp = (fd < 0) ? NULL : fdopendir(fd);
+#else
     dirp = opendir(path);
+#endif
     if (!dirp) {
 	int e = errno;
 	switch (rb_gc_for_fd(e)) {
 	  default:
+#if USE_OPENDIR_AT
+	    if ((fd >= 0) || (fd = openat(basefd, path, 0, opendir_flags)) >= 0) {
+		dirp = fdopendir(fd);
+	    }
+#else
 	    dirp = opendir(path);
+#endif
 	    if (dirp) break;
 	    e = errno;
+#if USE_OPENDIR_AT
+	    if (fd >= 0) close(fd);
+#endif
 	    /* fallback */
 	  case 0:
 	    *status = 0;
@@ -1713,6 +1758,7 @@ replace_real_basename(char *path, long b https://github.com/ruby/ruby/blob/trunk/dir.c#L1758
 struct glob_args {
     void (*func)(const char *, VALUE, void *);
     const char *path;
+    const char *base;
     VALUE value;
     rb_encoding *enc;
 };
@@ -1797,6 +1843,7 @@ dirent_match(const char *pat, rb_encodin https://github.com/ruby/ruby/blob/trunk/dir.c#L1843
 
 static int
 glob_helper(
+    int fd,
     const char *path,
     long pathlen,
     int dirsep, /* '/' should be placed before appending child entry's name to 'path'. */
@@ -1847,7 +1894,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1894
 
     if (*path) {
 	if (match_all && pathtype == path_unknown) {
-	    if (do_lstat(path, &st, flags, enc) == 0) {
+	    if (do_lstat(fd, path, &st, flags, enc) == 0) {
 		pathtype = IFTODT(st.st_mode);
 	    }
 	    else {
@@ -1855,7 +1902,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1902
 	    }
 	}
 	if (match_dir && pathtype == path_unknown) {
-	    if (do_stat(path, &st, flags, enc) == 0) {
+	    if (do_stat(fd, path, &st, flags, enc) == 0) {
 		pathtype = IFTODT(st.st_mode);
 	    }
 	    else {
@@ -1888,14 +1935,14 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1935
 	if (cur + 1 == end && (*cur)->type <= ALPHA) {
 	    plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str));
 	    if (!plainname) return -1;
-	    dirp = do_opendir(plainname, flags, enc, funcs->error, arg, &status);
+	    dirp = do_opendir(fd, plainname, flags, enc, funcs->error, arg, &status);
 	    GLOB_FREE(plainname);
 	}
 	else
 # else
 	    ;
 # endif
-	dirp = do_opendir(*path ? path : ".", flags, enc, funcs->error, arg, &status);
+	dirp = do_opendir(fd, *path ? path : ".", flags, enc, funcs->error, arg, &status);
 	if (dirp == NULL) {
 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
 	    if ((magical < 2) && !recursive && (errno == EACCES)) {
@@ -1963,7 +2010,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L2010
 		    /* fall back to call lstat(2) */
 #endif
 		/* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
-		if (do_lstat(buf, &st, flags, enc) == 0)
+		if (do_lstat(fd, buf, &st, flags, enc) == 0)
 		    new_pathtype = IFTODT(st.st_mode);
 		else
 		    new_pathtype = path_noent;
@@ -2001,7 +2048,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L2048
 		}
 	    }
 
-	    status = glob_helper(buf, name - buf + namlen, 1,
+	    status = glob_helper(fd, buf, name - buf + namlen, 1,
 				 new_pathtype, new_beg, new_end,
 				 flags, funcs, arg, enc);
 	    GLOB_FREE(buf);
@@ -2066,7 +2113,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L2113
 		    if (!buf) break;
 		}
 #endif
-		status = glob_helper(buf, pathlen + strlen(buf + pathlen), 1,
+		status = glob_helper(fd, buf, pathlen + strlen(buf + pathlen), 1,
 				     new_pathtype, new_beg, new_end,
 				     flags, funcs, arg, enc);
 		GLOB_FREE(buf);
@@ -2082,7 +2129,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L2129
 }
 
 static int
-ruby_glob0(const char *path, const char *base, int flags,
+ruby_glob0(const char *path, int fd, const char *base, int flags,
 	   const ruby_glob_funcs_t *funcs, VALUE arg,
 	   rb_encoding *enc)
 {
@@ -2116,7 +2163,7 @@ ruby_glob0(const char *path, const char https://github.com/ruby/ruby/blob/trunk/dir.c#L2163
 	GLOB_FREE(buf);
 	return -1;
     }
-    status = glob_helper(buf, n, dirsep, path_unknown, &list, &list + 1,
+    status = glob_helper(fd, buf, n, dirsep, path_unknown, &list, &list + 1,
 			 flags, funcs, arg, enc);
     glob_free_pattern(list);
     GLOB_FREE(buf);
@@ -2130,7 +2177,7 @@ ruby_glob(const char *path, int flags, r https://github.com/ruby/ruby/blob/trunk/dir.c#L2177
     ruby_glob_funcs_t funcs;
     funcs.match = func;
     funcs.error = NULL;
-    return ruby_glob0(path, 0, flags & ~GLOB_VERBOSE,
+    return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE,
 		      &funcs, arg, rb_ascii8bit_encoding());
 }
 
@@ -2159,7 +2206,7 @@ rb_glob(const char *path, void (*func)(c https://github.com/ruby/ruby/blob/trunk/dir.c#L2206
     args.value = arg;
     args.enc = rb_ascii8bit_encoding();
 
-    status = ruby_glob0(path, 0, GLOB_VERBOSE, &rb_glob_funcs,
+    status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs,
 			(VALUE)&args, args.enc);
     if (status) GLOB_JUMP_TAG(status);
 }
@@ -2248,7 +2295,7 @@ glob_brace(const char *path, VALUE val, https://github.com/ruby/ruby/blob/trunk/dir.c#L2295
 {
     struct brace_args *arg = (struct brace_args *)val;
 
-    return ruby_glob0(path, 0, arg->flags, &arg->funcs, arg->value, enc);
+    return ruby_glob0(path, AT_FDCWD, 0, arg->flags, &arg->funcs, arg->value, enc);
 }
 
 int
@@ -2272,8 +2319,8 @@ ruby_brace_glob(const char *str, int fla https://github.com/ruby/ruby/blob/trunk/dir.c#L2319
 
 struct push_glob_args {
     struct glob_args glob;
-    const char *base;
     int flags;
+    int fd;
 };
 
 static int
@@ -2281,7 +2328,7 @@ push_caller(const char *path, VALUE val, https://github.com/ruby/ruby/blob/trunk/dir.c#L2328
 {
     struct push_glob_args *arg = (struct push_glob_args *)val;
 
-    return ruby_glob0(path, arg->base, arg->flags, &rb_glob_funcs,
+    return ruby_glob0(path, arg->fd, arg->glob.base, arg->flags, &rb_glob_funcs,
 		      (VALUE)&arg->glob, enc);
 }
 
@@ -2302,10 +2349,20 @@ push_glob(VALUE ary, VALUE str, VALUE ba https://github.com/ruby/ruby/blob/trunk/dir.c#L2349
     args.glob.func = push_pattern;
     args.glob.value = ary;
     args.glob.enc = enc;
-    args.base = 0;
+    args.glob.base = 0;
     args.flags = flags;
-    if (!NIL_P(base) && rb_enc_check(str, base)) {
-	args.base = RSTRING_PTR(base);
+    args.fd = AT_FDCWD;
+    if (!NIL_P(base)) {
+	if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) {
+	    struct dir_data *dirp = DATA_PTR(base);
+	    if (!dirp->dir) dir_closed();
+#ifdef HAVE_DIRFD
+	    if ((args.fd = dirfd(dirp->dir)) == -1)
+		rb_sys_fail_path(dir_inspect(base));
+#endif
+	    base = dirp->path;
+	}
+	args.glob.base = RSTRING_PTR(base);
     }
 #if defined _WIN32 || defined __APPLE__
     enc = rb_utf8_encoding();
@@ -2371,6 +2428,11 @@ dir_glob_options(VALUE opt, VALUE *base, https://github.com/ruby/ruby/blob/trunk/dir.c#L2428
     if (args[0] == Qundef) {
 	*base = Qnil;
     }
+#if USE_OPENDIR_AT
+    else if (rb_typeddata_is_kind_of(args[0], &dir_data_type)) {
+	*base = args[0];
+    }
+#endif
     else {
 	GlobPathValue(args[0], TRUE);
 	*base = args[0];
Index: test/ruby/test_dir.rb
===================================================================
--- test/ruby/test_dir.rb	(revision 58859)
+++ test/ruby/test_dir.rb	(revision 58860)
@@ -208,6 +208,12 @@ class TestDir < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_dir.rb#L208
     assert_equal(files, Dir.glob("*/*.c", base: @root).sort)
   end
 
+  def test_glob_base_dir
+    files = %w[a/foo.c c/bar.c].map {|n| File.join(@root, n)}
+    files.each {|n| File.write(n, "")}
+    assert_equal(files, Dir.open(@root) {|d| Dir.glob("*/*.c", base: d)}.sort)
+  end
+
   def assert_entries(entries)
     entries.sort!
     assert_equal(%w(. ..) + ("a".."z").to_a, entries)

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

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