xref: /openbsd-src/usr.bin/diff/diffdir.c (revision d0c3f5756de861fe4b7784c4a45fd032d25633c0)
1 /*	$OpenBSD: diffdir.c,v 1.2 2003/06/25 01:23:38 deraadt 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 static	char *sccsid = "@(#)diffdir.c	4.12 (Berkeley) 4/30/89";
38 
39 #include "diff.h"
40 /*
41  * diff - directory comparison
42  */
43 #define	d_flags	d_ino
44 
45 #define	ONLY	1		/* Only in this directory */
46 #define	SAME	2		/* Both places and same */
47 #define	DIFFER	4		/* Both places and different */
48 #define	DIRECT	8		/* Directory */
49 
50 struct dir {
51 	u_long	d_ino;
52 	short	d_reclen;
53 	short	d_namlen;
54 	char	*d_entry;
55 };
56 
57 struct	dir *setupdir();
58 int	header;
59 static int	dirstatus;		/* exit status from diffdir */
60 extern int	status;
61 char	title[2*BUFSIZ], *etitle;
62 
63 diffdir(argv)
64 	char **argv;
65 {
66 	register struct dir *d1, *d2;
67 	struct dir *dir1, *dir2;
68 	register int i;
69 	int cmp;
70 
71 	if (opt == D_IFDEF) {
72 		fprintf(stderr, "diff: can't specify -I with directories\n");
73 		done();
74 	}
75 	if (opt == D_EDIT && (sflag || lflag))
76 		fprintf(stderr,
77 		    "diff: warning: shouldn't give -s or -l with -e\n");
78 	title[0] = 0;
79 	strcpy(title, "diff ");
80 	for (i = 1; diffargv[i+2]; i++) {
81 		if (!strcmp(diffargv[i], "-"))
82 			continue;	/* was -S, dont look silly */
83 		strcat(title, diffargv[i]);
84 		strcat(title, " ");
85 	}
86 	for (etitle = title; *etitle; etitle++)
87 		;
88 	setfile(&file1, &efile1, file1);
89 	setfile(&file2, &efile2, file2);
90 	argv[0] = file1;
91 	argv[1] = file2;
92 	dir1 = setupdir(file1);
93 	dir2 = setupdir(file2);
94 	d1 = dir1; d2 = dir2;
95 	while (d1->d_entry != 0 || d2->d_entry != 0) {
96 		if (d1->d_entry && useless(d1->d_entry)) {
97 			d1++;
98 			continue;
99 		}
100 		if (d2->d_entry && useless(d2->d_entry)) {
101 			d2++;
102 			continue;
103 		}
104 		if (d1->d_entry == 0)
105 			cmp = 1;
106 		else if (d2->d_entry == 0)
107 			cmp = -1;
108 		else
109 			cmp = strcmp(d1->d_entry, d2->d_entry);
110 		if (cmp < 0) {
111 			if (lflag)
112 				d1->d_flags |= ONLY;
113 			else if (opt == 0 || opt == 2)
114 				only(d1, 1);
115 			d1++;
116 			dirstatus |= 1;
117 		} else if (cmp == 0) {
118 			compare(d1);
119 			d1++;
120 			d2++;
121 		} else {
122 			if (lflag)
123 				d2->d_flags |= ONLY;
124 			else if (opt == 0 || opt == 2)
125 				only(d2, 2);
126 			d2++;
127 			dirstatus |= 1;
128 		}
129 	}
130 	if (lflag) {
131 		scanpr(dir1, ONLY, "Only in %.*s", file1, efile1, 0, 0);
132 		scanpr(dir2, ONLY, "Only in %.*s", file2, efile2, 0, 0);
133 		scanpr(dir1, SAME, "Common identical files in %.*s and %.*s",
134 		    file1, efile1, file2, efile2);
135 		scanpr(dir1, DIFFER, "Binary files which differ in %.*s and %.*s",
136 		    file1, efile1, file2, efile2);
137 		scanpr(dir1, DIRECT, "Common subdirectories of %.*s and %.*s",
138 		    file1, efile1, file2, efile2);
139 	}
140 	if (rflag) {
141 		if (header && lflag)
142 			printf("\f");
143 		for (d1 = dir1; d1->d_entry; d1++)  {
144 			if ((d1->d_flags & DIRECT) == 0)
145 				continue;
146 			strcpy(efile1, d1->d_entry);
147 			strcpy(efile2, d1->d_entry);
148 			calldiff(0);
149 		}
150 	}
151 	status = dirstatus;
152 }
153 
154 setfile(fpp, epp, file)
155 	char **fpp, **epp;
156 	char *file;
157 {
158 	register char *cp;
159 
160 	*fpp = malloc(BUFSIZ);
161 	if (*fpp == 0) {
162 		fprintf(stderr, "diff: ran out of memory\n");
163 		exit(1);
164 	}
165 	strcpy(*fpp, file);
166 	for (cp = *fpp; *cp; cp++)
167 		continue;
168 	*cp++ = '/';
169 	*epp = cp;
170 }
171 
172 scanpr(dp, test, title, file1, efile1, file2, efile2)
173 	register struct dir *dp;
174 	int test;
175 	char *title, *file1, *efile1, *file2, *efile2;
176 {
177 	int titled = 0;
178 
179 	for (; dp->d_entry; dp++) {
180 		if ((dp->d_flags & test) == 0)
181 			continue;
182 		if (titled == 0) {
183 			if (header == 0)
184 				header = 1;
185 			else
186 				printf("\n");
187 			printf(title,
188 			    efile1 - file1 - 1, file1,
189 			    efile2 - file2 - 1, file2);
190 			printf(":\n");
191 			titled = 1;
192 		}
193 		printf("\t%s\n", dp->d_entry);
194 	}
195 }
196 
197 only(dp, which)
198 	struct dir *dp;
199 	int which;
200 {
201 	char *file = which == 1 ? file1 : file2;
202 	char *efile = which == 1 ? efile1 : efile2;
203 
204 	printf("Only in %.*s: %s\n", efile - file - 1, file, dp->d_entry);
205 
206 }
207 
208 int	entcmp();
209 
210 struct dir *
211 setupdir(cp)
212 	char *cp;
213 {
214 	register struct dir *dp, *ep;
215 	register struct direct *rp;
216 	register int nitems, n;
217 	DIR *dirp;
218 
219 	dirp = opendir(cp);
220 	if (dirp == NULL) {
221 		fprintf(stderr, "diff: ");
222 		perror(cp);
223 		done();
224 	}
225 	nitems = 0;
226 	dp = (struct dir *)malloc(sizeof (struct dir));
227 	if (dp == 0) {
228 		fprintf(stderr, "diff: ran out of memory\n");
229 		done();
230 	}
231 	while (rp = readdir(dirp)) {
232 		ep = &dp[nitems++];
233 		ep->d_reclen = rp->d_reclen;
234 		ep->d_namlen = rp->d_namlen;
235 		ep->d_entry = 0;
236 		ep->d_flags = 0;
237 		if (ep->d_namlen > 0) {
238 			ep->d_entry = malloc(ep->d_namlen + 1);
239 			if (ep->d_entry == 0) {
240 				fprintf(stderr, "diff: out of memory\n");
241 				done();
242 			}
243 			strcpy(ep->d_entry, rp->d_name);
244 		}
245 		dp = (struct dir *)realloc((char *)dp,
246 			(nitems + 1) * sizeof (struct dir));
247 		if (dp == 0) {
248 			fprintf(stderr, "diff: ran out of memory\n");
249 			done();
250 		}
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 entcmp(d1, d2)
259 	struct dir *d1, *d2;
260 {
261 	return (strcmp(d1->d_entry, d2->d_entry));
262 }
263 
264 compare(dp)
265 	register struct dir *dp;
266 {
267 	register int i, j;
268 	int f1, f2, fmt1, fmt2;
269 	struct stat stb1, stb2;
270 	int flag = 0;
271 	char buf1[BUFSIZ], buf2[BUFSIZ];
272 
273 	strcpy(efile1, dp->d_entry);
274 	strcpy(efile2, dp->d_entry);
275 	f1 = open(file1, 0);
276 	if (f1 < 0) {
277 		perror(file1);
278 		return;
279 	}
280 	f2 = open(file2, 0);
281 	if (f2 < 0) {
282 		perror(file2);
283 		close(f1);
284 		return;
285 	}
286 	fstat(f1, &stb1); fstat(f2, &stb2);
287 	fmt1 = stb1.st_mode & S_IFMT;
288 	fmt2 = stb2.st_mode & S_IFMT;
289 	if (fmt1 != S_IFREG || fmt2 != S_IFREG) {
290 		if (fmt1 == fmt2) {
291 			if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev)
292 				goto same;
293 			if (fmt1 == S_IFDIR) {
294 				dp->d_flags = DIRECT;
295 				if (lflag || opt == D_EDIT)
296 					goto closem;
297 				printf("Common subdirectories: %s and %s\n",
298 				    file1, file2);
299 				goto closem;
300 			}
301 		}
302 		goto notsame;
303 	}
304 	if (stb1.st_size != stb2.st_size)
305 		goto notsame;
306 	for (;;) {
307 		i = read(f1, buf1, BUFSIZ);
308 		j = read(f2, buf2, BUFSIZ);
309 		if (i < 0 || j < 0 || i != j)
310 			goto notsame;
311 		if (i == 0 && j == 0)
312 			goto same;
313 		for (j = 0; j < i; j++)
314 			if (buf1[j] != buf2[j])
315 				goto notsame;
316 	}
317 same:
318 	if (sflag == 0)
319 		goto closem;
320 	if (lflag)
321 		dp->d_flags = SAME;
322 	else
323 		printf("Files %s and %s are identical\n", file1, file2);
324 	goto closem;
325 notsame:
326 	dirstatus |= 1;
327 	if (!ascii(f1) || !ascii(f2)) {
328 		if (lflag)
329 			dp->d_flags |= DIFFER;
330 		else if (opt == D_NORMAL || opt == D_CONTEXT)
331 			printf("Binary files %s and %s differ\n",
332 			    file1, file2);
333 		goto closem;
334 	}
335 	close(f1); close(f2);
336 	anychange = 1;
337 	if (lflag)
338 		calldiff(title);
339 	else {
340 		if (opt == D_EDIT) {
341 			printf("ed - %s << '-*-END-*-'\n", dp->d_entry);
342 			calldiff(0);
343 		} else {
344 			printf("%s%s %s\n", title, file1, file2);
345 			calldiff(0);
346 		}
347 		if (opt == D_EDIT)
348 			printf("w\nq\n-*-END-*-\n");
349 	}
350 	return;
351 closem:
352 	close(f1); close(f2);
353 }
354 
355 char	*prargs[] = { "pr", "-h", 0, "-f", 0, 0 };
356 
357 calldiff(wantpr)
358 	char *wantpr;
359 {
360 	int pid, lstatus, lstatus2, pv[2];
361 
362 	prargs[2] = wantpr;
363 	fflush(stdout);
364 	if (wantpr) {
365 		(void)sprintf(etitle, "%s %s", file1, file2);
366 		pipe(pv);
367 		pid = fork();
368 		if (pid == -1) {
369 			fprintf(stderr, "No more processes");
370 			done();
371 		}
372 		if (pid == 0) {
373 			close(0);
374 			dup(pv[0]);
375 			close(pv[0]);
376 			close(pv[1]);
377 			execv(pr+4, prargs);
378 			execv(pr, prargs);
379 			perror(pr);
380 			done();
381 		}
382 	}
383 	pid = fork();
384 	if (pid == -1) {
385 		fprintf(stderr, "diff: No more processes\n");
386 		done();
387 	}
388 	if (pid == 0) {
389 		if (wantpr) {
390 			close(1);
391 			dup(pv[1]);
392 			close(pv[0]);
393 			close(pv[1]);
394 		}
395 		execv(diff+4, diffargv);
396 		execv(diff, diffargv);
397 		perror(diff);
398 		done();
399 	}
400 	if (wantpr) {
401 		close(pv[0]);
402 		close(pv[1]);
403 	}
404 	while (wait(&lstatus) != pid)
405 		continue;
406 	while (wait(&lstatus2) != -1)
407 		continue;
408 /*
409 	if ((lstatus >> 8) >= 2)
410 		done();
411 */
412 	dirstatus |= lstatus >> 8;
413 }
414 
415 #include <a.out.h>
416 
417 ascii(f)
418 	int f;
419 {
420 	char buf[BUFSIZ];
421 	register int cnt;
422 	register char *cp;
423 
424 	lseek(f, (long)0, 0);
425 	cnt = read(f, buf, BUFSIZ);
426 	if (cnt >= sizeof (struct exec)) {
427 		struct exec hdr;
428 		hdr = *(struct exec *)buf;
429 		if (!N_BADMAG(hdr))
430 			return (0);
431 	}
432 	cp = buf;
433 	while (--cnt >= 0)
434 		if (*cp++ & 0200)
435 			return (0);
436 	return (1);
437 }
438 
439 /*
440  * THIS IS CRUDE.
441  */
442 useless(cp)
443 register char *cp;
444 {
445 
446 	if (cp[0] == '.') {
447 		if (cp[1] == '\0')
448 			return (1);	/* directory "." */
449 		if (cp[1] == '.' && cp[2] == '\0')
450 			return (1);	/* directory ".." */
451 	}
452 	if (start && strcmp(start, cp) > 0)
453 		return (1);
454 	return (0);
455 }
456