Back to index

lightning-sunbird  0.9+nobinonly
macDirectoryCopy.c
Go to the documentation of this file.
00001 /*
00002 **     Apple Macintosh Developer Technical Support
00003 **
00004 **     DirectoryCopy: A robust, general purpose directory copy routine.
00005 **
00006 **     by Jim Luther, Apple Developer Technical Support Emeritus
00007 **
00008 **     File:         DirectoryCopy.c
00009 **
00010 **     Copyright  1992-1998 Apple Computer, Inc.
00011 **     All rights reserved.
00012 **
00013 **     You may incorporate this sample code into your applications without
00014 **     restriction, though the sample code has been provided "AS IS" and the
00015 **     responsibility for its operation is 100% yours.  However, what you are
00016 **     not permitted to do is to redistribute the source as "DSC Sample Code"
00017 **     after having made changes. If you're going to re-distribute the source,
00018 **     we require that you make it clear in the source that the code was
00019 **     descended from Apple Sample Code, but that you've made changes.
00020 */
00021 
00022 // Modified to allow renaming the destination folder
00023 
00024 #include <Types.h>
00025 #include <Errors.h>
00026 #include <Memory.h>
00027 #include <Files.h>
00028 #include <Script.h>
00029 
00030 #define       __COMPILINGMOREFILES
00031 
00032 #include "MoreFiles.h"
00033 #include "MoreFilesExtras.h"
00034 #include "MoreDesktopMgr.h"
00035 #include "FileCopy.h"
00036 #include "MacDirectoryCopy.h"
00037 #include <string.h>
00038 
00039 
00040 /*****************************************************************************/
00041 
00042 enum
00043 {
00044        getNextItemOp               = 1,   /* couldn't access items in this directory - no access privileges */
00045        copyDirCommentOp            = 2,   /* couldn't copy directory's Finder comment */
00046        copyDirAccessPrivsOp = 3,   /* couldn't copy directory's AFP access privileges */
00047        copyDirFMAttributesOp       = 4,   /* couldn't copy directory's File Manager attributes */
00048        dirCreateOp                        = 5,   /* couldn't create destination directory */
00049        fileCopyOp                         = 6           /* couldn't copy file */
00050 };
00051 
00052 /*****************************************************************************/
00053 
00054 
00055 
00056 #define CallCopyErrProc(userRoutine, error, failedOperation, srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName) \
00057               (*(userRoutine))((error), (failedOperation), (srcVRefNum), (srcDirID), (srcName), (dstVRefNum), (dstDirID), (dstName))
00058 
00059 /*****************************************************************************/
00060 
00061 typedef       pascal Boolean       (*CopyFilterProcPtr) (const CInfoPBRec * const cpbPtr);
00062 
00063 /*      Prototype for the CopyFilterProc function.
00064        This is the prototype for the CopyFilterProc function called by
00065        FilteredDirectoryCopy and GetLevelSize. If true is returned,
00066        the file/folder is included in the copy, otherwise it is excluded.
00067        
00068        pb     input: Points to the CInfoPBRec for the item under consideration.
00069 
00070        __________
00071        
00072        Also see:     FilteredDirectoryCopy, FSpFilteredDirectoryCopy
00073 */
00074 
00075 #define CallCopyFilterProc(userRoutine, cpbPtr) (*(userRoutine))((cpbPtr))
00076 
00077 
00078 
00079 /*****************************************************************************/
00080 
00081 /* local constants */
00082 
00083 enum
00084 {
00085        dirCopyBigCopyBuffSize  = 0x00004000,
00086        dirCopyMinCopyBuffSize  = 0x00000200
00087 };
00088 
00089 
00090 /*****************************************************************************/
00091 
00092 /* local data structures */
00093 
00094 /* The EnumerateGlobals structure is used to minimize the amount of
00095 ** stack space used when recursively calling CopyLevel and to hold
00096 ** global information that might be needed at any time. */
00097 
00098 #if PRAGMA_STRUCT_ALIGN
00099 #pragma options align=mac68k
00100 #endif
00101 struct EnumerateGlobals
00102 {
00103        Ptr                  copyBuffer;                 /* pointer to buffer used for file copy operations */
00104        long          bufferSize;                 /* the size of the copy buffer */
00105        CopyErrProcPtr errorHandler;       /* pointer to error handling function */
00106        CopyFilterProcPtr copyFilterProc; /* pointer to filter function */
00107        OSErr         error;                      /* temporary holder of results - saves 2 bytes of stack each level */
00108        Boolean              bailout;                    /* set to true to by error handling function if fatal error */
00109        short         destinationVRefNum;  /* the destination vRefNum */
00110        Str63         itemName;                   /* the name of the current item */
00111        CInfoPBRec    myCPB;                      /* the parameter block used for PBGetCatInfo calls */
00112 };
00113 #if PRAGMA_STRUCT_ALIGN
00114 #pragma options align=reset
00115 #endif
00116 
00117 typedef struct EnumerateGlobals EnumerateGlobals;
00118 typedef EnumerateGlobals *EnumerateGlobalsPtr;
00119 
00120 
00121 /* The PreflightGlobals structure is used to minimize the amount of
00122 ** stack space used when recursively calling GetLevelSize and to hold
00123 ** global information that might be needed at any time. */
00124 
00125 #if PRAGMA_STRUCT_ALIGN
00126 #pragma options align=mac68k
00127 #endif
00128 struct PreflightGlobals
00129 {
00130        OSErr                result;                            /* temporary holder of results - saves 2 bytes of stack each level */
00131        Str63                itemName;                   /* the name of the current item */
00132        CInfoPBRec           myCPB;                      /* the parameter block used for PBGetCatInfo calls */
00133 
00134        unsigned long dstBlksPerAllocBlk;  /* the number of 512 byte blocks per allocation block on destination */
00135                                                                       
00136        unsigned long allocBlksNeeded;     /* the total number of allocation blocks needed  */
00137 
00138        unsigned long tempBlocks;                 /* temporary storage for calculations (save some stack space)  */
00139        CopyFilterProcPtr copyFilterProc;  /* pointer to filter function */
00140 };
00141 #if PRAGMA_STRUCT_ALIGN
00142 #pragma options align=reset
00143 #endif
00144 
00145 typedef struct PreflightGlobals PreflightGlobals;
00146 typedef PreflightGlobals *PreflightGlobalsPtr;
00147 
00148 /*****************************************************************************/
00149 
00150 /* static prototypes */
00151 
00152 static void   GetLevelSize(long currentDirID,
00153                                                   PreflightGlobals *theGlobals);
00154 
00155 static OSErr  PreflightDirectoryCopySpace(short srcVRefNum,
00156                                                                              long srcDirID,
00157                                                                              short dstVRefNum,
00158                                                                              CopyFilterProcPtr copyFilterProc,
00159                                                                              Boolean *spaceOK);
00160 
00161 static void   CopyLevel(long sourceDirID,
00162                                             long dstDirID,
00163                                             EnumerateGlobals *theGlobals);
00164                                             
00165 /*****************************************************************************/
00166 
00167 static void   GetLevelSize(long currentDirID,
00168                                                   PreflightGlobals *theGlobals)
00169 {
00170        short  index = 1;
00171        
00172        do
00173        {
00174               theGlobals->myCPB.dirInfo.ioFDirIndex = index;
00175               theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID;     /* we need to do this every time */
00176                                                                                                          /* through, since GetCatInfo  */
00177                                                                                                          /* returns ioFlNum in this field */
00178               theGlobals->result = PBGetCatInfoSync(&theGlobals->myCPB);
00179               if ( theGlobals->result == noErr )
00180               {
00181                      if ( (theGlobals->copyFilterProc == NULL) ||
00182                              CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
00183                      {
00184                             /* Either there's no filter proc OR the filter proc says to use this item */
00185                             if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
00186                             {
00187                                    /* we have a directory */
00188                                    
00189                                    GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
00190                                    theGlobals->result = noErr; /* clear error return on way back */
00191                             }
00192                             else
00193                             {
00194                                    /* We have a file - add its allocation blocks to allocBlksNeeded. */
00195                                    /* Since space on Mac OS disks is always allocated in allocation blocks, */
00196                                    /* this takes into account rounding up to the end of an allocation block. */
00197                                    
00198                                    /* get number of 512-byte blocks needed for data fork */
00199                                    if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 )
00200                                    {
00201                                           theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1;
00202                                    }
00203                                    else
00204                                    {
00205                                           theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9;
00206                                    }
00207                                    /* now, calculate number of new allocation blocks needed for the data fork and add it to the total */
00208                                    if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
00209                                    {
00210                                           theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
00211                                    }
00212                                    else
00213                                    {
00214                                           theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
00215                                    }
00216                                    
00217                                    /* get number of 512-byte blocks needed for resource fork */
00218                                    if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 )
00219                                    {
00220                                           theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1;
00221                                    }
00222                                    else
00223                                    {
00224                                           theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9;
00225                                    }
00226                                    /* now, calculate number of new allocation blocks needed for the resource  fork and add it to the total */
00227                                    if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
00228                                    {
00229                                           theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
00230                                    }
00231                                    else
00232                                    {
00233                                           theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
00234                                    }
00235                             }
00236                      }
00237               }
00238               ++index;
00239        } while ( theGlobals->result == noErr );
00240 }
00241 
00242 /*****************************************************************************/
00243 
00244 static OSErr  PreflightDirectoryCopySpace(short srcVRefNum,
00245                                                                              long srcDirID,
00246                                                                              short dstVRefNum,
00247                                                                              CopyFilterProcPtr copyFilterProc,
00248                                                                              Boolean *spaceOK)
00249 {
00250        XVolumeParam pb;
00251        OSErr error;
00252        unsigned long dstFreeBlocks;
00253        PreflightGlobals theGlobals;
00254        
00255        error = XGetVolumeInfoNoName(NULL, dstVRefNum, &pb);
00256        if ( error == noErr )
00257        {
00258               /* Convert freeBytes to free disk blocks (512-byte blocks) */
00259               dstFreeBlocks = (pb.ioVFreeBytes >> 9);
00260        
00261               /* get allocation block size (always multiple of 512) and divide by 512
00262                 to get number of 512-byte blocks per allocation block */
00263               theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.ioVAlBlkSiz >> 9);
00264               
00265               theGlobals.allocBlksNeeded = 0;
00266 
00267               theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
00268               theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
00269               
00270               theGlobals.copyFilterProc = copyFilterProc;
00271               
00272               GetLevelSize(srcDirID, &theGlobals);
00273               
00274               /* Is there enough room on the destination volume for the source file?                                   */
00275               /* Note:      This will work because the largest number of disk blocks supported                  */
00276               /*                   on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk)     */
00277               /*                   will always be less than 0xffffffff.                                                                     */
00278               *spaceOK = ((theGlobals.allocBlksNeeded * theGlobals.dstBlksPerAllocBlk) <= dstFreeBlocks);
00279        }
00280 
00281        return ( error );
00282 }
00283 
00284 /*****************************************************************************/
00285 
00286 static void   CopyLevel(long sourceDirID,
00287                                             long dstDirID,
00288                                             EnumerateGlobals *theGlobals)
00289 {
00290        long currentSrcDirID;
00291        long newDirID;
00292        short index = 1;
00293        
00294        do
00295        {      
00296               /* Get next source item at the current directory level */
00297               
00298               theGlobals->myCPB.dirInfo.ioFDirIndex = index;
00299               theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
00300               theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB);             
00301 
00302               if ( theGlobals->error == noErr )
00303               {
00304                      if ( (theGlobals->copyFilterProc == NULL) ||
00305                              CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
00306                      {
00307                             /* Either there's no filter proc OR the filter proc says to use this item */
00308 
00309                             /* We have an item.  Is it a file or directory? */
00310                             if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
00311                             {
00312                                    /* We have a directory */
00313                                    
00314                                    /* Create a new directory at the destination. No errors allowed! */
00315                                    theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
00316                                    if ( theGlobals->error == noErr )
00317                                    {
00318                                           /* Save the current source directory ID where we can get it when we come back
00319                                           ** from recursion land. */
00320                                           currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
00321                                           
00322                                           /* Dive again (copy the directory level we just found below this one) */
00323                                           CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
00324                                           
00325                                           if ( !theGlobals->bailout )
00326                                           {
00327                                                  /* Copy comment from old to new directory. */
00328                                                  /* Ignore the result because we really don't care if it worked or not. */
00329                                                  (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL);
00330                                                  
00331                                                  /* Copy directory attributes (dates, etc.) to newDirID. */
00332                                                  /* No errors allowed */
00333                                                  theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true);
00334                                                  
00335                                                  /* handle any errors from CopyFileMgrAttributes */
00336                                                  if ( theGlobals->error != noErr )
00337                                                  {
00338                                                         if ( theGlobals->errorHandler != NULL )
00339                                                         {
00340                                                                theGlobals->bailout =  CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp,
00341                                                                                                          theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
00342                                                                                                          theGlobals->destinationVRefNum, newDirID, NULL);
00343                                                         }
00344                                                         else
00345                                                         {
00346                                                                /* If you don't handle the errors with an error handler, */
00347                                                                /* then the copy stops here. */
00348                                                                theGlobals->bailout = true;
00349                                                         }
00350                                                  }
00351                                           }
00352                                    }
00353                                    else   /* error handling for DirCreate */
00354                                    {
00355                                           if ( theGlobals->errorHandler != NULL )
00356                                           {
00357                                                  theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp,
00358                                                                                                   theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
00359                                                                                                   theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
00360                                           }
00361                                           else
00362                                           {
00363                                                  /* If you don't handle the errors with an error handler, */
00364                                                  /* then the copy stops here. */
00365                                                  theGlobals->bailout = true;
00366                                           }
00367                                    }
00368                                    
00369                                    if ( !theGlobals->bailout )
00370                                    {
00371                                           /* clear error return on way back if we aren't bailing out */
00372                                           theGlobals->error = noErr;
00373                                    }
00374                             }
00375                             else
00376                             {
00377                                    /* We have a file, so copy it */
00378                                    
00379                                    theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
00380                                                                                      theGlobals->myCPB.hFileInfo.ioFlParID,
00381                                                                                      theGlobals->itemName,
00382                                                                                      theGlobals->destinationVRefNum,
00383                                                                                      dstDirID,
00384                                                                                      NULL,
00385                                                                                      NULL,
00386                                                                                      theGlobals->copyBuffer,
00387                                                                                      theGlobals->bufferSize,
00388                                                                                      false);
00389                                                  
00390                                    /* handle any errors from FileCopy */
00391                                    if ( theGlobals->error != noErr )
00392                                    {
00393                                           if ( theGlobals->errorHandler != NULL )
00394                                           {
00395                                                  theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp,
00396                                                                                            theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
00397                                                                                            theGlobals->destinationVRefNum, dstDirID, NULL);
00398                                                  if ( !theGlobals->bailout )
00399                                                  {
00400                                                         /* If the CopyErrProc handled the problem, clear the error here */
00401                                                         theGlobals->error = noErr;
00402                                                  }
00403                                           }
00404                                           else
00405                                           {
00406                                                  /* If you don't handle the errors with an error handler, */
00407                                                  /* then the copy stops here. */
00408                                                  theGlobals->bailout = true;
00409                                           }
00410                                    }
00411                             }
00412                      }
00413               }
00414               else
00415               {      /* error handling for PBGetCatInfo */
00416                      /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
00417                      if ( theGlobals->error != fnfErr )
00418                      {
00419                             if ( theGlobals->errorHandler != NULL )
00420                             { 
00421                                    theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp,
00422                                                                              theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
00423                                    if ( !theGlobals->bailout )
00424                                    {
00425                                           /* If the CopyErrProc handled the problem, clear the error here */
00426                                           theGlobals->error = noErr;
00427                                    }
00428                             }
00429                             else
00430                             {
00431                                    /* If you don't handle the errors with an error handler, */
00432                                    /* then the copy stops here. */
00433                                    theGlobals->bailout = true;
00434                             }
00435                      }
00436               }
00437               ++index; /* prepare to get next item */
00438        } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
00439 }
00440 
00441 /*****************************************************************************/
00442 
00443 pascal OSErr  FilteredDirectoryCopy(short srcVRefNum,
00444                                                                  long srcDirID,
00445                                                                  ConstStr255Param srcName,
00446                                                                  short dstVRefNum,
00447                                                                  long dstDirID,
00448                                                                  ConstStr255Param dstName,
00449                                                                  void *copyBufferPtr,
00450                                                                  long copyBufferSize,
00451                                                                  Boolean preflight,
00452                                                                  CopyErrProcPtr copyErrHandler,
00453                                                                  CopyFilterProcPtr copyFilterProc, ConstStr255Param newName );
00454 /*      Make a copy of a directory structure in a new location with item filtering.
00455        The FilteredDirectoryCopy function makes a copy of a directory
00456        structure in a new location. If copyBufferPtr <> NIL, it points to
00457        a buffer of copyBufferSize that is used to copy files data. The
00458        larger the supplied buffer, the faster the copy. If
00459        copyBufferPtr = NIL, then this routine allocates a buffer in the
00460        application heap. If you pass a copy buffer to this routine, make
00461        its size a multiple of 512 ($200) bytes for optimum performance.
00462        
00463        The optional copyFilterProc parameter lets a routine you define
00464        decide what files or directories are copied to the destination.
00465        
00466        FilteredDirectoryCopy normally creates a new directory *in* the
00467        specified destination directory and copies the source directory's
00468        content into the new directory. However, if root parent directory
00469        (fsRtParID) is passed as the dstDirID parameter and NULL is
00470        passed as the dstName parameter, DirectoryCopy renames the
00471        destination volume to the source directory's name (truncating
00472        if the name is longer than 27 characters) and copies the source
00473        directory's content into the destination volume's root directory.
00474        This special case is supported by FilteredDirectoryCopy, but
00475        not by FSpFilteredDirectoryCopy since with FSpFilteredDirectoryCopy,
00476        the dstName parameter can not be NULL.
00477        
00478        srcVRefNum           input: Source volume specification.
00479        srcDirID             input: Source directory ID.
00480        srcName                     input: Source directory name, or nil if
00481                                                  srcDirID specifies the directory.
00482        dstVRefNum           input: Destination volume specification.
00483        dstDirID             input: Destination directory ID.
00484        dstName                     input: Destination directory name, or nil if
00485                                                  dstDirID specifies the directory.
00486        copyBufferPtr input: Points to a buffer of copyBufferSize that
00487                                                  is used the i/o buffer for the copy or
00488                                                  nil if you want DirectoryCopy to allocate its
00489                                                  own buffer in the application heap.
00490        copyBufferSize       input: The size of the buffer pointed to
00491                                                  by copyBufferPtr.
00492        preflight            input: If true, DirectoryCopy makes sure there are
00493                                                  enough allocation blocks on the destination
00494                                                  volume to hold the directory's files before
00495                                                  starting the copy.
00496        copyErrHandler       input: A pointer to the routine you want called if an
00497                                                  error condition is detected during the copy, or
00498                                                  nil if you don't want to handle error conditions.
00499                                                  If you don't handle error conditions, the first
00500                                                  error will cause the copy to quit and
00501                                                  DirectoryCopy will return the error.
00502                                                  Error handling is recommended...
00503        copyFilterProc       input: A pointer to the filter routine you want called
00504                                                  for each item in the source directory, or NULL
00505                                                  if you don't want to filter.
00506        
00507        Result Codes
00508               noErr                       0             No error
00509               readErr                            19           Driver does not respond to read requests
00510               writErr                            20           Driver does not respond to write requests
00511               badUnitErr                  21           Driver reference number does not
00512                                                                match unit table
00513               unitEmptyErr         22           Driver reference number specifies a
00514                                                                nil handle in unit table
00515               abortErr                    27           Request aborted by KillIO
00516               notOpenErr                  28           Driver not open
00517               dskFulErr                   -34           Destination volume is full
00518               nsvErr                      -35           No such volume
00519               ioErr                       -36           I/O error
00520               bdNamErr                    -37           Bad filename
00521               tmfoErr                            -42           Too many files open
00522               fnfErr                      -43           Source file not found, or destination
00523                                                                directory does not exist
00524               wPrErr                      -44           Volume locked by hardware
00525               fLckdErr                    -45           File is locked
00526               vLckdErr                    -46           Destination volume is read-only
00527               fBsyErr                            -47           The source or destination file could
00528                                                                not be opened with the correct access
00529                                                                modes
00530               dupFNErr                    -48           Destination file already exists
00531               opWrErr                            -49           File already open for writing
00532               paramErr                    -50           No default volume or function not
00533                                                                supported by volume
00534               permErr                            -54           File is already open and cannot be opened using specified deny modes
00535               memFullErr                  -108   Copy buffer could not be allocated
00536               dirNFErr                    -120   Directory not found or incomplete pathname
00537               wrgVolTypErr         -123   Function not supported by volume
00538               afpAccessDenied             -5000  User does not have the correct access
00539               afpDenyConflict             -5006  The source or destination file could
00540                                                                not be opened with the correct access
00541                                                                modes
00542               afpObjectTypeErr     -5025  Source is a directory, directory not found
00543                                                                or incomplete pathname
00544        
00545        __________
00546        
00547        Also see:     CopyErrProcPtr, CopyFilterProcPtr, FSpFilteredDirectoryCopy,
00548                             DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy
00549 */
00550 
00551 /*****************************************************************************/
00552 
00553 pascal OSErr  FilteredDirectoryCopy(short srcVRefNum,
00554                                                                  long srcDirID,
00555                                                                  ConstStr255Param srcName,
00556                                                                  short dstVRefNum,
00557                                                                  long dstDirID,
00558                                                                  ConstStr255Param dstName,
00559                                                                  void *copyBufferPtr,
00560                                                                  long copyBufferSize,
00561                                                                  Boolean preflight,
00562                                                                  CopyErrProcPtr copyErrHandler,
00563                                                                  CopyFilterProcPtr copyFilterProc, ConstStr255Param newName)
00564 {
00565        EnumerateGlobals theGlobals;
00566        Boolean       isDirectory;
00567        OSErr  error;
00568        Boolean ourCopyBuffer = false;
00569        Str63  srcDirName, oldDiskName;
00570        Boolean spaceOK;                   
00571        
00572        /* Make sure a copy buffer is allocated. */
00573        if ( copyBufferPtr == NULL )
00574        {
00575               /* The caller didn't supply a copy buffer so grab one from the application heap.
00576               ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
00577               ** If 512 bytes aren't available, we're in trouble. */
00578               copyBufferSize = dirCopyBigCopyBuffSize;
00579               copyBufferPtr = NewPtr(copyBufferSize);
00580               if ( copyBufferPtr == NULL )
00581               {
00582                      copyBufferSize = dirCopyMinCopyBuffSize;
00583                      copyBufferPtr = NewPtr(copyBufferSize);
00584                      if ( copyBufferPtr == NULL )
00585                      {
00586                             return ( memFullErr );
00587                      }
00588               }
00589               ourCopyBuffer = true;
00590        }
00591        
00592        /* Get the real dirID where we're copying from and make sure it is a directory. */
00593        error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
00594        if ( error != noErr )
00595        {
00596               goto ErrorExit;
00597        }
00598        if ( !isDirectory )
00599        {
00600               error = dirNFErr;
00601               goto ErrorExit;
00602        }
00603        
00604        /* Special case destination if it is the root parent directory. */
00605        /* Since you can't create the root directory, this is needed if */
00606        /* you want to copy a directory's content to a disk's root directory. */
00607        if ( (dstDirID == fsRtParID) && (dstName == NULL) )
00608        {
00609               dstDirID = fsRtParID;
00610               isDirectory = true;
00611               error = noErr;
00612        }
00613        else
00614        {
00615               /*  Get the real dirID where we're going to put the copy and make sure it is a directory. */
00616               error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
00617               if ( error != noErr )
00618               {
00619                      goto ErrorExit;
00620               }
00621               if ( !isDirectory )
00622               {
00623                      error =  dirNFErr;
00624                      goto ErrorExit;
00625               }
00626        }
00627        
00628        /* Get the real vRefNum of both the source and destination */
00629        error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
00630        if ( error != noErr )
00631        {
00632               goto ErrorExit;
00633        }
00634        error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
00635        if ( error != noErr )
00636        {
00637               goto ErrorExit;
00638        }
00639        
00640        if ( preflight )
00641        {
00642               error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK);
00643               if ( error != noErr )
00644               {
00645                      goto ErrorExit;
00646               }
00647               if ( !spaceOK )
00648               {
00649                      error = dskFulErr; /* not enough room on destination */
00650                      goto ErrorExit;
00651               }
00652        }
00653 
00654        /* Create the new directory in the destination directory with the */
00655        /* same name as the source directory. */
00656        /* 
00657               if newName is not empty use it rather than the original dir name.
00658        */
00659        if ( newName[0] == 0 )
00660        {
00661               error = GetDirName(srcVRefNum, srcDirID, srcDirName);
00662               if ( error != noErr )
00663               {
00664                      goto ErrorExit;
00665               }
00666        }
00667        else
00668        {
00669               memcpy(srcDirName, newName, 32 );
00670               
00671        }      
00672        /* Again, special case destination if the destination is the */
00673        /* root parent directory. This time, we'll rename the disk to */
00674        /* the source directory name. */
00675        if ( dstDirID == fsRtParID )
00676        {
00677               /* Get the current name of the destination disk */
00678               error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName);
00679               if ( error == noErr )       
00680               {
00681                      /* Shorten the name if it's too long to be the volume name */
00682                      TruncPString(srcDirName, srcDirName, 27);
00683                      
00684                      /* Rename the disk */
00685                      error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName);
00686                      /* and copy to the root directory */
00687                      dstDirID = fsRtDirID;
00688               }
00689        }
00690        else
00691        {
00692               error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
00693        }
00694        if ( error != noErr )
00695        {
00696               /* handle any errors from DirCreate */
00697               if ( copyErrHandler != NULL )
00698               {
00699                      if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp,
00700                                                                                            srcVRefNum, srcDirID, NULL,
00701                                                                                            dstVRefNum, dstDirID, srcDirName) )
00702                      {
00703                             goto ErrorExit;
00704                      }
00705                      else
00706                      {
00707                             /* If the CopyErrProc handled the problem, clear the error here */
00708                             /* and continue */
00709                             error = noErr;
00710                      }
00711               }
00712               else
00713               {
00714                      /* If you don't handle the errors with an error handler, */
00715                      /* then the copy stops here. */
00716                      goto ErrorExit;
00717               }
00718        }
00719        
00720        /* dstDirID is now the newly created directory! */
00721               
00722        /* Set up the globals we need to access from the recursive routine. */
00723        theGlobals.copyBuffer = (Ptr)copyBufferPtr;
00724        theGlobals.bufferSize = copyBufferSize;
00725        theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
00726        theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
00727        theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
00728        theGlobals.errorHandler = copyErrHandler;
00729        theGlobals.bailout = false;
00730        theGlobals.copyFilterProc =  copyFilterProc;
00731               
00732        /* Here we go into recursion land... */
00733        CopyLevel(srcDirID, dstDirID, &theGlobals);
00734        error = theGlobals.error;   /* get the result */
00735        
00736        if ( !theGlobals.bailout )
00737        {
00738               /* Copy comment from source to destination directory. */
00739               /* Ignore the result because we really don't care if it worked or not. */
00740               (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL);
00741               
00742               /* Copy the File Manager attributes */
00743               error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
00744                                    dstVRefNum, dstDirID, NULL, true);
00745               
00746               /* handle any errors from CopyFileMgrAttributes */
00747               if ( (error != noErr) && (copyErrHandler != NULL) )
00748               {
00749                      theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp,
00750                                                                                     srcVRefNum, srcDirID, NULL,
00751                                                                                     dstVRefNum, dstDirID, NULL);
00752               }
00753        }
00754 
00755 ErrorExit:
00756        /* Get rid of the copy buffer if we allocated it. */
00757        if ( ourCopyBuffer )
00758        {
00759               DisposePtr((Ptr)copyBufferPtr);
00760        }
00761 
00762        return ( error );
00763 }
00764 
00765 
00766 /*****************************************************************************/
00767 
00768 
00769 pascal OSErr  MacFSpDirectoryCopyRename(const FSSpec *srcSpec,
00770                                                          const FSSpec *dstSpec,
00771                                                          ConstStr255Param newName,
00772                                                          void *copyBufferPtr,
00773                                                          long copyBufferSize,
00774                                                          Boolean preflight,
00775                                                          CopyErrProcPtr copyErrHandler)
00776 {
00777        return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
00778                                                            dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
00779                                                            copyBufferPtr, copyBufferSize, preflight,
00780                                                            copyErrHandler, NULL, newName) );
00781 }