# HG changeset patch # User Jim Meyering # Date 1227894008 -3600 # Node ID 36479bd141eeffef3432431601f3895f9503ac52 # Parent 4a02c5d90eaf2c4ef882ebaf4410e58f9e49c3fd 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. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2008-11-29 Jim Meyering + + 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 * lib/sys_time.in.h: Add extern "C" block for C++. Suggested by @@ -16,7 +29,7 @@ Reported by Albert Chin . 2008-11-18 Alexandre Duret-Lutz - Bruno Haible + Bruno Haible * 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 diff --git a/lib/fts.c b/lib/fts.c --- a/lib/fts.c +++ b/lib/fts.c @@ -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)); diff --git a/lib/fts_.h b/lib/fts_.h --- a/lib/fts_.h +++ b/lib/fts_.h @@ -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 */