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