amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (02/19/91)
Submitted-by: mrr@mrsoft.Newport.RI.US (Mark Rinfret) Posting-number: Volume 91, Issue 013 Archive-name: utilities/diffdir-2.0/part01 This is DiffDir Version 2.0, an AmigaDOS directory comparison utility. DiffDir will scan two directories and generate a report describing all differences encountered. In addition, DiffDir will optionally do a binary file compare on files which otherwise seem to be the same. A user-customizable file comparison script can optionally be generated, inserting the user's favorite file-comparison command. Source, documentation, and a two test directory hierarchies are included. #!/bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 1)." # Contents: DiffDir.c MRDates.c MRDates.h dir1 dir1/File1 dir1/File3 # dir1/Level2 dir1/Level2/File1 dir1/Level2/File3 dir1/Level2/Level3 # dir1/Level2/Level3/File1 dir1/Level2/Level3/File2 # dir1/Level2/Level3/File3 dir1/file2 dir2 dir2/DifferentType # dir2/File1 dir2/File2 dir2/Level2 dir2/Level2/File1 # dir2/Level2/File2 dir2/Level2/File3 dir2/Level2/Level3 # dir2/Level2/Level3/File2 dir2/Level2/Level3/File3 dir2/file3 # makefile # Wrapped by tadguy@ab20 on Mon Feb 18 15:58:36 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'DiffDir.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'DiffDir.c'\" else echo shar: Extracting \"'DiffDir.c'\" \(27265 characters\) sed "s/^X//" >'DiffDir.c' <<'END_OF_FILE' X/* DiffDir - Compare directories for differences. X Filename: DiffDir.c X X (C)Copyright 1988-1991 by Mark R. Rinfret, All Rights Reserved. X This software may be freely distributed for non-profit use only. X You are free to make changes and redistribute this program as X long as the source is distributed and this notice is kept intact. X X History (most recent change first): X X 02/15/91 V2.0 (MRR) X Oh, what the hey! Let's really change things! There are lots of X internal as well as external differences. Read the docs :-). X X 02/09/91 V1.2 (MRR) X Added -b flag (binary file compare) X X 07/04/89 V1.1 (MRR) X Added flags for specific tests. X X 12/31/88 V1.0 (MRR) X Program conception and implementation. X X I wrote DiffDir to assist me with configuration management. Though X I keep all of my PD files on floppy disk, I usually roll them onto X the hard disk when I want to make changes. Sometimes, I forget to X copy the hard disk version back to floppy or I forget that I've already X done it. DiffDir scans two directories and reports the following X discrepancies: X X 1. File dates are different. X 2. File protection flags are different. X 3. File names are not exact (case discrepancy). X 4. File sizes are different. X 5. File comments are different. X 6. File exists in one directory but not the other. X 7. File contents differ. X X See the Usage() function for command line options. X X*/ X X#include <stdio.h> X#include <stdlib.h> X#include <string.h> X#include <ctype.h> X#include <fcntl.h> X#include <exec/types.h> X#include <exec/memory.h> X#include <libraries/dos.h> X#include <functions.h> X#include "MRDates.h" X Xchar *version = "DiffDir 2.0, Mark R. Rinfret, 02/15/91\n"; X Xtypedef struct fileList { X USHORT fileCount; X char *dName; /* directory name for this list */ X struct fileNode *firstEntry, *lastEntry; X } FileList; X Xtypedef struct fileNode { X struct fileNode *next, *prev; X struct fileList *parentList; /* the list that I belong to */ X char *name; X LONG flags; /* protection, other bits */ X char *comment; /* NULL if comment was empty */ X struct DateStamp date; X ULONG size; /* in bytes */ X BOOL isDir; /* TRUE => node is a directory */ X struct FileNode *subList; /* sublist for directory node */ X } FileNode; X X#define NAMES_DONT_MATCH 1 X#define DATES_DONT_MATCH 2 X#define FLAGS_DONT_MATCH 4 X#define SIZES_DONT_MATCH 8 X#define COMMENTS_DONT_MATCH 16 X X#define ITEM_COUNT 5 /* Make sure this tracks the list above! */ X Xstatic char *errorDesc[ITEM_COUNT] = { X " names ", " dates ", " flags ", " sizes ", " comments " }; X X/* The scriptCmd is inserted into the command string output to the optional X * comparison script. It may be over-ridden by the user. X */ Xchar scriptCmd[128] = "cmp"; X Xvoid AddNode(FileNode *node, FileList *list); Xint CollectFiles(FileList *list); Xint CompareDirs(FileList *list1, FileList *list2); Xvoid CompareFileContents(FileNode *f1, FileNode *f2); Xint CompareFiles(FileList *l1, FileList *l2); Xint CompareLists(FileList *l1, FileList *l2); Xchar *DupString(const char *oldString); XFileNode *FindFile(FileNode *node, FileList *list); Xvoid FreeNode(FileNode *node, FileList *list); Xchar *MakeDirName(const char *s1, const char *s2); Xvoid *MyAlloc(size_t size); Xvoid MyExit(int code); Xvoid MyFree(void *ptr); Xint OpenFile(FileNode *f); Xvoid ReportStats(void); Xint stricmp(const char *s1, const char *s2); Xvoid Usage(void); Xvoid WriteFileInfo(FileNode *node); X X Xint checkContents = TRUE; Xstruct FileInfoBlock *fib; XBOOL ignoreCase = FALSE; XUSHORT level = 0; XFileList list1, list2; XLONG maxMemUsed, memInUse; XBOOL outputScript = FALSE; XFILE *scriptFile; XULONG testFlags = 0; XLONG totalFiles, totalDirs; XBOOL verbose = FALSE; X Xmain(argc, argv) X int argc; char **argv; X{ X int leng; X char *ptr; X X testFlags = 0xFFFFFFFF; X X while (--argc > 0 && **++argv == '-') { X ptr = &argv[0][1]; X leng = strlen(ptr); X if (leng < 5) { X Usage(); X } X if (strncmp("nocontents", ptr, leng) == 0) { X /* Compare file contents. */ X checkContents = FALSE; X } else if (strncmp(ptr, "nocomments", leng) == 0) { X /* Ignore comments. */ X testFlags &= (~COMMENTS_DONT_MATCH); X } else if (strncmp(ptr, "nodates", leng) == 0) { X /* Suppress date test. */ X testFlags &= (~DATES_DONT_MATCH); X } else if (strncmp(ptr, "noflags", leng) == 0) { X /* Ignore flags (protection word). */ X testFlags &= (~FLAGS_DONT_MATCH); X } else if (strncmp(ptr, "nosizes", leng) == 0) { X testFlags &= (~SIZES_DONT_MATCH); X } else if (strncmp(ptr, "nocase", leng) == 0) { X /* Ignore filename case. */ X ignoreCase = TRUE; X } else if (strncmp(ptr, "script", leng) == 0) { X if (--argc) { X ++argv; X scriptFile = fopen(*argv, "w"); X if (!scriptFile) { X perror("Script file would not open!"); X exit(20); X } X } X } else if (strncmp(ptr, "command", leng) == 0) { X if (--argc) { X ++argv; X strcpy(scriptCmd, *argv); X } X else X Usage(); X } else if (strncmp(ptr, "verbose", leng) == 0) { X verbose = TRUE; X } else { X Usage(); X } X } X if (argc != 2) Usage(); X list1.dName = MakeDirName("",*argv++); X list2.dName = MakeDirName("",*argv); X fib = MyAlloc(sizeof(*fib)); X if (fib == NULL) { X printf("DiffDir: unable to allocate file info block!\n"); X goto done; X } X X if (! CollectFiles(&list1)) X if (! CollectFiles(&list2)) X CompareLists(&list1, &list2); Xdone: X if (fib) MyFree(fib); X if (verbose) ReportStats(); X} X X/* FUNCTION X AddNode - add file info node to list. X X SYNOPSIS X AddNode(FileNode *node, FileList *list); X X DESCRIPTION X AddNode adds the <node> to the <list>. Right now, a very lazy X approach is taken (adds to end of list). Perhaps later, we'll X make the list a binary tree or better. X X*/ X Xvoid XAddNode(FileNode *node, FileList *list) X{ X if (list->firstEntry) { /* List has stuff in it? */ X list->lastEntry->next = node; X } X else { X list->firstEntry = node; /* This is the first entry. */ X } X node->prev = list->lastEntry; X list->lastEntry = node; X ++list->fileCount; X if (node->isDir) X ++totalDirs; X else X ++totalFiles; X} X X/* FUNCTION X CollectFiles - collect files for one directory level. X X SYNOPSIS X int CollectFiles(FileList *list); X X DESCRIPTION X CollectFiles scans the directory pointed to by <list> and creates X list entry nodes for each file or directory found. A zero is X returned on success, non-zero otherwise. X*/ X Xint XCollectFiles(FileList *list) X{ X int errCode; X BPTR lock = 0; X FileNode *fNode = NULL; X int result = 0; X X if (verbose) X printf("DiffDir: scanning '%s'\n", list->dName); X X lock = Lock(list->dName, SHARED_LOCK); X if (lock == 0) { X result = IoErr(); X printf("DiffDir: failed to lock '%s'!\n", list->dName); X goto done; X } X if (Examine(lock, fib) == 0) { X result = IoErr(); X printf("DiffDir: failed to get info for '%s'!\n", list->dName); X goto done; X } X X if (fib->fib_DirEntryType < 0) { X result = -1; X printf("DiffDir: '%s' is not a directory!\n", list->dName); X goto done; X } X X while (!result && ExNext(lock, fib)) { X fNode = MyAlloc(sizeof(FileNode)); X fNode->parentList = list; X fNode->name = DupString(fib->fib_FileName); X fNode->isDir = (fib->fib_DirEntryType > 0); X fNode->flags = fib->fib_Protection; X if (*fib->fib_Comment) X fNode->comment = DupString(fib->fib_Comment); X fNode->date = fib->fib_Date; X fNode->size = fib->fib_Size; X AddNode(fNode, list); X } X errCode = IoErr(); X if (errCode != ERROR_NO_MORE_ENTRIES) { X result = errCode; X printf("DiffDir: scan of directory '%s' failed!\n", list->dName); X } Xdone: X if (lock) UnLock(lock); X return result; X} X X/* FUNCTION X CompareLists - compare files and directories in two lists. X X SYNOPSIS X int CompareLists(FileList *l1, FileList *l2); X X DESCRIPTION X Comparelists first makes an overall assessment of lists <l1> and X <l2>. If the number of files/directories in the lists differ, X that fact is reported. Next, CompareLists tests for the condition X where an entry in one list has the same name as an entry in the X other list, but one entry represents a file and the other entry X represents a directory. These entries are removed from the list. X CompareFiles is then called to compare all file nodes, removing X them as they are "used". Finally, CompareDirs is called to X compare all directory nodes, again removing the nodes as they X are used. A non-zero return indicates a fatal error. X*/ Xint XCompareLists(FileList *l1, FileList *l2) X{ Xstatic char *isDirMsg = " is a directory"; Xstatic char *isFileMsg = " is a file"; X X FileNode *f1, *f2, *nextEntry; X int result = 0; X X ++level; X if (verbose) { X printf("DiffDir: comparing directory\n '%s' to '%s'\n", X l1->dName, l2->dName); X } X /* Scan the lists for nodes whose names match but whose types X differ (file vs. directory). X */ X for (f1 = l1->firstEntry; f1; f1 = nextEntry) { X nextEntry = f1->next; X f2 = FindFile(f1, l2); X if (f2 && (f2->isDir != f1->isDir) ) { /* Ooops! */ X printf("*** '%s%s' %s\n*** but '%s%s' %s!\n", X l1->dName,f1->name, f1->isDir ? isDirMsg : isFileMsg, X l2->dName,f2->name, f2->isDir ? isDirMsg : isFileMsg); X FreeNode(f1, l1); X FreeNode(f2, l2); X } X } X if (! (result = CompareFiles(l1, l2))) X result = CompareDirs(l1, l2); X --level; X return result; X} X X/* FUNCTION X CompareDirs - compare directory entries. X X SYNOPSIS X int CompareDirs(FileList *list1, FileList *list2); X X DESCRIPTION X CompareDirs scans <list1>, attempting to match its directory node X entries with entries in <list2>. For each matching entry found, X CompareDirs creates a new sublist, recursively calling CompareLists X to compare the contents of those directories. When CompareLists X returns, CompareDirs removes the "used" directory entries from X both lists. A non-zero return code indicates a fatal error. X*/ X Xint XCompareDirs(FileList *list1, FileList *list2) X{ Xstatic char *missing = "*** Directory missing: '%s%s'\n"; X X FileNode *n1, *n2, *nextEntry; X int result = 0; X FileList *subList1, *subList2; X X for (n1 = list1->firstEntry; n1 && !result; n1 = nextEntry) { X nextEntry = n1->next; X /* Note: there should only be directory nodes in the list X at this point! X */ X if (! n1->isDir) { X puts("DiffDir: non-directory node found in CompareDirs!"); X MyExit(20); /* What else can we do? */ X } X n2 = FindFile(n1, list2); X if (n2 == NULL) { X printf(missing, list2->dName, n1->name); X } X else { X subList1 = MyAlloc( sizeof(FileList) ); X subList1->dName = MakeDirName(list1->dName, n1->name); X subList2 = MyAlloc( sizeof(FileList) ); X subList2->dName = MakeDirName(list2->dName, n2->name); X result = CollectFiles(subList1); X if (!result) X result = CollectFiles(subList2); X if (!result) X result = CompareLists(subList1, subList2); X X /* Give back the memories :-) */ X MyFree(subList1->dName); X MyFree(subList1); X MyFree(subList2->dName); X MyFree(subList2); X } X FreeNode(n1, list1); X if (n2) FreeNode(n2, list2); X } X if (!result) { X for (n2 = list2->firstEntry; n2; n2 = nextEntry) { X nextEntry = n2->next; X printf(missing, list1->dName, n2->name); X FreeNode(n2, list2); X } X } X return result; X} X X/* FUNCTION X CompareFile - compare the attributes of two similar files. X X SYNOPSIS X void CompareFile(FileNode *f1, FileNode *f2); X X DESCRIPTION X CompareFile is called with two file description nodes, <f1> and X <f2> which are expected to represent similar files in different X directory hierarchies. CompareFile compares the currently selected X file attributes and will report any discrepancies to standard output. X*/ Xvoid XCompareFile(FileNode *f1, FileNode *f2) X{ X USHORT error = 0, item, mask; X X if (f1->isDir != f2->isDir) { X puts("*** File type mismatch (file vs. dir) - program error!"); X FreeNode(f1, f1->parentList); X FreeNode(f2, f2->parentList); X } X else { X if ((testFlags & FLAGS_DONT_MATCH) && (f1->flags != f2->flags) ) X error |= FLAGS_DONT_MATCH; X X if ((testFlags & DATES_DONT_MATCH) && X (CompareDS( (long *) &f1->date, (long *) &f2->date) ) ) X error |= DATES_DONT_MATCH; X X if (!ignoreCase) { X if (strcmp(f1->name, f2->name) != 0) X error |= NAMES_DONT_MATCH; X } X X if ( (testFlags & SIZES_DONT_MATCH) && (f1->size != f2->size) ) { X error |= SIZES_DONT_MATCH; X } X X /* Do we want to compare all files? */ X X if (scriptFile) { X fprintf(scriptFile, "%s %s%s %s%s\n", X scriptCmd, X f1->parentList->dName,f1->name, X f2->parentList->dName,f2->name); X } X X if ((testFlags & COMMENTS_DONT_MATCH) && X (strcmp(f1->comment, f2->comment) != 0) ) X error |= COMMENTS_DONT_MATCH; X } X X if (error) { /* Aw, darn... */ X printf("*** Mismatch: "); X for (item = 0, mask = 1; item < ITEM_COUNT; X ++item, mask= (mask << 1)) { X if (error & mask) X printf(errorDesc[item]); X } X puts(""); X puts(f1->parentList->dName); X WriteFileInfo(f1); X puts("------------------------------------"); X puts(f2->parentList->dName); X WriteFileInfo(f2); X puts("===================================="); X } X if (checkContents && (f1->size == f2->size) ) { X CompareFileContents(f1, f2); X } X} X X X/* FUNCTION X * CompareFileContents - do a binary comparison of two files. X * X * SYNOPSIS X * void CompareFileContents(FileNode *f1, FileNode *f2); X * X * DESCRIPTION X * This function performs a byte-by-byte comparison of two files, X * terminating with an error message on the first mismatch. X */ Xvoid XCompareFileContents(FileNode *f1, FileNode *f2) X{ X#define BUFFER_SIZE 32768 X X static char *readErr = "Read error on file '%s%s'\n"; X X int fd1 = -1, fd2 = -1; X char *buf1 = NULL, *buf2 = NULL; X size_t i; X size_t leng1, leng2; X X buf1 = MyAlloc(BUFFER_SIZE); X buf2 = MyAlloc(BUFFER_SIZE); X X fd1 = OpenFile(f1); X if (fd1 == -1) goto done; X X fd2 = OpenFile(f2); X if (fd2 == -1) goto done; X X while (1) { X leng1 = read(fd1, buf1, BUFFER_SIZE); X leng2 = read(fd2, buf2, BUFFER_SIZE); X if (leng1 == -1) { X printf(readErr,f1->parentList->dName,f1->name); X goto done; X } X if (leng2 == -1) { X printf(readErr,f2->parentList->dName, f2->name); X goto done; X } X if (leng1 != leng2) { X printf("File length mismatch: '%s%s' vs. '%s%s'\n", X f1->parentList->dName,f1->name, X f2->parentList->dName,f2->name); X } X for (i = 0; i < leng1; ++i) { X if (buf1[i] != buf2[i]) { X printf( X "*** File data mismatch at byte %d:\n '%s%s' vs. '%s%s'\n", X i, X f1->parentList->dName,f1->name, X f2->parentList->dName,f2->name); X goto done; X } X } X if (leng1 < BUFFER_SIZE) break; /* End of file? */ X } X Xdone: X if (buf1) MyFree(buf1); X if (buf2) MyFree(buf2); X if (fd1 != -1) close(fd1); X if (fd2 != -1) close(fd2); X} X X/* FUNCTION X CompareFiles - compare all file nodes in two lists. X X SYNOPSIS X int CompareFiles(FileList *l1, FileList *l2); X X DESCRIPTION X The file attributes for all files in list <l1> are compared to X those in list <l2>. Discrepancies are reported to standard X output. After all the files in <l1> have been tested, a second X scan is made over list <l2> for any remaining file nodes. These X represent files which were not found in <l1>. Upon return, all X file nodes will have been removed from lists <l1> and <l2>, X leaving behind any directory nodes for CompareDirs(). X*/ X Xint XCompareFiles(FileList *l1, FileList *l2) X{ X static char *missing = "*** File missing: '%s%s'\n"; X FileNode *f1, *f2, *nextNode; X X /* Loop through all file entries in list1. Since the list may be X * modified along the way, save a look-ahead pointer to the next X * entry. X */ X X for (f1 = l1->firstEntry; f1; f1 = nextNode) { X nextNode = f1->next; X if (f1->isDir) continue; X f2 = FindFile(f1, l2); X if (f2 == NULL) { X printf(missing, l2->dName, f1->name); X } X else { X CompareFile(f1, f2); X } X FreeNode(f1, l1); X if (f2) X FreeNode(f2, l2); X } X X /* Look for "leftovers" in list 2. */ X X for (f2 = l2->firstEntry; f2; f2 = nextNode) { X nextNode = f2->next; X if (f2->isDir) continue; X printf(missing, l1->dName, f2->name); X FreeNode(f2, l2); X } X return 0; X} X X/* FUNCTION X DupString - duplicate a string. X X SYNOPSIS X char *DupString(const char *oldString); X X DESCRIPTION X DupString dynamically allocates space for a new copy of <oldString>, X copies <oldString> to the new area and returns a pointer to the new X string. X X*/ X Xchar * XDupString(const char *oldString) X{ X char *newString; X X newString = MyAlloc(strlen(oldString)+1); X strcpy(newString, oldString); X return newString; X} X X/* FUNCTION X FindFile - find a file node by name. X X SYNOPSIS X FileNode *FindFile(FileNode *node, FileNode *list) X X DESCRIPTION X FindFile searches <list> for a file description node whose name X matches the name in <node>. A case-insensitive name comparison X is performed. If the matching entry is found, a pointer to it X is returned. Otherwise, NULL is returned. X*/ X XFileNode * XFindFile(FileNode *node, FileList *list) X{ X FileNode *tNode; X X for (tNode = list->firstEntry; tNode; tNode = tNode->next) { X if (stricmp(node->name, tNode->name) == 0) X return tNode; X } X return NULL; /* Sorry...not found. */ X} X X/* FUNCTION X FreeNode - free a file node from a list. X X SYNOPSIS X void FreeNode(node, list) X FileNode *node; X FileList *list; X*/ Xvoid XFreeNode(FileNode *node, FileList *list) X{ X if (node->prev) X node->prev->next = node->next; X if (node->next) X node->next->prev = node->prev; X if (node == list->firstEntry) X list->firstEntry = node->next; X if (node == list->lastEntry) X list->lastEntry = node->prev; X X MyFree(node->name); X MyFree(node->comment); X MyFree(node); X} X X/* FUNCTION X MakeDirName - assemble a directory name from components. X X SYNOPSIS X char *MakeDirName(const char *s1, const char *s2); X X DESCRIPTION X MakeDirName dynamically allocates a string large enough to hold X a composite name formed from strings <s1> and <s2>. It also adds X a directory separator (/) to the end of the new name if the X new result does not end in a colon (:). The new name is returned X as the function result. X*/ Xchar * XMakeDirName(const char *s1, const char *s2) X{ X char *dirName; X X dirName = MyAlloc(strlen(s1)+strlen(s2)+2); X strcpy(dirName, s1); X strcat(dirName, s2); X if (dirName[strlen(dirName)-1] != ':') strcat(dirName, "/"); X return dirName; X} X X/* FUNCTION X MyAlloc - perform memory allocation with error checking. X X SYNOPSIS X void *MyAlloc(size_t size); X X DESCRIPTION X MyAlloc attempts to allocate <size> bytes of memory. If it fails, X an error message is sent to standard output and the program is X terminated. Otherwise, MyAlloc returns a pointer to the newly X allocated (zero-filled) memory block. X*/ Xvoid * XMyAlloc(size_t size) X{ X X long *ptr; X X size += 4; /* Add word for size tracking. */ X ptr = AllocMem(size, MEMF_CLEAR | MEMF_PUBLIC); X if (ptr == NULL) { X printf("DiffDir: failed to allocate %ld bytes!\n", size); X MyExit(20); X } X *ptr = size; /* Store size. */ X ++ptr; /* Advance pointer to data block. */ X memInUse += size; X if (memInUse > maxMemUsed) maxMemUsed = memInUse; X return (void *) ptr; X} X X/* FUNCTION X MyExit - terminate program with cleanup. X X SYNOPSIS X void MyExit(int code); X X DESCRIPTION X MyExit simply provides a graceful way for the program to exit, X performing any necessary cleanup chores. X*/ Xvoid XMyExit(int code) X{ X if (fib) MyFree(fib); X ReportStats(); X exit(code); X} X X X/* FUNCTION X * MyFree - free memory block, tracking memory usage. X * X * SYNOPSIS X * void MyFree(void *); X * X * DESCRIPTION X * MyFree releases a block of memory allocated by MyAlloc. It X * also safeguards against null pointers. X */ X Xvoid MyFree(void * ptr) X{ X long size; X long *xptr; X X if (ptr) { /* Ignore null pointers. */ X xptr = (long *)ptr; X --xptr; X size = *xptr; X FreeMem(xptr, size); X memInUse -= size; X } X} X X/* FUNCTION X * OpenFile - open a file for read-only access. X * X * SYNOPSIS X * int OpenFile(FileNode *f); X * X * DESCRIPTION X * OpenFile opens a file for non-buffered I/O. It returns a file X * descriptor which is -1 if the file fails to open X */ X Xint XOpenFile(FileNode *f) X{ X int fd; X char fileName[256]; X X strcpy(fileName, f->parentList->dName); X strcat(fileName, f->name); X fd = open(fileName, O_RDONLY); X if (fd == -1) { X printf("** Failed to open file: '%s'\n", fileName); X } X return fd; X} X X/* FUNCTION X ReportStats - report program statistics. X X SYNOPSIS X void ReportStats(void); X X DESCRIPTION X ReportMem reports the maximum memory used, total number of file X nodes and total number of directory nodes for this invocation X of DiffDir, ONLY if the verbose option is turned on or if the X program terminates abnormally. X*/ Xvoid XReportStats(void) X{ X printf("DiffDir: Files: %ld; directories: %ld; max memory usage: %ld bytes\n", X totalFiles, totalDirs, maxMemUsed); X} X X X/* FUNCTION X stricmp - perform a case-insensitive string compare. X X SYNOPSIS X int stricmp(const char *s1, const char *s2); X X DESCRIPTION X Strings <s1> and <s2> are compared, ignoring differences in case. X A result code is returned according to the following: X 0 => strings match X <0 => s1 < s2 X >0 => s1 > s2 X*/ X Xint Xstricmp(const char *s1, const char *s2) X{ X int c1, c2, cd; X X do { X c1 = tolower(*s1++); X c2 = tolower(*s2++); X if (cd = (c1 - c2)) break; X } while (c1 && c2); X X return cd; X} X X/* FUNCTION X Usage - describe program usage and exit. X X SYNOPSIS X void Usage(void); X X DESCRIPTION X Usage is called when the user invokes DiffDir with incorrect X or insufficient parameters. The correct invocation syntax X is displayed and the program is terminated. X*/ Xvoid XUsage(void) X{ X puts(version); X puts( X"Usage: DiffDir [ options ] dir1 dir2\n" X" where options may be:\n" X" -nocase ignore filename case differences\n" X" -nocomments ignore comment (filenote) differences\n" X" -nocontents don't compare file contents when sizes are equal\n" X" -nodates ignore creation/modification date differences\n" X" -noflags ignore flag (protection word) differences\n" X" -nosizes ignore file sizes (dumb, but if you need to...)\n" X" -script <scriptfile> generate a comparison script (name = scriptfile)\n" X" -command <string> use <string> to create script commands\n" X" -verbose generate verbose output\n" X "\n"); X X MyExit(20); X} X X/* FUNCTION X WriteFileInfo - write a full file description to standard output. X X SYNOPSIS X void WriteFileInfo(FileNode *node); X X DESCRIPTION X WriteFileInfo writes complete info about the file specified by X <node> to the standard output. This only happens when an error X occurs. X X*/ X Xvoid XWriteFileInfo(FileNode *node) X{ X static char flagSetNames[9] = { X '-', '-', '-', '-', 'a', 'p', 's', 'h', 'C' X }; X static char flagClearNames[9] = { X 'd', 'e', 'w', 'r', '-', '-', '-', '-', '-' X }; X X ULONG flags; X SHORT i; X ULONG mask; X char temp[30]; X X DSToStr(temp,"%02m-%02d-%02y %02h:%02n:%02s ", (long *) &node->date); X printf(temp); X flags = node->flags; X for (i = 0, mask = 1; i < 9; ++i, mask = (mask << 1) ) X if (flags & mask) X temp[8 - i] = flagSetNames[i]; X else X temp[8 - i] = flagClearNames[i]; X X temp[9] = '\0'; X X printf("%s %8ld %s\n", temp, node->size, node->name); X if (node->comment) X printf(": %s\n",node->comment); X} END_OF_FILE if test 27265 -ne `wc -c <'DiffDir.c'`; then echo shar: \"'DiffDir.c'\" unpacked with wrong size! fi # end of 'DiffDir.c' fi if test -f 'MRDates.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MRDates.c'\" else echo shar: Extracting \"'MRDates.c'\" \(15280 characters\) sed "s/^X//" >'MRDates.c' <<'END_OF_FILE' X/* X MRDates - AmigaDOS date support routines. X 07/03/88 X X This package is a hybrid of code from Thad Floryan, Doug Merrit and X myself. I wanted a reliable set of AmigaDOS date conversion routines X and this combination seems to work pretty well. The star of the show X here is Thad's algorithm for converting the "days elapsed" field of X an AmigaDOS DateStamp, using an intermediate Julian date format. I X lifted/embellished some of the data structures from Doug's ShowDate X package and wrote the DateToDS function. X X History: (most recent change first) X X 12/31/88 -MRR- X StrToDS was not handling the null string properly. X X 11/01/88 -MRR- Added Unix-style documentation. X X 07/03/88 - Changed some names: X Str2DS => StrToDS X DS2Str => DSToStr X */ X X#define MRDATES X#include "MRDates.h" X#include <exec/types.h> X#include <stdio.h> X#include <ctype.h> X#include <string.h> X X X#define DATE_SEPARATORS "/:-." X#define MINS_PER_HOUR 60 X#define SECS_PER_MIN 60 X#define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR) X#define TICS_PER_SEC 50 X X#define YEARS_PER_CENTURY 100 X X X/* X definitions to calculate current date X */ X#define FEB 1 /* index of feb. in table (for leap years) */ X#define DAYS_PER_WEEK 7 X#define DAYS_PER_YEAR 365 X#define YEARS_PER_LEAP 4 X#define START_YEAR 1978 X#define FIRST_LEAP_YEAR 1980 X#define LEAP_ADJUST (FIRST_LEAP_YEAR - START_YEAR) X#define LEAP_FEB_DAYS 29 X#define NORM_FEB_DAYS 28 X#define IsLeap(N) (((N) % YEARS_PER_LEAP) ? 0 : 1) X X X/* FUNCTION X DSToDate - convert a DateStamp to a DATE. X X SYNOPSIS X int DSToDate(dateStamp, date) X struct DateStamp *dateStamp; X DATE *date; X X DESCRIPTION X Extracts the date components from an AmigaDOS datestamp. X The calculations herein use the following assertions: X X 146097 = number of days in 400 years per 400 * 365.2425 = 146097.00 X 36524 = number of days in 100 years per 100 * 365.2425 = 36524.25 X 1461 = number of days in 4 years per 4 * 365.2425 = 1460.97 X X AUTHOR X Thad Floryan, 12-NOV-85 X Mods by Mark Rinfret, 04-JUL-88 X X SEE ALSO X Include file MRDates.h. X X */ X X#define DDELTA 722449 /* days from Jan.1,0000 to Jan.1,1978 */ X Xstatic int mthvec[] = X {-1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364}; X Xint XDSToDate(ds, date) X long *ds; DATE *date; X X{ X X long jdate, day0, day1, day2, day3; X long year, month, day, temp; X X jdate = ds[0] + DDELTA; /* adjust internal date to Julian */ X X year = (jdate / 146097) * 400; X day0 = day1 = jdate %= 146097; X year += (jdate / 36524) * 100; X day2 = day1 %= 36524; X year += (day2 / 1461) * 4; X day3 = day1 %= 1461; X year += day3 / 365; X month = 1 + (day1 %= 365); X day = month % 30; X month /= 30; X X if ( ( day3 >= 59 && day0 < 59 ) || X ( day3 < 59 && (day2 >= 59 || day0 < 59) ) ) X ++day1; X X if (day1 > mthvec[1 + month]) ++month; X day = day1 - mthvec[month]; X date->Dyear = year; X date->Dmonth = month; X date->Dday = day; X date->Dweekday = ds[0] % DAYS_PER_WEEK; X X temp = ds[1]; /* get ds_Minute value */ X date->Dhour = temp / MINS_PER_HOUR; X date->Dminute = temp % MINS_PER_HOUR; X date->Dsecond = ds[2] / TICS_PER_SEC; X return 0; X} X X/* FUNCTION X DateToDS(date, dateStamp) X X SYNOPSIS X void DateToDS(date, dateStamp) X DATE *date; X struct DateStamp *dateStamp; X X DESCRIPTION X DateToDS converts the special DATE format to a DateStamp. X */ X XDateToDS(date, ds) X DATE *date; long *ds; X{ X long daysElapsed, yearsElapsed, leapYears, thisMonth, thisDay, thisYear; X int month; X X /* Note the special handling for year < START_YEAR. In this case, X * the other fields are not even checked - the user just gets the X * "start of time". X */ X if ((thisYear = date->Dyear) < START_YEAR) { X ds[0] = ds[1] = ds[2] = 0; X return; X } X if (IsLeap(thisYear)) X calendar[FEB].Mdays = LEAP_FEB_DAYS; X X thisDay = date->Dday - 1; X thisMonth = date->Dmonth -1; X yearsElapsed = thisYear - START_YEAR; X leapYears = (yearsElapsed + LEAP_ADJUST -1) / YEARS_PER_LEAP; X daysElapsed = (yearsElapsed * DAYS_PER_YEAR) + leapYears; X for (month = 0; month < thisMonth; ++month) X daysElapsed += calendar[month].Mdays; X daysElapsed += thisDay; X calendar[FEB].Mdays = NORM_FEB_DAYS; X ds[0] = daysElapsed; X ds[1] = date->Dhour * MINS_PER_HOUR + date->Dminute; X ds[2] = date->Dsecond * TICS_PER_SEC; X} X/* FUNCTION X CompareDS - compare two DateStamp values. X X SYNOPSIS X int CompareDS(date1, date2) X struct DateStamp *date1, *date2; X X DESCRIPTION X CompareDS performs an ordered comparison between two DateStamp X values, returning the following result codes: X X -1 => date1 < date2 X 0 => date1 == date2 X 1 => date1 > date2 X X NOTE: X This routine makes an assumption about the DateStamp structure, X specifically that it can be viewed as an array of 3 long integers X in days, minutes and ticks order. X */ X Xint XCompareDS(d1, d2) X long *d1, *d2; X{ X USHORT i; X long compare; X X for (i = 0; i < 3; ++i) { X if (compare = (d1[i] - d2[i])) { X if (compare < 0) return -1; X return 1; X } X } X return 0; /* dates match */ X} X X/* FUNCTION X DSToStr - convert a DateStamp to a formatted string. X X SYNOPSIS X void DSToStr(str,fmt,d) X char *str, *fmt; X struct DateStamp *d; X X DESCRIPTION X DSToStr works a little like sprintf. It converts a DateStamp X to an ascii formatted string. The formatting style is dependent X upon the contents of the format string, fmt, passed to this X function. X X The content of the format string is very similar to that X for printf, with the exception that the following letters X have special significance: X y => year minus 1900 X Y => full year value X m => month value as integer X M => month name X d => day of month (1..31) X D => day name ("Monday".."Sunday") X h => hour in twenty-four hour notation X H => hour in twelve hour notation X i => 12 hour indicator for H notation (AM or PM) X I => same as i X n => minutes (sorry...conflict with m = months) X N => same as n X s => seconds X S => same as s X X All other characters are passed through as part of the normal X formatting process. The following are some examples with X Saturday, July 18, 1987, 13:53 as an input date: X X "%y/%m/%d" => 87/7/18 X "%02m/%02d/%2y" => 07/18/87 X "%D, %M %d, %Y" => Saturday, July 18, 1987 X "%02H:%02m i" => 01:53 PM X "Time now: %h%m" => Time now: 13:53 X X */ Xvoid XDSToStr(str,fmt,d) X char *str, *fmt; long *d; X{ X DATE date; X char fc,*fs,*out; X USHORT ivalue; X char new_fmt[256]; /* make it big to be "safe" */ X USHORT new_fmt_lng; X char *svalue; X X DSToDate(d, &date); /* convert DateStamp to DATE format */ X X *str = '\0'; /* insure output is empty */ X out = str; X fs = fmt; /* make copy of format string pointer */ X X while (fc = *fs++) { /* get format characters */ X if (fc == '%') { /* formatting meta-character? */ X new_fmt_lng = 0; X new_fmt[new_fmt_lng++] = fc; X /* copy width information */ X while (isdigit(fc = *fs++) || fc == '-') X new_fmt[new_fmt_lng++] = fc; X X switch (fc) { /* what are we trying to do? */ X case 'y': /* year - 1980 */ X ivalue = date.Dyear % 100; Xwrite_int: X new_fmt[new_fmt_lng++] = 'd'; X new_fmt[new_fmt_lng] = '\0'; X sprintf(out,new_fmt,ivalue); X out = str + strlen(str); X break; X case 'Y': /* full year value */ X ivalue = date.Dyear; X goto write_int; X X case 'm': /* month */ X ivalue = date.Dmonth; X goto write_int; X X case 'M': /* month name */ X svalue = calendar[date.Dmonth - 1].Mname; Xwrite_str: X new_fmt[new_fmt_lng++] = 's'; X new_fmt[new_fmt_lng] = '\0'; X sprintf(out,new_fmt,svalue); X out = str + strlen(str); X break; X X case 'd': /* day */ X ivalue = date.Dday; X goto write_int; X X case 'D': /* day name */ X svalue = dayNames[d[0] % DAYS_PER_WEEK]; X goto write_str; X X case 'h': /* hour */ X ivalue = date.Dhour; X goto write_int; X X case 'H': /* hour in 12 hour notation */ X ivalue = date.Dhour; X if (ivalue >= 12) ivalue -= 12; X goto write_int; X X case 'i': /* AM/PM indicator */ X case 'I': X if (date.Dhour >= 12) X svalue = "PM"; X else X svalue = "AM"; X goto write_str; X X case 'n': /* minutes */ X case 'N': X ivalue = date.Dminute; X goto write_int; X X case 's': /* seconds */ X case 'S': X ivalue = date.Dsecond; X goto write_int; X X default: X /* We are in deep caca - don't know what to do with this X * format character. Copy the raw format string to the X * output as debugging information. X */ X new_fmt[new_fmt_lng++] = fc; X new_fmt[new_fmt_lng] = '\0'; X strcat(out, new_fmt); X out = out + strlen(out); /* advance string pointer */ X break; X } X } X else X *out++ = fc; /* copy literal character */ X } X *out = '\0'; /* terminating null */ X} X X/* FUNCTION X StrToDS - convert a string to a DateStamp. X X SYNOPSIS X int StrToDS(string, date) X char *string; X struct DateStamp *date; X X DESCRIPTION X StrToDS expects its string argument to contain a date in X MM/DD/YY HH:MM:SS format. The time portion is optional. X StrToDS will attempt to convert the string to a DateStamp X representation. If successful, it will return 0. On X failure, a 1 is returned. X X */ X Xint XStrToDS(str, d) X char *str; long *d; X{ X register char c; X int count; X int i, item; X DATE date; /* unpacked DateStamp */ X char *s; X X int values[3]; X int value; X X s = str; X for (item = 0; item < 2; ++item) { /* item = date, then time */ X for (i = 0; i < 3; ++i) values[i] = 0; X count = 0; X while (c = *s++) { /* get date value */ X if (c <= ' ') X break; X X if (isdigit(c)) { X value = 0; X do { X value = value*10 + c - '0'; X c = *s++; X } while (isdigit(c)); X if (count == 3) { X bad_value: X#ifdef DEBUG X puts("Error in date-time format.\n"); X printf("at %s: values(%d) = %d, %d, %d\n", X s, count, values[0], values[1], values[2]); X#endif X return 1; X } X values[count++] = value; X if (c <= ' ') X break; X } X else if (! strchr(DATE_SEPARATORS, c) ) X goto bad_value; /* Illegal character - quit. */ X } /* end while */ X if (item) { /* Getting time? */ X date.Dhour = values[0]; X date.Dminute = values[1]; X date.Dsecond = values[2]; X } X else { /* Getting date? */ X X/* It's OK to have a null date string, but it's not OK to specify only X 1 or 2 of the date components. X */ X if (count && count != 3) X goto bad_value; X date.Dmonth = values[0]; X date.Dday = values[1]; X date.Dyear = values[2]; X if (date.Dyear == 0) { X date.Dyear = START_YEAR; X date.Dday = 1; X } X else { X if (date.Dyear < (START_YEAR - 1900) ) X date.Dyear += 100; X date.Dyear += 1900; X } X } X } /* end for */ X DateToDS(&date, d); X return 0; X} /* StrToDS */ X X X#ifdef DEBUG X#include "stdio.h" Xmain(ac, av) X int ac; X char **av; X{ X long datestamp[3]; /* A little dangerous with Aztec */ X long datestamp2[3]; X DATE date, oldDate; X long day, lastDay; X int errors = 0; X X /* X * display results from DateStamp() (hours:minutes:seconds) X */ X DateStamp(datestamp); X X /* X * display results from DSToDate() (e.g. "03-May-88") X */ X DSToDate(datestamp, &date); X printf("Current date: %02d-%s-%02d\n", X date.Dday, calendar[ date.Dmonth - 1].Mname, X (date.Dyear % YEARS_PER_CENTURY)); X X printf("Current time: %02d:%02d:%02d\n", X date.Dhour, date.Dminute, date.Dsecond); X X printf("\nDoing sanity check through year 2000...\n\t"); X lastDay = (2000L - START_YEAR) * 365L; X lastDay += (2000L - START_YEAR) / YEARS_PER_LEAP; X for (day = 0; day <= lastDay; ++day) { X if (day % 1000 == 0) { X printf(" %ld", day); X fflush(stdout); X } X datestamp[0] = day; X datestamp[1] = MINS_PER_HOUR - 1; X datestamp[2] = TICS_PER_SEC * (SECS_PER_MIN - 1); X DSToDate(datestamp, &date); X if (day && date == oldDate) { X printf("Got same date for days %d, %d: %02d-%s-%02d\n", X day - 1, day, X date.Dday, X calendar[ date.Dmonth - 1 ].Mname, X (date.Dyear % YEARS_PER_CENTURY)); X X if (++errors == 10) X exit(1); X } X DateToDS(&date, datestamp2); X if (day != datestamp2[0]) { X printf("\nConversion mismatch at day %ld!\n", day); X printf("\tBad value = %ld", datestamp2[0]); X printf("\tDate: %02d-%s-%02d\n", X date.Dday, X calendar[ date.Dmonth -1 ].Mname, X (date.Dyear % YEARS_PER_CENTURY)); X if (++errors == 10) X exit(1); X } X oldDate = date; X } X printf("\nSanity check passed.\n"); X} /* main() */ X#endif X END_OF_FILE if test 15280 -ne `wc -c <'MRDates.c'`; then echo shar: \"'MRDates.c'\" unpacked with wrong size! fi # end of 'MRDates.c' fi if test -f 'MRDates.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MRDates.h'\" else echo shar: Extracting \"'MRDates.h'\" \(1183 characters\) sed "s/^X//" >'MRDates.h' <<'END_OF_FILE' X/* MRDates.h - Declarations for types and variables used by MRDates. */ X X#ifndef MRDATES_H X#define MRDATES_H 1 X Xtypedef struct { X int Dyear; /* year AD (e.g. 1987) */ X int Dmonth; /* month of year (0-11) */ X int Dday; /* day in month (1-31) */ X int Dhour; /* 0-23 */ X int Dminute; /* 0-59 */ X int Dsecond; /* 0-59 */ X int Dweekday; /* day of week (Sun=0) */ X} DATE; X Xtypedef struct { X char *Mname; X int Mdays; X } CalEntry; X X#ifdef MRDATES XCalEntry calendar[12] = { X { "Jan", 31 }, { "Feb", 28 }, { "Mar", 31 }, { "Apr", 30 }, X { "May", 31 }, { "Jun", 30 }, { "Jul", 31 }, { "Aug", 31 }, X { "Sep", 30 }, { "Oct", 31 }, { "Nov", 30 }, { "Dec", 31 } X }; X#else Xextern CalEntry calendar[12]; X#endif X X#ifdef MRDATES Xchar *dayNames[7] = { X "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" X }; X#else Xextern char *dayNames[7]; X#endif X X/* mrdates.c */ Xint DSToDate(long *ds, DATE *date); Xint DateToDS(DATE *date, long *ds); Xint CompareDS(long *d1, long *d2); Xvoid DSToStr(char *str, char *fmt, long *d); Xint StrToDS(char *str, long *d); X X#endif /* MRDATES_H */ END_OF_FILE if test 1183 -ne `wc -c <'MRDates.h'`; then echo shar: \"'MRDates.h'\" unpacked with wrong size! fi # end of 'MRDates.h' fi if test ! -d 'dir1' ; then echo shar: Creating directory \"'dir1'\" mkdir 'dir1' fi if test -f 'dir1/File1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/File1'\" else echo shar: Extracting \"'dir1/File1'\" \(18 characters\) sed "s/^X//" >'dir1/File1' <<'END_OF_FILE' XThis is file 1. X X END_OF_FILE if test 18 -ne `wc -c <'dir1/File1'`; then echo shar: \"'dir1/File1'\" unpacked with wrong size! fi # end of 'dir1/File1' fi if test -f 'dir1/File3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/File3'\" else echo shar: Extracting \"'dir1/File3'\" \(18 characters\) sed "s/^X//" >'dir1/File3' <<'END_OF_FILE' XThis is file 3. X X END_OF_FILE if test 18 -ne `wc -c <'dir1/File3'`; then echo shar: \"'dir1/File3'\" unpacked with wrong size! fi # end of 'dir1/File3' fi if test ! -d 'dir1/Level2' ; then echo shar: Creating directory \"'dir1/Level2'\" mkdir 'dir1/Level2' fi if test -f 'dir1/Level2/File1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/Level2/File1'\" else echo shar: Extracting \"'dir1/Level2/File1'\" \(81 characters\) sed "s/^X//" >'dir1/Level2/File1' <<'END_OF_FILE' XThis is file 1. XIt is longer than the equivalent file in the other directory. X X X END_OF_FILE if test 81 -ne `wc -c <'dir1/Level2/File1'`; then echo shar: \"'dir1/Level2/File1'\" unpacked with wrong size! fi # end of 'dir1/Level2/File1' fi if test -f 'dir1/Level2/File3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/Level2/File3'\" else echo shar: Extracting \"'dir1/Level2/File3'\" \(18 characters\) sed "s/^X//" >'dir1/Level2/File3' <<'END_OF_FILE' XThis is file 3. X X END_OF_FILE if test 18 -ne `wc -c <'dir1/Level2/File3'`; then echo shar: \"'dir1/Level2/File3'\" unpacked with wrong size! fi # end of 'dir1/Level2/File3' fi if test ! -d 'dir1/Level2/Level3' ; then echo shar: Creating directory \"'dir1/Level2/Level3'\" mkdir 'dir1/Level2/Level3' fi if test -f 'dir1/Level2/Level3/File1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/Level2/Level3/File1'\" else echo shar: Extracting \"'dir1/Level2/Level3/File1'\" \(18 characters\) sed "s/^X//" >'dir1/Level2/Level3/File1' <<'END_OF_FILE' XThis is file 1. X X END_OF_FILE if test 18 -ne `wc -c <'dir1/Level2/Level3/File1'`; then echo shar: \"'dir1/Level2/Level3/File1'\" unpacked with wrong size! fi # end of 'dir1/Level2/Level3/File1' fi if test -f 'dir1/Level2/Level3/File2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/Level2/Level3/File2'\" else echo shar: Extracting \"'dir1/Level2/Level3/File2'\" \(18 characters\) sed "s/^X//" >'dir1/Level2/Level3/File2' <<'END_OF_FILE' XThis is file 2. X X END_OF_FILE if test 18 -ne `wc -c <'dir1/Level2/Level3/File2'`; then echo shar: \"'dir1/Level2/Level3/File2'\" unpacked with wrong size! fi # end of 'dir1/Level2/Level3/File2' fi if test -f 'dir1/Level2/Level3/File3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/Level2/Level3/File3'\" else echo shar: Extracting \"'dir1/Level2/Level3/File3'\" \(18 characters\) sed "s/^X//" >'dir1/Level2/Level3/File3' <<'END_OF_FILE' XThis is file 3. X X END_OF_FILE if test 18 -ne `wc -c <'dir1/Level2/Level3/File3'`; then echo shar: \"'dir1/Level2/Level3/File3'\" unpacked with wrong size! fi # end of 'dir1/Level2/Level3/File3' fi if test -f 'dir1/file2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir1/file2'\" else echo shar: Extracting \"'dir1/file2'\" \(18 characters\) sed "s/^X//" >'dir1/file2' <<'END_OF_FILE' XThis is dile 2. X X END_OF_FILE if test 18 -ne `wc -c <'dir1/file2'`; then echo shar: \"'dir1/file2'\" unpacked with wrong size! fi # end of 'dir1/file2' fi if test ! -d 'dir2' ; then echo shar: Creating directory \"'dir2'\" mkdir 'dir2' fi if test -f 'dir2/DifferentType' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/DifferentType'\" else echo shar: Extracting \"'dir2/DifferentType'\" \(0 characters\) sed "s/^X//" >'dir2/DifferentType' <<'END_OF_FILE' END_OF_FILE if test 0 -ne `wc -c <'dir2/DifferentType'`; then echo shar: \"'dir2/DifferentType'\" unpacked with wrong size! fi # end of 'dir2/DifferentType' fi if test -f 'dir2/File1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/File1'\" else echo shar: Extracting \"'dir2/File1'\" \(18 characters\) sed "s/^X//" >'dir2/File1' <<'END_OF_FILE' XThis is file 1. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/File1'`; then echo shar: \"'dir2/File1'\" unpacked with wrong size! fi # end of 'dir2/File1' fi if test -f 'dir2/File2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/File2'\" else echo shar: Extracting \"'dir2/File2'\" \(18 characters\) sed "s/^X//" >'dir2/File2' <<'END_OF_FILE' XThis is file 2. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/File2'`; then echo shar: \"'dir2/File2'\" unpacked with wrong size! fi # end of 'dir2/File2' fi if test ! -d 'dir2/Level2' ; then echo shar: Creating directory \"'dir2/Level2'\" mkdir 'dir2/Level2' fi if test -f 'dir2/Level2/File1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/Level2/File1'\" else echo shar: Extracting \"'dir2/Level2/File1'\" \(18 characters\) sed "s/^X//" >'dir2/Level2/File1' <<'END_OF_FILE' XThis is file 1. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/Level2/File1'`; then echo shar: \"'dir2/Level2/File1'\" unpacked with wrong size! fi # end of 'dir2/Level2/File1' fi if test -f 'dir2/Level2/File2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/Level2/File2'\" else echo shar: Extracting \"'dir2/Level2/File2'\" \(18 characters\) sed "s/^X//" >'dir2/Level2/File2' <<'END_OF_FILE' XThis is file 2. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/Level2/File2'`; then echo shar: \"'dir2/Level2/File2'\" unpacked with wrong size! fi # end of 'dir2/Level2/File2' fi if test -f 'dir2/Level2/File3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/Level2/File3'\" else echo shar: Extracting \"'dir2/Level2/File3'\" \(18 characters\) sed "s/^X//" >'dir2/Level2/File3' <<'END_OF_FILE' XThis is file 3. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/Level2/File3'`; then echo shar: \"'dir2/Level2/File3'\" unpacked with wrong size! fi # end of 'dir2/Level2/File3' fi if test ! -d 'dir2/Level2/Level3' ; then echo shar: Creating directory \"'dir2/Level2/Level3'\" mkdir 'dir2/Level2/Level3' fi if test -f 'dir2/Level2/Level3/File2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/Level2/Level3/File2'\" else echo shar: Extracting \"'dir2/Level2/Level3/File2'\" \(18 characters\) sed "s/^X//" >'dir2/Level2/Level3/File2' <<'END_OF_FILE' XThis is file 2. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/Level2/Level3/File2'`; then echo shar: \"'dir2/Level2/Level3/File2'\" unpacked with wrong size! fi # end of 'dir2/Level2/Level3/File2' fi if test -f 'dir2/Level2/Level3/File3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/Level2/Level3/File3'\" else echo shar: Extracting \"'dir2/Level2/Level3/File3'\" \(18 characters\) sed "s/^X//" >'dir2/Level2/Level3/File3' <<'END_OF_FILE' XThis is file 3. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/Level2/Level3/File3'`; then echo shar: \"'dir2/Level2/Level3/File3'\" unpacked with wrong size! fi # end of 'dir2/Level2/Level3/File3' fi if test -f 'dir2/file3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir2/file3'\" else echo shar: Extracting \"'dir2/file3'\" \(18 characters\) sed "s/^X//" >'dir2/file3' <<'END_OF_FILE' XThis is file 3. X X END_OF_FILE if test 18 -ne `wc -c <'dir2/file3'`; then echo shar: \"'dir2/file3'\" unpacked with wrong size! fi # end of 'dir2/file3' fi if test -f 'makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile'\" else echo shar: Extracting \"'makefile'\" \(573 characters\) sed "s/^X//" >'makefile' <<'END_OF_FILE' X# Aztec C Makefile for DiffDir program X X#CFLAGS = -bs X#LFLAGS = -w -g X XCFLAGS= -so XLFLAGS= X XOBJ = DiffDir.o MRDates.o X XDiffDir: $(OBJ) X ln $(LFLAGS) -o DiffDir $(OBJ) -lc X XDiffDir.o: DiffDir.c MRDates.h X Xclean: X delete (#?.o|#?.dbg) X XSRC = DiffDir.c MRDates.h MRDates.c Makefile XBIN = DiffDir.man DiffDir.n DiffDir Sample.Output XMISC= dir1 dir2 X XDiffDir.lzh: $(SRC) $(BIN) $(MISC) X delete (diffdir.lzh) X lharc -b32 -r -xa a DiffDir $(SRC) dir1/* dir2/* X Xshar: DiffDir.lzh X uuencode >DiffDir.lzu DiffDir.lzh DiffDir.lzh X makekit -n SHAR DiffDir.lzu X END_OF_FILE if test 573 -ne `wc -c <'makefile'`; then echo shar: \"'makefile'\" unpacked with wrong size! fi # end of 'makefile' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Mail submissions (sources or binaries) to <amiga@uunet.uu.net>. Mail comments to the moderator at <amiga-request@uunet.uu.net>. Post requests for sources, and general discussion to comp.sys.amiga.misc.