ruby-changes:41767
From: nobu <ko1@a...>
Date: Tue, 16 Feb 2016 17:34:24 +0900 (JST)
Subject: [ruby-changes:41767] nobu:r53841 (trunk): dir.c: Dir.empty?
nobu 2016-02-16 17:34:47 +0900 (Tue, 16 Feb 2016) New Revision: 53841 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=53841 Log: dir.c: Dir.empty? * dir.c (rb_dir_s_empty_p): add Dir.empty? method, which tells the argument is the name of an empty directory. [Feature #10121] Modified files: trunk/ChangeLog trunk/NEWS trunk/dir.c trunk/test/ruby/test_dir.rb Index: NEWS =================================================================== --- NEWS (revision 53840) +++ NEWS (revision 53841) @@ -16,6 +16,10 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L16 === Core classes updates (outstanding ones only) +* Dir + + * Dir#empty?. [Feature #10121] + === Stdlib updates (outstanding ones only) * CSV Index: ChangeLog =================================================================== --- ChangeLog (revision 53840) +++ ChangeLog (revision 53841) @@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Feb 16 17:34:18 2016 Nobuyoshi Nakada <nobu@r...> + + * dir.c (rb_dir_s_empty_p): add Dir.empty? method, which tells the + argument is the name of an empty directory. [Feature #10121] + Tue Feb 16 09:51:20 2016 Nobuyoshi Nakada <nobu@r...> * tool/rbinstall.rb (without_destdir): just strip a drive letter Index: test/ruby/test_dir.rb =================================================================== --- test/ruby/test_dir.rb (revision 53840) +++ test/ruby/test_dir.rb (revision 53841) @@ -331,4 +331,13 @@ class TestDir < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_dir.rb#L331 end } end + + def test_empty? + assert_not_send([Dir, :empty?, @root]) + assert_send([Dir, :empty?, File.join(@root, "a")]) + open(File.join(@root, "a", "..."), "w") {} + assert_not_send([Dir, :empty?, File.join(@root, "a")]) + assert_raise(Errno::ENOENT) {Dir.empty?(@nodir)} + assert_not_send([Dir, :empty?, File.join(@root, "b")]) + end end Index: dir.c =================================================================== --- dir.c (revision 53840) +++ dir.c (revision 53841) @@ -21,6 +21,7 @@ https://github.com/ruby/ruby/blob/trunk/dir.c#L21 #include <unistd.h> #endif +#undef HAVE_DIRENT_NAMLEN #if defined HAVE_DIRENT_H && !defined _WIN32 # include <dirent.h> # define NAMLEN(dirent) strlen((dirent)->d_name) @@ -30,6 +31,7 @@ https://github.com/ruby/ruby/blob/trunk/dir.c#L31 #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen +# define HAVE_DIRENT_NAMLEN 1 # if HAVE_SYS_NDIR_H # include <sys/ndir.h> # endif @@ -699,6 +701,27 @@ fundamental_encoding_p(rb_encoding *enc) https://github.com/ruby/ruby/blob/trunk/dir.c#L701 #else # define READDIR(dir, enc) readdir((dir)) #endif +static int +to_be_skipped(const struct dirent *dp) +{ + const char *name = dp->d_name; + if (name[0] != '.') return FALSE; +#ifdef HAVE_DIRENT_NAMLEN + switch (NAMLEN(dp)) { + case 2: + if (name[1] != '.') return FALSE; + case 1: + return TRUE; + default: + break; + } +#else + if (!name[1]) return TRUE; + if (name[1] != '.') return FALSE; + if (!name[2]) return TRUE; +#endif + return FALSE; +} /* * call-seq: @@ -2611,6 +2634,73 @@ rb_dir_exists_p(VALUE obj, VALUE fname) https://github.com/ruby/ruby/blob/trunk/dir.c#L2634 } /* + * call-seq: + * Dir.empty?(path_name) -> true or false + * + * Returns <code>true</code> if the named file is an empty directory, + * <code>false</code> if it is not a directory or non-empty. + */ +static VALUE +rb_dir_s_empty_p(VALUE obj, VALUE dirname) +{ + DIR *dir; + struct dirent *dp; + VALUE result = Qtrue, orig; + const char *path; + enum {false_on_notdir = 1}; + + GlobPathValue(dirname, FALSE); + orig = rb_str_dup_frozen(dirname); + dirname = rb_str_encode_ospath(dirname); + dirname = rb_str_dup_frozen(dirname); + path = RSTRING_PTR(dirname); + +#if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT + { + u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)]; + struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,}; + if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) != 0) + rb_sys_fail_path(orig); + if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) { + al.commonattr = 0; + al.dirattr = ATTR_DIR_ENTRYCOUNT; + if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) == 0) { + if (attrbuf[0] >= 2 * sizeof(u_int32_t)) + return attrbuf[1] ? Qfalse : Qtrue; + if (false_on_notdir) return Qfalse; + } + rb_sys_fail_path(orig); + } + } +#endif + + dir = opendir(path); + if (!dir) { + int e = errno; + switch (e) { + case EMFILE: case ENFILE: + rb_gc(); + dir = opendir(path); + if (dir) break; + e = errno; + /* fall through */ + default: + if (false_on_notdir && e == ENOTDIR) return Qfalse; + rb_syserr_fail_path(e, orig); + } + } + errno = 0; + while ((dp = READDIR(dir, NULL)) != NULL) { + if (!to_be_skipped(dp)) { + result = Qfalse; + break; + } + } + closedir(dir); + return result; +} + +/* * Objects of class <code>Dir</code> are directory streams representing * directories in the underlying file system. They provide a variety of * ways to list directories and their contents. See also @@ -2661,6 +2751,7 @@ Init_Dir(void) https://github.com/ruby/ruby/blob/trunk/dir.c#L2751 rb_define_singleton_method(rb_cDir,"[]", dir_s_aref, -1); rb_define_singleton_method(rb_cDir,"exist?", rb_file_directory_p, 1); rb_define_singleton_method(rb_cDir,"exists?", rb_dir_exists_p, 1); + rb_define_singleton_method(rb_cDir,"empty?", rb_dir_s_empty_p, 1); rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1); rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/