]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/cmp/cmp.c
cmp: add -n, --bytes to limit number of bytes to compare
[FreeBSD/FreeBSD.git] / usr.bin / cmp / cmp.c
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1987, 1990, 1993, 1994
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #ifndef lint
33 static const char copyright[] =
34 "@(#) Copyright (c) 1987, 1990, 1993, 1994\n\
35         The Regents of the University of California.  All rights reserved.\n";
36 #endif
37
38 #if 0
39 #ifndef lint
40 static char sccsid[] = "@(#)cmp.c       8.3 (Berkeley) 4/2/94";
41 #endif
42 #endif
43
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46
47 #include <sys/types.h>
48 #include <sys/stat.h>
49
50 #include <capsicum_helpers.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <getopt.h>
55 #include <nl_types.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include <libutil.h>
62
63 #include "extern.h"
64
65 bool    lflag, sflag, xflag, zflag;
66
67 static const struct option long_opts[] =
68 {
69         {"verbose",     no_argument,            NULL, 'l'},
70         {"bytes",       required_argument,      NULL, 'n'},
71         {"silent",      no_argument,            NULL, 's'},
72         {"quiet",       no_argument,            NULL, 's'},
73         {NULL,          no_argument,            NULL, 0}
74 };
75
76 static void usage(void);
77
78 int
79 main(int argc, char *argv[])
80 {
81         struct stat sb1, sb2;
82         off_t skip1, skip2, limit;
83         int ch, fd1, fd2, oflag;
84         bool special;
85         const char *file1, *file2;
86
87         skip1 = skip2 = 0;
88         oflag = O_RDONLY;
89         while ((ch = getopt_long(argc, argv, "+hln:sxz", long_opts, NULL)) != -1)
90                 switch (ch) {
91                 case 'h':               /* Don't follow symlinks */
92                         oflag |= O_NOFOLLOW;
93                         break;
94                 case 'l':               /* print all differences */
95                         lflag = true;
96                         break;
97                 case 'n':               /* Limit */
98                         if (expand_number(optarg, &limit) < 0 || limit < 0) {
99                                 fprintf(stderr, "Invalid --bytes: %s\n",
100                                     optarg);
101                                 usage();
102                         }
103                         break;
104                 case 's':               /* silent run */
105                         sflag = true;
106                         break;
107                 case 'x':               /* hex output */
108                         lflag = true;
109                         xflag = true;
110                         break;
111                 case 'z':               /* compare size first */
112                         zflag = true;
113                         break;
114                 case '?':
115                 default:
116                         usage();
117                 }
118         argv += optind;
119         argc -= optind;
120
121         if (lflag && sflag)
122                 errx(ERR_EXIT, "specifying -s with -l or -x is not permitted");
123
124         if (argc < 2 || argc > 4)
125                 usage();
126
127         /* Don't limit rights on stdin since it may be one of the inputs. */
128         if (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
129                 err(ERR_EXIT, "unable to limit rights on stdout");
130         if (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
131                 err(ERR_EXIT, "unable to limit rights on stderr");
132
133         /* Backward compatibility -- handle "-" meaning stdin. */
134         special = false;
135         if (strcmp(file1 = argv[0], "-") == 0) {
136                 special = true;
137                 fd1 = STDIN_FILENO;
138                 file1 = "stdin";
139         } else if ((fd1 = open(file1, oflag, 0)) < 0 && errno != EMLINK) {
140                 if (!sflag)
141                         err(ERR_EXIT, "%s", file1);
142                 else
143                         exit(ERR_EXIT);
144         }
145         if (strcmp(file2 = argv[1], "-") == 0) {
146                 if (special)
147                         errx(ERR_EXIT,
148                                 "standard input may only be specified once");
149                 special = true;
150                 fd2 = STDIN_FILENO;
151                 file2 = "stdin";
152         } else if ((fd2 = open(file2, oflag, 0)) < 0 && errno != EMLINK) {
153                 if (!sflag)
154                         err(ERR_EXIT, "%s", file2);
155                 else
156                         exit(ERR_EXIT);
157         }
158
159         if (argc > 2 && expand_number(argv[2], &skip1) < 0) {
160                 fprintf(stderr, "Invalid skip1: %s\n", argv[2]);
161                 usage();
162         }
163
164         if (argc == 4 && expand_number(argv[3], &skip2) < 0) {
165                 fprintf(stderr, "Invalid skip2: %s\n", argv[3]);
166                 usage();
167         }
168
169         if (sflag && skip1 == 0 && skip2 == 0)
170                 zflag = true;
171
172         if (fd1 == -1) {
173                 if (fd2 == -1) {
174                         c_link(file1, skip1, file2, skip2, limit);
175                         exit(0);
176                 } else if (!sflag)
177                         errx(ERR_EXIT, "%s: Not a symbolic link", file2);
178                 else
179                         exit(ERR_EXIT);
180         } else if (fd2 == -1) {
181                 if (!sflag)
182                         errx(ERR_EXIT, "%s: Not a symbolic link", file1);
183                 else
184                         exit(ERR_EXIT);
185         }
186
187         /* FD rights are limited in c_special() and c_regular(). */
188         caph_cache_catpages();
189
190         if (!special) {
191                 if (fstat(fd1, &sb1)) {
192                         if (!sflag)
193                                 err(ERR_EXIT, "%s", file1);
194                         else
195                                 exit(ERR_EXIT);
196                 }
197                 if (!S_ISREG(sb1.st_mode))
198                         special = true;
199                 else {
200                         if (fstat(fd2, &sb2)) {
201                                 if (!sflag)
202                                         err(ERR_EXIT, "%s", file2);
203                                 else
204                                         exit(ERR_EXIT);
205                         }
206                         if (!S_ISREG(sb2.st_mode))
207                                 special = true;
208                 }
209         }
210
211         if (special)
212                 c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
213         else {
214                 if (zflag && sb1.st_size != sb2.st_size) {
215                         if (!sflag)
216                                 (void) printf("%s %s differ: size\n",
217                                     file1, file2);
218                         exit(DIFF_EXIT);
219                 }
220                 c_regular(fd1, file1, skip1, sb1.st_size,
221                     fd2, file2, skip2, sb2.st_size, limit);
222         }
223         exit(0);
224 }
225
226 static void
227 usage(void)
228 {
229
230         (void)fprintf(stderr,
231             "usage: cmp [-l | -s | -x] [-hz] file1 file2 [skip1 [skip2]]\n");
232         exit(ERR_EXIT);
233 }