xref: /openbsd-src/usr.bin/diff/diffdir.c (revision 5004e76e0957e576070580d43a0b7bf642d3309c)
1 /*	$OpenBSD: diffdir.c,v 1.8 2003/06/25 03:39:23 tedu Exp $	*/
2 
3 /*
4  * Copyright (C) Caldera International Inc.  2001-2002.
5  * 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 and documentation must retain the above
11  *    copyright 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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed or owned by Caldera
18  *	International, Inc.
19  * 4. Neither the name of Caldera International, Inc. nor the names of other
20  *    contributors may be used to endorse or promote products derived from
21  *    this software without specific prior written permission.
22  *
23  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
24  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
28  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <string.h>
45 
46 #include "diff.h"
47 
48 #if 0
49 static const char sccsid[] = "@(#)diffdir.c	4.12 (Berkeley) 4/30/89";
50 #endif
51 
52 /*
53  * diff - directory comparison
54  */
55 #define	d_flags	d_ino
56 
57 #define	ONLY	1		/* Only in this directory */
58 #define	SAME	2		/* Both places and same */
59 #define	DIFFER	4		/* Both places and different */
60 #define	DIRECT	8		/* Directory */
61 
62 struct dir {
63 	u_long d_ino;
64 	short d_reclen;
65 	short d_namlen;
66 	char *d_entry;
67 };
68 
69 int header;
70 static int dirstatus;		/* exit status from diffdir */
71 extern int status;
72 char title[2 * BUFSIZ];
73 char *etitle;
74 char *prargs[] = {"pr", "-h", 0, "-f", 0, 0};
75 
76 
77 static struct dir *setupdir(char *);
78 static int ascii(int);
79 static void compare(struct dir *);
80 static void calldiff(char *);
81 static void setfile(char **fpp, char **epp, char *file);
82 static int useless(char *);
83 static void only(struct dir *dp, int which);
84 static void scanpr(struct dir *, int, char *, char *, char *, char *, char *);
85 static int entcmp(const void *, const void *);
86 
87 
88 void
89 diffdir(char **argv)
90 {
91 	struct dir *d1, *d2;
92 	struct dir *dir1, *dir2;
93 	int i;
94 	int cmp;
95 
96 	if (opt == D_IFDEF) {
97 		fprintf(stderr, "diff: can't specify -I with directories\n");
98 		done(0);
99 	}
100 	if (opt == D_EDIT && (sflag || lflag))
101 		fprintf(stderr,
102 		    "diff: warning: shouldn't give -s or -l with -e\n");
103 	strlcpy(title, "diff ", sizeof title);
104 	for (i = 1; diffargv[i + 2]; i++) {
105 		if (!strcmp(diffargv[i], "-"))
106 			continue;	/* was -S, dont look silly */
107 		strlcat(title, diffargv[i], sizeof title);
108 		strlcat(title, " ", sizeof title);
109 	}
110 	for (etitle = title; *etitle; etitle++);
111 	setfile(&file1, &efile1, file1);
112 	setfile(&file2, &efile2, file2);
113 	argv[0] = file1;
114 	argv[1] = file2;
115 	dir1 = setupdir(file1);
116 	dir2 = setupdir(file2);
117 	d1 = dir1;
118 	d2 = dir2;
119 	while (d1->d_entry != 0 || d2->d_entry != 0) {
120 		if (d1->d_entry && useless(d1->d_entry)) {
121 			d1++;
122 			continue;
123 		}
124 		if (d2->d_entry && useless(d2->d_entry)) {
125 			d2++;
126 			continue;
127 		}
128 		if (d1->d_entry == 0)
129 			cmp = 1;
130 		else if (d2->d_entry == 0)
131 			cmp = -1;
132 		else
133 			cmp = strcmp(d1->d_entry, d2->d_entry);
134 		if (cmp < 0) {
135 			if (lflag)
136 				d1->d_flags |= ONLY;
137 			else if (opt == 0 || opt == 2)
138 				only(d1, 1);
139 			d1++;
140 			dirstatus |= 1;
141 		} else if (cmp == 0) {
142 			compare(d1);
143 			d1++;
144 			d2++;
145 		} else {
146 			if (lflag)
147 				d2->d_flags |= ONLY;
148 			else if (opt == 0 || opt == 2)
149 				only(d2, 2);
150 			d2++;
151 			dirstatus |= 1;
152 		}
153 	}
154 	if (lflag) {
155 		scanpr(dir1, ONLY, "Only in %.*s", file1, efile1, 0, 0);
156 		scanpr(dir2, ONLY, "Only in %.*s", file2, efile2, 0, 0);
157 		scanpr(dir1, SAME, "Common identical files in %.*s and %.*s",
158 		    file1, efile1, file2, efile2);
159 		scanpr(dir1, DIFFER, "Binary files which differ in %.*s and %.*s",
160 		    file1, efile1, file2, efile2);
161 		scanpr(dir1, DIRECT, "Common subdirectories of %.*s and %.*s",
162 		    file1, efile1, file2, efile2);
163 	}
164 	if (rflag) {
165 		if (header && lflag)
166 			printf("\f");
167 		for (d1 = dir1; d1->d_entry; d1++) {
168 			if ((d1->d_flags & DIRECT) == 0)
169 				continue;
170 			strcpy(efile1, d1->d_entry);
171 			strcpy(efile2, d1->d_entry);
172 			calldiff(0);
173 		}
174 	}
175 	status = dirstatus;
176 }
177 
178 void
179 setfile(char **fpp, char **epp, char *file)
180 {
181 	char *cp;
182 
183 	*fpp = talloc(BUFSIZ);
184 	strcpy(*fpp, file);
185 	for (cp = *fpp; *cp; cp++)
186 		continue;
187 	*cp++ = '/';
188 	*epp = cp;
189 }
190 
191 static void
192 scanpr(struct dir *dp, int test, char *title, char *file1, char *efile1,
193     char *file2, char *efile2)
194 {
195 	int titled = 0;
196 
197 	for (; dp->d_entry; dp++) {
198 		if ((dp->d_flags & test) == 0)
199 			continue;
200 		if (titled == 0) {
201 			if (header == 0)
202 				header = 1;
203 			else
204 				printf("\n");
205 			printf(title,
206 			    efile1 - file1 - 1, file1,
207 			    efile2 - file2 - 1, file2);
208 			printf(":\n");
209 			titled = 1;
210 		}
211 		printf("\t%s\n", dp->d_entry);
212 	}
213 }
214 
215 void
216 only(struct dir *dp, int which)
217 {
218 	char *file = which == 1 ? file1 : file2;
219 	char *efile = which == 1 ? efile1 : efile2;
220 
221 	printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, dp->d_entry);
222 }
223 
224 struct dir *
225 setupdir(char *cp)
226 {
227 	struct dir *dp, *ep;
228 	struct direct *rp;
229 	int nitems;
230 	DIR *dirp;
231 
232 	dirp = opendir(cp);
233 	if (dirp == NULL) {
234 		fprintf(stderr, "diff: ");
235 		perror(cp);
236 		done(0);
237 	}
238 	nitems = 0;
239 	dp = talloc(sizeof(struct dir));
240 	while ((rp = readdir(dirp))) {
241 		ep = &dp[nitems++];
242 		ep->d_reclen = rp->d_reclen;
243 		ep->d_namlen = rp->d_namlen;
244 		ep->d_entry = 0;
245 		ep->d_flags = 0;
246 		if (ep->d_namlen > 0) {
247 			ep->d_entry = talloc(ep->d_namlen + 1);
248 			strcpy(ep->d_entry, rp->d_name);
249 		}
250 		dp = ralloc(dp, (nitems + 1) * sizeof(struct dir));
251 	}
252 	dp[nitems].d_entry = 0;	/* delimiter */
253 	closedir(dirp);
254 	qsort(dp, nitems, sizeof(struct dir), entcmp);
255 	return (dp);
256 }
257 
258 static int
259 entcmp(const void *v1, const void *v2)
260 {
261 	const struct dir *d1, *d2;
262 
263 	d1 = v1;
264 	d2 = v2;
265 	return (strcmp(d1->d_entry, d2->d_entry));
266 }
267 
268 static void
269 compare(struct dir *dp)
270 {
271 	int i, j;
272 	int f1, f2, fmt1, fmt2;
273 	struct stat stb1, stb2;
274 	char buf1[BUFSIZ], buf2[BUFSIZ];
275 
276 	strcpy(efile1, dp->d_entry);
277 	strcpy(efile2, dp->d_entry);
278 	f1 = open(file1, 0);
279 	if (f1 < 0) {
280 		perror(file1);
281 		return;
282 	}
283 	f2 = open(file2, 0);
284 	if (f2 < 0) {
285 		perror(file2);
286 		close(f1);
287 		return;
288 	}
289 	fstat(f1, &stb1);
290 	fstat(f2, &stb2);
291 	fmt1 = stb1.st_mode & S_IFMT;
292 	fmt2 = stb2.st_mode & S_IFMT;
293 	if (fmt1 != S_IFREG || fmt2 != S_IFREG) {
294 		if (fmt1 == fmt2) {
295 			if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev)
296 				goto same;
297 			if (fmt1 == S_IFDIR) {
298 				dp->d_flags = DIRECT;
299 				if (lflag || opt == D_EDIT)
300 					goto closem;
301 				printf("Common subdirectories: %s and %s\n",
302 				    file1, file2);
303 				goto closem;
304 			}
305 		}
306 		goto notsame;
307 	}
308 	if (stb1.st_size != stb2.st_size)
309 		goto notsame;
310 	for (;;) {
311 		i = read(f1, buf1, BUFSIZ);
312 		j = read(f2, buf2, BUFSIZ);
313 		if (i < 0 || j < 0 || i != j)
314 			goto notsame;
315 		if (i == 0 && j == 0)
316 			goto same;
317 		for (j = 0; j < i; j++)
318 			if (buf1[j] != buf2[j])
319 				goto notsame;
320 	}
321 same:
322 	if (sflag == 0)
323 		goto closem;
324 	if (lflag)
325 		dp->d_flags = SAME;
326 	else
327 		printf("Files %s and %s are identical\n", file1, file2);
328 	goto closem;
329 notsame:
330 	dirstatus |= 1;
331 	if (!ascii(f1) || !ascii(f2)) {
332 		if (lflag)
333 			dp->d_flags |= DIFFER;
334 		else if (opt == D_NORMAL || opt == D_CONTEXT)
335 			printf("Binary files %s and %s differ\n",
336 			    file1, file2);
337 		goto closem;
338 	}
339 	close(f1);
340 	close(f2);
341 	anychange = 1;
342 	if (lflag)
343 		calldiff(title);
344 	else {
345 		if (opt == D_EDIT) {
346 			printf("ed - %s << '-*-END-*-'\n", dp->d_entry);
347 			calldiff(0);
348 		} else {
349 			printf("%s%s %s\n", title, file1, file2);
350 			calldiff(0);
351 		}
352 		if (opt == D_EDIT)
353 			printf("w\nq\n-*-END-*-\n");
354 	}
355 	return;
356 closem:
357 	close(f1);
358 	close(f2);
359 }
360 
361 static void
362 calldiff(char *wantpr)
363 {
364 	int pid, lstatus, lstatus2, pv[2];
365 
366 	prargs[2] = wantpr;
367 	fflush(stdout);
368 	if (wantpr) {
369 		snprintf(etitle, title + sizeof title - etitle,
370 		    "%s %s", file1, file2);
371 		pipe(pv);
372 		pid = fork();
373 		if (pid == -1) {
374 			fprintf(stderr, "No more processes");
375 			done(0);
376 		}
377 		if (pid == 0) {
378 			close(0);
379 			dup(pv[0]);
380 			close(pv[0]);
381 			close(pv[1]);
382 			execv(pr + 4, prargs);
383 			execv(pr, prargs);
384 			perror(pr);
385 			done(0);
386 		}
387 	}
388 	pid = fork();
389 	if (pid == -1) {
390 		fprintf(stderr, "diff: No more processes\n");
391 		done(0);
392 	}
393 	if (pid == 0) {
394 		if (wantpr) {
395 			close(1);
396 			dup(pv[1]);
397 			close(pv[0]);
398 			close(pv[1]);
399 		}
400 		execv(diff + 4, diffargv);
401 		execv(diff, diffargv);
402 		perror(diff);
403 		done(0);
404 	}
405 	if (wantpr) {
406 		close(pv[0]);
407 		close(pv[1]);
408 	}
409 	while (wait(&lstatus) != pid)
410 		continue;
411 	while (wait(&lstatus2) != -1)
412 		continue;
413 	/*
414 		if ((lstatus >> 8) >= 2)
415 			done(0);
416 	*/
417 	dirstatus |= lstatus >> 8;
418 }
419 
420 int
421 ascii(int f)
422 {
423 	char buf[BUFSIZ];
424 	int cnt;
425 	char *cp;
426 
427 	lseek(f, (off_t)0, SEEK_SET);
428 	cnt = read(f, buf, BUFSIZ);
429 	cp = buf;
430 	while (--cnt >= 0)
431 		if (*cp++ & 0200)
432 			return (0);
433 	return (1);
434 }
435 
436 /*
437  * THIS IS CRUDE.
438  */
439 int
440 useless(char *cp)
441 {
442 	if (cp[0] == '.') {
443 		if (cp[1] == '\0')
444 			return (1);	/* directory "." */
445 		if (cp[1] == '.' && cp[2] == '\0')
446 			return (1);	/* directory ".." */
447 	}
448 	if (start && strcmp(start, cp) > 0)
449 		return (1);
450 	return (0);
451 }
452