xref: /freebsd/usr.bin/cmp/cmp.c (revision 3c37828ee1874754e1c5e96268016113c1e02ba2)
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