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
This commit is contained in:
parent
8f79bd9b85
commit
f4be3645a1
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
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 (noderef) {
|
||||
if (lstat(path1, &stb1) != 0) {
|
||||
if (!(Nflag || Pflag) || errno != ENOENT) {
|
||||
warn("%s", path1);
|
||||
return;
|
||||
}
|
||||
flags |= D_EMPTY1;
|
||||
memset(&stb1, 0, sizeof(stb1));
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user