git: 3931c072c63d - main - diff: fix failed compare when ignoring file case

From: Tom Jones <thj_at_FreeBSD.org>
Date: Fri, 18 Feb 2022 15:55:27 UTC
The branch main has been updated by thj:

URL: https://cgit.FreeBSD.org/src/commit/?id=3931c072c63d5a51d83157009e7f7510e08da80c

commit 3931c072c63d5a51d83157009e7f7510e08da80c
Author:     Tom Jones <thj@FreeBSD.org>
AuthorDate: 2022-02-18 15:21:23 +0000
Commit:     Tom Jones <thj@FreeBSD.org>
CommitDate: 2022-02-18 15:51:38 +0000

    diff: fix failed compare when ignoring file case
    
    With --ignore-file-name-case we need to compare files regardless of
    case. We need to propigate both names down to diffit so we can look up
    the correct file when the names differ based on case, otherwise we try
    to look up the file using the case from the a tree which might not be
    discoverable if its case is different in the b tree.
    
    Reviewed by:    bapt
    Sponsored by:   Klara Inc.
    Differential Revision:  https://reviews.freebsd.org/D34201
---
 usr.bin/diff/diffdir.c          | 27 ++++++++++++++++++---------
 usr.bin/diff/tests/diff_test.sh | 13 +++++++++++++
 2 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/usr.bin/diff/diffdir.c b/usr.bin/diff/diffdir.c
index 527cd31a8c30..c5a32c217c36 100644
--- a/usr.bin/diff/diffdir.c
+++ b/usr.bin/diff/diffdir.c
@@ -38,7 +38,8 @@ __FBSDID("$FreeBSD$");
 #include "diff.h"
 
 static int selectfile(const struct dirent *);
-static void diffit(struct dirent *, char *, size_t, char *, size_t, int);
+static void diffit(struct dirent *, char *, size_t, struct dirent *,
+	char *, size_t, int);
 static void print_only(const char *, size_t, const char *);
 
 #define d_status	d_type		/* we need to store status for -l */
@@ -128,14 +129,14 @@ diffdir(char *p1, char *p2, int flags)
 		    strcmp(dent1->d_name, dent2->d_name) ;
 		if (pos == 0) {
 			/* file exists in both dirs, diff it */
-			diffit(dent1, path1, dirlen1, path2, dirlen2, flags);
+			diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
 			dp1++;
 			dp2++;
 		} else if (pos < 0) {
 			/* file only in first dir, only diff if -N */
 			if (Nflag) {
-				diffit(dent1, path1, dirlen1, path2, dirlen2,
-				    flags);
+				diffit(dent1, path1, dirlen1, dent2, path2,
+					dirlen2, flags);
 			} else {
 				print_only(path1, dirlen1, dent1->d_name);
 				status |= 1;
@@ -144,8 +145,8 @@ diffdir(char *p1, char *p2, int flags)
 		} else {
 			/* file only in second dir, only diff if -N or -P */
 			if (Nflag || Pflag)
-				diffit(dent2, path1, dirlen1, path2, dirlen2,
-				    flags);
+				diffit(dent2, path1, dirlen1, dent1, path2,
+					dirlen2, flags);
 			else {
 				print_only(path2, dirlen2, dent2->d_name);
 				status |= 1;
@@ -171,12 +172,20 @@ closem:
  * Do the actual diff by calling either diffreg() or diffdir().
  */
 static void
-diffit(struct dirent *dp, char *path1, size_t plen1, char *path2, size_t plen2,
-    int flags)
+diffit(struct dirent *dp, char *path1, size_t plen1, struct dirent *dp2,
+	char *path2, size_t plen2, int flags)
 {
 	flags |= D_HEADER;
 	strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
-	strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
+
+	/*
+	 * If we are ignoring file case, use dent2s name here if both names are
+	 * the same apart from case.
+	 */
+	if (ignore_file_case && strcasecmp(dp2->d_name, dp2->d_name) == 0)
+		strlcpy(path2 + plen2, dp2->d_name, PATH_MAX - plen2);
+	else
+		strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
 
 	if (noderef) {
 		if (lstat(path1, &stb1) != 0) {
diff --git a/usr.bin/diff/tests/diff_test.sh b/usr.bin/diff/tests/diff_test.sh
index 91e43b570655..fa76c7ada3eb 100755
--- a/usr.bin/diff/tests/diff_test.sh
+++ b/usr.bin/diff/tests/diff_test.sh
@@ -21,6 +21,7 @@ atf_test_case non_regular_file
 atf_test_case binary
 atf_test_case functionname
 atf_test_case noderef
+atf_test_case ignorecase
 
 simple_body()
 {
@@ -324,6 +325,17 @@ noderef_body()
 	atf_check -o inline:"Symbolic links A/test-file and B/test-file differ\n" -s exit:1 diff -r --no-dereference A B
 }
 
+ignorecase_body()
+{
+	atf_check mkdir A
+	atf_check mkdir B
+
+	atf_check -x "echo hello > A/foo"
+	atf_check -x "echo hello > B/FOO"
+
+	atf_check -o empty -s exit:0 diff -u -r --ignore-file-name-case A B
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case simple
@@ -347,4 +359,5 @@ atf_init_test_cases()
 	atf_add_test_case binary
 	atf_add_test_case functionname
 	atf_add_test_case noderef
+	atf_add_test_case ignorecase
 }