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.