diff options
Diffstat (limited to 'usr.bin/cmp')
| -rw-r--r-- | usr.bin/cmp/Makefile | 11 | ||||
| -rw-r--r-- | usr.bin/cmp/Makefile.depend | 18 | ||||
| -rw-r--r-- | usr.bin/cmp/cmp.1 | 210 | ||||
| -rw-r--r-- | usr.bin/cmp/cmp.c | 269 | ||||
| -rw-r--r-- | usr.bin/cmp/extern.h | 48 | ||||
| -rw-r--r-- | usr.bin/cmp/link.c | 103 | ||||
| -rw-r--r-- | usr.bin/cmp/misc.c | 62 | ||||
| -rw-r--r-- | usr.bin/cmp/regular.c | 191 | ||||
| -rw-r--r-- | usr.bin/cmp/special.c | 126 | ||||
| -rw-r--r-- | usr.bin/cmp/tests/Makefile | 13 | ||||
| -rw-r--r-- | usr.bin/cmp/tests/Makefile.depend | 10 | ||||
| -rw-r--r-- | usr.bin/cmp/tests/b_flag.out | 1 | ||||
| -rw-r--r-- | usr.bin/cmp/tests/bl_flag.out | 1 | ||||
| -rwxr-xr-x | usr.bin/cmp/tests/cmp_test2.sh | 175 |
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 +} |
