xref: /openbsd-src/usr.bin/diff/diff.c (revision cab5d83c9d6e6ab1ee51c5ad293daabd8930d5f9)
1 /*	$OpenBSD: diff.c,v 1.25 2003/07/06 22:17:21 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.25 2003/07/06 22:17:21 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, Pflag, 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:efhinNPqrS: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 	{ "unidirectional-new-file",	no_argument,		0,	'P' },
59 	{ "brief",			no_argument,		0,	'q' },
60 	{ "recursive",			no_argument,		0,	'r' },
61 	{ "report-identical-files",	no_argument,		0,	's' },
62 	{ "starting-file",		required_argument,	0,	'S' },
63 	{ "expand-tabs",		no_argument,		0,	't' },
64 	{ "unified",			optional_argument,	0,	'U' },
65 	{ "ignore-all-space",		no_argument,		0,	'w' },
66 	{ "exclude",			required_argument,	0,	'x' },
67 	{ "exclude-from",		required_argument,	0,	'X' },
68 };
69 
70 __dead void usage(void);
71 void push_excludes(char *);
72 void read_excludes_file(char *file);
73 void set_argstr(char **, char **);
74 
75 int
76 main(int argc, char **argv)
77 {
78 	char *ep, **oargv;
79 	long  l;
80 	int   ch, gotstdin;
81 
82 	oargv = argv;
83 	gotstdin = 0;
84 
85 	while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
86 		switch (ch) {
87 		case 'a':
88 			aflag = 1;
89 			break;
90 		case 'b':
91 			bflag = 1;
92 			break;
93 		case 'C':
94 		case 'c':
95 			format = D_CONTEXT;
96 			if (optarg != NULL) {
97 				l = strtol(optarg, &ep, 10);
98 				if (*ep != '\0' || l < 0 || l >= INT_MAX)
99 					usage();
100 				context = (int)l;
101 			} else
102 				context = 3;
103 			break;
104 		case 'D':
105 			format = D_IFDEF;
106 			ifdefname = optarg;
107 			break;
108 		case 'e':
109 			format = D_EDIT;
110 			break;
111 		case 'f':
112 			format = D_REVERSE;
113 			break;
114 		case 'h':
115 			/* silently ignore for backwards compatibility */
116 			break;
117 		case 'i':
118 			iflag = 1;
119 			break;
120 		case 'N':
121 			Nflag = 1;
122 			break;
123 		case 'n':
124 			format = D_NREVERSE;
125 			break;
126 		case 'P':
127 			Pflag = 1;
128 			break;
129 		case 'r':
130 			rflag = 1;
131 			break;
132 		case 'q':
133 			format = D_BRIEF;
134 			break;
135 		case 'S':
136 			start = optarg;
137 			break;
138 		case 's':
139 			sflag = 1;
140 			break;
141 		case 't':
142 			tflag = 1;
143 			break;
144 		case 'U':
145 		case 'u':
146 			format = D_UNIFIED;
147 			if (optarg != NULL) {
148 				l = strtol(optarg, &ep, 10);
149 				if (*ep != '\0' || l < 0 || l >= INT_MAX)
150 					usage();
151 				context = (int)l;
152 			} else
153 				context = 3;
154 			break;
155 		case 'w':
156 			wflag = 1;
157 			break;
158 		case 'X':
159 			read_excludes_file(optarg);
160 			break;
161 		case 'x':
162 			push_excludes(optarg);
163 			break;
164 		default:
165 			usage();
166 			break;
167 		}
168 	}
169 	argc -= optind;
170 	argv += optind;
171 
172 	/*
173 	 * Do sanity checks, fill in stb1 and stb2 and call the appropriate
174 	 * driver routine.  Both drivers use the contents of stb1 and stb2.
175 	 */
176 	if (argc != 2)
177 		usage();
178 	if (strcmp(argv[0], "-") == 0) {
179 		stb1.st_mode = S_IFREG;
180 		gotstdin = 1;
181 	} else if (stat(argv[0], &stb1) != 0)
182 		error("%s", argv[0]);
183 	if (strcmp(argv[1], "-") == 0) {
184 		stb2.st_mode = S_IFREG;
185 		gotstdin = 1;
186 	} else if (stat(argv[1], &stb2) != 0)
187 		error("%s", argv[1]);
188 	if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
189 		errorx("can't compare - to a directory");
190 	if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
191 		if (format == D_IFDEF)
192 			errorx("-D option not supported with directories");
193 		set_argstr(oargv, argv);
194 		diffdir(argv[0], argv[1]);
195 	} else
196 		diffreg(argv[0], argv[1], 0);
197 	exit(status);
198 }
199 
200 void
201 quit(int signo)
202 {
203 	if (tempfiles[0] != NULL)
204 		unlink(tempfiles[0]);
205 	if (tempfiles[1] != NULL)
206 		unlink(tempfiles[1]);
207 	_exit(status);
208 }
209 
210 void *
211 emalloc(size_t n)
212 {
213 	void *p;
214 
215 	if ((p = malloc(n)) == NULL)
216 		error(NULL);
217 	return (p);
218 }
219 
220 void *
221 erealloc(void *p, size_t n)
222 {
223 	void *q;
224 
225 	if ((q = realloc(p, n)) == NULL)
226 		error(NULL);
227 	return (q);
228 }
229 
230 __dead void
231 error(const char *fmt, ...)
232 {
233 	va_list ap;
234 	int sverrno = errno;
235 
236 	if (tempfiles[0] != NULL)
237 		unlink(tempfiles[0]);
238 	if (tempfiles[1] != NULL)
239 		unlink(tempfiles[1]);
240 	errno = sverrno;
241 	va_start(ap, fmt);
242 	verr(2, fmt, ap);
243 	va_end(ap);
244 }
245 
246 __dead void
247 errorx(const char *fmt, ...)
248 {
249 	va_list ap;
250 
251 	if (tempfiles[0] != NULL)
252 		unlink(tempfiles[0]);
253 	if (tempfiles[1] != NULL)
254 		unlink(tempfiles[1]);
255 	va_start(ap, fmt);
256 	verrx(2, fmt, ap);
257 	va_end(ap);
258 }
259 
260 void
261 set_argstr(char **av, char **ave)
262 {
263 	size_t argsize;
264 	char **ap;
265 
266 	argsize = 4 + (char *)ave - (char *)av + 1;
267 	diffargs = emalloc(argsize);
268 	strlcpy(diffargs, "diff", argsize);
269 	for (ap = av + 1; ap < ave; ap++) {
270 		if (strcmp(*ap, "--") != 0) {
271 			strlcat(diffargs, " ", argsize);
272 			strlcat(diffargs, *ap, argsize);
273 		}
274 	}
275 }
276 
277 /*
278  * Read in an excludes file and push each line.
279  */
280 void
281 read_excludes_file(char *file)
282 {
283 	FILE *fp;
284 	char *buf, *pattern;
285 	size_t len;
286 
287 	if (strcmp(file, "-") == 0)
288 		fp = stdin;
289 	else if ((fp = fopen(file, "r")) == NULL)
290 		error("%s", file);
291 	while ((buf = fgetln(fp, &len)) != NULL) {
292 		if (buf[len - 1] == '\n')
293 			len--;
294 		pattern = emalloc(len + 1);
295 		memcpy(pattern, buf, len);
296 		pattern[len] = '\0';
297 		push_excludes(pattern);
298 	}
299 	if (strcmp(file, "-") != 0)
300 		fclose(fp);
301 }
302 
303 /*
304  * Push a pattern onto the excludes list.
305  */
306 void
307 push_excludes(char *pattern)
308 {
309 	struct excludes *entry;
310 
311 	entry = emalloc(sizeof(*entry));
312 	entry->pattern = pattern;
313 	entry->next = excludes_list;
314 	excludes_list = entry;
315 }
316 
317 __dead void
318 usage(void)
319 {
320 	(void)fprintf(stderr,
321 	    "usage: diff [-biqtw] [-c | -e | -f | -n | -u ] file1 file2\n"
322 	    "       diff [-biqtw] -C number file1 file2\n"
323 	    "       diff [-biqtw] -D string file1 file2\n"
324 	    "       diff [-biqtw] -U number file1 file2\n"
325 	    "       diff [-biNPqwt] [-c | -e | -f | -n | -u ] [-r] [-s] [-S name]"
326 	    " [-X file]\n            [-x pattern] dir1 dir2\n");
327 
328 	exit(2);
329 }
330