treecmp.c

Go to the documentation of this file.
00001 /* treecmp - compare two trees          Author: Andy Tanenbaum */
00002 
00003 /* This program recursively compares two trees and reports on differences.
00004  * It can be used, for example, when a project consists of a large number
00005  * of files and directories.  When a new release (i.e., a new tree) has been
00006  * prepared, the old and new tree can be compared to give a list of what has
00007  * changed.  The algorithm used is that the second tree is recursively
00008  * descended and for each file or directory found, the corresponding one in
00009  * the other tree checked.  The two arguments are not completely symmetric
00010  * because the second tree is descended, not the first one, but reversing
00011  * the arguments will still detect all the differences, only they will be
00012  * printed in a different order.  The program needs lots of stack space
00013  * because routines with local arrays are called recursively. The call is
00014  *    treecmp [-cv] old_dir new_dir
00015  * The -v flag (verbose) prints the directory names as they are processed.
00016  * The -c flag (changes) just prints the names of changed and new files.
00017  */
00018 
00019 #include <sys/types.h>
00020 #include <sys/stat.h>
00021 #include <fcntl.h>
00022 #include <string.h>
00023 #include <unistd.h>
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 
00027 #define BUFSIZE 4096            /* size of file buffers */
00028 #define MAXPATH 128             /* longest acceptable path */
00029 #define DIRENTLEN 14            /* number of characters in a file name */
00030 
00031 struct dirstruct {              /* layout of a directory entry */
00032   ino_t inum;
00033   char fname[DIRENTLEN];
00034 };
00035 
00036 struct stat stat1, stat2;       /* stat buffers */
00037 
00038 char buf1[BUFSIZE];             /* used for comparing bufs */
00039 char buf2[BUFSIZE];             /* used for comparing bufs */
00040 
00041 int changes;                    /* set on -c flag */
00042 int verbose;                    /* set on -v flag */
00043 
00044 _PROTOTYPE(int main, (int argc, char **argv));
00045 _PROTOTYPE(void compare, (char *old, char *new));
00046 _PROTOTYPE(void regular, (char *old, char *new));
00047 _PROTOTYPE(void directory, (char *old, char *new));
00048 _PROTOTYPE(void check, (char *s, struct dirstruct *dp1, int ent1, char *new));
00049 _PROTOTYPE(void usage, (void));
00050 
00051 int main(argc, argv)
00052 int argc;
00053 char *argv[];
00054 {
00055   char *p;
00056 
00057   if (argc < 3 || argc > 4) usage();
00058   p = argv[1];
00059   if (argc == 4) {
00060         if (*p != '-') usage();
00061         p++;
00062         if (*p == '\0') usage();
00063         while (*p) {
00064                 if (*p == 'c') changes++;
00065                 if (*p == 'v') verbose++;
00066                 if (*p != 'c' && *p != 'v') usage();
00067                 p++;
00068         }
00069   }
00070   if (argc == 3)
00071         compare(argv[1], argv[2]);
00072   else
00073         compare(argv[2], argv[3]);
00074 
00075   return(0);
00076 }
00077 
00078 void compare(old, new)
00079 char *old, *new;
00080 {
00081 /* This is the main comparision routine.  It gets two path names as arguments
00082  * and stats them both.  Depending on the results, it calls other routines
00083  * to compare directories or files.
00084  */
00085 
00086   int type1, type2;
00087 
00088   if (stat(new, &stat1) < 0) {
00089         /* The new file does not exist. */
00090         if (changes == 0)
00091                 fprintf(stderr, "Cannot stat: %s\n", new);
00092         else
00093                 printf("%s\n", new);
00094         return;
00095   }
00096   if (stat(old, &stat2) < 0) {
00097         /* The old file does not exist. */
00098         if (changes == 0) 
00099                 fprintf(stderr, "Missing file: %s\n", old);
00100         else
00101                 printf("%s\n", new);
00102         return;
00103   }
00104 
00105   /* Examine the types of the files. */
00106   type1 = stat1.st_mode & S_IFMT;
00107   type2 = stat2.st_mode & S_IFMT;
00108   if (type1 != type2) {
00109         fprintf(stderr, "Type diff: %s and %s\n", new, old);
00110         return;
00111   }
00112 
00113   /* The types are the same. */
00114   switch (type1) {
00115       case S_IFREG:     regular(old, new);      break;
00116       case S_IFDIR:     directory(old, new);    break;
00117       case S_IFCHR:     break;
00118       case S_IFBLK:     break;
00119       default:          fprintf(stderr, "Unknown file type %o\n", type1);
00120   }
00121   return;
00122 }
00123 
00124 void regular(old, new)
00125 char *old, *new;
00126 {
00127 /* Compare to regular files.  If they are different, complain. */
00128 
00129   int fd1, fd2, n1, n2;
00130   unsigned bytes;
00131   long count;
00132 
00133   if (stat1.st_size != stat2.st_size) {
00134         if (changes == 0)
00135                 printf("Size diff: %s and %s\n", new, old);
00136         else
00137                 printf("%s\n", new);
00138         return;
00139   }
00140 
00141   /* The sizes are the same.  We actually have to read the files now. */
00142   fd1 = open(new, O_RDONLY);
00143   if (fd1 < 0) {
00144         fprintf(stderr, "Cannot open %s for reading\n", new);
00145         return;
00146   }
00147   fd2 = open(old, O_RDONLY);
00148   if (fd2 < 0) {
00149         fprintf(stderr, "Cannot open %s for reading\n", old);
00150         return;
00151   }
00152   count = stat1.st_size;
00153   while (count > 0L) {
00154         bytes = (unsigned) (count > BUFSIZE ? BUFSIZE : count); /* rd count */
00155         n1 = read(fd1, buf1, bytes);
00156         n2 = read(fd2, buf2, bytes);
00157         if (n1 != n2) {
00158                 if (changes == 0)
00159                         printf("Length diff: %s and %s\n", new, old);
00160                 else
00161                         printf("%s\n", new);
00162                 close(fd1);
00163                 close(fd2);
00164                 return;
00165         }
00166 
00167         /* Compare the buffers. */
00168         if (memcmp((void *) buf1, (void *) buf2, (size_t) n1) != 0) {
00169                 if (changes == 0)
00170                         printf("File diff: %s and %s\n", new, old);
00171                 else
00172                         printf("%s\n", new);
00173                 close(fd1);
00174                 close(fd2);
00175                 return;
00176         }
00177         count -= n1;
00178   }
00179   close(fd1);
00180   close(fd2);
00181 }
00182 
00183 void directory(old, new)
00184 char *old, *new;
00185 {
00186 /* Recursively compare two directories by reading them and comparing their
00187  * contents.  The order of the entries need not be the same.
00188  */
00189 
00190   int fd1, fd2, n1, n2, ent1, ent2, i, used1 = 0, used2 = 0;
00191   char *dir1buf, *dir2buf;
00192   char name1buf[MAXPATH], name2buf[MAXPATH];
00193   struct dirstruct *dp1, *dp2;
00194   unsigned dir1bytes, dir2bytes;
00195 
00196   /* Allocate space to read in the directories */
00197   dir1bytes = (unsigned) stat1.st_size;
00198   dir1buf = (char *)malloc((size_t)dir1bytes);
00199   if (dir1buf == 0) {
00200         fprintf(stderr, "Cannot process directory %s: out of memory\n", new);
00201         return;
00202   }
00203   dir2bytes = (unsigned) stat2.st_size;
00204   dir2buf = (char *)malloc((size_t)dir2bytes);
00205   if (dir2buf == 0) {
00206         fprintf(stderr, "Cannot process directory %s: out of memory\n", old);
00207         free(dir1buf);
00208         return;
00209   }
00210 
00211   /* Read in the directories. */
00212   fd1 = open(new, O_RDONLY);
00213   if (fd1 > 0) n1 = read(fd1, dir1buf, dir1bytes);
00214   if (fd1 < 0 || n1 != dir1bytes) {
00215         fprintf(stderr, "Cannot read directory %s\n", new);
00216         free(dir1buf);
00217         free(dir2buf);
00218         if (fd1 > 0) close(fd1);
00219         return;
00220   }
00221   close(fd1);
00222 
00223   fd2 = open(old, O_RDONLY);
00224   if (fd2 > 0) n2 = read(fd2, dir2buf, dir2bytes);
00225   if (fd2 < 0 || n2 != dir2bytes) {
00226         fprintf(stderr, "Cannot read directory %s\n", old);
00227         free(dir1buf);
00228         free(dir2buf);
00229         close(fd1);
00230         if (fd2 > 0) close(fd2);
00231         return;
00232   }
00233   close(fd2);
00234 
00235   /* Linearly search directories */
00236   ent1 = dir1bytes / sizeof(struct dirstruct);
00237   dp1 = (struct dirstruct *) dir1buf;
00238   for (i = 0; i < ent1; i++) {
00239         if (dp1->inum != 0) used1++;
00240         dp1++;
00241   }
00242 
00243   ent2 = dir2bytes / sizeof(struct dirstruct);
00244   dp2 = (struct dirstruct *) dir2buf;
00245   for (i = 0; i < ent2; i++) {
00246         if (dp2->inum != 0) used2++;
00247         dp2++;
00248   }
00249 
00250   if (verbose) printf("Directory %s: %d entries\n", new, used1);
00251 
00252   /* Check to see if any entries in dir2 are missing from dir1. */
00253   dp1 = (struct dirstruct *) dir1buf;
00254   dp2 = (struct dirstruct *) dir2buf;
00255   for (i = 0; i < ent2; i++) {
00256         if (dp2->inum == 0 || strcmp(dp2->fname, ".") == 0 ||
00257                                             strcmp(dp2->fname, "..") == 0) {
00258                 dp2++;
00259                 continue;
00260         }
00261         check(dp2->fname, dp1, ent1, new);
00262         dp2++;
00263   }
00264 
00265   /* Recursively process all the entries in dir1. */
00266   dp1 = (struct dirstruct *) dir1buf;
00267   for (i = 0; i < ent1; i++) {
00268         if (dp1->inum == 0 || strcmp(dp1->fname, ".") == 0 ||
00269             strcmp(dp1->fname, "..") == 0) {
00270                 dp1++;
00271                 continue;
00272         }
00273         if (strlen(new) + DIRENTLEN >= MAXPATH) {
00274                 fprintf(stderr, "Path too long: %s\n", new);
00275                 free(dir1buf);
00276                 free(dir2buf);
00277                 return;
00278         }
00279         if (strlen(old) + DIRENTLEN >= MAXPATH) {
00280                 fprintf(stderr, "Path too long: %s\n", old);
00281                 free(dir1buf);
00282                 free(dir2buf);
00283                 return;
00284         }
00285         strcpy(name1buf, old);
00286         strcat(name1buf, "/");
00287         strncat(name1buf, dp1->fname, (size_t)DIRENTLEN);
00288         strcpy(name2buf, new);
00289         strcat(name2buf, "/");
00290         strncat(name2buf, dp1->fname, (size_t)DIRENTLEN);
00291 
00292         /* Here is the recursive call to process an entry. */
00293         compare(name1buf, name2buf);    /* recursive call */
00294         dp1++;
00295   }
00296 
00297   free(dir1buf);
00298   free(dir2buf);
00299 }
00300 
00301 void check(s, dp1, ent1, new)
00302 char *s;
00303 struct dirstruct *dp1;
00304 int ent1;
00305 char *new;
00306 {
00307 /* See if the file name 's' is present in the directory 'dirbuf'. */
00308   int i;
00309   char file[DIRENTLEN+1];
00310 
00311   for (i = 0; i < ent1; i++) {
00312         if (strncmp(dp1->fname, s, (size_t)DIRENTLEN) == 0) return;
00313         dp1++;
00314   }
00315   if (changes == 0) {
00316         strncpy(file, s, DIRENTLEN);
00317         file[DIRENTLEN] = '\0';
00318         printf("Missing file: %s/%s\n", new, file);
00319   }
00320         
00321 }
00322 
00323 void usage()
00324 {
00325   printf("Usage: treecmp [-cv] old_dir new_dir\n");
00326   exit(1);
00327 }

Generated on Fri Apr 14 22:57:12 2006 for minix by  doxygen 1.4.6