2 **      Apple Macintosh Developer Technical Support 
   4 **      DirectoryCopy: A robust, general purpose directory copy routine. 
   6 **      by Jim Luther, Apple Developer Technical Support Emeritus 
   8 **      File:           DirectoryCopy.c 
  10 **      Copyright © 1992-1998 Apple Computer, Inc. 
  11 **      All rights reserved. 
  13 **      You may incorporate this sample code into your applications without 
  14 **      restriction, though the sample code has been provided "AS IS" and the 
  15 **      responsibility for its operation is 100% yours.  However, what you are 
  16 **      not permitted to do is to redistribute the source as "DSC Sample Code" 
  17 **      after having made changes. If you're going to re-distribute the source, 
  18 **      we require that you make it clear in the source that the code was 
  19 **      descended from Apple Sample Code, but that you've made changes. 
  28 #define __COMPILINGMOREFILES 
  36 /*****************************************************************************/ 
  42         dirCopyBigCopyBuffSize  
= 0x00004000, 
  43         dirCopyMinCopyBuffSize  
= 0x00000200 
  47 /*****************************************************************************/ 
  49 /* local data structures */ 
  51 /* The EnumerateGlobals structure is used to minimize the amount of 
  52 ** stack space used when recursively calling CopyLevel and to hold 
  53 ** global information that might be needed at any time. */ 
  55 #if PRAGMA_ALIGN_SUPPORTED 
  56 #pragma options align=mac68k 
  58 struct EnumerateGlobals
 
  60         Ptr                     copyBuffer
;                     /* pointer to buffer used for file copy operations */ 
  61         long            bufferSize
;                     /* the size of the copy buffer */ 
  62         CopyErrProcPtr errorHandler
;    /* pointer to error handling function */ 
  63         CopyFilterProcPtr copyFilterProc
; /* pointer to filter function */ 
  64         OSErr           error
;                          /* temporary holder of results - saves 2 bytes of stack each level */ 
  65         Boolean         bailout
;                        /* set to true to by error handling function if fatal error */ 
  66         short           destinationVRefNum
;     /* the destination vRefNum */ 
  67         Str63           itemName
;                       /* the name of the current item */ 
  68         CInfoPBRec      myCPB
;                          /* the parameter block used for PBGetCatInfo calls */ 
  70 #if PRAGMA_ALIGN_SUPPORTED 
  71 #pragma options align=reset 
  74 typedef struct EnumerateGlobals EnumerateGlobals
; 
  75 typedef EnumerateGlobals 
*EnumerateGlobalsPtr
; 
  78 /* The PreflightGlobals structure is used to minimize the amount of 
  79 ** stack space used when recursively calling GetLevelSize and to hold 
  80 ** global information that might be needed at any time. */ 
  82 #if PRAGMA_ALIGN_SUPPORTED 
  83 #pragma options align=mac68k 
  85 struct PreflightGlobals
 
  87         OSErr                   result
;                         /* temporary holder of results - saves 2 bytes of stack each level */ 
  88         Str63                   itemName
;                       /* the name of the current item */ 
  89         CInfoPBRec              myCPB
;                          /* the parameter block used for PBGetCatInfo calls */ 
  91         unsigned long   dstBlksPerAllocBlk
;     /* the number of 512 byte blocks per allocation block on destination */ 
  93         unsigned long   allocBlksNeeded
;        /* the total number of allocation blocks needed  */ 
  95         unsigned long   tempBlocks
;                     /* temporary storage for calculations (save some stack space)  */ 
  96         CopyFilterProcPtr copyFilterProc
;       /* pointer to filter function */ 
  98 #if PRAGMA_ALIGN_SUPPORTED 
  99 #pragma options align=reset 
 102 typedef struct PreflightGlobals PreflightGlobals
; 
 103 typedef PreflightGlobals 
*PreflightGlobalsPtr
; 
 105 /*****************************************************************************/ 
 107 /* static prototypes */ 
 109 static  void    GetLevelSize(long currentDirID
, 
 110                                                          PreflightGlobals 
*theGlobals
); 
 112 static  OSErr   
PreflightDirectoryCopySpace(short srcVRefNum
, 
 115                                                                                         CopyFilterProcPtr copyFilterProc
, 
 118 static  void    CopyLevel(long sourceDirID
, 
 120                                                   EnumerateGlobals 
*theGlobals
); 
 122 /*****************************************************************************/ 
 124 static  void    GetLevelSize(long currentDirID
, 
 125                                                          PreflightGlobals 
*theGlobals
) 
 131                 theGlobals
->myCPB
.dirInfo
.ioFDirIndex 
= index
; 
 132                 theGlobals
->myCPB
.dirInfo
.ioDrDirID 
= currentDirID
;     /* we need to do this every time */ 
 133                                                                                                                         /* through, since GetCatInfo  */ 
 134                                                                                                                         /* returns ioFlNum in this field */ 
 135                 theGlobals
->result 
= PBGetCatInfoSync(&theGlobals
->myCPB
); 
 136                 if ( theGlobals
->result 
== noErr 
) 
 138                         if ( (theGlobals
->copyFilterProc 
== NULL
) || 
 139                                  CallCopyFilterProc(theGlobals
->copyFilterProc
, &theGlobals
->myCPB
) ) /* filter if filter proc was supplied */ 
 141                                 /* Either there's no filter proc OR the filter proc says to use this item */ 
 142                                 if ( (theGlobals
->myCPB
.dirInfo
.ioFlAttrib 
& ioDirMask
) != 0 ) 
 144                                         /* we have a directory */ 
 146                                         GetLevelSize(theGlobals
->myCPB
.dirInfo
.ioDrDirID
, theGlobals
); /* recurse */ 
 147                                         theGlobals
->result 
= noErr
; /* clear error return on way back */ 
 151                                         /* We have a file - add its allocation blocks to allocBlksNeeded. */ 
 152                                         /* Since space on Mac OS disks is always allocated in allocation blocks, */ 
 153                                         /* this takes into account rounding up to the end of an allocation block. */ 
 155                                         /* get number of 512-byte blocks needed for data fork */ 
 156                                         if ( ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlLgLen 
& 0x000001ff) != 0 ) 
 158                                                 theGlobals
->tempBlocks 
= ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlLgLen 
>> 9) + 1; 
 162                                                 theGlobals
->tempBlocks 
= (unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlLgLen 
>> 9; 
 164                                         /* now, calculate number of new allocation blocks needed for the data fork and add it to the total */ 
 165                                         if ( theGlobals
->tempBlocks 
% theGlobals
->dstBlksPerAllocBlk 
) 
 167                                                 theGlobals
->allocBlksNeeded 
+= (theGlobals
->tempBlocks 
/ theGlobals
->dstBlksPerAllocBlk
) + 1; 
 171                                                 theGlobals
->allocBlksNeeded 
+= theGlobals
->tempBlocks 
/ theGlobals
->dstBlksPerAllocBlk
; 
 174                                         /* get number of 512-byte blocks needed for resource fork */ 
 175                                         if ( ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlRLgLen 
& 0x000001ff) != 0 ) 
 177                                                 theGlobals
->tempBlocks 
= ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlRLgLen 
>> 9) + 1; 
 181                                                 theGlobals
->tempBlocks 
= (unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlRLgLen 
>> 9; 
 183                                         /* now, calculate number of new allocation blocks needed for the resource  fork and add it to the total */ 
 184                                         if ( theGlobals
->tempBlocks 
% theGlobals
->dstBlksPerAllocBlk 
) 
 186                                                 theGlobals
->allocBlksNeeded 
+= (theGlobals
->tempBlocks 
/ theGlobals
->dstBlksPerAllocBlk
) + 1; 
 190                                                 theGlobals
->allocBlksNeeded 
+= theGlobals
->tempBlocks 
/ theGlobals
->dstBlksPerAllocBlk
; 
 196         } while ( theGlobals
->result 
== noErr 
); 
 199 /*****************************************************************************/ 
 201 static  OSErr   
PreflightDirectoryCopySpace(short srcVRefNum
, 
 204                                                                                         CopyFilterProcPtr copyFilterProc
, 
 209         unsigned long dstFreeBlocks
; 
 210         PreflightGlobals theGlobals
; 
 212         error 
= XGetVolumeInfoNoName(NULL
, dstVRefNum
, &pb
); 
 213         if ( error 
== noErr 
) 
 215                 /* Convert freeBytes to free disk blocks (512-byte blocks) */ 
 216                 dstFreeBlocks 
= (pb
.ioVFreeBytes
.hi 
<< 23) + (pb
.ioVFreeBytes
.lo 
>> 9); 
 218                 /* get allocation block size (always multiple of 512) and divide by 512 
 219                   to get number of 512-byte blocks per allocation block */ 
 220                 theGlobals
.dstBlksPerAllocBlk 
= ((unsigned long)pb
.ioVAlBlkSiz 
>> 9); 
 222                 theGlobals
.allocBlksNeeded 
= 0; 
 224                 theGlobals
.myCPB
.dirInfo
.ioNamePtr 
= theGlobals
.itemName
; 
 225                 theGlobals
.myCPB
.dirInfo
.ioVRefNum 
= srcVRefNum
; 
 227                 theGlobals
.copyFilterProc 
= copyFilterProc
; 
 229                 GetLevelSize(srcDirID
, &theGlobals
); 
 231                 /* Is there enough room on the destination volume for the source file?                                  */ 
 232                 /* Note:        This will work because the largest number of disk blocks supported                      */ 
 233                 /*                      on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk)        */ 
 234                 /*                      will always be less than 0xffffffff.                                                                            */ 
 235                 *spaceOK 
= ((theGlobals
.allocBlksNeeded 
* theGlobals
.dstBlksPerAllocBlk
) <= dstFreeBlocks
); 
 241 /*****************************************************************************/ 
 243 static  void    CopyLevel(long sourceDirID
, 
 245                                                   EnumerateGlobals 
*theGlobals
) 
 247         long currentSrcDirID
; 
 253                 /* Get next source item at the current directory level */ 
 255                 theGlobals
->myCPB
.dirInfo
.ioFDirIndex 
= index
; 
 256                 theGlobals
->myCPB
.dirInfo
.ioDrDirID 
= sourceDirID
; 
 257                 theGlobals
->error 
= PBGetCatInfoSync(&theGlobals
->myCPB
);                
 259                 if ( theGlobals
->error 
== noErr 
) 
 261                         if ( (theGlobals
->copyFilterProc 
== NULL
) || 
 262                                  CallCopyFilterProc(theGlobals
->copyFilterProc
, &theGlobals
->myCPB
) ) /* filter if filter proc was supplied */ 
 264                                 /* Either there's no filter proc OR the filter proc says to use this item */ 
 266                                 /* We have an item.  Is it a file or directory? */ 
 267                                 if ( (theGlobals
->myCPB
.hFileInfo
.ioFlAttrib 
& ioDirMask
) != 0 ) 
 269                                         /* We have a directory */ 
 271                                         /* Create a new directory at the destination. No errors allowed! */ 
 272                                         theGlobals
->error 
= DirCreate(theGlobals
->destinationVRefNum
, dstDirID
, theGlobals
->itemName
, &newDirID
); 
 273                                         if ( theGlobals
->error 
== noErr 
) 
 275                                                 /* Save the current source directory ID where we can get it when we come back 
 276                                                 ** from recursion land. */ 
 277                                                 currentSrcDirID 
= theGlobals
->myCPB
.dirInfo
.ioDrDirID
; 
 279                                                 /* Dive again (copy the directory level we just found below this one) */ 
 280                                                 CopyLevel(theGlobals
->myCPB
.dirInfo
.ioDrDirID
, newDirID
, theGlobals
); 
 282                                                 if ( !theGlobals
->bailout 
) 
 284                                                         /* Copy comment from old to new directory. */ 
 285                                                         /* Ignore the result because we really don't care if it worked or not. */ 
 286                                                         (void) DTCopyComment(theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
, theGlobals
->destinationVRefNum
, newDirID
, NULL
); 
 288                                                         /* Copy directory attributes (dates, etc.) to newDirID. */ 
 289                                                         /* No errors allowed */ 
 290                                                         theGlobals
->error 
= CopyFileMgrAttributes(theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
, theGlobals
->destinationVRefNum
, newDirID
, NULL
, true); 
 292                                                         /* handle any errors from CopyFileMgrAttributes */ 
 293                                                         if ( theGlobals
->error 
!= noErr 
) 
 295                                                                 if ( theGlobals
->errorHandler 
!= NULL 
) 
 297                                                                         theGlobals
->bailout 
=  CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, copyDirFMAttributesOp
, 
 298                                                                                                                         theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
, 
 299                                                                                                                         theGlobals
->destinationVRefNum
, newDirID
, NULL
); 
 303                                                                         /* If you don't handle the errors with an error handler, */ 
 304                                                                         /* then the copy stops here. */ 
 305                                                                         theGlobals
->bailout 
= true; 
 310                                         else    /* error handling for DirCreate */ 
 312                                                 if ( theGlobals
->errorHandler 
!= NULL 
) 
 314                                                         theGlobals
->bailout 
= CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, dirCreateOp
, 
 315                                                                                                                 theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
, 
 316                                                                                                                 theGlobals
->destinationVRefNum
, dstDirID
, theGlobals
->itemName
); 
 320                                                         /* If you don't handle the errors with an error handler, */ 
 321                                                         /* then the copy stops here. */ 
 322                                                         theGlobals
->bailout 
= true; 
 326                                         if ( !theGlobals
->bailout 
) 
 328                                                 /* clear error return on way back if we aren't bailing out */ 
 329                                                 theGlobals
->error 
= noErr
; 
 334                                         /* We have a file, so copy it */ 
 336                                         theGlobals
->error 
= FileCopy(theGlobals
->myCPB
.hFileInfo
.ioVRefNum
, 
 337                                                                                                  theGlobals
->myCPB
.hFileInfo
.ioFlParID
, 
 338                                                                                                  theGlobals
->itemName
, 
 339                                                                                                  theGlobals
->destinationVRefNum
, 
 343                                                                                                  theGlobals
->copyBuffer
, 
 344                                                                                                  theGlobals
->bufferSize
, 
 347                                         /* handle any errors from FileCopy */ 
 348                                         if ( theGlobals
->error 
!= noErr 
) 
 350                                                 if ( theGlobals
->errorHandler 
!= NULL 
) 
 352                                                         theGlobals
->bailout 
= CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, fileCopyOp
, 
 353                                                                                                         theGlobals
->myCPB
.hFileInfo
.ioVRefNum
, theGlobals
->myCPB
.hFileInfo
.ioFlParID
, theGlobals
->itemName
, 
 354                                                                                                         theGlobals
->destinationVRefNum
, dstDirID
, NULL
); 
 355                                                         if ( !theGlobals
->bailout 
) 
 357                                                                 /* If the CopyErrProc handled the problem, clear the error here */ 
 358                                                                 theGlobals
->error 
= noErr
; 
 363                                                         /* If you don't handle the errors with an error handler, */ 
 364                                                         /* then the copy stops here. */ 
 365                                                         theGlobals
->bailout 
= true; 
 372                 {       /* error handling for PBGetCatInfo */ 
 373                         /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */ 
 374                         if ( theGlobals
->error 
!= fnfErr 
) 
 376                                 if ( theGlobals
->errorHandler 
!= NULL 
) 
 378                                         theGlobals
->bailout 
= CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, getNextItemOp
, 
 379                                                                                         theGlobals
->myCPB
.dirInfo
.ioVRefNum
, sourceDirID
, NULL
, 0, 0, NULL
); 
 380                                         if ( !theGlobals
->bailout 
) 
 382                                                 /* If the CopyErrProc handled the problem, clear the error here */ 
 383                                                 theGlobals
->error 
= noErr
; 
 388                                         /* If you don't handle the errors with an error handler, */ 
 389                                         /* then the copy stops here. */ 
 390                                         theGlobals
->bailout 
= true; 
 394                 ++index
; /* prepare to get next item */ 
 395         } while ( (theGlobals
->error 
== noErr
) && (!theGlobals
->bailout
) ); /* time to fall back a level? */ 
 398 /*****************************************************************************/ 
 400 pascal  OSErr   
FilteredDirectoryCopy(short srcVRefNum
, 
 402                                                                           ConstStr255Param srcName
, 
 405                                                                           ConstStr255Param dstName
, 
 409                                                                           CopyErrProcPtr copyErrHandler
, 
 410                                                                           CopyFilterProcPtr copyFilterProc
) 
 412         EnumerateGlobals theGlobals
; 
 415         Boolean ourCopyBuffer 
= false; 
 416         Str63   srcDirName
, oldDiskName
; 
 419         /* Make sure a copy buffer is allocated. */ 
 420         if ( copyBufferPtr 
== NULL 
) 
 422                 /* The caller didn't supply a copy buffer so grab one from the application heap. 
 423                 ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer. 
 424                 ** If 512 bytes aren't available, we're in trouble. */ 
 425                 copyBufferSize 
= dirCopyBigCopyBuffSize
; 
 426                 copyBufferPtr 
= NewPtr(copyBufferSize
); 
 427                 if ( copyBufferPtr 
== NULL 
) 
 429                         copyBufferSize 
= dirCopyMinCopyBuffSize
; 
 430                         copyBufferPtr 
= NewPtr(copyBufferSize
); 
 431                         if ( copyBufferPtr 
== NULL 
) 
 433                                 return ( memFullErr 
); 
 436                 ourCopyBuffer 
= true; 
 439         /* Get the real dirID where we're copying from and make sure it is a directory. */ 
 440         error 
= GetDirectoryID(srcVRefNum
, srcDirID
, srcName
, &srcDirID
, &isDirectory
); 
 441         if ( error 
!= noErr 
) 
 451         /* Special case destination if it is the root parent directory. */ 
 452         /* Since you can't create the root directory, this is needed if */ 
 453         /* you want to copy a directory's content to a disk's root directory. */ 
 454         if ( (dstDirID 
== fsRtParID
) && (dstName 
== NULL
) ) 
 456                 dstDirID 
= fsRtParID
; 
 462                 /*  Get the real dirID where we're going to put the copy and make sure it is a directory. */ 
 463                 error 
= GetDirectoryID(dstVRefNum
, dstDirID
, dstName
, &dstDirID
, &isDirectory
); 
 464                 if ( error 
!= noErr 
) 
 475         /* Get the real vRefNum of both the source and destination */ 
 476         error 
= DetermineVRefNum(srcName
, srcVRefNum
, &srcVRefNum
); 
 477         if ( error 
!= noErr 
) 
 481         error 
= DetermineVRefNum(dstName
, dstVRefNum
, &dstVRefNum
); 
 482         if ( error 
!= noErr 
) 
 489                 error 
= PreflightDirectoryCopySpace(srcVRefNum
, srcDirID
, dstVRefNum
, copyFilterProc
, &spaceOK
); 
 490                 if ( error 
!= noErr 
) 
 496                         error 
= dskFulErr
; /* not enough room on destination */ 
 501         /* Create the new directory in the destination directory with the */ 
 502         /* same name as the source directory. */ 
 503         error 
= GetDirName(srcVRefNum
, srcDirID
, srcDirName
); 
 504         if ( error 
!= noErr 
) 
 509         /* Again, special case destination if the destination is the */ 
 510         /* root parent directory. This time, we'll rename the disk to */ 
 511         /* the source directory name. */ 
 512         if ( dstDirID 
== fsRtParID 
) 
 514                 /* Get the current name of the destination disk */ 
 515                 error 
= GetDirName(dstVRefNum
, fsRtDirID
, oldDiskName
); 
 516                 if ( error 
== noErr 
)    
 518                         /* Shorten the name if it's too long to be the volume name */ 
 519                         TruncPString(srcDirName
, srcDirName
, 27); 
 521                         /* Rename the disk */ 
 522                         error 
= HRename(dstVRefNum
, fsRtParID
, oldDiskName
, srcDirName
); 
 523                         /* and copy to the root directory */ 
 524                         dstDirID 
= fsRtDirID
; 
 529                 error 
= DirCreate(dstVRefNum
, dstDirID
, srcDirName
, &dstDirID
); 
 531         if ( error 
!= noErr 
) 
 533                 /* handle any errors from DirCreate */ 
 534                 if ( copyErrHandler 
!= NULL 
) 
 536                         if ( CallCopyErrProc(copyErrHandler
, error
, dirCreateOp
, 
 537                                                                                                         srcVRefNum
, srcDirID
, NULL
, 
 538                                                                                                         dstVRefNum
, dstDirID
, srcDirName
) ) 
 544                                 /* If the CopyErrProc handled the problem, clear the error here */ 
 551                         /* If you don't handle the errors with an error handler, */ 
 552                         /* then the copy stops here. */ 
 557         /* dstDirID is now the newly created directory! */ 
 559         /* Set up the globals we need to access from the recursive routine. */ 
 560         theGlobals
.copyBuffer 
= (Ptr
)copyBufferPtr
; 
 561         theGlobals
.bufferSize 
= copyBufferSize
; 
 562         theGlobals
.destinationVRefNum 
= dstVRefNum
; /* so we can get to it always */ 
 563         theGlobals
.myCPB
.hFileInfo
.ioNamePtr 
= (StringPtr
)&theGlobals
.itemName
; 
 564         theGlobals
.myCPB
.hFileInfo
.ioVRefNum 
= srcVRefNum
; 
 565         theGlobals
.errorHandler 
= copyErrHandler
; 
 566         theGlobals
.bailout 
= false; 
 567         theGlobals
.copyFilterProc 
=  copyFilterProc
; 
 569         /* Here we go into recursion land... */ 
 570         CopyLevel(srcDirID
, dstDirID
, &theGlobals
); 
 571         error 
= theGlobals
.error
;       /* get the result */ 
 573         if ( !theGlobals
.bailout 
) 
 575                 /* Copy comment from source to destination directory. */ 
 576                 /* Ignore the result because we really don't care if it worked or not. */ 
 577                 (void) DTCopyComment(srcVRefNum
, srcDirID
, NULL
, dstVRefNum
, dstDirID
, NULL
); 
 579                 /* Copy the File Manager attributes */ 
 580                 error 
= CopyFileMgrAttributes(srcVRefNum
, srcDirID
, NULL
, 
 581                                         dstVRefNum
, dstDirID
, NULL
, true); 
 583                 /* handle any errors from CopyFileMgrAttributes */ 
 584                 if ( (error 
!= noErr
) && (copyErrHandler 
!= NULL
) ) 
 586                         theGlobals
.bailout 
= CallCopyErrProc(copyErrHandler
, error
, copyDirFMAttributesOp
, 
 587                                                                                                 srcVRefNum
, srcDirID
, NULL
, 
 588                                                                                                 dstVRefNum
, dstDirID
, NULL
); 
 593         /* Get rid of the copy buffer if we allocated it. */ 
 596                 DisposePtr((Ptr
)copyBufferPtr
); 
 602 /*****************************************************************************/ 
 604 pascal  OSErr   
DirectoryCopy(short srcVRefNum
, 
 606                                                           ConstStr255Param srcName
, 
 609                                                           ConstStr255Param dstName
, 
 613                                                           CopyErrProcPtr copyErrHandler
) 
 615         return ( FilteredDirectoryCopy(srcVRefNum
, srcDirID
, srcName
, 
 616                                                                    dstVRefNum
, dstDirID
, dstName
, 
 617                                                                    copyBufferPtr
, copyBufferSize
, preflight
, 
 618                                                                    copyErrHandler
, NULL
) ); 
 621 /*****************************************************************************/ 
 623 pascal  OSErr   
FSpFilteredDirectoryCopy(const FSSpec 
*srcSpec
, 
 624                                                                                  const FSSpec 
*dstSpec
, 
 628                                                                                  CopyErrProcPtr copyErrHandler
, 
 629                                                                                  CopyFilterProcPtr copyFilterProc
) 
 631         return ( FilteredDirectoryCopy(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
, 
 632                                                                    dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
, 
 633                                                                    copyBufferPtr
, copyBufferSize
, preflight
, 
 634                                                                    copyErrHandler
, copyFilterProc
) ); 
 637 /*****************************************************************************/ 
 639 pascal  OSErr   
FSpDirectoryCopy(const FSSpec 
*srcSpec
, 
 640                                                                  const FSSpec 
*dstSpec
, 
 644                                                                  CopyErrProcPtr copyErrHandler
) 
 646         return ( FilteredDirectoryCopy(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
, 
 647                                                                    dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
, 
 648                                                                    copyBufferPtr
, copyBufferSize
, preflight
, 
 649                                                                    copyErrHandler
, NULL
) ); 
 652 /*****************************************************************************/