git: 77eb877714d6 - main - grep: Fix various bugs in recursive tree handling

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Wed, 04 Sep 2024 19:53:55 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=77eb877714d69ee0279d70eb3331920fba90db95

commit 77eb877714d69ee0279d70eb3331920fba90db95
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2024-09-04 19:53:17 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2024-09-04 19:53:17 +0000

    grep: Fix various bugs in recursive tree handling
    
    The -OpS options were effectively ignored due to a collection of
    bugs in the use of fts(3):
    
    - fts_open(3) requires one of FTS_PHYSICAL or FTS_LOGICAL to be
      specified, but in the -O case, only FTS_COMFOLLOW was given.  Fix
      this to use FTS_COMFOLLOW | FTS_PHYSICAL.
    
    - The switch on the entry type returned by fts_read() did not check
      for symbolic links, so symbolic links fell into the default case and
      were always passed to procfile() even when -p was given.  Fix this
      by adding cases in the switch statement to explicitly ignore FTS_SL.
    
    - FTS_NOSTAT was passed to fts_open(), so fts_open() couldn't detect
      symbolic links when FTS_PHYSICAL was passed, instead both regular
      files and symbolic links were returned as FTS_NSOK entries.  Fix
      by only using FTS_NOSTAT with FTS_LOGICAL.
    
    While here, fix a few other nits:
    
    - Treat FTS_NS as an error like FTS_DNR and FTS_ERR.
    
    - Just ignore FTS_DP.  The logic to skip descending into skipped
      directories is only relevant when a directory is first visited, not
      after the directory has been visited.
    
    - Use warnc instead of warnx + strerror.
    
    PR:             280676
    Reviewed by:    kevans
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D46255
---
 usr.bin/grep/util.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/usr.bin/grep/util.c b/usr.bin/grep/util.c
index 936abc41b3ef..4e1c44b442f2 100644
--- a/usr.bin/grep/util.c
+++ b/usr.bin/grep/util.c
@@ -136,16 +136,16 @@ grep_tree(char **argv)
 	/* This switch effectively initializes 'fts_flags' */
 	switch(linkbehave) {
 	case LINK_EXPLICIT:
-		fts_flags = FTS_COMFOLLOW;
+		fts_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
 		break;
 	case LINK_SKIP:
 		fts_flags = FTS_PHYSICAL;
 		break;
 	default:
-		fts_flags = FTS_LOGICAL;
+		fts_flags = FTS_LOGICAL | FTS_NOSTAT;
 	}
 
-	fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
+	fts_flags |= FTS_NOCHDIR;
 
 	fts = fts_open((argv[0] == NULL) ?
 	    __DECONST(char * const *, wd) : argv, fts_flags, NULL);
@@ -154,15 +154,13 @@ grep_tree(char **argv)
 	while (errno = 0, (p = fts_read(fts)) != NULL) {
 		switch (p->fts_info) {
 		case FTS_DNR:
-			/* FALLTHROUGH */
 		case FTS_ERR:
+		case FTS_NS:
 			file_err = true;
 			if(!sflag)
-				warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+				warnc(p->fts_errno, "%s", p->fts_path);
 			break;
 		case FTS_D:
-			/* FALLTHROUGH */
-		case FTS_DP:
 			if (dexclude || dinclude)
 				if (!dir_matching(p->fts_name) ||
 				    !dir_matching(p->fts_path))
@@ -173,6 +171,17 @@ grep_tree(char **argv)
 			warnx("warning: %s: recursive directory loop",
 			    p->fts_path);
 			break;
+		case FTS_DP:
+			break;
+		case FTS_SL:
+			/*
+			 * Skip symlinks for LINK_EXPLICIT and
+			 * LINK_SKIP.  Note that due to FTS_COMFOLLOW,
+			 * symlinks on the command line are followed
+			 * for LINK_EXPLICIT and not reported as
+			 * symlinks.
+			 */
+			break;
 		default:
 			/* Check for file exclusion/inclusion */
 			ok = true;