home *** CD-ROM | disk | FTP | other *** search
- /* DiffDir - Compare directories for differences.
- Filename: DiffDir.c
- (C)Copyright 1988 by Mark R. Rinfret, All Rights Reserved.
- This software may be freely distributed for non-profit use only.
- You are free to make changes and redistribute this program as
- long as the source is distributed and this notice is kept intact.
-
- History (most recent change first):
-
- 12/31/88 V1.0 (MRR)
- Program conception and implementation.
-
- I wrote DiffDir to assist me with configuration management. Though
- I keep all of my PD files on floppy disk, I usually roll them onto
- the hard disk when I want to make changes. Sometimes, I forget to
- copy the hard disk version back to floppy or I forget that I've already
- done it. DiffDir scans two directories and reports the following
- discrepancies:
-
- 1. File dates are different.
- 2. File protection flags are different.
- 3. File names are not exact (case discrepancy).
- 4. File sizes are different.
- 5. File comments are different.
- 6. File exists in one directory but not the other.
-
- DiffDir does not perform file content comparisons. It will, however,
- optionally generate a script for performing comparisons on files whose
- attributes differ.
-
- Usage: DiffDir [-c] [-s scriptfile] [-v] <dir1> <dir2>
- Where:
- -c specifies that letter case should be ignored when comparing
- filenames
-
- -s specifies that a file comparison script is to be output
-
- -v specifies verbose output
-
- <dir1> is the name of the first directory
-
- <dir2> is the name of the second directory
-
- */
-
- #include <stdio.h>
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <libraries/dos.h>
- #include <functions.h>
-
-
- typedef struct fileList {
- USHORT fileCount;
- char *dName; /* directory name for this list */
- struct fileNode *firstEntry, *lastEntry;
- } FileList;
-
- typedef struct fileNode {
- struct fileNode *next, *prev;
- struct fileList *parentList; /* the list that I belong to */
- char *name;
- LONG flags; /* protection, other bits */
- char *comment; /* NULL if comment was empty */
- struct DateStamp date;
- ULONG size; /* in bytes */
- BOOL isDir; /* TRUE => node is a directory */
- struct FileNode *subList; /* sublist for directory node */
- } FileNode;
-
-
- char *DupString();
- FileNode *FindFile();
- void FreeNode();
- char *MakeDirName();
- void *MyAlloc();
- void MyExit();
- void ReportStats();
- void WriteFileInfo();
-
-
- struct FileInfoBlock *fib;
- BOOL ignoreCase = FALSE;
- USHORT level = 0;
- FileList list1, list2;
- LONG maxMemUsed, memInUse;
- BOOL outputScript = FALSE;
- FILE *scriptFile;
- LONG totalFiles, totalDirs;
- BOOL verbose = FALSE;
-
- main(argc, argv)
- int argc; char **argv;
- {
- char flag;
-
- while (--argc > 0 && **++argv == '-') {
- flag = (*argv)[1];
- switch (flag) {
- case 'c':
- ignoreCase = TRUE;
- break;
- case 's':
- if (--argc) {
- ++argv;
- scriptFile = fopen(*argv, "w");
- if (!scriptFile) {
- perror("Script file would not open!");
- exit(1);
- }
- }
- else
- Usage();
- break;
- case 'v':
- verbose = TRUE;
- break;
- default:
- Usage();
- }
- }
- if (argc != 2) Usage();
- list1.dName = MakeDirName("",*argv++);
- list2.dName = MakeDirName("",*argv);
- /* fib must be longword aligned, thus the AllocMem call. */
- fib = AllocMem((long) sizeof(*fib), MEMF_PUBLIC|MEMF_CLEAR);
- if (fib == NULL) {
- printf("DiffDir: unable to allocate file info block!\n");
- goto done;
- }
-
- if (! CollectFiles(&list1))
- if (! CollectFiles(&list2))
- CompareLists(&list1, &list2);
- done:
- if (fib) FreeMem(fib, (long) sizeof(*fib));
- if (verbose) ReportStats();
- }
-
- /* FUNCTION
- AddNode - add file info node to list.
-
- SYNOPSIS
- AddNode(node, list)
- FileNode *node;
- FileList *list;
-
- DESCRIPTION
- AddNode adds the <node> to the <list>. Right now, a very lazy
- approach is taken (adds to end of list). Perhaps later, we'll
- make the list a binary tree or better.
-
- */
-
- void
- AddNode(node, list)
- FileNode *node; FileList *list;
- {
- if (list->firstEntry) { /* List has stuff in it? */
- list->lastEntry->next = node;
- }
- else {
- list->firstEntry = node; /* This is the first entry. */
- }
- node->prev = list->lastEntry;
- list->lastEntry = node;
- ++list->fileCount;
- if (node->isDir)
- ++totalDirs;
- else
- ++totalFiles;
- }
-
- /* FUNCTION
- CollectFiles - collect files for one directory level.
-
- SYNOPSIS
- int CollectFiles(list)
- FileList *list;
-
- DESCRIPTION
- CollectFiles scans the directory pointed to by <list> and creates
- list entry nodes for each file or directory found. A zero is
- returned on success, non-zero otherwise.
- */
-
- int
- CollectFiles(list)
- FileList *list;
- {
- int errCode;
- struct Lock *lock = NULL;
- FileNode *fNode;
- int result = 0;
-
- if (verbose)
- printf("DiffDir: scanning '%s'\n", list->dName);
-
- lock = (struct Lock *) Lock(list->dName, SHARED_LOCK);
- if (lock == NULL) {
- result = IoErr();
- printf("DiffDir: failed to lock '%s'!\n", list->dName);
- goto done;
- }
- if (Examine(lock, fib) == 0) {
- result = IoErr();
- printf("DiffDir: failed to get info for '%s'!\n", list->dName);
- goto done;
- }
-
- if (fib->fib_DirEntryType < 0) {
- result = -1;
- printf("DiffDir: '%s' is not a directory!\n", list->dName);
- goto done;
- }
-
- while (!result && ExNext(lock, fib)) {
- fNode = MyAlloc(sizeof(FileNode));
- fNode->parentList = list;
- fNode->name = DupString(fib->fib_FileName);
- fNode->isDir = (fib->fib_DirEntryType > 0);
- fNode->flags = fib->fib_Protection;
- if (*fib->fib_Comment)
- fNode->comment = DupString(fib->fib_Comment);
- fNode->date = fib->fib_Date;
- fNode->size = fib->fib_Size;
- AddNode(fNode, list);
- }
- errCode = IoErr();
- if (errCode != ERROR_NO_MORE_ENTRIES) {
- result = errCode;
- printf("DiffDir: scan of directory '%s' failed!\n", list->dName);
- }
- done:
- if (lock) UnLock(lock);
- return result;
- }
-
- /* FUNCTION
- CompareLists - compare files and directories in two lists.
-
- SYNOPSIS
- int CompareLists(l1, l2)
- FileList *l1, *l2;
-
- DESCRIPTION
- Comparelists first makes an overall assessment of lists <l1> and
- <l2>. If the number of files/directories in the lists differ,
- that fact is reported. Next, CompareLists tests for the condition
- where an entry in one list has the same name as an entry in the
- other list, but one entry represents a file and the other entry
- represents a directory. These entries are removed from the list.
- CompareFiles is then called to compare all file nodes, removing
- them as they are "used". Finally, CompareDirs is called to
- compare all directory nodes, again removing the nodes as they
- are used. A non-zero return indicates a fatal error.
- */
- int
- CompareLists(l1, l2)
- FileList *l1, *l2;
- {
- static char *isDirMsg = " is a directory";
- static char *isFileMsg = " is a file";
-
- FileNode *f1, *f2, *nextEntry;
- int i;
- int result = 0;
-
- ++level;
- if (verbose) {
- printf("DiffDir: comparing directory\n '%s' to '%s'\n",
- l1->dName, l2->dName);
- }
- /* Scan the lists for nodes whose names match but whose types
- differ (file vs. directory).
- */
- for (f1 = l1->firstEntry; f1; f1 = nextEntry) {
- nextEntry = f1->next;
- f2 = FindFile(f1, l2);
- if (f2 && (f2->isDir != f1->isDir) ) { /* Ooops! */
- printf("*** '%s%s' %s\n*** but '%s%s' %s!\n",
- l1->dName,f1->name, f1->isDir ? isDirMsg : isFileMsg,
- l2->dName,f2->name, f2->isDir ? isDirMsg : isFileMsg);
- FreeNode(f1, l1);
- FreeNode(f2, l2);
- }
- }
- if (! (result = CompareFiles(l1, l2)))
- result = CompareDirs(l1, l2);
- --level;
- return result;
- }
-
- /* FUNCTION
- CompareDirs - compare directory entries.
-
- SYNOPSIS
- int CompareDirs(list1, list2)
- FileList *list1, *list2;
-
- DESCRIPTION
- CompareDirs scans <list1>, attempting to match its directory node
- entries with entries in <list2>. For each matching entry found,
- CompareDirs creates a new sublist, recursively calling CompareLists
- to compare the contents of those directories. When CompareLists
- returns, CompareDirs removes the "used" directory entries from
- both lists. A non-zero return code indicates a fatal error.
- */
-
- int
- CompareDirs(list1, list2)
- FileList *list1, *list2;
- {
- static char *missing = "*** Directory missing: '%s%s'\n";
-
- FileNode *n1, *n2, *nextEntry;
- int result = 0;
- FileList *subList1, *subList2;
-
- for (n1 = list1->firstEntry; n1 && !result; n1 = nextEntry) {
- nextEntry = n1->next;
- /* Note: there should only be directory nodes in the list
- at this point!
- */
- if (! n1->isDir) {
- puts("DiffDir: non-directory node found in CompareDirs!");
- MyExit(); /* Dis be real bad! */
- }
- n2 = FindFile(n1, list2);
- if (n2 == NULL) {
- printf(missing, list2->dName, n1->name);
- }
- else {
- subList1 = MyAlloc( sizeof(FileList) );
- subList1->dName = MakeDirName(list1->dName, n1->name);
- subList2 = MyAlloc( sizeof(FileList) );
- subList2->dName = MakeDirName(list2->dName, n2->name);
- result = CollectFiles(subList1);
- if (!result)
- result = CollectFiles(subList2);
- if (!result)
- result = CompareLists(subList1, subList2);
-
- /* Give back the memories :-) */
- free(subList1->dName);
- free(subList1);
- free(subList2->dName);
- free(subList2);
- }
- FreeNode(n1, list1);
- if (n2) FreeNode(n2, list2);
- }
- if (!result) {
- for (n2 = list2->firstEntry; n2; n2 = nextEntry) {
- nextEntry = n2->next;
- printf(missing, list1->dName, n2->name);
- FreeNode(n2, list2);
- }
- }
- return result;
- }
-
- /* FUNCTION
- CompareFile - compare the attributes of two similar files.
-
- SYNOPSIS
- void CompareFile(f1, f2)
- FileNode *f1, *f2;
-
- DESCRIPTION
- CompareFile is called with two file description nodes, <f1> and
- <f2> which are expected to represent similar files in different
- directory hierarchies. CompareFile will report any discrepancies
- to standard output.
- */
- void
- CompareFile(f1, f2)
- FileNode *f1, *f2;
- {
-
- #define NAMES_DONT_MATCH 1
- #define DATES_DONT_MATCH 2
- #define FLAGS_DONT_MATCH 4
- #define SIZES_DONT_MATCH 8
- #define COMMENTS_DONT_MATCH 16
-
- #define ITEM_COUNT 5 /* Make sure this tracks the list above! */
-
- static char *errorDesc[ITEM_COUNT] = {
- " names ", " dates ", " flags ", " sizes ", " comments " };
-
- USHORT error = 0, item, mask;
-
- if (f1->isDir != f2->isDir) {
- puts("*** File type mismatch (file vs. dir) - program error!");
- FreeNode(f1, f1->parentList);
- FreeNode(f2, f2->parentList);
- }
- else {
- if (f1->flags != f2->flags)
- error |= FLAGS_DONT_MATCH;
-
- if (CompareDS(&f1->date, &f2->date))
- error |= DATES_DONT_MATCH;
-
- if (!ignoreCase) {
- if (strcmp(f1->name, f2->name) != 0)
- error |= NAMES_DONT_MATCH;
- }
-
- if (f1->size != f2->size) {
- error |= SIZES_DONT_MATCH;
- if (scriptFile)
- fprintf(scriptFile,"%%COMPARE%% %s%s %s%s\n",
- f1->parentList->dName,f1->name,
- f2->parentList->dName,f2->name);
- }
- if (strcmp(f1->comment, f2->comment) != 0)
- error |= COMMENTS_DONT_MATCH;
- }
- if (error) { /* Aw, darn... */
- printf("*** Mismatch: ");
- for (item = 0, mask = 1; item < ITEM_COUNT;
- ++item, mask= (mask << 1)) {
- if (error & mask)
- printf(errorDesc[item]);
- }
- puts("");
- puts(f1->parentList->dName);
- WriteFileInfo(f1);
- puts("------------------------------------");
- puts(f2->parentList->dName);
- WriteFileInfo(f2);
- puts("====================================");
- }
- }
-
- /* FUNCTION
- CompareFiles - compare all file nodes in two lists.
-
- SYNOPSIS
- int CompareFiles(l1, l2)
- FileList *l1, *l2;
-
- DESCRIPTION
- The file attributes for all files in list <l1> are compared to
- those in list <l2>. Discrepancies are reported to standard
- output. After all the files in <l1> have been tested, a second
- scan is made over list <l2> for any remaining file nodes. These
- represent files which were not found in <l1>. Upon return, all
- file nodes will have been removed from lists <l1> and <l2>,
- leaving behind any directory nodes for CompareDirs().
- */
-
- int
- CompareFiles(l1, l2)
- FileList *l1, *l2;
- {
- static char *missing = "*** File missing: '%s%s'\n";
- FileNode *f1, *f2;
-
- /* Loop through all file entries in list1. */
- for (f1 = l1->firstEntry; f1; f1 = f1->next) {
- if (f1->isDir) continue;
- f2 = FindFile(f1, l2);
- if (f2 == NULL) {
- printf(missing, l2->dName, f1->name);
- }
- else {
- CompareFile(f1, f2);
- }
- FreeNode(f1, l1);
- if (f2)
- FreeNode(f2, l2);
- }
-
- /* Look for "leftovers" in list 2. */
- for (f2 = l2->firstEntry; f2; f2 = f2->next) {
- if (f2->isDir) continue;
- printf(missing, l1->dName, f2->name);
- FreeNode(f2, l2);
- }
- return 0;
- }
-
- /* FUNCTION
- DupString - duplicate a string.
-
- SYNOPSIS
- char *DupString(oldString)
- char *oldString;
-
- DESCRIPTION
- DupString dynamically allocates space for a new copy of <oldString>,
- copies <oldString> to the new area and returns a pointer to the new
- string.
-
- */
-
- char *
- DupString(oldString)
- char *oldString;
- {
- char *newString;
-
- newString = MyAlloc(strlen(oldString)+1);
- strcpy(newString, oldString);
- return newString;
- }
-
- /* FUNCTION
- FindFile - find a file node by name.
-
- SYNOPSIS
- FileNode *FindFile(node, list)
- FileNode *node;
- FileList *list;
-
- DESCRIPTION
- FindFile searches <list> for a file description node whose name
- matches the name in <node>. A case-insensitive name comparison
- is performed. If the matching entry is found, a pointer to it
- is returned. Otherwise, NULL is returned.
- */
-
- FileNode *
- FindFile(node, list)
- FileNode *node; FileList *list;
- {
- FileNode *tNode;
-
- for (tNode = list->firstEntry; tNode; tNode = tNode->next) {
- if (stricmp(node->name, tNode->name) == 0)
- return tNode;
- }
- return NULL; /* Sorry...not found. */
- }
-
- /* FUNCTION
- FreeNode - free a file node from a list.
-
- SYNOPSIS
- void FreeNode(node, list)
- FileNode *node;
- FileList *list;
- */
- void
- FreeNode(node, list)
- FileNode *node; FileList *list;
- {
- if (node->prev)
- node->prev->next = node->next;
- if (node->next)
- node->next->prev = node->prev;
- if (node == list->firstEntry)
- list->firstEntry = node->next;
- if (node == list->lastEntry)
- list->lastEntry = node->prev;
-
- free(node->name);
- free(node->comment);
- free(node);
- }
-
- /* FUNCTION
- MakeDirName - assemble a directory name from components.
-
- SYNOPSIS
- char *MakeDirName(s1, s2)
- char *s1, *s2;
-
- DESCRIPTION
- MakeDirName dynamically allocates a string large enough to hold
- a composite name formed from strings <s1> and <s2>. It also adds
- a directory separator (/) to the end of the new name if the
- new result does not end in a colon (:). The new name is returned
- as the function result.
- */
- char *
- MakeDirName(s1, s2)
- char *s1, *s2;
- {
- char *index();
-
- char *dirName;
-
- dirName = MyAlloc(strlen(s1)+strlen(s2)+2);
- strcpy(dirName, s1);
- strcat(dirName, s2);
- if (dirName[strlen(dirName)-1] != ':') strcat(dirName, "/");
- return dirName;
- }
-
- /* FUNCTION
- MyAlloc - perform memory allocation with error checking.
-
- SYNOPSIS
- void *MyAlloc(size)
- USHORT size;
-
- DESCRIPTION
- MyAlloc attempts to allocate <size> bytes of memory. If it fails,
- an error message is sent to standard output and the program is
- terminated. Otherwise, MyAlloc returns a pointer to the newly
- allocated (zero-filled) memory block.
- */
- void *
- MyAlloc(size)
- USHORT size;
- {
- void *calloc();
-
- void *ptr;
-
- ptr = calloc(size, 1);
- if (ptr == NULL) {
- printf("DiffDir: failed to allocate %ld bytes!\n", size);
- MyExit();
- }
- memInUse += size;
- if (memInUse > maxMemUsed) maxMemUsed = memInUse;
- return ptr;
- }
-
- /* FUNCTION
- MyExit - terminate program with cleanup.
-
- SYNOPSIS
- void MyExit();
-
- DESCRIPTION
- MyExit simply provides a graceful way for the program to exit,
- performing any necessary cleanup chores.
- */
- void
- MyExit()
- {
- if (fib) FreeMem(fib, (long) sizeof(*fib));
- puts("DiffDir: abnormal exit!");
- ReportStats();
- exit(1);
- }
- /* FUNCTION
- ReportStats - report program statistics.
-
- SYNOPSIS
- void ReportStats();
-
- DESCRIPTION
- ReportMem reports the maximum memory used, total number of file
- nodes and total number of directory nodes for this invocation
- of DiffDir, ONLY if the verbose option is turned on or if the
- program terminates abnormally.
- */
- void
- ReportStats()
- {
- printf("DiffDir: Files: %ld; directories: %ld; max memory: %ld bytes\n",
- totalFiles, totalDirs, maxMemUsed);
- }
-
-
- /* FUNCTION
- stricmp - perform a case-insensitive string compare.
-
- SYNOPSIS
- int stricmp(s1, s2)
- char *s1, *s2;
-
- DESCRIPTION
- Strings <s1> and <s2> are compared, ignoring differences in case.
- A result code is returned according to the following:
- 0 => strings match
- <0 => s1 < s2
- >0 => s1 > s2
- */
-
- int
- stricmp(s1, s2)
- register char *s1, *s2;
- {
- int c1, c2, cd;
-
- do {
- c1 = tolower(*s1++);
- c2 = tolower(*s2++);
- if (cd = (c1 - c2)) break;
- } while (c1 && c2);
-
- return cd;
- }
-
- /* FUNCTION
- Usage - describe program usage and exit.
-
- SYNOPSIS
- void Usage();
-
- DESCRIPTION
- Usage is called when the user invokes DiffDir with incorrect
- or insufficient parameters. The correct invocation syntax
- is displayed and the program is terminated.
- */
- Usage()
- {
- puts("Usage: DiffDir [-c] [-s scriptfile] dirname1 dirname2");
- MyExit();
- }
-
- /* FUNCTION
- WriteFileInfo - write a full file description to standard output.
-
- SYNOPSIS
- void WriteFileInfo(node)
- FileNode *node;
-
- DESCRIPTION
- WriteFileInfo writes complete info about the file specified by
- <node> to the standard output. This only happens when an error
- occurs.
-
- */
-
- void
- WriteFileInfo(node)
- FileNode *node;
-
- {
- static char flagSetNames[9] = {
- '-', '-', '-', '-', 'a', 'p', 's', '?', '?'
- };
- static char flagClearNames[9] = {
- 'd', 'e', 'w', 'r', '-', '-', '-', '-', '-'
- };
-
- ULONG flags;
- SHORT i;
- ULONG mask;
- char temp[30];
-
- DSToStr(temp,"%02m-%02d-%02y %02h:%02n:%02s ",&node->date);
- printf(temp);
- flags = node->flags;
- for (i = 0, mask = 1; i < 9; ++i, mask = (mask << 1) )
- if (flags & mask)
- temp[8 - i] = flagSetNames[i];
- else
- temp[8 - i] = flagClearNames[i];
-
- temp[9] = '\0';
-
- printf("%s %8ld %s\n", temp, node->size, node->name);
- if (node->comment)
- printf(": %s\n",node->comment);
- }
-
-
-