From 2276c2959446be677a52f639d8f92e5a0e216c32 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Mon, 1 Feb 1999 15:43:00 +0000 Subject: [PATCH] latest CW additions git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@1561 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- samples/minimal/make_cw.mcp | Bin 131407 -> 133039 bytes src/mac/morefile/Director.cpp | 653 +++++++ src/mac/morefile/Director.h | 493 +++++ src/mac/morefile/FSpCompa.cpp | 928 ++++++++++ src/mac/morefile/FSpCompa.h | 488 +++++ src/mac/morefile/FileCopy.cpp | 593 ++++++ src/mac/morefile/FileCopy.h | 220 +++ src/mac/morefile/FullPath.cpp | 246 +++ src/mac/morefile/FullPath.h | 243 +++ src/mac/morefile/IterateD.cpp | 189 ++ src/mac/morefile/IterateD.h | 171 ++ src/mac/morefile/MoreDesk.cpp | 1254 +++++++++++++ src/mac/morefile/MoreDesk.h | 541 ++++++ src/mac/morefile/MoreExtr.cpp | 3228 +++++++++++++++++++++++++++++++++ src/mac/morefile/MoreExtr.h | 3141 ++++++++++++++++++++++++++++++++ src/mac/morefile/MoreFile.cpp | 628 +++++++ src/mac/morefile/MoreFile.h | 1244 +++++++++++++ src/mac/morefile/Optim.h | 91 + src/mac/morefile/OptimEnd.h | 40 + src/mac/morefile/Search.cpp | 1275 +++++++++++++ src/mac/morefile/Search.h | 246 +++ src/make_cw.mcp | Bin 229358 -> 264734 bytes 22 files changed, 15912 insertions(+) create mode 100644 src/mac/morefile/Director.cpp create mode 100644 src/mac/morefile/Director.h create mode 100644 src/mac/morefile/FSpCompa.cpp create mode 100644 src/mac/morefile/FSpCompa.h create mode 100644 src/mac/morefile/FileCopy.cpp create mode 100644 src/mac/morefile/FileCopy.h create mode 100644 src/mac/morefile/FullPath.cpp create mode 100644 src/mac/morefile/FullPath.h create mode 100644 src/mac/morefile/IterateD.cpp create mode 100644 src/mac/morefile/IterateD.h create mode 100644 src/mac/morefile/MoreDesk.cpp create mode 100644 src/mac/morefile/MoreDesk.h create mode 100644 src/mac/morefile/MoreExtr.cpp create mode 100644 src/mac/morefile/MoreExtr.h create mode 100644 src/mac/morefile/MoreFile.cpp create mode 100644 src/mac/morefile/MoreFile.h create mode 100644 src/mac/morefile/Optim.h create mode 100644 src/mac/morefile/OptimEnd.h create mode 100644 src/mac/morefile/Search.cpp create mode 100644 src/mac/morefile/Search.h diff --git a/samples/minimal/make_cw.mcp b/samples/minimal/make_cw.mcp index 3450e5aa43d419576670b3294ab07be5d062c95c..38041ef961f2aed0fa349f56007aa80df208e0cb 100644 GIT binary patch delta 2749 zcmZuyeQZ=!7Qg3pX8JmPeM~=x4lSK_sx1O77?hb#ttBmL!9OMoCa{&#p%wY)P?pw+ zLYyjQH&GH9E_nS;i1@`u5npg>HZ0iSN8Ck%1QxAP{EC=ewneNTflz<<-8XSCp7!2z z&hMOe?m73|d*}E=`u@lClAiwlKE_xMV^q)RhkiyA;y}rNbKNU|-pMcL*73iaMqvEs zbTE@Kz9T;xFK4;7Lx3R+sEifZyn?xGUMbfH`I+11iyVEb!>2jlH90)&_(dFjK5sUv zN?UAinKA}uTj%rQb)`XB9wOW2VqfImg!sXUP+m2p8e5BREk@Nx_`-r(w-+vXeB677 zc(k7ae4A0avnZdxYM7*nfB2~qNUM?Gc<*NXuk5+EVGTcST*q%pn*1lDhM)Bnh~*iFX1 zTt03_#O)(AWr{4){zD*;b>0kXvSBt^vkMJM#XS7%LEY+)x4DYk1`M+OAh#b=S872e zkNH-McTZ7eD&`fPGstVj{5|b@BHq)mX<$T%}(bY$Jd_&Pp>+Pap$_-+z6`Cf-U`n5Fp%WT^ zQupPKK6Geu^0T1zx?ZZ;JM{dn=IY=r9k(yUfvA;S_C#`Y&hO|oZ|u&YX$UxYb1zDL z#B-&3zxLzJ`?Wq>zu%Mgy+FPkq~sw!G!e2EZkS&vPG3*c__6K|P1}-M5aQ1r^;r2s zyGpF)ucuH27C@v5xd3>j!>8Mvf3Q0uS6c=P{{wD)F>xS(EzV0HevKwwsaT$M_FyV( z4g7ySanE=?RSRmNTJdZP#pG14>Q6`BAyc!4CR%Fv7k5?Uo_y*)#+DabbKkr~eDdzH z+>^&%WNdb)0{rXBJfo38#ytGNf_7`pnqjgU`^vQR89!|=;YaksOa@U(Pwu01PI}u2 zeM4e!J?%{&YNj?4u@<_5Bw*at0gvThZ6@|sqW7H6ccjI)j#NbBxd|}r!q(33Pf<39%1M$JW7Cg(IHx+s~ zG1%AtJ3I!xJ~1!||D1D>9a`Ou2X9tltX1bnzC^3QnB~~HOAYr!l8#5 zDu_VJgv-LcL%=iPvhf|jXE|Gxg&WJn=mokJ_cruB-P*AUUL_kh-L;ycZSO&T7XxJc z6_eM-n^F>!Hjp8s8p{8MQpoR16woum`korg_d#w^pY;zz{+Beud`?`#YV@IKyGVyS zWcLb`*Og}=|3~@&x(1U%l$Y&(Vm??iVoMg_gg8Z>WftzCS4|G~H1L@ENoytiv zL%wNTuBs0-%14s9_K8nN!ZP&w=CjOZqQpLzvf9oPA3GOtEpc z4)G{g#c9QskWb<3i_u~|sSL(A428;ITnp}$v=3!ck;1+0;Hso($!6dPAjxQwlUn%7 zN5D&Lm>IW#n+iWQ1YGVtD1rBiJPJRZd>BfZGMI898=xKqq2YUnNCX6uIHfmlp(SKa)tV! z`w{}hRG_^d0OR|A(P3uGb*=M+Yld;%QjLXtU($+;3Uhk(~g6t40LI4!Ga z1ujo3s8HJNmL;j_7XgFZda#Jx>JL;xonrv%6yCHA@>G@3H{U^)j~rMOm9dwV|I$AJ zz-~y2&%xB7{QvhN@at@tKEvauIcMe29l(<_Z3FYm9@VHop+zvvQuqeBBTi)uCz@s} zJh>Izxivps2HxQK-vI6`aPAl2jW$jRoE5fH#s9NBH!?wxmV7)taG9!H^uTVebp_6D X0p^l^%sW>WdZ+D1WAjBmY3;uPDWXTh delta 2500 zcmZ9Mdu&ui6o=>BZg+3Dy9+H#A4{PNRJuTEc}nS0QY{pU7zo9HwqTdG$ZJ_>tD;f1 z;T02!T8DUv5s`=TR310Ytr!9l5u?E>hG&S@Ka3EuK(s)Cu>NLeFsqZjbI$$Fxo6It zGqVTZ);ref-nvL+Ib$q|F$ys1>c!~B_`}{m9NeMjwa(D{*T7iISh9;T9V)9~F=pF4 zQ>b^Y@@aZi7CvQ8JVNS64mhk>?DI9yjjelWfYrN(?h!9@+jxib1oh+VQ-aY9=7ycI zOqLv%)yx)`mCO^D)0q~RSxROzNBrH#C@%XcnWMhzul&X=J;!nF_Ml&li!;|%=Lv@9XNS5Eq&D?pmnk=n` zx-~N=Y$txEw$3)UYF0U)=ku*!In30S&EEKC zTNh6p=&h)nF$KHG6B1{`FC_BFRPVWF*Wx6~D{qQme_1G91)ixf493PzU~TuVJDELR zQr-Nu`>;u=nr3#kwHBL^t}s!qaj=x~_@x`et%s#FEy*f9MNZ9Z>7F#m zcr!wNE8wS%UbC`S3Yj?_>Jp`Svcqv96Q?*8Nh_2AJ2{_PY2jPciTC z^s=f?l3O#j1!8tXexhY&Ggn;Hgv1r`Zz+D=kxYxz*=*L zlCjN;RBN|f=c!&#oQ*Br6w*Sq#{F(8^rfR}$t>KwQe0wrFrhH!2^i(KsW9r=KY{B3ya&Qf&G1jU3SA9-3%XMfUfJ|N-q49lUmtFM0qg(Ae-=Ir zgpHfGX{axC6JD-_L-8b4Kp{4NXpFf-MU_nndq9;R4fm7KT97;J>bAyB?voIY_LSfs z8f)xFxX`hLaN zu!w@?r=fq12gCIqCTEl(rh5ij54A%jpPY)GkAB8?ieijiu`c4=b zb7vXVJy`UDv9pJcR}K+KdOiFA`UT;GuH(L`WJfQ@z8gGK z@h(UiD431gLRpGGfPPs9I*G1Pw&bJF<7SMO+!Pz|N&dKwW2(n_Q1p-bF&2f1f+z{~ zmWBx{py$XBYQhLKO9tBYJ$Qe~C;B1P0>2LhA0YYjXn|e&2W#hm=Ssc^txHOm@ykSu z{gO|dE!JNs4b$$zFi;wnh!2b9!tIN|#mRzLG8eNvg~6)J05}(MUXNK_iQz5a;(`f( zwCPdHa>@W66Bt}vp?J%pRMKOBh~q5`Z(;scCRBX`AYbyO^Pp$RgnqjQ9c{p%Wh*i7 zk^XaL1E3m$FBdH;l>XO00T`xu1rD7$E61Jzj}DiH75JD9l7R+KhoM06W=OR&h7(OA z6n_I!-I_b%926?P15zz;wFkUN@y{UNmGPhZ6nv!QTBOYSNz-y|wA=X0t}XEW96^RQ U;mDk$#7=9ZV`r1G%%M&G2jq1Oy#N3J diff --git a/src/mac/morefile/Director.cpp b/src/mac/morefile/Director.cpp new file mode 100644 index 0000000000..f8b0d0a891 --- /dev/null +++ b/src/mac/morefile/Director.cpp @@ -0,0 +1,653 @@ +/* +** Apple Macintosh Developer Technical Support +** +** DirectoryCopy: A robust, general purpose directory copy routine. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: DirectoryCopy.c +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFile.h" +#include "MoreExtr.h" +#include "MoreDesk.h" +#include "FileCopy.h" +#include "Director.h" + +/*****************************************************************************/ + +/* local constants */ + +enum +{ + dirCopyBigCopyBuffSize = 0x00004000, + dirCopyMinCopyBuffSize = 0x00000200 +}; + + +/*****************************************************************************/ + +/* local data structures */ + +/* The EnumerateGlobals structure is used to minimize the amount of +** stack space used when recursively calling CopyLevel and to hold +** global information that might be needed at any time. */ + +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +struct EnumerateGlobals +{ + Ptr copyBuffer; /* pointer to buffer used for file copy operations */ + long bufferSize; /* the size of the copy buffer */ + CopyErrProcPtr errorHandler; /* pointer to error handling function */ + CopyFilterProcPtr copyFilterProc; /* pointer to filter function */ + OSErr error; /* temporary holder of results - saves 2 bytes of stack each level */ + Boolean bailout; /* set to true to by error handling function if fatal error */ + short destinationVRefNum; /* the destination vRefNum */ + Str63 itemName; /* the name of the current item */ + CInfoPBRec myCPB; /* the parameter block used for PBGetCatInfo calls */ +}; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + +typedef struct EnumerateGlobals EnumerateGlobals; +typedef EnumerateGlobals *EnumerateGlobalsPtr; + + +/* The PreflightGlobals structure is used to minimize the amount of +** stack space used when recursively calling GetLevelSize and to hold +** global information that might be needed at any time. */ + +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +struct PreflightGlobals +{ + OSErr result; /* temporary holder of results - saves 2 bytes of stack each level */ + Str63 itemName; /* the name of the current item */ + CInfoPBRec myCPB; /* the parameter block used for PBGetCatInfo calls */ + + unsigned long dstBlksPerAllocBlk; /* the number of 512 byte blocks per allocation block on destination */ + + unsigned long allocBlksNeeded; /* the total number of allocation blocks needed */ + + unsigned long tempBlocks; /* temporary storage for calculations (save some stack space) */ + CopyFilterProcPtr copyFilterProc; /* pointer to filter function */ +}; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + +typedef struct PreflightGlobals PreflightGlobals; +typedef PreflightGlobals *PreflightGlobalsPtr; + +/*****************************************************************************/ + +/* static prototypes */ + +static void GetLevelSize(long currentDirID, + PreflightGlobals *theGlobals); + +static OSErr PreflightDirectoryCopySpace(short srcVRefNum, + long srcDirID, + short dstVRefNum, + CopyFilterProcPtr copyFilterProc, + Boolean *spaceOK); + +static void CopyLevel(long sourceDirID, + long dstDirID, + EnumerateGlobals *theGlobals); + +/*****************************************************************************/ + +static void GetLevelSize(long currentDirID, + PreflightGlobals *theGlobals) +{ + short index = 1; + + do + { + theGlobals->myCPB.dirInfo.ioFDirIndex = index; + theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID; /* we need to do this every time */ + /* through, since GetCatInfo */ + /* returns ioFlNum in this field */ + theGlobals->result = PBGetCatInfoSync(&theGlobals->myCPB); + if ( theGlobals->result == noErr ) + { + if ( (theGlobals->copyFilterProc == NULL) || + CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */ + { + /* Either there's no filter proc OR the filter proc says to use this item */ + if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* we have a directory */ + + GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */ + theGlobals->result = noErr; /* clear error return on way back */ + } + else + { + /* We have a file - add its allocation blocks to allocBlksNeeded. */ + /* Since space on Mac OS disks is always allocated in allocation blocks, */ + /* this takes into account rounding up to the end of an allocation block. */ + + /* get number of 512-byte blocks needed for data fork */ + if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 ) + { + theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1; + } + else + { + theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9; + } + /* now, calculate number of new allocation blocks needed for the data fork and add it to the total */ + if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk ) + { + theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1; + } + else + { + theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk; + } + + /* get number of 512-byte blocks needed for resource fork */ + if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 ) + { + theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1; + } + else + { + theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9; + } + /* now, calculate number of new allocation blocks needed for the resource fork and add it to the total */ + if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk ) + { + theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1; + } + else + { + theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk; + } + } + } + } + ++index; + } while ( theGlobals->result == noErr ); +} + +/*****************************************************************************/ + +static OSErr PreflightDirectoryCopySpace(short srcVRefNum, + long srcDirID, + short dstVRefNum, + CopyFilterProcPtr copyFilterProc, + Boolean *spaceOK) +{ + XVolumeParam pb; + OSErr error; + unsigned long dstFreeBlocks; + PreflightGlobals theGlobals; + + error = XGetVolumeInfoNoName(NULL, dstVRefNum, &pb); + if ( error == noErr ) + { + /* Convert freeBytes to free disk blocks (512-byte blocks) */ + dstFreeBlocks = (pb.ioVFreeBytes.hi << 23) + (pb.ioVFreeBytes.lo >> 9); + + /* get allocation block size (always multiple of 512) and divide by 512 + to get number of 512-byte blocks per allocation block */ + theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.ioVAlBlkSiz >> 9); + + theGlobals.allocBlksNeeded = 0; + + theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName; + theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum; + + theGlobals.copyFilterProc = copyFilterProc; + + GetLevelSize(srcDirID, &theGlobals); + + /* Is there enough room on the destination volume for the source file? */ + /* Note: This will work because the largest number of disk blocks supported */ + /* on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk) */ + /* will always be less than 0xffffffff. */ + *spaceOK = ((theGlobals.allocBlksNeeded * theGlobals.dstBlksPerAllocBlk) <= dstFreeBlocks); + } + + return ( error ); +} + +/*****************************************************************************/ + +static void CopyLevel(long sourceDirID, + long dstDirID, + EnumerateGlobals *theGlobals) +{ + long currentSrcDirID; + long newDirID; + short index = 1; + + do + { + /* Get next source item at the current directory level */ + + theGlobals->myCPB.dirInfo.ioFDirIndex = index; + theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID; + theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB); + + if ( theGlobals->error == noErr ) + { + if ( (theGlobals->copyFilterProc == NULL) || + CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */ + { + /* Either there's no filter proc OR the filter proc says to use this item */ + + /* We have an item. Is it a file or directory? */ + if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* We have a directory */ + + /* Create a new directory at the destination. No errors allowed! */ + theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID); + if ( theGlobals->error == noErr ) + { + /* Save the current source directory ID where we can get it when we come back + ** from recursion land. */ + currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID; + + /* Dive again (copy the directory level we just found below this one) */ + CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals); + + if ( !theGlobals->bailout ) + { + /* Copy comment from old to new directory. */ + /* Ignore the result because we really don't care if it worked or not. */ + (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL); + + /* Copy directory attributes (dates, etc.) to newDirID. */ + /* No errors allowed */ + theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true); + + /* handle any errors from CopyFileMgrAttributes */ + if ( theGlobals->error != noErr ) + { + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp, + theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, + theGlobals->destinationVRefNum, newDirID, NULL); + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + } + } + else /* error handling for DirCreate */ + { + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp, + theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, + theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName); + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + + if ( !theGlobals->bailout ) + { + /* clear error return on way back if we aren't bailing out */ + theGlobals->error = noErr; + } + } + else + { + /* We have a file, so copy it */ + + theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum, + theGlobals->myCPB.hFileInfo.ioFlParID, + theGlobals->itemName, + theGlobals->destinationVRefNum, + dstDirID, + NULL, + NULL, + theGlobals->copyBuffer, + theGlobals->bufferSize, + false); + + /* handle any errors from FileCopy */ + if ( theGlobals->error != noErr ) + { + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp, + theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName, + theGlobals->destinationVRefNum, dstDirID, NULL); + if ( !theGlobals->bailout ) + { + /* If the CopyErrProc handled the problem, clear the error here */ + theGlobals->error = noErr; + } + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + } + } + } + else + { /* error handling for PBGetCatInfo */ + /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */ + if ( theGlobals->error != fnfErr ) + { + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp, + theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL); + if ( !theGlobals->bailout ) + { + /* If the CopyErrProc handled the problem, clear the error here */ + theGlobals->error = noErr; + } + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + } + ++index; /* prepare to get next item */ + } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */ +} + +/*****************************************************************************/ + +pascal OSErr FilteredDirectoryCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc) +{ + EnumerateGlobals theGlobals; + Boolean isDirectory; + OSErr error; + Boolean ourCopyBuffer = false; + Str63 srcDirName, oldDiskName; + Boolean spaceOK; + + /* Make sure a copy buffer is allocated. */ + if ( copyBufferPtr == NULL ) + { + /* The caller didn't supply a copy buffer so grab one from the application heap. + ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer. + ** If 512 bytes aren't available, we're in trouble. */ + copyBufferSize = dirCopyBigCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + copyBufferSize = dirCopyMinCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + return ( memFullErr ); + } + } + ourCopyBuffer = true; + } + + /* Get the real dirID where we're copying from and make sure it is a directory. */ + error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory); + if ( error != noErr ) + { + goto ErrorExit; + } + if ( !isDirectory ) + { + error = dirNFErr; + goto ErrorExit; + } + + /* Special case destination if it is the root parent directory. */ + /* Since you can't create the root directory, this is needed if */ + /* you want to copy a directory's content to a disk's root directory. */ + if ( (dstDirID == fsRtParID) && (dstName == NULL) ) + { + dstDirID = fsRtParID; + isDirectory = true; + error = noErr; + } + else + { + /* Get the real dirID where we're going to put the copy and make sure it is a directory. */ + error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory); + if ( error != noErr ) + { + goto ErrorExit; + } + if ( !isDirectory ) + { + error = dirNFErr; + goto ErrorExit; + } + } + + /* Get the real vRefNum of both the source and destination */ + error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum); + if ( error != noErr ) + { + goto ErrorExit; + } + error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum); + if ( error != noErr ) + { + goto ErrorExit; + } + + if ( preflight ) + { + error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK); + if ( error != noErr ) + { + goto ErrorExit; + } + if ( !spaceOK ) + { + error = dskFulErr; /* not enough room on destination */ + goto ErrorExit; + } + } + + /* Create the new directory in the destination directory with the */ + /* same name as the source directory. */ + error = GetDirName(srcVRefNum, srcDirID, srcDirName); + if ( error != noErr ) + { + goto ErrorExit; + } + + /* Again, special case destination if the destination is the */ + /* root parent directory. This time, we'll rename the disk to */ + /* the source directory name. */ + if ( dstDirID == fsRtParID ) + { + /* Get the current name of the destination disk */ + error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName); + if ( error == noErr ) + { + /* Shorten the name if it's too long to be the volume name */ + TruncPString(srcDirName, srcDirName, 27); + + /* Rename the disk */ + error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName); + /* and copy to the root directory */ + dstDirID = fsRtDirID; + } + } + else + { + error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID); + } + if ( error != noErr ) + { + /* handle any errors from DirCreate */ + if ( copyErrHandler != NULL ) + { + if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp, + srcVRefNum, srcDirID, NULL, + dstVRefNum, dstDirID, srcDirName) ) + { + goto ErrorExit; + } + else + { + /* If the CopyErrProc handled the problem, clear the error here */ + /* and continue */ + error = noErr; + } + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + goto ErrorExit; + } + } + + /* dstDirID is now the newly created directory! */ + + /* Set up the globals we need to access from the recursive routine. */ + theGlobals.copyBuffer = (Ptr)copyBufferPtr; + theGlobals.bufferSize = copyBufferSize; + theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */ + theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName; + theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum; + theGlobals.errorHandler = copyErrHandler; + theGlobals.bailout = false; + theGlobals.copyFilterProc = copyFilterProc; + + /* Here we go into recursion land... */ + CopyLevel(srcDirID, dstDirID, &theGlobals); + error = theGlobals.error; /* get the result */ + + if ( !theGlobals.bailout ) + { + /* Copy comment from source to destination directory. */ + /* Ignore the result because we really don't care if it worked or not. */ + (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL); + + /* Copy the File Manager attributes */ + error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL, + dstVRefNum, dstDirID, NULL, true); + + /* handle any errors from CopyFileMgrAttributes */ + if ( (error != noErr) && (copyErrHandler != NULL) ) + { + theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp, + srcVRefNum, srcDirID, NULL, + dstVRefNum, dstDirID, NULL); + } + } + +ErrorExit: + /* Get rid of the copy buffer if we allocated it. */ + if ( ourCopyBuffer ) + { + DisposePtr((Ptr)copyBufferPtr); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DirectoryCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler) +{ + return ( FilteredDirectoryCopy(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, + copyBufferPtr, copyBufferSize, preflight, + copyErrHandler, NULL) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpFilteredDirectoryCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc) +{ + return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyBufferPtr, copyBufferSize, preflight, + copyErrHandler, copyFilterProc) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpDirectoryCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler) +{ + return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyBufferPtr, copyBufferSize, preflight, + copyErrHandler, NULL) ); +} + +/*****************************************************************************/ + diff --git a/src/mac/morefile/Director.h b/src/mac/morefile/Director.h new file mode 100644 index 0000000000..a042fd721d --- /dev/null +++ b/src/mac/morefile/Director.h @@ -0,0 +1,493 @@ +/* +** Apple Macintosh Developer Technical Support +** +** DirectoryCopy: A robust, general purpose directory copy routine. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: DirectoryCopy.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __DIRECTORYCOPY__ +#define __DIRECTORYCOPY__ + +#include +#include + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +enum +{ + getNextItemOp = 1, /* couldn't access items in this directory - no access privileges */ + copyDirCommentOp = 2, /* couldn't copy directory's Finder comment */ + copyDirAccessPrivsOp = 3, /* couldn't copy directory's AFP access privileges */ + copyDirFMAttributesOp = 4, /* couldn't copy directory's File Manager attributes */ + dirCreateOp = 5, /* couldn't create destination directory */ + fileCopyOp = 6 /* couldn't copy file */ +}; + +/*****************************************************************************/ + +typedef pascal Boolean (*CopyErrProcPtr) (OSErr error, + short failedOperation, + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName); +/* ¦ Prototype for the CopyErrProc function DirectoryCopy calls. + This is the prototype for the CopyErrProc function DirectoryCopy + calls if an error condition is detected sometime during the copy. If + CopyErrProc returns false, then DirectoryCopy attempts to continue with + the directory copy operation. If CopyErrProc returns true, then + DirectoryCopy stops the directory copy operation. + + error input: The error result code that caused CopyErrProc to + be called. + failedOperation input: The operation that returned an error to + DirectoryCopy. + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source file or directory name, or nil if + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Destination file or directory name, or nil if + dstDirID specifies the directory. + + __________ + + Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy, DirectoryCopy, FSpDirectoryCopy +*/ + +#define CallCopyErrProc(userRoutine, error, failedOperation, srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName) \ + (*(userRoutine))((error), (failedOperation), (srcVRefNum), (srcDirID), (srcName), (dstVRefNum), (dstDirID), (dstName)) + +/*****************************************************************************/ + +typedef pascal Boolean (*CopyFilterProcPtr) (const CInfoPBRec * const cpbPtr); + +/* ¦ Prototype for the CopyFilterProc function. + This is the prototype for the CopyFilterProc function called by + FilteredDirectoryCopy and GetLevelSize. If true is returned, + the file/folder is included in the copy, otherwise it is excluded. + + pb input: Points to the CInfoPBRec for the item under consideration. + + __________ + + Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy +*/ + +#define CallCopyFilterProc(userRoutine, cpbPtr) (*(userRoutine))((cpbPtr)) + +/*****************************************************************************/ + +pascal OSErr FilteredDirectoryCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc); +/* ¦ Make a copy of a directory structure in a new location with item filtering. + The FilteredDirectoryCopy function makes a copy of a directory + structure in a new location. If copyBufferPtr <> NIL, it points to + a buffer of copyBufferSize that is used to copy files data. The + larger the supplied buffer, the faster the copy. If + copyBufferPtr = NIL, then this routine allocates a buffer in the + application heap. If you pass a copy buffer to this routine, make + its size a multiple of 512 ($200) bytes for optimum performance. + + The optional copyFilterProc parameter lets a routine you define + decide what files or directories are copied to the destination. + + FilteredDirectoryCopy normally creates a new directory *in* the + specified destination directory and copies the source directory's + content into the new directory. However, if root parent directory + (fsRtParID) is passed as the dstDirID parameter and NULL is + passed as the dstName parameter, DirectoryCopy renames the + destination volume to the source directory's name (truncating + if the name is longer than 27 characters) and copies the source + directory's content into the destination volume's root directory. + This special case is supported by FilteredDirectoryCopy, but + not by FSpFilteredDirectoryCopy since with FSpFilteredDirectoryCopy, + the dstName parameter can not be NULL. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source directory name, or nil if + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Destination directory name, or nil if + dstDirID specifies the directory. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, DirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + copyFilterProc input: A pointer to the filter routine you want called + for each item in the source directory, or NULL + if you don't want to filter. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, CopyFilterProcPtr, FSpFilteredDirectoryCopy, + DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +pascal OSErr FSpFilteredDirectoryCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc); +/* ¦ Make a copy of a directory structure in a new location with item filtering. + The FSpFilteredDirectoryCopy function makes a copy of a directory + structure in a new location. If copyBufferPtr <> NIL, it points to + a buffer of copyBufferSize that is used to copy files data. The + larger the supplied buffer, the faster the copy. If + copyBufferPtr = NIL, then this routine allocates a buffer in the + application heap. If you pass a copy buffer to this routine, make + its size a multiple of 512 ($200) bytes for optimum performance. + + The optional copyFilterProc parameter lets a routine you define + decide what files or directories are copied to the destination. + + srcSpec input: An FSSpec record specifying the directory to copy. + dstSpec input: An FSSpec record specifying destination directory + of the copy. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FSpDirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + copyFilterProc input: A pointer to the filter routine you want called + for each item in the source directory, or NULL + if you don't want to filter. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, CopyFilterProcPtr, FilteredDirectoryCopy, + DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +pascal OSErr DirectoryCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler); +/* ¦ Make a copy of a directory structure in a new location. + The DirectoryCopy function makes a copy of a directory structure in a + new location. If copyBufferPtr <> NIL, it points to a buffer of + copyBufferSize that is used to copy files data. The larger the + supplied buffer, the faster the copy. If copyBufferPtr = NIL, then this + routine allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + DirectoryCopy normally creates a new directory *in* the specified + destination directory and copies the source directory's content into + the new directory. However, if root parent directory (fsRtParID) + is passed as the dstDirID parameter and NULL is passed as the + dstName parameter, DirectoryCopy renames the destination volume to + the source directory's name (truncating if the name is longer than + 27 characters) and copies the source directory's content into the + destination volume's root directory. This special case is supported + by DirectoryCopy, but not by FSpDirectoryCopy since with + FSpDirectoryCopy, the dstName parameter can not be NULL. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source directory name, or nil if + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Destination directory name, or nil if + dstDirID specifies the directory. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, DirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, FSpDirectoryCopy, FilteredDirectoryCopy, + FSpFilteredDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDirectoryCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler); +/* ¦ Make a copy of a directory structure in a new location. + The FSpDirectoryCopy function makes a copy of a directory structure in a + new location. If copyBufferPtr <> NIL, it points to a buffer of + copyBufferSize that is used to copy files data. The larger the + supplied buffer, the faster the copy. If copyBufferPtr = NIL, then this + routine allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + srcSpec input: An FSSpec record specifying the directory to copy. + dstSpec input: An FSSpec record specifying destination directory + of the copy. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FSpDirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, DirectoryCopy, FilteredDirectoryCopy, + FSpFilteredDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __DIRECTORYCOPY__ */ diff --git a/src/mac/morefile/FSpCompa.cpp b/src/mac/morefile/FSpCompa.cpp new file mode 100644 index 0000000000..b35e20eb01 --- /dev/null +++ b/src/mac/morefile/FSpCompa.cpp @@ -0,0 +1,928 @@ +/* +** Apple Macintosh Developer Technical Support +** +** FSSpec compatibility functions. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: FSpCompat.c +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +/* +** If building application 68K code, set GENERATENODATA to 0 for faster code. +** If building stand-alone 68K code, set GENERATENODATA to 1 so globals +** (static variables) are not used. +*/ +#ifndef GENERATENODATA +#define GENERATENODATA 0 +#endif + +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreExtr.h" +#include "FSpCompa.h" + +/*****************************************************************************/ + +/* local constants */ + +enum { + gestaltBugFixAttrsTwo = 'bugy', + gestaltFSpExchangeFilesCompatibilityFix = 26, + gestaltBugFixAttrsThree = 'bugx', + gestaltFSpCreateScriptSupportFix = 1 +}; + +/*****************************************************************************/ + +/* static prototypes */ + + +#if !__MACOSSEVENORLATER +static Boolean FSHasFSSpecCalls(void); + +static Boolean QTHasFSSpecCalls(void); +#endif /* !__MACOSSEVENORLATER */ + +#if !__MACOSSEVENFIVEORLATER +static Boolean HasFSpExchangeFilesCompatibilityFix(void); + +static OSErr GenerateUniqueName(short volume, + long *startSeed, + long dir1, + long dir2, + StringPtr uniqueName); +#endif /* !__MACOSSEVENFIVEORLATER */ + +#if !__MACOSSEVENFIVEONEORLATER +static Boolean HasFSpCreateScriptSupportFix(void); +#endif /* !__MACOSSEVENFIVEONEORLATER */ + +/*****************************************************************************/ + +/* FSHasFSSpecCalls returns true if the file system provides FSSpec calls. */ + +#if !__MACOSSEVENORLATER +static Boolean FSHasFSSpecCalls(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else + Boolean result = false; +#endif + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif + if ( Gestalt(gestaltFSAttr, &response) == noErr ) + { + result = ((response & (1L << gestaltHasFSSpecCalls)) != 0); + } +#if !GENERATENODATA + } +#endif + return ( result ); +} +#endif /* !__MACOSSEVENORLATER */ + +/*****************************************************************************/ + +/* QTHasFSSpecCalls returns true if QuickTime provides FSSpec calls */ +/* except for FSpExchangeFiles. */ + +#if !__MACOSSEVENORLATER +static Boolean QTHasFSSpecCalls(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else + Boolean result = false; +#endif + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif + result = (Gestalt(gestaltQuickTimeVersion, &response) == noErr); +#if !GENERATENODATA + } +#endif + return ( result ); +} +#endif /* !__MACOSSEVENORLATER */ + +/*****************************************************************************/ + +/* HasFSpExchangeFilesCompatibilityFix returns true if FSpExchangeFiles */ +/* compatibility code has been fixed in system software. */ +/* This was fixed by System Update 3.0, so if SystemSevenFiveOrLater */ +/* is true, then we know the fix is in. */ + +#if !__MACOSSEVENFIVEORLATER +static Boolean HasFSpExchangeFilesCompatibilityFix(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else /* !GENERATENODATA */ + Boolean result = false; +#endif /* !GENERATENODATA */ + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif /* !GENERATENODATA */ + if ( Gestalt(gestaltBugFixAttrsTwo, &response) == noErr ) + { + result = ((response & (1L << gestaltFSpExchangeFilesCompatibilityFix)) != 0); + } +#if !GENERATENODATA + } +#endif /* !GENERATENODATA */ + return ( result ); +} +#endif /* !__MACOSSEVENFIVEORLATER */ + +/*****************************************************************************/ + +/* HasFSpCreateScriptSupportFix returns true if FSpCreate and */ +/* FSpCreateResFile have been fixed in system software to correctly set */ +/* the scriptCode in the volume's catalog. */ +/* This was fixed by System 7.5 Update 1.0 */ + +#if !__MACOSSEVENFIVEONEORLATER +static Boolean HasFSpCreateScriptSupportFix(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else + Boolean result = false; +#endif /* !GENERATENODATA */ + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif /* !GENERATENODATA */ + if ( Gestalt(gestaltBugFixAttrsThree, &response) == noErr ) + { + result = ((response & (1L << gestaltFSpCreateScriptSupportFix)) != 0); + } +#if !GENERATENODATA + } +#endif /* !GENERATENODATA */ + return ( result ); +} +#endif /* !__MACOSSEVENFIVEONEORLATER */ + +/*****************************************************************************/ + +/* +** File Manager FSp calls +*/ + +/*****************************************************************************/ + +pascal OSErr FSMakeFSSpecCompat(short vRefNum, + long dirID, + ConstStr255Param fileName, + FSSpec *spec) +{ + OSErr result; + +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + Boolean isDirectory; + + result = GetObjectLocation(vRefNum, dirID, fileName, + &(spec->vRefNum), &(spec->parID), spec->name, + &isDirectory); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + /* Let the file system create the FSSpec if it can since it does the job */ + /* much more efficiently than I can. */ + result = FSMakeFSSpec(vRefNum, dirID, fileName, spec); + + /* Fix a bug in Macintosh PC Exchange's MakeFSSpec code where 0 is */ + /* returned in the parID field when making an FSSpec to the volume's */ + /* root directory by passing a full pathname in MakeFSSpec's */ + /* fileName parameter. Fixed in Mac OS 8.1 */ + if ( (result == noErr) && (spec->parID == 0) ) + spec->parID = fsRtParID; + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenDFCompat(const FSSpec *spec, + char permission, + short *refNum) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + pb.ioParam.ioPermssn = permission; + pb.ioParam.ioMisc = NULL; + result = PBHOpenSync(&pb); /* OpenDF not supported by System 6, so use Open */ + *refNum = pb.ioParam.ioRefNum; + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpOpenDF(spec, permission, refNum) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenRFCompat(const FSSpec *spec, + char permission, + short *refNum) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + pb.ioParam.ioPermssn = permission; + pb.ioParam.ioMisc = NULL; + result = PBHOpenRFSync(&pb); + *refNum = pb.ioParam.ioRefNum; + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpOpenRF(spec, permission, refNum) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpCreateCompat(const FSSpec *spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag) +{ +#if !__MACOSSEVENFIVEONEORLATER + OSErr result; + UniversalFMPB pb; + + + if ( +#if !__MACOSSEVENORLATER + (!FSHasFSSpecCalls() && !QTHasFSSpecCalls()) || +#endif /* !__MACOSSEVENORLATER */ + !HasFSpCreateScriptSupportFix() ) + { + /* If FSpCreate isn't called, this code will be executed */ + pb.hPB.fileParam.ioVRefNum = spec->vRefNum; + pb.hPB.fileParam.ioDirID = spec->parID; + pb.hPB.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.hPB.fileParam.ioFVersNum = 0; + result = PBHCreateSync(&(pb.hPB)); + if ( result == noErr ) + { + /* get info on created item */ + pb.ciPB.hFileInfo.ioFDirIndex = 0; + result = PBGetCatInfoSync(&(pb.ciPB)); + if ( result == noErr ) + { + /* Set fdScript in FXInfo */ + /* The negative script constants (smSystemScript, smCurrentScript, and smAllScripts) */ + /* don't make sense on disk, so only use scriptTag if scriptTag >= smRoman */ + /* (smRoman is 0). fdScript is valid if high bit is set (see IM-6, page 9-38) */ + pb.ciPB.hFileInfo.ioFlXFndrInfo.fdScript = (scriptTag >= smRoman) ? + ((char)scriptTag | (char)0x80) : + (smRoman); + /* Set creator/fileType */ + pb.ciPB.hFileInfo.ioFlFndrInfo.fdCreator = creator; + pb.ciPB.hFileInfo.ioFlFndrInfo.fdType = fileType; + /* Restore ioDirID field in pb which was changed by PBGetCatInfo */ + pb.ciPB.hFileInfo.ioDirID = spec->parID; + result = PBSetCatInfoSync(&(pb.ciPB)); + } + } + return ( result ); + } + else +#endif /* !__MACOSSEVENFIVEONEORLATER */ + { + return ( FSpCreate(spec, creator, fileType, scriptTag) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpDirCreateCompat(const FSSpec *spec, + ScriptCode scriptTag, + long *createdDirID) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + UniversalFMPB pb; + + pb.hPB.fileParam.ioVRefNum = spec->vRefNum; + pb.hPB.fileParam.ioDirID = spec->parID; + pb.hPB.fileParam.ioNamePtr = (StringPtr) &(spec->name); + result = PBDirCreateSync(&(pb.hPB)); + *createdDirID = pb.hPB.fileParam.ioDirID; + if ( result == noErr ) + { + /* get info on created item */ + pb.ciPB.dirInfo.ioFDirIndex = 0; + pb.ciPB.dirInfo.ioDrDirID = spec->parID; + result = PBGetCatInfoSync(&(pb.ciPB)); + if ( result == noErr ) + { + /* Set frScript in DXInfo */ + /* The negative script constants (smSystemScript, smCurrentScript, and smAllScripts) */ + /* don't make sense on disk, so only use scriptTag if scriptTag >= smRoman */ + /* (smRoman is 0). frScript is valid if high bit is set (see IM-6, page 9-38) */ + pb.ciPB.dirInfo.ioDrFndrInfo.frScript = (scriptTag >= smRoman) ? + ((char)scriptTag | (char)0x80) : + (smRoman); + /* Restore ioDirID field in pb which was changed by PBGetCatInfo */ + pb.ciPB.dirInfo.ioDrDirID = spec->parID; + result = PBSetCatInfoSync(&(pb.ciPB)); + } + } + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpDirCreate(spec, scriptTag, createdDirID) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpDeleteCompat(const FSSpec *spec) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + return ( PBHDeleteSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpDelete(spec) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFInfoCompat(const FSSpec *spec, + FInfo *fndrInfo) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioFDirIndex = 0; + result = PBHGetFInfoSync(&pb); + *fndrInfo = pb.fileParam.ioFlFndrInfo; + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpGetFInfo(spec, fndrInfo) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpSetFInfoCompat(const FSSpec *spec, + const FInfo *fndrInfo) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioFDirIndex = 0; + result = PBHGetFInfoSync(&pb); + if ( result == noErr ) + { + pb.fileParam.ioFlFndrInfo = *fndrInfo; + pb.fileParam.ioDirID = spec->parID; + result = PBHSetFInfoSync(&pb); + } + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpSetFInfo(spec, fndrInfo) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpSetFLockCompat(const FSSpec *spec) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + return ( PBHSetFLockSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpSetFLock(spec) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpRstFLockCompat(const FSSpec *spec) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + return ( PBHRstFLockSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpRstFLock(spec) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpRenameCompat(const FSSpec *spec, + ConstStr255Param newName) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + pb.ioParam.ioMisc = (Ptr) newName; + return ( PBHRenameSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpRename(spec, newName) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpCatMoveCompat(const FSSpec *source, + const FSSpec *dest) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + CMovePBRec pb; + + /* source and destination volume must be the same */ + if ( source->vRefNum != dest->vRefNum ) + return ( paramErr ); + + pb.ioNamePtr = (StringPtr) &(source->name); + pb.ioVRefNum = source->vRefNum; + pb.ioDirID = source->parID; + pb.ioNewDirID = dest->parID; + pb.ioNewName = (StringPtr) &(dest->name); + return ( PBCatMoveSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpCatMove(source, dest) ); + } +} + +/*****************************************************************************/ + +/* GenerateUniqueName generates a name that is unique in both dir1 and dir2 */ +/* on the specified volume. Ripped off from Feldman's code. */ + +#if !__MACOSSEVENFIVEORLATER +static OSErr GenerateUniqueName(short volume, + long *startSeed, + long dir1, + long dir2, + StringPtr uniqueName) +{ + OSErr error = noErr; + long i; + CInfoPBRec cinfo; + unsigned char hexStr[16]; + + for ( i = 0; i < 16; ++i ) + { + if ( i < 10 ) + { + hexStr[i] = 0x30 + i; + } + else + { + hexStr[i] = 0x37 + i; + } + } + + cinfo.hFileInfo.ioVRefNum = volume; + cinfo.hFileInfo.ioFDirIndex = 0; + cinfo.hFileInfo.ioNamePtr = uniqueName; + + while ( error != fnfErr ) + { + (*startSeed)++; + cinfo.hFileInfo.ioNamePtr[0] = 8; + for ( i = 1; i <= 8; i++ ) + { + cinfo.hFileInfo.ioNamePtr[i] = hexStr[((*startSeed >> ((8-i)*4)) & 0xf)]; + } + cinfo.hFileInfo.ioDirID = dir1; + error = fnfErr; + for ( i = 1; i <= 2; i++ ) + { + error = error & PBGetCatInfoSync(&cinfo); + cinfo.hFileInfo.ioDirID = dir2; + if ( (error != fnfErr) && (error != noErr) ) + { + return ( error ); + } + } + } + return ( noErr ); +} +#endif /* !__MACOSSEVENFIVEORLATER */ + +/*****************************************************************************/ + +pascal OSErr FSpExchangeFilesCompat(const FSSpec *source, + const FSSpec *dest) +{ +#if !__MACOSSEVENFIVEORLATER + if ( +#if !__MACOSSEVENORLATER + !FSHasFSSpecCalls() || +#endif /* !__MACOSSEVENORLATER */ + !HasFSpExchangeFilesCompatibilityFix() ) + { + HParamBlockRec pb; + CInfoPBRec catInfoSource, catInfoDest; + OSErr result, result2; + Str31 unique1, unique2; + StringPtr unique1Ptr, unique2Ptr, swapola; + GetVolParmsInfoBuffer volInfo; + long theSeed, temp; + + /* Make sure the source and destination are on the same volume */ + if ( source->vRefNum != dest->vRefNum ) + { + result = diffVolErr; + goto errorExit3; + } + + /* Try PBExchangeFiles first since it preserves the file ID reference */ + pb.fidParam.ioNamePtr = (StringPtr) &(source->name); + pb.fidParam.ioVRefNum = source->vRefNum; + pb.fidParam.ioDestNamePtr = (StringPtr) &(dest->name); + pb.fidParam.ioDestDirID = dest->parID; + pb.fidParam.ioSrcDirID = source->parID; + + result = PBExchangeFilesSync(&pb); + + /* Note: The compatibility case won't work for files with *Btree control blocks. */ + /* Right now the only *Btree files are created by the system. */ + if ( result != noErr ) + { + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioBuffer = (Ptr) &volInfo; + pb.ioParam.ioReqCount = sizeof(volInfo); + result2 = PBHGetVolParmsSync(&pb); + + /* continue if volume has no fileID support (or no GetVolParms support) */ + if ( (result2 == noErr) && hasFileIDs(volInfo) ) + { + goto errorExit3; + } + + /* Get the catalog information for each file */ + /* and make sure both files are *really* files */ + catInfoSource.hFileInfo.ioVRefNum = source->vRefNum; + catInfoSource.hFileInfo.ioFDirIndex = 0; + catInfoSource.hFileInfo.ioNamePtr = (StringPtr) &(source->name); + catInfoSource.hFileInfo.ioDirID = source->parID; + catInfoSource.hFileInfo.ioACUser = 0; /* ioACUser used to be filler2 */ + result = PBGetCatInfoSync(&catInfoSource); + if ( result != noErr ) + { + goto errorExit3; + } + if ( (catInfoSource.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + result = notAFileErr; + goto errorExit3; + } + + catInfoDest.hFileInfo.ioVRefNum = dest->vRefNum; + catInfoDest.hFileInfo.ioFDirIndex = 0; + catInfoDest.hFileInfo.ioNamePtr = (StringPtr) &(dest->name); + catInfoDest.hFileInfo.ioDirID = dest->parID; + catInfoDest.hFileInfo.ioACUser = 0; /* ioACUser used to be filler2 */ + result = PBGetCatInfoSync(&catInfoDest); + if ( result != noErr ) + { + goto errorExit3; + } + if ( (catInfoDest.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + result = notAFileErr; + goto errorExit3; + } + + /* generate 2 filenames that are unique in both directories */ + theSeed = 0x64666A6C; /* a fine unlikely filename */ + unique1Ptr = (StringPtr)&unique1; + unique2Ptr = (StringPtr)&unique2; + + result = GenerateUniqueName(source->vRefNum, &theSeed, source->parID, dest->parID, unique1Ptr); + if ( result != noErr ) + { + goto errorExit3; + } + + GenerateUniqueName(source->vRefNum, &theSeed, source->parID, dest->parID, unique2Ptr); + if ( result != noErr ) + { + goto errorExit3; + } + + /* rename source to unique1 */ + pb.fileParam.ioNamePtr = (StringPtr) &(source->name); + pb.ioParam.ioMisc = (Ptr) unique1Ptr; + pb.ioParam.ioVersNum = 0; + result = PBHRenameSync(&pb); + if ( result != noErr ) + { + goto errorExit3; + } + + /* rename dest to unique2 */ + pb.ioParam.ioMisc = (Ptr) unique2Ptr; + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr) &(dest->name); + pb.fileParam.ioDirID = dest->parID; + result = PBHRenameSync(&pb); + if ( result != noErr ) + { + goto errorExit2; /* back out gracefully by renaming unique1 back to source */ + } + + /* If files are not in same directory, swap their locations */ + if ( source->parID != dest->parID ) + { + /* move source file to dest directory */ + pb.copyParam.ioNamePtr = unique1Ptr; + pb.copyParam.ioNewName = NULL; + pb.copyParam.ioNewDirID = dest->parID; + pb.copyParam.ioDirID = source->parID; + result = PBCatMoveSync((CMovePBPtr) &pb); + if ( result != noErr ) + { + goto errorExit1; /* back out gracefully by renaming both files to original names */ + } + + /* move dest file to source directory */ + pb.copyParam.ioNamePtr = unique2Ptr; + pb.copyParam.ioNewDirID = source->parID; + pb.copyParam.ioDirID = dest->parID; + result = PBCatMoveSync((CMovePBPtr) &pb); + if ( result != noErr) + { + /* life is very bad. We'll at least try to move source back */ + pb.copyParam.ioNamePtr = unique1Ptr; + pb.copyParam.ioNewName = NULL; + pb.copyParam.ioNewDirID = source->parID; + pb.copyParam.ioDirID = dest->parID; + (void) PBCatMoveSync((CMovePBPtr) &pb); /* ignore errors */ + goto errorExit1; /* back out gracefully by renaming both files to original names */ + } + } + + /* Make unique1Ptr point to file in source->parID */ + /* and unique2Ptr point to file in dest->parID */ + /* This lets us fall through to the rename code below */ + swapola = unique1Ptr; + unique1Ptr = unique2Ptr; + unique2Ptr = swapola; + + /* At this point, the files are in their new locations (if they were moved) */ + /* Source is named Unique1 (name pointed to by unique2Ptr) and is in dest->parID */ + /* Dest is named Unique2 (name pointed to by unique1Ptr) and is in source->parID */ + /* Need to swap attributes except mod date and swap names */ + + /* swap the catalog info by re-aiming the CInfoPB's */ + catInfoSource.hFileInfo.ioNamePtr = unique1Ptr; + catInfoDest.hFileInfo.ioNamePtr = unique2Ptr; + + catInfoSource.hFileInfo.ioDirID = source->parID; + catInfoDest.hFileInfo.ioDirID = dest->parID; + + /* Swap the original mod dates with each file */ + temp = catInfoSource.hFileInfo.ioFlMdDat; + catInfoSource.hFileInfo.ioFlMdDat = catInfoDest.hFileInfo.ioFlMdDat; + catInfoDest.hFileInfo.ioFlMdDat = temp; + + /* Here's the swap (ignore errors) */ + (void) PBSetCatInfoSync(&catInfoSource); + (void) PBSetCatInfoSync(&catInfoDest); + + /* rename unique2 back to dest */ +errorExit1: + pb.ioParam.ioMisc = (Ptr) &(dest->name); + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioNamePtr = unique2Ptr; + pb.fileParam.ioDirID = dest->parID; + (void) PBHRenameSync(&pb); /* ignore errors */ + + /* rename unique1 back to source */ +errorExit2: + pb.ioParam.ioMisc = (Ptr) &(source->name); + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioNamePtr = unique1Ptr; + pb.fileParam.ioDirID = source->parID; + (void) PBHRenameSync(&pb); /* ignore errors */ + } +errorExit3: { /* null statement */ } + return ( result ); + } + else +#endif /* !__MACOSSEVENFIVEORLATER */ + { + return ( FSpExchangeFiles(source, dest) ); + } +} + +/*****************************************************************************/ + +/* +** Resource Manager FSp calls +*/ + +/*****************************************************************************/ + +pascal short FSpOpenResFileCompat(const FSSpec *spec, + SignedByte permission) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + return ( HOpenResFile(spec->vRefNum, spec->parID, spec->name, permission) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpOpenResFile(spec, permission) ); + } +} + +/*****************************************************************************/ + +pascal void FSpCreateResFileCompat(const FSSpec *spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag) +{ +#if !__MACOSSEVENFIVEONEORLATER + if ( +#if !__MACOSSEVENORLATER + (!FSHasFSSpecCalls() && !QTHasFSSpecCalls()) || +#endif /* !__MACOSSEVENORLATER */ + !HasFSpCreateScriptSupportFix() ) + { + OSErr result; + CInfoPBRec pb; + + HCreateResFile(spec->vRefNum, spec->parID, spec->name); + if ( ResError() == noErr ) + { + /* get info on created item */ + pb.hFileInfo.ioVRefNum = spec->vRefNum; + pb.hFileInfo.ioDirID = spec->parID; + pb.hFileInfo.ioNamePtr = (StringPtr) &(spec->name); + pb.hFileInfo.ioFDirIndex = 0; + result = PBGetCatInfoSync(&pb); + if ( result == noErr ) + { + /* Set fdScript in FXInfo */ + /* The negative script constants (smSystemScript, smCurrentScript, and smAllScripts) */ + /* don't make sense on disk, so only use scriptTag if scriptTag >= smRoman */ + /* (smRoman is 0). fdScript is valid if high bit is set (see IM-6, page 9-38) */ + pb.hFileInfo.ioFlXFndrInfo.fdScript = (scriptTag >= smRoman) ? + ((char)scriptTag | (char)0x80) : + (smRoman); + /* Set creator/fileType */ + pb.hFileInfo.ioFlFndrInfo.fdCreator = creator; + pb.hFileInfo.ioFlFndrInfo.fdType = fileType; + + /* Restore ioDirID field in pb which was changed by PBGetCatInfo */ + pb.hFileInfo.ioDirID = spec->parID; + result = PBSetCatInfoSync(&pb); + } + /* Set ResErr low memory global to result */ + LMSetResErr(result); + } + return; + } + else +#endif /* !__MACOSSEVENFIVEONEORLATER */ + { + FSpCreateResFile(spec, creator, fileType, scriptTag); + return; + } +} + +/*****************************************************************************/ diff --git a/src/mac/morefile/FSpCompa.h b/src/mac/morefile/FSpCompa.h new file mode 100644 index 0000000000..1dbc11f22b --- /dev/null +++ b/src/mac/morefile/FSpCompa.h @@ -0,0 +1,488 @@ +/* +** Apple Macintosh Developer Technical Support +** +** FSSpec compatibility functions. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: FSpCompat.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __FSPCOMPAT__ +#define __FSPCOMPAT__ + +#include +#include + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +pascal OSErr FSMakeFSSpecCompat(short vRefNum, + long dirID, + ConstStr255Param fileName, + FSSpec *spec); +/* ¦ Initialize a FSSpec record. + The FSMakeFSSpecCompat function fills in the fields of an FSSpec record. + If the file system can't create the FSSpec, then the compatibility code + creates a FSSpec that is exactly like an FSSpec except that spec.name + for a file may not have the same capitalization as the file's catalog + entry on the disk volume. That is because fileName is parsed to get the + name instead of getting the name back from the file system. This works + fine with System 6 where FSMakeSpec isn't available. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + spec output: A file system specification to be filled in by + FSMakeFSSpecCompat. + + Result Codes + noErr 0 No error + nsvErr -35 Volume doesnÕt exist + fnfErr -43 File or directory does not exist + (FSSpec is still valid) +*/ + +/*****************************************************************************/ + +pascal OSErr FSpOpenDFCompat(const FSSpec *spec, + char permission, + short *refNum); +/* ¦ Open a file's data fork. + The FSpOpenDFCompat function opens the data fork of the file specified + by spec. + Differences from FSpOpenDF: If FSpOpenDF isn't available, + FSpOpenDFCompat uses PHBOpen because System 6 doesn't support PBHOpenDF. + This means FSpOpenDFCompat could accidentally open a driver if the + spec->name begins with a period. + + spec input: An FSSpec record specifying the file whose data + fork is to be opened. + permission input: A constant indicating the desired file access + permissions. + refNum output: A reference number of an access path to the file's + data fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 File not found + opWrErr -49 File already open for writing + permErr -54 Attempt to open locked file for writing + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSpOpenAware +*/ + +/*****************************************************************************/ + +pascal OSErr FSpOpenRFCompat(const FSSpec *spec, + char permission, + short *refNum); +/* ¦ Open a file's resource fork. + The FSpOpenRFCompat function opens the resource fork of the file + specified by spec. + + spec input: An FSSpec record specifying the file whose resource + fork is to be opened. + permission input: A constant indicating the desired file access + permissions. + refNum output: A reference number of an access path to the file's + resource fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 File not found + opWrErr -49 File already open for writing + permErr -54 Attempt to open locked file for writing + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSpOpenRFAware +*/ + + +/*****************************************************************************/ + +pascal OSErr FSpCreateCompat(const FSSpec *spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag); +/* ¦ Create a new file. + The FSpCreateCompat function creates a new file with the specified + type, creator, and script code. + Differences from FSpCreate: FSpCreateCompat correctly sets the + fdScript in the file's FXInfo record to scriptTag if the problem + isn't fixed in the File Manager code. + + spec input: An FSSpec record specifying the file to create. + creator input: The creator of the new file. + fileType input The file type of the new file. + scriptCode input: The code of the script system in which the file + name is to be displayed. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 A directory exists with that name +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDirCreateCompat(const FSSpec *spec, + ScriptCode scriptTag, + long *createdDirID); +/* ¦ Create a new directory. + The FSpDirCreateCompat function creates a new directory and returns the + directory ID of the newDirectory. + + spec input: An FSSpec record specifying the directory to + create. + scriptCode input: The code of the script system in which the + directory name is to be displayed. + createdDirID output: The directory ID of the directory that was + created. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Not an HFS volume + afpAccessDenied -5000 User does not have the correct access +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDeleteCompat(const FSSpec *spec); +/* ¦ Delete a file or directory. + The FSpDeleteCompat function deletes a file or directory. + + spec input: An FSSpec record specifying the file or + directory to delete. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + fBsyErr -47 File busy, directory not empty, or + working directory control block open + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetFInfoCompat(const FSSpec *spec, + FInfo *fndrInfo); +/* ¦ Get the finder information for a file. + The FSpGetFInfoCompat function gets the finder information for a file. + + spec input: An FSSpec record specifying the file. + fndrInfo output: If the object is a file, then its FInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpGetDInfo +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetFInfoCompat(const FSSpec *spec, + const FInfo *fndrInfo); +/* ¦ Set the finder information for a file. + The FSpSetFInfoCompat function sets the finder information for a file. + + spec input: An FSSpec record specifying the file. + fndrInfo input: The FInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Object was a directory + + __________ + + Also see: FSpSetDInfo +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetFLockCompat(const FSSpec *spec); +/* ¦ Lock a file. + The FSpSetFLockCompat function locks a file. + + spec input: An FSSpec record specifying the file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + afpObjectTypeErr -5025 Folder locking not supported by volume +*/ + +/*****************************************************************************/ + +pascal OSErr FSpRstFLockCompat(const FSSpec *spec); +/* ¦ Unlock a file. + The FSpRstFLockCompat function unlocks a file. + + spec input: An FSSpec record specifying the file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + afpObjectTypeErr -5025 Folder locking not supported by volume +*/ + +/*****************************************************************************/ + +pascal OSErr FSpRenameCompat(const FSSpec *spec, + ConstStr255Param newName); +/* ¦ Rename a file or directory. + The FSpRenameCompat function renames a file or directory. + + spec input: An FSSpec record specifying the file. + newName input: The new name of the file or directory. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + paramErr -50 No default volume + fsRnErr -59 Problem during rename + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +pascal OSErr FSpCatMoveCompat(const FSSpec *source, + const FSSpec *dest); +/* ¦ Move a file or directory to a different location on on the same volume. + The FSpCatMoveCompat function moves a file or directory to a different + location on on the same volume. + + source input: An FSSpec record specifying the file or directory. + dest input: An FSSpec record specifying the name and location + of the directory into which the source file or + directory is to be moved. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename or attempt to move into + a file + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 Target directory is locked + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + paramErr -50 No default volume + badMovErr -122 Attempt to move into offspring + wrgVolTypErr -123 Not an HFS volume + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +pascal OSErr FSpExchangeFilesCompat(const FSSpec *source, + const FSSpec *dest); +/* ¦ Exchange the data stored in two files on the same volume. + The FSpExchangeFilesCompat function swaps the data in two files by + changing the information in the volume's catalog and, if the files + are open, in the file control blocks. + Differences from FSpExchangeFiles: Correctly exchanges files on volumes + that don't support PBExchangeFiles. FSpExchangeFiles attempts to support + volumes that don't support PBExchangeFiles, but in System 7, 7.0.1, 7.1, + and 7 Pro, the compatibility code just doesn't work on volumes that + don't support PBExchangeFiles (even though you may get a noErr result). + System Update 3.0 and System 7.5 and later have the problems in + FSpExchangeFiles corrected. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + wrgVolTypErr -123 Not an HFS volume + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Object is a directory, not a file + afpSameObjectErr -5038 Source and destination files are the same +*/ + +/*****************************************************************************/ + +pascal short FSpOpenResFileCompat(const FSSpec *spec, + SignedByte permission); +/* ¦ Open a file's resource file. + The FSpOpenResFileCompat function opens the resource file specified + by spec. + + spec input: An FSSpec record specifying the file whose + resource file is to be opened. + permission input: A constant indicating the desired file access + permissions. + function result output: A resource file reference number, or if there's + an error -1. + + Result Codes + noErr 0 No error + nsvErr Ð35 No such volume + ioErr Ð36 I/O error + bdNamErr Ð37 Bad filename or volume name (perhaps zero + length) + eofErr Ð39 End of file + tmfoErr Ð42 Too many files open + fnfErr Ð43 File not found + opWrErr Ð49 File already open with write permission + permErr Ð54 Permissions error (on file open) + extFSErr Ð58 Volume belongs to an external file system + memFullErr Ð108 Not enough room in heap zone + dirNFErr Ð120 Directory not found + mapReadErr Ð199 Map inconsistent with operation +*/ + +/*****************************************************************************/ + +pascal void FSpCreateResFileCompat(const FSSpec *spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag); +/* ¦ Create a resource file. + The FSpCreateResFileCompat function creates a new resource file with + the specified type, creator, and script code. + Differences from FSpCreateResFile: FSpCreateResFileCompat correctly + sets the fdScript in the file's FXInfo record to scriptTag if the + problem isn't fixed in the File Manager code. + + spec input: An FSSpec record specifying the resource file to create. + creator input: The creator of the new file. + fileType input The file type of the new file. + scriptCode input: The code of the script system in which the file + name is to be displayed. + + Result Codes + noErr 0 No error + dirFulErr Ð33 Directory full + dskFulErr Ð34 Disk full + nsvErr Ð35 No such volume + ioErr Ð36 I/O error + bdNamErr Ð37 Bad filename or volume name (perhaps zero + length) + tmfoErr Ð42 Too many files open + wPrErrw Ð44 Disk is write-protected + fLckdErr Ð45 File is locked +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __FSPCOMPAT__ */ + diff --git a/src/mac/morefile/FileCopy.cpp b/src/mac/morefile/FileCopy.cpp new file mode 100644 index 0000000000..8843c9b988 --- /dev/null +++ b/src/mac/morefile/FileCopy.cpp @@ -0,0 +1,593 @@ +/* +** Apple Macintosh Developer Technical Support +** +** FileCopy: A robust, general purpose file copy routine. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: FileCopy.c +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFile.h" +#include "MoreExtr.h" +#include "MoreDesk.h" +#include "FileCopy.h" + +/*****************************************************************************/ + +/* local constants */ + +/* The deny-mode privileges to use when opening the source and destination files. */ + +enum +{ + srcCopyMode = dmRdDenyWr, + dstCopyMode = dmWrDenyRdWr +}; + +/* The largest (16K) and smallest (.5K) copy buffer to use if the caller doesn't supply +** their own copy buffer. */ + +enum +{ + bigCopyBuffSize = 0x00004000, + minCopyBuffSize = 0x00000200 +}; + +/*****************************************************************************/ + +/* static prototypes */ + +static OSErr GetDestinationDirInfo(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory, + Boolean *isDropBox); +/* GetDestinationDirInfo tells us if the destination is a directory, it's + directory ID, and if it's an AppleShare drop box (write privileges only -- + no read or search privileges). + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + theDirID output: If the object is a file, then its parent directory + ID. If the object is a directory, then its ID. + isDirectory output: True if object is a directory; false if + object is a file. + isDropBox output: True if directory is an AppleShare drop box. +*/ + +static OSErr CheckForForks(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean *hasDataFork, + Boolean *hasResourceFork); +/* CheckForForks tells us if there is a data or resource fork to copy. + vRefNum input: Volume specification of the file's current + location. + dirID input: Directory ID of the file's current location. + name input: The name of the file. +*/ + +static OSErr PreflightFileCopySpace(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + ConstStr255Param dstVolName, + short dstVRefNum, + Boolean *spaceOK); +/* PreflightFileCopySpace determines if there's enough space on a + volume to copy the specified file to that volume. + Note: The results of this routine are not perfect. For example if the + volume's catalog or extents overflow file grows when the new file is + created, more allocation blocks may be needed beyond those needed for + the file's data and resource forks. + + srcVRefNum input: Volume specification of the file's current + location. + srcDirID input: Directory ID of the file's current location. + srcName input: The name of the file. + dstVolName input: A pointer to the name of the volume where + the file will be copied or NULL. + dstVRefNum input: Volume specification indicating the volume + where the file will be copied. + spaceOK output: true if there's enough space on the volume for + the file's data and resource forks. +*/ + +/*****************************************************************************/ + +static OSErr GetDestinationDirInfo(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory, + Boolean *isDropBox) +{ + CInfoPBRec pb; + OSErr error; + + pb.dirInfo.ioACUser = 0; /* ioACUser used to be filler2, clear it before calling GetCatInfo */ + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + *theDirID = pb.dirInfo.ioDrDirID; + *isDirectory = (pb.dirInfo.ioFlAttrib & ioDirMask) != 0; + /* see if access priviledges are make changes, not see folder, and not see files (drop box) */ + *isDropBox = ((pb.dirInfo.ioACUser & 0x07) == 0x03); + + return ( error ); +} + +/*****************************************************************************/ + +static OSErr CheckForForks(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean *hasDataFork, + Boolean *hasResourceFork) +{ + HParamBlockRec pb; + OSErr error; + + pb.fileParam.ioNamePtr = (StringPtr)name; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioDirID = dirID; + pb.fileParam.ioFDirIndex = 0; + error = PBHGetFInfoSync(&pb); + *hasDataFork = (pb.fileParam.ioFlLgLen != 0); + *hasResourceFork = (pb.fileParam.ioFlRLgLen != 0); + + return ( error ); +} + +/*****************************************************************************/ + +static OSErr PreflightFileCopySpace(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + ConstStr255Param dstVolName, + short dstVRefNum, + Boolean *spaceOK) +{ + UniversalFMPB pb; + OSErr error; + unsigned long dstFreeBlocks; + unsigned long dstBlksPerAllocBlk; + unsigned long srcDataBlks; + unsigned long srcResourceBlks; + + error = XGetVolumeInfoNoName(dstVolName, dstVRefNum, &pb.xPB); + if ( error == noErr ) + { + /* get allocation block size (always multiple of 512) and divide by 512 + to get number of 512-byte blocks per allocation block */ + dstBlksPerAllocBlk = ((unsigned long)pb.xPB.ioVAlBlkSiz >> 9); + + /* Convert freeBytes to free disk blocks (512-byte blocks) */ + dstFreeBlocks = (pb.xPB.ioVFreeBytes.hi << 23) + (pb.xPB.ioVFreeBytes.lo >> 9); + + /* Now, get the size of the file's data resource forks */ + pb.hPB.fileParam.ioNamePtr = (StringPtr)srcName; + pb.hPB.fileParam.ioVRefNum = srcVRefNum; + pb.hPB.fileParam.ioFVersNum = 0; + pb.hPB.fileParam.ioDirID = srcDirID; + pb.hPB.fileParam.ioFDirIndex = 0; + error = PBHGetFInfoSync(&pb.hPB); + if ( error == noErr ) + { + /* Since space on Mac OS disks is always allocated in allocation blocks, */ + /* this code takes into account rounding up to the end of an allocation block. */ + + /* get number of 512-byte blocks needed for data fork */ + if ( ((unsigned long)pb.hPB.fileParam.ioFlLgLen & 0x000001ff) != 0 ) + { + srcDataBlks = ((unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9) + 1; + } + else + { + srcDataBlks = (unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9; + } + + /* now, calculate number of new allocation blocks needed */ + if ( srcDataBlks % dstBlksPerAllocBlk ) + { + srcDataBlks = (srcDataBlks / dstBlksPerAllocBlk) + 1; + } + else + { + srcDataBlks /= dstBlksPerAllocBlk; + } + + /* get number of 512-byte blocks needed for resource fork */ + if ( ((unsigned long)pb.hPB.fileParam.ioFlRLgLen & 0x000001ff) != 0 ) + { + srcResourceBlks = ((unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9) + 1; + } + else + { + srcResourceBlks = (unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9; + } + + /* now, calculate number of new allocation blocks needed */ + if ( srcResourceBlks % dstBlksPerAllocBlk ) + { + srcResourceBlks = (srcResourceBlks / dstBlksPerAllocBlk) + 1; + } + else + { + srcResourceBlks /= dstBlksPerAllocBlk; + } + + /* Is there enough room on the destination volume for the source file? */ + *spaceOK = ( ((srcDataBlks + srcResourceBlks) * dstBlksPerAllocBlk) <= dstFreeBlocks ); + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FileCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight) +{ + OSErr err; + + short srcRefNum = 0, /* 0 when source data and resource fork are closed */ + dstDataRefNum = 0, /* 0 when destination data fork is closed */ + dstRsrcRefNum = 0; /* 0 when destination resource fork is closed */ + + Str63 dstName; /* The filename of the destination. It might be the + ** source filename, it might be a new name... */ + + GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */ + long srcServerAdr; /* AppleTalk server address of source (if any) */ + + Boolean dstCreated = false, /* true when destination file has been created */ + ourCopyBuffer = false, /* true if we had to allocate the copy buffer */ + isDirectory, /* true if destination is really a directory */ + isDropBox; /* true if destination is an AppleShare drop box */ + + long tempLong; + short tempInt; + + Boolean spaceOK; /* true if there's enough room to copy the file to the destination volume */ + + Boolean hasDataFork; + Boolean hasResourceFork; + + /* Preflight for size */ + if ( preflight ) + { + err = PreflightFileCopySpace(srcVRefNum, srcDirID, srcName, + dstPathname, dstVRefNum, &spaceOK); + if ( err != noErr ) + { + return ( err ); + } + + if ( !spaceOK ) + { + return ( dskFulErr ); + } + } + + /* get the destination's real dirID and make sure it really is a directory */ + err = GetDestinationDirInfo(dstVRefNum, dstDirID, dstPathname, + &dstDirID, &isDirectory, &isDropBox); + if ( err != noErr ) + { + goto ErrorExit; + } + + if ( !isDirectory ) + { + return ( dirNFErr ); + } + + /* get the destination's real vRefNum */ + err = DetermineVRefNum(dstPathname, dstVRefNum, &dstVRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* See if PBHCopyFile can be used. Using PBHCopyFile saves time by letting the file server + ** copy the file if the source and destination locations are on the same file server. */ + tempLong = sizeof(infoBuffer); + err = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong); + if ( (err != noErr) && (err != paramErr) ) + { + return ( err ); + } + + if ( (err != paramErr) && hasCopyFile(infoBuffer) ) + { + /* The source volume supports PBHCopyFile. */ + srcServerAdr = infoBuffer.vMServerAdr; + + /* Now, see if the destination volume is on the same file server. */ + tempLong = sizeof(infoBuffer); + err = HGetVolParms(NULL, dstVRefNum, &infoBuffer, &tempLong); + if ( (err != noErr) && (err != paramErr) ) + { + return ( err ); + } + if ( (err != paramErr) && (srcServerAdr == infoBuffer.vMServerAdr) ) + { + /* Source and Dest are on same server and PBHCopyFile is supported. Copy with CopyFile. */ + err = HCopyFile(srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, NULL, copyName); + if ( err != noErr ) + { + return ( err ); + } + + /* AppleShare's CopyFile clears the isAlias bit, so I still need to attempt to copy + the File's attributes to attempt to get things right. */ + if ( copyName != NULL ) /* Did caller supply copy file name? */ + { + /* Yes, use the caller supplied copy file name. */ + (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, copyName, true); + } + else + { + /* They didn't, so get the source file name and use it. */ + if ( GetFilenameFromPathname(srcName, dstName) == noErr ) + { + /* */ + (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, true); + } + } + return ( err ); + } + } + + /* If we're here, then PBHCopyFile couldn't be used so we have to copy the file by hand. */ + + /* Make sure a copy buffer is allocated. */ + if ( copyBufferPtr == NULL ) + { + /* The caller didn't supply a copy buffer so grab one from the application heap. + ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer. + ** If 512 bytes aren't available, we're in trouble. */ + copyBufferSize = bigCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + copyBufferSize = minCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + return ( memFullErr ); + } + } + ourCopyBuffer = true; + } + + /* Open the source data fork. */ + err = HOpenAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum); + if ( err != noErr ) + return ( err ); + + /* Once a file is opened, we have to exit via ErrorExit to make sure things are cleaned up */ + + /* See if the copy will be renamed. */ + if ( copyName != NULL ) /* Did caller supply copy file name? */ + BlockMoveData(copyName, dstName, copyName[0] + 1); /* Yes, use the caller supplied copy file name. */ + else + { /* They didn't, so get the source file name and use it. */ + err = GetFileLocation(srcRefNum, &tempInt, &tempLong, dstName); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + /* Create the destination file. */ + err = HCreateMinimum(dstVRefNum, dstDirID, dstName); + if ( err != noErr ) + { + goto ErrorExit; + } + dstCreated = true; /* After creating the destination file, any + ** error conditions should delete the destination file */ + + /* An AppleShare dropbox folder is a folder for which the user has the Make Changes + ** privilege (write access), but not See Files (read access) and See Folders (search access). + ** Copying a file into an AppleShare dropbox presents some special problems. Here are the + ** rules we have to follow to copy a file into a dropbox: + ** ¥ File attributes can be changed only when both forks of a file are empty. + ** ¥ DeskTop Manager comments can be added to a file only when both forks of a file + ** are empty. + ** ¥ A fork can be opened for write access only when both forks of a file are empty. + ** So, with those rules to live with, we'll do those operations now while both forks + ** are empty. */ + + if ( isDropBox ) + { + /* We only set the file attributes now if the file is being copied into a + ** drop box. In all other cases, it is better to set the attributes last + ** so that if FileCopy is modified to give up time to other processes + ** periodicly, the Finder won't try to read any bundle information (because + ** the bundle-bit will still be clear) from a partially copied file. If the + ** copy is into a drop box, we have to set the attributes now, but since the + ** destination forks are opened with write/deny-read/deny-write permissions, + ** any Finder that might see the file in the drop box won't be able to open + ** its resource fork until the resource fork is closed. + ** + ** Note: if you do modify FileCopy to give up time to other processes, don't + ** give up time between the time the destination file is created (above) and + ** the time both forks are opened (below). That way, you stand the best chance + ** of making sure the Finder doesn't read a partially copied resource fork. + */ + /* Copy attributes but don't lock the destination. */ + err = CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, false); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + /* Attempt to copy the comments while both forks are empty. + ** Ignore the result because we really don't care if it worked or not. */ + (void) DTCopyComment(srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName); + + /* See which forks we need to copy. By doing this, we won't create a data or resource fork + ** for the destination unless it's really needed (some foreign file systems such as + ** the ProDOS File System and Macintosh PC Exchange have to create additional disk + ** structures to support resource forks). */ + err = CheckForForks(srcVRefNum, srcDirID, srcName, &hasDataFork, &hasResourceFork); + if ( err != noErr ) + { + goto ErrorExit; + } + + if ( hasDataFork ) + { + /* Open the destination data fork. */ + err = HOpenAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstDataRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + if ( hasResourceFork ) + { + /* Open the destination resource fork. */ + err = HOpenRFAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstRsrcRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + if ( hasDataFork ) + { + /* Copy the data fork. */ + err = CopyFork(srcRefNum, dstDataRefNum, copyBufferPtr, copyBufferSize); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* Close both data forks and clear reference numbers. */ + (void) FSClose(srcRefNum); + (void) FSClose(dstDataRefNum); + srcRefNum = dstDataRefNum = 0; + } + else + { + /* Close the source data fork since it was opened earlier */ + (void) FSClose(srcRefNum); + srcRefNum = 0; + } + + if ( hasResourceFork ) + { + /* Open the source resource fork. */ + err = HOpenRFAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* Copy the resource fork. */ + err = CopyFork(srcRefNum, dstRsrcRefNum, copyBufferPtr, copyBufferSize); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* Close both resource forks and clear reference numbers. */ + (void) FSClose(srcRefNum); + (void) FSClose(dstRsrcRefNum); + srcRefNum = dstRsrcRefNum = 0; + } + + /* Get rid of the copy buffer if we allocated it. */ + if ( ourCopyBuffer ) + { + DisposePtr((Ptr)copyBufferPtr); + } + + /* Attempt to copy attributes again to set mod date. Copy lock condition this time + ** since we're done with the copy operation. This operation will fail if we're copying + ** into an AppleShare dropbox, so we don't check for error conditions. */ + CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, true); + + /* Hey, we did it! */ + return ( noErr ); + +ErrorExit: + if ( srcRefNum != 0 ) + { + (void) FSClose(srcRefNum); /* Close the source file */ + } + if ( dstDataRefNum != 0 ) + { + (void) FSClose(dstDataRefNum); /* Close the destination file data fork */ + } + if ( dstRsrcRefNum != 0 ) + { + (void) FSClose(dstRsrcRefNum); /* Close the destination file resource fork */ + } + if ( dstCreated ) + { + (void) HDelete(dstVRefNum, dstDirID, dstName); /* Delete dest file. This may fail if the file + is in a "drop folder" */ + } + if ( ourCopyBuffer ) /* dispose of any memory we allocated */ + { + DisposePtr((Ptr)copyBufferPtr); + } + + return ( err ); +} + +/*****************************************************************************/ + +pascal OSErr FSpFileCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight) +{ + return ( FileCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyName, copyBufferPtr, copyBufferSize, preflight) ); +} + +/*****************************************************************************/ + diff --git a/src/mac/morefile/FileCopy.h b/src/mac/morefile/FileCopy.h new file mode 100644 index 0000000000..5f4c008e25 --- /dev/null +++ b/src/mac/morefile/FileCopy.h @@ -0,0 +1,220 @@ +/* +** Apple Macintosh Developer Technical Support +** +** FileCopy: A robust, general purpose file copy routine. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: FileCopy.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __FILECOPY__ +#define __FILECOPY__ + +#include +#include + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +pascal OSErr FileCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight); +/* ¦ Duplicate a file and optionally rename it. + The FileCopy function duplicates a file and optionally renames it. + Since the PBHCopyFile routine is only available on some + AFP server volumes under specific conditions, this routine + either uses PBHCopyFile, or does all of the work PBHCopyFile + does. The srcVRefNum, srcDirID and srcName are used to + determine the location of the file to copy. The dstVRefNum + dstDirID and dstPathname are used to determine the location of + the destination directory. If copyName <> NIL, then it points + to the name of the new file. If copyBufferPtr <> NIL, it + points to a buffer of copyBufferSize that is used to copy + the file's data. The larger the supplied buffer, the + faster the copy. If copyBufferPtr = NIL, then this routine + allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source file name. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstPathname input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new file name if the file is + to be renamed or nil if the file isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want FileCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FileCopy makes sure there are enough + allocation blocks on the destination volume to + hold both the data and resource forks before + starting the copy. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: FSpFileCopy, DirectoryCopy, FSpDirectoryCopy +*/ + +/*****************************************************************************/ + +pascal OSErr FSpFileCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight); +/* ¦ Duplicate a file and optionally rename it. + The FSpFileCopy function duplicates a file and optionally renames it. + Since the PBHCopyFile routine is only available on some + AFP server volumes under specific conditions, this routine + either uses PBHCopyFile, or does all of the work PBHCopyFile + does. The srcSpec is used to + determine the location of the file to copy. The dstSpec is + used to determine the location of the + destination directory. If copyName <> NIL, then it points + to the name of the new file. If copyBufferPtr <> NIL, it + points to a buffer of copyBufferSize that is used to copy + the file's data. The larger the supplied buffer, the + faster the copy. If copyBufferPtr = NIL, then this routine + allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + srcSpec input: An FSSpec record specifying the source file. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new file name if the file is + to be renamed or nil if the file isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want FileCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FSpFileCopy makes sure there are + enough allocation blocks on the destination + volume to hold both the data and resource forks + before starting the copy. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: FileCopy, DirectoryCopy, FSpDirectoryCopy +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __FILECOPY__ */ diff --git a/src/mac/morefile/FullPath.cpp b/src/mac/morefile/FullPath.cpp new file mode 100644 index 0000000000..3ad50b412d --- /dev/null +++ b/src/mac/morefile/FullPath.cpp @@ -0,0 +1,246 @@ +/* +** Apple Macintosh Developer Technical Support +** +** Routines for dealing with full pathnames... if you really must. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: FullPath.c +** +** Copyright © 1995-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "FSpCompa.h" +#include "FullPath.h" + +/* + IMPORTANT NOTE: + + The use of full pathnames is strongly discouraged. Full pathnames are + particularly unreliable as a means of identifying files, directories + or volumes within your application, for two primary reasons: + + ¥ The user can change the name of any element in the path at virtually + any time. + ¥ Volume names on the Macintosh are *not* unique. Multiple + mounted volumes can have the same name. For this reason, the use of + a full pathname to identify a specific volume may not produce the + results you expect. If more than one volume has the same name and + a full pathname is used, the File Manager currently uses the first + mounted volume it finds with a matching name in the volume queue. + + In general, you should use a fileÕs name, parent directory ID, and + volume reference number to identify a file you want to open, delete, + or otherwise manipulate. + + If you need to remember the location of a particular file across + subsequent system boots, use the Alias Manager to create an alias record + describing the file. If the Alias Manager is not available, you can save + the fileÕs name, its parent directory ID, and the name of the volume on + which itÕs located. Although none of these methods is foolproof, they are + much more reliable than using full pathnames to identify files. + + Nonetheless, it is sometimes useful to display a fileÕs full pathname to + the user. For example, a backup utility might display a list of full + pathnames of files as it copies them onto the backup medium. Or, a + utility might want to display a dialog box showing the full pathname of + a file when it needs the userÕs confirmation to delete the file. No + matter how unreliable full pathnames may be from a file-specification + viewpoint, users understand them more readily than volume reference + numbers or directory IDs. (Hint: Use the TruncString function from + TextUtils.h with truncMiddle as the truncWhere argument to shorten + full pathnames to a displayable length.) + + The following technique for constructing the full pathname of a file is + intended for display purposes only. Applications that depend on any + particular structure of a full pathname are likely to fail on alternate + foreign file systems or under future system software versions. +*/ + +/*****************************************************************************/ + +pascal OSErr GetFullPath(short vRefNum, + long dirID, + ConstStr255Param name, + short *fullPathLength, + Handle *fullPath) +{ + OSErr result; + FSSpec spec; + + *fullPathLength = 0; + *fullPath = NULL; + + result = FSMakeFSSpecCompat(vRefNum, dirID, name, &spec); + if ( (result == noErr) || (result == fnfErr) ) + { + result = FSpGetFullPath(&spec, fullPathLength, fullPath); + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFullPath(const FSSpec *spec, + short *fullPathLength, + Handle *fullPath) +{ + OSErr result; + OSErr realResult; + FSSpec tempSpec; + CInfoPBRec pb; + + *fullPathLength = 0; + *fullPath = NULL; + + // Default to noErr + realResult = noErr; + + /* Make a copy of the input FSSpec that can be modified */ + BlockMoveData(spec, &tempSpec, sizeof(FSSpec)); + + if ( tempSpec.parID == fsRtParID ) + { + /* The object is a volume */ + + /* Add a colon to make it a full pathname */ + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + + /* We're done */ + result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); + } + else + { + /* The object isn't a volume */ + + /* Is the object a file or a directory? */ + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrDirID = tempSpec.parID; + pb.dirInfo.ioFDirIndex = 0; + result = PBGetCatInfoSync(&pb); + // Allow file/directory name at end of path to not exist. + realResult = result; + if ( (result == noErr) || (result == fnfErr) ) + { + /* if the object is a directory, append a colon so full pathname ends with colon */ + if ( (result == noErr) && (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + } + + /* Put the object name in first */ + result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); + if ( result == noErr ) + { + /* Get the ancestor directory names */ + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrParID = tempSpec.parID; + do /* loop until we have an error or find the root directory */ + { + pb.dirInfo.ioFDirIndex = -1; + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + result = PBGetCatInfoSync(&pb); + if ( result == noErr ) + { + /* Append colon to directory name */ + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + + /* Add directory name to beginning of fullPath */ + (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]); + result = MemError(); + } + } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) ); + } + } + } + if ( result == noErr ) + { + /* Return the length */ + *fullPathLength = InlineGetHandleSize(*fullPath); + result = realResult; // return realResult in case it was fnfErr + } + else + { + /* Dispose of the handle and return NULL and zero length */ + if ( *fullPath != NULL ) + { + DisposeHandle(*fullPath); + } + *fullPath = NULL; + *fullPathLength = 0; + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpLocationFromFullPath(short fullPathLength, + const void *fullPath, + FSSpec *spec) +{ + AliasHandle alias; + OSErr result; + Boolean wasChanged; + Str32 nullString; + + /* Create a minimal alias from the full pathname */ + nullString[0] = 0; /* null string to indicate no zone or server name */ + result = NewAliasMinimalFromFullPath(fullPathLength, fullPath, nullString, nullString, &alias); + if ( result == noErr ) + { + /* Let the Alias Manager resolve the alias. */ + result = ResolveAlias(NULL, alias, spec, &wasChanged); + + DisposeHandle((Handle)alias); /* Free up memory used */ + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr LocationFromFullPath(short fullPathLength, + const void *fullPath, + short *vRefNum, + long *parID, + Str31 name) +{ + OSErr result; + FSSpec spec; + + result = FSpLocationFromFullPath(fullPathLength, fullPath, &spec); + if ( result == noErr ) + { + *vRefNum = spec.vRefNum; + *parID = spec.parID; + BlockMoveData(&spec.name[0], &name[0], spec.name[0] + 1); + } + return ( result ); +} + +/*****************************************************************************/ + diff --git a/src/mac/morefile/FullPath.h b/src/mac/morefile/FullPath.h new file mode 100644 index 0000000000..5a49e40ac7 --- /dev/null +++ b/src/mac/morefile/FullPath.h @@ -0,0 +1,243 @@ +/* +** Apple Macintosh Developer Technical Support +** +** Routines for dealing with full pathnames... if you really must. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: FullPath.h +** +** Copyright © 1995-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __FULLPATH__ +#define __FULLPATH__ + +#include +#include + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + IMPORTANT NOTE: + + The use of full pathnames is strongly discouraged. Full pathnames are + particularly unreliable as a means of identifying files, directories + or volumes within your application, for two primary reasons: + + ¥ The user can change the name of any element in the path at + virtually any time. + ¥ Volume names on the Macintosh are *not* unique. Multiple + mounted volumes can have the same name. For this reason, the use of + a full pathname to identify a specific volume may not produce the + results you expect. If more than one volume has the same name and + a full pathname is used, the File Manager currently uses the first + mounted volume it finds with a matching name in the volume queue. + + In general, you should use a fileÕs name, parent directory ID, and + volume reference number to identify a file you want to open, delete, + or otherwise manipulate. + + If you need to remember the location of a particular file across + subsequent system boots, use the Alias Manager to create an alias + record describing the file. If the Alias Manager is not available, you + can save the fileÕs name, its parent directory ID, and the name of the + volume on which itÕs located. Although none of these methods is + foolproof, they are much more reliable than using full pathnames to + identify files. + + Nonetheless, it is sometimes useful to display a fileÕs full pathname + to the user. For example, a backup utility might display a list of full + pathnames of files as it copies them onto the backup medium. Or, a + utility might want to display a dialog box showing the full pathname of + a file when it needs the userÕs confirmation to delete the file. No + matter how unreliable full pathnames may be from a file-specification + viewpoint, users understand them more readily than volume reference + numbers or directory IDs. (Hint: Use the TruncString function from + TextUtils.h with truncMiddle as the truncWhere argument to shorten + full pathnames to a displayable length.) + + The following technique for constructing the full pathname of a file is + intended for display purposes only. Applications that depend on any + particular structure of a full pathname are likely to fail on alternate + foreign file systems or under future system software versions. +*/ + +/*****************************************************************************/ + +pascal OSErr GetFullPath(short vRefNum, + long dirID, + ConstStr255Param name, + short *fullPathLength, + Handle *fullPath); +/* ¦ Get a full pathname to a volume, directory or file. + The GetFullPath function builds a full pathname to the specified + object. The full pathname is returned in the newly created handle + fullPath and the length of the full pathname is returned in + fullPathLength. Your program is responsible for disposing of the + fullPath handle. + + Note that a full pathname can be made to a file/directory that does not + yet exist if all directories up to that file/directory exist. In this case, + GetFullPath will return a fnfErr. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + fullPathLength output: The number of characters in the full pathname. + If the function fails to create a full + pathname, it sets fullPathLength to 0. + fullPath output: A handle to the newly created full pathname + buffer. If the function fails to create a + full pathname, it sets fullPath to NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File or directory does not exist (fullPath + and fullPathLength are still valid) + paramErr -50 No default volume + memFullErr -108 Not enough memory + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpGetFullPath +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetFullPath(const FSSpec *spec, + short *fullPathLength, + Handle *fullPath); +/* ¦ Get a full pathname to a volume, directory or file. + The GetFullPath function builds a full pathname to the specified + object. The full pathname is returned in the newly created handle + fullPath and the length of the full pathname is returned in + fullPathLength. Your program is responsible for disposing of the + fullPath handle. + + Note that a full pathname can be made to a file/directory that does not + yet exist if all directories up to that file/directory exist. In this case, + FSpGetFullPath will return a fnfErr. + + spec input: An FSSpec record specifying the object. + fullPathLength output: The number of characters in the full pathname. + If the function fails to create a full pathname, + it sets fullPathLength to 0. + fullPath output: A handle to the newly created full pathname + buffer. If the function fails to create a + full pathname, it sets fullPath to NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File or directory does not exist (fullPath + and fullPathLength are still valid) + paramErr -50 No default volume + memFullErr -108 Not enough memory + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: GetFullPath +*/ + +/*****************************************************************************/ + +pascal OSErr FSpLocationFromFullPath(short fullPathLength, + const void *fullPath, + FSSpec *spec); +/* ¦ Get a FSSpec from a full pathname. + The FSpLocationFromFullPath function returns a FSSpec to the object + specified by full pathname. This function requires the Alias Manager. + + fullPathLength input: The number of characters in the full pathname + of the target. + fullPath input: A pointer to a buffer that contains the full + pathname of the target. The full pathname + starts with the name of the volume, includes + all of the directory names in the path to the + target, and ends with the target name. + spec output: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 The volume is not mounted + fnfErr -43 Target not found, but volume and parent + directory found + paramErr -50 Parameter error + usrCanceledErr -128 The user canceled the operation + + __________ + + See also: LocationFromFullPath +*/ + +/*****************************************************************************/ + +pascal OSErr LocationFromFullPath(short fullPathLength, + const void *fullPath, + short *vRefNum, + long *parID, + Str31 name); +/* ¦ Get an object's location from a full pathname. + The LocationFromFullPath function returns the volume reference number, + parent directory ID and name of the object specified by full pathname. + This function requires the Alias Manager. + + fullPathLength input: The number of characters in the full pathname + of the target. + fullPath input: A pointer to a buffer that contains the full + pathname of the target. The full pathname starts + with the name of the volume, includes all of + the directory names in the path to the target, + and ends with the target name. + vRefNum output: The volume reference number. + parID output: The parent directory ID of the specified object. + name output: The name of the specified object. + + Result Codes + noErr 0 No error + nsvErr -35 The volume is not mounted + fnfErr -43 Target not found, but volume and parent + directory found + paramErr -50 Parameter error + usrCanceledErr -128 The user canceled the operation + + __________ + + See also: FSpLocationFromFullPath +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __FULLPATH__ */ \ No newline at end of file diff --git a/src/mac/morefile/IterateD.cpp b/src/mac/morefile/IterateD.cpp new file mode 100644 index 0000000000..6b7e52a78c --- /dev/null +++ b/src/mac/morefile/IterateD.cpp @@ -0,0 +1,189 @@ +/* +** IterateDirectory: File Manager directory iterator routines. +** +** by Jim Luther +** +** File: IterateDirectory.c +** +** Copyright © 1995-1998 Jim Luther and Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. +** +** IterateDirectory is designed to drop into the MoreFiles sample code +** library I wrote while in Apple Developer Technical Support +*/ + +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreExtr.h" +#include "IterateD.h" + +/* +** Type definitions +*/ + +/* The IterateGlobals structure is used to minimize the amount of +** stack space used when recursively calling IterateDirectoryLevel +** and to hold global information that might be needed at any time. +*/ +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +struct IterateGlobals +{ + IterateFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */ + CInfoPBRec cPB; /* the parameter block used for PBGetCatInfo calls */ + Str63 itemName; /* the name of the current item */ + OSErr result; /* temporary holder of results - saves 2 bytes of stack each level */ + Boolean quitFlag; /* set to true if filter wants to kill interation */ + unsigned short maxLevels; /* Maximum levels to iterate through */ + unsigned short currentLevel; /* The current level IterateLevel is on */ + void *yourDataPtr; /* A pointer to caller data the filter may need to access */ +}; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + +typedef struct IterateGlobals IterateGlobals; +typedef IterateGlobals *IterateGlobalsPtr; + +/*****************************************************************************/ + +/* Static Prototype */ + +static void IterateDirectoryLevel(long dirID, + IterateGlobals *theGlobals); + +/*****************************************************************************/ + +/* +** Functions +*/ + +static void IterateDirectoryLevel(long dirID, + IterateGlobals *theGlobals) +{ + if ( (theGlobals->maxLevels == 0) || /* if maxLevels is zero, we aren't checking levels */ + (theGlobals->currentLevel < theGlobals->maxLevels) ) /* if currentLevel < maxLevels, look at this level */ + { + short index = 1; + + ++theGlobals->currentLevel; /* go to next level */ + + do + { /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */ + + /* Get next source item at the current directory level */ + + theGlobals->cPB.dirInfo.ioFDirIndex = index; + theGlobals->cPB.dirInfo.ioDrDirID = dirID; + theGlobals->result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB); + + if ( theGlobals->result == noErr ) + { + /* Call the IterateFilterProc */ + CallIterateFilterProc(theGlobals->iterateFilter, &theGlobals->cPB, &theGlobals->quitFlag, theGlobals->yourDataPtr); + + /* Is it a directory? */ + if ( (theGlobals->cPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* We have a directory */ + if ( !theGlobals->quitFlag ) + { + /* Dive again if the IterateFilterProc didn't say "quit" */ + IterateDirectoryLevel(theGlobals->cPB.dirInfo.ioDrDirID, theGlobals); + } + } + } + + ++index; /* prepare to get next item */ + } while ( (theGlobals->result == noErr) && (!theGlobals->quitFlag) ); /* time to fall back a level? */ + + if ( (theGlobals->result == fnfErr) || /* fnfErr is OK - it only means we hit the end of this level */ + (theGlobals->result == afpAccessDenied) ) /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */ + { + theGlobals->result = noErr; + } + + --theGlobals->currentLevel; /* return to previous level as we leave */ + } +} + +/*****************************************************************************/ + +pascal OSErr IterateDirectory(short vRefNum, + long dirID, + ConstStr255Param name, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void *yourDataPtr) +{ + IterateGlobals theGlobals; + OSErr result; + long theDirID; + short theVRefNum; + Boolean isDirectory; + + /* Make sure there is a IterateFilter */ + if ( iterateFilter != NULL ) + { + /* Get the real directory ID and make sure it is a directory */ + result = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory); + if ( result == noErr ) + { + if ( isDirectory == true ) + { + /* Get the real vRefNum */ + result = DetermineVRefNum(name, vRefNum, &theVRefNum); + if ( result == noErr ) + { + /* Set up the globals we need to access from the recursive routine. */ + theGlobals.iterateFilter = iterateFilter; + theGlobals.cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName; + theGlobals.cPB.hFileInfo.ioVRefNum = theVRefNum; + theGlobals.itemName[0] = 0; + theGlobals.result = noErr; + theGlobals.quitFlag = false; + theGlobals.maxLevels = maxLevels; + theGlobals.currentLevel = 0; /* start at level 0 */ + theGlobals.yourDataPtr = yourDataPtr; + + /* Here we go into recursion land... */ + IterateDirectoryLevel(theDirID, &theGlobals); + + result = theGlobals.result; /* set the result */ + } + } + else + { + result = dirNFErr; /* a file was passed instead of a directory */ + } + } + } + else + { + result = paramErr; /* iterateFilter was NULL */ + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpIterateDirectory(const FSSpec *spec, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void *yourDataPtr) +{ + return ( IterateDirectory(spec->vRefNum, spec->parID, spec->name, + maxLevels, iterateFilter, yourDataPtr) ); +} + +/*****************************************************************************/ diff --git a/src/mac/morefile/IterateD.h b/src/mac/morefile/IterateD.h new file mode 100644 index 0000000000..ec36ee43bf --- /dev/null +++ b/src/mac/morefile/IterateD.h @@ -0,0 +1,171 @@ +/* +** IterateDirectory: File Manager directory iterator routines. +** +** by Jim Luther +** +** File: IterateDirectory.h +** +** Copyright © 1995-1998 Jim Luther and Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. +** +** IterateDirectory is designed to drop into the MoreFiles sample code +** library I wrote while in Apple Developer Technical Support +*/ + +#ifndef __ITERATEDIRECTORY__ +#define __ITERATEDIRECTORY__ + +#include +#include + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +typedef pascal void (*IterateFilterProcPtr) (const CInfoPBRec * const cpbPtr, + Boolean *quitFlag, + void *yourDataPtr); +/* ¦ Prototype for the IterateFilterProc function IterateDirectory calls. + This is the prototype for the IterateFilterProc function which is + called once for each file and directory found by IterateDirectory. The + IterateFilterProc gets a pointer to the CInfoPBRec that IterateDirectory + used to call PBGetCatInfo. The IterateFilterProc can use the read-only + data in the CInfoPBRec for whatever it wants. + + If the IterateFilterProc wants to stop IterateDirectory, it can set + quitFlag to true (quitFlag will be passed to the IterateFilterProc + false). + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateFilterProc. + + cpbPtr input: A pointer to the CInfoPBRec that IterateDirectory + used to call PBGetCatInfo. The CInfoPBRec and the + data it points to must not be changed by your + IterateFilterProc. + quitFlag output: Your IterateFilterProc can set quitFlag to true + if it wants to stop IterateDirectory. + yourDataPtr input: A pointer to whatever data structure you might + want to access from within the IterateFilterProc. + + __________ + + Also see: IterateDirectory, FSpIterateDirectory +*/ + +#define CallIterateFilterProc(userRoutine, cpbPtr, quitFlag, yourDataPtr) \ + (*(userRoutine))((cpbPtr), (quitFlag), (yourDataPtr)) + +/*****************************************************************************/ + +pascal OSErr IterateDirectory(short vRefNum, + long dirID, + ConstStr255Param name, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void *yourDataPtr); +/* ¦ Iterate (scan) through a directory's content. + The IterateDirectory function performs a recursive iteration (scan) of + the specified directory and calls your IterateFilterProc function once + for each file and directory found. + + The maxLevels parameter lets you control how deep the recursion goes. + If maxLevels is 1, IterateDirectory only scans the specified directory; + if maxLevels is 2, IterateDirectory scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateFilterProc. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + maxLevels input: Maximum number of directory levels to scan or + zero to scan all directory levels. + iterateFilter input: A pointer to the routine you want called once + for each file and directory found by + IterateDirectory. + yourDataPtr input: A pointer to whatever data structure you might + want to access from within the IterateFilterProc. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume or iterateFilter was NULL + dirNFErr -120 Directory not found or incomplete pathname + or a file was passed instead of a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: IterateFilterProcPtr, FSpIterateDirectory +*/ + +/*****************************************************************************/ + +pascal OSErr FSpIterateDirectory(const FSSpec *spec, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void *yourDataPtr); +/* ¦ Iterate (scan) through a directory's content. + The FSpIterateDirectory function performs a recursive iteration (scan) + of the specified directory and calls your IterateFilterProc function once + for each file and directory found. + + The maxLevels parameter lets you control how deep the recursion goes. + If maxLevels is 1, FSpIterateDirectory only scans the specified directory; + if maxLevels is 2, FSpIterateDirectory scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateFilterProc. + + spec input: An FSSpec record specifying the directory to scan. + maxLevels input: Maximum number of directory levels to scan or + zero to scan all directory levels. + iterateFilter input: A pointer to the routine you want called once + for each file and directory found by + FSpIterateDirectory. + yourDataPtr input: A pointer to whatever data structure you might + want to access from within the IterateFilterProc. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume or iterateFilter was NULL + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: IterateFilterProcPtr, IterateDirectory +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __ITERATEDIRECTORY__ */ diff --git a/src/mac/morefile/MoreDesk.cpp b/src/mac/morefile/MoreDesk.cpp new file mode 100644 index 0000000000..74a59fd8d1 --- /dev/null +++ b/src/mac/morefile/MoreDesk.cpp @@ -0,0 +1,1254 @@ +/* +** Apple Macintosh Developer Technical Support +** +** A collection of useful high-level Desktop Manager routines. +** If the Desktop Manager isn't available, use the Desktop file +** for 'read' operations. +** +** We do more because we can... +** +** by Jim Luther and Nitin Ganatra, Apple Developer Technical Support Emeriti +** +** File: MoreDesktopMgr.c +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFile.h" +#include "MoreExtr.h" +#include "Search.h" +#include "MoreDesk.h" + +/*****************************************************************************/ + +/* Desktop file notes: +** +** ¥ The Desktop file is owned by the Finder and is normally open by the +** Finder. That means that we only have read-only access to the Desktop +** file. +** ¥ Since the Resource Manager doesn't support shared access to resource +** files and we're using read-only access, we don't ever leave the +** Desktop file open. We open a path to it, get the data we want out +** of it, and then close the open path. This is the only safe way to +** open a resource file with read-only access since some other program +** could have it open with write access. +** ¥ The bundle related resources in the Desktop file are normally +** purgable, so when we're looking through them, we don't bother to +** release resources we're done looking at - closing the resource file +** (which we always do) will release them. +** ¥ Since we can't assume the Desktop file is named "Desktop" +** (it probably is everywhere but France), we get the Desktop +** file's name by searching the volume's root directory for a file +** with fileType == 'FNDR' and creator == 'ERIK'. The only problem with +** this scheme is that someone could create another file with that type +** and creator in the root directory and we'd find the wrong file. +** The chances of this are very slim. +*/ + +/*****************************************************************************/ + +/* local defines */ + +enum +{ + kBNDLResType = 'BNDL', + kFREFResType = 'FREF', + kIconFamResType = 'ICN#', + kFCMTResType = 'FCMT', + kAPPLResType = 'APPL' +}; + +/*****************************************************************************/ + +/* local data structures */ + +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif + +struct IDRec +{ + short localID; + short rsrcID; +}; +typedef struct IDRec IDRec; +typedef IDRec *IDRecPtr; + +struct BundleType +{ + OSType type; /* 'ICN#' or 'FREF' */ + short count; /* number of IDRecs - 1 */ + IDRec idArray[1]; +}; +typedef struct BundleType BundleType; +typedef BundleType *BundleTypePtr; + +struct BNDLRec +{ + OSType signature; /* creator type signature */ + short versionID; /* version - should always be 0 */ + short numTypes; /* number of elements in typeArray - 1 */ + BundleType typeArray[1]; +}; +typedef struct BNDLRec BNDLRec; +typedef BNDLRec **BNDLRecHandle; + +struct FREFRec +{ + OSType fileType; /* file type */ + short iconID; /* icon local ID */ + Str255 fileName; /* file name */ +}; +typedef struct FREFRec FREFRec; +typedef FREFRec **FREFRecHandle; + +struct APPLRec +{ + OSType creator; /* creator type signature */ + long parID; /* parent directory ID */ + Str255 applName; /* application name */ +}; +typedef struct APPLRec APPLRec; +typedef APPLRec *APPLRecPtr; + +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + +/*****************************************************************************/ + +/* static prototypes */ + +static OSErr GetDesktopFileName(short vRefNum, + Str255 desktopName); + +static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName, + short vRefNum, + OSType creator, + short *applVRefNum, + long *applParID, + Str255 applName); + +static OSErr FindBundleGivenCreator(OSType creator, + BNDLRecHandle *returnBndl); + +static OSErr FindTypeInBundle(OSType typeToFind, + BNDLRecHandle theBndl, + BundleTypePtr *returnBundleType); + +static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType, + OSType fileType, + short *iconLocalID); + +static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType, + short iconLocalID, + short *iconRsrcID); + +static OSType DTIconToResIcon(short iconType); + +static OSErr GetIconFromDesktopFile(ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle *iconHandle); + +static OSErr GetCommentID(short vRefNum, + long dirID, + ConstStr255Param name, + short *commentID); + +static OSErr GetCommentFromDesktopFile(short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment); + +/*****************************************************************************/ + +/* +** GetDesktopFileName +** +** Get the name of the Desktop file. +*/ +static OSErr GetDesktopFileName(short vRefNum, + Str255 desktopName) +{ + OSErr error; + HParamBlockRec pb; + short index; + Boolean found; + + pb.fileParam.ioNamePtr = desktopName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioFVersNum = 0; + index = 1; + found = false; + do + { + pb.fileParam.ioDirID = fsRtDirID; + pb.fileParam.ioFDirIndex = index; + error = PBHGetFInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.fileParam.ioFlFndrInfo.fdType == 'FNDR') && + (pb.fileParam.ioFlFndrInfo.fdCreator == 'ERIK') ) + { + found = true; + } + } + ++index; + } while ( (error == noErr) && !found ); + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTOpen(ConstStr255Param volName, + short vRefNum, + short *dtRefNum, + Boolean *newDTDatabase) +{ + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize; + DTPBRec pb; + + /* Check for volume Desktop Manager support before calling */ + infoSize = sizeof(GetVolParmsInfoBuffer); + error = HGetVolParms(volName, vRefNum, &volParmsInfo, &infoSize); + if ( error == noErr ) + { + if ( hasDesktopMgr(volParmsInfo) ) + { + pb.ioNamePtr = (StringPtr)volName; + pb.ioVRefNum = vRefNum; + error = PBDTOpenInform(&pb); + /* PBDTOpenInform informs us if the desktop was just created */ + /* by leaving the low bit of ioTagInfo clear (0) */ + *newDTDatabase = ((pb.ioTagInfo & 1L) == 0); + if ( error == paramErr ) + { + error = PBDTGetPath(&pb); + /* PBDTGetPath doesn't tell us if the database is new */ + /* so assume it is not new */ + *newDTDatabase = false; + } + *dtRefNum = pb.ioDTRefNum; + } + else + { + error = paramErr; + } + } + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetAPPLFromDesktopFile +** +** Get a application's location from the +** Desktop file's 'APPL' resources. +*/ +static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName, + short vRefNum, + OSType creator, + short *applVRefNum, + long *applParID, + Str255 applName) +{ + OSErr error; + short realVRefNum; + Str255 desktopName; + short savedResFile; + short dfRefNum; + Handle applResHandle; + Boolean foundCreator; + Ptr applPtr; + long applSize; + + error = DetermineVRefNum(volName, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = GetDesktopFileName(realVRefNum, desktopName); + if ( error == noErr ) + { + savedResFile = CurResFile(); + /* + ** Open the 'Desktop' file in the root directory. (because + ** opening the resource file could preload unwanted resources, + ** bracket the call with SetResLoad(s)) + */ + SetResLoad(false); + dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm); + SetResLoad(true); + + if ( dfRefNum != -1) + { + /* Get 'APPL' resource ID 0 */ + applResHandle = Get1Resource(kAPPLResType, 0); + if ( applResHandle != NULL ) + { + applSize = InlineGetHandleSize((Handle)applResHandle); + if ( applSize != 0 ) /* make sure the APPL resource isn't empty */ + { + foundCreator = false; + applPtr = *applResHandle; + + /* APPL's don't have a count so I have to use the size as the bounds */ + while ( (foundCreator == false) && + (applPtr < (*applResHandle + applSize)) ) + { + if ( ((APPLRecPtr)applPtr)->creator == creator ) + { + foundCreator = true; + } + else + { + /* fun with pointer math... */ + applPtr += sizeof(OSType) + + sizeof(long) + + ((APPLRecPtr)applPtr)->applName[0] + 1; + /* application mappings are word aligned within the resource */ + if ( ((unsigned long)applPtr % 2) != 0 ) + { + applPtr += 1; + } + } + } + if ( foundCreator == true ) + { + *applVRefNum = realVRefNum; + *applParID = ((APPLRecPtr)applPtr)->parID; + BlockMoveData(((APPLRecPtr)applPtr)->applName, + applName, + ((APPLRecPtr)applPtr)->applName[0] + 1); + /* error is already noErr */ + } + else + { + error = afpItemNotFound; /* didn't find a creator match */ + } + } + else + { + error = afpItemNotFound; /* no APPL mapping available */ + } + } + else + { + error = afpItemNotFound; /* no APPL mapping available */ + } + + /* restore the resource chain and close the Desktop file */ + UseResFile(savedResFile); + CloseResFile(dfRefNum); + } + else + { + error = afpItemNotFound; + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTXGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + short *applVRefNum, + long *applParID, + Str255 applName) +{ + OSErr error; + UniversalFMPB pb; + short dtRefNum; + Boolean newDTDatabase; + short realVRefNum; + short index; + Boolean applFound; + FSSpec spec; + long actMatchCount; + + /* get the real vRefNum */ + error = DetermineVRefNum(volName, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + if ( !newDTDatabase ) + { + index = 0; + applFound = false; + do + { + pb.dtPB.ioNamePtr = applName; + pb.dtPB.ioDTRefNum = dtRefNum; + pb.dtPB.ioIndex = index; + pb.dtPB.ioFileCreator = creator; + error = PBDTGetAPPLSync(&pb.dtPB); + if ( error == noErr ) + { + /* got a match - see if it is valid */ + + *applVRefNum = realVRefNum; /* get the vRefNum now */ + *applParID = pb.dtPB.ioAPPLParID; /* get the parent ID now */ + + /* pb.hPB.fileParam.ioNamePtr is already set */ + pb.hPB.fileParam.ioVRefNum = realVRefNum; + pb.hPB.fileParam.ioFVersNum = 0; + pb.hPB.fileParam.ioDirID = *applParID; + pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + if ( PBHGetFInfoSync(&pb.hPB) == noErr ) + { + if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator == creator) && + (pb.hPB.fileParam.ioFlFndrInfo.fdType == 'APPL') ) + { + applFound = true; + } + } + } + ++index; + } while ( (error == noErr) && !applFound ); + if ( error != noErr ) + { + error = afpItemNotFound; + } + } + else + { + /* Desktop database is empty (new), set error to try CatSearch */ + error = afpItemNotFound; + } + } + /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */ + if ( error == paramErr ) + { + /* if paramErr, the volume didn't support the Desktop Manager */ + /* try the Desktop file */ + + error = GetAPPLFromDesktopFile(volName, vRefNum, creator, + applVRefNum, applParID, applName); + if ( error == noErr ) + { + /* got a match - see if it is valid */ + + pb.hPB.fileParam.ioNamePtr = applName; + pb.hPB.fileParam.ioVRefNum = *applVRefNum; + pb.hPB.fileParam.ioFVersNum = 0; + pb.hPB.fileParam.ioDirID = *applParID; + pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + if ( PBHGetFInfoSync(&pb.hPB) == noErr ) + { + if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator != creator) || + (pb.hPB.fileParam.ioFlFndrInfo.fdType != 'APPL') ) + { + error = afpItemNotFound; + } + } + else if ( error == fnfErr ) + { + error = afpItemNotFound; + } + } + } + /* acceptable error from DesktopFile code to continue is afpItemNotFound */ + if ( (error == afpItemNotFound) && searchCatalog) + { + /* Couldn't be found in the Desktop file either, */ + /* try searching with CatSearch if requested */ + + error = CreatorTypeFileSearch(NULL, realVRefNum, creator, kAPPLResType, &spec, 1, + &actMatchCount, true); + if ( (error == noErr) || (error == eofErr) ) + { + if ( actMatchCount > 0 ) + { + *applVRefNum = spec.vRefNum; + *applParID = spec.parID; + BlockMoveData(spec.name, applName, spec.name[0] + 1); + } + else + { + error = afpItemNotFound; + } + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTXGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + FSSpec *spec) +{ + return ( DTXGetAPPL(volName, vRefNum, creator, searchCatalog, + &(spec->vRefNum), &(spec->parID), spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr DTGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + short *applVRefNum, + long *applParID, + Str255 applName) +{ + /* Call DTXGetAPPL with the "searchCatalog" parameter true */ + return ( DTXGetAPPL(volName, vRefNum, creator, true, + applVRefNum, applParID, applName) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + FSSpec *spec) +{ + /* Call DTXGetAPPL with the "searchCatalog" parameter true */ + return ( DTXGetAPPL(volName, vRefNum, creator, true, + &(spec->vRefNum), &(spec->parID), spec->name) ); +} + +/*****************************************************************************/ + +/* +** FindBundleGivenCreator +** +** Search the current resource file for the 'BNDL' resource with the given +** creator and return a handle to it. +*/ +static OSErr FindBundleGivenCreator(OSType creator, + BNDLRecHandle *returnBndl) +{ + OSErr error; + short numOfBundles; + short index; + BNDLRecHandle theBndl; + + error = afpItemNotFound; /* default to not found */ + + /* Search each BNDL resource until we find the one with a matching creator. */ + + numOfBundles = Count1Resources(kBNDLResType); + index = 1; + *returnBndl = NULL; + + while ( (index <= numOfBundles) && (*returnBndl == NULL) ) + { + theBndl = (BNDLRecHandle)Get1IndResource(kBNDLResType, index); + + if ( theBndl != NULL ) + { + if ( (*theBndl)->signature == creator ) + { + /* numTypes and typeArray->count will always be the actual count minus 1, */ + /* so 0 in both fields is valid. */ + if ( ((*theBndl)->numTypes >= 0) && ((*theBndl)->typeArray->count >= 0) ) + { + /* got it */ + *returnBndl = theBndl; + error = noErr; + } + } + } + + index ++; + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** FindTypeInBundle +** +** Given a Handle to a BNDL return a pointer to the desired type +** in it. If the type is not found, or if the type's count < 0, +** return afpItemNotFound. +*/ +static OSErr FindTypeInBundle(OSType typeToFind, + BNDLRecHandle theBndl, + BundleTypePtr *returnBundleType) +{ + OSErr error; + short index; + Ptr ptrIterator; /* use a Ptr so we can do ugly pointer math */ + + error = afpItemNotFound; /* default to not found */ + + ptrIterator = (Ptr)((*theBndl)->typeArray); + index = 0; + *returnBundleType = NULL; + + while ( (index < ((*theBndl)->numTypes + 1)) && + (*returnBundleType == NULL) ) + { + if ( (((BundleTypePtr)ptrIterator)->type == typeToFind) && + (((BundleTypePtr)ptrIterator)->count >= 0) ) + { + *returnBundleType = (BundleTypePtr)ptrIterator; + error = noErr; + } + else + { + ptrIterator += ( sizeof(OSType) + + sizeof(short) + + ( sizeof(IDRec) * (((BundleTypePtr)ptrIterator)->count + 1) ) ); + ++index; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetLocalIDFromFREF +** +** Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource +** looking for a matching fileType. If a matching fileType is found, return +** its icon local ID. If no match is found, return afpItemNotFound as the +** function result. +*/ +static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType, + OSType fileType, + short *iconLocalID) +{ + OSErr error; + short index; + IDRecPtr idIterator; + FREFRecHandle theFref; + + error = afpItemNotFound; /* default to not found */ + + /* For each localID in this type, get the FREF resource looking for fileType */ + index = 0; + idIterator = &theBundleType->idArray[0]; + *iconLocalID = 0; + + while ( (index <= theBundleType->count) && (*iconLocalID == 0) ) + { + theFref = (FREFRecHandle)Get1Resource(kFREFResType, idIterator->rsrcID); + if ( theFref != NULL ) + { + if ( (*theFref)->fileType == fileType ) + { + *iconLocalID = (*theFref)->iconID; + error = noErr; + } + } + + ++idIterator; + ++index; + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetIconRsrcIDFromLocalID +** +** Given a pointer to a 'ICN#' BundleType record, look for the IDRec with +** the localID that matches iconLocalID. If a matching IDRec is found, +** return the IDRec's rsrcID field value. If no match is found, return +** afpItemNotFound as the function result. +*/ +static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType, + short iconLocalID, + short *iconRsrcID) +{ + OSErr error; + short index; + IDRecPtr idIterator; + + error = afpItemNotFound; /* default to not found */ + + /* Find the rsrcID of the icon family type, given the localID */ + index = 0; + idIterator = &theBundleType->idArray[0]; + *iconRsrcID = 0; + + while ( (index <= theBundleType->count) && (*iconRsrcID == 0) ) + { + if ( idIterator->localID == iconLocalID ) + { + *iconRsrcID = idIterator->rsrcID; + error = noErr; + } + + idIterator ++; + index ++; + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** DTIconToResIcon +** +** Map a Desktop Manager icon type to the corresponding resource type. +** Return (OSType)0 if there is no corresponding resource type. +*/ +static OSType DTIconToResIcon(short iconType) +{ + OSType resType; + + switch ( iconType ) + { + case kLargeIcon: + resType = large1BitMask; + break; + case kLarge4BitIcon: + resType = large4BitData; + break; + case kLarge8BitIcon: + resType = large8BitData; + break; + case kSmallIcon: + resType = small1BitMask; + break; + case kSmall4BitIcon: + resType = small4BitData; + break; + case kSmall8BitIcon: + resType = small8BitData; + break; + default: + resType = (OSType)0; + break; + } + + return ( resType ); +} + +/*****************************************************************************/ + +/* +** GetIconFromDesktopFile +** +** INPUT a pointer to a non-existent Handle, because we'll allocate one +** +** search each BNDL resource for the right fileCreator and once we get it +** find the 'FREF' type in BNDL +** for each localID in the type, open the FREF resource +** if the FREF is the desired fileType +** get its icon localID +** get the ICN# type in BNDL +** get the icon resource number from the icon localID +** get the icon resource type from the desktop mgr's iconType +** get the icon of that type and number +*/ +static OSErr GetIconFromDesktopFile(ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle *iconHandle) +{ + OSErr error; + short realVRefNum; + Str255 desktopName; + short savedResFile; + short dfRefNum; + BNDLRecHandle theBndl = NULL; + BundleTypePtr theBundleType; + short iconLocalID; + short iconRsrcID; + OSType iconRsrcType; + Handle returnIconHandle; + char bndlState; + + *iconHandle = NULL; + + error = DetermineVRefNum(volName, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = GetDesktopFileName(realVRefNum, desktopName); + if ( error == noErr ) + { + savedResFile = CurResFile(); + + /* + ** Open the 'Desktop' file in the root directory. (because + ** opening the resource file could preload unwanted resources, + ** bracket the call with SetResLoad(s)) + */ + SetResLoad(false); + dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm); + SetResLoad(true); + + if ( dfRefNum != -1 ) + { + /* + ** Find the BNDL resource with the specified creator. + */ + error = FindBundleGivenCreator(fileCreator, &theBndl); + if ( error == noErr ) + { + /* Lock the BNDL resource so it won't be purged when other resources are loaded */ + bndlState = HGetState((Handle)theBndl); + HLock((Handle)theBndl); + + /* Find the 'FREF' BundleType record in the BNDL resource. */ + error = FindTypeInBundle(kFREFResType, theBndl, &theBundleType); + if ( error == noErr ) + { + /* Find the local ID in the 'FREF' resource with the specified fileType */ + error = GetLocalIDFromFREF(theBundleType, fileType, &iconLocalID); + if ( error == noErr ) + { + /* Find the 'ICN#' BundleType record in the BNDL resource. */ + error = FindTypeInBundle(kIconFamResType, theBndl, &theBundleType); + if ( error == noErr ) + { + /* Find the icon's resource ID in the 'ICN#' BundleType record */ + error = GetIconRsrcIDFromLocalID(theBundleType, iconLocalID, &iconRsrcID); + if ( error == noErr ) + { + /* Map Desktop Manager icon type to resource type */ + iconRsrcType = DTIconToResIcon(iconType); + + if ( iconRsrcType != (OSType)0 ) + { + /* Load the icon */ + returnIconHandle = Get1Resource(iconRsrcType, iconRsrcID); + if ( returnIconHandle != NULL ) + { + /* Copy the resource handle, and return the copy */ + HandToHand(&returnIconHandle); + if ( MemError() == noErr ) + { + *iconHandle = returnIconHandle; + } + else + { + error = afpItemNotFound; + } + } + else + { + error = afpItemNotFound; + } + } + } + } + } + } + /* Restore the state of the BNDL resource */ + HSetState((Handle)theBndl, bndlState); + } + /* Restore the resource chain and close the Desktop file */ + UseResFile(savedResFile); + CloseResFile(dfRefNum); + } + else + { + error = ResError(); /* could not open Desktop file */ + } + } + if ( (error != noErr) && (error != memFullErr) ) + { + error = afpItemNotFound; /* force an error we should return */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTGetIcon(ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle *iconHandle) +{ + OSErr error; + DTPBRec pb; + short dtRefNum; + Boolean newDTDatabase; + Size bufferSize; + + *iconHandle = NULL; + error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + /* there was a desktop database and it's now open */ + + if ( !newDTDatabase ) /* don't bother to look in a new (empty) database */ + { + /* get the buffer size for the requested icon type */ + switch ( iconType ) + { + case kLargeIcon: + bufferSize = kLargeIconSize; + break; + case kLarge4BitIcon: + bufferSize = kLarge4BitIconSize; + break; + case kLarge8BitIcon: + bufferSize = kLarge8BitIconSize; + break; + case kSmallIcon: + bufferSize = kSmallIconSize; + break; + case kSmall4BitIcon: + bufferSize = kSmall4BitIconSize; + break; + case kSmall8BitIcon: + bufferSize = kSmall8BitIconSize; + break; + default: + iconType = 0; + bufferSize = 0; + break; + } + if ( bufferSize != 0 ) + { + *iconHandle = NewHandle(bufferSize); + if ( *iconHandle != NULL ) + { + HLock(*iconHandle); + + pb.ioDTRefNum = dtRefNum; + pb.ioTagInfo = 0; + pb.ioDTBuffer = **iconHandle; + pb.ioDTReqCount = bufferSize; + pb.ioIconType = iconType; + pb.ioFileCreator = fileCreator; + pb.ioFileType = fileType; + error = PBDTGetIconSync(&pb); + + HUnlock(*iconHandle); + + if ( error != noErr ) + { + DisposeHandle(*iconHandle); /* dispose of the allocated memory */ + *iconHandle = NULL; + } + } + else + { + error = memFullErr; /* handle could not be allocated */ + } + } + else + { + error = paramErr; /* unknown icon type requested */ + } + } + else + { + error = afpItemNotFound; /* the desktop database was empty - nothing to return */ + } + } + else + { + /* There is no desktop database - try the Desktop file */ + + error = GetIconFromDesktopFile(volName, vRefNum, iconType, + fileCreator, fileType, iconHandle); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTSetComment(short vRefNum, + long dirID, + ConstStr255Param name, + ConstStr255Param comment) +{ + DTPBRec pb; + OSErr error; + short dtRefNum; + Boolean newDTDatabase; + + error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + pb.ioDTRefNum = dtRefNum; + pb.ioNamePtr = (StringPtr)name; + pb.ioDirID = dirID; + pb.ioDTBuffer = (Ptr)&comment[1]; + /* Truncate the comment to 200 characters just in case */ + /* some file system doesn't range check */ + if ( comment[0] <= 200 ) + { + pb.ioDTReqCount = comment[0]; + } + else + { + pb.ioDTReqCount = 200; + } + error = PBDTSetCommentSync(&pb); + } + return (error); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTSetComment(const FSSpec *spec, + ConstStr255Param comment) +{ + return (DTSetComment(spec->vRefNum, spec->parID, spec->name, comment)); +} + +/*****************************************************************************/ + +/* +** GetCommentID +** +** Get the comment ID number for the Desktop file's 'FCMT' resource ID from +** the file or folders fdComment (frComment) field. +*/ +static OSErr GetCommentID(short vRefNum, + long dirID, + ConstStr255Param name, + short *commentID) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + *commentID = pb.hFileInfo.ioFlXFndrInfo.fdComment; + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetCommentFromDesktopFile +** +** Get a file or directory's Finder comment field (if any) from the +** Desktop file's 'FCMT' resources. +*/ +static OSErr GetCommentFromDesktopFile(short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment) +{ + OSErr error; + short commentID; + short realVRefNum; + Str255 desktopName; + short savedResFile; + short dfRefNum; + StringHandle commentHandle; + + /* Get the comment ID number */ + error = GetCommentID(vRefNum, dirID, name, &commentID); + if ( error == noErr ) + { + if ( commentID != 0 ) /* commentID == 0 means there's no comment */ + { + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = GetDesktopFileName(realVRefNum, desktopName); + if ( error == noErr ) + { + savedResFile = CurResFile(); + /* + ** Open the 'Desktop' file in the root directory. (because + ** opening the resource file could preload unwanted resources, + ** bracket the call with SetResLoad(s)) + */ + SetResLoad(false); + dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm); + SetResLoad(true); + + if ( dfRefNum != -1) + { + /* Get the comment resource */ + commentHandle = (StringHandle)Get1Resource(kFCMTResType,commentID); + if ( commentHandle != NULL ) + { + if ( InlineGetHandleSize((Handle)commentHandle) > 0 ) + { + BlockMoveData(*commentHandle, comment, *commentHandle[0] + 1); + } + else + { + error = afpItemNotFound; /* no comment available */ + } + } + else + { + error = afpItemNotFound; /* no comment available */ + } + + /* restore the resource chain and close the Desktop file */ + UseResFile(savedResFile); + CloseResFile(dfRefNum); + } + else + { + error = afpItemNotFound; + } + } + else + { + error = afpItemNotFound; + } + } + } + else + { + error = afpItemNotFound; /* no comment available */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTGetComment(short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment) +{ + DTPBRec pb; + OSErr error; + short dtRefNum; + Boolean newDTDatabase; + + if (comment != NULL) + { + comment[0] = 0; /* return nothing by default */ + + /* attempt to open the desktop database */ + error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + /* There was a desktop database and it's now open */ + + if ( !newDTDatabase ) + { + pb.ioDTRefNum = dtRefNum; + pb.ioNamePtr = (StringPtr)name; + pb.ioDirID = dirID; + pb.ioDTBuffer = (Ptr)&comment[1]; + /* + ** IMPORTANT NOTE #1: Inside Macintosh says that comments + ** are up to 200 characters. While that may be correct for + ** the HFS file system's Desktop Manager, other file + ** systems (such as Apple Photo Access) return up to + ** 255 characters. Make sure the comment buffer is a Str255 + ** or you'll regret it. + ** + ** IMPORTANT NOTE #2: Although Inside Macintosh doesn't + ** mention it, ioDTReqCount is a input field to + ** PBDTGetCommentSync. Some file systems (like HFS) ignore + ** ioDTReqCount and always return the full comment -- + ** others (like AppleShare) respect ioDTReqCount and only + ** return up to ioDTReqCount characters of the comment. + */ + pb.ioDTReqCount = sizeof(Str255) - 1; + error = PBDTGetCommentSync(&pb); + if (error == noErr) + { + comment[0] = (unsigned char)pb.ioDTActCount; + } + } + } + else + { + /* There is no desktop database - try the Desktop file */ + error = GetCommentFromDesktopFile(vRefNum, dirID, name, comment); + if ( error != noErr ) + { + error = afpItemNotFound; /* return an expected error */ + } + } + } + else + { + error = paramErr; + } + + return (error); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTGetComment(const FSSpec *spec, + Str255 comment) +{ + return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment)); +} + +/*****************************************************************************/ + +pascal OSErr DTCopyComment(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName) +/* The destination volume must support the Desktop Manager for this to work */ +{ + OSErr error; + Str255 comment; + + error = DTGetComment(srcVRefNum, srcDirID, srcName, comment); + if ( (error == noErr) && (comment[0] > 0) ) + { + error = DTSetComment(dstVRefNum, dstDirID, dstName, comment); + } + return (error); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTCopyComment(const FSSpec *srcSpec, + const FSSpec *dstSpec) +/* The destination volume must support the Desktop Manager for this to work */ +{ + return (DTCopyComment(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name)); +} + +/*****************************************************************************/ diff --git a/src/mac/morefile/MoreDesk.h b/src/mac/morefile/MoreDesk.h new file mode 100644 index 0000000000..be2bfd341d --- /dev/null +++ b/src/mac/morefile/MoreDesk.h @@ -0,0 +1,541 @@ +/* +** Apple Macintosh Developer Technical Support +** +** A collection of useful high-level Desktop Manager routines. +** If the Desktop Manager isn't available, use the Desktop file +** for 'read' operations. +** +** We do more because we can... +** +** by Jim Luther and Nitin Ganatra, Apple Developer Technical Support Emeriti +** +** File: MoreDesktopMgr.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __MOREDESKTOPMGR__ +#define __MOREDESKTOPMGR__ + +#include +#include + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +pascal OSErr DTOpen(ConstStr255Param volName, + short vRefNum, + short *dtRefNum, + Boolean *newDTDatabase); +/* ¦ Open a volume's desktop database and return the desktop database refNum. + The DTOpen function opens a volume's desktop database. It returns + the reference number of the desktop database and indicates if the + desktop database was created as a result of this call (if it was created, + then it is empty). + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + dtRefNum output: The reference number of Desktop Manager's + desktop database on the specified volume. + newDTDatabase output: true if the desktop database was created as a + result of this call and thus empty. + false if the desktop database was already created, + or if it could not be determined if it was already + created. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete +*/ + +/*****************************************************************************/ + +pascal OSErr DTXGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + short *applVRefNum, + long *applParID, + Str255 applName); +/* ¦ Find an application on a volume that can open a file with a given creator. + The DTXGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails and searchCatalog is true, then it tries to find an application + with the specified creator using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + searchCatalog input: If true, search the catalog for the application + if it isn't found in the desktop database. + applVRefNum output: The volume reference number of the volume the + application is on. + applParID output: The parent directory ID of the application. + applName output: The name of the application. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTGetAPPL +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDTXGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + FSSpec *spec); +/* ¦ Find an application on a volume that can open a file with a given creator. + The FSpDTXGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails and searchCatalog is true, then it tries to find an application + with the specified creator using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + searchCatalog input: If true, search the catalog for the application + if it isn't found in the desktop database. + spec output: FSSpec record containing the application name and + location. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTGetAPPL +*/ + +/*****************************************************************************/ + +pascal OSErr DTGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + short *applVRefNum, + long *applParID, + Str255 applName); +/* ¦ Find an application on a volume that can open a file with a given creator. + The DTGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails, then it tries to find an application with the specified creator + using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + applVRefNum output: The volume reference number of the volume the + application is on. + applParID output: The parent directory ID of the application. + applName output: The name of the application. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTGetAPPL +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDTGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + FSSpec *spec); +/* ¦ Find an application on a volume that can open a file with a given creator. + The FSpDTGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails, then it tries to find an application with the specified creator + using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + spec output: FSSpec record containing the application name and + location. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTGetAPPL +*/ + +/*****************************************************************************/ + +pascal OSErr DTGetIcon(ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle *iconHandle); +/* ¦ Get an icon from the desktop database or Desktop file. + The DTGetIcon function retrieves the specified icon and returns it in + a newly created handle. The icon is retrieves from the Desktop Manager + or if the Desktop Manager is not available, from the Finder's Desktop + file. Your program is responsible for disposing of the handle when it is + done using the icon. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + iconType input: The icon type as defined in Files.h. Valid values are: + kLargeIcon + kLarge4BitIcon + kLarge8BitIcon + kSmallIcon + kSmall4BitIcon + kSmall8BitIcon + fileCreator input: The icon's creator type. + fileType input: The icon's file type. + iconHandle output: A Handle containing the newly created icon. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 Volume doesn't support this function + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + memFullErr -108 iconHandle could not be allocated + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found +*/ + +/*****************************************************************************/ + +pascal OSErr DTSetComment(short vRefNum, + long dirID, + ConstStr255Param name, + ConstStr255Param comment); +/* ¦ Set a file or directory's Finder comment field. + The DTSetComment function sets a file or directory's Finder comment + field. The volume must support the Desktop Manager because you only + have read access to the Desktop file. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + comment input: The comment to add. Comments are limited to 200 characters; + longer comments are truncated. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + paramErr -50 Volume doesn't support this function + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + rfNumErr Ð51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, FSpDTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDTSetComment(const FSSpec *spec, + ConstStr255Param comment); +/* ¦ Set a file or directory's Finder comment field. + The FSpDTSetComment function sets a file or directory's Finder comment + field. The volume must support the Desktop Manager because you only + have read access to the Desktop file. + + spec input: An FSSpec record specifying the file or directory. + comment input: The comment to add. Comments are limited to 200 characters; + longer comments are truncated. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + rfNumErr Ð51 Reference number invalid + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, DTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +pascal OSErr DTGetComment(short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment); +/* ¦ Get a file or directory's Finder comment field (if any). + The DTGetComment function gets a file or directory's Finder comment + field (if any) from the Desktop Manager or if the Desktop Manager is + not available, from the Finder's Desktop file. + + IMPORTANT NOTE: Inside Macintosh says that comments are up to + 200 characters. While that may be correct for the HFS file system's + Desktop Manager, other file systems (such as Apple Photo Access) return + up to 255 characters. Make sure the comment buffer is a Str255 or you'll + regret it. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + comment output: A Str255 where the comment is to be returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, DTSetComment, FSpDTSetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDTGetComment(const FSSpec *spec, + Str255 comment); +/* ¦ Get a file or directory's Finder comment field (if any). + The FSpDTGetComment function gets a file or directory's Finder comment + field (if any) from the Desktop Manager or if the Desktop Manager is + not available, from the Finder's Desktop file. + + IMPORTANT NOTE: Inside Macintosh says that comments are up to + 200 characters. While that may be correct for the HFS file system's + Desktop Manager, other file systems (such as Apple Photo Access) return + up to 255 characters. Make sure the comment buffer is a Str255 or you'll + regret it. + + spec input: An FSSpec record specifying the file or directory. + comment output: A Str255 where the comment is to be returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, DTSetComment, FSpDTSetComment, + DTGetComment +*/ + +/*****************************************************************************/ + +pascal OSErr DTCopyComment(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName); +/* ¦ Copy the file or folder comment from the source to the destination object. + The DTCopyComment function copies the file or folder comment from the + source to the destination object. The destination volume must support + the Desktop Manager because you only have read access to the Desktop file. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Pointer to source object name, or nil when srcDirID + specifies a directory that's the object. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination object name, or nil when + dstDirID specifies a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTCopyComment, DTSetComment, FSpDTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +pascal OSErr FSpDTCopyComment(const FSSpec *srcSpec, + const FSSpec *dstSpec); +/* ¦ Copy the desktop database comment from the source to the destination object. + The FSpDTCopyComment function copies the desktop database comment from + the source to the destination object. Both the source and the + destination volumes must support the Desktop Manager. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination object. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTCopyComment, DTSetComment, FSpDTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __MOREDESKTOPMGR__ */ diff --git a/src/mac/morefile/MoreExtr.cpp b/src/mac/morefile/MoreExtr.cpp new file mode 100644 index 0000000000..cc64cd6a15 --- /dev/null +++ b/src/mac/morefile/MoreExtr.cpp @@ -0,0 +1,3228 @@ +/* +** Apple Macintosh Developer Technical Support +** +** A collection of useful high-level File Manager routines. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: MoreFilesExtras.c +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFile.h" +#include "MoreExtr.h" +#include "MoreDesk.h" +#include "FSpCompa.h" + +/*****************************************************************************/ + +/* local data structures */ + +/* The DeleteEnumGlobals structure is used to minimize the amount of +** stack space used when recursively calling DeleteLevel and to hold +** global information that might be needed at any time. */ + +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +struct DeleteEnumGlobals +{ + OSErr error; /* temporary holder of results - saves 2 bytes of stack each level */ + Str63 itemName; /* the name of the current item */ + UniversalFMPB myPB; /* the parameter block used for PBGetCatInfo calls */ +}; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + +typedef struct DeleteEnumGlobals DeleteEnumGlobals; +typedef DeleteEnumGlobals *DeleteEnumGlobalsPtr; + +/*****************************************************************************/ + +pascal void TruncPString(StringPtr destination, + ConstStr255Param source, + short maxLength) +{ + short charType; + + if ( source != NULL && destination != NULL ) /* don't do anything stupid */ + { + if ( source[0] > maxLength ) + { + /* Make sure the string isn't truncated in the middle of */ + /* a multi-byte character. */ + while (maxLength != 0) + { + charType = CharByte((Ptr)&source[1], maxLength); + if ( (charType == smSingleByte) || (charType == smLastByte) ) + break; /* source[maxLength] is now a valid last character */ + --maxLength; + } + } + else + { + maxLength = source[0]; + } + /* Set the destination string length */ + destination[0] = maxLength; + /* and copy maxLength characters (if needed) */ + if ( source != destination ) + { + while ( maxLength != 0 ) + { + destination[maxLength] = source[maxLength]; + --maxLength; + } + } + } +} + +/*****************************************************************************/ + +pascal Ptr GetTempBuffer(long buffReqSize, + long *buffActSize) +{ + enum + { + kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */ + }; + Ptr tempPtr; + + /* Make request a multiple of 1024 bytes */ + buffReqSize = buffReqSize & 0xfffffc00; + + if ( buffReqSize < 0x00000400 ) + { + /* Request was smaller than 1024 bytes - make it 1024 */ + buffReqSize = 0x00000400; + } + + /* Attempt to allocate the memory */ + tempPtr = NewPtr(buffReqSize); + + /* If request failed, go to backup plan */ + if ( (tempPtr == NULL) && (buffReqSize > 0x00000400) ) + { + /* + ** Try to get largest 1024-byte block available + ** leaving some slop for the toolbox if possible + */ + long freeMemory = (FreeMem() - kSlopMemory) & 0xfffffc00; + + buffReqSize = MaxBlock() & 0xfffffc00; + + if ( buffReqSize > freeMemory ) + { + buffReqSize = freeMemory; + } + + if ( buffReqSize == 0 ) + { + buffReqSize = 0x00000400; + } + + tempPtr = NewPtr(buffReqSize); + } + + /* Return bytes allocated */ + if ( tempPtr != NULL ) + { + *buffActSize = buffReqSize; + } + else + { + *buffActSize = 0; + } + + return ( tempPtr ); +} + +/*****************************************************************************/ + +/* +** GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync +** in cases where the returned volume name is not needed by the caller. +** The pathname and vRefNum parameters are not touched, and the pb +** parameter is initialized by PBHGetVInfoSync except that ioNamePtr in +** the parameter block is always returned as NULL (since it might point +** to the local tempPathname). +** +** I noticed using this code in several places, so here it is once. +** This reduces the code size of MoreFiles. +*/ +pascal OSErr GetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + HParmBlkPtr pb) +{ + Str255 tempPathname; + OSErr error; + + /* Make sure pb parameter is not NULL */ + if ( pb != NULL ) + { + pb->volumeParam.ioVRefNum = vRefNum; + if ( pathname == NULL ) + { + pb->volumeParam.ioNamePtr = NULL; + pb->volumeParam.ioVolIndex = 0; /* use ioVRefNum only */ + } + else + { + BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */ + pb->volumeParam.ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */ + pb->volumeParam.ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */ + } + error = PBHGetVInfoSync(pb); + pb->volumeParam.ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */ + } + else + { + error = paramErr; + } + return ( error ); +} + +/*****************************************************************************/ + +/* +** XGetVolumeInfoNoName uses pathname and vRefNum to call PBXGetVolInfoSync +** in cases where the returned volume name is not needed by the caller. +** The pathname and vRefNum parameters are not touched, and the pb +** parameter is initialized by PBXGetVolInfoSync except that ioNamePtr in +** the parameter block is always returned as NULL (since it might point +** to the local tempPathname). +*/ +pascal OSErr XGetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + XVolumeParamPtr pb) +{ + Str255 tempPathname; + long response; + OSErr error; + + /* Make sure pb parameter is not NULL */ + if ( pb != NULL ) + { + pb->ioVRefNum = vRefNum; + pb->ioXVersion = 0; /* this XVolumeParam version (0) */ + if ( pathname == NULL ) + { + pb->ioNamePtr = NULL; + pb->ioVolIndex = 0; /* use ioVRefNum only */ + } + else + { + BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */ + pb->ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */ + pb->ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */ + } +#if !__MACOSSEVENFIVEONEORLATER + /* Is PBXGetVolInfo available? */ + if ( ( Gestalt(gestaltFSAttr, &response) != noErr ) || ((response & (1L << gestaltFSSupports2TBVols)) == 0) ) + { + /* No, fall back on PBHGetVInfo */ + error = PBHGetVInfoSync((HParmBlkPtr)pb); + if ( error == noErr ) + { + /* calculate the ioVTotalBytes and ioVFreeBytes fields */ + pb->ioVTotalBytes.hi = 0; + pb->ioVTotalBytes.lo = pb->ioVNmAlBlks * pb->ioVAlBlkSiz; /* calculated total number of bytes on volume */ + pb->ioVFreeBytes.hi = 0; + pb->ioVFreeBytes.lo = pb->ioVFrBlk * pb->ioVAlBlkSiz; /* calculated number of free bytes on volume */ + } + } + else +#endif // !__MACOSSEVENFIVEONEORLATER + { + /* Yes, so use it */ + error = PBXGetVolInfoSync(pb); + } + pb->ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */ + } + else + { + error = paramErr; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetCatInfoNoName(short vRefNum, + long dirID, + ConstStr255Param name, + CInfoPBPtr pb) +{ + Str31 tempName; + OSErr error; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb->dirInfo.ioNamePtr = tempName; + pb->dirInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb->dirInfo.ioNamePtr = (StringPtr)name; + pb->dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb->dirInfo.ioVRefNum = vRefNum; + pb->dirInfo.ioDrDirID = dirID; + error = PBGetCatInfoSync(pb); + pb->dirInfo.ioNamePtr = NULL; + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DetermineVRefNum(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + *realVRefNum = pb.volumeParam.ioVRefNum; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + unsigned long *freeBytes, + unsigned long *totalBytes) +{ + HParamBlockRec pb; + unsigned long allocationBlockSize; + unsigned short numAllocationBlocks; + unsigned short numFreeBlocks; + VCB *theVCB; + Boolean vcbFound; + OSErr result; + + /* Use the File Manager to get the real vRefNum */ + pb.volumeParam.ioVRefNum = volReference; + pb.volumeParam.ioNamePtr = volName; + pb.volumeParam.ioVolIndex = 0; /* use ioVRefNum only, return volume name */ + result = PBHGetVInfoSync(&pb); + + if ( result == noErr ) + { + /* The volume name was returned in volName (if not NULL) and */ + /* we have the volume's vRefNum and allocation block size */ + *vRefNum = pb.volumeParam.ioVRefNum; + allocationBlockSize = (unsigned long)pb.volumeParam.ioVAlBlkSiz; + + /* System 7.5 (and beyond) pins the number of allocation blocks and */ + /* the number of free allocation blocks returned by PBHGetVInfo to */ + /* a value so that when multiplied by the allocation block size, */ + /* the volume will look like it has $7fffffff bytes or less. This */ + /* was done so older applications that use signed math or that use */ + /* the GetVInfo function (which uses signed math) will continue to work. */ + /* However, the unpinned numbers (which we want) are always available */ + /* in the volume's VCB so we'll get those values from the VCB if possible. */ + + /* Find the volume's VCB */ + vcbFound = false; + theVCB = (VCB *)(GetVCBQHdr()->qHead); + while ( (theVCB != NULL) && !vcbFound ) + { + /* Check VCB signature before using VCB. Don't have to check for */ + /* MFS (0xd2d7) because they can't get big enough to be pinned */ + if ( theVCB->vcbSigWord == 0x4244 ) + { + if ( theVCB->vcbVRefNum == *vRefNum ) + { + vcbFound = true; + } + } + + if ( !vcbFound ) + { + theVCB = (VCB *)(theVCB->qLink); + } + } + + if ( theVCB != NULL ) + { + /* Found a VCB we can use. Get the un-pinned number of allocation blocks */ + /* and the number of free blocks from the VCB. */ + numAllocationBlocks = (unsigned short)theVCB->vcbNmAlBlks; + numFreeBlocks = (unsigned short)theVCB->vcbFreeBks; + } + else + { + /* Didn't find a VCB we can use. Return the number of allocation blocks */ + /* and the number of free blocks returned by PBHGetVInfoSync. */ + numAllocationBlocks = (unsigned short)pb.volumeParam.ioVNmAlBlks; + numFreeBlocks = (unsigned short)pb.volumeParam.ioVFrBlk; + } + + /* Now, calculate freeBytes and totalBytes using unsigned values */ + *freeBytes = numFreeBlocks * allocationBlockSize; + *totalBytes = numAllocationBlocks * allocationBlockSize; + } + + return ( result ); +} + +/*****************************************************************************/ + +/* +** PBXGetVolInfoSync is the glue code needed to make PBXGetVolInfoSync +** File Manager requests from CFM-based programs. At some point, Apple +** will get around to adding this to the standard libraries you link with +** and you'll get a duplicate symbol link error. At that time, just delete +** this code (or comment it out). +** +** Non-CFM 68K programs don't needs this glue (and won't get it) because +** they instead use the inline assembly glue found in the Files.h interface +** file. +*/ + +#if __WANTPASCALELIMINATION +#undef pascal +#endif + +#if GENERATINGCFM +pascal OSErr PBXGetVolInfoSync(XVolumeParamPtr paramBlock) +{ + enum + { + kXGetVolInfoSelector = 0x0012, /* Selector for XGetVolInfo */ + + uppFSDispatchProcInfo = kRegisterBased + | REGISTER_RESULT_LOCATION(kRegisterD0) + | RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) + | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(long))) /* trap word */ + | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(long))) /* selector */ + | REGISTER_ROUTINE_PARAMETER(3, kRegisterA0, SIZE_CODE(sizeof(XVolumeParamPtr))) + }; + + return ( CallOSTrapUniversalProc(NGetTrapAddress(_FSDispatch, OSTrap), + uppFSDispatchProcInfo, + _FSDispatch, + kXGetVolInfoSelector, + paramBlock) ); +} +#endif + +#if __WANTPASCALELIMINATION +#define pascal +#endif + +/*****************************************************************************/ + +pascal OSErr XGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + UnsignedWide *freeBytes, + UnsignedWide *totalBytes) +{ + OSErr result; + long response; + XVolumeParam pb; + + /* See if large volume support is available */ + if ( ( Gestalt(gestaltFSAttr, &response) == noErr ) && ((response & (1L << gestaltFSSupports2TBVols)) != 0) ) + { + /* Large volume support is available */ + pb.ioVRefNum = volReference; + pb.ioNamePtr = volName; + pb.ioXVersion = 0; /* this XVolumeParam version (0) */ + pb.ioVolIndex = 0; /* use ioVRefNum only, return volume name */ + result = PBXGetVolInfoSync(&pb); + if ( result == noErr ) + { + /* The volume name was returned in volName (if not NULL) and */ + /* we have the volume's vRefNum and allocation block size */ + *vRefNum = pb.ioVRefNum; + + /* return the freeBytes and totalBytes */ + *totalBytes = pb.ioVTotalBytes; + *freeBytes = pb.ioVFreeBytes; + } + } + else + { + /* No large volume support */ + + /* Use HGetVInfo to get the results */ + result = HGetVInfo(volReference, volName, vRefNum, &freeBytes->lo, &totalBytes->lo); + if ( result == noErr ) + { + /* zero the high longs of totalBytes and freeBytes */ + totalBytes->hi = 0; + freeBytes->hi = 0; + } + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr CheckVolLock(ConstStr255Param pathname, + short vRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + if ( (pb.volumeParam.ioVAtrb & 0x0080) != 0 ) + { + error = wPrErr; /* volume locked by hardware */ + } + else if ( (pb.volumeParam.ioVAtrb & 0x8000) != 0 ) + { + error = vLckdErr; /* volume locked by software */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetDriverName(short driverRefNum, + Str255 driverName) +{ + OSErr result; + DCtlHandle theDctl; + DRVRHeaderPtr dHeaderPtr; + + theDctl = GetDCtlEntry(driverRefNum); + if ( theDctl != NULL ) + { + if ( (**theDctl).dCtlFlags & 0x40 ) + { + /* dctlDriver is handle - dereference */ + dHeaderPtr = *((DRVRHeaderHandle)(**theDctl).dCtlDriver); + } + else + { + /* dctlDriver is pointer */ + dHeaderPtr = (DRVRHeaderPtr)(**theDctl).dCtlDriver; + } + BlockMoveData((*dHeaderPtr).drvrName, driverName, (*dHeaderPtr).drvrName[0] + 1); + result = noErr; + } + else + { + driverName[0] = 0; + result = badUnitErr; /* bad reference number */ + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FindDrive(ConstStr255Param pathname, + short vRefNum, + DrvQElPtr *driveQElementPtr) +{ + OSErr result; + HParamBlockRec hPB; + short driveNumber; + + *driveQElementPtr = NULL; + + /* First, use GetVolumeInfoNoName to determine the volume */ + result = GetVolumeInfoNoName(pathname, vRefNum, &hPB); + if ( result == noErr ) + { + /* + ** The volume can be either online, offline, or ejected. What we find in + ** ioVDrvInfo and ioVDRefNum will tell us which it is. + ** See Inside Macintosh: Files page 2-80 and the Technical Note + ** "FL 34 - VCBs and Drive Numbers : The Real Story" + ** Where we get the drive number depends on the state of the volume. + */ + if ( hPB.volumeParam.ioVDrvInfo != 0 ) + { + /* The volume is online and not ejected */ + /* Get the drive number */ + driveNumber = hPB.volumeParam.ioVDrvInfo; + } + else + { + /* The volume's is either offline or ejected */ + /* in either case, the volume is NOT online */ + + /* Is it ejected or just offline? */ + if ( hPB.volumeParam.ioVDRefNum > 0 ) + { + /* It's ejected, the drive number is ioVDRefNum */ + driveNumber = hPB.volumeParam.ioVDRefNum; + } + else + { + /* It's offline, the drive number is the negative of ioVDRefNum */ + driveNumber = (short)-hPB.volumeParam.ioVDRefNum; + } + } + + /* Get pointer to first element in drive queue */ + *driveQElementPtr = (DrvQElPtr)(GetDrvQHdr()->qHead); + + /* Search for a matching drive number */ + while ( (*driveQElementPtr != NULL) && ((*driveQElementPtr)->dQDrive != driveNumber) ) + { + *driveQElementPtr = (DrvQElPtr)(*driveQElementPtr)->qLink; + } + + if ( *driveQElementPtr == NULL ) + { + /* This should never happen since every volume must have a drive, but... */ + result = nsDrvErr; + } + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr GetDiskBlocks(ConstStr255Param pathname, + short vRefNum, + unsigned long *numBlocks) +{ + /* Various constants for GetDiskBlocks() */ + enum + { + /* return format list status code */ + kFmtLstCode = 6, + + /* reference number of .SONY driver */ + kSonyRefNum = 0xfffb, + + /* values returned by DriveStatus in DrvSts.twoSideFmt */ + kSingleSided = 0, + kDoubleSided = -1, + kSingleSidedSize = 800, /* 400K */ + kDoubleSidedSize = 1600, /* 800K */ + + /* values in DrvQEl.qType */ + kWordDrvSiz = 0, + kLongDrvSiz = 1, + + /* more than enough formatListRecords */ + kMaxFormatListRecs = 16 + }; + + DrvQElPtr driveQElementPtr; + unsigned long blocks; + ParamBlockRec pb; + FormatListRec formatListRecords[kMaxFormatListRecs]; + DrvSts status; + short formatListRecIndex; + OSErr result; + + blocks = 0; + + /* Find the drive queue element for this volume */ + result = FindDrive(pathname, vRefNum, &driveQElementPtr); + + /* + ** Make sure this is a real driver (dQRefNum < 0). + ** AOCE's Mail Enclosures volume uses 0 for dQRefNum which will cause + ** problems if you try to use it as a driver refNum. + */ + if ( (result == noErr) && (driveQElementPtr->dQRefNum >= 0) ) + { + result = paramErr; + } + else + { + /* Attempt to get the drive's format list. */ + /* (see the Technical Note "What Your Sony Drives For You") */ + + pb.cntrlParam.ioVRefNum = driveQElementPtr->dQDrive; + pb.cntrlParam.ioCRefNum = driveQElementPtr->dQRefNum; + pb.cntrlParam.csCode = kFmtLstCode; + pb.cntrlParam.csParam[0] = kMaxFormatListRecs; + *(long *)&pb.cntrlParam.csParam[1] = (long)&formatListRecords[0]; + + result = PBStatusSync(&pb); + + if ( result == noErr ) + { + /* The drive supports ReturnFormatList status call. */ + + /* Get the current disk's size. */ + for( formatListRecIndex = 0; + formatListRecIndex < pb.cntrlParam.csParam[0]; + ++formatListRecIndex ) + { + if ( (formatListRecords[formatListRecIndex].formatFlags & + diCIFmtFlagsCurrentMask) != 0 ) + { + blocks = formatListRecords[formatListRecIndex].volSize; + } + } + if ( blocks == 0 ) + { + /* This should never happen */ + result = paramErr; + } + } + else if ( driveQElementPtr->dQRefNum == (short)kSonyRefNum ) + { + /* The drive is a non-SuperDrive floppy which only supports 400K and 800K disks */ + + result = DriveStatus(driveQElementPtr->dQDrive, &status); + if ( result == noErr ) + { + switch ( status.twoSideFmt ) + { + case kSingleSided: + blocks = kSingleSidedSize; + break; + case kDoubleSided: + blocks = kDoubleSidedSize; + break; + default: + /* This should never happen */ + result = paramErr; + break; + } + } + } + else + { + /* The drive is not a floppy and it doesn't support ReturnFormatList */ + /* so use the dQDrvSz field(s) */ + + result = noErr; /* reset result */ + switch ( driveQElementPtr->qType ) + { + case kWordDrvSiz: + blocks = driveQElementPtr->dQDrvSz; + break; + case kLongDrvSiz: + blocks = ((unsigned long)driveQElementPtr->dQDrvSz2 << 16) + + driveQElementPtr->dQDrvSz; + break; + default: + /* This should never happen */ + result = paramErr; + break; + } + } + } + + if ( result == noErr ) + { + *numBlocks = blocks; + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr GetVolFileSystemID(ConstStr255Param pathname, + short vRefNum, + short *fileSystemID) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + *fileSystemID = pb.volumeParam.ioVFSID; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetVolState(ConstStr255Param pathname, + short vRefNum, + Boolean *volumeOnline, + Boolean *volumeEjected, + Boolean *driveEjectable, + Boolean *driverWantsEject) +{ + HParamBlockRec pb; + short driveNumber; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + if ( pb.volumeParam.ioVDrvInfo != 0 ) + { + /* the volume is online and not ejected */ + *volumeOnline = true; + *volumeEjected = false; + + /* Get the drive number */ + driveNumber = pb.volumeParam.ioVDrvInfo; + } + else + { + /* the volume's is either offline or ejected */ + /* in either case, the volume is NOT online */ + *volumeOnline = false; + + /* Is it ejected? */ + *volumeEjected = pb.volumeParam.ioVDRefNum > 0; + + if ( *volumeEjected ) + { + /* If ejected, the drive number is ioVDRefNum */ + driveNumber = pb.volumeParam.ioVDRefNum; + } + else + { + /* If offline, the drive number is the negative of ioVDRefNum */ + driveNumber = (short)-pb.volumeParam.ioVDRefNum; + } + } + + { + DrvQElPtr drvQElem; + + /* Find the drive queue element by searching the drive queue */ + drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead); + while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNumber) ) + { + drvQElem = (DrvQElPtr)drvQElem->qLink; + } + + if ( drvQElem != NULL ) + { + /* + ** Each drive queue element is preceded by 4 flag bytes. + ** Byte 1 (the second flag byte) has bits that tell us if a + ** drive is ejectable and if its driver wants an eject call. + ** See Inside Macintosh: Files, page 2-85. + */ + { + Ptr flagBytePtr; + + /* point to byte 1 of the flag bytes */ + flagBytePtr = (Ptr)drvQElem; + flagBytePtr -= 3; + + /* + ** The drive is ejectable if flag byte 1 does not contain + ** 0x08 (nonejectable) or 0x48 (nonejectable, but wants eject call). + */ + + *driveEjectable = (*flagBytePtr != 0x08) && (*flagBytePtr != 0x48); + + /* + ** The driver wants an eject call if flag byte 1 does not contain + ** 0x08 (nonejectable). This may seem like a minor point, but some + ** disk drivers use the Eject request to flush their caches to disk + ** and you wouldn't want to skip that step after unmounting a volume. + */ + + *driverWantsEject = (*flagBytePtr != 0x08); + } + } + else + { + /* Didn't find the drive (this should never happen) */ + *driveEjectable = false; + *driverWantsEject = false; + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr UnmountAndEject(ConstStr255Param pathname, + short vRefNum) +{ + HParamBlockRec pb; + short driveNum; + Boolean ejected, wantsEject; + DrvQElPtr drvQElem; + OSErr error; + + error = GetVolumeInfoNoName(pathname, vRefNum, &pb); + if ( error == noErr ) + { + if ( pb.volumeParam.ioVDrvInfo != 0 ) + { + /* the volume is online and not ejected */ + ejected = false; + + /* Get the drive number */ + driveNum = pb.volumeParam.ioVDrvInfo; + } + else + { + /* the volume is ejected or offline */ + + /* Is it ejected? */ + ejected = pb.volumeParam.ioVDRefNum > 0; + + if ( ejected ) + { + /* If ejected, the drive number is ioVDRefNum */ + driveNum = pb.volumeParam.ioVDRefNum; + } + else + { + /* If offline, the drive number is the negative of ioVDRefNum */ + driveNum = (short)-pb.volumeParam.ioVDRefNum; + } + } + + /* find the drive queue element */ + drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead); + while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNum) ) + { + drvQElem = (DrvQElPtr)drvQElem->qLink; + } + + if ( drvQElem != NULL ) + { + /* does the drive want an eject call */ + wantsEject = (*((Ptr)((Ptr)drvQElem - 3)) != 8); + } + else + { + /* didn't find the drive!! */ + wantsEject = false; + } + + /* unmount the volume */ + pb.volumeParam.ioNamePtr = NULL; + /* ioVRefNum is already filled in from PBHGetVInfo */ + error = PBUnmountVol((ParmBlkPtr)&pb); + if ( error == noErr ) + { + if ( wantsEject && !ejected ) + { + /* eject the media from the drive if needed */ + pb.volumeParam.ioVRefNum = driveNum; + error = PBEject((ParmBlkPtr)&pb); + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr OnLine(FSSpecPtr volumes, + short reqVolCount, + short *actVolCount, + short *volIndex) +{ + HParamBlockRec pb; + OSErr error = noErr; + FSSpec *endVolArray; + + if ( *volIndex > 0 ) + { + *actVolCount = 0; + for ( endVolArray = volumes + reqVolCount; (volumes < endVolArray) && (error == noErr); ++volumes ) + { + pb.volumeParam.ioNamePtr = (StringPtr) & volumes->name; + pb.volumeParam.ioVolIndex = *volIndex; + error = PBHGetVInfoSync(&pb); + if ( error == noErr ) + { + volumes->parID = fsRtParID; /* the root directory's parent is 1 */ + volumes->vRefNum = pb.volumeParam.ioVRefNum; + ++*volIndex; + ++*actVolCount; + } + } + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr SetDefault(short newVRefNum, + long newDirID, + short *oldVRefNum, + long *oldDirID) +{ + OSErr error; + + /* Get the current default volume/directory. */ + error = HGetVol(NULL, oldVRefNum, oldDirID); + if ( error == noErr ) + { + /* Set the new default volume/directory */ + error = HSetVol(NULL, newVRefNum, newDirID); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RestoreDefault(short oldVRefNum, + long oldDirID) +{ + OSErr error; + short defaultVRefNum; + long defaultDirID; + long defaultProcID; + + /* Determine if the default volume was a wdRefNum. */ + error = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID); + if ( error == noErr ) + { + /* Restore the old default volume/directory, one way or the other. */ + if ( defaultDirID != fsRtDirID ) + { + /* oldVRefNum was a wdRefNum - use SetVol */ + error = SetVol(NULL, oldVRefNum); + } + else + { + /* oldVRefNum was a real vRefNum - use HSetVol */ + error = HSetVol(NULL, oldVRefNum, oldDirID); + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + DInfo *fndrInfo) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + if ( (pb.dirInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* it's a directory, return the DInfo */ + *fndrInfo = pb.dirInfo.ioDrUsrWds; + } + else + { + /* oops, a file was passed */ + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDInfo(const FSSpec *spec, + DInfo *fndrInfo) +{ + return ( GetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) ); +} + +/*****************************************************************************/ + +pascal OSErr SetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + const DInfo *fndrInfo) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.dirInfo.ioNamePtr = (StringPtr)name; + pb.dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.dirInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* it's a directory, set the DInfo */ + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + } + else + { + pb.dirInfo.ioDrDirID = dirID; + } + pb.dirInfo.ioDrUsrWds = *fndrInfo; + error = PBSetCatInfoSync(&pb); + } + else + { + /* oops, a file was passed */ + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetDInfo(const FSSpec *spec, + const DInfo *fndrInfo) +{ + return ( SetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirectoryID(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + *isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0; + if ( *isDirectory ) + { + *theDirID = pb.dirInfo.ioDrDirID; + } + else + { + *theDirID = pb.hFileInfo.ioFlParID; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDirectoryID(const FSSpec *spec, + long *theDirID, + Boolean *isDirectory) +{ + return ( GetDirectoryID(spec->vRefNum, spec->parID, spec->name, + theDirID, isDirectory) ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirName(short vRefNum, + long dirID, + Str31 name) +{ + CInfoPBRec pb; + OSErr error; + + if ( name != NULL ) + { + pb.dirInfo.ioNamePtr = name; + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetIOACUser(short vRefNum, + long dirID, + ConstStr255Param name, + SInt8 *ioACUser) +{ + CInfoPBRec pb; + OSErr error; + + /* Clear ioACUser before calling PBGetCatInfo since some file systems + ** don't bother to set or clear this field. If ioACUser isn't set by the + ** file system, then you'll get the zero value back (full access) which + ** is the access you have on volumes that don't support ioACUser. + */ + pb.dirInfo.ioACUser = 0; /* ioACUser used to be filler2 */ + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 ) + { + /* oops, a file was passed */ + error = dirNFErr; + } + else + { + *ioACUser = pb.dirInfo.ioACUser; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetIOACUser(const FSSpec *spec, + SInt8 *ioACUser) +{ + return ( GetIOACUser(spec->vRefNum, spec->parID, spec->name, ioACUser) ); +} + +/*****************************************************************************/ + +pascal OSErr GetParentID(short vRefNum, + long dirID, + ConstStr255Param name, + long *parID) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + short realVRefNum; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + /* + ** There's a bug in HFS where the wrong parent dir ID can be + ** returned if multiple separators are used at the end of a + ** pathname. For example, if the pathname: + ** 'volumeName:System Folder:Extensions::' + ** is passed, the directory ID of the Extensions folder is + ** returned in the ioFlParID field instead of fsRtDirID. Since + ** multiple separators at the end of a pathname always specifies + ** a directory, we only need to work-around cases where the + ** object is a directory and there are multiple separators at + ** the end of the name parameter. + */ + if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* Its a directory */ + + /* is there a pathname? */ + if ( pb.hFileInfo.ioNamePtr == name ) + { + /* could it contain multiple separators? */ + if ( name[0] >= 2 ) + { + /* does it contain multiple separators at the end? */ + if ( (name[name[0]] == ':') && (name[name[0] - 1] == ':') ) + { + /* OK, then do the extra stuff to get the correct parID */ + + /* Get the real vRefNum (this should not fail) */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + /* we don't need the parent's name, but add protect against File Sharing problem */ + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioVRefNum = realVRefNum; + /* pb.dirInfo.ioDrDirID already contains the */ + /* dirID of the directory object */ + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + /* now, pb.dirInfo.ioDrParID contains the correct parID */ + } + } + } + } + } + + if ( error == noErr ) + { + /* if no errors, then pb.hFileInfo.ioFlParID (pb.dirInfo.ioDrParID) */ + /* contains the parent ID */ + *parID = pb.hFileInfo.ioFlParID; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetFilenameFromPathname(ConstStr255Param pathname, + Str255 filename) +{ + short index; + short nameEnd; + OSErr error; + + /* default to no filename */ + filename[0] = 0; + + /* check for no pathname */ + if ( pathname != NULL ) + { + /* get string length */ + index = pathname[0]; + + /* check for empty string */ + if ( index != 0 ) + { + /* skip over last trailing colon (if any) */ + if ( pathname[index] == ':' ) + { + --index; + } + + /* save the end of the string */ + nameEnd = index; + + /* if pathname ends with multiple colons, then this pathname refers */ + /* to a directory, not a file */ + if ( pathname[index] != ':' ) + { + /* parse backwards until we find a colon or hit the beginning of the pathname */ + while ( (index != 0) && (pathname[index] != ':') ) + { + --index; + } + + /* if we parsed to the beginning of the pathname and the pathname ended */ + /* with a colon, then pathname is a full pathname to a volume, not a file */ + if ( (index != 0) || (pathname[pathname[0]] != ':') ) + { + /* get the filename and return noErr */ + filename[0] = (char)(nameEnd - index); + BlockMoveData(&pathname[index+1], &filename[1], nameEnd - index); + error = noErr; + } + else + { + /* pathname to a volume, not a file */ + error = notAFileErr; + } + } + else + { + /* directory, not a file */ + error = notAFileErr; + } + } + else + { + /* empty string isn't a file */ + error = notAFileErr; + } + } + else + { + /* NULL pathname isn't a file */ + error = notAFileErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetObjectLocation(short vRefNum, + long dirID, + ConstStr255Param pathname, + short *realVRefNum, + long *realParID, + Str255 realName, + Boolean *isDirectory) +{ + OSErr error; + CInfoPBRec pb; + Str255 tempPathname; + + /* clear results */ + *realVRefNum = 0; + *realParID = 0; + realName[0] = 0; + + /* + ** Get the real vRefNum + */ + error = DetermineVRefNum(pathname, vRefNum, realVRefNum); + if ( error == noErr ) + { + /* + ** Determine if the object already exists and if so, + ** get the real parent directory ID if it's a file + */ + + /* Protection against File Sharing problem */ + if ( (pathname == NULL) || (pathname[0] == 0) ) + { + tempPathname[0] = 0; + pb.hFileInfo.ioNamePtr = tempPathname; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)pathname; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + /* + ** The file system object is present and we have the file's real parID + */ + + /* Is it a directory or a file? */ + *isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0; + if ( *isDirectory ) + { + /* + ** It's a directory, get its name and parent dirID, and then we're done + */ + + pb.dirInfo.ioNamePtr = realName; + pb.dirInfo.ioVRefNum = *realVRefNum; + /* pb.dirInfo.ioDrDirID already contains the dirID of the directory object */ + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + + /* get the parent ID here, because the file system can return the */ + /* wrong parent ID from the last call. */ + *realParID = pb.dirInfo.ioDrParID; + } + else + { + /* + ** It's a file - use the parent directory ID from the last call + ** to GetCatInfoparse, get the file name, and then we're done + */ + *realParID = pb.hFileInfo.ioFlParID; + error = GetFilenameFromPathname(pathname, realName); + } + } + else if ( error == fnfErr ) + { + /* + ** The file system object is not present - see if its parent is present + */ + + /* + ** Parse to get the object name from end of pathname + */ + error = GetFilenameFromPathname(pathname, realName); + + /* if we can't get the object name from the end, we can't continue */ + if ( error == noErr ) + { + /* + ** What we want now is the pathname minus the object name + ** for example: + ** if pathname is 'vol:dir:file' tempPathname becomes 'vol:dir:' + ** if pathname is 'vol:dir:file:' tempPathname becomes 'vol:dir:' + ** if pathname is ':dir:file' tempPathname becomes ':dir:' + ** if pathname is ':dir:file:' tempPathname becomes ':dir:' + ** if pathname is ':file' tempPathname becomes ':' + ** if pathname is 'file or file:' tempPathname becomes '' + */ + + /* get a copy of the pathname */ + BlockMoveData(pathname, tempPathname, pathname[0] + 1); + + /* remove the object name */ + tempPathname[0] -= realName[0]; + /* and the trailing colon (if any) */ + if ( pathname[pathname[0]] == ':' ) + { + --tempPathname[0]; + } + + /* OK, now get the parent's directory ID */ + + /* Protection against File Sharing problem */ + pb.hFileInfo.ioNamePtr = (StringPtr)tempPathname; + if ( tempPathname[0] != 0 ) + { + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + else + { + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + *realParID = pb.dirInfo.ioDrDirID; + + *isDirectory = false; /* we don't know what the object is really going to be */ + } + + if ( error != noErr ) + { + error = dirNFErr; /* couldn't find parent directory */ + } + else + { + error = fnfErr; /* we found the parent, but not the file */ + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirItems(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean getFiles, + Boolean getDirectories, + FSSpecPtr items, + short reqItemCount, + short *actItemCount, + short *itemIndex) /* start with 1, then use what's returned */ +{ + CInfoPBRec pb; + OSErr error; + long theDirID; + Boolean isDirectory; + FSSpec *endItemsArray; + + if ( *itemIndex > 0 ) + { + /* NOTE: If I could be sure that the caller passed a real vRefNum and real directory */ + /* to this routine, I could rip out calls to DetermineVRefNum and GetDirectoryID and this */ + /* routine would be much faster because of the overhead of DetermineVRefNum and */ + /* GetDirectoryID and because GetDirectoryID blows away the directory index hint the Macintosh */ + /* file system keeps for indexed calls. I can't be sure, so for maximum throughput, */ + /* pass a big array of FSSpecs so you can get the directory's contents with few calls */ + /* to this routine. */ + + /* get the real volume reference number */ + error = DetermineVRefNum(name, vRefNum, &pb.hFileInfo.ioVRefNum); + if ( error == noErr ) + { + /* and the real directory ID of this directory (and make sure it IS a directory) */ + error = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory); + if ( error == noErr ) + { + if ( isDirectory ) + { + *actItemCount = 0; + endItemsArray = items + reqItemCount; + while ( (items < endItemsArray) && (error == noErr) ) + { + pb.hFileInfo.ioNamePtr = (StringPtr) &items->name; + pb.hFileInfo.ioDirID = theDirID; + pb.hFileInfo.ioFDirIndex = *itemIndex; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + items->parID = pb.hFileInfo.ioFlParID; /* return item's parID */ + items->vRefNum = pb.hFileInfo.ioVRefNum; /* return item's vRefNum */ + ++*itemIndex; /* prepare to get next item in directory */ + + if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + if ( getDirectories ) + { + ++*actItemCount; /* keep this item */ + ++items; /* point to next item */ + } + } + else + { + if ( getFiles ) + { + ++*actItemCount; /* keep this item */ + ++items; /* point to next item */ + } + } + } + } + } + else + { + /* it wasn't a directory */ + error = dirNFErr; + } + } + } + } + else + { + /* bad itemIndex */ + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +static void DeleteLevel(long dirToDelete, + DeleteEnumGlobalsPtr theGlobals) +{ + long savedDir; + + do + { + /* prepare to delete directory */ + theGlobals->myPB.ciPB.dirInfo.ioNamePtr = (StringPtr)&theGlobals->itemName; + theGlobals->myPB.ciPB.dirInfo.ioFDirIndex = 1; /* get first item */ + theGlobals->myPB.ciPB.dirInfo.ioDrDirID = dirToDelete; /* in this directory */ + theGlobals->error = PBGetCatInfoSync(&(theGlobals->myPB.ciPB)); + if ( theGlobals->error == noErr ) + { + savedDir = dirToDelete; + /* We have an item. Is it a file or directory? */ + if ( (theGlobals->myPB.ciPB.dirInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* it's a directory */ + savedDir = theGlobals->myPB.ciPB.dirInfo.ioDrDirID; /* save dirID of directory instead */ + DeleteLevel(theGlobals->myPB.ciPB.dirInfo.ioDrDirID, theGlobals); /* Delete its contents */ + theGlobals->myPB.ciPB.dirInfo.ioNamePtr = NULL; /* prepare to delete directory */ + } + if ( theGlobals->error == noErr ) + { + theGlobals->myPB.ciPB.dirInfo.ioDrDirID = savedDir; /* restore dirID */ + theGlobals->myPB.hPB.fileParam.ioFVersNum = 0; /* just in case it's used on an MFS volume... */ + theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* delete this item */ + if ( theGlobals->error == fLckdErr ) + { + (void) PBHRstFLockSync(&(theGlobals->myPB.hPB)); /* unlock it */ + theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* and try again */ + } + } + } + } while ( theGlobals->error == noErr ); + + if ( theGlobals->error == fnfErr ) + { + theGlobals->error = noErr; + } +} + +/*****************************************************************************/ + +pascal OSErr DeleteDirectoryContents(short vRefNum, + long dirID, + ConstStr255Param name) +{ + DeleteEnumGlobals theGlobals; + Boolean isDirectory; + OSErr error; + + /* Get the real dirID and make sure it is a directory. */ + error = GetDirectoryID(vRefNum, dirID, name, &dirID, &isDirectory); + if ( error == noErr ) + { + if ( isDirectory ) + { + /* Get the real vRefNum */ + error = DetermineVRefNum(name, vRefNum, &vRefNum); + if ( error == noErr ) + { + /* Set up the globals we need to access from the recursive routine. */ + theGlobals.myPB.ciPB.dirInfo.ioVRefNum = vRefNum; + + /* Here we go into recursion land... */ + DeleteLevel(dirID, &theGlobals); + error = theGlobals.error; + } + } + else + { + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DeleteDirectory(short vRefNum, + long dirID, + ConstStr255Param name) +{ + OSErr error; + + /* Make sure a directory was specified and then delete its contents */ + error = DeleteDirectoryContents(vRefNum, dirID, name); + if ( error == noErr ) + { + error = HDelete(vRefNum, dirID, name); + if ( error == fLckdErr ) + { + (void) HRstFLock(vRefNum, dirID, name); /* unlock the directory locked by AppleShare */ + error = HDelete(vRefNum, dirID, name); /* and try again */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr CheckObjectLock(short vRefNum, + long dirID, + ConstStr255Param name) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + /* check locked bit */ + if ( (pb.hFileInfo.ioFlAttrib & 0x01) != 0 ) + { + error = fLckdErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCheckObjectLock(const FSSpec *spec) +{ + return ( CheckObjectLock(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr GetFileSize(short vRefNum, + long dirID, + ConstStr255Param fileName, + long *dataSize, + long *rsrcSize) +{ + HParamBlockRec pb; + OSErr error; + + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioDirID = dirID; + pb.fileParam.ioFDirIndex = 0; + error = PBHGetFInfoSync(&pb); + if ( error == noErr ) + { + *dataSize = pb.fileParam.ioFlLgLen; + *rsrcSize = pb.fileParam.ioFlRLgLen; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFileSize(const FSSpec *spec, + long *dataSize, + long *rsrcSize) +{ + return ( GetFileSize(spec->vRefNum, spec->parID, spec->name, dataSize, rsrcSize) ); +} + +/*****************************************************************************/ + +pascal OSErr BumpDate(short vRefNum, + long dirID, + ConstStr255Param name) +/* Given a file or directory, change its modification date to the current date/time. */ +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + unsigned long secs; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + GetDateTime(&secs); + /* set mod date to current date, or one second into the future + if mod date = current date */ + pb.hFileInfo.ioFlMdDat = (secs == pb.hFileInfo.ioFlMdDat) ? (++secs) : (secs); + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID; + } + else + { + pb.hFileInfo.ioDirID = dirID; + } + error = PBSetCatInfoSync(&pb); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpBumpDate(const FSSpec *spec) +{ + return ( BumpDate(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr ChangeCreatorType(short vRefNum, + long dirID, + ConstStr255Param name, + OSType creator, + OSType fileType) +{ + CInfoPBRec pb; + OSErr error; + short realVRefNum; + long parID; + + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 ) /* if file */ + { + parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */ + + /* If creator not 0x00000000, change creator */ + if ( creator != (OSType)0x00000000 ) + { + pb.hFileInfo.ioFlFndrInfo.fdCreator = creator; + } + + /* If fileType not 0x00000000, change fileType */ + if ( fileType != (OSType)0x00000000 ) + { + pb.hFileInfo.ioFlFndrInfo.fdType = fileType; + } + + pb.hFileInfo.ioDirID = dirID; + error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */ + + if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */ + { + /* get the real vRefNum in case a full pathname was passed */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = BumpDate(realVRefNum, parID, NULL); + /* and bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made */ + } + } + } + else + { + /* it was a directory, not a file */ + error = notAFileErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpChangeCreatorType(const FSSpec *spec, + OSType creator, + OSType fileType) +{ + return ( ChangeCreatorType(spec->vRefNum, spec->parID, spec->name, creator, fileType) ); +} + +/*****************************************************************************/ + +pascal OSErr ChangeFDFlags(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean setBits, + unsigned short flagBits) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + short realVRefNum; + long parID; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */ + + /* set or clear the appropriate bits in the Finder flags */ + if ( setBits ) + { + /* OR in the bits */ + pb.hFileInfo.ioFlFndrInfo.fdFlags |= flagBits; + } + else + { + /* AND out the bits */ + pb.hFileInfo.ioFlFndrInfo.fdFlags &= ~flagBits; + } + + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID; + } + else + { + pb.hFileInfo.ioDirID = dirID; + } + + error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */ + + if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */ + { + /* get the real vRefNum in case a full pathname was passed */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = BumpDate(realVRefNum, parID, NULL); + /* and bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made */ + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpChangeFDFlags(const FSSpec *spec, + Boolean setBits, + unsigned short flagBits) +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, setBits, flagBits) ); +} + +/*****************************************************************************/ + +pascal OSErr SetIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, make it invisible. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetIsInvisible(const FSSpec *spec) + /* Given a file or directory, make it invisible. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, make it visible. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearIsInvisible(const FSSpec *spec) + /* Given a file or directory, make it visible. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr SetNameLocked(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, lock its name. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetNameLocked(const FSSpec *spec) + /* Given a file or directory, lock its name. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearNameLocked(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, unlock its name. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearNameLocked(const FSSpec *spec) + /* Given a file or directory, unlock its name. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr SetIsStationery(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, make it a stationery pad. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetIsStationery(const FSSpec *spec) + /* Given a file, make it a stationery pad. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearIsStationery(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, clear the stationery bit. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearIsStationery(const FSSpec *spec) + /* Given a file, clear the stationery bit. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr SetHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, indicate that it has a custom icon. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetHasCustomIcon(const FSSpec *spec) + /* Given a file or directory, indicate that it has a custom icon. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, indicate that it does not have a custom icon. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearHasCustomIcon(const FSSpec *spec) + /* Given a file or directory, indicate that it does not have a custom icon. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearHasBeenInited(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, clear its "has been inited" bit. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearHasBeenInited(const FSSpec *spec) + /* Given a file, clear its "has been inited" bit. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +pascal OSErr CopyFileMgrAttributes(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + Boolean copyLockBit) +{ + UniversalFMPB pb; + Str31 tempName; + OSErr error; + Boolean objectIsDirectory; + + pb.ciPB.hFileInfo.ioVRefNum = srcVRefNum; + pb.ciPB.hFileInfo.ioDirID = srcDirID; + + /* Protection against File Sharing problem */ + if ( (srcName == NULL) || (srcName[0] == 0) ) + { + tempName[0] = 0; + pb.ciPB.hFileInfo.ioNamePtr = tempName; + pb.ciPB.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)srcName; + pb.ciPB.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + error = PBGetCatInfoSync(&pb.ciPB); + if ( error == noErr ) + { + objectIsDirectory = ( (pb.ciPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 ); + pb.ciPB.hFileInfo.ioVRefNum = dstVRefNum; + pb.ciPB.hFileInfo.ioDirID = dstDirID; + if ( (dstName != NULL) && (dstName[0] == 0) ) + { + pb.ciPB.hFileInfo.ioNamePtr = NULL; + } + else + { + pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)dstName; + } + /* don't copy the hasBeenInited bit */ + pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags = ( pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags & 0xfeff ); + error = PBSetCatInfoSync(&pb.ciPB); + if ( (error == noErr) && (copyLockBit) && ((pb.ciPB.hFileInfo.ioFlAttrib & 0x01) != 0) ) + { + pb.hPB.fileParam.ioFVersNum = 0; + error = PBHSetFLockSync(&pb.hPB); + if ( (error != noErr) && (objectIsDirectory) ) + { + error = noErr; /* ignore lock errors if destination is directory */ + } + } + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyFileMgrAttributes(const FSSpec *srcSpec, + const FSSpec *dstSpec, + Boolean copyLockBit) +{ + return ( CopyFileMgrAttributes(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyLockBit) ); +} + +/*****************************************************************************/ + +pascal OSErr HOpenAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum) +{ + HParamBlockRec pb; + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize = sizeof(GetVolParmsInfoBuffer); + + pb.ioParam.ioMisc = NULL; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + + /* get volume attributes */ + /* this preflighting is needed because Foreign File Access based file systems don't */ + /* return the correct error result to the OpenDeny call */ + error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasOpenDeny(volParmsInfo) ) + { + /* if volume supports OpenDeny, use it and return */ + pb.accessParam.ioDenyModes = denyModes; + error = PBHOpenDenySync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* OpenDeny isn't supported, so try File Manager Open functions */ + + /* If request includes write permission, then see if the volume is */ + /* locked by hardware or software. The HFS file system doesn't check */ + /* for this when a file is opened - you only find out later when you */ + /* try to write and the write fails with a wPrErr or a vLckdErr. */ + + if ( (denyModes & dmWr) != 0 ) + { + error = CheckVolLock(fileName, vRefNum); + } + else + { + error = noErr; + } + + if ( error == noErr ) + { + /* Set File Manager permissions to closest thing possible */ + if ( (denyModes == dmWr) || (denyModes == dmRdWr) ) + { + pb.ioParam.ioPermssn = fsRdWrShPerm; + } + else + { + pb.ioParam.ioPermssn = denyModes % 4; + } + + error = PBHOpenDFSync(&pb); /* Try OpenDF */ + if ( error == paramErr ) + { + error = PBHOpenSync(&pb); /* OpenDF not supported, so try Open */ + } + *refNum = pb.ioParam.ioRefNum; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenAware(const FSSpec *spec, + short denyModes, + short *refNum) +{ + return ( HOpenAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) ); +} + +/*****************************************************************************/ + +pascal OSErr HOpenRFAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum) +{ + HParamBlockRec pb; + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize = sizeof(GetVolParmsInfoBuffer); + + pb.ioParam.ioMisc = NULL; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + + /* get volume attributes */ + /* this preflighting is needed because Foreign File Access based file systems don't */ + /* return the correct error result to the OpenRFDeny call */ + error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasOpenDeny(volParmsInfo) ) + { + /* if volume supports OpenRFDeny, use it and return */ + if ( hasOpenDeny(volParmsInfo) ) + { + pb.accessParam.ioDenyModes = denyModes; + error = PBHOpenRFDenySync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* OpenRFDeny isn't supported, so try File Manager OpenRF function */ + + /* If request includes write permission, then see if the volume is */ + /* locked by hardware or software. The HFS file system doesn't check */ + /* for this when a file is opened - you only find out later when you */ + /* try to write and the write fails with a wPrErr or a vLckdErr. */ + + if ( (denyModes & dmWr) != 0 ) + { + error = CheckVolLock(fileName, vRefNum); + } + else + { + error = noErr; + } + + if ( error == noErr ) + { + /* Set File Manager permissions to closest thing possible */ + if ( (denyModes == dmWr) || (denyModes == dmRdWr) ) + { + pb.ioParam.ioPermssn = fsRdWrShPerm; + } + else + { + pb.ioParam.ioPermssn = denyModes % 4; + } + + error = PBHOpenRFSync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenRFAware(const FSSpec *spec, + short denyModes, + short *refNum) +{ + return ( HOpenRFAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) ); +} + +/*****************************************************************************/ + +pascal OSErr FSReadNoCache(short refNum, + long *count, + void *buffPtr) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + 0x0020; /* fsAtMark + noCacheBit */ + pb.ioParam.ioPosOffset = 0; + error = PBReadSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSWriteNoCache(short refNum, + long *count, + const void *buffPtr) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + 0x0020; /* fsAtMark + noCacheBit */ + pb.ioParam.ioPosOffset = 0; + error = PBWriteSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( error ); +} + +/*****************************************************************************/ + +/* +** See if numBytes bytes of buffer1 are equal to buffer2. +*/ +static Boolean EqualMemory(const void *buffer1, const void *buffer2, unsigned long numBytes) +{ + register unsigned char *b1 = (unsigned char *)buffer1; + register unsigned char *b2 = (unsigned char *)buffer2; + + if ( b1 != b2 ) /* if buffer pointers are same, then they are equal */ + { + while ( numBytes > 0 ) + { + /* compare the bytes and then increment the pointers */ + if ( (*b1++ - *b2++) != 0 ) + { + return ( false ); + } + --numBytes; + } + } + + return ( true ); +} + +/*****************************************************************************/ + +/* +** Read any number of bytes from an open file using read-verify mode. +** The FSReadVerify function reads any number of bytes from an open file +** and verifies them against the data in the buffer pointed to by buffPtr. +** +** Because of a bug in the HFS file system, only non-block aligned parts of +** the read are verified against the buffer data and the rest is *copied* +** into the buffer. Thus, you shouldn't verify against your original data; +** instead, you should verify against a copy of the original data and then +** compare the read-verified copy against the original data after calling +** FSReadVerify. That's why this function isn't exported - it needs the +** wrapper provided by FSWriteVerify. +*/ +static OSErr FSReadVerify(short refNum, + long *count, + void *buffPtr) +{ + ParamBlockRec pb; + OSErr result; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + rdVerify; + pb.ioParam.ioPosOffset = 0; + result = PBReadSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSWriteVerify(short refNum, + long *count, + const void *buffPtr) +{ + Ptr verifyBuffer; + long position; + long bufferSize; + long byteCount; + long bytesVerified; + Ptr startVerify; + OSErr result; + + /* + ** Allocate the verify buffer + ** Try to get get a large enough buffer to verify in one pass. + ** If that fails, use GetTempBuffer to get a buffer. + */ + bufferSize = *count; + verifyBuffer = NewPtr(bufferSize); + if ( verifyBuffer == NULL ) + { + verifyBuffer = GetTempBuffer(bufferSize, &bufferSize); + } + if ( verifyBuffer != NULL ) + { + /* Save the current position */ + result = GetFPos(refNum, &position); + if ( result == noErr ) + { + /* Write the data */ + result = FSWrite(refNum, count, buffPtr); + if ( result == noErr ) + { + /* Restore the original position */ + result = SetFPos(refNum, fsFromStart, position); + if ( result == noErr ) + { + /* + ** *count = total number of bytes to verify + ** bufferSize = the size of the verify buffer + ** bytesVerified = number of bytes verified + ** byteCount = number of bytes to verify this pass + ** startVerify = position in buffPtr + */ + bytesVerified = 0; + startVerify = (Ptr)buffPtr; + while ( (bytesVerified < *count) && ( result == noErr ) ) + { + if ( (*count - bytesVerified) > bufferSize ) + { + byteCount = bufferSize; + } + else + { + byteCount = *count - bytesVerified; + } + /* + ** Copy the write buffer into the verify buffer. + ** This step is needed because the File Manager + ** compares the data in any non-block aligned + ** data at the beginning and end of the read-verify + ** request back into the file system's cache + ** to the data in verify Buffer. However, the + ** File Manager does not compare any full blocks + ** and instead copies them into the verify buffer + ** so we still have to compare the buffers again + ** after the read-verify request completes. + */ + BlockMoveData(startVerify, verifyBuffer, byteCount); + + /* Read-verify the data back into the verify buffer */ + result = FSReadVerify(refNum, &byteCount, verifyBuffer); + if ( result == noErr ) + { + /* See if the buffers are the same */ + if ( !EqualMemory(verifyBuffer, startVerify, byteCount) ) + { + result = ioErr; + } + startVerify += byteCount; + bytesVerified += byteCount; + } + } + } + } + } + DisposePtr(verifyBuffer); + } + else + { + result = memFullErr; + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr CopyFork(short srcRefNum, + short dstRefNum, + void *copyBufferPtr, + long copyBufferSize) +{ + ParamBlockRec srcPB; + ParamBlockRec dstPB; + OSErr srcError; + OSErr dstError; + + if ( (copyBufferPtr == NULL) || (copyBufferSize == 0) ) + return ( paramErr ); + + srcPB.ioParam.ioRefNum = srcRefNum; + dstPB.ioParam.ioRefNum = dstRefNum; + + /* preallocate the destination fork and */ + /* ensure the destination fork's EOF is correct after the copy */ + srcError = PBGetEOFSync(&srcPB); + if ( srcError != noErr ) + return ( srcError ); + dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc; + dstError = PBSetEOFSync(&dstPB); + if ( dstError != noErr ) + return ( dstError ); + + /* reset source fork's mark */ + srcPB.ioParam.ioPosMode = fsFromStart; + srcPB.ioParam.ioPosOffset = 0; + srcError = PBSetFPosSync(&srcPB); + if ( srcError != noErr ) + return ( srcError ); + + /* reset destination fork's mark */ + dstPB.ioParam.ioPosMode = fsFromStart; + dstPB.ioParam.ioPosOffset = 0; + dstError = PBSetFPosSync(&dstPB); + if ( dstError != noErr ) + return ( dstError ); + + /* set up fields that won't change in the loop */ + srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr; + srcPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */ + /* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */ + /* This will make writes on local volumes faster */ + if ( (copyBufferSize >= 512) && ((copyBufferSize & 0x1ff) != 0) ) + { + srcPB.ioParam.ioReqCount = copyBufferSize & 0xfffffe00; + } + else + { + srcPB.ioParam.ioReqCount = copyBufferSize; + } + dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr; + dstPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */ + + while ( (srcError == noErr) && (dstError == noErr) ) + { + srcError = PBReadSync(&srcPB); + dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount; + dstError = PBWriteSync(&dstPB); + } + + /* make sure there were no errors at the destination */ + if ( dstError != noErr ) + return ( dstError ); + + /* make sure the only error at the source was eofErr */ + if ( srcError != eofErr ) + return ( srcError ); + + return ( noErr ); +} + +/*****************************************************************************/ + +pascal OSErr GetFileLocation(short refNum, + short *vRefNum, + long *dirID, + StringPtr fileName) +{ + FCBPBRec pb; + OSErr error; + + pb.ioNamePtr = fileName; + pb.ioVRefNum = 0; + pb.ioRefNum = refNum; + pb.ioFCBIndx = 0; + error = PBGetFCBInfoSync(&pb); + if ( error == noErr ) + { + *vRefNum = pb.ioFCBVRefNum; + *dirID = pb.ioFCBParID; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFileLocation(short refNum, + FSSpec *spec) +{ + return ( GetFileLocation(refNum, &(spec->vRefNum), &(spec->parID), spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr CopyDirectoryAccess(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName) +{ + OSErr error; + GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */ + long dstServerAdr; /* AppleTalk server address of destination (if any) */ + long ownerID, groupID, accessRights; + long tempLong; + + /* See if destination supports directory access control */ + tempLong = sizeof(infoBuffer); + error = HGetVolParms(dstName, dstVRefNum, &infoBuffer, &tempLong); + if ( (error == noErr) && hasAccessCntl(infoBuffer) ) + { + if ( hasAccessCntl(infoBuffer) ) + { + dstServerAdr = infoBuffer.vMServerAdr; + + /* See if source supports directory access control and is on same server */ + tempLong = sizeof(infoBuffer); + error = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong); + if ( error == noErr ) + { + if ( hasAccessCntl(infoBuffer) && (dstServerAdr == infoBuffer.vMServerAdr) ) + { + /* both volumes support directory access control and they are */ + /* on same server, so copy the access information */ + error = HGetDirAccess(srcVRefNum, srcDirID, srcName, &ownerID, &groupID, &accessRights); + if ( error == noErr ) + { + error = HSetDirAccess(dstVRefNum, dstDirID, dstName, ownerID, groupID, accessRights); + } + } + else + { + /* destination doesn't support directory access control or */ + /* they volumes aren't on the same server */ + error = paramErr; + } + } + } + else + { + /* destination doesn't support directory access control */ + error = paramErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyDirectoryAccess(const FSSpec *srcSpec, + const FSSpec *dstSpec) +{ + return ( CopyDirectoryAccess(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr HMoveRenameCompat(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName) +{ + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize; + short realVRefNum; + long realParID; + Str31 realName; + Boolean isDirectory; + long tempItemsDirID; + long uniqueTempDirID; + Str31 uniqueTempDirName; + unsigned short uniqueNameoverflow; + + /* Get volume attributes */ + infoSize = sizeof(GetVolParmsInfoBuffer); + error = HGetVolParms((StringPtr)srcName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasMoveRename(volParmsInfo) ) + { + /* If volume supports move and rename, so use it and return */ + error = HMoveRename(vRefNum, srcDirID, srcName, dstDirID, dstpathName, copyName); + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* MoveRename isn't supported by this volume, so do it by hand */ + + /* If copyName isn't supplied, we can simply CatMove and return */ + if ( copyName == NULL ) + { + error = CatMove(vRefNum, srcDirID, srcName, dstDirID, dstpathName); + } + else + { + /* Renaming is required, so we have some work to do... */ + + /* Get the object's real name, real parent ID and real vRefNum */ + error = GetObjectLocation(vRefNum, srcDirID, (StringPtr)srcName, + &realVRefNum, &realParID, realName, &isDirectory); + if ( error == noErr ) + { + /* Find the Temporary Items Folder on that volume */ + error = FindFolder(realVRefNum, kTemporaryFolderType, kCreateFolder, + &realVRefNum, &tempItemsDirID); + if ( error == noErr ) + { + /* Create a new uniquely named folder in the temporary items folder. */ + /* This is done to avoid the case where 'realName' or 'copyName' already */ + /* exists in the temporary items folder. */ + + /* Start with current tick count as uniqueTempDirName */ + NumToString(TickCount(), uniqueTempDirName); + uniqueNameoverflow = 0; + do + { + error = DirCreate(realVRefNum, tempItemsDirID, uniqueTempDirName, &uniqueTempDirID); + if ( error == dupFNErr ) + { + /* Duplicate name - change the first character to the next ASCII character */ + ++uniqueTempDirName[1]; + /* Make sure it isn't a colon! */ + if ( uniqueTempDirName[1] == ':' ) + { + ++uniqueTempDirName[1]; + } + /* Don't go too far... */ + ++uniqueNameoverflow; + } + } while ( (error == dupFNErr) && (uniqueNameoverflow <= 64) ); /* 64 new files per 1/60th second - not likely! */ + if ( error == noErr ) + { + /* Move the object to the folder with uniqueTempDirID for renaming */ + error = CatMove(realVRefNum, realParID, realName, uniqueTempDirID, NULL); + if ( error == noErr ) + { + /* Rename the object */ + error = HRename(realVRefNum, uniqueTempDirID, realName, copyName); + if ( error == noErr ) + { + /* Move object to its new home */ + error = CatMove(realVRefNum, uniqueTempDirID, copyName, dstDirID, dstpathName); + if ( error != noErr ) + { + /* Error handling: rename object back to original name - ignore errors */ + (void) HRename(realVRefNum, uniqueTempDirID, copyName, realName); + } + } + if ( error != noErr ) + { + /* Error handling: move object back to original location - ignore errors */ + (void) CatMove(realVRefNum, uniqueTempDirID, realName, realParID, NULL); + } + } + /* Done with ourTempDir, so delete it - ignore errors */ + (void) HDelete(realVRefNum, uniqueTempDirID, NULL); + } + } + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpMoveRenameCompat(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName) +{ + /* make sure the FSSpecs refer to the same volume */ + if (srcSpec->vRefNum != dstSpec->vRefNum) + return (diffVolErr); + return ( HMoveRenameCompat(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->parID, dstSpec->name, copyName) ); +} + +/*****************************************************************************/ + +pascal OSErr BuildAFPVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str32 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + AFPVolMountInfoPtr *afpInfoPtr) +{ + MyAFPVolMountInfoPtr infoPtr; + OSErr error; + + /* Allocate the AFPXVolMountInfo record */ + infoPtr = (MyAFPVolMountInfoPtr)NewPtrClear(sizeof(MyAFPVolMountInfo)); + if ( infoPtr != NULL ) + { + /* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */ + infoPtr->length = sizeof(MyAFPVolMountInfo); + infoPtr->media = AppleShareMediaType; + infoPtr->flags = flags; + infoPtr->nbpInterval = nbpInterval; + infoPtr->nbpCount = nbpCount; + infoPtr->uamType = uamType; + + infoPtr->zoneNameOffset = offsetof(MyAFPVolMountInfo, zoneName); + infoPtr->serverNameOffset = offsetof(MyAFPVolMountInfo, serverName); + infoPtr->volNameOffset = offsetof(MyAFPVolMountInfo, volName); + infoPtr->userNameOffset = offsetof(MyAFPVolMountInfo, userName); + infoPtr->userPasswordOffset = offsetof(MyAFPVolMountInfo, userPassword); + infoPtr->volPasswordOffset = offsetof(MyAFPVolMountInfo, volPassword); + + BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32)); + BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32)); + BlockMoveData(volName, infoPtr->volName, sizeof(Str27)); + BlockMoveData(userName, infoPtr->userName, sizeof(Str31)); + BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8)); + BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8)); + + *afpInfoPtr = (AFPVolMountInfoPtr)infoPtr; + error = noErr; + } + else + { + error = memFullErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPVolMountInfo(AFPVolMountInfoPtr afpInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName) +{ + StringPtr tempPtr; + OSErr error; + + /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */ + if ( afpInfoPtr->media == AppleShareMediaType ) + { + *flags = afpInfoPtr->flags; + *uamType = afpInfoPtr->uamType; + + if ( afpInfoPtr->zoneNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->zoneNameOffset); + BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->serverNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->serverNameOffset); + BlockMoveData(tempPtr, serverName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->volNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->volNameOffset); + BlockMoveData(tempPtr, volName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->userNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->userNameOffset); + BlockMoveData(tempPtr, userName, tempPtr[0] + 1); + } + + error = noErr; + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr BuildAFPXVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str32 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + Str32 uamName, + unsigned long alternateAddressLength, + void *alternateAddress, + AFPXVolMountInfoPtr *afpXInfoPtr) +{ + Size infoSize; + MyAFPXVolMountInfoPtr infoPtr; + OSErr error; + + /* Calculate the size of the AFPXVolMountInfo record */ + infoSize = sizeof(MyAFPXVolMountInfo) + alternateAddressLength - 1; + + /* Allocate the AFPXVolMountInfo record */ + infoPtr = (MyAFPXVolMountInfoPtr)NewPtrClear(infoSize); + if ( infoPtr != NULL ) + { + /* Fill in an AFPXVolMountInfo record that can be passed to VolumeMount */ + infoPtr->length = infoSize; + infoPtr->media = AppleShareMediaType; + infoPtr->flags = flags; + if ( alternateAddressLength != 0 ) + { + /* make sure the volMountExtendedFlagsBit is set if there's extended address info */ + infoPtr->flags |= volMountExtendedFlagsMask; + /* and set the only extendedFlags bit we know about */ + infoPtr->extendedFlags = kAFPExtendedFlagsAlternateAddressMask; + } + else + { + /* make sure the volMountExtendedFlagsBit is clear if there's no extended address info */ + infoPtr->flags &= ~volMountExtendedFlagsMask; + /* and clear the extendedFlags */ + infoPtr->extendedFlags = 0; + } + infoPtr->nbpInterval = nbpInterval; + infoPtr->nbpCount = nbpCount; + infoPtr->uamType = uamType; + + infoPtr->zoneNameOffset = offsetof(MyAFPXVolMountInfo, zoneName); + infoPtr->serverNameOffset = offsetof(MyAFPXVolMountInfo, serverName); + infoPtr->volNameOffset = offsetof(MyAFPXVolMountInfo, volName); + infoPtr->userNameOffset = offsetof(MyAFPXVolMountInfo, userName); + infoPtr->userPasswordOffset = offsetof(MyAFPXVolMountInfo, userPassword); + infoPtr->volPasswordOffset = offsetof(MyAFPXVolMountInfo, volPassword); + infoPtr->uamNameOffset = offsetof(MyAFPXVolMountInfo, uamName); + infoPtr->alternateAddressOffset = offsetof(MyAFPXVolMountInfo, alternateAddress); + + BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32)); + BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32)); + BlockMoveData(volName, infoPtr->volName, sizeof(Str27)); + BlockMoveData(userName, infoPtr->userName, sizeof(Str31)); + BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8)); + BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8)); + BlockMoveData(uamName, infoPtr->uamName, sizeof(Str32)); + BlockMoveData(alternateAddress, infoPtr->alternateAddress, alternateAddressLength); + + *afpXInfoPtr = (AFPXVolMountInfoPtr)infoPtr; + error = noErr; + } + else + { + error = memFullErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPXVolMountInfo(AFPXVolMountInfoPtr afpXInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName, + StringPtr uamName, + unsigned long *alternateAddressLength, + AFPAlternateAddress **alternateAddress) +{ + StringPtr tempPtr; + Ptr alternateAddressStart; + Ptr alternateAddressEnd; + Size alternateAddressDataSize; + OSErr error; + UInt8 addressCount; + + /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */ + if ( afpXInfoPtr->media == AppleShareMediaType ) + { + /* default to noErr */ + error = noErr; + + /* Is this an extended record? */ + if ( (afpXInfoPtr->flags & volMountExtendedFlagsMask) != 0 ) + { + if ( ((afpXInfoPtr->extendedFlags & kAFPExtendedFlagsAlternateAddressMask) != 0) && + (afpXInfoPtr->alternateAddressOffset != 0) ) + { + + alternateAddressStart = (Ptr)((long)afpXInfoPtr + afpXInfoPtr->alternateAddressOffset); + alternateAddressEnd = alternateAddressStart + 1; /* skip over alternate address version byte */ + addressCount = *(UInt8*)alternateAddressEnd; /* get the address count */ + ++alternateAddressEnd; /* skip over alternate address count byte */ + /* alternateAddressEnd now equals &AFPAlternateAddress.fAddressList[0] */ + while ( addressCount != 0 ) + { + /* parse the address list to find the end */ + alternateAddressEnd += *(UInt8*)alternateAddressEnd; /* add length of each AFPTagData record */ + --addressCount; + } + /* get the size of the alternateAddressData */ + alternateAddressDataSize = alternateAddressEnd - alternateAddressStart; + /* allocate memory for it */ + *alternateAddress = (AFPAlternateAddress *)NewPtr(alternateAddressDataSize); + if ( *alternateAddress != NULL ) + { + /* and return the data */ + BlockMoveData(alternateAddressStart, *alternateAddress, alternateAddressDataSize); + *alternateAddressLength = alternateAddressDataSize; + } + else + { + /* no memory - fail now */ + error = memFullErr; + } + } + + if ( error == noErr ) /* fill in more output parameters if everything is OK */ + { + if ( afpXInfoPtr->uamNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->uamNameOffset); + BlockMoveData(tempPtr, uamName, tempPtr[0] + 1); + } + } + } + + if ( error == noErr ) /* fill in more output parameters if everything is OK */ + { + *flags = afpXInfoPtr->flags; + *uamType = afpXInfoPtr->uamType; + + if ( afpXInfoPtr->zoneNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->zoneNameOffset); + BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->serverNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->serverNameOffset); + BlockMoveData(tempPtr, serverName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->volNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->volNameOffset); + BlockMoveData(tempPtr, volName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->userNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->userNameOffset); + BlockMoveData(tempPtr, userName, tempPtr[0] + 1); + } + } + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetUGEntries(short objType, + UGEntryPtr entries, + long reqEntryCount, + long *actEntryCount, + long *objID) +{ + HParamBlockRec pb; + OSErr error = noErr; + UGEntry *endEntryArray; + + pb.objParam.ioObjType = objType; + *actEntryCount = 0; + for ( endEntryArray = entries + reqEntryCount; (entries < endEntryArray) && (error == noErr); ++entries ) + { + pb.objParam.ioObjNamePtr = (StringPtr)entries->name; + pb.objParam.ioObjID = *objID; + /* Files.h in the universal interfaces, PBGetUGEntrySync takes a CMovePBPtr */ + /* as the parameter. Inside Macintosh and the original glue used HParmBlkPtr. */ + /* A CMovePBPtr works OK, but this will be changed in the future back to */ + /* HParmBlkPtr, so I'm just casting it here. */ + error = PBGetUGEntrySync(&pb); + if ( error == noErr ) + { + entries->objID = *objID = pb.objParam.ioObjID; + entries->objType = objType; + ++*actEntryCount; + } + } + + return ( error ); +} + +/*****************************************************************************/ + diff --git a/src/mac/morefile/MoreExtr.h b/src/mac/morefile/MoreExtr.h new file mode 100644 index 0000000000..927a824d98 --- /dev/null +++ b/src/mac/morefile/MoreExtr.h @@ -0,0 +1,3141 @@ +/* +** Apple Macintosh Developer Technical Support +** +** A collection of useful high-level File Manager routines. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: MoreFilesExtras.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __MOREFILESEXTRAS__ +#define __MOREFILESEXTRAS__ + +#include +#include + +#ifndef true +#define true 1 +#define false 0 +#endif + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +/* Constants and types from Universal Interfaces 3.0.1 Files.h */ + +#if UNIVERSAL_INTERFACES_VERSION < 0x0301 + +enum { + volMountNoLoginMsgFlagBit = 0, /* Input to VolumeMount: If set, the file system */ + volMountNoLoginMsgFlagMask = 0x0001, /* should suppresss any log-in message/greeting dialog */ + volMountExtendedFlagsBit = 7, /* Input to VolumeMount: If set, the mount info is a */ + volMountExtendedFlagsMask = 0x0080 /* AFPXVolMountInfo record for 3.7 AppleShare Client */ +}; + +/* AFPXVolMountInfo is the new AFP volume mount info record, requires the 3.7 AppleShare Client */ + +struct AFPXVolMountInfo { + short length; /* length of location data (including self) */ + VolumeType media; /* type of media */ + short flags; /* bits for no messages, no reconnect */ + SInt8 nbpInterval; /* NBP Interval parameter (IM2, p.322) */ + SInt8 nbpCount; /* NBP Interval parameter (IM2, p.322) */ + short uamType; /* User Authentication Method type */ + short zoneNameOffset; /* short positive offset from start of struct to Zone Name */ + short serverNameOffset; /* offset to pascal Server Name string */ + short volNameOffset; /* offset to pascal Volume Name string */ + short userNameOffset; /* offset to pascal User Name string */ + short userPasswordOffset; /* offset to pascal User Password string */ + short volPasswordOffset; /* offset to pascal Volume Password string */ + short extendedFlags; /* extended flags word */ + short uamNameOffset; /* offset to a pascal UAM name string */ + short alternateAddressOffset; /* offset to Alternate Addresses in tagged format */ + char AFPData[176]; /* variable length data may follow */ +}; +typedef struct AFPXVolMountInfo AFPXVolMountInfo; +typedef AFPXVolMountInfo * AFPXVolMountInfoPtr; + +enum { + kAFPExtendedFlagsAlternateAddressMask = 1 /* bit in AFPXVolMountInfo.extendedFlags that means alternateAddressOffset is used*/ +}; + +enum { + /* constants for use in AFPTagData.fType field*/ + kAFPTagTypeIP = 0x01, + kAFPTagTypeIPPort = 0x02, + kAFPTagTypeDDP = 0x03 /* Currently unused*/ +}; + +enum { + /* constants for use in AFPTagData.fLength field*/ + kAFPTagLengthIP = 0x06, + kAFPTagLengthIPPort = 0x08, + kAFPTagLengthDDP = 0x06 +}; + +struct AFPTagData { + UInt8 fLength; /* length of this data tag including the fLength field */ + UInt8 fType; + UInt8 fData[1]; /* variable length data */ +}; +typedef struct AFPTagData AFPTagData; + +struct AFPAlternateAddress { + UInt8 fAddressCount; + UInt8 fAddressList[1]; /* actually variable length packed set of AFPTagData */ +}; +typedef struct AFPAlternateAddress AFPAlternateAddress; + +#endif + +/*****************************************************************************/ + +/* +** Macros to get information out of GetVolParmsInfoBuffer +*/ + +#define isNetworkVolume(volParms) ((volParms).vMServerAdr != 0) +#define hasLimitFCBs(volParms) (((volParms).vMAttrib & (1L << bLimitFCBs)) != 0) +#define hasLocalWList(volParms) (((volParms).vMAttrib & (1L << bLocalWList)) != 0) +#define hasNoMiniFndr(volParms) (((volParms).vMAttrib & (1L << bNoMiniFndr)) != 0) +#define hasNoVNEdit(volParms) (((volParms).vMAttrib & (1L << bNoVNEdit)) != 0) +#define hasNoLclSync(volParms) (((volParms).vMAttrib & (1L << bNoLclSync)) != 0) +#define hasTrshOffLine(volParms) (((volParms).vMAttrib & (1L << bTrshOffLine)) != 0) +#define hasNoSwitchTo(volParms) (((volParms).vMAttrib & (1L << bNoSwitchTo)) != 0) +#define hasNoDeskItems(volParms) (((volParms).vMAttrib & (1L << bNoDeskItems)) != 0) +#define hasNoBootBlks(volParms) (((volParms).vMAttrib & (1L << bNoBootBlks)) != 0) +#define hasAccessCntl(volParms) (((volParms).vMAttrib & (1L << bAccessCntl)) != 0) +#define hasNoSysDir(volParms) (((volParms).vMAttrib & (1L << bNoSysDir)) != 0) +#define hasExtFSVol(volParms) (((volParms).vMAttrib & (1L << bHasExtFSVol)) != 0) +#define hasOpenDeny(volParms) (((volParms).vMAttrib & (1L << bHasOpenDeny)) != 0) +#define hasCopyFile(volParms) (((volParms).vMAttrib & (1L << bHasCopyFile)) != 0) +#define hasMoveRename(volParms) (((volParms).vMAttrib & (1L << bHasMoveRename)) != 0) +#define hasDesktopMgr(volParms) (((volParms).vMAttrib & (1L << bHasDesktopMgr)) != 0) +#define hasShortName(volParms) (((volParms).vMAttrib & (1L << bHasShortName)) != 0) +#define hasFolderLock(volParms) (((volParms).vMAttrib & (1L << bHasFolderLock)) != 0) +#define hasPersonalAccessPrivileges(volParms) \ + (((volParms).vMAttrib & (1L << bHasPersonalAccessPrivileges)) != 0) +#define hasUserGroupList(volParms) (((volParms).vMAttrib & (1L << bHasUserGroupList)) != 0) +#define hasCatSearch(volParms) (((volParms).vMAttrib & (1L << bHasCatSearch)) != 0) +#define hasFileIDs(volParms) (((volParms).vMAttrib & (1L << bHasFileIDs)) != 0) +#define hasBTreeMgr(volParms) (((volParms).vMAttrib & (1L << bHasBTreeMgr)) != 0) +#define hasBlankAccessPrivileges(volParms) \ + (((volParms).vMAttrib & (1L << bHasBlankAccessPrivileges)) != 0) + +/*****************************************************************************/ + + +/* +** Bit masks and macros to get common information out of ioACUser returned +** by PBGetCatInfo (remember to clear ioACUser before calling PBGetCatInfo +** since some file systems don't bother to set this field). +** +** Use the GetDirAccessRestrictions or FSpGetDirAccessRestrictions +** functions to retrieve the ioACUser access restrictions byte for +** a folder. +** +** Note: The access restriction byte returned by PBGetCatInfo is the +** 2's complement of the user's privileges byte returned in +** ioACAccess by PBHGetDirAccess. +*/ + +enum +{ + /* bits defined in ioACUser */ + acUserNoSeeFoldersMask = 0x01, + acUserNoSeeFilesMask = 0x02, + acUserNoMakeChangesMask = 0x04, + acUserNotOwnerMask = 0x80, + + /* mask for just the access restriction bits */ + acUserAccessMask = 0x07, + + /* common access privilege settings */ + acUserFull = 0x00, /* no access restiction bits on */ + acUserNone = acUserAccessMask, /* all access restiction bits on */ + acUserDropBox = acUserNoSeeFoldersMask + acUserNoSeeFilesMask, /* make changes, but not see files or folders */ + acUserBulletinBoard = acUserNoMakeChangesMask /* see files and folders, but not make changes */ +}; + +/* Macros for testing ioACUser bits */ +#define userIsOwner(ioACUser) \ + (((ioACUser) & acUserNotOwnerMask) == 0) +#define userHasFullAccess(ioACUser) \ + (((ioACUser) & (acUserAccessMask)) == acUserFull) +#define userHasDropBoxAccess(ioACUser) \ + (((ioACUser) & acUserAccessMask) == acUserDropBox) +#define userHasBulletinBoard(ioACUser) \ + (((ioACUser) & acUserAccessMask) == acUserBulletinBoard) +#define userHasNoAccess(ioACUser) \ + (((ioACUser) & acUserAccessMask) == acUserNone) + +/*****************************************************************************/ + +/* +** Deny mode permissions for use with the HOpenAware, HOpenRFAware, +** FSpOpenAware, and FSpOpenRFAware functions. +*/ + +enum +{ + dmNone = 0x0000, + dmNoneDenyRd = 0x0010, + dmNoneDenyWr = 0x0020, + dmNoneDenyRdWr = 0x0030, + dmRd = 0x0001, /* Single writer, multiple readers; the readers */ + dmRdDenyRd = 0x0011, + dmRdDenyWr = 0x0021, /* Browsing - equivalent to fsRdPerm */ + dmRdDenyRdWr = 0x0031, + dmWr = 0x0002, + dmWrDenyRd = 0x0012, + dmWrDenyWr = 0x0022, + dmWrDenyRdWr = 0x0032, + dmRdWr = 0x0003, /* Shared access - equivalent to fsRdWrShPerm */ + dmRdWrDenyRd = 0x0013, + dmRdWrDenyWr = 0x0023, /* Single writer, multiple readers; the writer */ + dmRdWrDenyRdWr = 0x0033 /* Exclusive access - equivalent to fsRdWrPerm */ +}; + +/*****************************************************************************/ + +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif + +/* +** For those times where you need to use more than one kind of File Manager parameter +** block but don't feel like wasting stack space, here's a parameter block you can reuse. +*/ + +union UniversalFMPB +{ + ParamBlockRec PB; + CInfoPBRec ciPB; + DTPBRec dtPB; + HParamBlockRec hPB; + CMovePBRec cmPB; + WDPBRec wdPB; + FCBPBRec fcbPB; + XVolumeParam xPB; +}; +typedef union UniversalFMPB UniversalFMPB; +typedef UniversalFMPB *UniversalFMPBPtr, **UniversalFMPBHandle; + + +/* +** Used by GetUGEntries to return user or group lists +*/ + +struct UGEntry +{ + short objType; /* object type: -1 = group; 0 = user */ + long objID; /* the user or group ID */ + Str31 name; /* the user or group name */ +}; +typedef struct UGEntry UGEntry; +typedef UGEntry *UGEntryPtr, **UGEntryHandle; + + +typedef unsigned char Str8[9]; + + +/* +** I use the following records instead of the AFPVolMountInfo and AFPXVolMountInfo structures in Files.h +*/ + +struct MyAFPVolMountInfo +{ + short length; /* length of this record */ + VolumeType media; /* type of media, always AppleShareMediaType */ + short flags; /* 0 = normal mount; set bit 0 to inhibit greeting messages */ + char nbpInterval; /* NBP interval parameter; 7 is a good choice */ + char nbpCount; /* NBP count parameter; 5 is a good choice */ + short uamType; /* User Authentication Method */ + short zoneNameOffset; /* offset from start of record to zoneName */ + short serverNameOffset; /* offset from start of record to serverName */ + short volNameOffset; /* offset from start of record to volName */ + short userNameOffset; /* offset from start of record to userName */ + short userPasswordOffset; /* offset from start of record to userPassword */ + short volPasswordOffset; /* offset from start of record to volPassword */ + Str32 zoneName; /* server's AppleTalk zone name */ + char filler1; /* to word align volPassword */ + Str32 serverName; /* server name */ + char filler2; /* to word align volPassword */ + Str27 volName; /* volume name */ + Str31 userName; /* user name (zero length Pascal string for guest) */ + Str8 userPassword; /* user password (zero length Pascal string if no user password) */ + char filler3; /* to word align volPassword */ + Str8 volPassword; /* volume password (zero length Pascal string if no volume password) */ + char filler4; /* to end record on word boundry */ +}; +typedef struct MyAFPVolMountInfo MyAFPVolMountInfo; +typedef MyAFPVolMountInfo *MyAFPVolMountInfoPtr, **MyAFPVolMountInfoHandle; + +struct MyAFPXVolMountInfo +{ + short length; /* length of this record */ + VolumeType media; /* type of media, always AppleShareMediaType */ + short flags; /* bits for no messages, no reconnect, etc */ + char nbpInterval; /* NBP interval parameter; 7 is a good choice */ + char nbpCount; /* NBP count parameter; 5 is a good choice */ + short uamType; /* User Authentication Method */ + short zoneNameOffset; /* offset from start of record to zoneName */ + short serverNameOffset; /* offset from start of record to serverName */ + short volNameOffset; /* offset from start of record to volName */ + short userNameOffset; /* offset from start of record to userName */ + short userPasswordOffset; /* offset from start of record to userPassword */ + short volPasswordOffset; /* offset from start of record to volPassword */ + short extendedFlags; /* extended flags word */ + short uamNameOffset; /* offset to a pascal UAM name string */ + short alternateAddressOffset; /* offset to Alternate Addresses in tagged format */ + Str32 zoneName; /* server's AppleTalk zone name */ + char filler1; /* to word align volPassword */ + Str32 serverName; /* server name */ + char filler2; /* to word align volPassword */ + Str27 volName; /* volume name */ + Str31 userName; /* user name (zero length Pascal string for guest) */ + Str8 userPassword; /* user password (zero length Pascal string if no user password) */ + char filler3; /* to word align volPassword */ + Str8 volPassword; /* volume password (zero length Pascal string if no volume password) */ + char filler4; /* to word align uamNameOffset */ + Str32 uamName; /* UAM name */ + char filler5; /* to word align alternateAddress */ + char alternateAddress[kVariableLengthArray]; /* AFPAlternateAddress */ +}; +typedef struct MyAFPXVolMountInfo MyAFPXVolMountInfo; +typedef MyAFPXVolMountInfo *MyAFPXVolMountInfoPtr, **MyAFPXVolMountInfoHandle; + +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + +/*****************************************************************************/ + +pascal void TruncPString(StringPtr destination, + ConstStr255Param source, + short maxLength); +/* ¦ International friendly string truncate routine. + The TruncPString function copies up to maxLength characters from + the source Pascal string to the destination Pascal string. TruncPString + ensures that the truncated string ends on a single-byte character, or on + the last byte of a multi-byte character. + + destination output: destination Pascal string. + source input: source Pascal string. + maxLength output: The maximum allowable length of the destination + string. +*/ + +/*****************************************************************************/ + +pascal Ptr GetTempBuffer(long buffReqSize, + long *buffActSize); +/* ¦ Allocate a temporary copy or search buffer. + The GetTempBuffer function allocates a temporary buffer for file system + operations which is at least 1024 bytes (1K) and a multiple of + 1024 bytes. + + buffReqSize input: Size you'd like the buffer to be. + buffActSize output: Size of buffer allocated. + function result output: Pointer to memory allocated or nil if no memory + was available. The caller is responsible for + disposing of this buffer with DisposePtr. +*/ + +/*****************************************************************************/ + +pascal OSErr GetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + HParmBlkPtr pb); +/* ¦ Call PBHGetVInfoSync ignoring returned name. + GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync + in cases where the returned volume name is not needed by the caller. + The pathname and vRefNum parameters are not touched, and the pb + parameter is initialized by PBHGetVInfoSync except that ioNamePtr in + the parameter block is always returned as NULL (since it might point + to GetVolumeInfoNoName's local variable tempPathname). + + I noticed using this code in several places, so here it is once. + This reduces the code size of MoreFiles. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + pb input: A pointer to HParamBlockRec. + output: The parameter block as filled in by PBHGetVInfoSync + except that ioNamePtr will always be NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +pascal OSErr XGetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + XVolumeParamPtr pb); +/* ¦ Call PBXGetVolInfoSync ignoring returned name. + XGetVolumeInfoNoName uses pathname and vRefNum to call PBXGetVolInfoSync + in cases where the returned volume name is not needed by the caller. + The pathname and vRefNum parameters are not touched, and the pb + parameter is initialized by PBXGetVolInfoSync except that ioNamePtr in + the parameter block is always returned as NULL (since it might point + to XGetVolumeInfoNoName's local variable tempPathname). + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + pb input: A pointer to HParamBlockRec. + output: The parameter block as filled in by PBXGetVolInfoSync + except that ioNamePtr will always be NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +pascal OSErr GetCatInfoNoName(short vRefNum, + long dirID, + ConstStr255Param name, + CInfoPBPtr pb); +/* ¦ Call PBGetCatInfoSync ignoring returned name. + GetCatInfoNoName uses vRefNum, dirID and name to call PBGetCatInfoSync + in cases where the returned object is not needed by the caller. + The vRefNum, dirID and name parameters are not touched, and the pb + parameter is initialized by PBGetCatInfoSync except that ioNamePtr in + the parameter block is always returned as NULL (since it might point + to GetCatInfoNoName's local variable tempName). + + I noticed using this code in several places, so here it is once. + This reduces the code size of MoreFiles. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + pb input: A pointer to CInfoPBRec. + output: The parameter block as filled in by + PBGetCatInfoSync except that ioNamePtr will + always be NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + +*/ + +/*****************************************************************************/ + +pascal OSErr DetermineVRefNum(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum); +/* ¦ Determine the real volume reference number. + The DetermineVRefNum function determines the volume reference number of + a volume from a pathname, a volume specification, or a combination + of the two. + WARNING: Volume names on the Macintosh are *not* unique -- Multiple + mounted volumes can have the same name. For this reason, the use of a + volume name or full pathname to identify a specific volume may not + produce the results you expect. If more than one volume has the same + name and a volume name or full pathname is used, the File Manager + currently uses the first volume it finds with a matching name in the + volume queue. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + realVRefNum output: The real volume reference number. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume +*/ + +/*****************************************************************************/ + +pascal OSErr HGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + unsigned long *freeBytes, + unsigned long *totalBytes); +/* ¦ Get information about a mounted volume. + The HGetVInfo function returns the name, volume reference number, + available space (in bytes), and total space (in bytes) for the + specified volume. You can specify the volume by providing its drive + number, volume reference number, or 0 for the default volume. + This routine is compatible with volumes up to 4 gigabytes. + + volReference input: The drive number, volume reference number, + or 0 for the default volume. + volName input: A pointer to a buffer (minimum Str27) where + the volume name is to be returned or must + be nil. + output: The volume name. + vRefNum output: The volume reference number. + freeBytes output: The number of free bytes on the volume. + freeBytes is an unsigned long value. + totalBytes output: The total number of bytes on the volume. + totalBytes is an unsigned long value. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume + + __________ + + Also see: XGetVInfo +*/ + +/*****************************************************************************/ + +pascal OSErr XGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + UnsignedWide *freeBytes, + UnsignedWide *totalBytes); +/* ¦ Get extended information about a mounted volume. + The XGetVInfo function returns the name, volume reference number, + available space (in bytes), and total space (in bytes) for the + specified volume. You can specify the volume by providing its drive + number, volume reference number, or 0 for the default volume. + This routine is compatible with volumes up to 2 terabytes. + + volReference input: The drive number, volume reference number, + or 0 for the default volume. + volName input: A pointer to a buffer (minimum Str27) where + the volume name is to be returned or must + be nil. + output: The volume name. + vRefNum output: The volume reference number. + freeBytes output: The number of free bytes on the volume. + freeBytes is an UnsignedWide value. + totalBytes output: The total number of bytes on the volume. + totalBytes is an UnsignedWide value. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume + + __________ + + Also see: HGetVInfo +*/ + +/*****************************************************************************/ + +pascal OSErr CheckVolLock(ConstStr255Param pathname, + short vRefNum); +/* ¦ Determine if a volume is locked. + The CheckVolLock function determines if a volume is locked - either by + hardware or by software. If CheckVolLock returns noErr, then the volume + is not locked. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + + Result Codes + noErr 0 No error - volume not locked + nsvErr -35 No such volume + wPrErr -44 Volume locked by hardware + vLckdErr -46 Volume locked by software + paramErr -50 No default volume +*/ + +/*****************************************************************************/ + +pascal OSErr GetDriverName(short driverRefNum, + Str255 driverName); +/* ¦ Get a device driver's name. + The GetDriverName function returns a device driver's name. + + driverRefNum input: The driver reference number. + driverName output: The driver's name. + + Result Codes + noErr 0 No error + badUnitErr -21 Bad driver reference number +*/ + +/*****************************************************************************/ + +pascal OSErr FindDrive(ConstStr255Param pathname, + short vRefNum, + DrvQElPtr *driveQElementPtr); +/* ¦ Find a volume's drive queue element in the drive queue. + The FindDrive function returns a pointer to a mounted volume's + drive queue element. + + pathName input: Pointer to a full pathname or nil. If you + pass in a partial pathname, it is ignored. + A full pathname to a volume must end with + a colon character (:). + vRefNum input: Volume specification (volume reference + number, working directory number, drive + number, or 0). + driveQElementPtr output: Pointer to a volume's drive queue element + in the drive queue. DO NOT change the + DrvQEl. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume + nsDrvErr -56 No such drive +*/ + +/*****************************************************************************/ + +pascal OSErr GetDiskBlocks(ConstStr255Param pathname, + short vRefNum, + unsigned long *numBlocks); +/* ¦ Return the number of physical disk blocks on a disk drive. + The GetDiskBlocks function returns the number of physical disk + blocks on a disk drive. NOTE: This is not the same as volume + allocation blocks! + + pathName input: Pointer to a full pathname or nil. If you + pass in a partial pathname, it is ignored. + A full pathname to a volume must end with + a colon character (:). + vRefNum input: Volume specification (volume reference + number, working directory number, drive + number, or 0). + numBlocks output: The number of physical disk blocks on the disk drive. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, driver reference + number is zero, ReturnFormatList + returned zero blocks, DriveStatus + returned an unknown value, or + driveQElementPtr->qType is unknown + nsDrvErr -56 No such drive + statusErr Ð18 Driver does not respond to this + status request + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies + a nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open +*/ + +/*****************************************************************************/ + +pascal OSErr GetVolFileSystemID(ConstStr255Param pathname, + short vRefNum, + short *fileSystemID); +/* ¦ Get a volume's file system ID. + The GetVolFileSystemID function returned the file system ID of + a mounted volume. The file system ID identifies the file system + that handles requests to a particular volume. Here's a partial list + of file system ID numbers (only Apple's file systems are listed): + FSID File System + ----- ----------------------------------------------------- + $0000 Macintosh HFS or MFS + $0100 ProDOS File System + $0101 PowerTalk Mail Enclosures + $4147 ISO 9660 File Access (through Foreign File Access) + $4242 High Sierra File Access (through Foreign File Access) + $464D QuickTake File System (through Foreign File Access) + $4953 Macintosh PC Exchange (MS-DOS) + $4A48 Audio CD Access (through Foreign File Access) + $4D4B Apple Photo Access (through Foreign File Access) + + See the Technical Note "FL 35 - Determining Which File System + Is Active" and the "Guide to the File System Manager" for more + information. + + pathName input: Pointer to a full pathname or nil. If you pass + in a partial pathname, it is ignored. A full + pathname to a volume must contain at least + one colon character (:) and must not start with + a colon character. + vRefNum input: Volume specification (volume reference number, + working directory number, drive number, or 0). + fileSystemID output: The volume's file system ID. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +pascal OSErr GetVolState(ConstStr255Param pathname, + short vRefNum, + Boolean *volumeOnline, + Boolean *volumeEjected, + Boolean *driveEjectable, + Boolean *driverWantsEject); +/* ¦ Returns a volume's online and eject information. + The GetVolState function determines if a volume is online or offline, + if an offline volume is ejected, and if the volume's driver is + ejectable or wants eject calls. + + pathName input: Pointer to a full pathname or nil. + vRefNum input: Volume specification (volume reference number, + working directory number, drive number, or 0). + volumeOnline output: True if the volume is online; + False if the volume is offline. + volumeEjected output: True if the volume is ejected (ejected + volumes are always offline); False if the + volume is not ejected. + driveEjectable output: True if the volume's drive is ejectable; + False if the volume's drive is not ejectable. + driverWantsEject output: True if the volume's driver wants an Eject + request after unmount (even if the drive + is not ejectable); False if the volume's + driver does not need an eject request. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +pascal OSErr UnmountAndEject(ConstStr255Param pathname, + short vRefNum); +/* ¦ Unmount and eject a volume. + The UnmountAndEject function unmounts and ejects a volume. The volume + is ejected only if it is ejectable and not already ejected. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad volume name + fBsyErr -47 One or more files are open + paramErr -50 No default volume + nsDrvErr -56 No such drive + extFSErr -58 External file system error - no file + system claimed this call. +*/ + +/*****************************************************************************/ + +pascal OSErr OnLine(FSSpecPtr volumes, + short reqVolCount, + short *actVolCount, + short *volIndex); +/* ¦ Return the list of volumes currently mounted. + The OnLine function returns the list of volumes currently mounted in + an array of FSSpec records. + + A noErr result indicates that the volumes array was filled + (actVolCount == reqVolCount) and there may be additional volumes + mounted. A nsvErr result indicates that the end of the volume list + was found and actVolCount volumes were actually found this time. + + volumes input: Pointer to array of FSSpec where the volume list + is returned. + reqVolCount input: Maximum number of volumes to return (the number of + elements in the volumes array). + actVolCount output: The number of volumes actually returned. + volIndex input: The current volume index position. Set to 1 to + start with the first volume. + output: The volume index position to get the next volume. + Pass this value the next time you call OnLine to + start where you left off. + + Result Codes + noErr 0 No error, but there are more volumes + to list + nsvErr -35 No more volumes to be listed + paramErr -50 volIndex was <= 0 +*/ + +/*****************************************************************************/ + +pascal OSErr SetDefault(short newVRefNum, + long newDirID, + short *oldVRefNum, + long *oldDirID); +/* ¦ Set the default volume before making Standard I/O requests. + The SetDefault function sets the default volume and directory to the + volume specified by newVRefNum and the directory specified by newDirID. + The current default volume reference number and directory ID are + returned in oldVRefNum and oldDir and must be used to restore the + default volume and directory to their previous state *as soon as + possible* with the RestoreDefault function. These two functions are + designed to be used as a wrapper around Standard I/O routines where + the location of the file is implied to be the default volume and + directory. In other words, this is how you should use these functions: + + error = SetDefault(newVRefNum, newDirID, &oldVRefNum, &oldDirID); + if ( error == noErr ) + { + // call the Stdio functions like remove, rename, tmpfile, + // fopen, freopen, etc. or non-ANSI extensions like + // fdopen,fsetfileinfo, -- create, open, unlink, etc. here! + + error = RestoreDefault(oldVRefNum, oldDirID); + } + + By using these functions as a wrapper, you won't need to open a working + directory (because SetDefault and RestoreDefault use HSetVol) and you + won't have to worry about the effects of using HSetVol (documented in + Technical Note "FL 11 - PBHSetVol is Dangerous" and in the + Inside Macintosh: Files book in the description of the HSetVol and + PBHSetVol functions) because the default volume/directory is restored + before giving up control to code that might be affected by HSetVol. + + newVRefNum input: Volume specification (volume reference number, + working directory number, drive number, or 0) of + the new default volume. + newDirID input: Directory ID of the new default directory. + oldVRefNum output: The volume specification to save for use with + RestoreDefault. + oldDirID output: The directory ID to save for use with + RestoreDefault. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + bdNamErr -37 Bad volume name + fnfErr -43 Directory not found + paramErr -50 No default volume + afpAccessDenied -5000 User does not have access to the directory + + __________ + + Also see: RestoreDefault +*/ + +/*****************************************************************************/ + +pascal OSErr RestoreDefault(short oldVRefNum, + long oldDirID); +/* ¦ Restore the default volume after making Standard C I/O requests. + The RestoreDefault function restores the default volume and directory + to the volume specified by oldVRefNum and the directory specified by + oldDirID. The oldVRefNum and oldDirID parameters were previously + obtained from the SetDefault function. These two functions are designed + to be used as a wrapper around Standard C I/O routines where the + location of the file is implied to be the default volume and directory. + In other words, this is how you should use these functions: + + error = SetDefault(newVRefNum, newDirID, &oldVRefNum, &oldDirID); + if ( error == noErr ) + { + // call the Stdio functions like remove, rename, tmpfile, + // fopen, freopen, etc. or non-ANSI extensions like + // fdopen,fsetfileinfo, -- create, open, unlink, etc. here! + + error = RestoreDefault(oldVRefNum, oldDirID); + } + + By using these functions as a wrapper, you won't need to open a working + directory (because SetDefault and RestoreDefault use HSetVol) and you + won't have to worry about the effects of using HSetVol (documented in + Technical Note "FL 11 - PBHSetVol is Dangerous" and in the + Inside Macintosh: Files book in the description of the HSetVol and + PBHSetVol functions) because the default volume/directory is restored + before giving up control to code that might be affected by HSetVol. + + oldVRefNum input: The volume specification to restore. + oldDirID input: The directory ID to restore. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + bdNamErr -37 Bad volume name + fnfErr -43 Directory not found + paramErr -50 No default volume + rfNumErr -51 Bad working directory reference number + afpAccessDenied -5000 User does not have access to the directory + + __________ + + Also see: SetDefault +*/ + +/*****************************************************************************/ + +pascal OSErr GetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + DInfo *fndrInfo); +/* ¦ Get the finder information for a directory. + The GetDInfo function gets the finder information for a directory. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + fndrInfo output: If the object is a directory, then its DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpGetDInfo, FSpGetFInfoCompat +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetDInfo(const FSSpec *spec, + DInfo *fndrInfo); +/* ¦ Get the finder information for a directory. + The FSpGetDInfo function gets the finder information for a directory. + + spec input: An FSSpec record specifying the directory. + fndrInfo output: If the object is a directory, then its DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpGetFInfoCompat, GetDInfo +*/ + +/*****************************************************************************/ + +pascal OSErr SetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + const DInfo *fndrInfo); +/* ¦ Set the finder information for a directory. + The SetDInfo function sets the finder information for a directory. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + fndrInfo input: The DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpSetDInfo, FSpSetFInfoCompat +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetDInfo(const FSSpec *spec, + const DInfo *fndrInfo); +/* ¦ Set the finder information for a directory. + The FSpSetDInfo function sets the finder information for a directory. + + spec input: An FSSpec record specifying the directory. + fndrInfo input: The DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpSetFInfoCompat, SetDInfo +*/ + +/*****************************************************************************/ + +#if OLDROUTINENAMES +#define GetDirID(vRefNum, dirID, name, theDirID, isDirectory) \ + GetDirectoryID(vRefNum, dirID, name, theDirID, isDirectory) +#endif + +pascal OSErr GetDirectoryID(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory); +/* ¦ Get the directory ID number of the directory specified. + The GetDirectoryID function gets the directory ID number of the + directory specified. If a file is specified, then the parent + directory of the file is returned and isDirectory is false. If + a directory is specified, then that directory's ID number is + returned and isDirectory is true. + WARNING: Volume names on the Macintosh are *not* unique -- Multiple + mounted volumes can have the same name. For this reason, the use of a + volume name or full pathname to identify a specific volume may not + produce the results you expect. If more than one volume has the same + name and a volume name or full pathname is used, the File Manager + currently uses the first volume it finds with a matching name in the + volume queue. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + theDirID output: If the object is a file, then its parent directory + ID. If the object is a directory, then its ID. + isDirectory output: True if object is a directory; false if + object is a file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +#if OLDROUTINENAMES +#define DirIDFromFSSpec(spec, theDirID, isDirectory) \ + FSpGetDirectoryID(spec, theDirID, isDirectory) +#endif + +pascal OSErr FSpGetDirectoryID(const FSSpec *spec, + long *theDirID, + Boolean *isDirectory); +/* ¦ Get the directory ID number of a directory. + The FSpGetDirectoryID function gets the directory ID number of the + directory specified by spec. If spec is to a file, then the parent + directory of the file is returned and isDirectory is false. If + spec is to a directory, then that directory's ID number is + returned and isDirectory is true. + + spec input: An FSSpec record specifying the directory. + theDirID output: The directory ID. + isDirectory output: True if object is a directory; false if + object is a file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +pascal OSErr GetDirName(short vRefNum, + long dirID, + Str31 name); +/* ¦ Get the name of a directory from its directory ID. + The GetDirName function gets the name of a directory from its + directory ID. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name output: Points to a Str31 where the directory name is to be + returned. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume or + name parameter was NULL + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +pascal OSErr GetIOACUser(short vRefNum, + long dirID, + ConstStr255Param name, + SInt8 *ioACUser); +/* ¦ Get a directory's access restrictions byte. + GetIOACUser returns a directory's access restrictions byte. + Use the masks and macro defined in MoreFilesExtras to check for + specific access priviledges. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + ioACUser output: The access restriction byte + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetIOACUser(const FSSpec *spec, + SInt8 *ioACUser); +/* ¦ Get a directory's access restrictions byte. + FSpGetIOACUser returns a directory's access restrictions byte. + Use the masks and macro defined in MoreFilesExtras to check for + specific access priviledges. + + spec input: An FSSpec record specifying the directory. + ioACUser output: The access restriction byte + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +pascal OSErr GetParentID(short vRefNum, + long dirID, + ConstStr255Param name, + long *parID); +/* ¦ Get the parent directory ID number of the specified object. + The GetParentID function gets the parent directory ID number of the + specified object. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + parID output: The parent directory ID of the specified object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +pascal OSErr GetFilenameFromPathname(ConstStr255Param pathname, + Str255 filename); +/* ¦ Get the object name from the end of a full or partial pathname. + The GetFilenameFromPathname function gets the file (or directory) name + from the end of a full or partial pathname. Returns notAFileErr if the + pathname is nil, the pathname is empty, or the pathname cannot refer to + a filename (with a noErr result, the pathname could still refer to a + directory). + + pathname input: A full or partial pathname. + filename output: The file (or directory) name. + + Result Codes + noErr 0 No error + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + + __________ + + See also: GetObjectLocation. +*/ + +/*****************************************************************************/ + +pascal OSErr GetObjectLocation(short vRefNum, + long dirID, + ConstStr255Param pathname, + short *realVRefNum, + long *realParID, + Str255 realName, + Boolean *isDirectory); +/* ¦ Get a file system object's location. + The GetObjectLocation function gets a file system object's location - + that is, its real volume reference number, real parent directory ID, + and name. While we're at it, determine if the object is a file or directory. + If GetObjectLocation returns fnfErr, then the location information + returned is valid, but it describes an object that doesn't exist. + You can use the location information for another operation, such as + creating a file or directory. + + vRefNum input: Volume specification. + dirID input: Directory ID. + pathname input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + realVRefNum output: The real volume reference number. + realParID output: The parent directory ID of the specified object. + realName output: The name of the specified object (the case of the + object name may not be the same as the object's + catalog entry on disk - since the Macintosh file + system is not case sensitive, it shouldn't matter). + isDirectory output: True if object is a directory; false if object + is a file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSMakeFSSpecCompat +*/ + +/*****************************************************************************/ + +pascal OSErr GetDirItems(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean getFiles, + Boolean getDirectories, + FSSpecPtr items, + short reqItemCount, + short *actItemCount, + short *itemIndex); +/* ¦ Return a list of items in a directory. + The GetDirItems function returns a list of items in the specified + directory in an array of FSSpec records. File, subdirectories, or + both can be returned in the list. + + A noErr result indicates that the items array was filled + (actItemCount == reqItemCount) and there may be additional items + left in the directory. A fnfErr result indicates that the end of + the directory list was found and actItemCount items were actually + found this time. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + getFiles input: Pass true to have files added to the items list. + getDirectories input: Pass true to have directories added to the + items list. + items input: Pointer to array of FSSpec where the item list + is returned. + reqItemCount input: Maximum number of items to return (the number + of elements in the items array). + actItemCount output: The number of items actually returned. + itemIndex input: The current item index position. Set to 1 to + start with the first item in the directory. + output: The item index position to get the next item. + Pass this value the next time you call + GetDirItems to start where you left off. + + Result Codes + noErr 0 No error, but there are more items + to list + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found, there are no more items + to be listed. + paramErr -50 No default volume or itemIndex was <= 0 + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +pascal OSErr DeleteDirectoryContents(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Delete the contents of a directory. + The DeleteDirectoryContents function deletes the contents of a directory. + All files and subdirectories in the specified directory are deleted. + If a locked file or directory is encountered, it is unlocked and then + deleted. If any unexpected errors are encountered, + DeleteDirectoryContents quits and returns to the caller. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + fBsyErr -47 File busy, directory not empty, or working directory control block open + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: DeleteDirectory +*/ + +/*****************************************************************************/ + +pascal OSErr DeleteDirectory(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Delete a directory and its contents. + The DeleteDirectory function deletes a directory and its contents. + All files and subdirectories in the specified directory are deleted. + If a locked file or directory is encountered, it is unlocked and then + deleted. After deleting the directories contents, the directory is + deleted. If any unexpected errors are encountered, DeleteDirectory + quits and returns to the caller. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + fBsyErr -47 File busy, directory not empty, or working directory control block open + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: DeleteDirectoryContents +*/ + +/*****************************************************************************/ + +pascal OSErr CheckObjectLock(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Determine if a file or directory is locked. + The CheckObjectLock function determines if a file or directory is locked. + If CheckObjectLock returns noErr, then the file or directory + is not locked. If CheckObjectLock returns fLckdErr, the it is locked. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpCheckObjectLock +*/ + +/*****************************************************************************/ + +pascal OSErr FSpCheckObjectLock(const FSSpec *spec); +/* ¦ Determine if a file or directory is locked. + The FSpCheckObjectLock function determines if a file or directory is locked. + If FSpCheckObjectLock returns noErr, then the file or directory + is not locked. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: CheckObjectLock +*/ + +/*****************************************************************************/ + +pascal OSErr GetFileSize(short vRefNum, + long dirID, + ConstStr255Param fileName, + long *dataSize, + long *rsrcSize); +/* ¦ Get the logical sizes of a file's forks. + The GetFileSize function returns the logical size of a file's + data and resource fork. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: The name of the file. + dataSize output: The number of bytes in the file's data fork. + rsrcSize output: The number of bytes in the file's resource fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpGetFileSize +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetFileSize(const FSSpec *spec, + long *dataSize, + long *rsrcSize); +/* ¦ Get the logical sizes of a file's forks. + The FSpGetFileSize function returns the logical size of a file's + data and resource fork. + + spec input: An FSSpec record specifying the file. + dataSize output: The number of bytes in the file's data fork. + rsrcSize output: The number of bytes in the file's resource fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: GetFileSize +*/ + +/*****************************************************************************/ + +pascal OSErr BumpDate(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Update the modification date of a file or directory. + The BumpDate function changes the modification date of a file or + directory to the current date/time. If the modification date is already + equal to the current date/time, then add one second to the + modification date. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpBumpDate +*/ + +/*****************************************************************************/ + +pascal OSErr FSpBumpDate(const FSSpec *spec); +/* ¦ Update the modification date of a file or directory. + The FSpBumpDate function changes the modification date of a file or + directory to the current date/time. If the modification date is already + equal to the current date/time, then add one second to the + modification date. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: BumpDate +*/ + +/*****************************************************************************/ + +pascal OSErr ChangeCreatorType(short vRefNum, + long dirID, + ConstStr255Param name, + OSType creator, + OSType fileType); +/* ¦ Change the creator or file type of a file. + The ChangeCreatorType function changes the creator or file type of a file. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: The name of the file. + creator input: The new creator type or 0x00000000 to leave + the creator type alone. + fileType input: The new file type or 0x00000000 to leave the + file type alone. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + notAFileErr -1302 Name was not a file + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpChangeCreatorType +*/ + +/*****************************************************************************/ + +pascal OSErr FSpChangeCreatorType(const FSSpec *spec, + OSType creator, + OSType fileType); +/* ¦ Change the creator or file type of a file. + The FSpChangeCreatorType function changes the creator or file type of a file. + + spec input: An FSSpec record specifying the file. + creator input: The new creator type or 0x00000000 to leave + the creator type alone. + fileType input: The new file type or 0x00000000 to leave the + file type alone. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + notAFileErr -1302 Name was not a file + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: ChangeCreatorType +*/ + +/*****************************************************************************/ + +pascal OSErr ChangeFDFlags(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean setBits, + unsigned short flagBits); +/* ¦ Set or clear Finder Flag bits. + The ChangeFDFlags function sets or clears Finder Flag bits in the + fdFlags field of a file or directory's FInfo record. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + setBits input: If true, then set the bits specified in flagBits. + If false, then clear the bits specified in flagBits. + flagBits input: The flagBits parameter specifies which Finder Flag + bits to set or clear. If a bit in flagBits is set, + then the same bit in fdFlags is either set or + cleared depending on the state of the setBits + parameter. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpChangeFDFlags +*/ + +/*****************************************************************************/ + +pascal OSErr FSpChangeFDFlags(const FSSpec *spec, + Boolean setBits, + unsigned short flagBits); +/* ¦ Set or clear Finder Flag bits. + The FSpChangeFDFlags function sets or clears Finder Flag bits in the + fdFlags field of a file or directory's FInfo record. + + spec input: An FSSpec record specifying the object. + setBits input: If true, then set the bits specified in flagBits. + If false, then clear the bits specified in flagBits. + flagBits input: The flagBits parameter specifies which Finder Flag + bits to set or clear. If a bit in flagBits is set, + then the same bit in fdFlags is either set or + cleared depending on the state of the setBits + parameter. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: ChangeFDFlags +*/ + +/*****************************************************************************/ + +pascal OSErr SetIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Set the invisible Finder Flag bit. + The SetIsInvisible function sets the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetIsInvisible, ClearIsInvisible, FSpClearIsInvisible +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetIsInvisible(const FSSpec *spec); +/* ¦ Set the invisible Finder Flag bit. + The FSpSetIsInvisible function sets the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsInvisible, ClearIsInvisible, FSpClearIsInvisible +*/ + +/*****************************************************************************/ + +pascal OSErr ClearIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Clear the invisible Finder Flag bit. + The ClearIsInvisible function clears the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsInvisible, FSpSetIsInvisible, FSpClearIsInvisible +*/ + +/*****************************************************************************/ + +pascal OSErr FSpClearIsInvisible(const FSSpec *spec); +/* ¦ Clear the invisible Finder Flag bit. + The FSpClearIsInvisible function clears the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsInvisible, FSpSetIsInvisible, ClearIsInvisible +*/ + +/*****************************************************************************/ + +pascal OSErr SetNameLocked(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Set the nameLocked Finder Flag bit. + The SetNameLocked function sets the nameLocked bit in the fdFlags word + of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetNameLocked, ClearNameLocked, FSpClearNameLocked +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetNameLocked(const FSSpec *spec); +/* ¦ Set the nameLocked Finder Flag bit. + The FSpSetNameLocked function sets the nameLocked bit in the fdFlags word + of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetNameLocked, ClearNameLocked, FSpClearNameLocked +*/ + +/*****************************************************************************/ + +pascal OSErr ClearNameLocked(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Clear the nameLocked Finder Flag bit. + The ClearNameLocked function clears the nameLocked bit in the fdFlags + word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetNameLocked, FSpSetNameLocked, FSpClearNameLocked +*/ + +/*****************************************************************************/ + +pascal OSErr FSpClearNameLocked(const FSSpec *spec); +/* ¦ Clear the nameLocked Finder Flag bit. + The FSpClearNameLocked function clears the nameLocked bit in the fdFlags + word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetNameLocked, FSpSetNameLocked, ClearNameLocked +*/ + +/*****************************************************************************/ + +pascal OSErr SetIsStationery(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Set the isStationery Finder Flag bit. + The SetIsStationery function sets the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetIsStationery, ClearIsStationery, FSpClearIsStationery +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetIsStationery(const FSSpec *spec); +/* ¦ Set the isStationery Finder Flag bit. + The FSpSetIsStationery function sets the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsStationery, ClearIsStationery, FSpClearIsStationery +*/ + +/*****************************************************************************/ + +pascal OSErr ClearIsStationery(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Clear the isStationery Finder Flag bit. + The ClearIsStationery function clears the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsStationery, FSpSetIsStationery, FSpClearIsStationery +*/ + +/*****************************************************************************/ + +pascal OSErr FSpClearIsStationery(const FSSpec *spec); +/* ¦ Clear the isStationery Finder Flag bit. + The FSpClearIsStationery function clears the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsStationery, FSpSetIsStationery, ClearIsStationery +*/ + +/*****************************************************************************/ + +pascal OSErr SetHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Set the hasCustomIcon Finder Flag bit. + The SetHasCustomIcon function sets the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetHasCustomIcon, ClearHasCustomIcon, FSpClearHasCustomIcon +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetHasCustomIcon(const FSSpec *spec); +/* ¦ Set the hasCustomIcon Finder Flag bit. + The FSpSetHasCustomIcon function sets the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetHasCustomIcon, ClearHasCustomIcon, FSpClearHasCustomIcon +*/ + +/*****************************************************************************/ + +pascal OSErr ClearHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Clear the hasCustomIcon Finder Flag bit. + The ClearHasCustomIcon function clears the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetHasCustomIcon, FSpSetHasCustomIcon, FSpClearHasCustomIcon +*/ + +/*****************************************************************************/ + +pascal OSErr FSpClearHasCustomIcon(const FSSpec *spec); +/* ¦ Clear the hasCustomIcon Finder Flag bit. + The FSpClearHasCustomIcon function clears the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetHasCustomIcon, FSpSetHasCustomIcon, ClearHasCustomIcon +*/ + +/*****************************************************************************/ + +pascal OSErr ClearHasBeenInited(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Clear the hasBeenInited Finder Flag bit. + The ClearHasBeenInited function clears the hasBeenInited bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpClearHasBeenInited +*/ + +/*****************************************************************************/ + +pascal OSErr FSpClearHasBeenInited(const FSSpec *spec); +/* ¦ Clear the hasBeenInited Finder Flag bit. + The FSpClearHasBeenInited function clears the hasBeenInited bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: ClearHasBeenInited +*/ + +/*****************************************************************************/ + +pascal OSErr CopyFileMgrAttributes(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + Boolean copyLockBit); +/* ¦ Copy all File Manager attributes from the source to the destination. + The CopyFileMgrAttributes function copies all File Manager attributes + from the source file or directory to the destination file or directory. + If copyLockBit is true, then set the locked state of the destination + to match the source. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Pointer to source object name, or nil when + srcDirID specifies a directory that's the object. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination object name, or nil when + dstDirID specifies a directory that's the object. + copyLockBit input: If true, set the locked state of the destination + to match the source. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpCopyFileMgrAttributes +*/ + +/*****************************************************************************/ + +pascal OSErr FSpCopyFileMgrAttributes(const FSSpec *srcSpec, + const FSSpec *dstSpec, + Boolean copyLockBit); +/* ¦ Copy all File Manager attributes from the source to the destination. + The FSpCopyFileMgrAttributes function copies all File Manager attributes + from the source file or directory to the destination file or directory. + If copyLockBit is true, then set the locked state of the destination + to match the source. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination object. + copyLockBit input: If true, set the locked state of the destination + to match the source. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: CopyFileMgrAttributes +*/ + +/*****************************************************************************/ + +pascal OSErr HOpenAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum); +/* ¦ Open the data fork of a file using deny mode permissions. + The HOpenAware function opens the data fork of a file using deny mode + permissions instead the normal File Manager permissions. If OpenDeny + is not available, then HOpenAware translates the deny modes to the + closest File Manager permissions and tries to open the file with + OpenDF first, and then Open if OpenDF isn't available. By using + HOpenAware with deny mode permissions, a program can be "AppleShare + aware" and fall back on the standard File Manager open calls + automatically. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: The name of the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: FSpOpenAware, HOpenRFAware, FSpOpenRFAware +*/ + +/*****************************************************************************/ + +pascal OSErr FSpOpenAware(const FSSpec *spec, + short denyModes, + short *refNum); +/* ¦ Open the data fork of a file using deny mode permissions. + The FSpOpenAware function opens the data fork of a file using deny mode + permissions instead the normal File Manager permissions. If OpenDeny + is not available, then FSpOpenAware translates the deny modes to the + closest File Manager permissions and tries to open the file with + OpenDF first, and then Open if OpenDF isn't available. By using + FSpOpenAware with deny mode permissions, a program can be "AppleShare + aware" and fall back on the standard File Manager open calls + automatically. + + spec input: An FSSpec record specifying the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: HOpenAware, HOpenRFAware, FSpOpenRFAware +*/ + +/*****************************************************************************/ + +pascal OSErr HOpenRFAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum); +/* ¦ Open the resource fork of a file using deny mode permissions. + The HOpenRFAware function opens the resource fork of a file using deny + mode permissions instead the normal File Manager permissions. If + OpenRFDeny is not available, then HOpenRFAware translates the deny + modes to the closest File Manager permissions and tries to open the + file with OpenRF. By using HOpenRFAware with deny mode permissions, + a program can be "AppleShare aware" and fall back on the standard + File Manager open calls automatically. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: The name of the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: HOpenAware, FSpOpenAware, FSpOpenRFAware +*/ + +/*****************************************************************************/ + +pascal OSErr FSpOpenRFAware(const FSSpec *spec, + short denyModes, + short *refNum); +/* ¦ Open the resource fork of a file using deny mode permissions. + The FSpOpenRFAware function opens the resource fork of a file using deny + mode permissions instead the normal File Manager permissions. If + OpenRFDeny is not available, then FSpOpenRFAware translates the deny + modes to the closest File Manager permissions and tries to open the + file with OpenRF. By using FSpOpenRFAware with deny mode permissions, + a program can be "AppleShare aware" and fall back on the standard + File Manager open calls automatically. + + spec input: An FSSpec record specifying the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: HOpenAware, FSpOpenAware, HOpenRFAware +*/ + +/*****************************************************************************/ + +pascal OSErr FSReadNoCache(short refNum, + long *count, + void *buffPtr); +/* ¦ Read any number of bytes from an open file requesting no caching. + The FSReadNoCache function reads any number of bytes from an open file + while asking the file system to bypass its cache mechanism. + + refNum input: The file reference number of an open file. + count input: The number of bytes to read. + output: The number of bytes actually read. + buffPtr input: A pointer to the data buffer into which the bytes are + to be read. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + rfNumErr -51 Bad reference number + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSWriteNoCache +*/ + +/*****************************************************************************/ + +pascal OSErr FSWriteNoCache(short refNum, + long *count, + const void *buffPtr); +/* ¦ Write any number of bytes to an open file requesting no caching. + The FSReadNoCache function writes any number of bytes to an open file + while asking the file system to bypass its cache mechanism. + + refNum input: The file reference number of an open file. + count input: The number of bytes to write to the file. + output: The number of bytes actually written. + buffPtr input: A pointer to the data buffer from which the bytes are + to be written. + + Result Codes + noErr 0 No error + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Disk full + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + rfNumErr -51 Bad reference number + wrPermErr -61 Read/write permission doesnÕt + allow writing + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSReadNoCache +*/ + +/*****************************************************************************/ + +pascal OSErr FSWriteVerify(short refNum, + long *count, + const void *buffPtr); +/* ¦ Write any number of bytes to an open file and then verify the data was written. + The FSWriteVerify function writes any number of bytes to an open file + and then verifies that the data was actually written to the device. + + refNum input: The file reference number of an open file. + count input: The number of bytes to write to the file. + output: The number of bytes actually written and verified. + buffPtr input: A pointer to the data buffer from which the bytes are + to be written. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Disk full + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + eofErr -39 Logical end-of-file reached + posErr -40 Attempt to position mark before start + of file + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + rfNumErr -51 Bad reference number + gfpErr -52 Error during GetFPos + wrPermErr -61 Read/write permission doesnÕt + allow writing + memFullErr -108 Not enough room in heap zone to allocate + verify buffer + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +pascal OSErr CopyFork(short srcRefNum, + short dstRefNum, + void *copyBufferPtr, + long copyBufferSize); +/* ¦ Copy all data from the source fork to the destination fork of open file forks. + The CopyFork function copies all data from the source fork to the + destination fork of open file forks and makes sure the destination EOF + is equal to the source EOF. + + srcRefNum input: The source file reference number. + dstRefNum input: The destination file reference number. + copyBufferPtr input: Pointer to buffer to use during copy. The + buffer should be at least 512-bytes minimum. + The larger the buffer, the faster the copy. + copyBufferSize input: The size of the copy buffer. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Disk full + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + rfNumErr -51 Bad reference number + wrPermErr -61 Read/write permission doesnÕt + allow writing + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +pascal OSErr GetFileLocation(short refNum, + short *vRefNum, + long *dirID, + StringPtr fileName); +/* ¦ Get the location of an open file. + The GetFileLocation function gets the location (volume reference number, + directory ID, and fileName) of an open file. + + refNum input: The file reference number of an open file. + vRefNum output: The volume reference number. + dirID output: The parent directory ID. + fileName input: Points to a buffer (minimum Str63) where the + filename is to be returned or must be nil. + output: The filename. + + Result Codes + noErr 0 No error + nsvErr -35 Specified volume doesnÕt exist + fnOpnErr -38 File not open + rfNumErr -51 Reference number specifies nonexistent + access path + + __________ + + See also: FSpGetFileLocation +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetFileLocation(short refNum, + FSSpec *spec); +/* ¦ Get the location of an open file in an FSSpec record. + The FSpGetFileLocation function gets the location of an open file in + an FSSpec record. + + refNum input: The file reference number of an open file. + spec output: FSSpec record containing the file name and location. + + Result Codes + noErr 0 No error + nsvErr -35 Specified volume doesnÕt exist + fnOpnErr -38 File not open + rfNumErr -51 Reference number specifies nonexistent + access path + + __________ + + See also: GetFileLocation +*/ + +/*****************************************************************************/ + +pascal OSErr CopyDirectoryAccess(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName); +/* ¦ Copy the AFP directory access privileges. + The CopyDirectoryAccess function copies the AFP directory access + privileges from one directory to another. Both directories must be on + the same file server, but not necessarily on the same server volume. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Pointer to source directory name, or nil when + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination directory name, or nil when + dstDirID specifies the directory. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Volume doesn't support this function + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + See also: FSpCopyDirectoryAccess +*/ + +/*****************************************************************************/ + +pascal OSErr FSpCopyDirectoryAccess(const FSSpec *srcSpec, + const FSSpec *dstSpec); +/* ¦ Copy the AFP directory access privileges. + The FSpCopyDirectoryAccess function copies the AFP directory access + privileges from one directory to another. Both directories must be on + the same file server, but not necessarily on the same server volume. + + srcSpec input: An FSSpec record specifying the source directory. + dstSpec input: An FSSpec record specifying the destination directory. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Volume doesn't support this function + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + See also: CopyDirectoryAccess +*/ + +/*****************************************************************************/ + +pascal OSErr HMoveRenameCompat(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName); +/* ¦ Move a file or directory and optionally rename it. + The HMoveRenameCompat function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + volume. This routine works even if the volume doesn't support MoveRename. + + vRefNum input: Volume specification. + srcDirID input: Source directory ID. + srcName input: The source object name. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 Volume not found + ioErr -36 I/O error + bdNamErr -37 Bad filename or attempt to move into + a file + fnfErr -43 Source file or directory not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 File busy, directory not empty, or + working directory control block open + dupFNErr -48 Destination already exists + paramErr -50 Volume doesn't support this function, + no default volume, or source and + volOfflinErr -53 Volume is offline + fsRnErr -59 Problem during rename + dirNFErr -120 Directory not found or incomplete pathname + badMovErr -122 Attempted to move directory into + offspring + wrgVolTypErr -123 Not an HFS volume (it's a MFS volume) + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 The user does not have the right to + move the file or directory + afpObjectTypeErr -5025 Directory not found or incomplete pathname + afpSameObjectErr -5038 Source and destination files are the same + + __________ + + See also: FSpMoveRenameCompat +*/ + +/*****************************************************************************/ + +pascal OSErr FSpMoveRenameCompat(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName); +/* ¦ Move a file or directory and optionally rename it. + The FSpMoveRenameCompat function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + volume. This routine works even if the volume doesn't support MoveRename. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 Volume not found + ioErr -36 I/O error + bdNamErr -37 Bad filename or attempt to move into + a file + fnfErr -43 Source file or directory not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 File busy, directory not empty, or + working directory control block open + dupFNErr -48 Destination already exists + paramErr -50 Volume doesn't support this function, + no default volume, or source and + volOfflinErr -53 Volume is offline + fsRnErr -59 Problem during rename + dirNFErr -120 Directory not found or incomplete pathname + badMovErr -122 Attempted to move directory into + offspring + wrgVolTypErr -123 Not an HFS volume (it's a MFS volume) + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 The user does not have the right to + move the file or directory + afpObjectTypeErr -5025 Directory not found or incomplete pathname + afpSameObjectErr -5038 Source and destination files are the same + + __________ + + See also: HMoveRenameCompat +*/ + +/*****************************************************************************/ + +pascal OSErr BuildAFPVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str31 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + AFPVolMountInfoPtr *afpInfoPtr); +/* ¦ Allocate and initializes the fields of an AFPVolMountInfo record. + The BuildAFPVolMountInfo function allocates and initializes the fields + of an AFPVolMountInfo record before using that record to call + the VolumeMount function. + + flags input: The AFP mounting flags. 0 = normal mount; + set bit 0 to inhibit greeting messages. + nbpInterval input: The interval used for VolumeMount's + NBP Lookup call. 7 is a good choice. + nbpCount input: The retry count used for VolumeMount's + NBP Lookup call. 5 is a good choice. + uamType input: The user authentication method to use. + zoneName input: The AppleTalk zone name of the server. + serverName input: The AFP server name. + volName input: The AFP volume name. + userName input: The user name (zero length Pascal string for + guest). + userPassWord input: The user password (zero length Pascal string + if no user password) + volPassWord input: The volume password (zero length Pascal string + if no volume password) + afpInfoPtr output: A pointer to the newly created and initialized + AFPVolMountInfo record. If the function fails to + create an AFPVolMountInfo record, it sets + afpInfoPtr to NULL and the function result is + memFullErr. Your program is responsible + for disposing of this pointer when it is finished + with it. + + Result Codes + noErr 0 No error + memFullErr -108 memory full error + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + RetrieveAFPVolMountInfo, BuildAFPXVolMountInfo, + RetrieveAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPVolMountInfo(AFPVolMountInfoPtr afpInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName); +/* ¦ Retrieve the AFP mounting information from an AFPVolMountInfo record. + The RetrieveAFPVolMountInfo function retrieves the AFP mounting + information returned in an AFPVolMountInfo record by the + GetVolMountInfo function. + + afpInfoPtr input: Pointer to AFPVolMountInfo record that contains + the AFP mounting information. + flags output: The AFP mounting flags. + uamType output: The user authentication method used. + zoneName output: The AppleTalk zone name of the server. + serverName output: The AFP server name. + volName output: The AFP volume name. + userName output: The user name (zero length Pascal string for + guest). + + Result Codes + noErr 0 No error + paramErr -50 media field in AFP mounting information + was not AppleShareMediaType + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + BuildAFPVolMountInfo, BuildAFPXVolMountInfo, + RetrieveAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +pascal OSErr BuildAFPXVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str31 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + Str32 uamName, + unsigned long alternateAddressLength, + void *alternateAddress, + AFPXVolMountInfoPtr *afpXInfoPtr); +/* ¦ Allocate and initializes the fields of an AFPXVolMountInfo record. + The BuildAFPXVolMountInfo function allocates and initializes the fields + of an AFPXVolMountInfo record before using that record to call + the VolumeMount function. + + flags input: The AFP mounting flags. + nbpInterval input: The interval used for VolumeMount's + NBP Lookup call. 7 is a good choice. + nbpCount input: The retry count used for VolumeMount's + NBP Lookup call. 5 is a good choice. + uamType input: The user authentication method to use. + zoneName input: The AppleTalk zone name of the server. + serverName input: The AFP server name. + volName input: The AFP volume name. + userName input: The user name (zero length Pascal string + for guest). + userPassWord input: The user password (zero length Pascal + string if no user password) + volPassWord input: The volume password (zero length Pascal + string if no volume password) + uamName input: The User Authentication Method name. + alternateAddressLength input: Length of alternateAddress data. + alternateAddress input The AFPAlternateAddress (variable length) + afpXInfoPtr output: A pointer to the newly created and + initialized AFPVolMountInfo record. + If the function fails to create an + AFPVolMountInfo record, it sets + afpInfoPtr to NULL and the function + result is memFullErr. Your program is + responsible for disposing of this pointer + when it is finished with it. + + Result Codes + noErr 0 No error + memFullErr -108 memory full error + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + BuildAFPVolMountInfo, RetrieveAFPVolMountInfo, + RetrieveAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPXVolMountInfo(AFPXVolMountInfoPtr afpXInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName, + StringPtr uamName, + unsigned long *alternateAddressLength, + AFPAlternateAddress **alternateAddress); +/* ¦ Retrieve the AFP mounting information from an AFPXVolMountInfo record. + The RetrieveAFPXVolMountInfo function retrieves the AFP mounting + information returned in an AFPXVolMountInfo record by the + GetVolMountInfo function. + + afpXInfoPtr input: Pointer to AFPXVolMountInfo record that + contains the AFP mounting information. + flags output: The AFP mounting flags. + uamType output: The user authentication method used. + zoneName output: The AppleTalk zone name of the server. + serverName output: The AFP server name. + volName output: The AFP volume name. + userName output: The user name (zero length Pascal + string for guest). + uamName output: The User Authentication Method name. + alternateAddressLength output: Length of alternateAddress data returned. + alternateAddress: output: A pointer to the newly created and + AFPAlternateAddress record (a variable + length record). If the function fails to + create an AFPAlternateAddress record, + it sets alternateAddress to NULL and the + function result is memFullErr. Your + program is responsible for disposing of + this pointer when it is finished with it. + + Result Codes + noErr 0 No error + paramErr -50 media field in AFP mounting information + was not AppleShareMediaType + memFullErr -108 memory full error + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + BuildAFPVolMountInfo, RetrieveAFXVolMountInfo, + BuildAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +pascal OSErr GetUGEntries(short objType, + UGEntryPtr entries, + long reqEntryCount, + long *actEntryCount, + long *objID); +/* ¦ Retrieve a list of user or group entries from the local file server. + The GetUGEntries functions retrieves a list of user or group entries + from the local file server. + + objType input: The object type: -1 = group; 0 = user + UGEntries input: Pointer to array of UGEntry records where the list + is returned. + reqEntryCount input: The number of elements in the UGEntries array. + actEntryCount output: The number of entries returned. + objID input: The current index position. Set to 0 to start with + the first entry. + output: The index position to get the next entry. Pass this + value the next time you call GetUGEntries to start + where you left off. + + Result Codes + noErr 0 No error + fnfErr -43 No more users or groups + paramErr -50 Function not supported; or, ioObjID is + negative + + __________ + + Also see: GetUGEntry +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __MOREFILESEXTRAS__ */ diff --git a/src/mac/morefile/MoreFile.cpp b/src/mac/morefile/MoreFile.cpp new file mode 100644 index 0000000000..16795680b6 --- /dev/null +++ b/src/mac/morefile/MoreFile.cpp @@ -0,0 +1,628 @@ +/* +** Apple Macintosh Developer Technical Support +** +** The long lost high-level and FSSpec File Manager functions. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: MoreFiles.c +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFile.h" +#include "MoreExtr.h" + +/*****************************************************************************/ + +pascal OSErr HGetVolParms(ConstStr255Param volName, + short vRefNum, + GetVolParmsInfoBuffer *volParmsInfo, + long *infoSize) +{ + HParamBlockRec pb; + OSErr error; + + pb.ioParam.ioNamePtr = (StringPtr)volName; + pb.ioParam.ioVRefNum = vRefNum; + pb.ioParam.ioBuffer = (Ptr)volParmsInfo; + pb.ioParam.ioReqCount = *infoSize; + error = PBHGetVolParmsSync(&pb); + if ( error == noErr ) + { + *infoSize = pb.ioParam.ioActCount; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HCreateMinimum(short vRefNum, + long dirID, + ConstStr255Param fileName) +{ + HParamBlockRec pb; + + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioDirID = dirID; + return ( PBHCreateSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCreateMinimum(const FSSpec *spec) +{ + return ( HCreateMinimum(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr ExchangeFiles(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstName) +{ + HParamBlockRec pb; + + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioSrcDirID = srcDirID; + pb.fidParam.ioNamePtr = (StringPtr)srcName; + pb.fidParam.ioDestDirID = dstDirID; + pb.fidParam.ioDestNamePtr = (StringPtr)dstName; + return ( PBExchangeFilesSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr ResolveFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID, + long *parID, + StringPtr fileName) +{ + HParamBlockRec pb; + OSErr error; + Str255 tempStr; + + tempStr[0] = 0; + if ( volName != NULL ) + { + BlockMoveData(volName, tempStr, volName[0] + 1); + } + pb.fidParam.ioNamePtr = (StringPtr)tempStr; + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioFileID = fileID; + error = PBResolveFileIDRefSync(&pb); + if ( error == noErr ) + { + *parID = pb.fidParam.ioSrcDirID; + if ( fileName != NULL ) + { + BlockMoveData(tempStr, fileName, tempStr[0] + 1); + } + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpResolveFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID, + FSSpec *spec) +{ + OSErr error; + + error = DetermineVRefNum(volName, vRefNum, &(spec->vRefNum)); + if ( error == noErr ) + { + error = ResolveFileIDRef(volName, vRefNum, fileID, &(spec->parID), spec->name); + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr CreateFileIDRef(short vRefNum, + long parID, + ConstStr255Param fileName, + long *fileID) +{ + HParamBlockRec pb; + OSErr error; + + pb.fidParam.ioNamePtr = (StringPtr)fileName; + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioSrcDirID = parID; + error = PBCreateFileIDRefSync(&pb); + if ( error == noErr ) + { + *fileID = pb.fidParam.ioFileID; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCreateFileIDRef(const FSSpec *spec, + long *fileID) +{ + return ( CreateFileIDRef(spec->vRefNum, spec->parID, spec->name, fileID) ); +} + +/*****************************************************************************/ + +pascal OSErr DeleteFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID) +{ + HParamBlockRec pb; + + pb.fidParam.ioNamePtr = (StringPtr)volName; + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioFileID = fileID; + return ( PBDeleteFileIDRefSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FlushFile(short refNum) +{ + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + return ( PBFlushFileSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr LockRange(short refNum, + long rangeLength, + long rangeStart) +{ + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + return ( PBLockRangeSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr UnlockRange(short refNum, + long rangeLength, + long rangeStart) +{ + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + return ( PBUnlockRangeSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr GetForeignPrivs(short vRefNum, + long dirID, + ConstStr255Param name, + void *foreignPrivBuffer, + long *foreignPrivSize, + long *foreignPrivInfo1, + long *foreignPrivInfo2, + long *foreignPrivInfo3, + long *foreignPrivInfo4) +{ + HParamBlockRec pb; + OSErr error; + + pb.foreignPrivParam.ioNamePtr = (StringPtr)name; + pb.foreignPrivParam.ioVRefNum = vRefNum; + pb.foreignPrivParam.ioForeignPrivDirID = dirID; + pb.foreignPrivParam.ioForeignPrivBuffer = (Ptr)foreignPrivBuffer; + pb.foreignPrivParam.ioForeignPrivReqCount = *foreignPrivSize; + error = PBGetForeignPrivsSync(&pb); + *foreignPrivSize = pb.foreignPrivParam.ioForeignPrivActCount; + *foreignPrivInfo1 = pb.foreignPrivParam.ioForeignPrivInfo1; + *foreignPrivInfo2 = pb.foreignPrivParam.ioForeignPrivInfo2; + *foreignPrivInfo3 = pb.foreignPrivParam.ioForeignPrivInfo3; + *foreignPrivInfo4 = pb.foreignPrivParam.ioForeignPrivInfo4; + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetForeignPrivs(const FSSpec *spec, + void *foreignPrivBuffer, + long *foreignPrivSize, + long *foreignPrivInfo1, + long *foreignPrivInfo2, + long *foreignPrivInfo3, + long *foreignPrivInfo4) +{ + return ( GetForeignPrivs(spec->vRefNum, spec->parID, spec->name, + foreignPrivBuffer, foreignPrivSize, + foreignPrivInfo1, foreignPrivInfo2, + foreignPrivInfo3, foreignPrivInfo4) ); +} + +/*****************************************************************************/ + +pascal OSErr SetForeignPrivs(short vRefNum, + long dirID, + ConstStr255Param name, + const void *foreignPrivBuffer, + long *foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4) +{ + HParamBlockRec pb; + OSErr error; + + pb.foreignPrivParam.ioNamePtr = (StringPtr)name; + pb.foreignPrivParam.ioVRefNum = vRefNum; + pb.foreignPrivParam.ioForeignPrivDirID = dirID; + pb.foreignPrivParam.ioForeignPrivBuffer = (Ptr)foreignPrivBuffer; + pb.foreignPrivParam.ioForeignPrivReqCount = *foreignPrivSize; + pb.foreignPrivParam.ioForeignPrivInfo1 = foreignPrivInfo1; + pb.foreignPrivParam.ioForeignPrivInfo2 = foreignPrivInfo2; + pb.foreignPrivParam.ioForeignPrivInfo3 = foreignPrivInfo3; + pb.foreignPrivParam.ioForeignPrivInfo4 = foreignPrivInfo4; + error = PBSetForeignPrivsSync(&pb); + if ( error == noErr ) + { + *foreignPrivSize = pb.foreignPrivParam.ioForeignPrivActCount; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetForeignPrivs(const FSSpec *spec, + const void *foreignPrivBuffer, + long *foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4) +{ + return ( SetForeignPrivs(spec->vRefNum, spec->parID, spec->name, + foreignPrivBuffer, foreignPrivSize, + foreignPrivInfo1, foreignPrivInfo2, + foreignPrivInfo3, foreignPrivInfo4) ); +} + +/*****************************************************************************/ + +pascal OSErr HGetLogInInfo(ConstStr255Param volName, + short vRefNum, + short *loginMethod, + StringPtr userName) +{ + HParamBlockRec pb; + OSErr error; + + pb.objParam.ioNamePtr = (StringPtr)volName; + pb.objParam.ioVRefNum = vRefNum; + pb.objParam.ioObjNamePtr = userName; + error = PBHGetLogInInfoSync(&pb); + if ( error == noErr ) + { + *loginMethod = pb.objParam.ioObjType; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HGetDirAccess(short vRefNum, + long dirID, + ConstStr255Param name, + long *ownerID, + long *groupID, + long *accessRights) +{ + HParamBlockRec pb; + OSErr error; + + pb.accessParam.ioNamePtr = (StringPtr)name; + pb.accessParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + error = PBHGetDirAccessSync(&pb); + if ( error == noErr ) + { + *ownerID = pb.accessParam.ioACOwnerID; + *groupID = pb.accessParam.ioACGroupID; + *accessRights = pb.accessParam.ioACAccess; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDirAccess(const FSSpec *spec, + long *ownerID, + long *groupID, + long *accessRights) +{ + return ( HGetDirAccess(spec->vRefNum, spec->parID, spec->name, + ownerID, groupID, accessRights) ); +} + +/*****************************************************************************/ + +pascal OSErr HSetDirAccess(short vRefNum, + long dirID, + ConstStr255Param name, + long ownerID, + long groupID, + long accessRights) +{ + HParamBlockRec pb; + + pb.accessParam.ioNamePtr = (StringPtr)name; + pb.accessParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + pb.accessParam.ioACOwnerID = ownerID; + pb.accessParam.ioACGroupID = groupID; + pb.accessParam.ioACAccess = accessRights; + return ( PBHSetDirAccessSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetDirAccess(const FSSpec *spec, + long ownerID, + long groupID, + long accessRights) +{ + return ( HSetDirAccess(spec->vRefNum, spec->parID, spec->name, + ownerID, groupID, accessRights) ); +} + +/*****************************************************************************/ + +pascal OSErr HMapID(ConstStr255Param volName, + short vRefNum, + long ugID, + short objType, + StringPtr name) +{ + HParamBlockRec pb; + + pb.objParam.ioNamePtr = (StringPtr)volName; + pb.objParam.ioVRefNum = vRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = name; + pb.objParam.ioObjID = ugID; + return ( PBHMapIDSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr HMapName(ConstStr255Param volName, + short vRefNum, + ConstStr255Param name, + short objType, + long *ugID) +{ + HParamBlockRec pb; + OSErr error; + + pb.objParam.ioNamePtr = (StringPtr)volName; + pb.objParam.ioVRefNum = vRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = (StringPtr)name; + error = PBHMapNameSync(&pb); + if ( error == noErr ) + { + *ugID = pb.objParam.ioObjID; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HCopyFile(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName) +{ + HParamBlockRec pb; + + pb.copyParam.ioVRefNum = srcVRefNum; + pb.copyParam.ioDirID = srcDirID; + pb.copyParam.ioNamePtr = (StringPtr)srcName; + pb.copyParam.ioDstVRefNum = dstVRefNum; + pb.copyParam.ioNewDirID = dstDirID; + pb.copyParam.ioNewName = (StringPtr)dstPathname; + pb.copyParam.ioCopyName = (StringPtr)copyName; + return ( PBHCopyFileSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyFile(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName) +{ + return ( HCopyFile(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, + dstSpec->name, copyName) ); +} + +/*****************************************************************************/ + +pascal OSErr HMoveRename(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName) +{ + HParamBlockRec pb; + + pb.copyParam.ioVRefNum = vRefNum; + pb.copyParam.ioDirID = srcDirID; + pb.copyParam.ioNamePtr = (StringPtr)srcName; + pb.copyParam.ioNewDirID = dstDirID; + pb.copyParam.ioNewName = (StringPtr)dstpathName; + pb.copyParam.ioCopyName = (StringPtr)copyName; + return ( PBHMoveRenameSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpMoveRename(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName) +{ + OSErr error; + + /* make sure the FSSpecs refer to the same volume */ + if ( srcSpec->vRefNum != dstSpec->vRefNum ) + { + error = diffVolErr; + } + else + { + error = HMoveRename(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->parID, dstSpec->name, copyName); + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetVolMountInfoSize(ConstStr255Param volName, + short vRefNum, + short *size) +{ + ParamBlockRec pb; + + pb.ioParam.ioNamePtr = (StringPtr)volName; + pb.ioParam.ioVRefNum = vRefNum; + pb.ioParam.ioBuffer = (Ptr)size; + return ( PBGetVolMountInfoSize(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr GetVolMountInfo(ConstStr255Param volName, + short vRefNum, + void *volMountInfo) +{ + ParamBlockRec pb; + + pb.ioParam.ioNamePtr = (StringPtr)volName; + pb.ioParam.ioVRefNum = vRefNum; + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + return ( PBGetVolMountInfo(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr VolumeMount(const void *volMountInfo, + short *vRefNum) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + error = PBVolumeMount(&pb); + if ( error == noErr ) + { + *vRefNum = pb.ioParam.ioVRefNum; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr Share(short vRefNum, + long dirID, + ConstStr255Param name) +{ + HParamBlockRec pb; + + pb.fileParam.ioNamePtr = (StringPtr)name; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + return ( PBShareSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpShare(const FSSpec *spec) +{ + return ( Share(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr Unshare(short vRefNum, + long dirID, + ConstStr255Param name) +{ + HParamBlockRec pb; + + pb.fileParam.ioNamePtr = (StringPtr)name; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + return ( PBUnshareSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpUnshare(const FSSpec *spec) +{ + return ( Unshare(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr GetUGEntry(short objType, + StringPtr objName, + long *objID) +{ + HParamBlockRec pb; + OSErr error; + + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = objName; + pb.objParam.ioObjID = *objID; + error = PBGetUGEntrySync(&pb); + if ( error == noErr ) + { + *objID = pb.objParam.ioObjID; + } + return ( error ); +} + +/*****************************************************************************/ diff --git a/src/mac/morefile/MoreFile.h b/src/mac/morefile/MoreFile.h new file mode 100644 index 0000000000..e4d4bb8a7f --- /dev/null +++ b/src/mac/morefile/MoreFile.h @@ -0,0 +1,1244 @@ +/* +** Apple Macintosh Developer Technical Support +** +** The long lost high-level and FSSpec File Manager functions. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: MoreFiles.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __MOREFILES__ +#define __MOREFILES__ + +#include +#include + +#ifndef true +#define true 1 +#define false 0 +#endif + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +pascal OSErr HGetVolParms(ConstStr255Param volName, + short vRefNum, + GetVolParmsInfoBuffer *volParmsInfo, + long *infoSize); +/* ¦ Determine the characteristics of a volume. + The HGetVolParms function returns information about the characteristics + of a volume. A result of paramErr usually just means the volume doesn't + support PBHGetVolParms and the feature you were going to check + for isn't available. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + volParmsInfo input: Pointer to GetVolParmsInfoBuffer where the + volume attributes information is returned. + output: Atributes information. + infoSize input: Size of buffer pointed to by volParmsInfo. + output: Size of data actually returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume doesn't support this function + + __________ + + Also see the macros for checking attribute bits in MoreFilesExtras.h +*/ + +/*****************************************************************************/ + +pascal OSErr HCreateMinimum(short vRefNum, + long dirID, + ConstStr255Param fileName); +/* ¦ Create a new file with no creator or file type. + The HCreateMinimum function creates a new file without attempting to set + the creator and file type of the new file. This function is needed to + create a file in an AppleShare "drop box" where the user can make + changes, but cannot see folder or files. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: The name of the new file. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 A directory exists with that name + + __________ + + Also see: FSpCreateMinimum +*/ + +/*****************************************************************************/ + +pascal OSErr FSpCreateMinimum(const FSSpec *spec); +/* ¦ Create a new file with no creator or file type. + The FSpCreateMinimum function creates a new file without attempting to set + the the creator and file type of the new file. This function is needed to + create a file in an AppleShare "dropbox" where the user can make + changes, but cannot see folder or files. + + spec input: An FSSpec record specifying the file to create. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 A directory exists with that name + + __________ + + Also see: HCreateMinimum +*/ + +/*****************************************************************************/ + +pascal OSErr ExchangeFiles(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstName); +/* ¦ Exchange the data stored in two files on the same volume. + The ExchangeFiles function swaps the data in two files on the same + volume by changing some of the information in the volume catalog and, + if the files are open, in the file control blocks. + + vRefNum input: Volume specification. + srcDirID input: Source directory ID. + srcName input: Source file name. + dstDirID input: Destination directory ID. + dstName input: Destination file name. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + wrgVolTypErr -123 Not an HFS volume + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Object is a directory, not a file + afpSameObjectErr -5038 Source and destination are the same + + __________ + + Also see: FSpExchangeFilesCompat +*/ + +/*****************************************************************************/ + +pascal OSErr ResolveFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID, + long *parID, + StringPtr fileName); +/* ¦ Retrieve the location of the file with the specified file ID reference. + The ResolveFileIDRef function returns the filename and parent directory ID + of the file with the specified file ID reference. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileID input: The file ID reference. + parID output: The parent directory ID of the file. + name input: Points to a buffer (minimum Str63) where the filename + is to be returned or must be nil. + output: The filename. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidNotFoundErr -1300 File ID reference not found + notAFileErr -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDNotFound -5034 File ID reference not found + afpBadIDErr -5039 File ID reference not found + + __________ + + Also see: FSpResolveFileIDRef, CreateFileIDRef, FSpCreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +pascal OSErr FSpResolveFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID, + FSSpecPtr spec); +/* ¦ Retrieve the location of the file with the specified file ID reference. + The FSpResolveFileIDRef function fills in an FSSpec with the location + of the file with the specified file ID reference. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileID input: The file ID reference. + spec input: A pointer to a FSSpec record. + output: A file system specification to be filled in by + FSpResolveFileIDRef. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Function not supported by volume or + no default volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidNotFoundErr -1300 File ID reference not found + notAFileErr -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDNotFound -5034 File ID reference not found + afpBadIDErr -5039 File ID reference not found + + __________ + + Also see: ResolveFileIDRef, CreateFileIDRef, FSpCreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +pascal OSErr CreateFileIDRef(short vRefNum, + long parID, + ConstStr255Param fileName, + long *fileID); +/* ¦ Establish a file ID reference for a file. + The CreateFileIDRef function creates a file ID reference for the + specified file, or if a file ID reference already exists, supplies + the file ID reference and returns the result code fidExists. + + vRefNum input: Volume specification. + parID input: Directory ID. + fileName input: The name of the file. + fileID output: The file ID reference. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidExists -1301 File ID reference already exists + notAFileErrn -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDExists -5035 File ID reference already exists + + __________ + + Also see: FSpResolveFileIDRef, ResolveFileIDRef, FSpCreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +pascal OSErr FSpCreateFileIDRef(const FSSpec *spec, + long *fileID); +/* ¦ Establish a file ID reference for a file. + The FSpCreateFileIDRef function creates a file ID reference for the + specified file, or if a file ID reference already exists, supplies + the file ID reference and returns the result code fidExists. + + spec input: An FSSpec record specifying the file. + fileID output: The file ID reference. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidExists -1301 File ID reference already exists + notAFileErrn -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDExists -5035 File ID reference already exists + + __________ + + Also see: FSpResolveFileIDRef, ResolveFileIDRef, CreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +pascal OSErr DeleteFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID); +/* ¦ Delete a file ID reference. + The DeleteFileIDRef function deletes a file ID reference. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileID input: The file ID reference. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Function is not supported by volume + fidNotFoundErr -1300 File ID reference not found + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDNotFound -5034 File ID reference not found + + __________ + + Also see: FSpResolveFileIDRef, ResolveFileIDRef, CreateFileIDRef, + FSpCreateFileIDRef +*/ + +/*****************************************************************************/ + +pascal OSErr FlushFile(short refNum); +/* ¦ Write the contents of a file's access path buffer (the fork data). + The FlushFile function writes the contents of a file's access path + buffer (the fork data) to the volume. Note: some of the file's catalog + information stored on the volume may not be correct until FlushVol + is called. + + refNum input: The file reference number of an open file. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnOpnErr -38 File not open + fnfErr -43 File not found + rfNumErr -51 Bad reference number + extFSErr -58 External file system error - no file + system claimed this call. +*/ + +/*****************************************************************************/ + +pascal OSErr LockRange(short refNum, + long rangeLength, + long rangeStart); +/* ¦ Lock a portion of a file. + The LockRange function locks (denies access to) a portion of a file + that was opened with shared read/write permission. + + refNum input: The file reference number of an open file. + rangeLength input: The number of bytes in the range. + rangeStart input: The starting byte in the range to lock. + + Result Codes + noErr 0 No error + ioErr -36 I/O error + fnOpnErr -38 File not open + eofErr -39 Logical end-of-file reached + fLckdErr -45 File is locked by another user + paramErr -50 Negative ioReqCount + rfNumErr -51 Bad reference number + extFSErr -58 External file system error - no file + system claimed this call. + volGoneErr -124 Server volume has been disconnected + afpNoMoreLocks -5015 No more ranges can be locked + afpRangeOverlap -5021 Part of range is already locked + + __________ + + Also see: UnlockRange +*/ + +/*****************************************************************************/ + +pascal OSErr UnlockRange(short refNum, + long rangeLength, + long rangeStart); +/* ¦ Unlock a previously locked range. + The UnlockRange function unlocks (allows access to) a previously locked + portion of a file that was opened with shared read/write permission. + + refNum input: The file reference number of an open file. + rangeLength input: The number of bytes in the range. + rangeStart input: The starting byte in the range to unlock. + + Result Codes + noErr 0 No error + ioErr -36 I/O error + fnOpnErr -38 File not open + eofErr -39 Logical end-of-file reached + paramErr -50 Negative ioReqCount + rfNumErr -51 Bad reference number + extFSErr -58 External file system error - no file + system claimed this call. + volGoneErr -124 Server volume has been disconnected + afpRangeNotLocked -5020 Specified range was not locked + + __________ + + Also see: LockRange +*/ + +/*****************************************************************************/ + +pascal OSErr GetForeignPrivs(short vRefNum, + long dirID, + ConstStr255Param name, + void *foreignPrivBuffer, + long *foreignPrivSize, + long *foreignPrivInfo1, + long *foreignPrivInfo2, + long *foreignPrivInfo3, + long *foreignPrivInfo4); +/* ¦ Retrieve the native access-control information. + The GetForeignPrivs function retrieves the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + foreignPrivBuffer input: Pointer to buffer where the privilege + information is returned. + output: Privilege information. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 output: Information specific to privilege model. + foreignPrivInfo2 output: Information specific to privilege model. + foreignPrivInfo3 output: Information specific to privilege model. + foreignPrivInfo4 output: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: FSpGetForeignPrivs, SetForeignPrivs, FSpSetForeignPrivs +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetForeignPrivs(const FSSpec *spec, + void *foreignPrivBuffer, + long *foreignPrivSize, + long *foreignPrivInfo1, + long *foreignPrivInfo2, + long *foreignPrivInfo3, + long *foreignPrivInfo4); +/* ¦ Retrieve the native access-control information. + The FSpGetForeignPrivs function retrieves the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + spec input: An FSSpec record specifying the object. + foreignPrivBuffer input: Pointer to buffer where the privilege + information is returned. + output: Privilege information. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 output: Information specific to privilege model. + foreignPrivInfo2 output: Information specific to privilege model. + foreignPrivInfo3 output: Information specific to privilege model. + foreignPrivInfo4 output: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: GetForeignPrivs, SetForeignPrivs, FSpSetForeignPrivs +*/ + +/*****************************************************************************/ + +pascal OSErr SetForeignPrivs(short vRefNum, + long dirID, + ConstStr255Param name, + const void *foreignPrivBuffer, + long *foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4); +/* ¦ Change the native access-control information. + The SetForeignPrivs function changes the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + foreignPrivBuffer input: Pointer to privilege information buffer. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 input: Information specific to privilege model. + foreignPrivInfo2 input: Information specific to privilege model. + foreignPrivInfo3 input: Information specific to privilege model. + foreignPrivInfo4 input: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: GetForeignPrivs, FSpGetForeignPrivs, FSpSetForeignPrivs +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetForeignPrivs(const FSSpec *spec, + const void *foreignPrivBuffer, + long *foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4); +/* ¦ Change the native access-control information. + The FSpSetForeignPrivs function changes the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + spec input: An FSSpec record specifying the object. + foreignPrivBuffer input: Pointer to privilege information buffer. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 input: Information specific to privilege model. + foreignPrivInfo2 input: Information specific to privilege model. + foreignPrivInfo3 input: Information specific to privilege model. + foreignPrivInfo4 input: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: GetForeignPrivs, FSpGetForeignPrivs, SetForeignPrivs +*/ + +/*****************************************************************************/ + +pascal OSErr HGetLogInInfo(ConstStr255Param volName, + short vRefNum, + short *loginMethod, + StringPtr userName); +/* ¦ Get the login method and user name used to log on to a shared volume. + The HGetLogInInfo function retrieves the login method and user name + used to log on to a particular shared volume. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: The volume reference number. + loginMethod output: The login method used (kNoUserAuthentication, + kPassword, kEncryptPassword, or + kTwoWayEncryptPassword). + userName input: Points to a buffer (minimum Str31) where the user + name is to be returned or must be nil. + output: The user name. + + Result Codes + noErr 0 No error + nsvErr -35 Specified volume doesnÕt exist + paramErr -50 Function not supported by volume + + __________ + + Also see: HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +pascal OSErr HGetDirAccess(short vRefNum, + long dirID, + ConstStr255Param name, + long *ownerID, + long *groupID, + long *accessRights); +/* ¦ Get a directory's access control information on a shared volume. + The HGetDirAccess function retrieves the directory access control + information for a directory on a shared volume. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + ownerID output: The directory's owner ID. + groupID output: The directory's group ID or + 0 if no group affiliation. + accessRights output: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + paramErr -50 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + to the directory + + __________ + + Also see: HGetLogInInfo, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +pascal OSErr FSpGetDirAccess(const FSSpec *spec, + long *ownerID, + long *groupID, + long *accessRights); +/* ¦ Get a directory's access control information on a shared volume. + The FSpGetDirAccess function retrieves the directory access control + information for a directory on a shared volume. + + spec input: An FSSpec record specifying the directory. + ownerID output: The directory's owner ID. + groupID output: The directory's group ID or + 0 if no group affiliation. + accessRights output: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + paramErr -50 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + to the directory + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +pascal OSErr HSetDirAccess(short vRefNum, + long dirID, + ConstStr255Param name, + long ownerID, + long groupID, + long accessRights); +/* ¦ Set a directory's access control information on a shared volume. + The HSetDirAccess function changes the directory access control + information for a directory on a shared volume. You must own a directory + to change its access control information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + ownerID input: The directory's owner ID. + groupID input: The directory's group ID or + 0 if no group affiliation. + accessRights input: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Parameter error + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +pascal OSErr FSpSetDirAccess(const FSSpec *spec, + long ownerID, + long groupID, + long accessRights); +/* ¦ Set a directory's access control information on a shared volume. + The FSpSetDirAccess function changes the directory access control + information for a directory on a shared volume. You must own a directory + to change its access control information. + + spec input: An FSSpec record specifying the directory. + ownerID input: The directory's owner ID. + groupID input: The directory's group ID or + 0 if no group affiliation. + accessRights input: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Parameter error + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + HMapName, HMapID +*/ + +/*****************************************************************************/ + +pascal OSErr HMapID(ConstStr255Param volName, + short vRefNum, + long ugID, + short objType, + StringPtr name); +/* ¦ Map a user or group ID to a user or group name. + The HMapID function determines the name of a user or group if you know + the user or group ID. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + objType input: The mapping function code: 1 if you're mapping a + user ID to a user name or 2 if you're mapping a + group ID to a group name. + name input: Points to a buffer (minimum Str31) where the user + or group name is to be returned or must be nil. + output: The user or group name. + + Result Codes + noErr 0 No error + fnfErr -43 Unrecognizable owner or group name + paramErr -50 Function not supported by volume + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName +*/ + +/*****************************************************************************/ + +pascal OSErr HMapName(ConstStr255Param volName, + short vRefNum, + ConstStr255Param name, + short objType, + long *ugID); +/* ¦ Map a user or group name to a user or group ID. + The HMapName function determines the user or group ID if you know the + user or group name. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + name input: The user or group name. + objType input: The mapping function code: 3 if you're mapping a + user name to a user ID or 4 if you're mapping a + group name to a group ID. + ugID output: The user or group ID. + + Result Codes + noErr 0 No error + fnfErr -43 Unrecognizable owner or group name + paramErr -50 Function not supported by volume + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapID +*/ + +/*****************************************************************************/ + +pascal OSErr HCopyFile(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName); +/* ¦ Duplicate a file on a file server and optionally to rename it. + The HCopyFile function duplicates a file and optionally to renames it. + The source and destination volumes must be on the same file server. + This function instructs the server to copy the file. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source file name. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstPathname input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new file name if the file is to be + renamed or nil if the file isn't to be renamed. + + Result Codes + noErr 0 No error + dskFulErr -34 Destination volume is full + fnfErr -43 Source file not found, or destination + directory does not exist + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + paramErr -50 Function not supported by volume + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 The user does not have the right to + read the source or write to the + destination + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory + + __________ + + Also see: FSpCopyFile, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +pascal OSErr FSpCopyFile(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName); +/* ¦ Duplicate a file on a file server and optionally to rename it. + The FSpCopyFile function duplicates a file and optionally to renames it. + The source and destination volumes must be on the same file server. + This function instructs the server to copy the file. + + srcSpec input: An FSSpec record specifying the source file. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new file name if the file is to be + renamed or nil if the file isn't to be renamed. + + Result Codes + noErr 0 No error + dskFulErr -34 Destination volume is full + fnfErr -43 Source file not found, or destination + directory does not exist + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + paramErr -50 Function not supported by volume + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 The user does not have the right to + read the source or write to the + destination + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory + + __________ + + Also see: HCopyFile, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +pascal OSErr HMoveRename(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName); +/* ¦ Move a file or directory on a file server and optionally to rename it. + The HMoveRename function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + shared volume. + + vRefNum input: Volume specification. + srcDirID input: Source directory ID. + srcName input: The source object name. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + fnfErr -43 Source file or directory not found + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + dupFNErr -48 Destination already exists + paramErr -50 Function not supported by volume + badMovErr -122 Attempted to move directory into + offspring + afpAccessDenied -5000 The user does not have the right to + move the file or directory + + __________ + + Also see: FSpMoveRename, HMoveRenameCompat, FSpMoveRenameCompat +*/ + +/*****************************************************************************/ + +pascal OSErr FSpMoveRename(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName); +/* ¦ Move a file or directory on a file server and optionally to rename it. + The FSpMoveRename function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + shared volume. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + fnfErr -43 Source file or directory not found + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + dupFNErr -48 Destination already exists + paramErr -50 Function not supported by volume + badMovErr -122 Attempted to move directory into + offspring + afpAccessDenied -5000 The user does not have the right to + move the file or directory + + __________ + + Also see: HMoveRename, HMoveRenameCompat, FSpMoveRenameCompat +*/ + +/*****************************************************************************/ + +pascal OSErr GetVolMountInfoSize(ConstStr255Param volName, + short vRefNum, + short *size); +/* ¦ Get the size of a volume mounting information record. + The GetVolMountInfoSize function determines the how much space the + program needs to allocate for a volume mounting information record. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + size output: The space needed (in bytes) of the volume mounting + information record. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Parameter error + extFSErr -58 External file system error - no file + system claimed this call. + + __________ + + Also see: GetVolMountInfo, VolumeMount BuildAFPVolMountInfo, + RetrieveAFPVolMountInfo +*/ + +/*****************************************************************************/ + +pascal OSErr GetVolMountInfo(ConstStr255Param volName, + short vRefNum, + void *volMountInfo); +/* ¦ Retrieve a volume mounting information record. + The GetVolMountInfo function retrieves a volume mounting information + record containing all the information needed to mount the volume, + except for passwords. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + volMountInfo output: Points to a volume mounting information + record where the mounting information is to + be returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Parameter error + extFSErr -58 External file system error - no file + system claimed this call. + + __________ + + Also see: GetVolMountInfoSize, VolumeMount, BuildAFPVolMountInfo, + RetrieveAFPVolMountInfo +*/ + +/*****************************************************************************/ + +pascal OSErr VolumeMount(const void *volMountInfo, + short *vRefNum); +/* ¦ Mount a volume using a volume mounting information record. + The VolumeMount function mounts a volume using a volume mounting + information record. + + volMountInfo input: Points to a volume mounting information record. + vRefNum output: A volume reference number. + + Result Codes + noErr 0 No error + notOpenErr -28 AppleTalk is not open + nsvErr -35 Volume not found + paramErr -50 Parameter error; typically, zone, server, + and volume name combination is not valid + or not complete, or the user name is not + recognized + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Not enough memory to create a new volume + control block for mounting the volume + afpBadUAM -5002 User authentication method is unknown + afpBadVersNum -5003 Workstation is using an AFP version that + the server doesnÕt recognize + afpNoServer -5016 Server is not responding + afpUserNotAuth -5023 User authentication failed (usually, + password is not correct) + afpPwdExpired -5042 Password has expired on server + afpBadDirIDType -5060 Not a fixed directory ID volume + afpCantMountMoreSrvrs -5061 Maximum number of volumes has been + mounted + afpAlreadyMounted -5062 Volume already mounted + afpSameNodeErr -5063 Attempt to log on to a server running + on the same machine + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, BuildAFPVolMountInfo, + RetrieveAFPVolMountInfo +*/ + +/*****************************************************************************/ + +pascal OSErr Share(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Establish a local volume or directory as a share point. + The Share function establishes a local volume or directory as a + share point. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + + Result Codes + noErr 0 No error + tmfoErr -42 Too many share points + fnfErr -43 File not found + dupFNErr -48 Already a share point with this name + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpAccessDenied -5000 This directory cannot be shared + afpObjectTypeErr -5025 Object was a file, not a directory + afpContainsSharedErr -5033 The directory contains a share point + afpInsideSharedErr -5043 The directory is inside a shared directory + + __________ + + Also see: FSpShare, Unshare, FSpUnshare +*/ + +/*****************************************************************************/ + +pascal OSErr FSpShare(const FSSpec *spec); +/* ¦ Establish a local volume or directory as a share point. + The FSpShare function establishes a local volume or directory as a + share point. + + spec input: An FSSpec record specifying the share point. + + Result Codes + noErr 0 No error + tmfoErr -42 Too many share points + fnfErr -43 File not found + dupFNErr -48 Already a share point with this name + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpAccessDenied -5000 This directory cannot be shared + afpObjectTypeErr -5025 Object was a file, not a directory + afpContainsSharedErr -5033 The directory contains a share point + afpInsideSharedErr -5043 The directory is inside a shared directory + + __________ + + Also see: Share, Unshare, FSpUnshare +*/ + +/*****************************************************************************/ + +pascal OSErr Unshare(short vRefNum, + long dirID, + ConstStr255Param name); +/* ¦ Remove a share point. + The Unshare function removes a share point. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + + Result Codes + noErr 0 No error + fnfErr -43 File not found + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpObjectTypeErr -5025 Object was a file, not a directory; or, + this directory is not a share point + + __________ + + Also see: Share, FSpShare, FSpUnshare +*/ + +/*****************************************************************************/ + +pascal OSErr FSpUnshare(const FSSpec *spec); +/* ¦ Remove a share point. + The FSpUnshare function removes a share point. + + spec input: An FSSpec record specifying the share point. + + Result Codes + noErr 0 No error + fnfErr -43 File not found + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpObjectTypeErr -5025 Object was a file, not a directory; or, + this directory is not a share point + + __________ + + Also see: Share, FSpShare, Unshare +*/ + +/*****************************************************************************/ + +pascal OSErr GetUGEntry(short objType, + StringPtr objName, + long *objID); +/* ¦ Retrieve a user or group entry from the local file server. + The GetUGEntry function retrieves user or group entries from the + local file server. + + objType input: The object type: -1 = group; 0 = user + objName input: Points to a buffer (minimum Str31) where the user + or group name is to be returned or must be nil. + output: The user or group name. + objID input: O to get the first user or group. If the entry objID + last returned by GetUGEntry is passed, then user or + group whose alphabetically next in the list of entries + is returned. + output: The user or group ID. + + Result Codes + noErr 0 No error + fnfErr -43 No more users or groups + paramErr -50 Function not supported; or, ioObjID is + negative + + __________ + + Also see: GetUGEntries +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __MOREFILES__ */ diff --git a/src/mac/morefile/Optim.h b/src/mac/morefile/Optim.h new file mode 100644 index 0000000000..d09caf71c0 --- /dev/null +++ b/src/mac/morefile/Optim.h @@ -0,0 +1,91 @@ +/* +** Apple Macintosh Developer Technical Support +** +** DirectoryCopy: #defines that let you make MoreFiles code more efficient. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: Optimization.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +** +** The Optimization changes to MoreFiles source and header files, along with +** this file and OptimizationEnd.h, let you optimize the code produced +** by MoreFiles in several ways. +** +** 1 -- MoreFiles contains extra code so that many routines can run under +** Mac OS systems back to System 6. If your program requires a specific +** version of Mac OS and your program checks for that version before +** calling MoreFiles routines, then you can remove a lot of compatibility +** code by defining one of the following to 1: +** +** __MACOSSEVENFIVEONEORLATER // assume Mac OS 7.5.1 or later +** __MACOSSEVENFIVEORLATER // assume Mac OS 7.5 or later +** __MACOSSEVENORLATER // assume Mac OS 7.0 or later +** +** By default, all compatibility code is ON. +** +** 2 -- You may disable Pascal calling conventions in all MoreFiles routines +** except for system callbacks that require Pascal calling conventions. +** This will make C programs both smaller and faster. +** Just define __WANTPASCALELIMINATION to be 1 to turn this optimization on +** when building MoreFiles for use from C programs (you'll need to keep +** Pascal calling conventions when linking MoreFiles routines with Pascal +** programs). +** +** 3 -- If Metrowerks compiler is used, "#pragma internal on" may help produce +** better code. However, this option can also cause problems if you're +** trying to build MoreFiles as a shared library, so it is by default not used. +** Just define __USEPRAGMAINTERNAL to be 1 to turn this optimization on. +** +** Original changes supplied by Fabrizio Oddone +** +** File: Optimization.h +*/ + + +#ifndef __MACOSSEVENFIVEONEORLATER + #define __MACOSSEVENFIVEONEORLATER 0 +#endif + +#ifndef __MACOSSEVENFIVEORLATER + #define __MACOSSEVENFIVEORLATER __MACOSSEVENFIVEONEORLATER +#endif + +#ifndef __MACOSSEVENORLATER + #if GENERATINGCFM + #define __MACOSSEVENORLATER 1 + #else + #define __MACOSSEVENORLATER __MACOSSEVENFIVEORLATER + #endif +#endif + + +#ifndef __WANTPASCALELIMINATION + #define __WANTPASCALELIMINATION 0 +#endif + +#if __WANTPASCALELIMINATION + #define pascal +#endif + + +#ifndef __USEPRAGMAINTERNAL + #define __USEPRAGMAINTERNAL 0 +#endif + +#if __USEPRAGMAINTERNAL + #if defined(__MWERKS__) + #pragma internal on + #endif +#endif + diff --git a/src/mac/morefile/OptimEnd.h b/src/mac/morefile/OptimEnd.h new file mode 100644 index 0000000000..5cab00a593 --- /dev/null +++ b/src/mac/morefile/OptimEnd.h @@ -0,0 +1,40 @@ +/* +** Apple Macintosh Developer Technical Support +** +** DirectoryCopy: #defines that let you make MoreFiles code more efficient. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: OptimizationEnd.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +** +** The Optimization changes to MoreFiles source and header files, along with +** this file and Optimization.h, let you optimize the code produced by MoreFiles +** in several ways. +** +** Original changes supplied by Fabrizio Oddone +*/ + + +#if __USEPRAGMAINTERNAL + #if defined(__MWERKS__) + #pragma internal reset + #endif +#endif + + +#if __WANTPASCALELIMINATION + #ifndef __COMPILINGMOREFILES + #undef pascal + #endif +#endif diff --git a/src/mac/morefile/Search.cpp b/src/mac/morefile/Search.cpp new file mode 100644 index 0000000000..bdcfd9ff1a --- /dev/null +++ b/src/mac/morefile/Search.cpp @@ -0,0 +1,1275 @@ +/* +** Apple Macintosh Developer Technical Support +** +** IndexedSearch and the PBCatSearch compatibility function. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: Search.c +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFile.h" +#include "MoreExtr.h" +#include "Search.h" + +/*****************************************************************************/ + +enum +{ + /* Number of LevelRecs to add each time the searchStack is grown */ + /* 20 levels is probably more than reasonable for most volumes. */ + /* If more are needed, they are allocated 20 levels at a time. */ + kAdditionalLevelRecs = 20 +}; + +/*****************************************************************************/ + +/* +** LevelRecs are used to store the directory ID and index whenever +** IndexedSearch needs to either scan a sub-directory, or return control +** to the caller because the call has timed out or the number of +** matches requested has been found. LevelRecs are stored in an array +** used as a stack. +*/ +struct LevelRec +{ + long dirModDate; /* for detecting most (but not all) catalog changes */ + long dirID; + short index; +}; +typedef struct LevelRec LevelRec; +typedef LevelRec *LevelRecPtr, **LevelRecHandle; + + +/* +** SearchPositionRec is my version of a CatPositionRec. It holds the +** information I need to resuming searching. +*/ +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +struct SearchPositionRec +{ + long initialize; /* Goofy checksum of volume information used to make */ + /* sure we're resuming a search on the same volume. */ + unsigned short stackDepth; /* Current depth on searchStack. */ + short priv[11]; /* For future use... */ +}; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif +typedef struct SearchPositionRec SearchPositionRec; +typedef SearchPositionRec *SearchPositionRecPtr; + + +/* +** ExtendedTMTask is a TMTask record extended to hold the timer flag. +*/ +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +struct ExtendedTMTask +{ + TMTask theTask; + Boolean stopSearch; /* the Time Mgr task will set stopSearch to */ + /* true when the timer expires */ +}; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif +typedef struct ExtendedTMTask ExtendedTMTask; +typedef ExtendedTMTask *ExtendedTMTaskPtr; + +/*****************************************************************************/ + +static OSErr CheckVol(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum, + long *volID); + +static OSErr CheckStack(unsigned short stackDepth, + LevelRecHandle searchStack, + Size *searchStackSize); + +static OSErr VerifyUserPB(CSParamPtr userPB, + Boolean *includeFiles, + Boolean *includeDirs, + Boolean *includeNames); + +static Boolean IsSubString(ConstStr255Param aStringPtr, + ConstStr255Param subStringPtr); + +static Boolean CompareMasked(const long *data1, + const long *data2, + const long *mask, + short longsToCompare); + +static void CheckForMatches(CInfoPBPtr cPB, + CSParamPtr userPB, + const Str63 matchName, + Boolean includeFiles, + Boolean includeDirs); + +#if __WANTPASCALELIMINATION +#undef pascal +#endif + +#if GENERATINGCFM + +static pascal void TimeOutTask(TMTaskPtr tmTaskPtr); + +#else + +static pascal TMTaskPtr GetTMTaskPtr(void); + +static void TimeOutTask(void); + +#endif + +#if __WANTPASCALELIMINATION +#define pascal +#endif + +static long GetDirModDate(short vRefNum, + long dirID); + +/*****************************************************************************/ + +/* +** CheckVol gets the volume's real vRefNum and builds a volID. The volID +** is used to help insure that calls to resume searching with IndexedSearch +** are to the same volume as the last call to IndexedSearch. +*/ +static OSErr CheckVol(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum, + long *volID) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname, vRefNum, &pb); + if ( error == noErr ) + { + /* Return the real vRefNum */ + *realVRefNum = pb.volumeParam.ioVRefNum; + + /* Add together a bunch of things that aren't supposed to change on */ + /* a mounted volume that's being searched and that should come up with */ + /* a fairly unique number */ + *volID = pb.volumeParam.ioVCrDate + + pb.volumeParam.ioVRefNum + + pb.volumeParam.ioVNmAlBlks + + pb.volumeParam.ioVAlBlkSiz + + pb.volumeParam.ioVFSID; + } + return ( error ); +} + +/*****************************************************************************/ + +/* +** CheckStack checks the size of the search stack (array) to see if there's +** room to push another LevelRec. If not, CheckStack grows the stack by +** another kAdditionalLevelRecs elements. +*/ +static OSErr CheckStack(unsigned short stackDepth, + LevelRecHandle searchStack, + Size *searchStackSize) +{ + OSErr result; + + if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) ) + { + /* Time to grow stack */ + SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec))); + result = MemError(); /* should be noErr */ + *searchStackSize = InlineGetHandleSize((Handle)searchStack); + } + else + { + result = noErr; + } + + return ( result ); +} + +/*****************************************************************************/ + +/* +** VerifyUserPB makes sure the parameter block passed to IndexedSearch has +** valid parameters. By making this check once, we don't have to worry about +** things like NULL pointers, strings being too long, etc. +** VerifyUserPB also determines if the search includes files and/or +** directories, and determines if a full or partial name search was requested. +*/ +static OSErr VerifyUserPB(CSParamPtr userPB, + Boolean *includeFiles, + Boolean *includeDirs, + Boolean *includeNames) +{ + CInfoPBPtr searchInfo1; + CInfoPBPtr searchInfo2; + + searchInfo1 = userPB->ioSearchInfo1; + searchInfo2 = userPB->ioSearchInfo2; + + /* ioMatchPtr cannot be NULL */ + if ( userPB->ioMatchPtr == NULL ) + { + goto ParamErrExit; + } + + /* ioSearchInfo1 cannot be NULL */ + if ( searchInfo1 == NULL ) + { + goto ParamErrExit; + } + + /* If any bits except partialName, fullName, or negate are set, then */ + /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required */ + if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) && + ( searchInfo2 == NULL )) + { + goto ParamErrExit; + } + + *includeFiles = false; + *includeDirs = false; + *includeNames = false; + + if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 ) + { + /* If any kind of name matching is requested, then ioNamePtr in */ + /* ioSearchInfo1 cannot be NULL or a zero-length string */ + if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) || + (searchInfo1->hFileInfo.ioNamePtr[0] == 0) || + (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) ) + { + goto ParamErrExit; + } + + *includeNames = true; + } + + if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 ) + { + /* The only attributes you can search on are the directory flag */ + /* and the locked flag. */ + if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(ioDirMask | 0x01)) != 0 ) + { + goto ParamErrExit; + } + + /* interested in the directory bit? */ + if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* yes, so do they want just directories or just files? */ + if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 ) + { + *includeDirs = true; + } + else + { + *includeFiles = true; + } + } + else + { + /* no interest in directory bit - get both files and directories */ + *includeDirs = true; + *includeFiles = true; + } + } + else + { + /* no attribute checking - get both files and directories */ + *includeDirs = true; + *includeFiles = true; + } + + /* If directories are included in the search, */ + /* then the locked attribute cannot be requested. */ + if ( *includeDirs && + ((userPB->ioSearchBits & fsSBFlAttrib) != 0) && + ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) ) + { + goto ParamErrExit; + } + + /* If files are included in the search, then there cannot be */ + /* a search on the number of files. */ + if ( *includeFiles && + ((userPB->ioSearchBits & fsSBDrNmFls) != 0) ) + { + goto ParamErrExit; + } + + /* If directories are included in the search, then there cannot */ + /* be a search on file lengths. */ + if ( *includeDirs && + ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) ) + { + goto ParamErrExit; + } + + return ( noErr ); + +ParamErrExit: + return ( paramErr ); +} + +/*****************************************************************************/ + +/* +** IsSubString checks to see if a string is a substring of another string. +** Both input strings have already been converted to all uppercase using +** UprString (the same non-international call the File Manager uses). +*/ +static Boolean IsSubString(ConstStr255Param aStringPtr, + ConstStr255Param subStringPtr) +{ + short strLength; /* length of string */ + short subStrLength; /* length of subString */ + Boolean found; /* result of test */ + short index; /* current index into string */ + + found = false; + strLength = aStringPtr[0]; + subStrLength = subStringPtr[0]; + + if ( subStrLength <= strLength) + { + register short count; /* search counter */ + register short strIndex; /* running index into string */ + register short subStrIndex; /* running index into subString */ + + /* start looking at first character */ + index = 1; + + /* continue looking until remaining string is shorter than substring */ + count = strLength - subStrLength + 1; + + do + { + strIndex = index; /* start string index at index */ + subStrIndex = 1; /* start subString index at 1 */ + + while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) ) + { + if ( subStrIndex == subStrLength ) + { + /* all characters in subString were found */ + found = true; + } + else + { + /* check next character of substring against next character of string */ + ++subStrIndex; + ++strIndex; + } + } + + if ( !found ) + { + /* start substring search again at next string character */ + ++index; + --count; + } + } while ( count != 0 && (!found) ); + } + + return ( found ); +} + +/*****************************************************************************/ + +/* +** CompareMasked does a bitwise comparison with mask on 1 or more longs. +** data1 and data2 are first exclusive-ORed together resulting with bits set +** where they are different. That value is then ANDed with the mask resulting +** with bits set if the test fails. true is returned if the tests pass. +*/ +static Boolean CompareMasked(const long *data1, + const long *data2, + const long *mask, + short longsToCompare) +{ + Boolean result = true; + + while ( (longsToCompare != 0) && (result == true) ) + { + /* (*data1 ^ *data2) = bits that are different, so... */ + /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */ + + if ( ((*data1 ^ *data2) & *mask) != 0 ) + result = false; + + ++data1; + ++data2; + ++mask; + --longsToCompare; + } + + return ( result ); +} + +/*****************************************************************************/ + +/* +** Check for matches compares the search criteria in userPB to the file +** system object in cPB. If there's a match, then the information in cPB is +** is added to the match array and the actual match count is incremented. +*/ +static void CheckForMatches(CInfoPBPtr cPB, + CSParamPtr userPB, + const Str63 matchName, + Boolean includeFiles, + Boolean includeDirs) +{ + long searchBits; + CInfoPBPtr searchInfo1; + CInfoPBPtr searchInfo2; + Str63 itemName; /* copy of object's name for partial name matching */ + Boolean foundMatch; + + foundMatch = false; /* default to no match */ + + searchBits = userPB->ioSearchBits; + searchInfo1 = userPB->ioSearchInfo1; + searchInfo2 = userPB->ioSearchInfo2; + + /* Into the if statements that go on forever... */ + + if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 ) + { + if (!includeFiles) + { + goto Failed; + } + } + else + { + if (!includeDirs) + { + goto Failed; + } + } + + if ( (searchBits & fsSBPartialName) != 0 ) + { + if ( (cPB->hFileInfo.ioNamePtr[0] > 0) && + (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) ) + { + /* Make uppercase copy of object name */ + BlockMoveData(cPB->hFileInfo.ioNamePtr, + itemName, + cPB->hFileInfo.ioNamePtr[0] + 1); + /* Use the same non-international call the File Manager uses */ + UpperString(itemName, true); + } + else + { + goto Failed; + } + + { + if ( !IsSubString(itemName, matchName) ) + { + goto Failed; + } + else if ( searchBits == fsSBPartialName ) + { + /* optimize for name matching only since it is most common way to search */ + goto Hit; + } + } + } + + if ( (searchBits & fsSBFullName) != 0 ) + { + /* Use the same non-international call the File Manager uses */ + if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) ) + { + goto Failed; + } + else if ( searchBits == fsSBFullName ) + { + /* optimize for name matching only since it is most common way to search */ + goto Hit; + } + } + + if ( (searchBits & fsSBFlParID) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) || + ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlAttrib) != 0 ) + { + if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) & + searchInfo2->hFileInfo.ioFlAttrib) != 0 ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBDrNmFls) != 0 ) + { + if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) || + ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlFndrInfo) != 0 ) /* fsSBFlFndrInfo is same as fsSBDrUsrWds */ + { + if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo), + (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo), + (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo), + sizeof(FInfo) / sizeof(long)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlXFndrInfo) != 0 ) /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */ + { + if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo), + (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo), + (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo), + sizeof(FXInfo) / sizeof(long)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlLgLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlPyLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlRLgLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlRPyLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlCrDat) != 0 ) /* fsSBFlCrDat is same as fsSBDrCrDat */ + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) || + ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlMdDat) != 0 ) /* fsSBFlMdDat is same as fsSBDrMdDat */ + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) || + ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlBkDat) != 0 ) /* fsSBFlBkDat is same as fsSBDrBkDat */ + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) || + ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) ) + { + goto Failed; + } + } + + /* Hey, we passed all of the tests! */ + +Hit: + foundMatch = true; + +/* foundMatch is false if code jumps to Failed */ +Failed: + /* Do we reverse our findings? */ + if ( (searchBits & fsSBNegate) != 0 ) + { + foundMatch = !foundMatch; /* matches are not, not matches are */ + } + + if ( foundMatch ) + { + + /* Move the match into the match buffer */ + userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum; + userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID; + if ( cPB->hFileInfo.ioNamePtr[0] > 63 ) + { + cPB->hFileInfo.ioNamePtr[0] = 63; + } + BlockMoveData(cPB->hFileInfo.ioNamePtr, + userPB->ioMatchPtr[userPB->ioActMatchCount].name, + cPB->hFileInfo.ioNamePtr[0] + 1); + + /* increment the actual count */ + ++(userPB->ioActMatchCount); + } +} + +/*****************************************************************************/ + +/* +** TimeOutTask is executed when the timer goes off. It simply sets the +** stopSearch field to true. After each object is found and possibly added +** to the matches buffer, stopSearch is checked to see if the search should +** continue. +*/ + +#if __WANTPASCALELIMINATION +#undef pascal +#endif + +#if GENERATINGCFM + +static pascal void TimeOutTask(TMTaskPtr tmTaskPtr) +{ + ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true; +} + +#else + +static pascal TMTaskPtr GetTMTaskPtr(void) + ONEWORDINLINE(0x2e89); /* MOVE.L A1,(SP) */ + +static void TimeOutTask(void) +{ + ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true; +} + +#endif + +#if __WANTPASCALELIMINATION +#define pascal +#endif + +/*****************************************************************************/ + +/* +** GetDirModDate returns the modification date of a directory. If there is +** an error getting the modification date, -1 is returned to indicate +** something went wrong. +*/ +static long GetDirModDate(short vRefNum, + long dirID) +{ + CInfoPBRec pb; + Str31 tempName; + long modDate; + + /* Protection against File Sharing problem */ + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + pb.dirInfo.ioFDirIndex = -1; /* use ioDrDirID */ + + if ( PBGetCatInfoSync(&pb) == noErr ) + { + modDate = pb.dirInfo.ioDrMdDat; + } + else + { + modDate = -1; + } + + return ( modDate ); +} + +/*****************************************************************************/ + +pascal OSErr IndexedSearch(CSParamPtr pb, + long dirID) +{ + static LevelRecHandle searchStack = NULL; /* static handle to LevelRec stack */ + static Size searchStackSize = 0; /* size of static handle */ + SearchPositionRecPtr catPosition; + long modDate; + short index; + ExtendedTMTask timerTask; + OSErr result; + short realVRefNum; + Str63 itemName; + CInfoPBRec cPB; + long tempLong; + Boolean includeFiles; + Boolean includeDirs; + Boolean includeNames; + Str63 upperName; + + timerTask.stopSearch = false; /* don't stop yet! */ + + /* If request has a timeout, install a Time Manager task. */ + if ( pb->ioSearchTime != 0 ) + { + /* Start timer */ + timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask); + InsTime((QElemPtr)&(timerTask.theTask)); + PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime); + } + + /* Check the parameter block passed for things that we don't want to assume */ + /* are OK later in the code. For example, make sure pointers to data structures */ + /* and buffers are not NULL. And while we're in there, see if the request */ + /* specified searching for files, directories, or both, and see if the search */ + /* was by full or partial name. */ + result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames); + if ( result == noErr ) + { + pb->ioActMatchCount = 0; /* no matches yet */ + + if ( includeNames ) + { + /* The search includes seach by full or partial name. */ + /* Make an upper case copy of the match string to pass to */ + /* CheckForMatches. */ + BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr, + upperName, + pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1); + /* Use the same non-international call the File Manager uses */ + UpperString(upperName, true); + } + + /* Prevent casting to my type throughout code */ + catPosition = (SearchPositionRecPtr)&pb->ioCatPosition; + + /* Create searchStack first time called */ + if ( searchStack == NULL ) + { + searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec)); + } + + /* Make sure searchStack really exists */ + if ( searchStack != NULL ) + { + searchStackSize = InlineGetHandleSize((Handle)searchStack); + + /* See if the search is a new search or a resumed search. */ + if ( catPosition->initialize == 0 ) + { + /* New search. */ + + /* Get the real vRefNum and fill in catPosition->initialize. */ + result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize); + if ( result == noErr ) + { + /* clear searchStack */ + catPosition->stackDepth = 0; + + /* use dirID parameter passed and... */ + index = -1; /* start with the passed directory itself! */ + } + } + else + { + /* We're resuming a search. */ + + /* Get the real vRefNum and make sure catPosition->initialize is valid. */ + result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong); + if ( result == noErr ) + { + /* Make sure the resumed search is to the same volume! */ + if ( catPosition->initialize == tempLong ) + { + /* For resume, catPosition->stackDepth > 0 */ + if ( catPosition->stackDepth > 0 ) + { + /* Position catPosition->stackDepth to access last saved level */ + --(catPosition->stackDepth); + + /* Get the dirID and index for the next item */ + dirID = (*searchStack)[catPosition->stackDepth].dirID; + index = (*searchStack)[catPosition->stackDepth].index; + + /* Check the dir's mod date against the saved mode date on our "stack" */ + modDate = GetDirModDate(realVRefNum, dirID); + if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate ) + { + result = catChangedErr; + } + } + else + { + /* Invalid catPosition record was passed */ + result = paramErr; + } + } + else + { + /* The volume is not the same */ + result = catChangedErr; + } + } + } + + if ( result == noErr ) + { + /* ioNamePtr and ioVRefNum only need to be set up once. */ + cPB.hFileInfo.ioNamePtr = itemName; + cPB.hFileInfo.ioVRefNum = realVRefNum; + + /* + ** Here's the loop that: + ** Finds the next item on the volume. + ** If noErr, calls the code to check for matches and add matches + ** to the match buffer. + ** Sets up dirID and index for to find the next item on the volume. + ** + ** The looping ends when: + ** (a) an unexpected error is returned by PBGetCatInfo. All that + ** is expected is noErr and fnfErr (after the last item in a + ** directory is found). + ** (b) the caller specified a timeout and our Time Manager task + ** has fired. + ** (c) the number of matches requested by the caller has been found. + ** (d) the last item on the volume was found. + */ + do + { + /* get the next item */ + cPB.hFileInfo.ioFDirIndex = index; + cPB.hFileInfo.ioDirID = dirID; + result = PBGetCatInfoSync(&cPB); + if ( index != -1 ) + { + if ( result == noErr ) + { + /* We found something */ + + CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs); + + ++index; + if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 ) + { + /* It's a directory */ + + result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize); + if ( result == noErr ) + { + /* Save the current state on the searchStack */ + /* when we come back, this is where we'll start */ + (*searchStack)[catPosition->stackDepth].dirID = dirID; + (*searchStack)[catPosition->stackDepth].index = index; + (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID); + + /* position catPosition->stackDepth for next saved level */ + ++(catPosition->stackDepth); + + /* The next item to get is the 1st item in the child directory */ + dirID = cPB.dirInfo.ioDrDirID; + index = 1; + } + } + /* else do nothing for files */ + } + else + { + /* End of directory found (or we had some error and that */ + /* means we have to drop out of this directory). */ + /* Restore last thing put on stack and */ + /* see if we need to continue or quit. */ + if ( catPosition->stackDepth > 0 ) + { + /* position catPosition->stackDepth to access last saved level */ + --(catPosition->stackDepth); + + dirID = (*searchStack)[catPosition->stackDepth].dirID; + index = (*searchStack)[catPosition->stackDepth].index; + + /* Check the dir's mod date against the saved mode date on our "stack" */ + modDate = GetDirModDate(realVRefNum, dirID); + if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate ) + { + result = catChangedErr; + } + else + { + /* Going back to ancestor directory. */ + /* Clear error so we can continue. */ + result = noErr; + } + } + else + { + /* We hit the bottom of the stack, so we'll let the */ + /* the eofErr drop us out of the loop. */ + result = eofErr; + } + } + } + else + { + /* Special case for index == -1; that means that we're starting */ + /* a new search and so the first item to check is the directory */ + /* passed to us. */ + if ( result == noErr ) + { + /* We found something */ + + CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs); + + /* Now, set the index to 1 and then we're ready to look inside */ + /* the passed directory. */ + index = 1; + } + } + } while ( (!timerTask.stopSearch) && /* timer hasn't fired */ + (result == noErr) && /* no unexpected errors */ + (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */ + + /* Did we drop out of the loop because of timeout or */ + /* ioReqMatchCount was found? */ + if ( result == noErr ) + { + result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize); + if ( result == noErr ) + { + /* Either there was a timeout or ioReqMatchCount was reached. */ + /* Save the dirID and index for the next time we're called. */ + + (*searchStack)[catPosition->stackDepth].dirID = dirID; + (*searchStack)[catPosition->stackDepth].index = index; + (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID); + + /* position catPosition->stackDepth for next saved level */ + + ++(catPosition->stackDepth); + } + } + } + } + else + { + /* searchStack Handle could not be allocated */ + result = memFullErr; + } + } + + if ( pb->ioSearchTime != 0 ) + { + /* Stop Time Manager task here if it was installed */ + RmvTime((QElemPtr)&(timerTask.theTask)); + DisposeRoutineDescriptor(timerTask.theTask.tmAddr); + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock) +{ + static Boolean fullExtFSDispatchingtested = false; + static Boolean hasFullExtFSDispatching = false; + OSErr result; + Boolean supportsCatSearch; + long response; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize; + + result = noErr; + + /* See if File Manager will pass CatSearch requests to external file systems */ + /* we'll store the results in a static variable so we don't have to call Gestalt */ + /* everytime we're called. */ + if ( !fullExtFSDispatchingtested ) + { + fullExtFSDispatchingtested = true; + if ( Gestalt(gestaltFSAttr, &response) == noErr ) + { + hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0); + } + } + + /* CatSearch is a per volume attribute, so we have to check each time we're */ + /* called to see if it is available on the volume specified. */ + supportsCatSearch = false; + if ( hasFullExtFSDispatching ) + { + infoSize = sizeof(GetVolParmsInfoBuffer); + result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum, + &volParmsInfo, &infoSize); + if ( result == noErr ) + { + supportsCatSearch = hasCatSearch(volParmsInfo); + } + } + + /* noErr or paramErr is OK here. */ + /* paramErr just means that GetVolParms isn't supported by this volume */ + if ( (result == noErr) || (result == paramErr) ) + { + if ( supportsCatSearch ) + { + /* Volume supports CatSearch so use it. */ + /* CatSearch is faster than an indexed search. */ + result = PBCatSearchSync(paramBlock); + } + else + { + /* Volume doesn't support CatSearch so */ + /* search using IndexedSearch from root directory. */ + result = IndexedSearch(paramBlock, fsRtDirID); + } + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr NameFileSearch(ConstStr255Param volName, + short vRefNum, + ConstStr255Param fileName, + FSSpecPtr matches, + long reqMatchCount, + long *actMatchCount, + Boolean newSearch, + Boolean partial) +{ + CInfoPBRec searchInfo1, searchInfo2; + HParamBlockRec pb; + OSErr error; + static CatPositionRec catPosition; + static short lastVRefNum = 0; + + /* get the real volume reference number */ + error = DetermineVRefNum(volName, vRefNum, &vRefNum); + if ( error != noErr ) + return ( error ); + + pb.csParam.ioNamePtr = NULL; + pb.csParam.ioVRefNum = vRefNum; + pb.csParam.ioMatchPtr = matches; + pb.csParam.ioReqMatchCount = reqMatchCount; + if ( partial ) /* tell CatSearch what we're looking for: */ + { + pb.csParam.ioSearchBits = fsSBPartialName + fsSBFlAttrib; /* partial name file matches or */ + } + else + { + pb.csParam.ioSearchBits = fsSBFullName + fsSBFlAttrib; /* full name file matches */ + } + pb.csParam.ioSearchInfo1 = &searchInfo1; + pb.csParam.ioSearchInfo2 = &searchInfo2; + pb.csParam.ioSearchTime = 0; + if ( (newSearch) || /* If caller specified new search */ + (lastVRefNum != vRefNum) ) /* or if last search was to another volume, */ + { + catPosition.initialize = 0; /* then search from beginning of catalog */ + } + pb.csParam.ioCatPosition = catPosition; + pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize); + + /* search for fileName */ + searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName; + searchInfo2.hFileInfo.ioNamePtr = NULL; + + /* only match files (not directories) */ + searchInfo1.hFileInfo.ioFlAttrib = 0x00; + searchInfo2.hFileInfo.ioFlAttrib = ioDirMask; + + error = PBCatSearchSyncCompat((CSParamPtr)&pb); + + if ( (error == noErr) || /* If no errors or the end of catalog was */ + (error == eofErr) ) /* found, then the call was successful so */ + { + *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */ + } + else + { + *actMatchCount = 0; /* else no matches found */ + } + + if ( (error == noErr) || /* If no errors */ + (error == catChangedErr) ) /* or there was a change in the catalog */ + { + catPosition = pb.csParam.ioCatPosition; + lastVRefNum = vRefNum; + /* we can probably start the next search where we stopped this time */ + } + else + { + catPosition.initialize = 0; + /* start the next search from beginning of catalog */ + } + + if ( pb.csParam.ioOptBuffer != NULL ) + { + DisposePtr(pb.csParam.ioOptBuffer); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr CreatorTypeFileSearch(ConstStr255Param volName, + short vRefNum, + OSType creator, + OSType fileType, + FSSpecPtr matches, + long reqMatchCount, + long *actMatchCount, + Boolean newSearch) +{ + CInfoPBRec searchInfo1, searchInfo2; + HParamBlockRec pb; + OSErr error; + static CatPositionRec catPosition; + static short lastVRefNum = 0; + + /* get the real volume reference number */ + error = DetermineVRefNum(volName, vRefNum, &vRefNum); + if ( error != noErr ) + return ( error ); + + pb.csParam.ioNamePtr = NULL; + pb.csParam.ioVRefNum = vRefNum; + pb.csParam.ioMatchPtr = matches; + pb.csParam.ioReqMatchCount = reqMatchCount; + pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo; /* Looking for finder info file matches */ + pb.csParam.ioSearchInfo1 = &searchInfo1; + pb.csParam.ioSearchInfo2 = &searchInfo2; + pb.csParam.ioSearchTime = 0; + if ( (newSearch) || /* If caller specified new search */ + (lastVRefNum != vRefNum) ) /* or if last search was to another volume, */ + { + catPosition.initialize = 0; /* then search from beginning of catalog */ + } + pb.csParam.ioCatPosition = catPosition; + pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize); + + /* no fileName */ + searchInfo1.hFileInfo.ioNamePtr = NULL; + searchInfo2.hFileInfo.ioNamePtr = NULL; + + /* only match files (not directories) */ + searchInfo1.hFileInfo.ioFlAttrib = 0x00; + searchInfo2.hFileInfo.ioFlAttrib = ioDirMask; + + /* search for creator; if creator = 0x00000000, ignore creator */ + searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator; + if ( creator == (OSType)0x00000000 ) + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0x00000000; + } + else + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0xffffffff; + } + + /* search for fileType; if fileType = 0x00000000, ignore fileType */ + searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType; + if ( fileType == (OSType)0x00000000 ) + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0x00000000; + } + else + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0xffffffff; + } + + /* zero all other FInfo fields */ + searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0; + searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0; + searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0; + searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0; + + searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0; + searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0; + searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0; + searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0; + + error = PBCatSearchSyncCompat((CSParamPtr)&pb); + + if ( (error == noErr) || /* If no errors or the end of catalog was */ + (error == eofErr) ) /* found, then the call was successful so */ + { + *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */ + } + else + { + *actMatchCount = 0; /* else no matches found */ + } + + if ( (error == noErr) || /* If no errors */ + (error == catChangedErr) ) /* or there was a change in the catalog */ + { + catPosition = pb.csParam.ioCatPosition; + lastVRefNum = vRefNum; + /* we can probably start the next search where we stopped this time */ + } + else + { + catPosition.initialize = 0; + /* start the next search from beginning of catalog */ + } + + if ( pb.csParam.ioOptBuffer != NULL ) + { + DisposePtr(pb.csParam.ioOptBuffer); + } + + return ( error ); +} + +/*****************************************************************************/ diff --git a/src/mac/morefile/Search.h b/src/mac/morefile/Search.h new file mode 100644 index 0000000000..fe79c120fd --- /dev/null +++ b/src/mac/morefile/Search.h @@ -0,0 +1,246 @@ +/* +** Apple Macintosh Developer Technical Support +** +** IndexedSearch and the PBCatSearch compatibility function. +** +** by Jim Luther, Apple Developer Technical Support Emeritus +** +** File: Search.h +** +** Copyright © 1992-1998 Apple Computer, Inc. +** All rights reserved. +** +** You may incorporate this sample code into your applications without +** restriction, though the sample code has been provided "AS IS" and the +** responsibility for its operation is 100% yours. However, what you are +** not permitted to do is to redistribute the source as "DSC Sample Code" +** after having made changes. If you're going to re-distribute the source, +** we require that you make it clear in the source that the code was +** descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __SEARCH__ +#define __SEARCH__ + +#include +#include + +#include "Optim.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +pascal OSErr IndexedSearch(CSParamPtr pb, + long dirID); +/* ¦ Search in and below a directory. + The IndexedSearch function performs an indexed search in and below the + specified directory using the same parameters (in pb) as is passed to + PBCatSearch. See Inside Macintosh: Files for a description of the + parameter block. + + pb input: A CSParamPtr record specifying the volume to search + and the search criteria. + output: Fields in the parameter block are returned indicating + the number of matches found, the matches, and if the + search ended with noErr, the CatPosition record that + lets you resume a search where the last search left + off. + dirID input: The directory to search. If fsRtDirID is passed, + the entire volume is searched. + + Note: If you use a high-level debugger and use ioSearchTime to limit + the length of time to run the search, you'll want to step over + calls to IndexedSearch because it installs a Time Manager task. + Most high-level debuggers don't deal gracefully with interrupt + driven code. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + + __________ + + See also: PBCatSearch, PBCatSearchSyncCompat +*/ + +/*****************************************************************************/ + +pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock); +/* ¦ Search a volume using PBCatSearch or IndexedSearch. + The PBCatSearchSyncCompat function uses PBCatSearch (if available) or + IndexedSearch (if PBCatSearch is not available) to search a volume + using a set of search criteria that you specify. It builds a list of all + files or directories that meet your specifications. + + pb input: A CSParamPtr record specifying the volume to search + and the search criteria. + output: Fields in the parameter block are returned indicating + the number of matches found, the matches, and if the + search ended with noErr, the CatPosition record that + lets you resume a search where the last search left + off. + + Note: If you use a high-level debugger and use ioSearchTime to limit + the length of time to run the search, you'll want to step over + calls to PBCatSearchSyncCompat because it calls IndexedSearch + which installs a Time Manager task. Most high-level debuggers + don't deal gracefully with interrupt driven code. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + afpCatalogChanged -5037 Catalog has changed and search cannot + be resumed + + __________ + + See also: PBCatSearch, IndexedSearch +*/ + +/*****************************************************************************/ + +pascal OSErr NameFileSearch(ConstStr255Param volName, + short vRefNum, + ConstStr255Param fileName, + FSSpecPtr matches, + long reqMatchCount, + long *actMatchCount, + Boolean newSearch, + Boolean partial); +/* ¦ Search for files by file name with PBCatSearch. + The NameFileSearch function searches for files with a specific file + name on a volume that supports PBCatSearch. + Note: A result of catChangedErr means the catalog has changed between + searches, but the search can be continued with the possiblity that you + may miss some matches or get duplicate matches. For all other results + (except for noErr), the search cannot be continued. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileName input: The name of the file to search for. + matches input: Pointer to array of FSSpec where the match list is + returned. + reqMatchCount input: Maximum number of matches to return (the number of + elements in the matches array). + actMatchCount output: The number of matches actually returned. + newSearch input: If true, start a new search. If false and if + vRefNum is the same as the last call to + NameFileSearch, then start searching at the + position where the last search left off. + partial input: If the partial parameter is false, then only files + that exactly match fileName will be found. If the + partial parameter is true, then all file names that + contain fileName will be found. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + afpCatalogChanged -5037 Catalog has changed and search cannot + be resumed + + __________ + + Also see: CreatorTypeFileSearch +*/ + +/*****************************************************************************/ + +pascal OSErr CreatorTypeFileSearch(ConstStr255Param volName, + short vRefNum, + OSType creator, + OSType fileType, + FSSpecPtr matches, + long reqMatchCount, + long *actMatchCount, + Boolean newSearch); +/* ¦ Search for files by creator/fileType with PBCatSearch. + The CreatorTypeFileSearch function searches for files with a specific + creator or fileType on a volume that supports PBCatSearch. + Note: A result of catChangedErr means the catalog has changed between + searches, but the search can be continued with the possiblity that you + may miss some matches or get duplicate matches. For all other results + (except for noErr), the search cannot be continued. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The creator type of the file to search for. + To ignore the creator type, pass 0x00000000 in + this field. + fileType input: The file type of the file to search for. + To ignore the file type, pass 0x00000000 in + this field. + matches input: Pointer to array of FSSpec where the match list is + returned. + reqMatchCount input: Maximum number of matches to return (the number of + elements in the matches array). + actMatchCount output: The number of matches actually returned. + newSearch input: If true, start a new search. If false and if + vRefNum is the same as the last call to + CreatorTypeFileSearch, then start searching at the + position where the last search left off. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + afpCatalogChanged -5037 Catalog has changed and search cannot + be resumed + + __________ + + Also see: NameFileSearch +*/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#include "OptimEnd.h" + +#endif /* __SEARCH__ */ diff --git a/src/make_cw.mcp b/src/make_cw.mcp index 4a0fabde9453f3f18fa6ac2232b381ba41d01229..4f2472ee1f4322adb5698199af94548660d81fcc 100644 GIT binary patch delta 12877 zcmcgyd3;nww!U?5cc-&-64IT8tPNoaNeEj&2&ma3fk2Ol0}_D{B3nXY5)cq<24z*$ za7nJdQG`K#D5C@RFg*w=qR)kyLEI4#!lEE30uACa%KJ`Lb<@G|`QH0u>X&oRS6|hw z^_;4@)j4~`wr*{7a&>)ut)eKJqOc4_JDsX%XG#?%bwA?6wXJ5If{6P}HDI;ADNsfT0tT6dN#ND40SD&x>{{kvyKp-$$uZcTqw( z-&J8;r%T;UzSmvmJFYPOl>xB>(LBn>Ld1J$5H(L>p`tz(Ci9;Vnq^Ub{hT?;*l1HT zjVCU!NM>s(=%u$CCgZtG7Hu3FsoE^ls~RUYH+uG&b*3t2eOPj$-X3Dt*Mv;%-h$HC zD@ry-w=dPyqSQ22HP<%|tHw*&&Zg$6d3n`(k~P!w>9`Q)v3+4-MpUI5r$>Dj%47O2 zTZBG5IZAhiJMXj=7 z>tBRKpe(y*ZA`nSVi+329mYMCYM35Z(Z@KmoF(eTxjnqm7g;fDaqaS)&N#sIjbT%a zN0+iaYohGBGr5;1HG8S*W({cmosdRI6Qmh36H*761DOlC3o-*z519>t-LQF(`H%&W zyCDl9iy-$v+>phP-$9l@?uFb3SqfPOSq@nNS*fHcY!$#P$o-H9Agdt{LLP!V?Aeer zNkjLYdKOD&S)O9{2R4dzMp7)KE2JAF36cy+fW$yxhAbYE4(S3(gE%1$NFt;=Bn1)& zN%fpi=dv95kD{D{)Ica-6r=~R8G^DDyr-ZZE8@UZE;z|+<2VvpEKRL z-D+hpNeJ`)rc~{3Vw$H@d>F%=oAulsoAmIC2)*y=PTtW`EP?5Ni`ZmrSkB7zJw+*mx6L(-ES@Qir0xcap{q=p!- zDcW)vzD0j%=wuK3%&O|~6*;Il+=^8o0%G%~4$^F@ex`hn=is7x)dxM|5@hJFF=vSh8J#bpjSneaz+Bf|6L#Aj5`nJMokuux3O_wXmH ze(qD#UnXhAs$QBLLoSSAJ;LC#%BcF;HHsd+HJJ~z?u@_aG44NU@wTTjiwdqApKb5m zObs>bUzWceJeQ%~{4=af^L%bZr;5oG^X)lQ8L5W&?PKd!^%m{dni4#-Ce_R^v1Ufb z+0XldO+9LJ-!-px#uR5`O;i1x#_F0TC9kf&vF45$wKaLAb82f{Rn60LrU#M3n`wQk zDbA^GXb7S!@KPyWx`r;E(O6TB)v!YmV`{1ztEbDHZhviof`e-dG@)Z{fnqz-fkt#F zVoXC(eO*JJ8(b*WR5Gu*@tQ&d^tE}5Ynox-j{E0b;F&wpf$YHr^Twjos)hzyR&pAZYBWp2JhAfRP*ka?svbSYSy9tCttJ>d zT~)IkHML4!9!wzk1v&_1Fu&=v=`BZhc)WwMXi zzc6d78Jk!qraw5e3ai~!s4l<|jt2o_H|BT*Fm_&!rvPJz=9o?k z*m*go4W4#agsAoTi1g%GiFy{8wqk^+ZNS)MIo=JNDe+$5EQ#BJv-M8#muTr`-8@rV zKGj`DTL3ohx24`A_1~oayVU=Xdau;)NWD+$ccp$$>i4DoKr{z&SNg?20ZCGm;W z2c$kI^&zQ0mHM#MN2G3-`l!^$q&_b738_Dm`g5sIa*g>sC5bPjJ}vbbslSx^E2+;) z{k7ERq&_e81*v^fUzGZi)Za*bnY0^2|E(mxllpt9e~|j0QvWFRPf}kInkiB}`I=LZ%@MnyY8Kb3+k=h}3tknEm zf(rTh13INNjawf}l^N1VTVbbCtDd=IduT1L5Pi(Jf?*;XPJU_mA(JmOY~H+CHcHvd zhDA@N2sjFUAkv9xDH!Q}bUl0Px?ejF(DIqempA`;&t%+aQ_H=fCs>3WBlm4X2FBW~}Fq`qi1U1#0_LO>u6=$6u zfwPX)tgjwAg_cmP`Nn*U`oT5(ffJ|OI5a`7;@D%(4{&(rEn^xp-VRs!0XVSYr+8ME z6ss8+>L^GwBnIMu#6sdBNstssDkRN&V2U0x-Rr2uXq@L#UWkNT4X-u8?#x0sQqe7Qd@H zt%#|JW$+m7wfMO9@lzvzw7AN|*I@k?b&oufC-OVp|?C9%bTp@v>Vy+U}V)L*e#I+oj;{dYGoL~RHhsDt;5Z)`*906_0^_=XWBTOBNrU4* z05{9iM8->q;QoLod;&aI;%|ZP61W$REp(4Sp;rztP6!;|1dMwHj?odiU*LEJFzy*R z#=Ovd1IKRxVR5HR z503W%BaP!rzz++Y9Sz*lQn3BkeBSRZ<1MO^WV}0yTk7^}3~oO&*5_&;2W}MpY$SDM z#m4F>+9>*p#|kr%m-FyoZALeKo`y3f{X4GnvanWCkN-E2<@t^&rj~*-KG?X6xuX8h(|eeC=i0PsJ&nUXG)rjU1Kzk2 ztHt%a`az#pP>s*}CVcBPPj6B>F3L^D(PYhT>u>>JJk&{xEe#ZxS2$-z?Nldy>{k8k z^}OPmDRZVxt7&xJQiX5J=ARY6uB(<1)U;Q-YWAS$k&dbFq-Y7o2dp3~eg=Vz_U<7vOt^euKRKB(+h zQFX@Es`Fe`wRJbwEck_nMmsvZ+!xXASKH)r6*-IQr`D9!%>G#yYaQs~)vQKzk{WAF zjL|IPu9EcrFH8m{xk^f}EjEwyzc5KGR&xaPbVGt>4<2OJSBnd3{Jg<~ezEcWvb5x& zl75pc4JrL<+v)Xx*>>ZdTx}CSwcz1mbeJmb%pBgy6WNc9P1lb;+tavyGIOfa8}vIj zr+Ci|R$ox{AzM<7T|?FG`t#e$nAzyGK<()rw1V9mXQta?htd894t6+m(n=j0h|!I7 zd>}@5%bf$USi}+oG1?E412MYQb_QZ8h;#>f z(iM5kim|swTQ!JZCuAVcjE;Gj3n%dGx`w8Xyt)Hzi2S4R;2m0_TGeX2cZW7HF&xJw z)!Kk7Ql((dcc>qy6$34Hzzq?+?e8cjTIAjj^OQ;rrR`JLcy5PV|0v1huLlNdwq4;JZe z9A=aPksk7%5i>(8>r5xR2>Cjl=1N3zQlZf}L(6dw7>M{Lk^TT}c^F>`acl9&;e7u0 zmB7P^Rg|)KG1juYP&!(cb6N?f16x;-|2Q>igy5rK1ByeGANnEiNWsT6Lnk)4slWNJ zFGQq5B*amIs389&Mot+e_+lM;jVNevEbwT-5BLZ)q5F=7*S z@I+lz1Lv_8$iNJ=)Vj-fex|lcjoWSH&BEv`-iApLjVZiDFFeU}vl0h>9~DzwG=O^b zlqjYA8JIu_Z%om|m%u$O_{xt17FslL1-Snd{IXZUn|S`hOSgmDB>3bAXonb`qO_M0 zLBSNVX*k0~1vQ6(pCwjNa&dl^qgK@G7%+~k6mkdPA#A>A0NhmhBe9sBEoi7klrz^3 z;yF$$*Bj6`2)>#h)Ss6L)Js`V2`)jEKM8)te+8VUXmC9^6ii{&69MNbx{n&UmD9>< z*pGCcq9G4}dqMEy9t${65x->dfHyv?)so7uKMp87rtsi&U`z^yS1^#$V~Q9x^DmrM zwp|K%Owr3^hF-y!bT`WDFsUOcpYWKXF{glE6zL<+Lfb^&dlTLv`5&N#2^YOiW4lxE z71&n%bJ5=iOcRb!fu-3$7q?snrujoy^9de%txvG@_3 zCQSHd(KcFuU@a6Xy5k{yvuGeq-fm*i53K&uHw(X-1>!YME2IAoEqt@6Il_$y7EB7G zsh6^6X@3L8WKwXccqEDjz4it$34$vb58)0)D@%djBo?oc-wEF=dZ!imEn*dAEsehL z&7yVG__ra(rP#60t^GDFrGX~c%ttOn#lKv1y9x`#>n>?Im!FPf~QFm4&>Xuj3(g&q7~&h z%t^YUZ#C8lg**-61ic_!(YI|d@JGa=AZnN>=%T#_nBGD73?jEN-vMeUO$x&22Lhhx zyR8=pvb+??>4<EDSR*{m%C?^_t0_AV=OumlB^7i=S&Y;VJ#jH~AzYBuGfWluN(!^(J$s#Q>Cp z&O^fQd=DH2{(@*!Kpho+=bJ`$d74-p&M+g=?|cgvfjPr@p% z$Ua3mZ8zNjd`0lRVAH~3SzCJlVTf>E+ver$of86PCKKp?2;vB8F zoApMc#7@atA9)HH$ZMgx(A05{Wv#E$XwW=Eb)}~q`Xv}%Mh*bgyBSY8^y!a+uu1+N zXl~c6tv~FV(WpT!?T_akH|+wfwH?Dp|L?;SFa^S6S(_8XPTz9GFL&U{J(ji6jU4MF z_};6axyQ2B4|;-+5&Y3zupLox>vSwR@+V!^8#X9B7N+2)9#g?k%Xi`_jrUe~nf$Gn zE#C)TTKlnC;NvJedVBy+S;0U1f`}LVad?gJSk}51wj+NJW+yO3!ed$M$FMv4ZP=K4 zmzFeId++lghZQqNWvS7F@$ftBRbJ5^K&sziukvaPtV69e_THssy5C2$9b5P=YwElg z)g@7p3Oj*3a+lRE`~gHV=hah-1G}uL-;3Z=IM2>50~Wiisc8gos^Gs~jk>I&{LF3O z(*%FM6}+jzPN#m;ZmPg35>EfwSTzre!0ERT7b~492a_j%s%ED@0MC6$F?Gi_FMqaX zKC&ipjx{}bGjMlV-Z5ygd7Ac~0^dXMXPnTooacb$FWc@X8iG*-eAa^_MBs`zNeh}1%c09#yXNtnM$%YWEs38~d>#xvlD3f}637N<;8nfoy$43i0k(6VPvG=^O-c`Rpgw=?aI0DptvZ7ZN<&z56_ zU^fcB45K1Sla0KYk(^RKLINw*=Q!5!8`(OWurtwCC!X1Xk%~iqXqx`Dr4p%txso^ zZ5GaD-t+~MXkeYEyjmCO`K@<{BjTVz>PN@VBLC_+M4fkkqZY?4KF)=vi zC`=rqg^kj?;kk#e2;9Oww?PHF`{IJP_AM_x!uu5~|F7Q=?k2ni0tkQhr z8SqmCU->OrCEqd>b2iONwMeifLkqJsw`~PKRq|wQ(khQ10Z+4vhT3Q}rO})utAybY ze;xUk>{#{d;HOC*jh0qvo@e_B3Daf52gYus!qGx2WGmzaaQ(6pH#5VA2zroMbKVot(agP|0$l|zVQI~I?gNBVqmfS+j}67|98rg<(Ld%p?3GMkC0F= zUZ@y@1aaSCe=`+)gW%0%vSKpqRnLJZS3)q8H3}EBSH2Ctk<*HqCQ(d=eF`Sd|0AcF z$`hVrU#o-n|KJ%)o0IUtn6rz4=g5NYffoCO{k8YO!yGA?cj6%nN`~M2e-w4y3Vc9} z1nUPpk7rjOe-gtazLjEpZt(L3|Mo>hq_f)JdI$*%c!JXRCSY-cWH$JJuE%K6 zWg*rC`)CyE|3$UuDWr-D?4xIcU&Qlk&Mz@#GXJur;Hg6hH=|7cG3G`=w?-lX4WW>c zh{qIB!7_{jTTHB?^&EzRCJR3IY4E?}yppjHS}YLu+s1=mBKQ!RBC$Z&r(;F;|4JJ| z4Ukhg9i#5{|708X8zhLijQOq)UO;Opgwe_(=dvFR$Ny9A@K3>u6~kWp40xnb_;@3* zSTXDiXbM&czTY@#v0}uuKLvgz=iN#fhFvZh_Hkrft9Sw{+(%1>=<(9-;P2{afgAIR)gFEyVvfxDh&m delta 7629 zcmZ`-3s_af+MZc!ZT1$}pxl&;h$uoNcsENcbc-mW^6RnVg}h*7mL@5Gzkl|NS}J+6 zqb^~r%&e?DWvT6ByUeuI>>TQtmrBLFn<$8)+q)^}{bmN#{?0tlp7)z~X3d)UzVDq` zuzX45vvuycag!%cWQ>`N@f2o0)`pn{0~w21fp*-wxDakvB{ug4wR)3#ueUDZiR=^x z7vL9x0ii$#eogU2-I2d|uq zw+vA`-LUMMEH*!}jQe&x11F5K?!3>7m67*0F8a~m3N zdCv~7GQ{W;JX_qKXSlsf!j6~;SnCk{25bBi)+${R03!hIx9tZ#cK`h$c24Jb-&f6f zu;qh4|WtGAn{w`;L5L)N!$9t9+(4V`=^9nd@9^|Hf$QtO(f4 z*r=97UE{vLd~8&3@k|fid-48k1D-4Vwo_Rmk0kGf+cRo=+6%MNz5U|m81`EuBE2uP z=xZ*@jrUIDF`PHGbFA+CxINQqI<6&i%J?KQI1!~aqx~?J#0-fE8e8f96ROQEl}a3W zL*uwp&BGgM-o;#!hZF5}Gm>rxOh?WsmVIo5d2>2H5a7*pIk|W>$jqKjB8?K+jZd?{8zh2#AY68UyaD{?pkoKDSlXCxDd0( zT&%C+5q1w}s7#a1wGP{#6X*NhZS*#rj0ckdyzK{vn=*s8p6F?xNlv$C*7g#MyPE6l z!?o#lyYmr?as%pLe9_zUksL0P3d~fs_(f?vu?Za0P{=chz6innMse>x{c=QUo?=+}5r<0Xxk zHC`bshU>3t;+n?m8gFR)P2)|Cw=~u%*@e5=BYEW;O`$rCc{q}qJj6I<@PIfpmn^-hu} z8F|pDE++(|6?cdx&l$-C={(u@^9FNdlxVceO!39;Ha_LPH(odUM*G?gGs`XC`ZBU2u}bYa$e{B|hqsbMW?Xl27v+af}H z8d^>qrXk*E0&zhfui`lrg(|~77}8N3Xv_1(jUclHx5TV8-VN@E@V(T{93vg|fJ@xt zG=ArW(0X^sJnCzTW5LGz_TH_D^?W3G+dlHmUmCm=Bi6W$=5{-$+g@Wj#qy_2m-oYO z_q)aAEF)QF9{0WZy4goN>OZQ&K5Tn@vN+h;d`*gr0v&8D z${kg|j0vs(aO1KYxnE}#o9<`n>u%E2qA3kmmo&az$ygZi^!a*6Y1^DWj=(>C2g#V> z{*gXG)jTGB#j0Y675F@zsph$Zr_W(#%@o_K&G}s&t1vhPWAMd!=NMOSOx$)-;nd&p zaYGpkULxk6F?$*L)uQ~2IVz?pK3NP`;go{MS|@|UBf#CS z_}CDUeb!9R4lG1lJ(uzM+pv46{+V0xT&|jL=?ZWUDn4r;-V>(zjo=VkGR)n7aWc2CtGG)V-WGqa*CG=ufy(_m++?vO6x$#Z6Uad0`>t5pBXjbQgx{cif; zO_zMe^*q@96rXnh-qiConhGvk@j>)gMxq*joVL_o^Unbz)clQpf}Nvy_hw*BVKIdz zW83p+3{V|GuK?8xGAc`9*HJ&tf z;_uS56Zvc7q_KsPr&a5ci(R@h#Cx^oe8at8bUY95l-S5=wU}O)=z*1TFwbnpSbSso zQcow^)w^nv2j3REvBI0X9xVj(0FsamM#o@1~-B$lyIyjW+or_(#IR}+h*gJ*ew zn&(*>^O2-k)KcI>iXX@Eea1dk9bt2UN*G>XR*Z+! zGC!3x+qSSlF`iA7hCao2e_8bMBdGUMTPemf z6|s;?J}RY6&-xc&e@!eFbPko$rl$)9Zws-EeV*K)&0aN6fgs7CbfS1EZF-7=DX80s z#ze29(mQMM9qez2Wvnwz8lx7pbvtbOp>$3|rFZsHAK2d#%UE~vmeQu@2V@re2eCN9 zXnv(l&vIJ)Kk*Ya7tQI#5B>^My~1Cx0(LomaKpG1#+vPvul6tK0EU)@atMCWO7!{5JOJWxg7znucPTk)OgyjMQ*Z(Rd>kK!|10hNIKpUw3Y7s1K)ssQ1oPZ=EmSbGEsLFvw4P7A6f8VA<_R7!XL z7DHgu0U(mDnK8w@Bm@F0nN%e2KL>bS1`NX{ioj0=aR=|5f_7u@6Yl0S7hrtvNktPU z-_4hFL0j&Dmo5*2O<9DsZd!#cua%qTWyUk0{HkO6Rk8aT(xW-@TPc>8MaQAgU?bb3Ed|R<-$82N-*8m;zA^QJO;aFT4#RPV&a7eGRv))nNnp=4yNu+@o(YZ`BT?}Z=v|w zSHN3^;q=vSZJ`$=sE+DbG5Zz{feVY#Rwtd6J|F#bYXUAHo%N+>CE=Rqtu?gV|=0X&idh5K)zswfog>dc_Y}M8%qWvzi4j$Gq@T=A=ukJFl|pLoCI?xy&le*o2uXN67&f4}7UmG^+^#ln5Me75Gl0_r8zAUYh$jbikF3AH@SnPVuT!r@TK3)ngjuVb5hfZ}uB03r`lk?@ZG zI28Cm-Ejb@Cwd2Y#pxEp3qE>LX~EJX>=nSsC&&Q@quZjH_riApPDyU zqJOCDH}kIIIdRFfQcYt(tVp!1w2mw%6`d*#9b>U^K0*#=J;-+|4IRs|P(D)f#z-;C zvZ7*)yb!dtbJ~nS|D&ouPpm_Kv9b1Pw993dIbNms(9MgJDTr*9WsXS1gO5>s7dre@ zmN}+;1%9mLjXV;w$}-2pxDt523MvwPBa| I#$mPmH&Hwm?*IS* -- 2.45.2