Advertisement
eXFq7GJ1cC

Untitled

Jul 26th, 2012
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 35.11 KB | None | 0 0
  1. commit c6e3ea61d9f08aa0128a0eb13d31a2fbad376f99
  2. Author: Paul Eggert <eggert@cs.ucla.edu>
  3. Date:   Sat Mar 10 11:29:32 2012 -0800
  4.  
  5.     grep: -r no longer follows symlinks; use fts
  6.    
  7.     Change -r to follow only command-line symlinks, and by default to
  8.     read only devices named on the command line.  This is a simple
  9.     way to get a more-useful behavior when searching random
  10.     directories; the idea is to use 'find' if you want something fancy.
  11.     -R acts as before and gets a new alias --dereference-recursive.
  12.     The code now uses fts internally, so it is more robust and
  13.     faster with large hierarchies.
  14.     * .gitignore: Remove lib/savedir.c, lib/savedir.h.
  15.     * tests/symlink: New file
  16.     * Makefile.boot (LIB_OBJS_core): Remove isdir.o, savedir.o.
  17.     Perhaps other changes are needed too, but I'm not sure what
  18.     this makefile is for.
  19.     * NEWS: Document changes.
  20.     * doc/grep.texi (File and Directory Selection): Likewise.
  21.     * bootstrap.conf (gnulib_modules): Remove dirent, dirname, isdir, open.
  22.     Add fstatat, fts, openat-safer.
  23.     * lib/Makefile.am (libgreputils_a_SOURCES): Remove savedir.c, savedir.h.
  24.     * lib/savedir.c, lib/savedir.h: Remove.
  25.     * po/POTFILES.in: Add lib/openat-die.c.
  26.     * src/main.c: Include fcntl-safer.h, fts_.h.  Don't include
  27.     isdir.h, savedir.h.
  28.     (struct stats, stats_base): Remove.
  29.     (long_options, usage, main): Add --dereference-recursive and
  30.     implement -r vs -R.
  31.     (filename_prefix_len, fts_options): New static vars.
  32.     (basic_fts_options, READ_COMMAND_LINE_DEVICES): New constants.
  33.     (devices): Now defaults to READ_COMMAND_LINE_DEVICES.
  34.     (reset, grep): Now takes just struct stat rather than file name and
  35.     struct stats.  All callers changed.
  36.     (fillbuf): Now takes struct stat reather than struct stats.
  37.     All callers changed.
  38.     (grep): Don't worry about recursing too deeply; fts and grepdesc
  39.     handle this now.
  40.     (is_device_mode, grepdirent, grepdesc, grep_command_line_args):
  41.     New functions.
  42.     (grepfile): New args DIRDESC, FOLLOW, COMMAND_LINE.  Remove struct stats
  43.     arg.  All callers changed.  Use openat_safer rather than open.
  44.     Use desc == STDIN_FILENO to tell whether we're reading "-".
  45.     Don't worry about EINTR when closing -- not possible, since we're
  46.     not catching signals.
  47.     * tests/Makefile.am (TESTS): Add symlink.
  48.     * tests/symlink: New file.
  49.  
  50. diff --git a/.gitignore b/.gitignore
  51. index 35f5d10..0b195d9 100644
  52. --- a/.gitignore
  53. +++ b/.gitignore
  54. @@ -64,5 +64,3 @@ TAGS
  55.  !/lib/colorize-posix.c
  56.  !/lib/colorize-w32.c
  57.  !/lib/colorize.h
  58. -!/lib/savedir.c
  59. -!/lib/savedir.h
  60. diff --git a/Makefile.boot b/Makefile.boot
  61. index 043429b..4414110 100644
  62. --- a/Makefile.boot
  63. +++ b/Makefile.boot
  64. @@ -41,10 +41,8 @@ LIB_OBJS_core =  \
  65.        $(libdir)/error.$(OBJEXT) \
  66.        $(libdir)/exclude.$(OBJEXT) \
  67.        $(libdir)/hard-locale.$(OBJEXT) \
  68. -      $(libdir)/isdir.$(OBJEXT) \
  69.        $(libdir)/quotearg.$(OBJEXT) \
  70.        $(libdir)/regex.$(OBJEXT) \
  71. -      $(libdir)/savedir.$(OBJEXT) \
  72.        $(libdir)/strtoumax.$(OBJEXT) \
  73.        $(libdir)/xmalloc.$(OBJEXT) \
  74.        $(libdir)/xstrtol.$(OBJEXT) \
  75. diff --git a/NEWS b/NEWS
  76. index d4d70f5..6dad608 100644
  77. --- a/NEWS
  78. +++ b/NEWS
  79. @@ -4,9 +4,25 @@ GNU grep NEWS                                    -*- outline -*-
  80.  
  81.  ** Bug fixes
  82.  
  83. -   grep no longer segfaults with -r --exclude-dir and no file operand.
  84. -   I.e., ":|grep -r --exclude-dir=D PAT" would segfault.
  85. +  grep no longer segfaults with -r --exclude-dir and no file operand.
  86. +  I.e., ":|grep -r --exclude-dir=D PAT" would segfault.
  87.  
  88. +  Recursive grep now uses fts for directory traversal, so it can
  89. +  handle much-larger directories without reporting things like "File
  90. +  name too long", and it can run much faster when dealing with large
  91. +  directory hierarchies.
  92. +
  93. +** New features
  94. +
  95. +  The -R option now has a long-option alias --dereference-recursive.
  96. +
  97. +** Changes in behavior
  98. +
  99. +  The -r (--recursive) option now follows only command-line symlinks.
  100. +  Also, by default -r now reads a device only if it is named on the command
  101. +  line; this can be overridden with --devices.  -R acts as before, so
  102. +  use -R if you prefer the old behavior of following all symlinks and
  103. +  defaulting to reading all devices.
  104.  
  105.  * Noteworthy changes in release 2.11 (2012-03-02) [stable]
  106.  
  107. diff --git a/bootstrap.conf b/bootstrap.conf
  108. index 45bb33d..7a71015 100644
  109. --- a/bootstrap.conf
  110. +++ b/bootstrap.conf
  111. @@ -24,13 +24,13 @@ binary-io
  112.  btowc
  113.  c-ctype
  114.  closeout
  115. -dirent
  116. -dirname
  117.  do-release-commit-and-tag
  118.  error
  119.  exclude
  120.  fcntl-h
  121.  fnmatch
  122. +fstatat
  123. +fts
  124.  getopt-gnu
  125.  getpagesize
  126.  gettext-h
  127. @@ -44,7 +44,6 @@ intprops
  128.  inttypes
  129.  isatty
  130.  isblank
  131. -isdir
  132.  iswctype
  133.  largefile
  134.  lseek
  135. @@ -58,7 +57,7 @@ memchr
  136.  mempcpy
  137.  minmax
  138.  obstack
  139. -open
  140. +openat-safer
  141.  progname
  142.  propername
  143.  quote
  144. diff --git a/doc/grep.texi b/doc/grep.texi
  145. index c014d8f..1840e21 100644
  146. --- a/doc/grep.texi
  147. +++ b/doc/grep.texi
  148. @@ -606,16 +606,21 @@ if the terminal driver interprets some of it as commands.
  149.  @opindex --devices
  150.  @cindex device search
  151.  If an input file is a device, FIFO, or socket, use @var{action} to process it.
  152. -By default, @var{action} is @samp{read},
  153. -which means that devices are read just as if they were ordinary files.
  154. +If @var{action} is @samp{read},
  155. +all devices are read just as if they were ordinary files.
  156.  If @var{action} is @samp{skip},
  157.  devices, FIFOs, and sockets are silently skipped.
  158. +By default, devices are read if they are on the command line or if the
  159. +@option{-R} (@option{--dereference-recursive}) option is used, and are
  160. +skipped if they are encountered recursively and the @option{-r}
  161. +(@option{--recursive}) option is used.
  162.  
  163.  @item -d @var{action}
  164.  @itemx --directories=@var{action}
  165.  @opindex -d
  166.  @opindex --directories
  167.  @cindex directory search
  168. +@cindex symbolic links
  169.  If an input file is a directory, use @var{action} to process it.
  170.  By default, @var{action} is @samp{read},
  171.  which means that directories are read just as if they were ordinary files
  172. @@ -624,7 +629,8 @@ and will cause @command{grep}
  173.  to print error messages for every directory or silently skip them).
  174.  If @var{action} is @samp{skip}, directories are silently skipped.
  175.  If @var{action} is @samp{recurse},
  176. -@command{grep} reads all files under each directory, recursively;
  177. +@command{grep} reads all files under each directory, recursively,
  178. +following command-line symbolic links and skipping other symlinks;
  179.  this is equivalent to the @option{-r} option.
  180.  
  181.  @item --exclude=@var{glob}
  182. @@ -663,16 +669,28 @@ Search only files whose base name matches @var{glob}
  183.  (using wildcard matching as described under @option{--exclude}).
  184.  
  185.  @item -r
  186. -@itemx -R
  187.  @itemx --recursive
  188.  @opindex -r
  189.  @opindex --recursive
  190.  @cindex recursive search
  191.  @cindex searching directory trees
  192. +@cindex symbolic links
  193.  For each directory operand,
  194.  read and process all files in that directory, recursively.
  195. +Follow symbolic links on the command line, but skip symlinks
  196. +that are encountered recursively.
  197.  This is the same as the @samp{--directories=recurse} option.
  198.  
  199. +@item -R
  200. +@itemx --dereference-recursive
  201. +@opindex -R
  202. +@opindex --dereference-recursive
  203. +@cindex recursive search
  204. +@cindex searching directory trees
  205. +@cindex symbolic links
  206. +For each directory operand, read and process all files in that
  207. +directory, recursively, following all symbolic links.
  208. +
  209.  @end table
  210.  
  211.  @node Other Options
  212. diff --git a/lib/Makefile.am b/lib/Makefile.am
  213. index 04ae51e..527c6f5 100644
  214. --- a/lib/Makefile.am
  215. +++ b/lib/Makefile.am
  216. @@ -32,7 +32,7 @@ INCLUDES = -I.. -I$(srcdir)
  217.  AM_CFLAGS += $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS)
  218.  
  219.  libgreputils_a_SOURCES += \
  220. -  colorize.c colorize.h savedir.c savedir.h
  221. +  colorize.c colorize.h
  222.  
  223.  EXTRA_DIST += colorize-posix.c colorize-w32.c
  224.  
  225. diff --git a/lib/savedir.c b/lib/savedir.c
  226. deleted file mode 100644
  227. index 3f93c16..0000000
  228. --- a/lib/savedir.c
  229. +++ /dev/null
  230. @@ -1,163 +0,0 @@
  231. -/* savedir.c -- save the list of files in a directory in a string
  232. -   Copyright (C) 1990, 1997-2001, 2009-2012 Free Software Foundation, Inc.
  233. -
  234. -   This program is free software; you can redistribute it and/or modify
  235. -   it under the terms of the GNU General Public License as published by
  236. -   the Free Software Foundation; either version 3, or (at your option)
  237. -   any later version.
  238. -
  239. -   This program is distributed in the hope that it will be useful,
  240. -   but WITHOUT ANY WARRANTY; without even the implied warranty of
  241. -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  242. -   GNU General Public License for more details.
  243. -
  244. -   You should have received a copy of the GNU General Public License
  245. -   along with this program; if not, write to the Free Software Foundation,
  246. -   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
  247. -
  248. -/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
  249. -
  250. -#include <config.h>
  251. -
  252. -#include <sys/types.h>
  253. -#include <unistd.h>
  254. -#include <dirent.h>
  255. -
  256. -#ifdef CLOSEDIR_VOID
  257. -/* Fake a return value. */
  258. -# define CLOSEDIR(d) (closedir (d), 0)
  259. -#else
  260. -# define CLOSEDIR(d) closedir (d)
  261. -#endif
  262. -
  263. -#include <stdlib.h>
  264. -#include <string.h>
  265. -#include <fnmatch.h>
  266. -#include "savedir.h"
  267. -#include "xalloc.h"
  268. -
  269. -static char *path;
  270. -static size_t pathlen;
  271. -
  272. -extern int isdir (const char *name);
  273. -
  274. -static int
  275. -isdir1 (const char *dir, const char *file)
  276. -{
  277. -  size_t dirlen = strlen (dir);
  278. -  size_t filelen = strlen (file);
  279. -
  280. -  while (dirlen && dir[dirlen - 1] == '/')
  281. -    dirlen--;
  282. -
  283. -  if ((dirlen + filelen + 2) > pathlen)
  284. -    {
  285. -      pathlen *= 2;
  286. -      if ((dirlen + filelen + 2) > pathlen)
  287. -        pathlen = dirlen + filelen + 2;
  288. -
  289. -      path = xrealloc (path, pathlen);
  290. -    }
  291. -
  292. -  memcpy (path, dir, dirlen);
  293. -  path[dirlen] = '/';
  294. -  strcpy (path + dirlen + 1, file);
  295. -  return isdir (path);
  296. -}
  297. -
  298. -/* Return a freshly allocated string containing the filenames
  299. -   in directory DIR, separated by '\0' characters;
  300. -   the end is marked by two '\0' characters in a row.
  301. -   NAME_SIZE is the number of bytes to initially allocate
  302. -   for the string; it will be enlarged as needed.
  303. -   Return NULL if DIR cannot be opened or if out of memory. */
  304. -char *
  305. -savedir (const char *dir, off_t name_size, struct exclude *included_patterns,
  306. -         struct exclude *excluded_patterns, struct exclude *excluded_directory_patterns )
  307. -{
  308. -  DIR *dirp;
  309. -  struct dirent *dp;
  310. -  char *name_space;
  311. -  char *namep;
  312. -
  313. -  dirp = opendir (dir);
  314. -  if (dirp == NULL)
  315. -    return NULL;
  316. -
  317. -  /* Be sure name_size is at least `1' so there's room for
  318. -     the final NUL byte.  */
  319. -  if (name_size <= 0)
  320. -    name_size = 1;
  321. -
  322. -  name_space = (char *) malloc (name_size);
  323. -  if (name_space == NULL)
  324. -    {
  325. -      closedir (dirp);
  326. -      return NULL;
  327. -    }
  328. -  namep = name_space;
  329. -
  330. -  while ((dp = readdir (dirp)) != NULL)
  331. -    {
  332. -      /* Skip "." and ".." (some NFS file systems' directories lack them). */
  333. -      if (dp->d_name[0] != '.'
  334. -          || (dp->d_name[1] != '\0'
  335. -              && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
  336. -        {
  337. -          size_t namlen = strlen (dp->d_name);
  338. -          size_t size_needed = (namep - name_space) + namlen + 2;
  339. -
  340. -          if ((included_patterns || excluded_patterns)
  341. -              && !isdir1 (dir, dp->d_name))
  342. -            {
  343. -              if (included_patterns
  344. -                  && excluded_file_name (included_patterns, dp->d_name))
  345. -                continue;
  346. -              if (excluded_patterns
  347. -                  && excluded_file_name (excluded_patterns, dp->d_name))
  348. -                continue;
  349. -            }
  350. -
  351. -          if ( excluded_directory_patterns
  352. -              && isdir1 (dir, dp->d_name) )
  353. -            {
  354. -              if (excluded_directory_patterns
  355. -                  && excluded_file_name (excluded_directory_patterns, dp->d_name))
  356. -                continue;
  357. -            }
  358. -
  359. -          if (size_needed > name_size)
  360. -            {
  361. -              char *new_name_space;
  362. -
  363. -              while (size_needed > name_size)
  364. -                name_size += 1024;
  365. -
  366. -              new_name_space = realloc (name_space, name_size);
  367. -              if (new_name_space == NULL)
  368. -                {
  369. -                  closedir (dirp);
  370. -                  goto fail;
  371. -                }
  372. -              namep = new_name_space + (namep - name_space);
  373. -              name_space = new_name_space;
  374. -            }
  375. -          strcpy (namep, dp->d_name);
  376. -          namep += namlen + 1;
  377. -        }
  378. -    }
  379. -  *namep = '\0';
  380. -  if (CLOSEDIR (dirp))
  381. -    {
  382. -     fail:
  383. -      free (name_space);
  384. -      name_space = NULL;
  385. -    }
  386. -  if (path)
  387. -    {
  388. -      free (path);
  389. -      path = NULL;
  390. -      pathlen = 0;
  391. -    }
  392. -  return name_space;
  393. -}
  394. diff --git a/lib/savedir.h b/lib/savedir.h
  395. deleted file mode 100644
  396. index 00cb1a9..0000000
  397. --- a/lib/savedir.h
  398. +++ /dev/null
  399. @@ -1,11 +0,0 @@
  400. -#if !defined SAVEDIR_H_
  401. -# define SAVEDIR_H_
  402. -
  403. -#include "exclude.h"
  404. -
  405. -extern char *
  406. -savedir (const char *dir, off_t name_size,
  407. -         struct exclude *, struct exclude *,
  408. -         struct exclude *);
  409. -
  410. -#endif
  411. diff --git a/po/POTFILES.in b/po/POTFILES.in
  412. index b33c126..65f4f57 100644
  413. --- a/po/POTFILES.in
  414. +++ b/po/POTFILES.in
  415. @@ -20,6 +20,7 @@ lib/closeout.c
  416.  lib/error.c
  417.  lib/getopt.c
  418.  lib/obstack.c
  419. +lib/openat-die.c
  420.  lib/quotearg.c
  421.  lib/regcomp.c
  422.  lib/version-etc.c
  423. diff --git a/src/main.c b/src/main.c
  424. index f4f1235..be1e2e6 100644
  425. --- a/src/main.c
  426. +++ b/src/main.c
  427. @@ -36,14 +36,14 @@
  428.  #include "error.h"
  429.  #include "exclude.h"
  430.  #include "exitfail.h"
  431. +#include "fcntl-safer.h"
  432. +#include "fts_.h"
  433.  #include "getopt.h"
  434.  #include "grep.h"
  435.  #include "intprops.h"
  436. -#include "isdir.h"
  437.  #include "progname.h"
  438.  #include "propername.h"
  439.  #include "quote.h"
  440. -#include "savedir.h"
  441.  #include "version-etc.h"
  442.  #include "xalloc.h"
  443.  #include "xstrtol.h"
  444. @@ -63,15 +63,6 @@
  445.     avoiding a potential (racy) infinite loop.  */
  446.  static struct stat out_stat;
  447.  
  448. -struct stats
  449. -{
  450. -  struct stats const *parent;
  451. -  struct stat stat;
  452. -};
  453. -
  454. -/* base of chain of stat buffers, used to detect directory loops */
  455. -static struct stats stats_base;
  456. -
  457.  /* if non-zero, display usage information and exit */
  458.  static int show_help;
  459.  
  460. @@ -347,7 +338,7 @@ static struct option const long_options[] =
  461.    {"only-matching", no_argument, NULL, 'o'},
  462.    {"quiet", no_argument, NULL, 'q'},
  463.    {"recursive", no_argument, NULL, 'r'},
  464. -  {"recursive", no_argument, NULL, 'R'},
  465. +  {"dereference-recursive", no_argument, NULL, 'R'},
  466.    {"regexp", required_argument, NULL, 'e'},
  467.    {"invert-match", no_argument, NULL, 'v'},
  468.    {"silent", no_argument, NULL, 'q'},
  469. @@ -369,6 +360,7 @@ unsigned char eolbyte;
  470.  /* For error messages. */
  471.  /* The input file name, or (if standard input) "-" or a --label argument.  */
  472.  static char const *filename;
  473. +static size_t filename_prefix_len;
  474.  static int errseen;
  475.  static int write_error_seen;
  476.  
  477. @@ -392,18 +384,29 @@ ARGMATCH_VERIFY (directories_args, directories_types);
  478.  
  479.  static enum directories_type directories = READ_DIRECTORIES;
  480.  
  481. +enum { basic_fts_options = FTS_CWDFD | FTS_NOSTAT | FTS_TIGHT_CYCLE_CHECK };
  482. +static int fts_options = basic_fts_options | FTS_COMFOLLOW | FTS_PHYSICAL;
  483. +
  484.  /* How to handle devices. */
  485.  static enum
  486.    {
  487. +    READ_COMMAND_LINE_DEVICES,
  488.      READ_DEVICES,
  489.      SKIP_DEVICES
  490. -  } devices = READ_DEVICES;
  491. +  } devices = READ_COMMAND_LINE_DEVICES;
  492.  
  493. -static int grepdir (char const *, struct stats const *);
  494. +static int grepfile (int, char const *, int, int);
  495. +static int grepdesc (int, int);
  496.  #if defined HAVE_DOS_FILE_CONTENTS
  497.  static inline int undossify_input (char *, size_t);
  498.  #endif
  499.  
  500. +static int
  501. +is_device_mode (mode_t m)
  502. +{
  503. +  return S_ISCHR (m) || S_ISBLK (m) || S_ISSOCK (m) || S_ISFIFO (m);
  504. +}
  505. +
  506.  /* Functions we'll use to search. */
  507.  static compile_fp_t compile;
  508.  static execute_fp_t execute;
  509. @@ -473,7 +476,7 @@ static off_t after_last_match;  /* Pointer after last matching line that
  510.  /* Reset the buffer for a new file, returning zero if we should skip it.
  511.     Initialize on the first time through. */
  512.  static int
  513. -reset (int fd, char const *file, struct stats *stats)
  514. +reset (int fd, struct stat const *st)
  515.  {
  516.    if (! pagesize)
  517.      {
  518. @@ -488,9 +491,9 @@ reset (int fd, char const *file, struct stats *stats)
  519.    bufbeg[-1] = eolbyte;
  520.    bufdesc = fd;
  521.  
  522. -  if (S_ISREG (stats->stat.st_mode))
  523. +  if (S_ISREG (st->st_mode))
  524.      {
  525. -      if (file)
  526. +      if (fd != STDIN_FILENO)
  527.          bufoffset = 0;
  528.        else
  529.          {
  530. @@ -510,7 +513,7 @@ reset (int fd, char const *file, struct stats *stats)
  531.     to the beginning of the buffer contents, and 'buflim'
  532.     points just after the end.  Return zero if there's an error.  */
  533.  static int
  534. -fillbuf (size_t save, struct stats const *stats)
  535. +fillbuf (size_t save, struct stat const *st)
  536.  {
  537.    size_t fillsize = 0;
  538.    int cc = 1;
  539. @@ -543,9 +546,9 @@ fillbuf (size_t save, struct stats const *stats)
  540.           is large.  However, do not use the original file size as a
  541.           heuristic if we've already read past the file end, as most
  542.           likely the file is growing.  */
  543. -      if (S_ISREG (stats->stat.st_mode))
  544. +      if (S_ISREG (st->st_mode))
  545.          {
  546. -          off_t to_be_read = stats->stat.st_size - bufoffset;
  547. +          off_t to_be_read = st->st_size - bufoffset;
  548.            off_t maxsize_off = save + to_be_read;
  549.            if (0 <= to_be_read && to_be_read <= maxsize_off
  550.                && maxsize_off == (size_t) maxsize_off
  551. @@ -1085,7 +1088,7 @@ grepbuf (char const *beg, char const *lim)
  552.     but if the file is a directory and we search it recursively, then
  553.     return -2 if there was a match, and -1 otherwise.  */
  554.  static intmax_t
  555. -grep (int fd, char const *file, struct stats *stats)
  556. +grep (int fd, struct stat const *st)
  557.  {
  558.    intmax_t nlines, i;
  559.    int not_text;
  560. @@ -1095,19 +1098,9 @@ grep (int fd, char const *file, struct stats *stats)
  561.    char *lim;
  562.    char eol = eolbyte;
  563.  
  564. -  if (!reset (fd, file, stats))
  565. +  if (! reset (fd, st))
  566.      return 0;
  567.  
  568. -  if (file && directories == RECURSE_DIRECTORIES
  569. -      && S_ISDIR (stats->stat.st_mode))
  570. -    {
  571. -      /* Close fd now, so that we don't open a lot of file descriptors
  572. -         when we recurse deeply.  */
  573. -      if (close (fd) != 0)
  574. -        suppressible_error (file, errno);
  575. -      return grepdir (file, stats) - 2;
  576. -    }
  577. -
  578.    totalcc = 0;
  579.    lastout = 0;
  580.    totalnl = 0;
  581. @@ -1119,7 +1112,7 @@ grep (int fd, char const *file, struct stats *stats)
  582.    residue = 0;
  583.    save = 0;
  584.  
  585. -  if (! fillbuf (save, stats))
  586. +  if (! fillbuf (save, st))
  587.      {
  588.        suppressible_error (filename, errno);
  589.        return 0;
  590. @@ -1190,7 +1183,7 @@ grep (int fd, char const *file, struct stats *stats)
  591.          totalcc = add_count (totalcc, buflim - bufbeg - save);
  592.        if (out_line)
  593.          nlscan (beg);
  594. -      if (! fillbuf (save, stats))
  595. +      if (! fillbuf (save, st))
  596.          {
  597.            suppressible_error (filename, errno);
  598.            goto finish_grep;
  599. @@ -1214,53 +1207,171 @@ grep (int fd, char const *file, struct stats *stats)
  600.  }
  601.  
  602.  static int
  603. -grepfile (char const *file, struct stats *stats)
  604. +grepdirent (FTS *fts, FTSENT *ent)
  605.  {
  606. -  int desc;
  607. -  intmax_t count;
  608. -  int status;
  609. +  int follow, dirdesc;
  610. +  int command_line = ent->fts_level == FTS_ROOTLEVEL;
  611. +  struct stat *st = ent->fts_statp;
  612.  
  613. -  filename = (file ? file : label ? label : _("(standard input)"));
  614. +  if (ent->fts_info == FTS_DP)
  615. +    {
  616. +      if (directories == RECURSE_DIRECTORIES && command_line)
  617. +        out_file &= ~ (2 * !no_filenames);
  618. +      return 1;
  619. +    }
  620.  
  621. -  if (! file)
  622. -    desc = STDIN_FILENO;
  623. -  else if (devices == SKIP_DEVICES)
  624. +  if ((ent->fts_info == FTS_D || ent->fts_info == FTS_DC
  625. +       || ent->fts_info == FTS_DNR)
  626. +      ? (directories == SKIP_DIRECTORIES
  627. +         || (! (command_line && filename_prefix_len != 0)
  628. +             && excluded_directory_patterns
  629. +             && excluded_file_name (excluded_directory_patterns,
  630. +                                    ent->fts_name)))
  631. +      : ((included_patterns
  632. +          && excluded_file_name (included_patterns, ent->fts_name))
  633. +         || (excluded_patterns
  634. +             && excluded_file_name (excluded_patterns, ent->fts_name))))
  635.      {
  636. -      /* Don't open yet, since that might have side effects on a device.  */
  637. -      desc = -1;
  638. +      fts_set (fts, ent, FTS_SKIP);
  639. +      return 1;
  640.      }
  641. -  else
  642. +
  643. +  filename = ent->fts_path + filename_prefix_len;
  644. +  follow = (fts->fts_options & FTS_LOGICAL
  645. +            || (fts->fts_options & FTS_COMFOLLOW && command_line));
  646. +
  647. +  switch (ent->fts_info)
  648.      {
  649. -      /* When skipping directories, don't worry about directories
  650. -         that can't be opened.  */
  651. -      desc = open (file, O_RDONLY);
  652. -      if (desc < 0 && directories != SKIP_DIRECTORIES)
  653. +    case FTS_D:
  654. +      if (directories == RECURSE_DIRECTORIES)
  655.          {
  656. -          suppressible_error (file, errno);
  657. +          out_file |= 2 * !no_filenames;
  658.            return 1;
  659.          }
  660. +      fts_set (fts, ent, FTS_SKIP);
  661. +      break;
  662. +
  663. +    case FTS_DC:
  664. +      if (!suppress_errors)
  665. +        error (0, 0, _("warning: %s: %s"), filename,
  666. +               _("recursive directory loop"));
  667. +      return 1;
  668. +
  669. +    case FTS_DNR:
  670. +    case FTS_ERR:
  671. +    case FTS_NS:
  672. +      suppressible_error (filename, ent->fts_errno);
  673. +      return 1;
  674. +
  675. +    case FTS_DEFAULT:
  676. +    case FTS_NSOK:
  677. +      if (devices == SKIP_DEVICES
  678. +          || (devices == READ_COMMAND_LINE_DEVICES && !command_line))
  679. +        {
  680. +          struct stat st1;
  681. +          if (! st->st_mode)
  682. +            {
  683. +              /* The file type is not already known.  Get the file status
  684. +                 before opening, since opening might have side effects
  685. +                 on a device.  */
  686. +              int flag = follow ? 0 : AT_SYMLINK_NOFOLLOW;
  687. +              if (fstatat (fts->fts_cwd_fd, ent->fts_accpath, &st1, flag) != 0)
  688. +                {
  689. +                  suppressible_error (filename, errno);
  690. +                  return 1;
  691. +                }
  692. +              st = &st1;
  693. +            }
  694. +          if (is_device_mode (st->st_mode))
  695. +            return 1;
  696. +        }
  697. +      break;
  698. +
  699. +    case FTS_F:
  700. +    case FTS_SLNONE:
  701. +      break;
  702. +
  703. +    case FTS_SL:
  704. +    case FTS_W:
  705. +      return 1;
  706. +
  707. +    default:
  708. +      abort ();
  709.      }
  710.  
  711. -  if (desc < 0
  712. -      ? stat (file, &stats->stat) != 0
  713. -      : fstat (desc, &stats->stat) != 0)
  714. +  dirdesc = ((fts->fts_options & (FTS_NOCHDIR | FTS_CWDFD)) == FTS_CWDFD
  715. +             ? fts->fts_cwd_fd
  716. +             : AT_FDCWD);
  717. +  return grepfile (dirdesc, ent->fts_accpath, follow, command_line);
  718. +}
  719. +
  720. +static int
  721. +grepfile (int dirdesc, char const *name, int follow, int command_line)
  722. +{
  723. +  int desc = openat_safer (dirdesc, name, O_RDONLY | (follow ? 0 : O_NOFOLLOW));
  724. +  if (desc < 0)
  725.      {
  726. -      suppressible_error (filename, errno);
  727. -      if (file)
  728. -        close (desc);
  729. +      if (follow || errno != ELOOP)
  730. +        suppressible_error (filename, errno);
  731.        return 1;
  732.      }
  733. +  return grepdesc (desc, command_line);
  734. +}
  735.  
  736. -  if ((directories == SKIP_DIRECTORIES && S_ISDIR (stats->stat.st_mode))
  737. -      || (devices == SKIP_DEVICES && (S_ISCHR (stats->stat.st_mode)
  738. -                                      || S_ISBLK (stats->stat.st_mode)
  739. -                                      || S_ISSOCK (stats->stat.st_mode)
  740. -                                      || S_ISFIFO (stats->stat.st_mode))))
  741. +static int
  742. +grepdesc (int desc, int command_line)
  743. +{
  744. +  intmax_t count;
  745. +  int status = 1;
  746. +  struct stat st;
  747. +
  748. +  /* Get the file status, possibly for the second time.  This catches
  749. +     a race condition if the directory entry changes after the
  750. +     directory entry is read and before the file is opened.  For
  751. +     example, normally DESC is a directory only at the top level, but
  752. +     there is an exception if some other process substitutes a
  753. +     directory for a non-directory while 'grep' is running.  */
  754. +  if (fstat (desc, &st) != 0)
  755.      {
  756. -      if (file)
  757. -        close (desc);
  758. -      return 1;
  759. +      suppressible_error (filename, errno);
  760. +      goto closeout;
  761. +    }
  762. +  if (desc != STDIN_FILENO
  763. +      && directories == RECURSE_DIRECTORIES && S_ISDIR (st.st_mode))
  764. +    {
  765. +      /* Traverse the directory starting with its full name, because
  766. +         unfortunately fts provides no way to traverse the directory
  767. +         starting from its file descriptor.  */
  768. +
  769. +      FTS *fts;
  770. +      FTSENT *ent;
  771. +      int opts = fts_options & ~(command_line ? 0 : FTS_COMFOLLOW);
  772. +      char *fts_arg[2];
  773. +
  774. +      /* Close DESC now, to conserve file descriptors if the race
  775. +         condition occurs many times in a deep recursion.  */
  776. +      if (close (desc) != 0)
  777. +        suppressible_error (filename, errno);
  778. +
  779. +      fts_arg[0] = (char *) filename;
  780. +      fts_arg[1] = NULL;
  781. +      fts = fts_open (fts_arg, opts, NULL);
  782. +
  783. +      if (!fts)
  784. +        xalloc_die ();
  785. +      while ((ent = fts_read (fts)))
  786. +        status &= grepdirent (fts, ent);
  787. +      if (errno)
  788. +        suppressible_error (filename, errno);
  789. +      if (fts_close (fts) != 0)
  790. +        suppressible_error (filename, errno);
  791. +      return status;
  792.      }
  793. +  if ((directories == SKIP_DIRECTORIES && S_ISDIR (st.st_mode))
  794. +      || ((devices == SKIP_DEVICES
  795. +           || (devices == READ_COMMAND_LINE_DEVICES && !command_line))
  796. +          && is_device_mode (st.st_mode)))
  797. +    goto closeout;
  798.  
  799.    /* If there is a regular file on stdout and the current file refers
  800.       to the same i-node, we have to report the problem and skip it.
  801. @@ -1282,24 +1393,12 @@ grepfile (char const *file, struct stats *stats)
  802.       condition that could result in "alternate" output.  */
  803.    if (!out_quiet && list_files == 0 && 1 < max_count
  804.        && S_ISREG (out_stat.st_mode) && out_stat.st_ino
  805. -      && SAME_INODE (stats->stat, out_stat))
  806. +      && SAME_INODE (st, out_stat))
  807.      {
  808.        if (! suppress_errors)
  809.          error (0, 0, _("input file %s is also the output"), quote (filename));
  810.        errseen = 1;
  811. -      if (file)
  812. -        close (desc);
  813. -      return 1;
  814. -    }
  815. -
  816. -  if (desc < 0)
  817. -    {
  818. -      desc = open (file, O_RDONLY);
  819. -      if (desc < 0)
  820. -        {
  821. -          suppressible_error (file, errno);
  822. -          return 1;
  823. -        }
  824. +      goto closeout;
  825.      }
  826.  
  827.  #if defined SET_BINARY
  828. @@ -1309,7 +1408,7 @@ grepfile (char const *file, struct stats *stats)
  829.      SET_BINARY (desc);
  830.  #endif
  831.  
  832. -  count = grep (desc, file, stats);
  833. +  count = grep (desc, &st);
  834.    if (count < 0)
  835.      status = count + 2;
  836.    else
  837. @@ -1334,103 +1433,35 @@ grepfile (char const *file, struct stats *stats)
  838.            fputc ('\n' & filename_mask, stdout);
  839.          }
  840.  
  841. -      if (! file)
  842. +      if (desc == STDIN_FILENO)
  843.          {
  844.            off_t required_offset = outleft ? bufoffset : after_last_match;
  845.            if (required_offset != bufoffset
  846.                && lseek (desc, required_offset, SEEK_SET) < 0
  847. -              && S_ISREG (stats->stat.st_mode))
  848. +              && S_ISREG (st.st_mode))
  849.              suppressible_error (filename, errno);
  850.          }
  851. -      else
  852. -        while (close (desc) != 0)
  853. -          if (errno != EINTR)
  854. -            {
  855. -              suppressible_error (file, errno);
  856. -              break;
  857. -            }
  858.      }
  859.  
  860. + closeout:
  861. +  if (desc != STDIN_FILENO && close (desc) != 0)
  862. +    suppressible_error (filename, errno);
  863.    return status;
  864.  }
  865.  
  866.  static int
  867. -grepdir (char const *dir, struct stats const *stats)
  868. +grep_command_line_arg (char const *arg)
  869.  {
  870. -  char const *dir_or_dot = (dir ? dir : ".");
  871. -  struct stats const *ancestor;
  872. -  char *name_space;
  873. -  int status = 1;
  874. -  if (dir && excluded_directory_patterns
  875. -      && excluded_file_name (excluded_directory_patterns, dir))
  876. -    return 1;
  877. -
  878. -  /* Mingw32 does not support st_ino.  No known working hosts use zero
  879. -     for st_ino, so assume that the Mingw32 bug applies if it's zero.  */
  880. -  if (stats->stat.st_ino)
  881. +  if (STREQ (arg, "-"))
  882.      {
  883. -      for (ancestor = stats; (ancestor = ancestor->parent) != 0;  )
  884. -        {
  885. -          if (ancestor->stat.st_ino == stats->stat.st_ino
  886. -              && ancestor->stat.st_dev == stats->stat.st_dev)
  887. -            {
  888. -              if (!suppress_errors)
  889. -                error (0, 0, _("warning: %s: %s"), dir,
  890. -                       _("recursive directory loop"));
  891. -              errseen = 1;
  892. -              return 1;
  893. -            }
  894. -        }
  895. -    }
  896. -
  897. -  name_space = savedir (dir_or_dot, stats->stat.st_size, included_patterns,
  898. -                        excluded_patterns, excluded_directory_patterns);
  899. -
  900. -  if (! name_space)
  901. -    {
  902. -      if (errno)
  903. -        suppressible_error (dir_or_dot, errno);
  904. -      else
  905. -        xalloc_die ();
  906. +      filename = label ? label : _("(standard input)");
  907. +      return grepdesc (STDIN_FILENO, 1);
  908.      }
  909.    else
  910.      {
  911. -      size_t dirlen = 0;
  912. -      int needs_slash = 0;
  913. -      char *file_space = NULL;
  914. -      char const *namep = name_space;
  915. -      struct stats child;
  916. -      if (dir)
  917. -        {
  918. -          dirlen = strlen (dir);
  919. -          needs_slash = ! (dirlen == FILE_SYSTEM_PREFIX_LEN (dir)
  920. -                           || ISSLASH (dir[dirlen - 1]));
  921. -        }
  922. -      child.parent = stats;
  923. -      out_file += !no_filenames;
  924. -      while (*namep)
  925. -        {
  926. -          size_t namelen = strlen (namep);
  927. -          char const *file;
  928. -          if (! dir)
  929. -            file = namep;
  930. -          else
  931. -            {
  932. -              file_space = xrealloc (file_space, dirlen + 1 + namelen + 1);
  933. -              strcpy (file_space, dir);
  934. -              file_space[dirlen] = '/';
  935. -              strcpy (file_space + dirlen + needs_slash, namep);
  936. -              file = file_space;
  937. -            }
  938. -          namep += namelen + 1;
  939. -          status &= grepfile (file, &child);
  940. -        }
  941. -      out_file -= !no_filenames;
  942. -      free (file_space);
  943. -      free (name_space);
  944. +      filename = arg;
  945. +      return grepfile (AT_FDCWD, arg, 1, 1);
  946.      }
  947. -
  948. -  return status;
  949.  }
  950.  
  951.  _Noreturn void usage (int);
  952. @@ -1500,7 +1531,8 @@ Output control:\n\
  953.                              ACTION is `read', `recurse', or `skip'\n\
  954.    -D, --devices=ACTION      how to handle devices, FIFOs and sockets;\n\
  955.                              ACTION is `read' or `skip'\n\
  956. -  -R, -r, --recursive       equivalent to --directories=recurse\n\
  957. +  -r, --recursive           like --directories=recurse\n\
  958. +  -R, --dereference-recursive  likewise, but follow all symlinks\n\
  959.  "));
  960.        printf (_("\
  961.        --include=FILE_PATTERN  search only files that match FILE_PATTERN\n\
  962. @@ -1981,6 +2013,8 @@ main (int argc, char **argv)
  963.          break;
  964.  
  965.        case 'R':
  966. +        fts_options = basic_fts_options | FTS_LOGICAL;
  967. +        /* Fall through.  */
  968.        case 'r':
  969.          directories = RECURSE_DIRECTORIES;
  970.          last_recursive = prev_optind;
  971. @@ -2177,48 +2211,24 @@ main (int argc, char **argv)
  972.    if (max_count == 0)
  973.      exit (EXIT_FAILURE);
  974.  
  975. +  if (fts_options & FTS_LOGICAL && devices == READ_COMMAND_LINE_DEVICES)
  976. +    devices = READ_DEVICES;
  977. +
  978.    if (optind < argc)
  979.      {
  980.        status = 1;
  981.        do
  982. -        {
  983. -          char *file = argv[optind];
  984. -          if (!STREQ (file, "-")
  985. -              && (included_patterns || excluded_patterns
  986. -                  || excluded_directory_patterns))
  987. -            {
  988. -              if (isdir (file))
  989. -                {
  990. -                  if (excluded_directory_patterns
  991. -                      && excluded_file_name (excluded_directory_patterns,
  992. -                                             file))
  993. -                    continue;
  994. -                }
  995. -              else
  996. -                {
  997. -                  if (included_patterns
  998. -                      && excluded_file_name (included_patterns, file))
  999. -                    continue;
  1000. -                  if (excluded_patterns
  1001. -                      && excluded_file_name (excluded_patterns, file))
  1002. -                    continue;
  1003. -                }
  1004. -            }
  1005. -          status &= grepfile (STREQ (file, "-") ? (char *) NULL : file,
  1006. -                              &stats_base);
  1007. -        }
  1008. +        status &= grep_command_line_arg (argv[optind]);
  1009.        while (++optind < argc);
  1010.      }
  1011.    else if (directories == RECURSE_DIRECTORIES && prepended < last_recursive)
  1012.      {
  1013. -      status = 1;
  1014. -      if (stat (".", &stats_base.stat) == 0)
  1015. -        status = grepdir (NULL, &stats_base);
  1016. -      else
  1017. -        suppressible_error (".", errno);
  1018. +      /* Grep through ".", omitting leading "./" from diagnostics.  */
  1019. +      filename_prefix_len = 2;
  1020. +      status = grep_command_line_arg (".");
  1021.      }
  1022.    else
  1023. -    status = grepfile ((char *) NULL, &stats_base);
  1024. +    status = grep_command_line_arg ("-");
  1025.  
  1026.    /* We register via atexit() to test stdout.  */
  1027.    exit (errseen ? EXIT_TROUBLE : status);
  1028. diff --git a/tests/Makefile.am b/tests/Makefile.am
  1029. index c2cd2f7..13061fe 100644
  1030. --- a/tests/Makefile.am
  1031. +++ b/tests/Makefile.am
  1032. @@ -82,6 +82,7 @@ TESTS =                       \
  1033.    spencer1                 \
  1034.    spencer1-locale              \
  1035.    status                   \
  1036. +  symlink                  \
  1037.    turkish-I                    \
  1038.    warn-char-classes                \
  1039.    word-delim-multibyte             \
  1040. diff --git a/tests/symlink b/tests/symlink
  1041. new file mode 100755
  1042. index 0000000..012d8f8
  1043. --- /dev/null
  1044. +++ b/tests/symlink
  1045. @@ -0,0 +1,65 @@
  1046. +#!/bin/sh
  1047. +# Check that "grep -r" does the right thing with symbolic links.
  1048. +
  1049. +# Copyright (C) 2012 Free Software Foundation, Inc.
  1050. +
  1051. +# This program is free software: you can redistribute it and/or modify
  1052. +# it under the terms of the GNU General Public License as published by
  1053. +# the Free Software Foundation, either version 3 of the License, or
  1054. +# (at your option) any later version.
  1055. +
  1056. +# This program is distributed in the hope that it will be useful,
  1057. +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  1058. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  1059. +# GNU General Public License for more details.
  1060. +
  1061. +# You should have received a copy of the GNU General Public License
  1062. +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
  1063. +
  1064. +# written by Paul Eggert
  1065. +
  1066. +. "${srcdir=.}/init.sh"; path_prepend_ ../src
  1067. +
  1068. +mkdir dir || framework_failure_
  1069. +echo a > dir/a || framework_failure_
  1070. +echo b > dir/b || framework_failure_
  1071. +ln -s a dir/c || framework_failure_
  1072. +ln -s . dir/d || framework_failure_
  1073. +ln -s dangling dir/e || framework_failure_
  1074. +
  1075. +touch out || framework_failure_
  1076. +
  1077. +for recursion in '' -r -R
  1078. +do
  1079. +  for files in '' '*'
  1080. +  do
  1081. +    case $recursion,$files in
  1082. +      -R,* | *,'*') expected_status=2 ;;
  1083. +      *) expected_status=0 ;;
  1084. +    esac
  1085. +
  1086. +    (cd dir && grep $recursion '^' $files <a ) >grepout
  1087. +    test $? -eq $expected_status || fail=1
  1088. +
  1089. +    case $recursion,$files in
  1090. +      ,)
  1091. +         exp='a\n' ;;
  1092. +      ,'*' | -R,)
  1093. +         exp='a:a\nb:b\nc:a\n' ;;
  1094. +      -r,)
  1095. +         exp='a:a\nb:b\n' ;;
  1096. +      -r,'*')
  1097. +         exp='a:a\nb:b\nc:a\nd/a:a\nd/b:b\n' ;;
  1098. +      -R,'*')
  1099. +         exp='a:a\nb:b\nc:a\nd/a:a\nd/b:b\nd/c:a\n' ;;
  1100. +      *)
  1101. +         framework_failure_ ;;
  1102. +    esac
  1103. +
  1104. +    printf "$exp" >exp || framework_failure_
  1105. +    LC_ALL=C sort grepout >out || fail=1
  1106. +    compare exp out || fail=1
  1107. +  done
  1108. +done
  1109. +
  1110. +Exit $fail
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement