[comp.sources.amiga] v91i013: DiffDir 2.0 - directory comparison utility, Part01/01

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.