Index: usr.bin/cmp/cmp.1 =================================================================== --- usr.bin/cmp/cmp.1 +++ usr.bin/cmp/cmp.1 @@ -41,6 +41,7 @@ .Nm .Op Fl l | s | x .Op Fl hz +.Op Fl -bytes Ns Cm = Ns Ar num .Ar file1 file2 .Op Ar skip1 Op Ar skip2 .Sh DESCRIPTION @@ -62,6 +63,10 @@ .It Fl l , Fl -verbose Print the byte number (decimal) and the differing byte values (octal) for each difference. +.It Fl n Ar num , Fl -bytes= Ns num +Only compare up to +.Ar num +bytes. .It Fl s , Fl -silent , Fl -quiet Print nothing for differing files; return exit status only. @@ -165,6 +170,7 @@ compatible. The .Fl h , +.Fl n , .Fl x , and .Fl z Index: usr.bin/cmp/cmp.c =================================================================== --- usr.bin/cmp/cmp.c +++ usr.bin/cmp/cmp.c @@ -62,11 +62,13 @@ #include "extern.h" +off_t limit; bool lflag, sflag, xflag, zflag; static const struct option long_opts[] = { {"verbose", no_argument, NULL, 'l'}, + {"bytes", required_argument, NULL, 'n'}, {"silent", no_argument, NULL, 's'}, {"quiet", no_argument, NULL, 's'}, {NULL, no_argument, NULL, 0} @@ -85,8 +87,15 @@ skip1 = skip2 = 0; oflag = O_RDONLY; - while ((ch = getopt_long(argc, argv, "+hlsxz", long_opts, NULL)) != -1) + while ((ch = getopt_long(argc, argv, "+hln:sxz", long_opts, NULL)) != -1) switch (ch) { + case 'n': /* Limit */ + if (expand_number(optarg, &limit) < 0 || limit < 0) { + fprintf(stderr, "Invalid --bytes: '%s'\n", + optarg); + usage(); + } + break; case 'h': /* Don't follow symlinks */ oflag |= O_NOFOLLOW; break; Index: usr.bin/cmp/extern.h =================================================================== --- usr.bin/cmp/extern.h +++ usr.bin/cmp/extern.h @@ -44,4 +44,5 @@ void diffmsg(const char *, const char *, off_t, off_t); void eofmsg(const char *); +extern off_t limit; extern bool lflag, sflag, xflag, zflag; Index: usr.bin/cmp/link.c =================================================================== --- usr.bin/cmp/link.c +++ usr.bin/cmp/link.c @@ -72,7 +72,8 @@ dfound = 0; byte = 1; - for (p1 = buf1 + skip1, p2 = buf2 + skip2; *p1 && *p2; p1++, p2++) { + for (p1 = buf1 + skip1, p2 = buf2 + skip2; + *p1 && *p2 && (limit == 0 || byte <= limit); p1++, p2++) { if ((ch = *p1) != *p2) { if (xflag) { dfound = 1; Index: usr.bin/cmp/regular.c =================================================================== --- usr.bin/cmp/regular.c +++ usr.bin/cmp/regular.c @@ -86,6 +86,8 @@ off2 = ROUNDPAGE(skip2); length = MIN(len1, len2); + if (limit > 0) + length = MIN(length, limit); if ((m1 = remmap(NULL, fd1, off1)) == NULL) { c_special(fd1, file1, skip1, fd2, file2, skip2); Index: usr.bin/cmp/special.c =================================================================== --- usr.bin/cmp/special.c +++ usr.bin/cmp/special.c @@ -76,7 +76,7 @@ if (getc(fp2) == EOF) goto eof; - for (byte = line = 1;; ++byte) { + for (byte = line = 1; limit == 0 || byte <= limit; ++byte) { ch1 = getc(fp1); ch2 = getc(fp2); if (ch1 == EOF || ch2 == EOF) Index: usr.bin/cmp/tests/cmp_test2.sh =================================================================== --- usr.bin/cmp/tests/cmp_test2.sh +++ usr.bin/cmp/tests/cmp_test2.sh @@ -91,10 +91,33 @@ atf_check -s exit:0 cmp -s a b 1k 1k } +atf_test_case limit +limit_head() +{ + atf_set "descr" "Test cmp(1) -n (limit)" +} +limit_body() +{ + echo -n "aaaabbbb" > a + echo -n "aaaaxxxx" > b + + atf_check -s exit:1 -o ignore cmp -s a b + atf_check -s exit:0 cmp -sn 4 a b + atf_check -s exit:0 cmp -sn 3 a b + atf_check -s exit:1 -o ignore cmp -sn 5 a b + + # Test special, too. The implementation for link is effectively + # identical. + atf_check -s exit:0 -e empty -x "cat a | cmp -sn 4 b -" + atf_check -s exit:0 -e empty -x "cat a | cmp -sn 3 b -" + atf_check -s exit:1 -o ignore -x "cat a | cmp -sn 5 b -" +} + atf_init_test_cases() { atf_add_test_case special atf_add_test_case symlink atf_add_test_case pr252542 atf_add_test_case skipsuff + atf_add_test_case limit }