aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/cmp
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/cmp')
-rw-r--r--usr.bin/cmp/Makefile11
-rw-r--r--usr.bin/cmp/Makefile.depend18
-rw-r--r--usr.bin/cmp/cmp.1210
-rw-r--r--usr.bin/cmp/cmp.c269
-rw-r--r--usr.bin/cmp/extern.h48
-rw-r--r--usr.bin/cmp/link.c103
-rw-r--r--usr.bin/cmp/misc.c62
-rw-r--r--usr.bin/cmp/regular.c191
-rw-r--r--usr.bin/cmp/special.c126
-rw-r--r--usr.bin/cmp/tests/Makefile13
-rw-r--r--usr.bin/cmp/tests/Makefile.depend10
-rw-r--r--usr.bin/cmp/tests/b_flag.out1
-rw-r--r--usr.bin/cmp/tests/bl_flag.out1
-rwxr-xr-xusr.bin/cmp/tests/cmp_test2.sh175
14 files changed, 1238 insertions, 0 deletions
diff --git a/usr.bin/cmp/Makefile b/usr.bin/cmp/Makefile
new file mode 100644
index 000000000000..6f4720226a3e
--- /dev/null
+++ b/usr.bin/cmp/Makefile
@@ -0,0 +1,11 @@
+.include <src.opts.mk>
+
+PROG= cmp
+SRCS= cmp.c link.c misc.c regular.c special.c
+
+LIBADD= util
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cmp/Makefile.depend b/usr.bin/cmp/Makefile.depend
new file mode 100644
index 000000000000..ad56045f44d3
--- /dev/null
+++ b/usr.bin/cmp/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcapsicum \
+ lib/libcasper/libcasper \
+ lib/libcompiler_rt \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.bin/cmp/cmp.1 b/usr.bin/cmp/cmp.1
new file mode 100644
index 000000000000..ab797fa89e2a
--- /dev/null
+++ b/usr.bin/cmp/cmp.1
@@ -0,0 +1,210 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd September 23, 2021
+.Dt CMP 1
+.Os
+.Sh NAME
+.Nm cmp
+.Nd compare two files
+.Sh SYNOPSIS
+.Nm
+.Op Fl l | s | x
+.Op Fl bhz
+.Op Fl -ignore-initial Ns Cm = Ns Ar num1 Ns Op :num2
+.Op Fl -bytes Ns Cm = Ns Ar num
+.Ar file1 file2
+.Op Ar skip1 Op Ar skip2
+.Sh DESCRIPTION
+The
+.Nm
+utility compares two files of any type and writes the results
+to the standard output.
+By default,
+.Nm
+is silent if the files are the same; if they differ, the byte
+and line number at which the first difference occurred is reported.
+.Pp
+Bytes and lines are numbered beginning with one.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl b , Fl -print-bytes
+Print each byte when a difference is found.
+.It Fl h
+Do not follow symbolic links.
+.It Fl i Ar num1 Ns Oo :num2 Oc , Fl -ignore-initial= Ns Ar num1 Ns Op :num2
+Skip
+.Ar num1
+bytes from
+.Ar file1 ,
+and optionally skip
+.Ar num2
+bytes from
+.Ar file2 .
+If
+.Ar num2
+is not specified, then
+.Ar num1
+is applied for both
+.Ar file1
+and
+.Ar file2 .
+.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.
+.It Fl x
+Like
+.Fl l
+but prints in hexadecimal and using zero as index
+for the first byte in the files.
+.It Fl z
+For regular files compare file sizes first, and fail the comparison if they
+are not equal.
+.El
+.Pp
+The optional arguments
+.Ar skip1
+and
+.Ar skip2
+are the byte offsets from the beginning of
+.Ar file1
+and
+.Ar file2 ,
+respectively, where the comparison will begin.
+The offset is decimal by default, but may be expressed as a hexadecimal
+or octal value by preceding it with a leading ``0x'' or ``0''.
+.Pp
+.Ar skip1
+and
+.Ar skip2
+may also be specified with SI size suffixes.
+.Sh EXIT STATUS
+The
+.Nm
+utility exits with one of the following values:
+.Bl -tag -width 4n
+.It 0
+The files are identical.
+.It 1
+The files are different; this includes the case
+where one file is identical to the first part of
+the other.
+In the latter case, if the
+.Fl s
+option has not been specified,
+.Nm
+writes to standard error that EOF was reached in the shorter
+file (before any differences were found).
+.It >1
+An error occurred.
+.El
+.Sh EXAMPLES
+Assuming a file named
+.Pa example.txt
+with the following contents:
+.Bd -literal -offset indent
+a
+b
+c
+.Ed
+.Pp
+Compare stdin with
+.Pa example.txt :
+.Bd -literal -offset indent
+$ echo -e "a\\nb\\nc" | cmp - example.txt
+.Ed
+.Pp
+Same as above but introducing a change in the third byte of stdin.
+Show the byte number (decimal) and differing byte (octal):
+.Bd -literal -offset indent
+$ echo -e "a\\nR\\nc" | cmp -l - example.txt
+ 3 122 142
+.Ed
+.Pp
+Compare file sizes of
+.Pa example.txt
+and
+.Pa /boot/loader.conf
+and return 1 if they are not equal.
+Note that
+.Fl z
+can only be used with regular files:
+.Bd -literal -offset indent
+$ cmp -z example.txt /boot/loader.conf
+example.txt /boot/loader.conf differ: size
+.Ed
+.Pp
+Compare stdin with
+.Pa example.txt
+omitting the first 4 bytes from stdin and the first 2 bytes from
+.Pa example.txt :
+.Bd -literal -offset indent
+$ echo -e "a\\nR\\nb\\nc" | cmp - example.txt 4 2
+.Ed
+.Sh SEE ALSO
+.Xr diff 1 ,
+.Xr diff3 1
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
+The
+.Fl b ,
+.Fl h ,
+.Fl i ,
+.Fl n ,
+.Fl x ,
+and
+.Fl z
+options are extensions to the standard.
+.Ar skip1
+and
+.Ar skip2
+arguments are extensions to the standard.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
+.Sh BUGS
+The phrase
+.Dq SI size suffixes
+above refers to the traditional power of two convention, as described in
+.Xr expand_number 3 .
diff --git a/usr.bin/cmp/cmp.c b/usr.bin/cmp/cmp.c
new file mode 100644
index 000000000000..746616c0c20b
--- /dev/null
+++ b/usr.bin/cmp/cmp.c
@@ -0,0 +1,269 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1987, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <capsicum_helpers.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <nl_types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libutil.h>
+
+#include "extern.h"
+
+bool bflag, lflag, sflag, xflag, zflag;
+
+static const struct option long_opts[] =
+{
+ {"print-bytes", no_argument, NULL, 'b'},
+ {"ignore-initial", required_argument, NULL, 'i'},
+ {"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}
+};
+
+#ifdef SIGINFO
+volatile sig_atomic_t info;
+
+static void
+siginfo(int signo)
+{
+ info = signo;
+}
+#endif
+
+static void usage(void) __dead2;
+
+static bool
+parse_iskipspec(char *spec, off_t *skip1, off_t *skip2)
+{
+ char *colon;
+
+ colon = strchr(spec, ':');
+ if (colon != NULL)
+ *colon++ = '\0';
+
+ if (expand_number(spec, skip1) < 0)
+ return (false);
+
+ if (colon != NULL)
+ return (expand_number(colon, skip2) == 0);
+
+ *skip2 = *skip1;
+ return (true);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb1, sb2;
+ off_t skip1, skip2, limit;
+ int ch, fd1, fd2, oflag;
+ bool special;
+ const char *file1, *file2;
+ int ret;
+
+ limit = skip1 = skip2 = ret = 0;
+ oflag = O_RDONLY;
+ while ((ch = getopt_long(argc, argv, "+bhi:ln:sxz", long_opts, NULL)) != -1)
+ switch (ch) {
+ case 'b': /* Print bytes */
+ bflag = true;
+ break;
+ case 'h': /* Don't follow symlinks */
+ oflag |= O_NOFOLLOW;
+ break;
+ case 'i':
+ if (!parse_iskipspec(optarg, &skip1, &skip2)) {
+ fprintf(stderr,
+ "Invalid --ignore-initial: %s\n",
+ optarg);
+ usage();
+ }
+ break;
+ case 'l': /* print all differences */
+ lflag = true;
+ break;
+ case 'n': /* Limit */
+ if (expand_number(optarg, &limit) < 0 || limit < 0) {
+ fprintf(stderr, "Invalid --bytes: %s\n",
+ optarg);
+ usage();
+ }
+ break;
+ case 's': /* silent run */
+ sflag = true;
+ break;
+ case 'x': /* hex output */
+ lflag = true;
+ xflag = true;
+ break;
+ case 'z': /* compare size first */
+ zflag = true;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (lflag && sflag)
+ errx(ERR_EXIT, "specifying -s with -l or -x is not permitted");
+
+ if (argc < 2 || argc > 4)
+ usage();
+
+ /* Don't limit rights on stdin since it may be one of the inputs. */
+ if (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
+ err(ERR_EXIT, "unable to limit rights on stdout");
+ if (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
+ err(ERR_EXIT, "unable to limit rights on stderr");
+
+ /* Backward compatibility -- handle "-" meaning stdin. */
+ special = false;
+ if (strcmp(file1 = argv[0], "-") == 0) {
+ special = true;
+ fd1 = STDIN_FILENO;
+ file1 = "stdin";
+ } else if ((fd1 = open(file1, oflag, 0)) < 0 && errno != EMLINK) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file1);
+ else
+ exit(ERR_EXIT);
+ }
+ if (strcmp(file2 = argv[1], "-") == 0) {
+ if (special)
+ errx(ERR_EXIT,
+ "standard input may only be specified once");
+ special = true;
+ fd2 = STDIN_FILENO;
+ file2 = "stdin";
+ } else if ((fd2 = open(file2, oflag, 0)) < 0 && errno != EMLINK) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file2);
+ else
+ exit(ERR_EXIT);
+ }
+
+ if (argc > 2 && expand_number(argv[2], &skip1) < 0) {
+ fprintf(stderr, "Invalid skip1: %s\n", argv[2]);
+ usage();
+ }
+
+ if (argc == 4 && expand_number(argv[3], &skip2) < 0) {
+ fprintf(stderr, "Invalid skip2: %s\n", argv[3]);
+ usage();
+ }
+
+ if (sflag && skip1 == 0 && skip2 == 0)
+ zflag = true;
+
+ if (fd1 == -1) {
+ if (fd2 == -1) {
+ ret = c_link(file1, skip1, file2, skip2, limit);
+ goto end;
+ } else if (!sflag)
+ errx(ERR_EXIT, "%s: Not a symbolic link", file2);
+ else
+ exit(ERR_EXIT);
+ } else if (fd2 == -1) {
+ if (!sflag)
+ errx(ERR_EXIT, "%s: Not a symbolic link", file1);
+ else
+ exit(ERR_EXIT);
+ }
+
+ /* FD rights are limited in c_special() and c_regular(). */
+ caph_cache_catpages();
+
+ if (!special) {
+ if (fstat(fd1, &sb1)) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file1);
+ else
+ exit(ERR_EXIT);
+ }
+ if (!S_ISREG(sb1.st_mode))
+ special = true;
+ else {
+ if (fstat(fd2, &sb2)) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file2);
+ else
+ exit(ERR_EXIT);
+ }
+ if (!S_ISREG(sb2.st_mode))
+ special = true;
+ }
+ }
+
+#ifdef SIGINFO
+ (void)signal(SIGINFO, siginfo);
+#endif
+ if (special) {
+ ret = c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
+ } else {
+ if (zflag && sb1.st_size != sb2.st_size) {
+ if (!sflag)
+ (void)printf("%s %s differ: size\n",
+ file1, file2);
+ ret = DIFF_EXIT;
+ } else {
+ ret = c_regular(fd1, file1, skip1, sb1.st_size,
+ fd2, file2, skip2, sb2.st_size, limit);
+ }
+ }
+end:
+ if (!sflag && fflush(stdout) != 0)
+ err(ERR_EXIT, "stdout");
+ exit(ret);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "usage: cmp [-l | -s | -x] [-hz] file1 file2 [skip1 [skip2]]\n");
+ exit(ERR_EXIT);
+}
diff --git a/usr.bin/cmp/extern.h b/usr.bin/cmp/extern.h
new file mode 100644
index 000000000000..4671b34653fa
--- /dev/null
+++ b/usr.bin/cmp/extern.h
@@ -0,0 +1,48 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define OK_EXIT 0
+#define DIFF_EXIT 1
+#define ERR_EXIT 2 /* error exit code */
+
+int c_link(const char *, off_t, const char *, off_t, off_t);
+int c_regular(int, const char *, off_t, off_t, int, const char *, off_t,
+ off_t, off_t);
+int c_special(int, const char *, off_t, int, const char *, off_t, off_t);
+void diffmsg(const char *, const char *, off_t, off_t, int, int);
+void eofmsg(const char *);
+
+extern bool bflag, lflag, sflag, xflag, zflag;
+
+#ifdef SIGINFO
+extern volatile sig_atomic_t info;
+#endif
diff --git a/usr.bin/cmp/link.c b/usr.bin/cmp/link.c
new file mode 100644
index 000000000000..dfa2f957d829
--- /dev/null
+++ b/usr.bin/cmp/link.c
@@ -0,0 +1,103 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2005 Brian Somers <brian@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int
+c_link(const char *file1, off_t skip1, const char *file2, off_t skip2,
+ off_t limit)
+{
+ char buf1[PATH_MAX], *p1;
+ char buf2[PATH_MAX], *p2;
+ ssize_t len1, len2;
+ int dfound;
+ off_t byte;
+ u_char ch;
+
+ if ((len1 = readlink(file1, buf1, sizeof(buf1) - 1)) < 0) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file1);
+ else
+ exit(ERR_EXIT);
+ }
+
+ if ((len2 = readlink(file2, buf2, sizeof(buf2) - 1)) < 0) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file2);
+ else
+ exit(ERR_EXIT);
+ }
+
+ if (skip1 > len1)
+ skip1 = len1;
+ buf1[len1] = '\0';
+
+ if (skip2 > len2)
+ skip2 = len2;
+ buf2[len2] = '\0';
+
+ dfound = 0;
+ byte = 1;
+ for (p1 = buf1 + skip1, p2 = buf2 + skip2;
+ *p1 && *p2 && (limit == 0 || byte <= limit); p1++, p2++) {
+ if ((ch = *p1) != *p2) {
+ if (xflag) {
+ dfound = 1;
+ (void)printf("%08llx %02x %02x\n",
+ (long long)byte - 1, ch, *p2);
+ } else if (lflag) {
+ dfound = 1;
+ if (bflag)
+ (void)printf("%6lld %3o %c %3o %c\n",
+ (long long)byte, ch, ch, *p2, *p2);
+ else
+ (void)printf("%6lld %3o %3o\n",
+ (long long)byte, ch, *p2);
+ } else {
+ diffmsg(file1, file2, byte, 1, ch, *p2);
+ return (DIFF_EXIT);
+ }
+ }
+ byte++;
+ }
+
+ if (*p1 || *p2) {
+ eofmsg (*p1 ? file2 : file1);
+ return (DIFF_EXIT);
+ }
+ return (dfound ? DIFF_EXIT : 0);
+}
diff --git a/usr.bin/cmp/misc.c b/usr.bin/cmp/misc.c
new file mode 100644
index 000000000000..4abefff31cf9
--- /dev/null
+++ b/usr.bin/cmp/misc.c
@@ -0,0 +1,62 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "extern.h"
+
+void
+eofmsg(const char *file)
+{
+ if (!sflag)
+ warnx("EOF on %s", file);
+}
+
+void
+diffmsg(const char *file1, const char *file2, off_t byte, off_t line,
+ int b1, int b2)
+{
+ if (sflag) {
+ /* nothing */
+ } else if (bflag) {
+ (void)printf("%s %s differ: char %lld, line %lld is %3o %c %3o %c\n",
+ file1, file2, (long long)byte, (long long)line, b1, b1,
+ b2, b2);
+ } else {
+ (void)printf("%s %s differ: char %lld, line %lld\n",
+ file1, file2, (long long)byte, (long long)line);
+ }
+}
diff --git a/usr.bin/cmp/regular.c b/usr.bin/cmp/regular.c
new file mode 100644
index 000000000000..c4407f708e8a
--- /dev/null
+++ b/usr.bin/cmp/regular.c
@@ -0,0 +1,191 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <capsicum_helpers.h>
+#include <err.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static u_char *remmap(u_char *, int, off_t);
+static void segv_handler(int);
+#define MMAP_CHUNK (8*1024*1024)
+
+#define ROUNDPAGE(i) ((i) & ~pagemask)
+
+int
+c_regular(int fd1, const char *file1, off_t skip1, off_t len1,
+ int fd2, const char *file2, off_t skip2, off_t len2, off_t limit)
+{
+ struct sigaction act, oact;
+ cap_rights_t rights;
+ u_char ch, *p1, *p2, *m1, *m2, *e1, *e2;
+ off_t byte, length, line;
+ off_t pagemask, off1, off2;
+ size_t pagesize;
+ int dfound;
+
+ if (skip1 > len1) {
+ eofmsg(file1);
+ return (DIFF_EXIT);
+ }
+ len1 -= skip1;
+ if (skip2 > len2) {
+ eofmsg(file2);
+ return (DIFF_EXIT);
+ }
+ len2 -= skip2;
+
+ if (sflag && len1 != len2)
+ return (DIFF_EXIT);
+
+ pagesize = getpagesize();
+ pagemask = (off_t)pagesize - 1;
+ off1 = ROUNDPAGE(skip1);
+ off2 = ROUNDPAGE(skip2);
+
+ length = MIN(len1, len2);
+ if (limit > 0)
+ length = MIN(length, limit);
+
+ if ((m1 = remmap(NULL, fd1, off1)) == NULL) {
+ return (c_special(fd1, file1, skip1, fd2, file2, skip2, limit));
+ }
+
+ if ((m2 = remmap(NULL, fd2, off2)) == NULL) {
+ munmap(m1, MMAP_CHUNK);
+ return (c_special(fd1, file1, skip1, fd2, file2, skip2, limit));
+ }
+
+ if (caph_rights_limit(fd1, cap_rights_init(&rights, CAP_MMAP_R)) < 0)
+ err(1, "unable to limit rights for %s", file1);
+ if (caph_rights_limit(fd2, cap_rights_init(&rights, CAP_MMAP_R)) < 0)
+ err(1, "unable to limit rights for %s", file2);
+ if (caph_enter() < 0)
+ err(ERR_EXIT, "unable to enter capability mode");
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_NODEFER;
+ act.sa_handler = segv_handler;
+ if (sigaction(SIGSEGV, &act, &oact))
+ err(ERR_EXIT, "sigaction()");
+
+ dfound = 0;
+ e1 = m1 + MMAP_CHUNK;
+ e2 = m2 + MMAP_CHUNK;
+ p1 = m1 + (skip1 - off1);
+ p2 = m2 + (skip2 - off2);
+
+ for (byte = line = 1; length--; ++byte) {
+#ifdef SIGINFO
+ if (info) {
+ (void)fprintf(stderr, "%s %s char %zu line %zu\n",
+ file1, file2, (size_t)byte, (size_t)line);
+ info = 0;
+ }
+#endif
+ if ((ch = *p1) != *p2) {
+ dfound = 1;
+ if (xflag) {
+ (void)printf("%08llx %02x %02x\n",
+ (long long)byte - 1, ch, *p2);
+ } else if (lflag) {
+ if (bflag)
+ (void)printf("%6lld %3o %c %3o %c\n",
+ (long long)byte, ch, ch, *p2, *p2);
+ else
+ (void)printf("%6lld %3o %3o\n",
+ (long long)byte, ch, *p2);
+ } else {
+ diffmsg(file1, file2, byte, line, ch, *p2);
+ return (DIFF_EXIT);
+ }
+ }
+ if (ch == '\n')
+ ++line;
+ if (++p1 == e1) {
+ off1 += MMAP_CHUNK;
+ if ((p1 = m1 = remmap(m1, fd1, off1)) == NULL) {
+ munmap(m2, MMAP_CHUNK);
+ err(ERR_EXIT, "remmap %s", file1);
+ }
+ e1 = m1 + MMAP_CHUNK;
+ }
+ if (++p2 == e2) {
+ off2 += MMAP_CHUNK;
+ if ((p2 = m2 = remmap(m2, fd2, off2)) == NULL) {
+ munmap(m1, MMAP_CHUNK);
+ err(ERR_EXIT, "remmap %s", file2);
+ }
+ e2 = m2 + MMAP_CHUNK;
+ }
+ }
+ munmap(m1, MMAP_CHUNK);
+ munmap(m2, MMAP_CHUNK);
+
+ if (sigaction(SIGSEGV, &oact, NULL))
+ err(ERR_EXIT, "sigaction()");
+
+ if (len1 != len2) {
+ eofmsg(len1 > len2 ? file2 : file1);
+ return (DIFF_EXIT);
+ }
+ return (dfound ? DIFF_EXIT : 0);
+}
+
+static u_char *
+remmap(u_char *mem, int fd, off_t offset)
+{
+ if (mem != NULL)
+ munmap(mem, MMAP_CHUNK);
+ mem = mmap(NULL, MMAP_CHUNK, PROT_READ, MAP_SHARED, fd, offset);
+ if (mem == MAP_FAILED)
+ return (NULL);
+ madvise(mem, MMAP_CHUNK, MADV_SEQUENTIAL);
+ return (mem);
+}
+
+static void
+segv_handler(int sig __unused) {
+ static const char msg[] = "cmp: Input/output error (caught SIGSEGV)\n";
+
+ write(STDERR_FILENO, msg, sizeof(msg));
+ _exit(EXIT_FAILURE);
+}
diff --git a/usr.bin/cmp/special.c b/usr.bin/cmp/special.c
new file mode 100644
index 000000000000..e25e82b17047
--- /dev/null
+++ b/usr.bin/cmp/special.c
@@ -0,0 +1,126 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <capsicum_helpers.h>
+#include <err.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "extern.h"
+
+int
+c_special(int fd1, const char *file1, off_t skip1,
+ int fd2, const char *file2, off_t skip2, off_t limit)
+{
+ int ch1, ch2;
+ off_t byte, line;
+ FILE *fp1, *fp2;
+ int dfound;
+
+ if (caph_limit_stream(fd1, CAPH_READ) < 0)
+ err(ERR_EXIT, "caph_limit_stream(%s)", file1);
+ if (caph_limit_stream(fd2, CAPH_READ) < 0)
+ err(ERR_EXIT, "caph_limit_stream(%s)", file2);
+ if (caph_enter() < 0)
+ err(ERR_EXIT, "unable to enter capability mode");
+
+ if ((fp1 = fdopen(fd1, "r")) == NULL)
+ err(ERR_EXIT, "%s", file1);
+ (void)setvbuf(fp1, NULL, _IOFBF, 65536);
+ if ((fp2 = fdopen(fd2, "r")) == NULL)
+ err(ERR_EXIT, "%s", file2);
+ (void)setvbuf(fp2, NULL, _IOFBF, 65536);
+
+ dfound = 0;
+ while (skip1--)
+ if (getc(fp1) == EOF)
+ goto eof;
+ while (skip2--)
+ if (getc(fp2) == EOF)
+ goto eof;
+
+ for (byte = line = 1; limit == 0 || byte <= limit; ++byte) {
+#ifdef SIGINFO
+ if (info) {
+ (void)fprintf(stderr, "%s %s char %zu line %zu\n",
+ file1, file2, (size_t)byte, (size_t)line);
+ info = 0;
+ }
+#endif
+ ch1 = getc(fp1);
+ ch2 = getc(fp2);
+ if (ch1 == EOF || ch2 == EOF)
+ break;
+ if (ch1 != ch2) {
+ if (xflag) {
+ dfound = 1;
+ (void)printf("%08llx %02x %02x\n",
+ (long long)byte - 1, ch1, ch2);
+ } else if (lflag) {
+ dfound = 1;
+ if (bflag)
+ (void)printf("%6lld %3o %c %3o %c\n",
+ (long long)byte, ch1, ch1, ch2,
+ ch2);
+ else
+ (void)printf("%6lld %3o %3o\n",
+ (long long)byte, ch1, ch2);
+ } else {
+ diffmsg(file1, file2, byte, line, ch1, ch2);
+ return (DIFF_EXIT);
+ }
+ }
+ if (ch1 == '\n')
+ ++line;
+ }
+
+eof: if (ferror(fp1))
+ err(ERR_EXIT, "%s", file1);
+ if (ferror(fp2))
+ err(ERR_EXIT, "%s", file2);
+ if (feof(fp1)) {
+ if (!feof(fp2)) {
+ eofmsg(file1);
+ return (DIFF_EXIT);
+ }
+ } else {
+ if (feof(fp2)) {
+ eofmsg(file2);
+ return (DIFF_EXIT);
+ }
+ }
+ fclose(fp2);
+ fclose(fp1);
+ return (dfound ? DIFF_EXIT : 0);
+}
diff --git a/usr.bin/cmp/tests/Makefile b/usr.bin/cmp/tests/Makefile
new file mode 100644
index 000000000000..8524fbaee43c
--- /dev/null
+++ b/usr.bin/cmp/tests/Makefile
@@ -0,0 +1,13 @@
+.include <bsd.own.mk>
+
+PACKAGE= tests
+
+ATF_TESTS_SH+= cmp_test2
+NETBSD_ATF_TESTS_SH= cmp_test
+
+${PACKAGE}FILES+= b_flag.out
+${PACKAGE}FILES+= bl_flag.out
+
+.include <netbsd-tests.test.mk>
+
+.include <bsd.test.mk>
diff --git a/usr.bin/cmp/tests/Makefile.depend b/usr.bin/cmp/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/usr.bin/cmp/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.bin/cmp/tests/b_flag.out b/usr.bin/cmp/tests/b_flag.out
new file mode 100644
index 000000000000..bb3d288716b0
--- /dev/null
+++ b/usr.bin/cmp/tests/b_flag.out
@@ -0,0 +1 @@
+a b differ: char 3, line 1 is 143 c 144 d
diff --git a/usr.bin/cmp/tests/bl_flag.out b/usr.bin/cmp/tests/bl_flag.out
new file mode 100644
index 000000000000..8ec9007d7ed3
--- /dev/null
+++ b/usr.bin/cmp/tests/bl_flag.out
@@ -0,0 +1 @@
+ 3 143 c 144 d
diff --git a/usr.bin/cmp/tests/cmp_test2.sh b/usr.bin/cmp/tests/cmp_test2.sh
new file mode 100755
index 000000000000..bca8fc18a6b8
--- /dev/null
+++ b/usr.bin/cmp/tests/cmp_test2.sh
@@ -0,0 +1,175 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Alan Somers
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case special
+special_head() {
+ atf_set "descr" "Test cmp(1)'s handling of non-regular files"
+}
+special_body() {
+ echo 0123456789abcdef > a
+ echo 0123456789abcdeg > b
+ atf_check -s exit:0 -o empty -e empty cmp a - <a
+ atf_check -s exit:0 -o empty -e empty cmp - a <a
+ atf_check -s exit:1 -o not-empty -e empty cmp a - <b
+ atf_check -s exit:1 -o not-empty -e empty cmp - a <b
+
+ atf_check -s exit:0 -o empty -e empty cmp a a <&-
+}
+
+atf_test_case symlink
+symlink_head() {
+ atf_set "descr" "Test cmp(1)'s handling of symlinks"
+}
+symlink_body() {
+ echo 0123456789abcdef > a
+ echo 0123456789abcdeg > b
+ ln -s a a.lnk
+ ln -s b b.lnk
+ ln -s a a2.lnk
+ cp a adup
+ ln -s adup adup.lnk
+ atf_check -s exit:0 cmp a a.lnk
+ atf_check -s exit:0 cmp a.lnk a
+ atf_check -s not-exit:0 -o ignore cmp a b.lnk
+ atf_check -s not-exit:0 -o ignore cmp b.lnk a
+ atf_check -s not-exit:0 -o ignore -e ignore cmp -h a a.lnk
+ atf_check -s not-exit:0 -o ignore -e ignore cmp -h a.lnk a
+ atf_check -s exit:0 cmp -h a.lnk a2.lnk
+ atf_check -s not-exit:0 -o ignore -e ignore cmp -h a.lnk adup.lnk
+}
+
+atf_test_case pr252542
+pr252542_head()
+{
+ atf_set "descr" "Test cmp(1) -s with file offset skips"
+}
+pr252542_body()
+{
+ echo -n '1234567890' > a
+ echo -n 'abc567890' > b
+ echo -n 'xbc567890' > c
+ atf_check -s exit:0 cmp -s a b 4 3
+ atf_check -s exit:0 cmp -i 4:3 -s a b
+ atf_check -s exit:0 cmp -i 1 -s b c
+ atf_check -s exit:1 -o ignore cmp -z a b 4 3
+ atf_check -s exit:1 -o ignore cmp -i 4:3 -z a b
+ atf_check -s exit:1 -o ignore cmp -i 1 -z a b
+}
+
+atf_test_case skipsuff
+skipsuff_head()
+{
+ atf_set "descr" "Test cmp(1) accepting SI suffixes on skips"
+}
+skipsuff_body()
+{
+
+ jot -nb a -s '' 1028 > a
+ jot -nb b -s '' 1024 > b
+ jot -nb a -s '' 4 >> b
+
+ atf_check -s exit:1 -o ignore cmp -s a b
+ 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 cmp -sn 4 b - <a
+ atf_check -s exit:0 -e empty cmp -sn 3 b - <a
+ atf_check -s exit:1 -o ignore cmp -sn 5 b - <a
+}
+
+atf_test_case bflag
+bflag_head()
+{
+ atf_set "descr" "Test cmp(1) -b (print bytes)"
+}
+bflag_body()
+{
+ echo -n "abcd" > a
+ echo -n "abdd" > b
+
+ atf_check -s exit:1 -o file:$(atf_get_srcdir)/b_flag.out \
+ cmp -b a b
+ atf_check -s exit:1 -o file:$(atf_get_srcdir)/bl_flag.out \
+ cmp -bl a b
+}
+
+# Helper for stdout test case
+atf_check_stdout()
+{
+ (
+ trap "" PIPE
+ sleep 1
+ cmp "$@" 2>stderr
+ echo $? >result
+ ) | true
+ atf_check -o inline:"2\n" cat result
+ atf_check -o match:"stdout" cat stderr
+}
+
+atf_test_case stdout
+stdout_head()
+{
+ atf_set descr "Failure to write to stdout"
+}
+stdout_body()
+{
+ echo a >a
+ echo b >b
+ atf_check_stdout a b
+ atf_check_stdout - b <a
+ atf_check_stdout a - <b
+ ln -s a alnk
+ ln -s b blnk
+ atf_check_stdout -h alnk blnk
+}
+
+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
+ atf_add_test_case bflag
+ atf_add_test_case stdout
+}