xref: /openbsd-src/usr.bin/diff/diff.c (revision 4ec4b3d54a5517b053ea56c6a0ae89ce93183500)
1 /*	$OpenBSD: diff.c,v 1.23 2003/07/06 20:48:59 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * Sponsored in part by the Defense Advanced Research Projects
19  * Agency (DARPA) and Air Force Research Laboratory, Air Force
20  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21  */
22 
23 #ifndef lint
24 static const char rcsid[] = "$OpenBSD: diff.c,v 1.23 2003/07/06 20:48:59 millert Exp $";
25 #endif /* not lint */
26 
27 #include <sys/param.h>
28 #include <sys/stat.h>
29 
30 #include <err.h>
31 #include <errno.h>
32 #include <getopt.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "diff.h"
40 
41 int	 aflag, bflag, iflag, Nflag, rflag, sflag, tflag, wflag;
42 int	 format, context, status;
43 char	*start, *ifdefname, *diffargs;
44 struct stat stb1, stb2;
45 struct excludes *excludes_list;
46 
47 #define	OPTIONS	"abC:cD:efhinNrS:stU:uwX:x:"
48 static struct option longopts[] = {
49 	{ "text",			no_argument,		0,	'a' },
50 	{ "ignore-space-change",	no_argument,		0,	'b' },
51 	{ "context",			optional_argument,	0,	'C' },
52 	{ "ifdef",			required_argument,	0,	'D' },
53 	{ "ed",				no_argument,		0,	'e' },
54 	{ "forward-ed",			no_argument,		0,	'f' },
55 	{ "ignore-case",		no_argument,		0,	'i' },
56 	{ "new-file",			no_argument,		0,	'N' },
57 	{ "rcs",			no_argument,		0,	'n' },
58 	{ "recursive",			no_argument,		0,	'r' },
59 	{ "report-identical-files",	no_argument,		0,	's' },
60 	{ "starting-file",		required_argument,	0,	'S' },
61 	{ "expand-tabs",		no_argument,		0,	't' },
62 	{ "unified",			optional_argument,	0,	'U' },
63 	{ "ignore-all-space",		no_argument,		0,	'w' },
64 	{ "exclude",			required_argument,	0,	'x' },
65 	{ "exclude-from",		required_argument,	0,	'X' },
66 };
67 
68 __dead void usage(void);
69 void push_excludes(char *);
70 void read_excludes_file(char *file);
71 void set_argstr(char **, char **);
72 
73 int
74 main(int argc, char **argv)
75 {
76 	char *ep, **oargv;
77 	long  l;
78 	int   ch, gotstdin;
79 
80 	oargv = argv;
81 	gotstdin = 0;
82 
83 	while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
84 		switch (ch) {
85 		case 'a':
86 			aflag = 1;
87 			break;
88 		case 'b':
89 			bflag = 1;
90 			break;
91 		case 'C':
92 		case 'c':
93 			format = D_CONTEXT;
94 			if (optarg != NULL) {
95 				l = strtol(optarg, &ep, 10);
96 				if (*ep != '\0' || l < 0 || l >= INT_MAX)
97 					usage();
98 				context = (int)l;
99 			} else
100 				context = 3;
101 			break;
102 		case 'D':
103 			format = D_IFDEF;
104 			ifdefname = optarg;
105 			break;
106 		case 'e':
107 			format = D_EDIT;
108 			break;
109 		case 'f':
110 			format = D_REVERSE;
111 			break;
112 		case 'h':
113 			/* silently ignore for backwards compatibility */
114 			break;
115 		case 'i':
116 			iflag = 1;
117 			break;
118 		case 'N':
119 			Nflag = 1;
120 			break;
121 		case 'n':
122 			format = D_NREVERSE;
123 			break;
124 		case 'r':
125 			rflag = 1;
126 			break;
127 		case 'S':
128 			start = optarg;
129 			break;
130 		case 's':
131 			sflag = 1;
132 			break;
133 		case 't':
134 			tflag = 1;
135 			break;
136 		case 'U':
137 		case 'u':
138 			format = D_UNIFIED;
139 			if (optarg != NULL) {
140 				l = strtol(optarg, &ep, 10);
141 				if (*ep != '\0' || l < 0 || l >= INT_MAX)
142 					usage();
143 				context = (int)l;
144 			} else
145 				context = 3;
146 			break;
147 		case 'w':
148 			wflag = 1;
149 			break;
150 		case 'X':
151 			read_excludes_file(optarg);
152 			break;
153 		case 'x':
154 			push_excludes(optarg);
155 			break;
156 		default:
157 			usage();
158 			break;
159 		}
160 	}
161 	argc -= optind;
162 	argv += optind;
163 
164 	/*
165 	 * Do sanity checks, fill in stb1 and stb2 and call the appropriate
166 	 * driver routine.  Both drivers use the contents of stb1 and stb2.
167 	 */
168 	if (argc != 2)
169 		usage();
170 	if (strcmp(argv[0], "-") == 0) {
171 		stb1.st_mode = S_IFREG;
172 		gotstdin = 1;
173 	} else if (stat(argv[0], &stb1) != 0)
174 		error("%s", argv[0]);
175 	if (strcmp(argv[1], "-") == 0) {
176 		stb2.st_mode = S_IFREG;
177 		gotstdin = 1;
178 	} else if (stat(argv[1], &stb2) != 0)
179 		error("%s", argv[1]);
180 	if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
181 		errorx("can't compare - to a directory");
182 	if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
183 		if (format == D_IFDEF)
184 			errorx("-D option not supported with directories");
185 		set_argstr(oargv, argv);
186 		diffdir(argv[0], argv[1]);
187 	} else
188 		diffreg(argv[0], argv[1], 0);
189 	exit(status);
190 }
191 
192 void
193 quit(int signo)
194 {
195 	if (tempfiles[0] != NULL)
196 		unlink(tempfiles[0]);
197 	if (tempfiles[1] != NULL)
198 		unlink(tempfiles[1]);
199 	_exit(status);
200 }
201 
202 void *
203 emalloc(size_t n)
204 {
205 	void *p;
206 
207 	if ((p = malloc(n)) == NULL)
208 		error(NULL);
209 	return (p);
210 }
211 
212 void *
213 erealloc(void *p, size_t n)
214 {
215 	void *q;
216 
217 	if ((q = realloc(p, n)) == NULL)
218 		error(NULL);
219 	return (q);
220 }
221 
222 __dead void
223 error(const char *fmt, ...)
224 {
225 	va_list ap;
226 	int sverrno = errno;
227 
228 	if (tempfiles[0] != NULL)
229 		unlink(tempfiles[0]);
230 	if (tempfiles[1] != NULL)
231 		unlink(tempfiles[1]);
232 	errno = sverrno;
233 	va_start(ap, fmt);
234 	verr(2, fmt, ap);
235 	va_end(ap);
236 }
237 
238 __dead void
239 errorx(const char *fmt, ...)
240 {
241 	va_list ap;
242 
243 	if (tempfiles[0] != NULL)
244 		unlink(tempfiles[0]);
245 	if (tempfiles[1] != NULL)
246 		unlink(tempfiles[1]);
247 	va_start(ap, fmt);
248 	verrx(2, fmt, ap);
249 	va_end(ap);
250 }
251 
252 void
253 set_argstr(char **av, char **ave)
254 {
255 	size_t argsize;
256 	char **ap;
257 
258 	argsize = 4 + (char *)ave - (char *)av + 1;
259 	diffargs = emalloc(argsize);
260 	strlcpy(diffargs, "diff", argsize);
261 	for (ap = av + 1; ap < ave; ap++) {
262 		if (strcmp(*ap, "--") != 0) {
263 			strlcat(diffargs, " ", argsize);
264 			strlcat(diffargs, *ap, argsize);
265 		}
266 	}
267 }
268 
269 /*
270  * Read in an excludes file and push each line.
271  */
272 void
273 read_excludes_file(char *file)
274 {
275 	FILE *fp;
276 	char *buf, *pattern;
277 	size_t len;
278 
279 	if (strcmp(file, "-") == 0)
280 		fp = stdin;
281 	else if ((fp = fopen(file, "r")) == NULL)
282 		error("%s", file);
283 	while ((buf = fgetln(fp, &len)) != NULL) {
284 		if (buf[len - 1] == '\n')
285 			len--;
286 		pattern = emalloc(len + 1);
287 		memcpy(pattern, buf, len);
288 		pattern[len] = '\0';
289 		push_excludes(pattern);
290 	}
291 	if (strcmp(file, "-") != 0)
292 		fclose(fp);
293 }
294 
295 /*
296  * Push a pattern onto the excludes list.
297  */
298 void
299 push_excludes(char *pattern)
300 {
301 	struct excludes *entry;
302 
303 	entry = emalloc(sizeof(*entry));
304 	entry->pattern = pattern;
305 	entry->next = excludes_list;
306 	excludes_list = entry;
307 }
308 
309 __dead void
310 usage(void)
311 {
312 	(void)fprintf(stderr,
313 	    "usage: diff [-bitw] [-c | -e | -f | -n | -u ] file1 file2\n"
314 	    "       diff [-bitw] -C number file1 file2\n"
315 	    "       diff [-bitw] -D string file1 file2\n"
316 	    "       diff [-bitw] -U number file1 file2\n"
317 	    "       diff [-biNwt] [-c | -e | -f | -n | -u ] [-r] [-s] [-S name]"
318 	    " [-X file]\n            [-x pattern] dir1 dir2\n");
319 
320 	exit(2);
321 }
322