]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/cmp/cmp.c
uniq: Clean up and test obsolete options.
[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 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #include <capsicum_helpers.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <nl_types.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #include <libutil.h>
47
48 #include "extern.h"
49
50 bool    bflag, lflag, sflag, xflag, zflag;
51
52 static const struct option long_opts[] =
53 {
54         {"print-bytes", no_argument,            NULL, 'b'},
55         {"ignore-initial", required_argument,   NULL, 'i'},
56         {"verbose",     no_argument,            NULL, 'l'},
57         {"bytes",       required_argument,      NULL, 'n'},
58         {"silent",      no_argument,            NULL, 's'},
59         {"quiet",       no_argument,            NULL, 's'},
60         {NULL,          no_argument,            NULL, 0}
61 };
62
63 #ifdef SIGINFO
64 volatile sig_atomic_t info;
65
66 static void
67 siginfo(int signo)
68 {
69
70         info = signo;
71 }
72 #endif
73
74 static void usage(void) __dead2;
75
76 static bool
77 parse_iskipspec(char *spec, off_t *skip1, off_t *skip2)
78 {
79         char *colon;
80
81         colon = strchr(spec, ':');
82         if (colon != NULL)
83                 *colon++ = '\0';
84
85         if (expand_number(spec, skip1) < 0)
86                 return (false);
87
88         if (colon != NULL)
89                 return (expand_number(colon, skip2) == 0);
90
91         *skip2 = *skip1;
92         return (true);
93 }
94
95 int
96 main(int argc, char *argv[])
97 {
98         struct stat sb1, sb2;
99         off_t skip1, skip2, limit;
100         int ch, fd1, fd2, oflag;
101         bool special;
102         const char *file1, *file2;
103
104         limit = skip1 = skip2 = 0;
105         oflag = O_RDONLY;
106         while ((ch = getopt_long(argc, argv, "+bhi:ln:sxz", long_opts, NULL)) != -1)
107                 switch (ch) {
108                 case 'b':               /* Print bytes */
109                         bflag = true;
110                         break;
111                 case 'h':               /* Don't follow symlinks */
112                         oflag |= O_NOFOLLOW;
113                         break;
114                 case 'i':
115                         if (!parse_iskipspec(optarg, &skip1, &skip2)) {
116                                 fprintf(stderr,
117                                     "Invalid --ignore-initial: %s\n",
118                                     optarg);
119                                 usage();
120                         }
121                         break;
122                 case 'l':               /* print all differences */
123                         lflag = true;
124                         break;
125                 case 'n':               /* Limit */
126                         if (expand_number(optarg, &limit) < 0 || limit < 0) {
127                                 fprintf(stderr, "Invalid --bytes: %s\n",
128                                     optarg);
129                                 usage();
130                         }
131                         break;
132                 case 's':               /* silent run */
133                         sflag = true;
134                         break;
135                 case 'x':               /* hex output */
136                         lflag = true;
137                         xflag = true;
138                         break;
139                 case 'z':               /* compare size first */
140                         zflag = true;
141                         break;
142                 case '?':
143                 default:
144                         usage();
145                 }
146         argv += optind;
147         argc -= optind;
148
149         if (lflag && sflag)
150                 errx(ERR_EXIT, "specifying -s with -l or -x is not permitted");
151
152         if (argc < 2 || argc > 4)
153                 usage();
154
155         /* Don't limit rights on stdin since it may be one of the inputs. */
156         if (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
157                 err(ERR_EXIT, "unable to limit rights on stdout");
158         if (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
159                 err(ERR_EXIT, "unable to limit rights on stderr");
160
161         /* Backward compatibility -- handle "-" meaning stdin. */
162         special = false;
163         if (strcmp(file1 = argv[0], "-") == 0) {
164                 special = true;
165                 fd1 = STDIN_FILENO;
166                 file1 = "stdin";
167         } else if ((fd1 = open(file1, oflag, 0)) < 0 && errno != EMLINK) {
168                 if (!sflag)
169                         err(ERR_EXIT, "%s", file1);
170                 else
171                         exit(ERR_EXIT);
172         }
173         if (strcmp(file2 = argv[1], "-") == 0) {
174                 if (special)
175                         errx(ERR_EXIT,
176                                 "standard input may only be specified once");
177                 special = true;
178                 fd2 = STDIN_FILENO;
179                 file2 = "stdin";
180         } else if ((fd2 = open(file2, oflag, 0)) < 0 && errno != EMLINK) {
181                 if (!sflag)
182                         err(ERR_EXIT, "%s", file2);
183                 else
184                         exit(ERR_EXIT);
185         }
186
187         if (argc > 2 && expand_number(argv[2], &skip1) < 0) {
188                 fprintf(stderr, "Invalid skip1: %s\n", argv[2]);
189                 usage();
190         }
191
192         if (argc == 4 && expand_number(argv[3], &skip2) < 0) {
193                 fprintf(stderr, "Invalid skip2: %s\n", argv[3]);
194                 usage();
195         }
196
197         if (sflag && skip1 == 0 && skip2 == 0)
198                 zflag = true;
199
200         if (fd1 == -1) {
201                 if (fd2 == -1) {
202                         c_link(file1, skip1, file2, skip2, limit);
203                         exit(0);
204                 } else if (!sflag)
205                         errx(ERR_EXIT, "%s: Not a symbolic link", file2);
206                 else
207                         exit(ERR_EXIT);
208         } else if (fd2 == -1) {
209                 if (!sflag)
210                         errx(ERR_EXIT, "%s: Not a symbolic link", file1);
211                 else
212                         exit(ERR_EXIT);
213         }
214
215         /* FD rights are limited in c_special() and c_regular(). */
216         caph_cache_catpages();
217
218         if (!special) {
219                 if (fstat(fd1, &sb1)) {
220                         if (!sflag)
221                                 err(ERR_EXIT, "%s", file1);
222                         else
223                                 exit(ERR_EXIT);
224                 }
225                 if (!S_ISREG(sb1.st_mode))
226                         special = true;
227                 else {
228                         if (fstat(fd2, &sb2)) {
229                                 if (!sflag)
230                                         err(ERR_EXIT, "%s", file2);
231                                 else
232                                         exit(ERR_EXIT);
233                         }
234                         if (!S_ISREG(sb2.st_mode))
235                                 special = true;
236                 }
237         }
238
239 #ifdef SIGINFO
240         (void)signal(SIGINFO, siginfo);
241 #endif
242         if (special)
243                 c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
244         else {
245                 if (zflag && sb1.st_size != sb2.st_size) {
246                         if (!sflag)
247                                 (void) printf("%s %s differ: size\n",
248                                     file1, file2);
249                         exit(DIFF_EXIT);
250                 }
251                 c_regular(fd1, file1, skip1, sb1.st_size,
252                     fd2, file2, skip2, sb2.st_size, limit);
253         }
254         exit(0);
255 }
256
257 static void
258 usage(void)
259 {
260
261         (void)fprintf(stderr,
262             "usage: cmp [-l | -s | -x] [-hz] file1 file2 [skip1 [skip2]]\n");
263         exit(ERR_EXIT);
264 }