Sources/DirectoryCopy.c

/*
    File:       DirectoryCopy.c
 
    Contains:   A robust, general purpose directory copy routine.
 
    Version:    MoreFiles
 
    Copyright:  © 1992-2002 by Apple Computer, Inc., all rights reserved.
 
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
    File Ownership:
 
        DRI:                Apple Macintosh Developer Technical Support
 
        Other Contact:      Apple Macintosh Developer Technical Support
                            <http://developer.apple.com/bugreporter/>
 
        Technology:         DTS Sample Code
 
    Writers:
 
        (JL)    Jim Luther
 
    Change History (most recent first):
 
         <3>     8/23/02    JL      [2853901]  Updated standard disclaimer.
         <2>      2/7/01    JL      Added standard header. Updated names of includes.
        <1>     12/06/99    JL      MoreFiles 1.5.
*/
 
#include <MacTypes.h>
#include <MacErrors.h>
#include <MacMemory.h>
#include <Files.h>
#include <Script.h>
#include <Math64.h>
 
#define __COMPILINGMOREFILES
 
#include "MoreFiles.h"
#include "MoreFilesExtras.h"
#include "MoreDesktopMgr.h"
#include "FileCopy.h"
#include "DirectoryCopy.h"
 
/*****************************************************************************/
 
/* local constants */
 
enum
{
    dirCopyBigCopyBuffSize  = 0x00004000,
    dirCopyMinCopyBuffSize  = 0x00000200
};
 
 
/*****************************************************************************/
 
/* local data structures */
 
/* The EnumerateGlobals structure is used to minimize the amount of
** stack space used when recursively calling CopyLevel and to hold
** global information that might be needed at any time. */
 
#if PRAGMA_STRUCT_ALIGN
#pragma options align=mac68k
#endif
struct EnumerateGlobals
{
    Ptr         copyBuffer;         /* pointer to buffer used for file copy operations */
    long        bufferSize;         /* the size of the copy buffer */
    CopyErrProcPtr errorHandler;    /* pointer to error handling function */
    CopyFilterProcPtr copyFilterProc; /* pointer to filter function */
    OSErr       error;              /* temporary holder of results - saves 2 bytes of stack each level */
    Boolean     bailout;            /* set to true to by error handling function if fatal error */
    short       destinationVRefNum; /* the destination vRefNum */
    Str63       itemName;           /* the name of the current item */
    CInfoPBRec  myCPB;              /* the parameter block used for PBGetCatInfo calls */
};
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#endif
 
typedef struct EnumerateGlobals EnumerateGlobals;
typedef EnumerateGlobals *EnumerateGlobalsPtr;
 
 
/* The PreflightGlobals structure is used to minimize the amount of
** stack space used when recursively calling GetLevelSize and to hold
** global information that might be needed at any time. */
 
#if PRAGMA_STRUCT_ALIGN
#pragma options align=mac68k
#endif
struct PreflightGlobals
{
    OSErr           result;             /* temporary holder of results - saves 2 bytes of stack each level */
    Str63           itemName;           /* the name of the current item */
    CInfoPBRec      myCPB;              /* the parameter block used for PBGetCatInfo calls */
 
    unsigned long   dstBlksPerAllocBlk; /* the number of 512 byte blocks per allocation block on destination */
                                        
    unsigned long   allocBlksNeeded;    /* the total number of allocation blocks needed  */
 
    unsigned long   tempBlocks;         /* temporary storage for calculations (save some stack space)  */
    CopyFilterProcPtr copyFilterProc;   /* pointer to filter function */
};
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#endif
 
typedef struct PreflightGlobals PreflightGlobals;
typedef PreflightGlobals *PreflightGlobalsPtr;
 
/*****************************************************************************/
 
/* static prototypes */
 
static  void    GetLevelSize(long currentDirID,
                             PreflightGlobals *theGlobals);
 
static  OSErr   PreflightDirectoryCopySpace(short srcVRefNum,
                                            long srcDirID,
                                            short dstVRefNum,
                                            CopyFilterProcPtr copyFilterProc,
                                            Boolean *spaceOK);
 
static  void    CopyLevel(long sourceDirID,
                          long dstDirID,
                          EnumerateGlobals *theGlobals);
                          
/*****************************************************************************/
 
static  void    GetLevelSize(long currentDirID,
                             PreflightGlobals *theGlobals)
{
    short   index = 1;
    
    do
    {
        theGlobals->myCPB.dirInfo.ioFDirIndex = index;
        theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID; /* we need to do this every time */
                                                            /* through, since GetCatInfo  */
                                                            /* returns ioFlNum in this field */
        theGlobals->result = PBGetCatInfoSync(&theGlobals->myCPB);
        if ( theGlobals->result == noErr )
        {
            if ( (theGlobals->copyFilterProc == NULL) ||
                 CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
            {
                /* Either there's no filter proc OR the filter proc says to use this item */
                if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
                {
                    /* we have a directory */
                    
                    GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
                    theGlobals->result = noErr; /* clear error return on way back */
                }
                else
                {
                    /* We have a file - add its allocation blocks to allocBlksNeeded. */
                    /* Since space on Mac OS disks is always allocated in allocation blocks, */
                    /* this takes into account rounding up to the end of an allocation block. */
                    
                    /* get number of 512-byte blocks needed for data fork */
                    if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 )
                    {
                        theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1;
                    }
                    else
                    {
                        theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9;
                    }
                    /* now, calculate number of new allocation blocks needed for the data fork and add it to the total */
                    if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
                    {
                        theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
                    }
                    else
                    {
                        theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
                    }
                    
                    /* get number of 512-byte blocks needed for resource fork */
                    if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 )
                    {
                        theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1;
                    }
                    else
                    {
                        theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9;
                    }
                    /* now, calculate number of new allocation blocks needed for the resource  fork and add it to the total */
                    if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
                    {
                        theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
                    }
                    else
                    {
                        theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
                    }
                }
            }
        }
        ++index;
    } while ( theGlobals->result == noErr );
}
 
/*****************************************************************************/
 
static  OSErr   PreflightDirectoryCopySpace(short srcVRefNum,
                                            long srcDirID,
                                            short dstVRefNum,
                                            CopyFilterProcPtr copyFilterProc,
                                            Boolean *spaceOK)
{
    XVolumeParam pb;
    OSErr error;
    unsigned long dstFreeBlocks;
    PreflightGlobals theGlobals;
    
    error = XGetVolumeInfoNoName(NULL, dstVRefNum, &pb);
    if ( error == noErr )
    {
        /* Convert freeBytes to free disk blocks (512-byte blocks) */
        dstFreeBlocks = U32SetU(U64ShiftRight(pb.ioVFreeBytes, 9));
        
        /* get allocation block size (always multiple of 512) and divide by 512
          to get number of 512-byte blocks per allocation block */
        theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.ioVAlBlkSiz >> 9);
        
        theGlobals.allocBlksNeeded = 0;
 
        theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
        theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
        
        theGlobals.copyFilterProc = copyFilterProc;
        
        GetLevelSize(srcDirID, &theGlobals);
        
        /* Is there enough room on the destination volume for the source file?                  */
        /* Note:    This will work because the largest number of disk blocks supported          */
        /*          on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk)    */
        /*          will always be less than 0xffffffff.                                        */
        *spaceOK = ((theGlobals.allocBlksNeeded * theGlobals.dstBlksPerAllocBlk) <= dstFreeBlocks);
    }
 
    return ( error );
}
 
/*****************************************************************************/
 
static  void    CopyLevel(long sourceDirID,
                          long dstDirID,
                          EnumerateGlobals *theGlobals)
{
    long currentSrcDirID;
    long newDirID;
    short index = 1;
    
    do
    {   
        /* Get next source item at the current directory level */
        
        theGlobals->myCPB.dirInfo.ioFDirIndex = index;
        theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
        theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB);       
 
        if ( theGlobals->error == noErr )
        {
            if ( (theGlobals->copyFilterProc == NULL) ||
                 CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
            {
                /* Either there's no filter proc OR the filter proc says to use this item */
 
                /* We have an item.  Is it a file or directory? */
                if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
                {
                    /* We have a directory */
                    
                    /* Create a new directory at the destination. No errors allowed! */
                    theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
                    if ( theGlobals->error == noErr )
                    {
                        /* Save the current source directory ID where we can get it when we come back
                        ** from recursion land. */
                        currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
                        
                        /* Dive again (copy the directory level we just found below this one) */
                        CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
                        
                        if ( !theGlobals->bailout )
                        {
                            /* Copy comment from old to new directory. */
                            /* Ignore the result because we really don't care if it worked or not. */
                            (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL);
                            
                            /* Copy directory attributes (dates, etc.) to newDirID. */
                            /* No errors allowed */
                            theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true);
                            
                            /* handle any errors from CopyFileMgrAttributes */
                            if ( theGlobals->error != noErr )
                            {
                                if ( theGlobals->errorHandler != NULL )
                                {
                                    theGlobals->bailout =  CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp,
                                                            theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
                                                            theGlobals->destinationVRefNum, newDirID, NULL);
                                }
                                else
                                {
                                    /* If you don't handle the errors with an error handler, */
                                    /* then the copy stops here. */
                                    theGlobals->bailout = true;
                                }
                            }
                        }
                    }
                    else    /* error handling for DirCreate */
                    {
                        if ( theGlobals->errorHandler != NULL )
                        {
                            theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp,
                                                        theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
                                                        theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
                        }
                        else
                        {
                            /* If you don't handle the errors with an error handler, */
                            /* then the copy stops here. */
                            theGlobals->bailout = true;
                        }
                    }
                    
                    if ( !theGlobals->bailout )
                    {
                        /* clear error return on way back if we aren't bailing out */
                        theGlobals->error = noErr;
                    }
                }
                else
                {
                    /* We have a file, so copy it */
                    
                    theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
                                                 theGlobals->myCPB.hFileInfo.ioFlParID,
                                                 theGlobals->itemName,
                                                 theGlobals->destinationVRefNum,
                                                 dstDirID,
                                                 NULL,
                                                 NULL,
                                                 theGlobals->copyBuffer,
                                                 theGlobals->bufferSize,
                                                 false);
                            
                    /* handle any errors from FileCopy */
                    if ( theGlobals->error != noErr )
                    {
                        if ( theGlobals->errorHandler != NULL )
                        {
                            theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp,
                                                    theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
                                                    theGlobals->destinationVRefNum, dstDirID, NULL);
                            if ( !theGlobals->bailout )
                            {
                                /* If the CopyErrProc handled the problem, clear the error here */
                                theGlobals->error = noErr;
                            }
                        }
                        else
                        {
                            /* If you don't handle the errors with an error handler, */
                            /* then the copy stops here. */
                            theGlobals->bailout = true;
                        }
                    }
                }
            }
        }
        else
        {   /* error handling for PBGetCatInfo */
            /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
            if ( theGlobals->error != fnfErr )
            {
                if ( theGlobals->errorHandler != NULL )
                { 
                    theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp,
                                            theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
                    if ( !theGlobals->bailout )
                    {
                        /* If the CopyErrProc handled the problem, clear the error here */
                        theGlobals->error = noErr;
                    }
                }
                else
                {
                    /* If you don't handle the errors with an error handler, */
                    /* then the copy stops here. */
                    theGlobals->bailout = true;
                }
            }
        }
        ++index; /* prepare to get next item */
    } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
}
 
/*****************************************************************************/
 
pascal  OSErr   FilteredDirectoryCopy(short srcVRefNum,
                                      long srcDirID,
                                      ConstStr255Param srcName,
                                      short dstVRefNum,
                                      long dstDirID,
                                      ConstStr255Param dstName,
                                      ConstStr255Param copyName,
                                      void *copyBufferPtr,
                                      long copyBufferSize,
                                      Boolean preflight,
                                      CopyErrProcPtr copyErrHandler,
                                      CopyFilterProcPtr copyFilterProc)
{
    EnumerateGlobals theGlobals;
    Boolean isDirectory;
    OSErr   error;
    Boolean ourCopyBuffer = false;
    Str63   srcDirName, oldDiskName;
    Boolean spaceOK;            
    
    /* Make sure a copy buffer is allocated. */
    if ( copyBufferPtr == NULL )
    {
        /* The caller didn't supply a copy buffer so grab one from the application heap.
        ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
        ** If 512 bytes aren't available, we're in trouble. */
        copyBufferSize = dirCopyBigCopyBuffSize;
        copyBufferPtr = NewPtr(copyBufferSize);
        if ( copyBufferPtr == NULL )
        {
            copyBufferSize = dirCopyMinCopyBuffSize;
            copyBufferPtr = NewPtr(copyBufferSize);
            if ( copyBufferPtr == NULL )
            {
                return ( memFullErr );
            }
        }
        ourCopyBuffer = true;
    }
    
    /* Get the real dirID where we're copying from and make sure it is a directory. */
    error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
    if ( error != noErr )
    {
        goto ErrorExit;
    }
    if ( !isDirectory )
    {
        error = dirNFErr;
        goto ErrorExit;
    }
    
    /* Special case destination if it is the root parent directory. */
    /* Since you can't create the root directory, this is needed if */
    /* you want to copy a directory's content to a disk's root directory. */
    if ( (dstDirID == fsRtParID) && (dstName == NULL) )
    {
        dstDirID = fsRtParID;
        isDirectory = true;
        error = noErr;
    }
    else
    {
        /*  Get the real dirID where we're going to put the copy and make sure it is a directory. */
        error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
        if ( error != noErr )
        {
            goto ErrorExit;
        }
        if ( !isDirectory )
        {
            error =  dirNFErr;
            goto ErrorExit;
        }
    }
    
    /* Get the real vRefNum of both the source and destination */
    error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
    if ( error != noErr )
    {
        goto ErrorExit;
    }
    error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
    if ( error != noErr )
    {
        goto ErrorExit;
    }
    
    if ( preflight )
    {
        error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK);
        if ( error != noErr )
        {
            goto ErrorExit;
        }
        if ( !spaceOK )
        {
            error = dskFulErr; /* not enough room on destination */
            goto ErrorExit;
        }
    }
 
    /* Create the new directory in the destination directory with the */
    /* same name as the source directory. */
    error = GetDirName(srcVRefNum, srcDirID, srcDirName);
    if ( error != noErr )
    {
        goto ErrorExit;
    }
    
    /* Again, special case destination if the destination is the */
    /* root parent directory. This time, we'll rename the disk to */
    /* the source directory name. */
    if ( dstDirID == fsRtParID )
    {
        /* Get the current name of the destination disk */
        error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName);
        if ( error == noErr )   
        {
            /* use the copyName as srcDirName if supplied */
            if ( copyName != NULL )
            {
                /* make a copy since copyName is a const input */
                BlockMoveData(copyName, srcDirName, sizeof(Str31));
            }
            /* Shorten the name if it's too long to be the volume name */
            TruncPString(srcDirName, srcDirName, 27);
            
            /* Rename the disk */
            error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName);
            
            /* and copy to the root directory */
            dstDirID = fsRtDirID;
        }
    }
    else
    {
        /* use the copyName as srcDirName if supplied */
        error = DirCreate(dstVRefNum, dstDirID, ((copyName != NULL) ? copyName : srcDirName), &dstDirID);
    }
    if ( error != noErr )
    {
        /* handle any errors from DirCreate */
        if ( copyErrHandler != NULL )
        {
            if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp,
                                                    srcVRefNum, srcDirID, NULL,
                                                    dstVRefNum, dstDirID, srcDirName) )
            {
                goto ErrorExit;
            }
            else
            {
                /* If the CopyErrProc handled the problem, clear the error here */
                /* and continue */
                error = noErr;
            }
        }
        else
        {
            /* If you don't handle the errors with an error handler, */
            /* then the copy stops here. */
            goto ErrorExit;
        }
    }
    
    /* dstDirID is now the newly created directory! */
        
    /* Set up the globals we need to access from the recursive routine. */
    theGlobals.copyBuffer = (Ptr)copyBufferPtr;
    theGlobals.bufferSize = copyBufferSize;
    theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
    theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
    theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
    theGlobals.errorHandler = copyErrHandler;
    theGlobals.bailout = false;
    theGlobals.copyFilterProc =  copyFilterProc;
        
    /* Here we go into recursion land... */
    CopyLevel(srcDirID, dstDirID, &theGlobals);
    error = theGlobals.error;   /* get the result */
    
    if ( !theGlobals.bailout )
    {
        /* Copy comment from source to destination directory. */
        /* Ignore the result because we really don't care if it worked or not. */
        (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL);
        
        /* Copy the File Manager attributes */
        error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
                    dstVRefNum, dstDirID, NULL, true);
        
        /* handle any errors from CopyFileMgrAttributes */
        if ( (error != noErr) && (copyErrHandler != NULL) )
        {
            theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp,
                                                srcVRefNum, srcDirID, NULL,
                                                dstVRefNum, dstDirID, NULL);
        }
    }
 
ErrorExit:
    /* Get rid of the copy buffer if we allocated it. */
    if ( ourCopyBuffer )
    {
        DisposePtr((Ptr)copyBufferPtr);
    }
 
    return ( error );
}
 
/*****************************************************************************/
 
pascal  OSErr   DirectoryCopy(short srcVRefNum,
                              long srcDirID,
                              ConstStr255Param srcName,
                              short dstVRefNum,
                              long dstDirID,
                              ConstStr255Param dstName,
                              ConstStr255Param copyName,
                              void *copyBufferPtr,
                              long copyBufferSize,
                              Boolean preflight,
                              CopyErrProcPtr copyErrHandler)
{
    return ( FilteredDirectoryCopy(srcVRefNum, srcDirID, srcName,
                                   dstVRefNum, dstDirID, dstName,
                                   copyName,
                                   copyBufferPtr, copyBufferSize, preflight,
                                   copyErrHandler, NULL) );
}
 
/*****************************************************************************/
 
pascal  OSErr   FSpFilteredDirectoryCopy(const FSSpec *srcSpec,
                                         const FSSpec *dstSpec,
                                         ConstStr255Param copyName,
                                         void *copyBufferPtr,
                                         long copyBufferSize,
                                         Boolean preflight,
                                         CopyErrProcPtr copyErrHandler,
                                         CopyFilterProcPtr copyFilterProc)
{
    return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
                                   dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
                                   copyName,
                                   copyBufferPtr, copyBufferSize, preflight,
                                   copyErrHandler, copyFilterProc) );
}
 
/*****************************************************************************/
 
pascal  OSErr   FSpDirectoryCopy(const FSSpec *srcSpec,
                                 const FSSpec *dstSpec,
                                 ConstStr255Param copyName,
                                 void *copyBufferPtr,
                                 long copyBufferSize,
                                 Boolean preflight,
                                 CopyErrProcPtr copyErrHandler)
{
    return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
                                   dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
                                   copyName,
                                   copyBufferPtr, copyBufferSize, preflight,
                                   copyErrHandler, NULL) );
}
 
/*****************************************************************************/