changeset 30534:666730b7ab74

fts: provide dirent.d_type via FTSENT.fts_statp, when possible * lib/fts.c (D_TYPE): Define. (DT_UNKNOWN, DT_BLK, DT_CHR) [HAVE_STRUCT_DIRENT_D_TYPE]: Define. (DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK): Likewise. (s_ifmt_shift_bits): New function. (set_stat_type): New function. (fts_build): When not calling fts_stat, call set_stat_type to propagate dirent.d_type info to fts_read caller. * lib/fts_.h (FTSENT) [FTS_DEFER_STAT]: Mention that fts_statp->st_mode type information may be valid.
author Jim Meyering <meyering@redhat.com>
date Fri, 28 Nov 2008 18:40:08 +0100
parents 6494e4e54db7
children d6ffd3abd63a
files ChangeLog lib/fts.c lib/fts_.h
diffstat 3 files changed, 98 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Nov 28 15:33:48 2008 +0100
+++ b/ChangeLog	Fri Nov 28 18:40:08 2008 +0100
@@ -1,3 +1,16 @@
+2008-11-29  Jim Meyering  <meyering@redhat.com>
+
+	fts: provide dirent.d_type via FTSENT.fts_statp, when possible
+	* lib/fts.c (D_TYPE): Define.
+	(DT_UNKNOWN, DT_BLK, DT_CHR) [HAVE_STRUCT_DIRENT_D_TYPE]: Define.
+	(DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK): Likewise.
+	(s_ifmt_shift_bits): New function.
+	(set_stat_type): New function.
+	(fts_build): When not calling fts_stat, call set_stat_type
+	to propagate dirent.d_type info to fts_read caller.
+	* lib/fts_.h (FTSENT) [FTS_DEFER_STAT]: Mention that
+	fts_statp->st_mode type information may be valid.
+
 2008-11-28  Simon Josefsson  <simon@josefsson.org>
 
 	* lib/sys_time.in.h: Add extern "C" block for C++.  Suggested by
@@ -16,7 +29,7 @@
 	Reported by Albert Chin <bug-gnulib@mlists.thewrittenword.com>.
 
 2008-11-18  Alexandre Duret-Lutz  <adl@lrde.epita.fr>
-            Bruno Haible  <bruno@clisp.org>
+	    Bruno Haible  <bruno@clisp.org>
 
 	* lib/stdint.in.h: Define all type macros so that their expansion is
 	a single typedef'ed token. Fixes a compilation failure in Boost which
--- a/lib/fts.c	Fri Nov 28 15:33:48 2008 +0100
+++ b/lib/fts.c	Fri Nov 28 18:40:08 2008 +0100
@@ -84,9 +84,31 @@
 # define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)
 /* True if the type of the directory entry D must be T.  */
 # define DT_MUST_BE(d, t) ((d)->d_type == (t))
+# define D_TYPE(d) ((d)->d_type)
 #else
 # define DT_IS_KNOWN(d) false
 # define DT_MUST_BE(d, t) false
+# define D_TYPE(d) DT_UNKNOWN
+
+# undef DT_UNKNOWN
+# define DT_UNKNOWN 0
+
+/* Any nonzero values will do here, so long as they're distinct.
+   Undef any existing macros out of the way.  */
+# undef DT_BLK
+# undef DT_CHR
+# undef DT_DIR
+# undef DT_FIFO
+# undef DT_LNK
+# undef DT_REG
+# undef DT_SOCK
+# define DT_BLK 1
+# define DT_CHR 2
+# define DT_DIR 3
+# define DT_FIFO 4
+# define DT_LNK 5
+# define DT_REG 6
+# define DT_SOCK 7
 #endif
 
 enum
@@ -989,6 +1011,61 @@
 	  : b[0]->fts_statp->st_ino < a[0]->fts_statp->st_ino ? 1 : 0);
 }
 
+/* Return the number of bits by which a d_type value must be shifted
+   left in order to put it into the S_IFMT bits of stat.st_mode.  */
+static int
+s_ifmt_shift_bits (void)
+{
+  unsigned int v = S_IFMT; /* usually, 0170000 */
+  static const int MultiplyDeBruijnBitPosition[32] =
+    {
+      0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+      31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+    };
+
+  /* Find the smallest power of two, P (e.g., 0010000) such that P & V == P. */
+  unsigned int p = v ^ (v & (v - 1));
+
+  /* Compute and return r = log2 (p), using code from
+     http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn */
+  return MultiplyDeBruijnBitPosition[(uint32_t) (p * 0x077CB531UL) >> 27];
+}
+
+/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode
+   S_IF* bit and set ST.st_mode, thus clearing all other bits in that field.  */
+static void
+set_stat_type (struct stat *st, unsigned int dtype)
+{
+  mode_t type;
+  switch (dtype)
+    {
+    case DT_BLK:
+      type = S_IFBLK;
+      break;
+    case DT_CHR:
+      type = S_IFCHR;
+      break;
+    case DT_DIR:
+      type = S_IFDIR;
+      break;
+    case DT_FIFO:
+      type = S_IFIFO;
+      break;
+    case DT_LNK:
+      type = S_IFLNK;
+      break;
+    case DT_REG:
+      type = S_IFREG;
+      break;
+    case DT_SOCK:
+      type = S_IFSOCK;
+      break;
+    default:
+      type = 0;
+    }
+  st->st_mode = dtype << s_ifmt_shift_bits ();
+}
+
 /*
  * This is the tricky part -- do not casually change *anything* in here.  The
  * idea is to build the linked list of entries that are used by fts_children
@@ -1218,6 +1295,9 @@
 					  && DT_IS_KNOWN(dp)
 					  && ! DT_MUST_BE(dp, DT_DIR));
 			p->fts_info = FTS_NSOK;
+			/* Propagate dirent.d_type information back
+			   to caller, when possible.  */
+			set_stat_type (p->fts_statp, D_TYPE (dp));
 			fts_set_stat_required(p, !skip_stat);
 			is_dir = (ISSET(FTS_PHYSICAL) && ISSET(FTS_NOSTAT)
 				  && DT_MUST_BE(dp, DT_DIR));
--- a/lib/fts_.h	Fri Nov 28 15:33:48 2008 +0100
+++ b/lib/fts_.h	Fri Nov 28 18:40:08 2008 +0100
@@ -131,7 +131,10 @@
      Use this flag to make fts_open and fts_read defer the stat/lstat/fststat
      of each entry until it is actually processed.  However, note that if you
      use this option and also specify a comparison function, that function may
-     not examine any data via fts_statp.  */
+     not examine any data via fts_statp.  However, when fts_statp->st_mode is
+     nonzero, the S_IFMT type bits are valid, with mapped dirent.d_type data.
+     Of course, that happens only on file systems that provide useful
+     dirent.d_type data.  */
 # define FTS_DEFER_STAT		0x0400
 
 # define FTS_OPTIONMASK	0x07ff		/* valid user option mask */