The branch main has been updated by thj:

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

commit f4be3645a14d4faa5a51846054318ccf77c9cd5e
Author:     Tom Jones <t...@freebsd.org>
AuthorDate: 2022-02-18 15:13:13 +0000
Commit:     Tom Jones <t...@freebsd.org>
CommitDate: 2022-02-18 15:17:13 +0000

    diff: add --no-dereference flag
    
    When diffing files and directories, don't follow symbolic links, instead
    compare where the links point to.
    
    Reviewed by:    bapt
    Sponsored by:   Klara Inc.
    Differential Revision:  https://reviews.freebsd.org/D34203
---
 usr.bin/diff/diff.c             |  8 +++-
 usr.bin/diff/diff.h             |  2 +-
 usr.bin/diff/diffdir.c          | 94 +++++++++++++++++++++++++++++++++--------
 usr.bin/diff/tests/diff_test.sh | 31 ++++++++++++++
 4 files changed, 116 insertions(+), 19 deletions(-)

diff --git a/usr.bin/diff/diff.c b/usr.bin/diff/diff.c
index 8074261742ae..224301d56d18 100644
--- a/usr.bin/diff/diff.c
+++ b/usr.bin/diff/diff.c
@@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$");
 #include "xmalloc.h"
 
 bool    lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
-bool    ignore_file_case, suppress_common, color;
+bool    ignore_file_case, suppress_common, color, noderef;
 int     diff_format, diff_context, status;
 int     tabsize = 8, width = 130;
 static int     colorflag = COLORFLAG_NEVER;
@@ -62,6 +62,7 @@ enum {
        OPT_CHANGED_GROUP_FORMAT,
        OPT_SUPPRESS_COMMON,
        OPT_COLOR,
+       OPT_NO_DEREFERENCE,
 };
 
 static struct option longopts[] = {
@@ -97,6 +98,7 @@ static struct option longopts[] = {
        { "side-by-side",               no_argument,            NULL,   'y' },
        { "ignore-file-name-case",      no_argument,            NULL,   
OPT_IGN_FN_CASE },
        { "horizon-lines",              required_argument,      NULL,   
OPT_HORIZON_LINES },
+       { "no-dereference",             no_argument,            NULL,   
OPT_NO_DEREFERENCE},
        { "no-ignore-file-name-case",   no_argument,            NULL,   
OPT_NO_IGN_FN_CASE },
        { "normal",                     no_argument,            NULL,   
OPT_NORMAL },
        { "strip-trailing-cr",          no_argument,            NULL,   
OPT_STRIPCR },
@@ -328,6 +330,10 @@ main(int argc, char **argv)
                                errx(2, "unsupported --color value '%s' (must 
be always, auto, or never)",
                                        optarg);
                        break;
+               case OPT_NO_DEREFERENCE:
+                       rflag = true;
+                       noderef = true;
+                       break;
                default:
                        usage();
                        break;
diff --git a/usr.bin/diff/diff.h b/usr.bin/diff/diff.h
index 4a7d19ee8982..fd649b017211 100644
--- a/usr.bin/diff/diff.h
+++ b/usr.bin/diff/diff.h
@@ -101,7 +101,7 @@ struct excludes {
 };
 
 extern bool    lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
-extern bool    ignore_file_case, suppress_common, color;
+extern bool    ignore_file_case, suppress_common, color, noderef;
 extern int     diff_format, diff_context, status;
 extern int     tabsize, width;
 extern char    *start, *ifdefname, *diffargs, *label[2];
diff --git a/usr.bin/diff/diffdir.c b/usr.bin/diff/diffdir.c
index ecb7c4a6c4ee..527cd31a8c30 100644
--- a/usr.bin/diff/diffdir.c
+++ b/usr.bin/diff/diffdir.c
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
+#include <unistd.h>
 
 #include "diff.h"
 
@@ -175,28 +176,87 @@ diffit(struct dirent *dp, char *path1, size_t plen1, char 
*path2, size_t plen2,
 {
        flags |= D_HEADER;
        strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
-       if (stat(path1, &stb1) != 0) {
-               if (!(Nflag || Pflag) || errno != ENOENT) {
-                       warn("%s", path1);
-                       return;
+       strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
+
+       if (noderef) {
+               if (lstat(path1, &stb1) != 0) {
+                       if (!(Nflag || Pflag) || errno != ENOENT) {
+                               warn("%s", path1);
+                               return;
+                       }
+                       flags |= D_EMPTY1;
+                       memset(&stb1, 0, sizeof(stb1));
                }
-               flags |= D_EMPTY1;
-               memset(&stb1, 0, sizeof(stb1));
-       }
 
-       strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
-       if (stat(path2, &stb2) != 0) {
-               if (!Nflag || errno != ENOENT) {
-                       warn("%s", path2);
+               if (lstat(path2, &stb2) != 0) {
+                       if (!Nflag || errno != ENOENT) {
+                               warn("%s", path2);
+                               return;
+                       }
+                       flags |= D_EMPTY2;
+                       memset(&stb2, 0, sizeof(stb2));
+                       stb2.st_mode = stb1.st_mode;
+               }
+               if (stb1.st_mode == 0)
+                       stb1.st_mode = stb2.st_mode;
+               if (S_ISLNK(stb1.st_mode) || S_ISLNK(stb2.st_mode)) {
+                       if  (S_ISLNK(stb1.st_mode) && S_ISLNK(stb2.st_mode)) {
+                               char buf1[PATH_MAX];
+                               char buf2[PATH_MAX];
+                               ssize_t len1 = 0;
+                               ssize_t len2 = 0;
+
+                               len1 = readlink(path1, buf1, sizeof(buf1));
+                               len2 = readlink(path2, buf2, sizeof(buf2));
+
+                               if (len1 < 0 || len2 < 0) {
+                                       perror("reading links");
+                                       return;
+                               }
+                               buf1[len1] = '\0';
+                               buf2[len2] = '\0';
+
+                               if (len1 != len2 || strncmp(buf1, buf2, len1) 
!= 0) {
+                                       printf("Symbolic links %s and %s 
differ\n",
+                                               path1, path2);
+                                       status |= 1;
+                               }
+
+                               return;
+                       }
+
+                       printf("File %s is a %s while file %s is a %s\n",
+                               path1, S_ISLNK(stb1.st_mode) ? "symbolic link" :
+                                       (S_ISDIR(stb1.st_mode) ? "directory" :
+                                       (S_ISREG(stb1.st_mode) ? "file" : 
"error")),
+                               path2, S_ISLNK(stb2.st_mode) ? "symbolic link" :
+                                       (S_ISDIR(stb2.st_mode) ? "directory" :
+                                       (S_ISREG(stb2.st_mode) ? "file" : 
"error")));
+                       status |= 1;
                        return;
                }
-               flags |= D_EMPTY2;
-               memset(&stb2, 0, sizeof(stb2));
-               stb2.st_mode = stb1.st_mode;
-       }
-       if (stb1.st_mode == 0)
-               stb1.st_mode = stb2.st_mode;
+       } else {
+               if (stat(path1, &stb1) != 0) {
+                       if (!(Nflag || Pflag) || errno != ENOENT) {
+                               warn("%s", path1);
+                               return;
+                       }
+                       flags |= D_EMPTY1;
+                       memset(&stb1, 0, sizeof(stb1));
+               }
 
+               if (stat(path2, &stb2) != 0) {
+                       if (!Nflag || errno != ENOENT) {
+                               warn("%s", path2);
+                               return;
+                       }
+                       flags |= D_EMPTY2;
+                       memset(&stb2, 0, sizeof(stb2));
+                       stb2.st_mode = stb1.st_mode;
+               }
+               if (stb1.st_mode == 0)
+                       stb1.st_mode = stb2.st_mode;
+       }
        if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
                if (rflag)
                        diffdir(path1, path2, flags);
diff --git a/usr.bin/diff/tests/diff_test.sh b/usr.bin/diff/tests/diff_test.sh
index 8c0219712db7..a024016edb10 100755
--- a/usr.bin/diff/tests/diff_test.sh
+++ b/usr.bin/diff/tests/diff_test.sh
@@ -20,6 +20,7 @@ atf_test_case report_identical
 atf_test_case non_regular_file
 atf_test_case binary
 atf_test_case functionname
+atf_test_case noderef
 
 simple_body()
 {
@@ -296,6 +297,35 @@ functionname_body()
                "$(atf_get_srcdir)/functionname.in" 
"$(atf_get_srcdir)/functionname_objcclassm.in"
 }
 
+noderef_body()
+{
+       atf_check mkdir A B
+
+       atf_check -x "echo 1 > A/test-file"
+       atf_check -x "echo 1 > test-file"
+       atf_check -x "echo 1 > test-file2"
+
+       atf_check ln -s $(pwd)/test-file B/test-file
+
+       atf_check -o empty -s exit:0 diff -r A B
+       atf_check -o inline:"File A/test-file is a file while file B/test-file 
is a symbolic link\n" \
+               -s exit:1 diff -r --no-dereference A B
+
+       # both test files are now the same symbolic link
+       atf_check rm A/test-file
+
+       atf_check ln -s $(pwd)/test-file A/test-file
+       atf_check -o empty -s exit:0 diff -r A B
+       atf_check -o empty -s exit:0 diff -r --no-dereference A B
+
+       # make test files different symbolic links, but same contents
+       atf_check unlink A/test-file
+       atf_check ln -s $(pwd)/test-file2 A/test-file
+
+       atf_check -o empty -s exit:0 diff -r A B
+       atf_check -o inline:"Symbolic links A/test-file and B/test-file 
differ\n" -s exit:1 diff -r --no-dereference A B
+}
+
 atf_init_test_cases()
 {
        atf_add_test_case simple
@@ -318,4 +348,5 @@ atf_init_test_cases()
        atf_add_test_case non_regular_file
        atf_add_test_case binary
        atf_add_test_case functionname
+       atf_add_test_case noderef
 }

Reply via email to