source: trunk/minix/commands/simple/treecmp.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 8.1 KB
Line 
1/* treecmp - compare two trees Author: Andy Tanenbaum */
2
3/* This program recursively compares two trees and reports on differences.
4 * It can be used, for example, when a project consists of a large number
5 * of files and directories. When a new release (i.e., a new tree) has been
6 * prepared, the old and new tree can be compared to give a list of what has
7 * changed. The algorithm used is that the second tree is recursively
8 * descended and for each file or directory found, the corresponding one in
9 * the other tree checked. The two arguments are not completely symmetric
10 * because the second tree is descended, not the first one, but reversing
11 * the arguments will still detect all the differences, only they will be
12 * printed in a different order. The program needs lots of stack space
13 * because routines with local arrays are called recursively. The call is
14 * treecmp [-cv] old_dir new_dir
15 * The -v flag (verbose) prints the directory names as they are processed.
16 * The -c flag (changes) just prints the names of changed and new files.
17 */
18
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22#include <string.h>
23#include <unistd.h>
24#include <stdlib.h>
25#include <stdio.h>
26
27#define BUFSIZE 4096 /* size of file buffers */
28#define MAXPATH 128 /* longest acceptable path */
29#define DIRENTLEN 14 /* number of characters in a file name */
30
31struct dirstruct { /* layout of a directory entry */
32 ino_t inum;
33 char fname[DIRENTLEN];
34};
35
36struct stat stat1, stat2; /* stat buffers */
37
38char buf1[BUFSIZE]; /* used for comparing bufs */
39char buf2[BUFSIZE]; /* used for comparing bufs */
40
41int changes; /* set on -c flag */
42int verbose; /* set on -v flag */
43
44_PROTOTYPE(int main, (int argc, char **argv));
45_PROTOTYPE(void compare, (char *old, char *new));
46_PROTOTYPE(void regular, (char *old, char *new));
47_PROTOTYPE(void directory, (char *old, char *new));
48_PROTOTYPE(void check, (char *s, struct dirstruct *dp1, int ent1, char *new));
49_PROTOTYPE(void usage, (void));
50
51int main(argc, argv)
52int argc;
53char *argv[];
54{
55 char *p;
56
57 if (argc < 3 || argc > 4) usage();
58 p = argv[1];
59 if (argc == 4) {
60 if (*p != '-') usage();
61 p++;
62 if (*p == '\0') usage();
63 while (*p) {
64 if (*p == 'c') changes++;
65 if (*p == 'v') verbose++;
66 if (*p != 'c' && *p != 'v') usage();
67 p++;
68 }
69 }
70 if (argc == 3)
71 compare(argv[1], argv[2]);
72 else
73 compare(argv[2], argv[3]);
74
75 return(0);
76}
77
78void compare(old, new)
79char *old, *new;
80{
81/* This is the main comparision routine. It gets two path names as arguments
82 * and stats them both. Depending on the results, it calls other routines
83 * to compare directories or files.
84 */
85
86 int type1, type2;
87
88 if (stat(new, &stat1) < 0) {
89 /* The new file does not exist. */
90 if (changes == 0)
91 fprintf(stderr, "Cannot stat: %s\n", new);
92 else
93 printf("%s\n", new);
94 return;
95 }
96 if (stat(old, &stat2) < 0) {
97 /* The old file does not exist. */
98 if (changes == 0)
99 fprintf(stderr, "Missing file: %s\n", old);
100 else
101 printf("%s\n", new);
102 return;
103 }
104
105 /* Examine the types of the files. */
106 type1 = stat1.st_mode & S_IFMT;
107 type2 = stat2.st_mode & S_IFMT;
108 if (type1 != type2) {
109 fprintf(stderr, "Type diff: %s and %s\n", new, old);
110 return;
111 }
112
113 /* The types are the same. */
114 switch (type1) {
115 case S_IFREG: regular(old, new); break;
116 case S_IFDIR: directory(old, new); break;
117 case S_IFCHR: break;
118 case S_IFBLK: break;
119 default: fprintf(stderr, "Unknown file type %o\n", type1);
120 }
121 return;
122}
123
124void regular(old, new)
125char *old, *new;
126{
127/* Compare to regular files. If they are different, complain. */
128
129 int fd1, fd2, n1, n2;
130 unsigned bytes;
131 long count;
132
133 if (stat1.st_size != stat2.st_size) {
134 if (changes == 0)
135 printf("Size diff: %s and %s\n", new, old);
136 else
137 printf("%s\n", new);
138 return;
139 }
140
141 /* The sizes are the same. We actually have to read the files now. */
142 fd1 = open(new, O_RDONLY);
143 if (fd1 < 0) {
144 fprintf(stderr, "Cannot open %s for reading\n", new);
145 return;
146 }
147 fd2 = open(old, O_RDONLY);
148 if (fd2 < 0) {
149 fprintf(stderr, "Cannot open %s for reading\n", old);
150 return;
151 }
152 count = stat1.st_size;
153 while (count > 0L) {
154 bytes = (unsigned) (count > BUFSIZE ? BUFSIZE : count); /* rd count */
155 n1 = read(fd1, buf1, bytes);
156 n2 = read(fd2, buf2, bytes);
157 if (n1 != n2) {
158 if (changes == 0)
159 printf("Length diff: %s and %s\n", new, old);
160 else
161 printf("%s\n", new);
162 close(fd1);
163 close(fd2);
164 return;
165 }
166
167 /* Compare the buffers. */
168 if (memcmp((void *) buf1, (void *) buf2, (size_t) n1) != 0) {
169 if (changes == 0)
170 printf("File diff: %s and %s\n", new, old);
171 else
172 printf("%s\n", new);
173 close(fd1);
174 close(fd2);
175 return;
176 }
177 count -= n1;
178 }
179 close(fd1);
180 close(fd2);
181}
182
183void directory(old, new)
184char *old, *new;
185{
186/* Recursively compare two directories by reading them and comparing their
187 * contents. The order of the entries need not be the same.
188 */
189
190 int fd1, fd2, n1, n2, ent1, ent2, i, used1 = 0, used2 = 0;
191 char *dir1buf, *dir2buf;
192 char name1buf[MAXPATH], name2buf[MAXPATH];
193 struct dirstruct *dp1, *dp2;
194 unsigned dir1bytes, dir2bytes;
195
196 /* Allocate space to read in the directories */
197 dir1bytes = (unsigned) stat1.st_size;
198 dir1buf = (char *)malloc((size_t)dir1bytes);
199 if (dir1buf == 0) {
200 fprintf(stderr, "Cannot process directory %s: out of memory\n", new);
201 return;
202 }
203 dir2bytes = (unsigned) stat2.st_size;
204 dir2buf = (char *)malloc((size_t)dir2bytes);
205 if (dir2buf == 0) {
206 fprintf(stderr, "Cannot process directory %s: out of memory\n", old);
207 free(dir1buf);
208 return;
209 }
210
211 /* Read in the directories. */
212 fd1 = open(new, O_RDONLY);
213 if (fd1 > 0) n1 = read(fd1, dir1buf, dir1bytes);
214 if (fd1 < 0 || n1 != dir1bytes) {
215 fprintf(stderr, "Cannot read directory %s\n", new);
216 free(dir1buf);
217 free(dir2buf);
218 if (fd1 > 0) close(fd1);
219 return;
220 }
221 close(fd1);
222
223 fd2 = open(old, O_RDONLY);
224 if (fd2 > 0) n2 = read(fd2, dir2buf, dir2bytes);
225 if (fd2 < 0 || n2 != dir2bytes) {
226 fprintf(stderr, "Cannot read directory %s\n", old);
227 free(dir1buf);
228 free(dir2buf);
229 close(fd1);
230 if (fd2 > 0) close(fd2);
231 return;
232 }
233 close(fd2);
234
235 /* Linearly search directories */
236 ent1 = dir1bytes / sizeof(struct dirstruct);
237 dp1 = (struct dirstruct *) dir1buf;
238 for (i = 0; i < ent1; i++) {
239 if (dp1->inum != 0) used1++;
240 dp1++;
241 }
242
243 ent2 = dir2bytes / sizeof(struct dirstruct);
244 dp2 = (struct dirstruct *) dir2buf;
245 for (i = 0; i < ent2; i++) {
246 if (dp2->inum != 0) used2++;
247 dp2++;
248 }
249
250 if (verbose) printf("Directory %s: %d entries\n", new, used1);
251
252 /* Check to see if any entries in dir2 are missing from dir1. */
253 dp1 = (struct dirstruct *) dir1buf;
254 dp2 = (struct dirstruct *) dir2buf;
255 for (i = 0; i < ent2; i++) {
256 if (dp2->inum == 0 || strcmp(dp2->fname, ".") == 0 ||
257 strcmp(dp2->fname, "..") == 0) {
258 dp2++;
259 continue;
260 }
261 check(dp2->fname, dp1, ent1, new);
262 dp2++;
263 }
264
265 /* Recursively process all the entries in dir1. */
266 dp1 = (struct dirstruct *) dir1buf;
267 for (i = 0; i < ent1; i++) {
268 if (dp1->inum == 0 || strcmp(dp1->fname, ".") == 0 ||
269 strcmp(dp1->fname, "..") == 0) {
270 dp1++;
271 continue;
272 }
273 if (strlen(new) + DIRENTLEN >= MAXPATH) {
274 fprintf(stderr, "Path too long: %s\n", new);
275 free(dir1buf);
276 free(dir2buf);
277 return;
278 }
279 if (strlen(old) + DIRENTLEN >= MAXPATH) {
280 fprintf(stderr, "Path too long: %s\n", old);
281 free(dir1buf);
282 free(dir2buf);
283 return;
284 }
285 strcpy(name1buf, old);
286 strcat(name1buf, "/");
287 strncat(name1buf, dp1->fname, (size_t)DIRENTLEN);
288 strcpy(name2buf, new);
289 strcat(name2buf, "/");
290 strncat(name2buf, dp1->fname, (size_t)DIRENTLEN);
291
292 /* Here is the recursive call to process an entry. */
293 compare(name1buf, name2buf); /* recursive call */
294 dp1++;
295 }
296
297 free(dir1buf);
298 free(dir2buf);
299}
300
301void check(s, dp1, ent1, new)
302char *s;
303struct dirstruct *dp1;
304int ent1;
305char *new;
306{
307/* See if the file name 's' is present in the directory 'dirbuf'. */
308 int i;
309 char file[DIRENTLEN+1];
310
311 for (i = 0; i < ent1; i++) {
312 if (strncmp(dp1->fname, s, (size_t)DIRENTLEN) == 0) return;
313 dp1++;
314 }
315 if (changes == 0) {
316 strncpy(file, s, DIRENTLEN);
317 file[DIRENTLEN] = '\0';
318 printf("Missing file: %s/%s\n", new, file);
319 }
320
321}
322
323void usage()
324{
325 printf("Usage: treecmp [-cv] old_dir new_dir\n");
326 exit(1);
327}
Note: See TracBrowser for help on using the repository browser.