Index: usr.bin/diff/diff.h =================================================================== --- usr.bin/diff/diff.h +++ usr.bin/diff/diff.h @@ -101,7 +101,7 @@ }; 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]; Index: usr.bin/diff/diff.c =================================================================== --- usr.bin/diff/diff.c +++ usr.bin/diff/diff.c @@ -39,7 +39,7 @@ #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 @@ OPT_CHANGED_GROUP_FORMAT, OPT_SUPPRESS_COMMON, OPT_COLOR, + OPT_NO_DEREFERENCE, }; static struct option longopts[] = { @@ -97,6 +98,7 @@ { "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 @@ 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; Index: usr.bin/diff/diffdir.c =================================================================== --- usr.bin/diff/diffdir.c +++ usr.bin/diff/diffdir.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "diff.h" @@ -175,28 +176,84 @@ { 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); + + 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) ? "link" : "error")), + path2, S_ISLNK(stb2.st_mode) ? "symbolic link" : + (S_ISDIR(stb2.st_mode) ? "directory" : + (S_ISREG(stb2.st_mode) ? "file" : "error"))); 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);