]> git.saurik.com Git - wxWidgets.git/blob - src/mac/morefile/Director.cpp
#include textfile.h out of the WX_PRECOMP section (bcc)
[wxWidgets.git] / src / mac / morefile / Director.cpp
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 #include <Types.h>
23 #include <Errors.h>
24 #include <Memory.h>
25 #include <Files.h>
26 #include <Script.h>
27
28 #define __COMPILINGMOREFILES
29
30 #include "MoreFile.h"
31 #include "MoreExtr.h"
32 #include "MoreDesk.h"
33 #include "FileCopy.h"
34 #include "Director.h"
35
36 /*****************************************************************************/
37
38 /* local constants */
39
40 enum
41 {
42 dirCopyBigCopyBuffSize = 0x00004000,
43 dirCopyMinCopyBuffSize = 0x00000200
44 };
45
46
47 /*****************************************************************************/
48
49 /* local data structures */
50
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. */
54
55 #if PRAGMA_ALIGN_SUPPORTED
56 #pragma options align=mac68k
57 #endif
58 struct EnumerateGlobals
59 {
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 */
69 };
70 #if PRAGMA_ALIGN_SUPPORTED
71 #pragma options align=reset
72 #endif
73
74 typedef struct EnumerateGlobals EnumerateGlobals;
75 typedef EnumerateGlobals *EnumerateGlobalsPtr;
76
77
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. */
81
82 #if PRAGMA_ALIGN_SUPPORTED
83 #pragma options align=mac68k
84 #endif
85 struct PreflightGlobals
86 {
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 */
90
91 unsigned long dstBlksPerAllocBlk; /* the number of 512 byte blocks per allocation block on destination */
92
93 unsigned long allocBlksNeeded; /* the total number of allocation blocks needed */
94
95 unsigned long tempBlocks; /* temporary storage for calculations (save some stack space) */
96 CopyFilterProcPtr copyFilterProc; /* pointer to filter function */
97 };
98 #if PRAGMA_ALIGN_SUPPORTED
99 #pragma options align=reset
100 #endif
101
102 typedef struct PreflightGlobals PreflightGlobals;
103 typedef PreflightGlobals *PreflightGlobalsPtr;
104
105 /*****************************************************************************/
106
107 /* static prototypes */
108
109 static void GetLevelSize(long currentDirID,
110 PreflightGlobals *theGlobals);
111
112 static OSErr PreflightDirectoryCopySpace(short srcVRefNum,
113 long srcDirID,
114 short dstVRefNum,
115 CopyFilterProcPtr copyFilterProc,
116 Boolean *spaceOK);
117
118 static void CopyLevel(long sourceDirID,
119 long dstDirID,
120 EnumerateGlobals *theGlobals);
121
122 /*****************************************************************************/
123
124 static void GetLevelSize(long currentDirID,
125 PreflightGlobals *theGlobals)
126 {
127 short index = 1;
128
129 do
130 {
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 )
137 {
138 if ( (theGlobals->copyFilterProc == NULL) ||
139 CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
140 {
141 /* Either there's no filter proc OR the filter proc says to use this item */
142 if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
143 {
144 /* we have a directory */
145
146 GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
147 theGlobals->result = noErr; /* clear error return on way back */
148 }
149 else
150 {
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. */
154
155 /* get number of 512-byte blocks needed for data fork */
156 if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 )
157 {
158 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1;
159 }
160 else
161 {
162 theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9;
163 }
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 )
166 {
167 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
168 }
169 else
170 {
171 theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
172 }
173
174 /* get number of 512-byte blocks needed for resource fork */
175 if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 )
176 {
177 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1;
178 }
179 else
180 {
181 theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9;
182 }
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 )
185 {
186 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
187 }
188 else
189 {
190 theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
191 }
192 }
193 }
194 }
195 ++index;
196 } while ( theGlobals->result == noErr );
197 }
198
199 /*****************************************************************************/
200
201 static OSErr PreflightDirectoryCopySpace(short srcVRefNum,
202 long srcDirID,
203 short dstVRefNum,
204 CopyFilterProcPtr copyFilterProc,
205 Boolean *spaceOK)
206 {
207 XVolumeParam pb;
208 OSErr error;
209 unsigned long dstFreeBlocks;
210 PreflightGlobals theGlobals;
211
212 error = XGetVolumeInfoNoName(NULL, dstVRefNum, &pb);
213 if ( error == noErr )
214 {
215 /* Convert freeBytes to free disk blocks (512-byte blocks) */
216 dstFreeBlocks = (pb.ioVFreeBytes.hi << 23) + (pb.ioVFreeBytes.lo >> 9);
217
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);
221
222 theGlobals.allocBlksNeeded = 0;
223
224 theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
225 theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
226
227 theGlobals.copyFilterProc = copyFilterProc;
228
229 GetLevelSize(srcDirID, &theGlobals);
230
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);
236 }
237
238 return ( error );
239 }
240
241 /*****************************************************************************/
242
243 static void CopyLevel(long sourceDirID,
244 long dstDirID,
245 EnumerateGlobals *theGlobals)
246 {
247 long currentSrcDirID;
248 long newDirID;
249 short index = 1;
250
251 do
252 {
253 /* Get next source item at the current directory level */
254
255 theGlobals->myCPB.dirInfo.ioFDirIndex = index;
256 theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
257 theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB);
258
259 if ( theGlobals->error == noErr )
260 {
261 if ( (theGlobals->copyFilterProc == NULL) ||
262 CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
263 {
264 /* Either there's no filter proc OR the filter proc says to use this item */
265
266 /* We have an item. Is it a file or directory? */
267 if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
268 {
269 /* We have a directory */
270
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 )
274 {
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;
278
279 /* Dive again (copy the directory level we just found below this one) */
280 CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
281
282 if ( !theGlobals->bailout )
283 {
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);
287
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);
291
292 /* handle any errors from CopyFileMgrAttributes */
293 if ( theGlobals->error != noErr )
294 {
295 if ( theGlobals->errorHandler != NULL )
296 {
297 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp,
298 theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
299 theGlobals->destinationVRefNum, newDirID, NULL);
300 }
301 else
302 {
303 /* If you don't handle the errors with an error handler, */
304 /* then the copy stops here. */
305 theGlobals->bailout = true;
306 }
307 }
308 }
309 }
310 else /* error handling for DirCreate */
311 {
312 if ( theGlobals->errorHandler != NULL )
313 {
314 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp,
315 theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
316 theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
317 }
318 else
319 {
320 /* If you don't handle the errors with an error handler, */
321 /* then the copy stops here. */
322 theGlobals->bailout = true;
323 }
324 }
325
326 if ( !theGlobals->bailout )
327 {
328 /* clear error return on way back if we aren't bailing out */
329 theGlobals->error = noErr;
330 }
331 }
332 else
333 {
334 /* We have a file, so copy it */
335
336 theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
337 theGlobals->myCPB.hFileInfo.ioFlParID,
338 theGlobals->itemName,
339 theGlobals->destinationVRefNum,
340 dstDirID,
341 NULL,
342 NULL,
343 theGlobals->copyBuffer,
344 theGlobals->bufferSize,
345 false);
346
347 /* handle any errors from FileCopy */
348 if ( theGlobals->error != noErr )
349 {
350 if ( theGlobals->errorHandler != NULL )
351 {
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 )
356 {
357 /* If the CopyErrProc handled the problem, clear the error here */
358 theGlobals->error = noErr;
359 }
360 }
361 else
362 {
363 /* If you don't handle the errors with an error handler, */
364 /* then the copy stops here. */
365 theGlobals->bailout = true;
366 }
367 }
368 }
369 }
370 }
371 else
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 )
375 {
376 if ( theGlobals->errorHandler != NULL )
377 {
378 theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp,
379 theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
380 if ( !theGlobals->bailout )
381 {
382 /* If the CopyErrProc handled the problem, clear the error here */
383 theGlobals->error = noErr;
384 }
385 }
386 else
387 {
388 /* If you don't handle the errors with an error handler, */
389 /* then the copy stops here. */
390 theGlobals->bailout = true;
391 }
392 }
393 }
394 ++index; /* prepare to get next item */
395 } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
396 }
397
398 /*****************************************************************************/
399
400 pascal OSErr FilteredDirectoryCopy(short srcVRefNum,
401 long srcDirID,
402 ConstStr255Param srcName,
403 short dstVRefNum,
404 long dstDirID,
405 ConstStr255Param dstName,
406 void *copyBufferPtr,
407 long copyBufferSize,
408 Boolean preflight,
409 CopyErrProcPtr copyErrHandler,
410 CopyFilterProcPtr copyFilterProc)
411 {
412 EnumerateGlobals theGlobals;
413 Boolean isDirectory;
414 OSErr error;
415 Boolean ourCopyBuffer = false;
416 Str63 srcDirName, oldDiskName;
417 Boolean spaceOK;
418
419 /* Make sure a copy buffer is allocated. */
420 if ( copyBufferPtr == NULL )
421 {
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 )
428 {
429 copyBufferSize = dirCopyMinCopyBuffSize;
430 copyBufferPtr = NewPtr(copyBufferSize);
431 if ( copyBufferPtr == NULL )
432 {
433 return ( memFullErr );
434 }
435 }
436 ourCopyBuffer = true;
437 }
438
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 )
442 {
443 goto ErrorExit;
444 }
445 if ( !isDirectory )
446 {
447 error = dirNFErr;
448 goto ErrorExit;
449 }
450
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) )
455 {
456 dstDirID = fsRtParID;
457 isDirectory = true;
458 error = noErr;
459 }
460 else
461 {
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 )
465 {
466 goto ErrorExit;
467 }
468 if ( !isDirectory )
469 {
470 error = dirNFErr;
471 goto ErrorExit;
472 }
473 }
474
475 /* Get the real vRefNum of both the source and destination */
476 error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
477 if ( error != noErr )
478 {
479 goto ErrorExit;
480 }
481 error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
482 if ( error != noErr )
483 {
484 goto ErrorExit;
485 }
486
487 if ( preflight )
488 {
489 error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK);
490 if ( error != noErr )
491 {
492 goto ErrorExit;
493 }
494 if ( !spaceOK )
495 {
496 error = dskFulErr; /* not enough room on destination */
497 goto ErrorExit;
498 }
499 }
500
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 )
505 {
506 goto ErrorExit;
507 }
508
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 )
513 {
514 /* Get the current name of the destination disk */
515 error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName);
516 if ( error == noErr )
517 {
518 /* Shorten the name if it's too long to be the volume name */
519 TruncPString(srcDirName, srcDirName, 27);
520
521 /* Rename the disk */
522 error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName);
523 /* and copy to the root directory */
524 dstDirID = fsRtDirID;
525 }
526 }
527 else
528 {
529 error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
530 }
531 if ( error != noErr )
532 {
533 /* handle any errors from DirCreate */
534 if ( copyErrHandler != NULL )
535 {
536 if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp,
537 srcVRefNum, srcDirID, NULL,
538 dstVRefNum, dstDirID, srcDirName) )
539 {
540 goto ErrorExit;
541 }
542 else
543 {
544 /* If the CopyErrProc handled the problem, clear the error here */
545 /* and continue */
546 error = noErr;
547 }
548 }
549 else
550 {
551 /* If you don't handle the errors with an error handler, */
552 /* then the copy stops here. */
553 goto ErrorExit;
554 }
555 }
556
557 /* dstDirID is now the newly created directory! */
558
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;
568
569 /* Here we go into recursion land... */
570 CopyLevel(srcDirID, dstDirID, &theGlobals);
571 error = theGlobals.error; /* get the result */
572
573 if ( !theGlobals.bailout )
574 {
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);
578
579 /* Copy the File Manager attributes */
580 error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
581 dstVRefNum, dstDirID, NULL, true);
582
583 /* handle any errors from CopyFileMgrAttributes */
584 if ( (error != noErr) && (copyErrHandler != NULL) )
585 {
586 theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp,
587 srcVRefNum, srcDirID, NULL,
588 dstVRefNum, dstDirID, NULL);
589 }
590 }
591
592 ErrorExit:
593 /* Get rid of the copy buffer if we allocated it. */
594 if ( ourCopyBuffer )
595 {
596 DisposePtr((Ptr)copyBufferPtr);
597 }
598
599 return ( error );
600 }
601
602 /*****************************************************************************/
603
604 pascal OSErr DirectoryCopy(short srcVRefNum,
605 long srcDirID,
606 ConstStr255Param srcName,
607 short dstVRefNum,
608 long dstDirID,
609 ConstStr255Param dstName,
610 void *copyBufferPtr,
611 long copyBufferSize,
612 Boolean preflight,
613 CopyErrProcPtr copyErrHandler)
614 {
615 return ( FilteredDirectoryCopy(srcVRefNum, srcDirID, srcName,
616 dstVRefNum, dstDirID, dstName,
617 copyBufferPtr, copyBufferSize, preflight,
618 copyErrHandler, NULL) );
619 }
620
621 /*****************************************************************************/
622
623 pascal OSErr FSpFilteredDirectoryCopy(const FSSpec *srcSpec,
624 const FSSpec *dstSpec,
625 void *copyBufferPtr,
626 long copyBufferSize,
627 Boolean preflight,
628 CopyErrProcPtr copyErrHandler,
629 CopyFilterProcPtr copyFilterProc)
630 {
631 return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
632 dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
633 copyBufferPtr, copyBufferSize, preflight,
634 copyErrHandler, copyFilterProc) );
635 }
636
637 /*****************************************************************************/
638
639 pascal OSErr FSpDirectoryCopy(const FSSpec *srcSpec,
640 const FSSpec *dstSpec,
641 void *copyBufferPtr,
642 long copyBufferSize,
643 Boolean preflight,
644 CopyErrProcPtr copyErrHandler)
645 {
646 return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
647 dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
648 copyBufferPtr, copyBufferSize, preflight,
649 copyErrHandler, NULL) );
650 }
651
652 /*****************************************************************************/
653