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/