On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / io / macDirectoryCopy.c
blob27cc82d142fac3227e3ae3086d313ab44325cc52
1 /*
2 ** Apple Macintosh Developer Technical Support
3 **
4 ** DirectoryCopy: A robust, general purpose directory copy routine.
5 **
6 ** by Jim Luther, Apple Developer Technical Support Emeritus
7 **
8 ** File: DirectoryCopy.c
9 **
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
24 #include <Types.h>
25 #include <Errors.h>
26 #include <Memory.h>
27 #include <Files.h>
28 #include <Script.h>
30 #define __COMPILINGMOREFILES
32 #include "MoreFiles.h"
33 #include "MoreFilesExtras.h"
34 #include "MoreDesktopMgr.h"
35 #include "FileCopy.h"
36 #include "MacDirectoryCopy.h"
37 #include <string.h>
40 /*****************************************************************************/
42 enum
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.
70 __________
72 Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy
75 #define CallCopyFilterProc(userRoutine, cpbPtr) (*(userRoutine))((cpbPtr))
79 /*****************************************************************************/
81 /* local constants */
83 enum
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
100 #endif
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
115 #endif
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
127 #endif
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
143 #endif
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,
156 long srcDirID,
157 short dstVRefNum,
158 CopyFilterProcPtr copyFilterProc,
159 Boolean *spaceOK);
161 static void CopyLevel(long sourceDirID,
162 long dstDirID,
163 EnumerateGlobals *theGlobals);
165 /*****************************************************************************/
167 static void GetLevelSize(long currentDirID,
168 PreflightGlobals *theGlobals)
170 short index = 1;
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 */
192 else
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;
203 else
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;
212 else
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;
222 else
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;
231 else
233 theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
238 ++index;
239 } while ( theGlobals->result == noErr );
242 /*****************************************************************************/
244 static OSErr PreflightDirectoryCopySpace(short srcVRefNum,
245 long srcDirID,
246 short dstVRefNum,
247 CopyFilterProcPtr copyFilterProc,
248 Boolean *spaceOK)
250 XVolumeParam pb;
251 OSErr error;
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);
281 return ( error );
284 /*****************************************************************************/
286 static void CopyLevel(long sourceDirID,
287 long dstDirID,
288 EnumerateGlobals *theGlobals)
290 long currentSrcDirID;
291 long newDirID;
292 short index = 1;
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);
344 else
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);
361 else
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;
375 else
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,
383 dstDirID,
384 NULL,
385 NULL,
386 theGlobals->copyBuffer,
387 theGlobals->bufferSize,
388 false);
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;
404 else
406 /* If you don't handle the errors with an error handler, */
407 /* then the copy stops here. */
408 theGlobals->bailout = true;
414 else
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;
429 else
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,
444 long srcDirID,
445 ConstStr255Param srcName,
446 short dstVRefNum,
447 long dstDirID,
448 ConstStr255Param dstName,
449 void *copyBufferPtr,
450 long copyBufferSize,
451 Boolean preflight,
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
491 by copyBufferPtr.
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
495 starting the copy.
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.
507 Result Codes
508 noErr 0 No error
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
512 match unit table
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
519 ioErr -36 I/O error
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
529 modes
530 dupFNErr -48 Destination file already exists
531 opWrErr -49 File already open for writing
532 paramErr -50 No default volume or function not
533 supported by volume
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
541 modes
542 afpObjectTypeErr -5025 Source is a directory, directory not found
543 or incomplete pathname
545 __________
547 Also see: CopyErrProcPtr, CopyFilterProcPtr, FSpFilteredDirectoryCopy,
548 DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy
551 /*****************************************************************************/
553 pascal OSErr FilteredDirectoryCopy(short srcVRefNum,
554 long srcDirID,
555 ConstStr255Param srcName,
556 short dstVRefNum,
557 long dstDirID,
558 ConstStr255Param dstName,
559 void *copyBufferPtr,
560 long copyBufferSize,
561 Boolean preflight,
562 CopyErrProcPtr copyErrHandler,
563 CopyFilterProcPtr copyFilterProc, ConstStr255Param newName)
565 EnumerateGlobals theGlobals;
566 Boolean isDirectory;
567 OSErr error;
568 Boolean ourCopyBuffer = false;
569 Str63 srcDirName, oldDiskName;
570 Boolean spaceOK;
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 )
596 goto ErrorExit;
598 if ( !isDirectory )
600 error = dirNFErr;
601 goto ErrorExit;
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;
610 isDirectory = true;
611 error = noErr;
613 else
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 )
619 goto ErrorExit;
621 if ( !isDirectory )
623 error = dirNFErr;
624 goto ErrorExit;
628 /* Get the real vRefNum of both the source and destination */
629 error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
630 if ( error != noErr )
632 goto ErrorExit;
634 error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
635 if ( error != noErr )
637 goto ErrorExit;
640 if ( preflight )
642 error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK);
643 if ( error != noErr )
645 goto ErrorExit;
647 if ( !spaceOK )
649 error = dskFulErr; /* not enough room on destination */
650 goto ErrorExit;
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 )
664 goto ErrorExit;
667 else
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;
690 else
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) )
703 goto ErrorExit;
705 else
707 /* If the CopyErrProc handled the problem, clear the error here */
708 /* and continue */
709 error = noErr;
712 else
714 /* If you don't handle the errors with an error handler, */
715 /* then the copy stops here. */
716 goto ErrorExit;
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);
755 ErrorExit:
756 /* Get rid of the copy buffer if we allocated it. */
757 if ( ourCopyBuffer )
759 DisposePtr((Ptr)copyBufferPtr);
762 return ( error );
766 /*****************************************************************************/
769 pascal OSErr MacFSpDirectoryCopyRename(const FSSpec *srcSpec,
770 const FSSpec *dstSpec,
771 ConstStr255Param newName,
772 void *copyBufferPtr,
773 long copyBufferSize,
774 Boolean preflight,
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) );