19b50d902SRodney W. Grimes /*
28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni *
49b50d902SRodney W. Grimes * Copyright (c) 1987, 1990, 1993, 1994
59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved.
69b50d902SRodney W. Grimes *
79b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without
89b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions
99b50d902SRodney W. Grimes * are met:
109b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
119b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer.
129b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
139b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
149b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
169b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software
179b50d902SRodney W. Grimes * without specific prior written permission.
189b50d902SRodney W. Grimes *
199b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299b50d902SRodney W. Grimes * SUCH DAMAGE.
309b50d902SRodney W. Grimes */
319b50d902SRodney W. Grimes
329b50d902SRodney W. Grimes #include <sys/types.h>
339b50d902SRodney W. Grimes #include <sys/stat.h>
349b50d902SRodney W. Grimes
35a4e3fc54SMariusz Zaborski #include <capsicum_helpers.h>
369b50d902SRodney W. Grimes #include <err.h>
37e1bfde1bSBrian Somers #include <errno.h>
389b50d902SRodney W. Grimes #include <fcntl.h>
398facfdcfSKyle Evans #include <getopt.h>
40e75a7302SConrad Meyer #include <nl_types.h>
41d350e8d7SDag-Erling Smørgrav #include <stdbool.h>
429b50d902SRodney W. Grimes #include <stdio.h>
439b50d902SRodney W. Grimes #include <stdlib.h>
449b50d902SRodney W. Grimes #include <string.h>
459b50d902SRodney W. Grimes #include <unistd.h>
469b50d902SRodney W. Grimes
47f6787614SKyle Evans #include <libutil.h>
48f6787614SKyle Evans
499b50d902SRodney W. Grimes #include "extern.h"
509b50d902SRodney W. Grimes
51f66b9b40SKyle Evans bool bflag, lflag, sflag, xflag, zflag;
529b50d902SRodney W. Grimes
538facfdcfSKyle Evans static const struct option long_opts[] =
548facfdcfSKyle Evans {
55f66b9b40SKyle Evans {"print-bytes", no_argument, NULL, 'b'},
568d546b68SKyle Evans {"ignore-initial", required_argument, NULL, 'i'},
578facfdcfSKyle Evans {"verbose", no_argument, NULL, 'l'},
584e380e84SKyle Evans {"bytes", required_argument, NULL, 'n'},
598facfdcfSKyle Evans {"silent", no_argument, NULL, 's'},
608facfdcfSKyle Evans {"quiet", no_argument, NULL, 's'},
618facfdcfSKyle Evans {NULL, no_argument, NULL, 0}
628facfdcfSKyle Evans };
638facfdcfSKyle Evans
646673a547SDag-Erling Smørgrav #ifdef SIGINFO
656673a547SDag-Erling Smørgrav volatile sig_atomic_t info;
666673a547SDag-Erling Smørgrav
676673a547SDag-Erling Smørgrav static void
siginfo(int signo)686673a547SDag-Erling Smørgrav siginfo(int signo)
696673a547SDag-Erling Smørgrav {
706673a547SDag-Erling Smørgrav info = signo;
716673a547SDag-Erling Smørgrav }
726673a547SDag-Erling Smørgrav #endif
736673a547SDag-Erling Smørgrav
74498a0a9cSAlfonso Gregory static void usage(void) __dead2;
759b50d902SRodney W. Grimes
768d546b68SKyle Evans static bool
parse_iskipspec(char * spec,off_t * skip1,off_t * skip2)778d546b68SKyle Evans parse_iskipspec(char *spec, off_t *skip1, off_t *skip2)
788d546b68SKyle Evans {
798d546b68SKyle Evans char *colon;
808d546b68SKyle Evans
818d546b68SKyle Evans colon = strchr(spec, ':');
828d546b68SKyle Evans if (colon != NULL)
838d546b68SKyle Evans *colon++ = '\0';
848d546b68SKyle Evans
858d546b68SKyle Evans if (expand_number(spec, skip1) < 0)
868d546b68SKyle Evans return (false);
878d546b68SKyle Evans
888d546b68SKyle Evans if (colon != NULL)
898d546b68SKyle Evans return (expand_number(colon, skip2) == 0);
908d546b68SKyle Evans
918d546b68SKyle Evans *skip2 = *skip1;
928d546b68SKyle Evans return (true);
938d546b68SKyle Evans }
948d546b68SKyle Evans
959b50d902SRodney W. Grimes int
main(int argc,char * argv[])96f2e8e0daSDavid Malone main(int argc, char *argv[])
979b50d902SRodney W. Grimes {
989b50d902SRodney W. Grimes struct stat sb1, sb2;
994e380e84SKyle Evans off_t skip1, skip2, limit;
1001f766174SEd Maste int ch, fd1, fd2, oflag;
1011f766174SEd Maste bool special;
102c5250ed1SDavid Malone const char *file1, *file2;
103*3c37828eSDag-Erling Smørgrav int ret;
1049b50d902SRodney W. Grimes
105*3c37828eSDag-Erling Smørgrav limit = skip1 = skip2 = ret = 0;
106e1bfde1bSBrian Somers oflag = O_RDONLY;
107f66b9b40SKyle Evans while ((ch = getopt_long(argc, argv, "+bhi:ln:sxz", long_opts, NULL)) != -1)
1089b50d902SRodney W. Grimes switch (ch) {
109f66b9b40SKyle Evans case 'b': /* Print bytes */
110f66b9b40SKyle Evans bflag = true;
111f66b9b40SKyle Evans break;
112e1bfde1bSBrian Somers case 'h': /* Don't follow symlinks */
113e1bfde1bSBrian Somers oflag |= O_NOFOLLOW;
114e1bfde1bSBrian Somers break;
1158d546b68SKyle Evans case 'i':
1168d546b68SKyle Evans if (!parse_iskipspec(optarg, &skip1, &skip2)) {
1178d546b68SKyle Evans fprintf(stderr,
1188d546b68SKyle Evans "Invalid --ignore-initial: %s\n",
1198d546b68SKyle Evans optarg);
1208d546b68SKyle Evans usage();
1218d546b68SKyle Evans }
1228d546b68SKyle Evans break;
1239b50d902SRodney W. Grimes case 'l': /* print all differences */
1241f766174SEd Maste lflag = true;
1259b50d902SRodney W. Grimes break;
1264e380e84SKyle Evans case 'n': /* Limit */
1274e380e84SKyle Evans if (expand_number(optarg, &limit) < 0 || limit < 0) {
1284e380e84SKyle Evans fprintf(stderr, "Invalid --bytes: %s\n",
1294e380e84SKyle Evans optarg);
1304e380e84SKyle Evans usage();
1314e380e84SKyle Evans }
1324e380e84SKyle Evans break;
1339b50d902SRodney W. Grimes case 's': /* silent run */
1341f766174SEd Maste sflag = true;
1359b50d902SRodney W. Grimes break;
136e03983a3SPoul-Henning Kamp case 'x': /* hex output */
1371f766174SEd Maste lflag = true;
1381f766174SEd Maste xflag = true;
139e03983a3SPoul-Henning Kamp break;
1403e6902efSBrian Somers case 'z': /* compare size first */
1411f766174SEd Maste zflag = true;
1423e6902efSBrian Somers break;
1439b50d902SRodney W. Grimes case '?':
1449b50d902SRodney W. Grimes default:
1459b50d902SRodney W. Grimes usage();
1469b50d902SRodney W. Grimes }
1479b50d902SRodney W. Grimes argv += optind;
1489b50d902SRodney W. Grimes argc -= optind;
1499b50d902SRodney W. Grimes
1509b50d902SRodney W. Grimes if (lflag && sflag)
1513e6902efSBrian Somers errx(ERR_EXIT, "specifying -s with -l or -x is not permitted");
1529b50d902SRodney W. Grimes
1539b50d902SRodney W. Grimes if (argc < 2 || argc > 4)
1549b50d902SRodney W. Grimes usage();
1559b50d902SRodney W. Grimes
1562528b7e2SMark Johnston /* Don't limit rights on stdin since it may be one of the inputs. */
1572528b7e2SMark Johnston if (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
1582528b7e2SMark Johnston err(ERR_EXIT, "unable to limit rights on stdout");
1592528b7e2SMark Johnston if (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
1602528b7e2SMark Johnston err(ERR_EXIT, "unable to limit rights on stderr");
1612528b7e2SMark Johnston
1629b50d902SRodney W. Grimes /* Backward compatibility -- handle "-" meaning stdin. */
1631f766174SEd Maste special = false;
1649b50d902SRodney W. Grimes if (strcmp(file1 = argv[0], "-") == 0) {
1651f766174SEd Maste special = true;
1662528b7e2SMark Johnston fd1 = STDIN_FILENO;
1679b50d902SRodney W. Grimes file1 = "stdin";
1682528b7e2SMark Johnston } else if ((fd1 = open(file1, oflag, 0)) < 0 && errno != EMLINK) {
169e5266259SJordan K. Hubbard if (!sflag)
1709b50d902SRodney W. Grimes err(ERR_EXIT, "%s", file1);
171e5266259SJordan K. Hubbard else
172c8718058STim J. Robbins exit(ERR_EXIT);
173e5266259SJordan K. Hubbard }
1749b50d902SRodney W. Grimes if (strcmp(file2 = argv[1], "-") == 0) {
1759b50d902SRodney W. Grimes if (special)
1769b50d902SRodney W. Grimes errx(ERR_EXIT,
1779b50d902SRodney W. Grimes "standard input may only be specified once");
1781f766174SEd Maste special = true;
1792528b7e2SMark Johnston fd2 = STDIN_FILENO;
1809b50d902SRodney W. Grimes file2 = "stdin";
1812528b7e2SMark Johnston } else if ((fd2 = open(file2, oflag, 0)) < 0 && errno != EMLINK) {
182e5266259SJordan K. Hubbard if (!sflag)
1839b50d902SRodney W. Grimes err(ERR_EXIT, "%s", file2);
184e5266259SJordan K. Hubbard else
185c8718058STim J. Robbins exit(ERR_EXIT);
186e5266259SJordan K. Hubbard }
1879b50d902SRodney W. Grimes
188f6787614SKyle Evans if (argc > 2 && expand_number(argv[2], &skip1) < 0) {
189f6787614SKyle Evans fprintf(stderr, "Invalid skip1: %s\n", argv[2]);
190f6787614SKyle Evans usage();
191f6787614SKyle Evans }
192f6787614SKyle Evans
193f6787614SKyle Evans if (argc == 4 && expand_number(argv[3], &skip2) < 0) {
194f6787614SKyle Evans fprintf(stderr, "Invalid skip2: %s\n", argv[3]);
195f6787614SKyle Evans usage();
196f6787614SKyle Evans }
1979b50d902SRodney W. Grimes
19880445b7aSEd Maste if (sflag && skip1 == 0 && skip2 == 0)
19980445b7aSEd Maste zflag = true;
20080445b7aSEd Maste
201e1bfde1bSBrian Somers if (fd1 == -1) {
202e1bfde1bSBrian Somers if (fd2 == -1) {
203*3c37828eSDag-Erling Smørgrav ret = c_link(file1, skip1, file2, skip2, limit);
204*3c37828eSDag-Erling Smørgrav goto end;
205e1bfde1bSBrian Somers } else if (!sflag)
206e1bfde1bSBrian Somers errx(ERR_EXIT, "%s: Not a symbolic link", file2);
207e1bfde1bSBrian Somers else
208e1bfde1bSBrian Somers exit(ERR_EXIT);
209e1bfde1bSBrian Somers } else if (fd2 == -1) {
210e1bfde1bSBrian Somers if (!sflag)
211e1bfde1bSBrian Somers errx(ERR_EXIT, "%s: Not a symbolic link", file1);
212e1bfde1bSBrian Somers else
213e1bfde1bSBrian Somers exit(ERR_EXIT);
214e1bfde1bSBrian Somers }
215e1bfde1bSBrian Somers
2162528b7e2SMark Johnston /* FD rights are limited in c_special() and c_regular(). */
217a4e3fc54SMariusz Zaborski caph_cache_catpages();
218e75a7302SConrad Meyer
2199b50d902SRodney W. Grimes if (!special) {
220e5266259SJordan K. Hubbard if (fstat(fd1, &sb1)) {
221e5266259SJordan K. Hubbard if (!sflag)
2229b50d902SRodney W. Grimes err(ERR_EXIT, "%s", file1);
223e5266259SJordan K. Hubbard else
224c8718058STim J. Robbins exit(ERR_EXIT);
225e5266259SJordan K. Hubbard }
2269b50d902SRodney W. Grimes if (!S_ISREG(sb1.st_mode))
2271f766174SEd Maste special = true;
2289b50d902SRodney W. Grimes else {
229e5266259SJordan K. Hubbard if (fstat(fd2, &sb2)) {
230e5266259SJordan K. Hubbard if (!sflag)
2319b50d902SRodney W. Grimes err(ERR_EXIT, "%s", file2);
232e5266259SJordan K. Hubbard else
233c8718058STim J. Robbins exit(ERR_EXIT);
234e5266259SJordan K. Hubbard }
2359b50d902SRodney W. Grimes if (!S_ISREG(sb2.st_mode))
2361f766174SEd Maste special = true;
2379b50d902SRodney W. Grimes }
2389b50d902SRodney W. Grimes }
2399b50d902SRodney W. Grimes
2406673a547SDag-Erling Smørgrav #ifdef SIGINFO
2416673a547SDag-Erling Smørgrav (void)signal(SIGINFO, siginfo);
2426673a547SDag-Erling Smørgrav #endif
243*3c37828eSDag-Erling Smørgrav if (special) {
244*3c37828eSDag-Erling Smørgrav ret = c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
245*3c37828eSDag-Erling Smørgrav } else {
2463e6902efSBrian Somers if (zflag && sb1.st_size != sb2.st_size) {
2473e6902efSBrian Somers if (!sflag)
2483e6902efSBrian Somers (void)printf("%s %s differ: size\n",
2493e6902efSBrian Somers file1, file2);
250*3c37828eSDag-Erling Smørgrav ret = DIFF_EXIT;
251*3c37828eSDag-Erling Smørgrav } else {
252*3c37828eSDag-Erling Smørgrav ret = c_regular(fd1, file1, skip1, sb1.st_size,
2534e380e84SKyle Evans fd2, file2, skip2, sb2.st_size, limit);
2541727cb4cSSheldon Hearn }
255*3c37828eSDag-Erling Smørgrav }
256*3c37828eSDag-Erling Smørgrav end:
257*3c37828eSDag-Erling Smørgrav if (!sflag && fflush(stdout) != 0)
258*3c37828eSDag-Erling Smørgrav err(ERR_EXIT, "stdout");
259*3c37828eSDag-Erling Smørgrav exit(ret);
2609b50d902SRodney W. Grimes }
2619b50d902SRodney W. Grimes
2629b50d902SRodney W. Grimes static void
usage(void)263f2e8e0daSDavid Malone usage(void)
2649b50d902SRodney W. Grimes {
2659b50d902SRodney W. Grimes
2669b50d902SRodney W. Grimes (void)fprintf(stderr,
267e1bfde1bSBrian Somers "usage: cmp [-l | -s | -x] [-hz] file1 file2 [skip1 [skip2]]\n");
2689b50d902SRodney W. Grimes exit(ERR_EXIT);
2699b50d902SRodney W. Grimes }
270