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.
22 // Modified to allow renaming the destination folder
30 #define __COMPILINGMOREFILES
32 #include "MoreFiles.h"
33 #include "MoreFilesExtras.h"
34 #include "MoreDesktopMgr.h"
36 #include "MacDirectoryCopy.h"
40 /*****************************************************************************/
44 getNextItemOp
= 1, /* couldn't access items in this directory - no access privileges */
45 copyDirCommentOp
= 2, /* couldn't copy directory's Finder comment */
46 copyDirAccessPrivsOp
= 3, /* couldn't copy directory's AFP access privileges */
47 copyDirFMAttributesOp
= 4, /* couldn't copy directory's File Manager attributes */
48 dirCreateOp
= 5, /* couldn't create destination directory */
49 fileCopyOp
= 6 /* couldn't copy file */
52 /*****************************************************************************/
56 #define CallCopyErrProc(userRoutine, error, failedOperation, srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName) \
57 (*(userRoutine))((error), (failedOperation), (srcVRefNum), (srcDirID), (srcName), (dstVRefNum), (dstDirID), (dstName))
59 /*****************************************************************************/
61 typedef pascal Boolean (*CopyFilterProcPtr
) (const CInfoPBRec
* const cpbPtr
);
63 /* ¦ Prototype for the CopyFilterProc function.
64 This is the prototype for the CopyFilterProc function called by
65 FilteredDirectoryCopy and GetLevelSize. If true is returned,
66 the file/folder is included in the copy, otherwise it is excluded.
68 pb input: Points to the CInfoPBRec for the item under consideration.
72 Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy
75 #define CallCopyFilterProc(userRoutine, cpbPtr) (*(userRoutine))((cpbPtr))
79 /*****************************************************************************/
85 dirCopyBigCopyBuffSize
= 0x00004000,
86 dirCopyMinCopyBuffSize
= 0x00000200
90 /*****************************************************************************/
92 /* local data structures */
94 /* The EnumerateGlobals structure is used to minimize the amount of
95 ** stack space used when recursively calling CopyLevel and to hold
96 ** global information that might be needed at any time. */
98 #if PRAGMA_STRUCT_ALIGN
99 #pragma options align=mac68k
101 struct EnumerateGlobals
103 Ptr copyBuffer
; /* pointer to buffer used for file copy operations */
104 long bufferSize
; /* the size of the copy buffer */
105 CopyErrProcPtr errorHandler
; /* pointer to error handling function */
106 CopyFilterProcPtr copyFilterProc
; /* pointer to filter function */
107 OSErr error
; /* temporary holder of results - saves 2 bytes of stack each level */
108 Boolean bailout
; /* set to true to by error handling function if fatal error */
109 short destinationVRefNum
; /* the destination vRefNum */
110 Str63 itemName
; /* the name of the current item */
111 CInfoPBRec myCPB
; /* the parameter block used for PBGetCatInfo calls */
113 #if PRAGMA_STRUCT_ALIGN
114 #pragma options align=reset
117 typedef struct EnumerateGlobals EnumerateGlobals
;
118 typedef EnumerateGlobals
*EnumerateGlobalsPtr
;
121 /* The PreflightGlobals structure is used to minimize the amount of
122 ** stack space used when recursively calling GetLevelSize and to hold
123 ** global information that might be needed at any time. */
125 #if PRAGMA_STRUCT_ALIGN
126 #pragma options align=mac68k
128 struct PreflightGlobals
130 OSErr result
; /* temporary holder of results - saves 2 bytes of stack each level */
131 Str63 itemName
; /* the name of the current item */
132 CInfoPBRec myCPB
; /* the parameter block used for PBGetCatInfo calls */
134 unsigned long dstBlksPerAllocBlk
; /* the number of 512 byte blocks per allocation block on destination */
136 unsigned long allocBlksNeeded
; /* the total number of allocation blocks needed */
138 unsigned long tempBlocks
; /* temporary storage for calculations (save some stack space) */
139 CopyFilterProcPtr copyFilterProc
; /* pointer to filter function */
141 #if PRAGMA_STRUCT_ALIGN
142 #pragma options align=reset
145 typedef struct PreflightGlobals PreflightGlobals
;
146 typedef PreflightGlobals
*PreflightGlobalsPtr
;
148 /*****************************************************************************/
150 /* static prototypes */
152 static void GetLevelSize(long currentDirID
,
153 PreflightGlobals
*theGlobals
);
155 static OSErr
PreflightDirectoryCopySpace(short srcVRefNum
,
158 CopyFilterProcPtr copyFilterProc
,
161 static void CopyLevel(long sourceDirID
,
163 EnumerateGlobals
*theGlobals
);
165 /*****************************************************************************/
167 static void GetLevelSize(long currentDirID
,
168 PreflightGlobals
*theGlobals
)
174 theGlobals
->myCPB
.dirInfo
.ioFDirIndex
= index
;
175 theGlobals
->myCPB
.dirInfo
.ioDrDirID
= currentDirID
; /* we need to do this every time */
176 /* through, since GetCatInfo */
177 /* returns ioFlNum in this field */
178 theGlobals
->result
= PBGetCatInfoSync(&theGlobals
->myCPB
);
179 if ( theGlobals
->result
== noErr
)
181 if ( (theGlobals
->copyFilterProc
== NULL
) ||
182 CallCopyFilterProc(theGlobals
->copyFilterProc
, &theGlobals
->myCPB
) ) /* filter if filter proc was supplied */
184 /* Either there's no filter proc OR the filter proc says to use this item */
185 if ( (theGlobals
->myCPB
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
187 /* we have a directory */
189 GetLevelSize(theGlobals
->myCPB
.dirInfo
.ioDrDirID
, theGlobals
); /* recurse */
190 theGlobals
->result
= noErr
; /* clear error return on way back */
194 /* We have a file - add its allocation blocks to allocBlksNeeded. */
195 /* Since space on Mac OS disks is always allocated in allocation blocks, */
196 /* this takes into account rounding up to the end of an allocation block. */
198 /* get number of 512-byte blocks needed for data fork */
199 if ( ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlLgLen
& 0x000001ff) != 0 )
201 theGlobals
->tempBlocks
= ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlLgLen
>> 9) + 1;
205 theGlobals
->tempBlocks
= (unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlLgLen
>> 9;
207 /* now, calculate number of new allocation blocks needed for the data fork and add it to the total */
208 if ( theGlobals
->tempBlocks
% theGlobals
->dstBlksPerAllocBlk
)
210 theGlobals
->allocBlksNeeded
+= (theGlobals
->tempBlocks
/ theGlobals
->dstBlksPerAllocBlk
) + 1;
214 theGlobals
->allocBlksNeeded
+= theGlobals
->tempBlocks
/ theGlobals
->dstBlksPerAllocBlk
;
217 /* get number of 512-byte blocks needed for resource fork */
218 if ( ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlRLgLen
& 0x000001ff) != 0 )
220 theGlobals
->tempBlocks
= ((unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlRLgLen
>> 9) + 1;
224 theGlobals
->tempBlocks
= (unsigned long)theGlobals
->myCPB
.hFileInfo
.ioFlRLgLen
>> 9;
226 /* now, calculate number of new allocation blocks needed for the resource fork and add it to the total */
227 if ( theGlobals
->tempBlocks
% theGlobals
->dstBlksPerAllocBlk
)
229 theGlobals
->allocBlksNeeded
+= (theGlobals
->tempBlocks
/ theGlobals
->dstBlksPerAllocBlk
) + 1;
233 theGlobals
->allocBlksNeeded
+= theGlobals
->tempBlocks
/ theGlobals
->dstBlksPerAllocBlk
;
239 } while ( theGlobals
->result
== noErr
);
242 /*****************************************************************************/
244 static OSErr
PreflightDirectoryCopySpace(short srcVRefNum
,
247 CopyFilterProcPtr copyFilterProc
,
252 unsigned long dstFreeBlocks
;
253 PreflightGlobals theGlobals
;
255 error
= XGetVolumeInfoNoName(NULL
, dstVRefNum
, &pb
);
256 if ( error
== noErr
)
258 /* Convert freeBytes to free disk blocks (512-byte blocks) */
259 dstFreeBlocks
= (pb
.ioVFreeBytes
>> 9);
261 /* get allocation block size (always multiple of 512) and divide by 512
262 to get number of 512-byte blocks per allocation block */
263 theGlobals
.dstBlksPerAllocBlk
= ((unsigned long)pb
.ioVAlBlkSiz
>> 9);
265 theGlobals
.allocBlksNeeded
= 0;
267 theGlobals
.myCPB
.dirInfo
.ioNamePtr
= theGlobals
.itemName
;
268 theGlobals
.myCPB
.dirInfo
.ioVRefNum
= srcVRefNum
;
270 theGlobals
.copyFilterProc
= copyFilterProc
;
272 GetLevelSize(srcDirID
, &theGlobals
);
274 /* Is there enough room on the destination volume for the source file? */
275 /* Note: This will work because the largest number of disk blocks supported */
276 /* on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk) */
277 /* will always be less than 0xffffffff. */
278 *spaceOK
= ((theGlobals
.allocBlksNeeded
* theGlobals
.dstBlksPerAllocBlk
) <= dstFreeBlocks
);
284 /*****************************************************************************/
286 static void CopyLevel(long sourceDirID
,
288 EnumerateGlobals
*theGlobals
)
290 long currentSrcDirID
;
296 /* Get next source item at the current directory level */
298 theGlobals
->myCPB
.dirInfo
.ioFDirIndex
= index
;
299 theGlobals
->myCPB
.dirInfo
.ioDrDirID
= sourceDirID
;
300 theGlobals
->error
= PBGetCatInfoSync(&theGlobals
->myCPB
);
302 if ( theGlobals
->error
== noErr
)
304 if ( (theGlobals
->copyFilterProc
== NULL
) ||
305 CallCopyFilterProc(theGlobals
->copyFilterProc
, &theGlobals
->myCPB
) ) /* filter if filter proc was supplied */
307 /* Either there's no filter proc OR the filter proc says to use this item */
309 /* We have an item. Is it a file or directory? */
310 if ( (theGlobals
->myCPB
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
312 /* We have a directory */
314 /* Create a new directory at the destination. No errors allowed! */
315 theGlobals
->error
= DirCreate(theGlobals
->destinationVRefNum
, dstDirID
, theGlobals
->itemName
, &newDirID
);
316 if ( theGlobals
->error
== noErr
)
318 /* Save the current source directory ID where we can get it when we come back
319 ** from recursion land. */
320 currentSrcDirID
= theGlobals
->myCPB
.dirInfo
.ioDrDirID
;
322 /* Dive again (copy the directory level we just found below this one) */
323 CopyLevel(theGlobals
->myCPB
.dirInfo
.ioDrDirID
, newDirID
, theGlobals
);
325 if ( !theGlobals
->bailout
)
327 /* Copy comment from old to new directory. */
328 /* Ignore the result because we really don't care if it worked or not. */
329 (void) DTCopyComment(theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
, theGlobals
->destinationVRefNum
, newDirID
, NULL
);
331 /* Copy directory attributes (dates, etc.) to newDirID. */
332 /* No errors allowed */
333 theGlobals
->error
= CopyFileMgrAttributes(theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
, theGlobals
->destinationVRefNum
, newDirID
, NULL
, true);
335 /* handle any errors from CopyFileMgrAttributes */
336 if ( theGlobals
->error
!= noErr
)
338 if ( theGlobals
->errorHandler
!= NULL
)
340 theGlobals
->bailout
= CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, copyDirFMAttributesOp
,
341 theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
,
342 theGlobals
->destinationVRefNum
, newDirID
, NULL
);
346 /* If you don't handle the errors with an error handler, */
347 /* then the copy stops here. */
348 theGlobals
->bailout
= true;
353 else /* error handling for DirCreate */
355 if ( theGlobals
->errorHandler
!= NULL
)
357 theGlobals
->bailout
= CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, dirCreateOp
,
358 theGlobals
->myCPB
.dirInfo
.ioVRefNum
, currentSrcDirID
, NULL
,
359 theGlobals
->destinationVRefNum
, dstDirID
, theGlobals
->itemName
);
363 /* If you don't handle the errors with an error handler, */
364 /* then the copy stops here. */
365 theGlobals
->bailout
= true;
369 if ( !theGlobals
->bailout
)
371 /* clear error return on way back if we aren't bailing out */
372 theGlobals
->error
= noErr
;
377 /* We have a file, so copy it */
379 theGlobals
->error
= FileCopy(theGlobals
->myCPB
.hFileInfo
.ioVRefNum
,
380 theGlobals
->myCPB
.hFileInfo
.ioFlParID
,
381 theGlobals
->itemName
,
382 theGlobals
->destinationVRefNum
,
386 theGlobals
->copyBuffer
,
387 theGlobals
->bufferSize
,
390 /* handle any errors from FileCopy */
391 if ( theGlobals
->error
!= noErr
)
393 if ( theGlobals
->errorHandler
!= NULL
)
395 theGlobals
->bailout
= CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, fileCopyOp
,
396 theGlobals
->myCPB
.hFileInfo
.ioVRefNum
, theGlobals
->myCPB
.hFileInfo
.ioFlParID
, theGlobals
->itemName
,
397 theGlobals
->destinationVRefNum
, dstDirID
, NULL
);
398 if ( !theGlobals
->bailout
)
400 /* If the CopyErrProc handled the problem, clear the error here */
401 theGlobals
->error
= noErr
;
406 /* If you don't handle the errors with an error handler, */
407 /* then the copy stops here. */
408 theGlobals
->bailout
= true;
415 { /* error handling for PBGetCatInfo */
416 /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
417 if ( theGlobals
->error
!= fnfErr
)
419 if ( theGlobals
->errorHandler
!= NULL
)
421 theGlobals
->bailout
= CallCopyErrProc(theGlobals
->errorHandler
, theGlobals
->error
, getNextItemOp
,
422 theGlobals
->myCPB
.dirInfo
.ioVRefNum
, sourceDirID
, NULL
, 0, 0, NULL
);
423 if ( !theGlobals
->bailout
)
425 /* If the CopyErrProc handled the problem, clear the error here */
426 theGlobals
->error
= noErr
;
431 /* If you don't handle the errors with an error handler, */
432 /* then the copy stops here. */
433 theGlobals
->bailout
= true;
437 ++index
; /* prepare to get next item */
438 } while ( (theGlobals
->error
== noErr
) && (!theGlobals
->bailout
) ); /* time to fall back a level? */
441 /*****************************************************************************/
443 pascal OSErr
FilteredDirectoryCopy(short srcVRefNum
,
445 ConstStr255Param srcName
,
448 ConstStr255Param dstName
,
452 CopyErrProcPtr copyErrHandler
,
453 CopyFilterProcPtr copyFilterProc
, ConstStr255Param newName
);
454 /* ¦ Make a copy of a directory structure in a new location with item filtering.
455 The FilteredDirectoryCopy function makes a copy of a directory
456 structure in a new location. If copyBufferPtr <> NIL, it points to
457 a buffer of copyBufferSize that is used to copy files data. The
458 larger the supplied buffer, the faster the copy. If
459 copyBufferPtr = NIL, then this routine allocates a buffer in the
460 application heap. If you pass a copy buffer to this routine, make
461 its size a multiple of 512 ($200) bytes for optimum performance.
463 The optional copyFilterProc parameter lets a routine you define
464 decide what files or directories are copied to the destination.
466 FilteredDirectoryCopy normally creates a new directory *in* the
467 specified destination directory and copies the source directory's
468 content into the new directory. However, if root parent directory
469 (fsRtParID) is passed as the dstDirID parameter and NULL is
470 passed as the dstName parameter, DirectoryCopy renames the
471 destination volume to the source directory's name (truncating
472 if the name is longer than 27 characters) and copies the source
473 directory's content into the destination volume's root directory.
474 This special case is supported by FilteredDirectoryCopy, but
475 not by FSpFilteredDirectoryCopy since with FSpFilteredDirectoryCopy,
476 the dstName parameter can not be NULL.
478 srcVRefNum input: Source volume specification.
479 srcDirID input: Source directory ID.
480 srcName input: Source directory name, or nil if
481 srcDirID specifies the directory.
482 dstVRefNum input: Destination volume specification.
483 dstDirID input: Destination directory ID.
484 dstName input: Destination directory name, or nil if
485 dstDirID specifies the directory.
486 copyBufferPtr input: Points to a buffer of copyBufferSize that
487 is used the i/o buffer for the copy or
488 nil if you want DirectoryCopy to allocate its
489 own buffer in the application heap.
490 copyBufferSize input: The size of the buffer pointed to
492 preflight input: If true, DirectoryCopy makes sure there are
493 enough allocation blocks on the destination
494 volume to hold the directory's files before
496 copyErrHandler input: A pointer to the routine you want called if an
497 error condition is detected during the copy, or
498 nil if you don't want to handle error conditions.
499 If you don't handle error conditions, the first
500 error will cause the copy to quit and
501 DirectoryCopy will return the error.
502 Error handling is recommended...
503 copyFilterProc input: A pointer to the filter routine you want called
504 for each item in the source directory, or NULL
505 if you don't want to filter.
509 readErr Ð19 Driver does not respond to read requests
510 writErr Ð20 Driver does not respond to write requests
511 badUnitErr Ð21 Driver reference number does not
513 unitEmptyErr Ð22 Driver reference number specifies a
514 nil handle in unit table
515 abortErr Ð27 Request aborted by KillIO
516 notOpenErr Ð28 Driver not open
517 dskFulErr -34 Destination volume is full
518 nsvErr -35 No such volume
520 bdNamErr -37 Bad filename
521 tmfoErr -42 Too many files open
522 fnfErr -43 Source file not found, or destination
523 directory does not exist
524 wPrErr -44 Volume locked by hardware
525 fLckdErr -45 File is locked
526 vLckdErr -46 Destination volume is read-only
527 fBsyErr -47 The source or destination file could
528 not be opened with the correct access
530 dupFNErr -48 Destination file already exists
531 opWrErr -49 File already open for writing
532 paramErr -50 No default volume or function not
534 permErr -54 File is already open and cannot be opened using specified deny modes
535 memFullErr -108 Copy buffer could not be allocated
536 dirNFErr -120 Directory not found or incomplete pathname
537 wrgVolTypErr -123 Function not supported by volume
538 afpAccessDenied -5000 User does not have the correct access
539 afpDenyConflict -5006 The source or destination file could
540 not be opened with the correct access
542 afpObjectTypeErr -5025 Source is a directory, directory not found
543 or incomplete pathname
547 Also see: CopyErrProcPtr, CopyFilterProcPtr, FSpFilteredDirectoryCopy,
548 DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy
551 /*****************************************************************************/
553 pascal OSErr
FilteredDirectoryCopy(short srcVRefNum
,
555 ConstStr255Param srcName
,
558 ConstStr255Param dstName
,
562 CopyErrProcPtr copyErrHandler
,
563 CopyFilterProcPtr copyFilterProc
, ConstStr255Param newName
)
565 EnumerateGlobals theGlobals
;
568 Boolean ourCopyBuffer
= false;
569 Str63 srcDirName
, oldDiskName
;
572 /* Make sure a copy buffer is allocated. */
573 if ( copyBufferPtr
== NULL
)
575 /* The caller didn't supply a copy buffer so grab one from the application heap.
576 ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
577 ** If 512 bytes aren't available, we're in trouble. */
578 copyBufferSize
= dirCopyBigCopyBuffSize
;
579 copyBufferPtr
= NewPtr(copyBufferSize
);
580 if ( copyBufferPtr
== NULL
)
582 copyBufferSize
= dirCopyMinCopyBuffSize
;
583 copyBufferPtr
= NewPtr(copyBufferSize
);
584 if ( copyBufferPtr
== NULL
)
586 return ( memFullErr
);
589 ourCopyBuffer
= true;
592 /* Get the real dirID where we're copying from and make sure it is a directory. */
593 error
= GetDirectoryID(srcVRefNum
, srcDirID
, srcName
, &srcDirID
, &isDirectory
);
594 if ( error
!= noErr
)
604 /* Special case destination if it is the root parent directory. */
605 /* Since you can't create the root directory, this is needed if */
606 /* you want to copy a directory's content to a disk's root directory. */
607 if ( (dstDirID
== fsRtParID
) && (dstName
== NULL
) )
609 dstDirID
= fsRtParID
;
615 /* Get the real dirID where we're going to put the copy and make sure it is a directory. */
616 error
= GetDirectoryID(dstVRefNum
, dstDirID
, dstName
, &dstDirID
, &isDirectory
);
617 if ( error
!= noErr
)
628 /* Get the real vRefNum of both the source and destination */
629 error
= DetermineVRefNum(srcName
, srcVRefNum
, &srcVRefNum
);
630 if ( error
!= noErr
)
634 error
= DetermineVRefNum(dstName
, dstVRefNum
, &dstVRefNum
);
635 if ( error
!= noErr
)
642 error
= PreflightDirectoryCopySpace(srcVRefNum
, srcDirID
, dstVRefNum
, copyFilterProc
, &spaceOK
);
643 if ( error
!= noErr
)
649 error
= dskFulErr
; /* not enough room on destination */
654 /* Create the new directory in the destination directory with the */
655 /* same name as the source directory. */
657 if newName is not empty use it rather than the original dir name.
659 if ( newName
[0] == 0 )
661 error
= GetDirName(srcVRefNum
, srcDirID
, srcDirName
);
662 if ( error
!= noErr
)
669 memcpy(srcDirName
, newName
, 32 );
672 /* Again, special case destination if the destination is the */
673 /* root parent directory. This time, we'll rename the disk to */
674 /* the source directory name. */
675 if ( dstDirID
== fsRtParID
)
677 /* Get the current name of the destination disk */
678 error
= GetDirName(dstVRefNum
, fsRtDirID
, oldDiskName
);
679 if ( error
== noErr
)
681 /* Shorten the name if it's too long to be the volume name */
682 TruncPString(srcDirName
, srcDirName
, 27);
684 /* Rename the disk */
685 error
= HRename(dstVRefNum
, fsRtParID
, oldDiskName
, srcDirName
);
686 /* and copy to the root directory */
687 dstDirID
= fsRtDirID
;
692 error
= DirCreate(dstVRefNum
, dstDirID
, srcDirName
, &dstDirID
);
694 if ( error
!= noErr
)
696 /* handle any errors from DirCreate */
697 if ( copyErrHandler
!= NULL
)
699 if ( CallCopyErrProc(copyErrHandler
, error
, dirCreateOp
,
700 srcVRefNum
, srcDirID
, NULL
,
701 dstVRefNum
, dstDirID
, srcDirName
) )
707 /* If the CopyErrProc handled the problem, clear the error here */
714 /* If you don't handle the errors with an error handler, */
715 /* then the copy stops here. */
720 /* dstDirID is now the newly created directory! */
722 /* Set up the globals we need to access from the recursive routine. */
723 theGlobals
.copyBuffer
= (Ptr
)copyBufferPtr
;
724 theGlobals
.bufferSize
= copyBufferSize
;
725 theGlobals
.destinationVRefNum
= dstVRefNum
; /* so we can get to it always */
726 theGlobals
.myCPB
.hFileInfo
.ioNamePtr
= (StringPtr
)&theGlobals
.itemName
;
727 theGlobals
.myCPB
.hFileInfo
.ioVRefNum
= srcVRefNum
;
728 theGlobals
.errorHandler
= copyErrHandler
;
729 theGlobals
.bailout
= false;
730 theGlobals
.copyFilterProc
= copyFilterProc
;
732 /* Here we go into recursion land... */
733 CopyLevel(srcDirID
, dstDirID
, &theGlobals
);
734 error
= theGlobals
.error
; /* get the result */
736 if ( !theGlobals
.bailout
)
738 /* Copy comment from source to destination directory. */
739 /* Ignore the result because we really don't care if it worked or not. */
740 (void) DTCopyComment(srcVRefNum
, srcDirID
, NULL
, dstVRefNum
, dstDirID
, NULL
);
742 /* Copy the File Manager attributes */
743 error
= CopyFileMgrAttributes(srcVRefNum
, srcDirID
, NULL
,
744 dstVRefNum
, dstDirID
, NULL
, true);
746 /* handle any errors from CopyFileMgrAttributes */
747 if ( (error
!= noErr
) && (copyErrHandler
!= NULL
) )
749 theGlobals
.bailout
= CallCopyErrProc(copyErrHandler
, error
, copyDirFMAttributesOp
,
750 srcVRefNum
, srcDirID
, NULL
,
751 dstVRefNum
, dstDirID
, NULL
);
756 /* Get rid of the copy buffer if we allocated it. */
759 DisposePtr((Ptr
)copyBufferPtr
);
766 /*****************************************************************************/
769 pascal OSErr
MacFSpDirectoryCopyRename(const FSSpec
*srcSpec
,
770 const FSSpec
*dstSpec
,
771 ConstStr255Param newName
,
775 CopyErrProcPtr copyErrHandler
)
777 return ( FilteredDirectoryCopy(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
778 dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
,
779 copyBufferPtr
, copyBufferSize
, preflight
,
780 copyErrHandler
, NULL
, newName
) );