VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/macDirectoryCopy.c@ 4837

Last change on this file since 4837 was 1, checked in by vboxsync, 54 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.4 KB
Line 
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.
12**
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.
20*/
21
22// Modified to allow renaming the destination folder
23
24#include <Types.h>
25#include <Errors.h>
26#include <Memory.h>
27#include <Files.h>
28#include <Script.h>
29
30#define __COMPILINGMOREFILES
31
32#include "MoreFiles.h"
33#include "MoreFilesExtras.h"
34#include "MoreDesktopMgr.h"
35#include "FileCopy.h"
36#include "MacDirectoryCopy.h"
37#include <string.h>
38
39
40/*****************************************************************************/
41
42enum
43{
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 */
50};
51
52/*****************************************************************************/
53
54
55
56#define CallCopyErrProc(userRoutine, error, failedOperation, srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName) \
57 (*(userRoutine))((error), (failedOperation), (srcVRefNum), (srcDirID), (srcName), (dstVRefNum), (dstDirID), (dstName))
58
59/*****************************************************************************/
60
61typedef pascal Boolean (*CopyFilterProcPtr) (const CInfoPBRec * const cpbPtr);
62
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.
67
68 pb input: Points to the CInfoPBRec for the item under consideration.
69
70 __________
71
72 Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy
73*/
74
75#define CallCopyFilterProc(userRoutine, cpbPtr) (*(userRoutine))((cpbPtr))
76
77
78
79/*****************************************************************************/
80
81/* local constants */
82
83enum
84{
85 dirCopyBigCopyBuffSize = 0x00004000,
86 dirCopyMinCopyBuffSize = 0x00000200
87};
88
89
90/*****************************************************************************/
91
92/* local data structures */
93
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. */
97
98#if PRAGMA_STRUCT_ALIGN
99#pragma options align=mac68k
100#endif
101struct EnumerateGlobals
102{
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 */
112};
113#if PRAGMA_STRUCT_ALIGN
114#pragma options align=reset
115#endif
116
117typedef struct EnumerateGlobals EnumerateGlobals;
118typedef EnumerateGlobals *EnumerateGlobalsPtr;
119
120
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. */
124
125#if PRAGMA_STRUCT_ALIGN
126#pragma options align=mac68k
127#endif
128struct PreflightGlobals
129{
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 */
133
134 unsigned long dstBlksPerAllocBlk; /* the number of 512 byte blocks per allocation block on destination */
135
136 unsigned long allocBlksNeeded; /* the total number of allocation blocks needed */
137
138 unsigned long tempBlocks; /* temporary storage for calculations (save some stack space) */
139 CopyFilterProcPtr copyFilterProc; /* pointer to filter function */
140};
141#if PRAGMA_STRUCT_ALIGN
142#pragma options align=reset
143#endif
144
145typedef struct PreflightGlobals PreflightGlobals;
146typedef PreflightGlobals *PreflightGlobalsPtr;
147
148/*****************************************************************************/
149
150/* static prototypes */
151
152static void GetLevelSize(long currentDirID,
153 PreflightGlobals *theGlobals);
154
155static OSErr PreflightDirectoryCopySpace(short srcVRefNum,
156 long srcDirID,
157 short dstVRefNum,
158 CopyFilterProcPtr copyFilterProc,
159 Boolean *spaceOK);
160
161static void CopyLevel(long sourceDirID,
162 long dstDirID,
163 EnumerateGlobals *theGlobals);
164
165/*****************************************************************************/
166
167static void GetLevelSize(long currentDirID,
168 PreflightGlobals *theGlobals)
169{
170 short index = 1;
171
172 do
173 {
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 )
180 {
181 if ( (theGlobals->copyFilterProc == NULL) ||
182 CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
183 {
184 /* Either there's no filter proc OR the filter proc says to use this item */
185 if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
186 {
187 /* we have a directory */
188
189 GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
190 theGlobals->result = noErr; /* clear error return on way back */
191 }
192 else
193 {
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. */
197
198 /* get number of 512-byte blocks needed for data fork */
199 if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 )
200 {
201 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1;
202 }
203 else
204 {
205 theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9;
206 }
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 )
209 {
210 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
211 }
212 else
213 {
214 theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
215 }
216
217 /* get number of 512-byte blocks needed for resource fork */
218 if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 )
219 {
220 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1;
221 }
222 else
223 {
224 theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9;
225 }
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 )
228 {
229 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
230 }
231 else
232 {
233 theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
234 }
235 }
236 }
237 }
238 ++index;
239 } while ( theGlobals->result == noErr );
240}
241
242/*****************************************************************************/
243
244static OSErr PreflightDirectoryCopySpace(short srcVRefNum,
245 long srcDirID,
246 short dstVRefNum,
247 CopyFilterProcPtr copyFilterProc,
248 Boolean *spaceOK)
249{
250 XVolumeParam pb;
251 OSErr error;
252 unsigned long dstFreeBlocks;
253 PreflightGlobals theGlobals;
254
255 error = XGetVolumeInfoNoName(NULL, dstVRefNum, &pb);
256 if ( error == noErr )
257 {
258 /* Convert freeBytes to free disk blocks (512-byte blocks) */
259#if (UNIVERSAL_INTERFACES_VERSION >= 0x0330)
260 dstFreeBlocks = (pb.ioVFreeBytes >> 9);
261#else
262 dstFreeBlocks = (pb.ioVFreeBytes.hi << 23) + (pb.ioVFreeBytes.lo >> 9);
263#endif
264 /* get allocation block size (always multiple of 512) and divide by 512
265 to get number of 512-byte blocks per allocation block */
266 theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.ioVAlBlkSiz >> 9);
267
268 theGlobals.allocBlksNeeded = 0;
269
270 theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
271 theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
272
273 theGlobals.copyFilterProc = copyFilterProc;
274
275 GetLevelSize(srcDirID, &theGlobals);
276
277 /* Is there enough room on the destination volume for the source file? */
278 /* Note: This will work because the largest number of disk blocks supported */
279 /* on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk) */
280 /* will always be less than 0xffffffff. */
281 *spaceOK = ((theGlobals.allocBlksNeeded * theGlobals.dstBlksPerAllocBlk) <= dstFreeBlocks);
282 }
283
284 return ( error );
285}
286
287/*****************************************************************************/
288
289static void CopyLevel(long sourceDirID,
290 long dstDirID,
291 EnumerateGlobals *theGlobals)
292{
293 long currentSrcDirID;
294 long newDirID;
295 short index = 1;
296
297 do
298 {
299 /* Get next source item at the current directory level */
300
301 theGlobals->myCPB.dirInfo.ioFDirIndex = index;
302 theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
303 theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB);
304
305 if ( theGlobals->error == noErr )
306 {
307 if ( (theGlobals->copyFilterProc == NULL) ||
308 CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
309 {
310 /* Either there's no filter proc OR the filter proc says to use this item */
311
312 /* We have an item. Is it a file or directory? */
313 if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
314 {
315 /* We have a directory */
316
317 /* Create a new directory at the destination. No errors allowed! */
318 theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
319 if ( theGlobals->error == noErr )
320 {
321 /* Save the current source directory ID where we can get it when we come back
322 ** from recursion land. */
323 currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
324
325 /* Dive again (copy the directory level we just found below this one) */
326 CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
327
328 if ( !theGlobals->bailout )
329 {
330 /* Copy comment from old to new directory. */
331 /* Ignore the result because we really don't care if it worked or not. */
332 (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL);
333
334 /* Copy directory attributes (dates, etc.) to newDirID. */
335 /* No errors allowed */
336 theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true);
337
338 /* handle any errors from CopyFileMgrAttributes */
339 if ( theGlobals->error != noErr )
340 {
341 if ( theGlobals->errorHandler != NULL )
342 {
343 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp,
344 theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
345 theGlobals->destinationVRefNum, newDirID, NULL);
346 }
347 else
348 {
349 /* If you don't handle the errors with an error handler, */
350 /* then the copy stops here. */
351 theGlobals->bailout = true;
352 }
353 }
354 }
355 }
356 else /* error handling for DirCreate */
357 {
358 if ( theGlobals->errorHandler != NULL )
359 {
360 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp,
361 theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
362 theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
363 }
364 else
365 {
366 /* If you don't handle the errors with an error handler, */
367 /* then the copy stops here. */
368 theGlobals->bailout = true;
369 }
370 }
371
372 if ( !theGlobals->bailout )
373 {
374 /* clear error return on way back if we aren't bailing out */
375 theGlobals->error = noErr;
376 }
377 }
378 else
379 {
380 /* We have a file, so copy it */
381
382 theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
383 theGlobals->myCPB.hFileInfo.ioFlParID,
384 theGlobals->itemName,
385 theGlobals->destinationVRefNum,
386 dstDirID,
387 NULL,
388 NULL,
389 theGlobals->copyBuffer,
390 theGlobals->bufferSize,
391 false);
392
393 /* handle any errors from FileCopy */
394 if ( theGlobals->error != noErr )
395 {
396 if ( theGlobals->errorHandler != NULL )
397 {
398 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp,
399 theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
400 theGlobals->destinationVRefNum, dstDirID, NULL);
401 if ( !theGlobals->bailout )
402 {
403 /* If the CopyErrProc handled the problem, clear the error here */
404 theGlobals->error = noErr;
405 }
406 }
407 else
408 {
409 /* If you don't handle the errors with an error handler, */
410 /* then the copy stops here. */
411 theGlobals->bailout = true;
412 }
413 }
414 }
415 }
416 }
417 else
418 { /* error handling for PBGetCatInfo */
419 /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
420 if ( theGlobals->error != fnfErr )
421 {
422 if ( theGlobals->errorHandler != NULL )
423 {
424 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp,
425 theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
426 if ( !theGlobals->bailout )
427 {
428 /* If the CopyErrProc handled the problem, clear the error here */
429 theGlobals->error = noErr;
430 }
431 }
432 else
433 {
434 /* If you don't handle the errors with an error handler, */
435 /* then the copy stops here. */
436 theGlobals->bailout = true;
437 }
438 }
439 }
440 ++index; /* prepare to get next item */
441 } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
442}
443
444/*****************************************************************************/
445
446pascal OSErr FilteredDirectoryCopy(short srcVRefNum,
447 long srcDirID,
448 ConstStr255Param srcName,
449 short dstVRefNum,
450 long dstDirID,
451 ConstStr255Param dstName,
452 void *copyBufferPtr,
453 long copyBufferSize,
454 Boolean preflight,
455 CopyErrProcPtr copyErrHandler,
456 CopyFilterProcPtr copyFilterProc, ConstStr255Param newName );
457/* ¦ Make a copy of a directory structure in a new location with item filtering.
458 The FilteredDirectoryCopy function makes a copy of a directory
459 structure in a new location. If copyBufferPtr <> NIL, it points to
460 a buffer of copyBufferSize that is used to copy files data. The
461 larger the supplied buffer, the faster the copy. If
462 copyBufferPtr = NIL, then this routine allocates a buffer in the
463 application heap. If you pass a copy buffer to this routine, make
464 its size a multiple of 512 ($200) bytes for optimum performance.
465
466 The optional copyFilterProc parameter lets a routine you define
467 decide what files or directories are copied to the destination.
468
469 FilteredDirectoryCopy normally creates a new directory *in* the
470 specified destination directory and copies the source directory's
471 content into the new directory. However, if root parent directory
472 (fsRtParID) is passed as the dstDirID parameter and NULL is
473 passed as the dstName parameter, DirectoryCopy renames the
474 destination volume to the source directory's name (truncating
475 if the name is longer than 27 characters) and copies the source
476 directory's content into the destination volume's root directory.
477 This special case is supported by FilteredDirectoryCopy, but
478 not by FSpFilteredDirectoryCopy since with FSpFilteredDirectoryCopy,
479 the dstName parameter can not be NULL.
480
481 srcVRefNum input: Source volume specification.
482 srcDirID input: Source directory ID.
483 srcName input: Source directory name, or nil if
484 srcDirID specifies the directory.
485 dstVRefNum input: Destination volume specification.
486 dstDirID input: Destination directory ID.
487 dstName input: Destination directory name, or nil if
488 dstDirID specifies the directory.
489 copyBufferPtr input: Points to a buffer of copyBufferSize that
490 is used the i/o buffer for the copy or
491 nil if you want DirectoryCopy to allocate its
492 own buffer in the application heap.
493 copyBufferSize input: The size of the buffer pointed to
494 by copyBufferPtr.
495 preflight input: If true, DirectoryCopy makes sure there are
496 enough allocation blocks on the destination
497 volume to hold the directory's files before
498 starting the copy.
499 copyErrHandler input: A pointer to the routine you want called if an
500 error condition is detected during the copy, or
501 nil if you don't want to handle error conditions.
502 If you don't handle error conditions, the first
503 error will cause the copy to quit and
504 DirectoryCopy will return the error.
505 Error handling is recommended...
506 copyFilterProc input: A pointer to the filter routine you want called
507 for each item in the source directory, or NULL
508 if you don't want to filter.
509
510 Result Codes
511 noErr 0 No error
512 readErr Ð19 Driver does not respond to read requests
513 writErr Ð20 Driver does not respond to write requests
514 badUnitErr Ð21 Driver reference number does not
515 match unit table
516 unitEmptyErr Ð22 Driver reference number specifies a
517 nil handle in unit table
518 abortErr Ð27 Request aborted by KillIO
519 notOpenErr Ð28 Driver not open
520 dskFulErr -34 Destination volume is full
521 nsvErr -35 No such volume
522 ioErr -36 I/O error
523 bdNamErr -37 Bad filename
524 tmfoErr -42 Too many files open
525 fnfErr -43 Source file not found, or destination
526 directory does not exist
527 wPrErr -44 Volume locked by hardware
528 fLckdErr -45 File is locked
529 vLckdErr -46 Destination volume is read-only
530 fBsyErr -47 The source or destination file could
531 not be opened with the correct access
532 modes
533 dupFNErr -48 Destination file already exists
534 opWrErr -49 File already open for writing
535 paramErr -50 No default volume or function not
536 supported by volume
537 permErr -54 File is already open and cannot be opened using specified deny modes
538 memFullErr -108 Copy buffer could not be allocated
539 dirNFErr -120 Directory not found or incomplete pathname
540 wrgVolTypErr -123 Function not supported by volume
541 afpAccessDenied -5000 User does not have the correct access
542 afpDenyConflict -5006 The source or destination file could
543 not be opened with the correct access
544 modes
545 afpObjectTypeErr -5025 Source is a directory, directory not found
546 or incomplete pathname
547
548 __________
549
550 Also see: CopyErrProcPtr, CopyFilterProcPtr, FSpFilteredDirectoryCopy,
551 DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy
552*/
553
554/*****************************************************************************/
555
556pascal OSErr FilteredDirectoryCopy(short srcVRefNum,
557 long srcDirID,
558 ConstStr255Param srcName,
559 short dstVRefNum,
560 long dstDirID,
561 ConstStr255Param dstName,
562 void *copyBufferPtr,
563 long copyBufferSize,
564 Boolean preflight,
565 CopyErrProcPtr copyErrHandler,
566 CopyFilterProcPtr copyFilterProc, ConstStr255Param newName)
567{
568 EnumerateGlobals theGlobals;
569 Boolean isDirectory;
570 OSErr error;
571 Boolean ourCopyBuffer = false;
572 Str63 srcDirName, oldDiskName;
573 Boolean spaceOK;
574
575 /* Make sure a copy buffer is allocated. */
576 if ( copyBufferPtr == NULL )
577 {
578 /* The caller didn't supply a copy buffer so grab one from the application heap.
579 ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
580 ** If 512 bytes aren't available, we're in trouble. */
581 copyBufferSize = dirCopyBigCopyBuffSize;
582 copyBufferPtr = NewPtr(copyBufferSize);
583 if ( copyBufferPtr == NULL )
584 {
585 copyBufferSize = dirCopyMinCopyBuffSize;
586 copyBufferPtr = NewPtr(copyBufferSize);
587 if ( copyBufferPtr == NULL )
588 {
589 return ( memFullErr );
590 }
591 }
592 ourCopyBuffer = true;
593 }
594
595 /* Get the real dirID where we're copying from and make sure it is a directory. */
596 error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
597 if ( error != noErr )
598 {
599 goto ErrorExit;
600 }
601 if ( !isDirectory )
602 {
603 error = dirNFErr;
604 goto ErrorExit;
605 }
606
607 /* Special case destination if it is the root parent directory. */
608 /* Since you can't create the root directory, this is needed if */
609 /* you want to copy a directory's content to a disk's root directory. */
610 if ( (dstDirID == fsRtParID) && (dstName == NULL) )
611 {
612 dstDirID = fsRtParID;
613 isDirectory = true;
614 error = noErr;
615 }
616 else
617 {
618 /* Get the real dirID where we're going to put the copy and make sure it is a directory. */
619 error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
620 if ( error != noErr )
621 {
622 goto ErrorExit;
623 }
624 if ( !isDirectory )
625 {
626 error = dirNFErr;
627 goto ErrorExit;
628 }
629 }
630
631 /* Get the real vRefNum of both the source and destination */
632 error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
633 if ( error != noErr )
634 {
635 goto ErrorExit;
636 }
637 error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
638 if ( error != noErr )
639 {
640 goto ErrorExit;
641 }
642
643 if ( preflight )
644 {
645 error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK);
646 if ( error != noErr )
647 {
648 goto ErrorExit;
649 }
650 if ( !spaceOK )
651 {
652 error = dskFulErr; /* not enough room on destination */
653 goto ErrorExit;
654 }
655 }
656
657 /* Create the new directory in the destination directory with the */
658 /* same name as the source directory. */
659 /*
660 if newName is not empty use it rather than the original dir name.
661 */
662 if ( newName[0] == 0 )
663 {
664 error = GetDirName(srcVRefNum, srcDirID, srcDirName);
665 if ( error != noErr )
666 {
667 goto ErrorExit;
668 }
669 }
670 else
671 {
672 memcpy(srcDirName, newName, 32 );
673
674 }
675 /* Again, special case destination if the destination is the */
676 /* root parent directory. This time, we'll rename the disk to */
677 /* the source directory name. */
678 if ( dstDirID == fsRtParID )
679 {
680 /* Get the current name of the destination disk */
681 error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName);
682 if ( error == noErr )
683 {
684 /* Shorten the name if it's too long to be the volume name */
685 TruncPString(srcDirName, srcDirName, 27);
686
687 /* Rename the disk */
688 error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName);
689 /* and copy to the root directory */
690 dstDirID = fsRtDirID;
691 }
692 }
693 else
694 {
695 error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
696 }
697 if ( error != noErr )
698 {
699 /* handle any errors from DirCreate */
700 if ( copyErrHandler != NULL )
701 {
702 if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp,
703 srcVRefNum, srcDirID, NULL,
704 dstVRefNum, dstDirID, srcDirName) )
705 {
706 goto ErrorExit;
707 }
708 else
709 {
710 /* If the CopyErrProc handled the problem, clear the error here */
711 /* and continue */
712 error = noErr;
713 }
714 }
715 else
716 {
717 /* If you don't handle the errors with an error handler, */
718 /* then the copy stops here. */
719 goto ErrorExit;
720 }
721 }
722
723 /* dstDirID is now the newly created directory! */
724
725 /* Set up the globals we need to access from the recursive routine. */
726 theGlobals.copyBuffer = (Ptr)copyBufferPtr;
727 theGlobals.bufferSize = copyBufferSize;
728 theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
729 theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
730 theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
731 theGlobals.errorHandler = copyErrHandler;
732 theGlobals.bailout = false;
733 theGlobals.copyFilterProc = copyFilterProc;
734
735 /* Here we go into recursion land... */
736 CopyLevel(srcDirID, dstDirID, &theGlobals);
737 error = theGlobals.error; /* get the result */
738
739 if ( !theGlobals.bailout )
740 {
741 /* Copy comment from source to destination directory. */
742 /* Ignore the result because we really don't care if it worked or not. */
743 (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL);
744
745 /* Copy the File Manager attributes */
746 error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
747 dstVRefNum, dstDirID, NULL, true);
748
749 /* handle any errors from CopyFileMgrAttributes */
750 if ( (error != noErr) && (copyErrHandler != NULL) )
751 {
752 theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp,
753 srcVRefNum, srcDirID, NULL,
754 dstVRefNum, dstDirID, NULL);
755 }
756 }
757
758ErrorExit:
759 /* Get rid of the copy buffer if we allocated it. */
760 if ( ourCopyBuffer )
761 {
762 DisposePtr((Ptr)copyBufferPtr);
763 }
764
765 return ( error );
766}
767
768
769/*****************************************************************************/
770
771
772pascal OSErr MacFSpDirectoryCopyRename(const FSSpec *srcSpec,
773 const FSSpec *dstSpec,
774 ConstStr255Param newName,
775 void *copyBufferPtr,
776 long copyBufferSize,
777 Boolean preflight,
778 CopyErrProcPtr copyErrHandler)
779{
780 return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
781 dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
782 copyBufferPtr, copyBufferSize, preflight,
783 copyErrHandler, NULL, newName) );
784}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use