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

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/

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